@everymatrix/user-actions 1.90.27 → 1.90.29

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,723 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ const index = require('./index-66cfb45f.js');
6
+
7
+ const actionsMapping = {
8
+ "verification-popup": {
9
+ "emit": "openKycVerificationModal",
10
+ "listen": "closeKycVerificationModal"
11
+ },
12
+ "get-temporary-account-consents": {
13
+ "emit": "openTemporaryConsentsModal",
14
+ "listen": "closeTemporaryConsentsModal"
15
+ },
16
+ "limits-popup": {
17
+ "emit": "openLugasPopup",
18
+ "listen": "closeLugasPopup"
19
+ },
20
+ "video-verification-popup": {
21
+ "emit": "openKycVerificationModal",
22
+ "listen": "closeKycVerificationModal"
23
+ },
24
+ "unverified-phone-number": {
25
+ "emit": "openSmsVerificationModal",
26
+ "listen": "closeSmsVerificationModal"
27
+ },
28
+ "expired-consents": {
29
+ "emit": "openExpiredConsentsModal",
30
+ "listen": "closeExpiredConsentsModal"
31
+ },
32
+ "financial-limit-notification": {
33
+ "emit": "openLimitNotificationModal",
34
+ "listen": "closeLimitNotificationModal"
35
+ },
36
+ "account-review-notification": {
37
+ "emit": "openAccountReviewModal",
38
+ "listen": "closeAccountReviewModal"
39
+ },
40
+ "accept-tax-consent": {
41
+ "emit": "openStakeModal",
42
+ "listen": "closeStakeModal"
43
+ }
44
+ };
45
+
46
+ const userActionsCss = ":host{display:block}";
47
+ const UserActionsStyle0 = userActionsCss;
48
+
49
+ const UserActions = class {
50
+ constructor(hostRef) {
51
+ index.registerInstance(this, hostRef);
52
+ this.emitCurrentActionEvent = () => {
53
+ window.postMessage({ type: actionsMapping[this.actionStack[0].action].emit });
54
+ };
55
+ this.shiftActionStack = () => {
56
+ this.actionStack = this.actionStack.slice(1);
57
+ };
58
+ this.mbSource = undefined;
59
+ this.endpoint = undefined;
60
+ this.userid = undefined;
61
+ this.operatorid = undefined;
62
+ this.playercurrency = 'EUR';
63
+ this.hasfundsacknowledgment = 'false';
64
+ this.session = "";
65
+ this.language = 'en';
66
+ this.clientStyling = undefined;
67
+ this.clientStylingUrl = undefined;
68
+ this.translationUrl = '';
69
+ this.currencylocale = 'en';
70
+ this.currencydisplay = 'symbol';
71
+ this.maximumfractiondigits = '2';
72
+ this.minimumfractiondigits = '0';
73
+ this.customdisplayformat = '';
74
+ this.alwaysshowdecimals = 'true';
75
+ this.gmversion = 'gm16';
76
+ this.totalcalculationmode = 'totalCashAmount';
77
+ this.actionStack = [];
78
+ this.customerFundsProtectionActive = false;
79
+ }
80
+ handleEvent(e) {
81
+ var _a, _b;
82
+ if (((_b = actionsMapping[(_a = this.actionStack[0]) === null || _a === void 0 ? void 0 : _a.action]) === null || _b === void 0 ? void 0 : _b.listen) === e.data.type) {
83
+ this.shiftActionStack();
84
+ if (this.actionStack.length > 0)
85
+ this.emitCurrentActionEvent();
86
+ }
87
+ }
88
+ fetchUserActions() {
89
+ const url = new URL(`${this.endpoint}/v1/player/${this.userid}/legislation/actions`);
90
+ return fetch(url.href)
91
+ .then((response) => response.json())
92
+ .then(data => {
93
+ var _a, _b;
94
+ // filtering necessary to prevent widget from throwing "cannot read properties of 'undefined'"
95
+ // when the actions received are not part of actionsMapping
96
+ this.actionStack = data.actions.filter(action => actionsMapping[action.action]);
97
+ if (this.actionStack.length > 0)
98
+ this.emitCurrentActionEvent();
99
+ this.customerFundsProtectionActive = (_b = (_a = data.actions) === null || _a === void 0 ? void 0 : _a.some((item) => item.action === "customer-funds-protection")) !== null && _b !== void 0 ? _b : false;
100
+ // signal to prompt modal
101
+ if (this.customerFundsProtectionActive) {
102
+ window.postMessage({ type: 'ProtectionFundsActive' }, window.location.href);
103
+ }
104
+ });
105
+ }
106
+ componentWillLoad() {
107
+ return this.fetchUserActions();
108
+ }
109
+ render() {
110
+ if (!this.customerFundsProtectionActive || this.hasfundsacknowledgment === 'false') {
111
+ return;
112
+ }
113
+ else {
114
+ return (index.h("user-funds-acknowledgment", { endpoint: this.endpoint, session: this.session, operatorid: this.operatorid, language: this.language, "mb-source": this.mbSource, "client-styling": this.clientStyling, "client-styling-url": this.clientStylingUrl, "translation-url": this.translationUrl, currencylocale: this.currencylocale, currencydisplay: this.currencydisplay, maximumfractiondigits: this.maximumfractiondigits, minimumfractiondigits: this.minimumfractiondigits, playercurrency: this.playercurrency, userid: this.userid, gmversion: this.gmversion, totalcalculationmode: this.totalcalculationmode }));
115
+ }
116
+ }
117
+ };
118
+ UserActions.style = UserActionsStyle0;
119
+
120
+ const StyleCacheKey = '__WIDGET_GLOBAL_STYLE_CACHE__';
121
+
122
+ /**
123
+ * @name setClientStyling
124
+ * @description Method used to create and append to the passed element of the widget a style element with the content received
125
+ * @param {HTMLElement} stylingContainer The reference element of the widget
126
+ * @param {string} clientStyling The style content
127
+ */
128
+ function setClientStyling(stylingContainer, clientStyling) {
129
+ if (stylingContainer) {
130
+ const sheet = document.createElement('style');
131
+ sheet.innerHTML = clientStyling;
132
+ stylingContainer.appendChild(sheet);
133
+ }
134
+ }
135
+
136
+ /**
137
+ * @name setClientStylingURL
138
+ * @description Method used to create and append to the passed element of the widget a style element with the content fetched from a given URL
139
+ * @param {HTMLElement} stylingContainer The reference element of the widget
140
+ * @param {string} clientStylingUrl The URL of the style content
141
+ */
142
+ function setClientStylingURL(stylingContainer, clientStylingUrl) {
143
+ if (!stylingContainer || !clientStylingUrl) return;
144
+
145
+ const url = new URL(clientStylingUrl);
146
+
147
+ fetch(url.href)
148
+ .then((res) => res.text())
149
+ .then((data) => {
150
+ const cssFile = document.createElement('style');
151
+ cssFile.innerHTML = data;
152
+ if (stylingContainer) {
153
+ stylingContainer.appendChild(cssFile);
154
+ }
155
+ })
156
+ .catch((err) => {
157
+ console.error('There was an error while trying to load client styling from URL', err);
158
+ });
159
+ }
160
+
161
+ /**
162
+ * @name setStreamLibrary
163
+ * @description Method used to create and append to the passed element of the widget a style element with content fetched from the MessageBus
164
+ * @param {HTMLElement} stylingContainer The highest element of the widget
165
+ * @param {string} domain The domain from where the content should be fetched (e.g. 'Casino.Style', 'App.Style', 'casino-footer.style', etc.)
166
+ * @param {ref} subscription A reference to a variable where the subscription should be saved for unsubscribing when no longer needed
167
+ * @param {boolean} useAdoptedStyleSheets A flag to gradually enable testing of adoptedStyleSheets
168
+ */
169
+ function setStreamStyling(stylingContainer, domain, subscription, useAdoptedStyleSheets = false) {
170
+ if (!window.emMessageBus) return;
171
+
172
+ const supportAdoptStyle = 'adoptedStyleSheets' in Document.prototype;
173
+
174
+ if (!supportAdoptStyle || !useAdoptedStyleSheets) {
175
+ subscription = getStyleTagSubscription(stylingContainer, domain);
176
+
177
+ return subscription;
178
+ }
179
+
180
+ if (!window[StyleCacheKey]) {
181
+ window[StyleCacheKey] = {};
182
+ }
183
+ subscription = getAdoptStyleSubscription(stylingContainer, domain);
184
+
185
+ const originalUnsubscribe = subscription.unsubscribe.bind(subscription);
186
+ const wrappedUnsubscribe = () => {
187
+ if (window[StyleCacheKey][domain]) {
188
+ const cachedObject = window[StyleCacheKey][domain];
189
+ cachedObject.refCount > 1
190
+ ? (cachedObject.refCount = cachedObject.refCount - 1)
191
+ : delete window[StyleCacheKey][domain];
192
+ }
193
+
194
+ originalUnsubscribe();
195
+ };
196
+ subscription.unsubscribe = wrappedUnsubscribe;
197
+
198
+ return subscription;
199
+ }
200
+
201
+ function getStyleTagSubscription(stylingContainer, domain) {
202
+ const sheet = document.createElement('style');
203
+
204
+ return window.emMessageBus.subscribe(domain, (data) => {
205
+ if (stylingContainer) {
206
+ sheet.innerHTML = data;
207
+ stylingContainer.appendChild(sheet);
208
+ }
209
+ });
210
+ }
211
+
212
+ function getAdoptStyleSubscription(stylingContainer, domain) {
213
+ return window.emMessageBus.subscribe(domain, (data) => {
214
+ if (!stylingContainer) return;
215
+
216
+ const shadowRoot = stylingContainer.getRootNode();
217
+ const cacheStyleObject = window[StyleCacheKey];
218
+ let cachedStyle = cacheStyleObject[domain] && cacheStyleObject[domain].sheet;
219
+
220
+ if (!cachedStyle) {
221
+ cachedStyle = new CSSStyleSheet();
222
+ cachedStyle.replaceSync(data);
223
+ cacheStyleObject[domain] = {
224
+ sheet: cachedStyle,
225
+ refCount: 1
226
+ };
227
+ } else {
228
+ cacheStyleObject[domain].refCount = cacheStyleObject[domain].refCount + 1;
229
+ }
230
+
231
+ const currentSheets = shadowRoot.adoptedStyleSheets || [];
232
+ if (!currentSheets.includes(cachedStyle)) {
233
+ shadowRoot.adoptedStyleSheets = [...currentSheets, cachedStyle];
234
+ }
235
+ });
236
+ }
237
+
238
+ const DEFAULT_LANGUAGE = 'en';
239
+ const TRANSLATIONS = {
240
+ en: {
241
+ protectionTitle: 'Protection of Customer Funds',
242
+ protectionContent: '<p>We hold customer funds separate from company funds.<sup class="note"><a href="https://www.gamblingcommission.gov.uk/guidance/customer-funds-segregation-disclosure-to-customers-and-reporting/example-statements-that-might-be-used-in-terms-and-conditions-for-each-of#ref-2" target="_blank">12</a></sup> These funds ar <b>not protected</b> in the event of insolvency: <b>not protected segregation.</b></p><p>For more information, please see the <a href="https://www.gamblingcommission.gov.uk/guidance/customer-funds-segregation-disclosure-to-customers-and-reporting/the-customer-funds-insolvency-ratings-system" target="_blank">customer funds insolvency ratings system</a>.</p>',
243
+ balanceArea: 'Your current balance:',
244
+ protectionCheckboxLabel: 'I understand that my funds are <b>not protected</b> if the operator becomes insolvent.',
245
+ userNoticeText: 'Before you can proceed you must consent to the following',
246
+ submitButtonText: 'Confirm',
247
+ serverNotResponding: 'Server might not be responding',
248
+ apiCallError: 'Request failed with status '
249
+ },
250
+ 'en-us': {
251
+ protectionTitle: 'Terms and Conditions',
252
+ protectionContent: 'We hold customer funds separate from company funds.',
253
+ balanceArea: 'Email marketing',
254
+ privacyPolicyTitle: 'Privacy Policy',
255
+ userNoticeText: 'Before you can proceed you must consent to the following',
256
+ submitButtonText: 'Submit',
257
+ rejectButtonText: 'Reject',
258
+ rejectText: 'Rejecting new consents will result in the inability to continue the login process and you will be logged out.',
259
+ consentUpdateSuccess: 'Consent update successful!',
260
+ serverNotResponding: 'Server might not be responding'
261
+ },
262
+ ro: {
263
+ protectionTitle: 'Termeni și Condiții',
264
+ protectionContent: 'We hold customer funds separate from company funds.',
265
+ balanceArea: 'Marketing prin Email',
266
+ privacyPolicyTitle: 'Politica de Confidențialitate',
267
+ userNoticeText: 'Înainte de a continua, trebuie să vă dați consimțământul pentru următoarele',
268
+ submitButtonText: 'Trimite',
269
+ rejectButtonText: 'Respinge',
270
+ rejectText: 'Respingerea noilor consimțăminte va duce la imposibilitatea de a continua procesul de autentificare și veți fi deconectat.',
271
+ consentUpdateSuccess: 'Actualizare consimțământ reușită!',
272
+ serverNotResponding: 'Serverul s-ar putea să nu răspundă'
273
+ },
274
+ hr: {
275
+ protectionTitle: 'Opći uvjeti i odredbe',
276
+ protectionContent: 'We hold customer funds separate from company funds.',
277
+ balanceArea: 'E-mail makretinški sadržaj',
278
+ privacyPolicyTitle: 'Politika Privatnosti',
279
+ userNoticeText: 'Prije nego što možete nastaviti, morate pristati na sljedeće',
280
+ submitButtonText: 'Pošalji',
281
+ rejectButtonText: 'Odbij',
282
+ rejectText: 'Odbijanje novih pristanka rezultirat će nemogućnošću nastavka procesa prijave i bit ćete odjavljeni.',
283
+ consentUpdateSuccess: 'Ažuriranje pristanka uspješno!',
284
+ serverNotResponding: 'Poslužitelj možda ne odgovara'
285
+ },
286
+ fr: {
287
+ protectionTitle: 'Termes et Conditions',
288
+ protectionContent: 'We hold customer funds separate from company funds.',
289
+ balanceArea: 'Marketing par Email',
290
+ privacyPolicyTitle: 'Politique de Confidentialité',
291
+ userNoticeText: 'Avant de continuer, vous devez consentir aux éléments suivants',
292
+ submitButtonText: 'Soumettre',
293
+ rejectButtonText: 'Rejeter',
294
+ rejectText: 'Le rejet des nouveaux consentements entraînera l\'impossibilité de continuer le processus de connexion et vous serez déconnecté.',
295
+ consentUpdateSuccess: 'Mise à jour du consentement réussie!',
296
+ serverNotResponding: 'Le serveur ne répond peut-être pas'
297
+ },
298
+ cs: {
299
+ protectionTitle: 'Podmínky a ujednání',
300
+ protectionContent: 'We hold customer funds separate from company funds.',
301
+ balanceArea: 'Emailový marketing',
302
+ privacyPolicyTitle: 'Zásady ochrany osobních údajů',
303
+ userNoticeText: 'Než budete moci pokračovat, musíte souhlasit s následujícím',
304
+ submitButtonText: 'Odeslat',
305
+ rejectButtonText: 'Odmítnout',
306
+ rejectText: 'Odmítnutí nových souhlasů povede k nemožnosti pokračovat v procesu přihlášení a budete odhlášeni.',
307
+ consentUpdateSuccess: 'Aktualizace souhlasu proběhla úspěšně!',
308
+ serverNotResponding: 'Server možná neodpovídá'
309
+ },
310
+ de: {
311
+ protectionTitle: 'Allgemeine Geschäftsbedingungen',
312
+ protectionContent: 'We hold customer funds separate from company funds.',
313
+ balanceArea: 'E-Mail-Marketing',
314
+ privacyPolicyTitle: 'Datenschutzrichtlinie',
315
+ userNoticeText: 'Bevor Sie fortfahren können, müssen Sie den folgenden Punkten zustimmen',
316
+ submitButtonText: 'Absenden',
317
+ rejectButtonText: 'Ablehnen',
318
+ rejectText: 'Das Ablehnen neuer Zustimmungen führt dazu, dass der Anmeldevorgang nicht fortgesetzt werden kann und Sie abgemeldet werden.',
319
+ consentUpdateSuccess: 'Zustimmung erfolgreich aktualisiert!',
320
+ serverNotResponding: 'Der Server antwortet möglicherweise nicht'
321
+ },
322
+ es: {
323
+ protectionTitle: 'Términos y Condiciones',
324
+ protectionContent: 'We hold customer funds separate from company funds.',
325
+ balanceArea: 'Marketing por Email',
326
+ privacyPolicyTitle: 'Política de Privacidad',
327
+ userNoticeText: 'Antes de continuar, debe dar su consentimiento a lo siguiente',
328
+ submitButtonText: 'Enviar',
329
+ rejectButtonText: 'Rechazar',
330
+ rejectText: 'Rechazar nuevos consentimientos resultará en la imposibilidad de continuar el proceso de inicio de sesión y se cerrará la sesión.',
331
+ consentUpdateSuccess: '¡Actualización del consentimiento exitosa!',
332
+ serverNotResponding: 'El servidor podría no estar respondiendo'
333
+ },
334
+ pt: {
335
+ protectionTitle: 'Termos e Condições',
336
+ protectionContent: 'We hold customer funds separate from company funds.',
337
+ balanceArea: 'Marketing por Email',
338
+ privacyPolicyTitle: 'Política de Privacidade',
339
+ userNoticeText: 'Antes de continuar, você deve consentir com o seguinte',
340
+ submitButtonText: 'Enviar',
341
+ rejectButtonText: 'Rejeitar',
342
+ rejectText: 'Rejeitar novos consentimentos resultará na impossibilidade de continuar o processo de login e você será desconectado.',
343
+ consentUpdateSuccess: 'Atualização de consentimento bem-sucedida!',
344
+ serverNotResponding: 'O servidor pode não estar respondendo'
345
+ },
346
+ 'es-mx': {
347
+ protectionTitle: 'Términos y Condiciones',
348
+ protectionContent: 'We hold customer funds separate from company funds.',
349
+ balanceArea: 'Marketing por Email',
350
+ privacyPolicyTitle: 'Política de Privacidad',
351
+ userNoticeText: 'Antes de continuar, debe dar su consentimiento a lo siguiente',
352
+ submitButtonText: 'Enviar',
353
+ rejectButtonText: 'Rechazar',
354
+ rejectText: 'Rechazar nuevos consentimientos resultará en la imposibilidad de continuar el proceso de inicio de sesión y se cerrará la sesión.',
355
+ consentUpdateSuccess: '¡Actualización del consentimiento exitosa!',
356
+ serverNotResponding: 'El servidor podría no estar respondiendo'
357
+ },
358
+ 'pt-br': {
359
+ protectionTitle: 'Termos e Condições',
360
+ protectionContent: 'We hold customer funds separate from company funds.',
361
+ balanceArea: 'Marketing por Email',
362
+ privacyPolicyTitle: 'Política de Privacidade',
363
+ userNoticeText: 'Antes de continuar, você deve consentir com o seguinte',
364
+ submitButtonText: 'Enviar',
365
+ rejectButtonText: 'Rejeitar',
366
+ rejectText: 'Rejeitar novos consentimentos resultará na impossibilidade de continuar o processo de login e você será desconectado.',
367
+ consentUpdateSuccess: 'Atualização de consentimento bem-sucedida!',
368
+ serverNotResponding: 'O servidor pode não estar respondendo'
369
+ }
370
+ };
371
+ const getTranslations = (url) => {
372
+ return new Promise((resolve) => {
373
+ fetch(url)
374
+ .then((res) => res.json())
375
+ .then((data) => {
376
+ Object.keys(data).forEach((lang) => {
377
+ if (!TRANSLATIONS[lang]) {
378
+ TRANSLATIONS[lang] = {};
379
+ }
380
+ for (let key in data[lang]) {
381
+ TRANSLATIONS[lang][key] = data[lang][key];
382
+ }
383
+ });
384
+ resolve(true);
385
+ });
386
+ });
387
+ };
388
+ const translate = (key, customLang, values) => {
389
+ const lang = customLang;
390
+ let translation = TRANSLATIONS[(lang !== undefined) && (lang in TRANSLATIONS) ? lang : DEFAULT_LANGUAGE][key];
391
+ if (values !== undefined) {
392
+ for (const [key, value] of Object.entries(values.values)) {
393
+ const regex = new RegExp(`{${key}}`, 'g');
394
+ translation = translation.replace(regex, value);
395
+ }
396
+ }
397
+ return translation;
398
+ };
399
+
400
+ const userFundsAcknowledgmentCss = ":host{display:block}.QueryReferenceContainer{height:100%;width:100%}.UserProtectionFundsTitle{font-size:1.2rem;font-weight:200;text-align:center}.CloseButton{width:25px;height:25px;align-self:flex-end}.UserProtectionFundsContainer{font-family:inherit;font-weight:100;height:100%;padding:1rem 1.5rem;background-color:var(--emw--color-white, #FFFFFF);display:flex;flex-direction:column;justify-content:space-between;border-radius:var(--emw--border-radius-large, 20px);max-width:fit-content}.ConsentSubmitButton{font-size:1rem;font-family:var(--emw--button-typography);padding:0.4rem 1.4rem;background:var(--emw--button-background-color, #FFFFFF);border:var(--emw--button-border, 2px solid #000000);color:var(--emw--button-text-color, #000000);border-radius:var(--emw--button-border-radius, 10px);align-self:center;cursor:pointer}.ConsentSubmitButton:disabled{border:2px solid var(--emw--color-disabled, #58586B);color:var(--emw--color-disabled, #58586B);cursor:unset}.ButtonsWrapper{display:flex;flex-direction:column;margin-top:10px;gap:40px}.ButtonsWrapper button:only-child{margin-left:auto}.UserProtectionError{color:var(--emw--color-error, #FD2839);text-align:center;height:22px}@media screen and (max-width: 320px){.QueryReferenceContainer{font-size:0.8rem;color:var(--emw--button-text-color, #000000)}}.spinner{animation:rotate 2s linear infinite;z-index:2;position:absolute;top:50%;left:50%;margin:-25px 0 0 -25px;width:50px;height:50px}.spinner .path{stroke:var(--emw--login-color-primary, var(--emw--color-primary, #22B04E));stroke-linecap:round;animation:dash 1.5s ease-in-out infinite}@keyframes rotate{100%{transform:rotate(360deg)}}@keyframes dash{0%{stroke-dasharray:1, 150;stroke-dashoffset:0}50%{stroke-dasharray:90, 150;stroke-dashoffset:-35}100%{stroke-dasharray:90, 150;stroke-dashoffset:-124}}";
401
+ const UserFundsAcknowledgmentStyle0 = userFundsAcknowledgmentCss;
402
+
403
+ const UserFundsAcknowledgment = class {
404
+ constructor(hostRef) {
405
+ index.registerInstance(this, hostRef);
406
+ this.abortController = null;
407
+ this.TIMEOUT_MS = 10000; // 10 seconds
408
+ this.currency = '';
409
+ this.getPlayerAccountBalanceDataCore = () => {
410
+ const url = new URL(`${this.endpoint}/v1/player/${this.userid}/account`);
411
+ fetch(url, { method: 'GET', headers: { 'X-SessionID': this.session } })
412
+ .then(res => {
413
+ if (!res.ok) {
414
+ this.errorAmountMessage = `${translate('apiCallError', this.language)} ${res.status}`;
415
+ throw new Error(`Failed to fetch balance data: ${res.status}`);
416
+ }
417
+ return res.json();
418
+ })
419
+ .then((data) => {
420
+ var _a, _b;
421
+ if (!data.items || !Array.isArray(data.items)) {
422
+ this.errorAmountMessage = `${translate('apiCallError', this.language)} ${data.status}`;
423
+ throw new Error('Unexpected data format: items array missing');
424
+ }
425
+ const casinoItem = data.items.find(b => b.displayName === 'Casino');
426
+ if (!(casinoItem === null || casinoItem === void 0 ? void 0 : casinoItem.currency)) {
427
+ throw new Error('No Casino item found in balance items');
428
+ }
429
+ this.currency = casinoItem.currency;
430
+ this.totalCashAmount = (_b = (_a = data.totalAmount) === null || _a === void 0 ? void 0 : _a[this.currency]) !== null && _b !== void 0 ? _b : 0;
431
+ })
432
+ .catch(err => {
433
+ if (!this.errorAmountMessage) {
434
+ this.errorAmountMessage = translate('serverNotResponding', this.language);
435
+ }
436
+ console.error('Balance error:', err);
437
+ })
438
+ .finally(() => {
439
+ this.isLoading = false;
440
+ });
441
+ };
442
+ this.getPlayerAccountBalanceData16 = () => {
443
+ const url = new URL(`${this.endpoint}/v2/player/${this.userid}/balance`);
444
+ const headers = new Headers();
445
+ headers.append('X-SessionID', this.session);
446
+ fetch(url, { method: 'GET', headers })
447
+ .then(res => {
448
+ if (!res.ok) {
449
+ this.errorAmountMessage = `${translate('apiCallError', this.language)} ${res.status}`;
450
+ throw new Error(`Failed to fetch balance data: ${res.status}`);
451
+ }
452
+ return res.json();
453
+ })
454
+ .then((data) => {
455
+ var _a, _b;
456
+ if (!data.items || !Array.isArray(data.items)) {
457
+ this.errorAmountMessage = `${translate('apiCallError', this.language)} ${data.status}`;
458
+ throw new Error('Unexpected data format: items array missing');
459
+ }
460
+ const firstRealOrdinary = data.items.find(item => item.type === 'Real' && item.walletAccountType === 'Ordinary');
461
+ if (!(firstRealOrdinary === null || firstRealOrdinary === void 0 ? void 0 : firstRealOrdinary.currency)) {
462
+ throw new Error('No Real/Ordinary wallet found in balance items');
463
+ }
464
+ this.currency = firstRealOrdinary.currency;
465
+ // Resolve calculation mode fallback
466
+ if (!data[this.totalcalculationmode]) {
467
+ this.totalcalculationmode = 'totalCashAmount';
468
+ }
469
+ this.totalCashAmount = (_b = (_a = data[this.totalcalculationmode]) === null || _a === void 0 ? void 0 : _a[this.currency]) !== null && _b !== void 0 ? _b : 0;
470
+ })
471
+ .catch(err => {
472
+ if (!this.errorAmountMessage) {
473
+ this.errorAmountMessage = translate('serverNotResponding', this.language);
474
+ }
475
+ console.error('Balance fetch error', err);
476
+ })
477
+ .finally(() => {
478
+ this.isLoading = false;
479
+ });
480
+ };
481
+ this.userLegislationConsentHandler = () => {
482
+ if (this.checkboxInput) {
483
+ this.isConsentChecked = this.checkboxInput.checked;
484
+ }
485
+ // In case of a network error, permit the user to retry consents submission
486
+ if (this.isConsentChecked) {
487
+ this.consentsSubmitted = false;
488
+ this.errorMessage = '';
489
+ }
490
+ };
491
+ /**
492
+ * Returns the formatted balance
493
+ * @param balance -> the balance value to be formatted
494
+ */
495
+ this.formatBalance = (balance) => {
496
+ if (this.customdisplayformat) {
497
+ return this.formatFromCustom(balance);
498
+ }
499
+ const locales = this.currencylocale || this.language;
500
+ const formatOptions = {
501
+ style: 'currency',
502
+ currency: this.playercurrency,
503
+ useGrouping: true,
504
+ currencyDisplay: this.currencydisplay,
505
+ maximumFractionDigits: this.maximumfractiondigits === '' || isNaN(Number(this.maximumfractiondigits)) ? 2 : Number(this.maximumfractiondigits),
506
+ minimumFractionDigits: this.minimumfractiondigits === '' || isNaN(Number(this.minimumfractiondigits)) ? 0 : Number(this.minimumfractiondigits),
507
+ };
508
+ return new Intl.NumberFormat(locales, formatOptions).format(balance);
509
+ };
510
+ /**
511
+ * Parses customdisplayformat and returns the formatted balance accordingly.
512
+ * Example:
513
+ * if customdisplayformat = '{amount|,.1} {currency}', currency = 'EUR'
514
+ * then formatFromCustom(123123.199) = '123,123.1 EUR'
515
+ * @param balance
516
+ */
517
+ this.formatFromCustom = (balance) => {
518
+ const tokens = [];
519
+ let acc = '';
520
+ for (const char of this.customdisplayformat) {
521
+ switch (char) {
522
+ default:
523
+ acc += char;
524
+ break;
525
+ case '{':
526
+ acc && tokens.push(acc);
527
+ acc = '';
528
+ break;
529
+ case '}':
530
+ const [variable, args] = acc.split('|');
531
+ acc = '';
532
+ if (variable.toLowerCase() === 'currency') {
533
+ acc = this.playercurrency;
534
+ }
535
+ else if (variable.toLowerCase() === 'amount') {
536
+ // defaults
537
+ let sepThousands = ',';
538
+ let sepDecimal = '.';
539
+ let decimals = 2;
540
+ // override defaults if args provided (' ' is considered empty so default is used)
541
+ if (args) {
542
+ args[0] !== ' ' && (sepThousands = args[0]);
543
+ args[1] !== ' ' && (sepDecimal = args[1]);
544
+ args[2] !== ' ' && !isNaN(Number(args[2])) && (decimals = Number(args[2]));
545
+ }
546
+ // separate int and fractional part, also round down to desired precision
547
+ let [integerPart, fractionalPart] = String(Math.floor(balance * 10 ** decimals) / 10 ** decimals).split('.');
548
+ // format int part by adding thousands separators
549
+ acc += integerPart[0];
550
+ for (let i = 1; i < integerPart.length; ++i) {
551
+ if ((integerPart.length - i) % 3 === 0) {
552
+ acc += (sepThousands + integerPart[i]);
553
+ }
554
+ else {
555
+ acc += integerPart[i];
556
+ }
557
+ }
558
+ let finalFraction = fractionalPart || '';
559
+ const forceDecimals = this.alwaysshowdecimals === 'true';
560
+ const maxFractionDigits = Number(this.maximumfractiondigits);
561
+ if (forceDecimals) {
562
+ // Always show exactly maxFractionDigits decimals
563
+ finalFraction = finalFraction.padEnd(maxFractionDigits || 2, '0').substring(0, maxFractionDigits);
564
+ }
565
+ else {
566
+ // Default behavior: only show decimals if they exist
567
+ if (!finalFraction) {
568
+ break;
569
+ }
570
+ }
571
+ // Append decimals
572
+ acc += sepDecimal + finalFraction;
573
+ }
574
+ acc && tokens.push(acc);
575
+ acc = '';
576
+ break;
577
+ }
578
+ }
579
+ tokens.push(acc);
580
+ return tokens.join('');
581
+ };
582
+ this.handleSubmit = () => {
583
+ var _a;
584
+ this.consentsSubmitted = true;
585
+ // Abort any in-flight request before starting a new one
586
+ (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.abort();
587
+ this.abortController = new AbortController();
588
+ const timeoutId = setTimeout(() => {
589
+ var _a;
590
+ (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.abort('Request timed out');
591
+ }, this.TIMEOUT_MS);
592
+ const url = `${this.endpoint}/api/pam/v1/actions/customer-funds-protection`;
593
+ const headers = new Headers();
594
+ headers.append('Content-Type', 'application/json');
595
+ headers.append('X-SessionId', `${this.session}`);
596
+ headers.append('X-Tenant-ID', `${this.operatorid}`);
597
+ return fetch(url, {
598
+ method: 'POST',
599
+ headers: headers,
600
+ body: JSON.stringify({ accepted: true }),
601
+ signal: this.abortController.signal,
602
+ })
603
+ .then((res) => {
604
+ if (!res.ok) {
605
+ this.errorMessage = `${translate('apiCallError', this.language)} ${res.status}`;
606
+ throw new Error(`Request failed with status ${res.status}`);
607
+ }
608
+ // 202 Accepted has no body, skip res.json()
609
+ if (res.status === 202) {
610
+ this.errorMessage = '';
611
+ window.postMessage({ type: 'ProtectionFundsAccepted' }, window.location.href);
612
+ return;
613
+ }
614
+ return res.json();
615
+ })
616
+ .catch((error) => {
617
+ var _a;
618
+ if (error.name === 'AbortError') {
619
+ // Validate between timeout/unmount
620
+ const reason = (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.signal.reason;
621
+ if (reason === 'Request timed out') {
622
+ this.errorMessage = translate('serverNotResponding', this.language);
623
+ }
624
+ return;
625
+ }
626
+ console.error('Customer funds protection request failed:', error.message);
627
+ this.errorMessage = translate('serverNotResponding', this.language);
628
+ })
629
+ .finally(() => {
630
+ clearTimeout(timeoutId); // clear timer
631
+ this.abortController = null;
632
+ });
633
+ };
634
+ this.session = undefined;
635
+ this.operatorid = undefined;
636
+ this.mbSource = undefined;
637
+ this.clientStyling = undefined;
638
+ this.clientStylingUrl = undefined;
639
+ this.endpoint = undefined;
640
+ this.language = 'en';
641
+ this.translationUrl = '';
642
+ this.currencylocale = 'en';
643
+ this.currencydisplay = 'symbol';
644
+ this.maximumfractiondigits = '2';
645
+ this.minimumfractiondigits = '0';
646
+ this.customdisplayformat = '';
647
+ this.alwaysshowdecimals = 'true';
648
+ this.playercurrency = 'EUR';
649
+ this.userid = '';
650
+ this.gmversion = 'gm16';
651
+ this.totalcalculationmode = 'totalCashAmount';
652
+ this.isLoading = true;
653
+ this.isConsentChecked = false;
654
+ this.totalCashAmount = 0;
655
+ this.errorMessage = '';
656
+ this.errorAmountMessage = '';
657
+ this.consentsSubmitted = false;
658
+ }
659
+ handleNewTranslations() {
660
+ getTranslations(this.translationUrl);
661
+ }
662
+ handleClientStylingChange(newValue, oldValue) {
663
+ if (newValue != oldValue) {
664
+ setClientStyling(this.stylingContainer, this.clientStyling);
665
+ }
666
+ }
667
+ handleClientStylingUrlChange(newValue, oldValue) {
668
+ if (newValue != oldValue) {
669
+ setClientStylingURL(this.stylingContainer, this.clientStylingUrl);
670
+ }
671
+ }
672
+ handleMbSourceChange(newValue, oldValue) {
673
+ if (newValue != oldValue) {
674
+ setStreamStyling(this.stylingContainer, `${this.mbSource}.Style`, this.stylingSubscription);
675
+ }
676
+ }
677
+ async componentWillLoad() {
678
+ if (this.translationUrl.length > 2) {
679
+ await getTranslations(this.translationUrl);
680
+ }
681
+ }
682
+ componentDidLoad() {
683
+ if (this.stylingContainer) {
684
+ if (this.mbSource)
685
+ setStreamStyling(this.stylingContainer, `${this.mbSource}.Style`, this.stylingSubscription);
686
+ if (this.clientStyling)
687
+ setClientStyling(this.stylingContainer, this.clientStyling);
688
+ if (this.clientStylingUrl)
689
+ setClientStylingURL(this.stylingContainer, this.clientStylingUrl);
690
+ }
691
+ switch (this.gmversion) {
692
+ case 'gmcore':
693
+ this.getPlayerAccountBalanceDataCore();
694
+ break;
695
+ case 'gm16':
696
+ this.getPlayerAccountBalanceData16();
697
+ break;
698
+ default:
699
+ throw Error(`Invalid apiversion attribute: ${this.gmversion}`);
700
+ }
701
+ }
702
+ disconnectedCallback() {
703
+ var _a;
704
+ (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.abort('Component unmounted');
705
+ this.stylingSubscription && this.stylingSubscription.unsubscribe();
706
+ }
707
+ consentsArea() {
708
+ return (index.h("div", { class: "UserProtectionFundsCheckboxArea" }, index.h("label", { class: "UserProtectionFundsCheckboxContent", htmlFor: "userProtectionFunds" }, index.h("input", { ref: el => this.checkboxInput = el, id: "userProtectionFunds", type: "checkbox", onInput: this.userLegislationConsentHandler }), index.h("span", { innerHTML: `${translate('protectionCheckboxLabel', this.language)}` }))));
709
+ }
710
+ render() {
711
+ return (index.h("div", { key: '9b36bd1e33e989aaa037919387f3da77020b6e9e', class: "QueryReferenceContainer", ref: el => this.stylingContainer = el }, this.isLoading ? (index.h("div", null, index.h("slot", { name: 'spinner' }), index.h("svg", { class: "spinner", viewBox: "0 0 50 50" }, index.h("circle", { class: "path", cx: "25", cy: "25", r: "20", fill: "none", "stroke-width": "5" })))) : (index.h("div", { class: "UserProtectionFundsContainer" }, index.h("div", { class: "UserProtectionFundsTitle" }, index.h("h2", null, translate('protectionTitle', this.language))), index.h("div", { class: "UserProtectionFundsWrapper" }, index.h("p", { innerHTML: `${translate('protectionContent', this.language)}` })), index.h("div", { class: "UserProtectionFundsBalance" }, index.h("p", null, translate('balanceArea', this.language), " ", index.h("span", null, this.formatBalance(this.totalCashAmount)))), index.h("div", { class: "UserProtectionFundsButtonsWrapper" }, index.h("div", null, this.consentsArea()), index.h("div", null, index.h("button", { class: "UserProtectionFundsSubmitButton", disabled: !this.isConsentChecked || this.errorMessage != '' || this.consentsSubmitted, onClick: this.handleSubmit }, translate('submitButtonText', this.language))), index.h("div", { class: "UserProtectionError" }, index.h("p", null, this.errorMessage != '' && this.errorMessage), index.h("p", null, this.errorAmountMessage != '' && this.errorAmountMessage)))))));
712
+ }
713
+ static get watchers() { return {
714
+ "translationUrl": ["handleNewTranslations"],
715
+ "clientStyling": ["handleClientStylingChange"],
716
+ "clientStylingUrl": ["handleClientStylingUrlChange"],
717
+ "mbSource": ["handleMbSourceChange"]
718
+ }; }
719
+ };
720
+ UserFundsAcknowledgment.style = UserFundsAcknowledgmentStyle0;
721
+
722
+ exports.user_actions = UserActions;
723
+ exports.user_funds_acknowledgment = UserFundsAcknowledgment;