@klauswuestefeld/jux-web 2026.3.7 → 2026.7.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/jux-web.js CHANGED
@@ -36,6 +36,61 @@ var juxWeb = (function (exports) {
36
36
  };
37
37
  // @ts-ignore
38
38
  const getTranslation = (k) => translations[k] || k;
39
+ const formatTextToMarkdown = (container, text) => {
40
+ // Split the text on bold markers (**...**), <br>, markdown-style links [text](url),
41
+ // and semantic color tags like {warning|text}.
42
+ // The regex uses a capture group to include the delimiters in the results.
43
+ const parts = text.split(/(\*\*.*?\*\*|<br>|\[.*?\]\(https?:\/\/.*?\)|\{[a-z0-9-]+\|.*?\})/gi);
44
+ parts.forEach(part => {
45
+ if (!part)
46
+ return;
47
+ if (part === '<br>') {
48
+ container.appendChild(document.createElement('br'));
49
+ return;
50
+ }
51
+ if (part.startsWith('**') && part.endsWith('**')) {
52
+ const bold = document.createElement('b');
53
+ bold.textContent = part.slice(2, -2);
54
+ container.appendChild(bold);
55
+ return;
56
+ }
57
+ if (part.startsWith('[') && part.includes('](') && part.endsWith(')')) {
58
+ const match = part.match(/\[(.*?)\]\((https?:\/\/.*?)\)/);
59
+ if (match) {
60
+ const [, label, href] = match;
61
+ const link = document.createElement('a');
62
+ link.href = href;
63
+ link.textContent = label;
64
+ link.target = '_blank';
65
+ link.rel = 'noopener noreferrer';
66
+ container.appendChild(link);
67
+ return;
68
+ }
69
+ }
70
+ if (part.startsWith('{') && part.includes('|') && part.endsWith('}')) {
71
+ const match = part.match(/\{([a-z0-9-]+)\|(.*?)\}/i);
72
+ if (match) {
73
+ const [, className, text] = match;
74
+ const coloredText = document.createElement('span');
75
+ coloredText.className = `${className.toLowerCase()}`;
76
+ formatTextToMarkdown(coloredText, text);
77
+ container.appendChild(coloredText);
78
+ return;
79
+ }
80
+ }
81
+ container.appendChild(document.createTextNode(part));
82
+ });
83
+ };
84
+ const createFormattedParagraph = (text) => {
85
+ const paragraph = document.createElement('p');
86
+ formatTextToMarkdown(paragraph, text);
87
+ return paragraph;
88
+ };
89
+ const applyFormattedTranslation = (element, key) => {
90
+ const wrapper = document.createElement('span');
91
+ formatTextToMarkdown(wrapper, getTranslation(key));
92
+ element.replaceChildren(...Array.from(wrapper.childNodes));
93
+ };
39
94
  const initLanguage = (translations_, defaultLanguage = 'en') => {
40
95
  exports.chosenLanguage = getLocalStorageItem(OVERRIDE_KEY) || getBrowserLanguage();
41
96
  translations = translations_[exports.chosenLanguage] || translations_[defaultLanguage];
@@ -172,234 +227,6 @@ var juxWeb = (function (exports) {
172
227
  return result;
173
228
  };
174
229
 
175
- const juxWhite = '#FFF';
176
- const juxDarkGrey = '#282828';
177
- const juxDimmedDarkGrey = '#28282822';
178
- const juxDimmedBlack = '#00000088';
179
- const juxLightGreen = '#20B03D';
180
- const juxMediumGreen = '#0D631E';
181
-
182
- const onKeyDown = (ev, el, persitent, onClose) => {
183
- if (ev.key === 'Escape') {
184
- if (persitent) {
185
- return;
186
- }
187
- close(el, onClose);
188
- }
189
- else if (ev.key === 'Enter') {
190
- if (ev.target.id === 'members-textarea') {
191
- return;
192
- }
193
- el.dispatchEvent(new CustomEvent('ok'));
194
- }
195
- };
196
- const close = (el, onClose) => {
197
- el.remove();
198
- if (onClose) {
199
- onClose();
200
- }
201
- };
202
- const attachEventHandlers = (el, persitent, onClose) => {
203
- const cancelButton = el.querySelector('.cancel');
204
- cancelButton === null || cancelButton === void 0 ? void 0 : cancelButton.addEventListener('click', (_e) => {
205
- el.dispatchEvent(new CustomEvent('cancel'));
206
- if (el.hasAttribute('dont-remove')) {
207
- return;
208
- }
209
- close(el, onClose);
210
- });
211
- const closeButton = el.querySelector('.close');
212
- if (closeButton) {
213
- closeButton.addEventListener('click', (_e) => {
214
- close(el, onClose);
215
- });
216
- }
217
- const okButton = el.querySelector('.ok');
218
- okButton === null || okButton === void 0 ? void 0 : okButton.addEventListener('click', (_e) => {
219
- el.dispatchEvent(new CustomEvent('ok'));
220
- });
221
- el.removeEventListener('keydown', (ev) => onKeyDown(ev, el, persitent, () => onClose));
222
- el.addEventListener('keydown', (ev) => onKeyDown(ev, el, persitent, () => onClose));
223
- el === null || el === void 0 ? void 0 : el.addEventListener('click', (e) => {
224
- if (e.target === el) {
225
- if (persitent) {
226
- return;
227
- }
228
- close(el, onClose);
229
- }
230
- });
231
- };
232
- // TODO: Mudar construtor para usar essa interface
233
- // interface JuxModalOptions {
234
- // title?: string,
235
- // btns?: string[],
236
- // content?: string,
237
- // isPersistent?: boolean,
238
- // isBusy?: boolean,
239
- // body?: HTMLElement,
240
- // okTxt?: string,
241
- // cancelTxt?: string,
242
- // onClose?: any,
243
- // titleStyle?: string,
244
- // }
245
- const applyWrapperStyle = (wrapper) => {
246
- wrapper.style.alignItems = 'center';
247
- wrapper.style.backgroundColor = juxWhite;
248
- wrapper.style.borderRadius = '.3125rem';
249
- wrapper.style.boxShadow = `0 0 1.33rem ${juxDimmedBlack}`;
250
- wrapper.style.boxSizing = 'content-box';
251
- wrapper.style.display = 'flex';
252
- wrapper.style.flexDirection = 'column';
253
- wrapper.style.justifyContent = 'flex-start';
254
- wrapper.style.margin = '0';
255
- wrapper.style.maxHeight = '88%';
256
- wrapper.style.maxWidth = '40%';
257
- wrapper.style.minWidth = '20%';
258
- wrapper.style.padding = '2.8rem';
259
- wrapper.style.position = 'relative';
260
- };
261
- const applyModalStyle = (modal) => {
262
- modal.style.alignItems = 'center';
263
- modal.style.backgroundColor = juxDimmedBlack;
264
- modal.style.display = 'flex';
265
- modal.style.height = '100vh';
266
- modal.style.justifyContent = 'center';
267
- modal.style.left = '0';
268
- modal.style.overflow = 'hidden';
269
- modal.style.position = 'fixed';
270
- modal.style.top = '0';
271
- modal.style.width = '100%';
272
- modal.style.zIndex = '9999';
273
- };
274
- const applyHeaderStyle = (header) => {
275
- header.style.alignItems = 'center';
276
- header.style.color = juxMediumGreen;
277
- header.style.flexDirection = 'row';
278
- header.style.fontSize = '16px';
279
- header.style.fontWeight = '700';
280
- header.style.justifyContent = 'space-between';
281
- header.style.margin = '0';
282
- header.style.display = 'flex';
283
- header.style.position = 'relative';
284
- header.style.width = '100%';
285
- };
286
- const applyJuxModalContentStyle = (el) => {
287
- el.style.display = 'flex';
288
- el.style.position = 'relative';
289
- el.style.width = '100%';
290
- el.style.flexFlow = 'column';
291
- el.style.marginTop = '1.33rem';
292
- el.style.fontSize = '14px';
293
- el.style.rowGap = '8px';
294
- };
295
- const applyFooterStyle = (footer) => {
296
- footer.style.display = 'flex';
297
- footer.style.width = '100%';
298
- footer.style.justifyContent = 'flex-end';
299
- footer.style.alignItems = 'center';
300
- footer.style.gap = '0.75rem';
301
- footer.style.marginTop = '1.5rem';
302
- footer.style.paddingTop = '1rem';
303
- };
304
- const applyModalButtonStyle = (btn) => {
305
- btn.style.display = 'inline-flex';
306
- btn.style.alignItems = 'center';
307
- btn.style.justifyContent = 'center';
308
- btn.style.height = '36px';
309
- btn.style.minWidth = '88px';
310
- btn.style.padding = '0 16px';
311
- btn.style.background = 'rgb(255, 255, 255)';
312
- btn.style.border = '1px solid rgb(160, 160, 160)';
313
- btn.style.borderRadius = '4px';
314
- btn.style.boxShadow = '0 1px 2px rgba(0,0,0,0.15)';
315
- btn.style.color = 'rgb(68, 68, 68)';
316
- btn.style.fontSize = '14px';
317
- btn.style.fontWeight = '500';
318
- btn.style.whiteSpace = 'nowrap';
319
- btn.style.cursor = 'pointer';
320
- };
321
- const juxModal = (title, btns = ['ok', 'cancel'], content = '', persistent = false, busy = false, body, okTxt = getTranslation('alert-ok-btn-txt'), cancelTxt = getTranslation('alert-cancel-btn-txt'), onClose, titleStyle) => {
322
- const result = document.createElement('jux-modal');
323
- applyModalStyle(result);
324
- const wrapper = document.createElement('modal-wrapper');
325
- applyWrapperStyle(wrapper);
326
- // START close
327
- const closeBtn = document.createElement('button');
328
- if (!persistent) {
329
- closeBtn.className = 'close';
330
- closeBtn.setAttribute('aria-label', getTranslation('close'));
331
- }
332
- // END close
333
- // START header
334
- const header = document.createElement('modal-header');
335
- applyHeaderStyle(header);
336
- if (title) {
337
- if (titleStyle) {
338
- header.classList.add(titleStyle);
339
- }
340
- const h3 = document.createElement('h3');
341
- h3.textContent = title;
342
- h3.style.margin = '0';
343
- header.appendChild(h3);
344
- }
345
- // END header
346
- // START content
347
- const modalContent = document.createElement('jux-modal-content');
348
- applyJuxModalContentStyle(modalContent);
349
- if (content) {
350
- modalContent.innerHTML = content;
351
- }
352
- else if (body) {
353
- modalContent.appendChild(body);
354
- }
355
- // END content
356
- // START footer
357
- const footer = document.createElement('modal-footer');
358
- applyFooterStyle(footer);
359
- if (btns.includes('cancel')) {
360
- const cancelBtn = document.createElement('button');
361
- cancelBtn.classList.add('secondary-button', 'modal-button', 'action', 'cancel-btn', 'cancel');
362
- applyModalButtonStyle(cancelBtn);
363
- const cancelText = document.createElement('button-text');
364
- cancelText.textContent = cancelTxt;
365
- const cancelSpinner = document.createElement('loading-spinner');
366
- if (!busy)
367
- cancelSpinner.style.display = 'none';
368
- cancelBtn.append(cancelText, cancelSpinner);
369
- footer.appendChild(cancelBtn);
370
- }
371
- if (btns.includes('ok')) {
372
- const okBtn = document.createElement('button');
373
- okBtn.classList.add('primary-button', 'modal-button', 'action', 'ok');
374
- applyModalButtonStyle(okBtn);
375
- const okText = document.createElement('button-text');
376
- okText.textContent = okTxt;
377
- const okSpinner = document.createElement('loading-spinner');
378
- if (!busy)
379
- okSpinner.style.display = 'none';
380
- okBtn.append(okText, okSpinner);
381
- footer.appendChild(okBtn);
382
- }
383
- // END footer
384
- if (footer.hasChildNodes()) {
385
- wrapper.append(closeBtn, header, modalContent, footer);
386
- }
387
- else {
388
- wrapper.append(closeBtn, header, modalContent);
389
- }
390
- result.appendChild(wrapper);
391
- if (!persistent) {
392
- result.querySelector('.close').focus();
393
- }
394
- else {
395
- if (btns.includes('ok')) {
396
- result.querySelector('.ok').focus();
397
- }
398
- }
399
- attachEventHandlers(result, persistent, onClose);
400
- return result;
401
- };
402
-
403
230
  /******************************************************************************
404
231
  Copyright (c) Microsoft Corporation.
405
232
 
@@ -786,103 +613,384 @@ var juxWeb = (function (exports) {
786
613
  // @ts-ignore
787
614
  options.body = content;
788
615
  }
789
- return apiRequest(endpoint, options, onSuccess, onError, onRedirect, requestType, handleUnauthorized);
790
- };
791
- const get = (endpoint, onSuccess, onError, onRedirect, handleUnauthorized) => {
792
- const options = {
793
- method: 'GET',
794
- headers: {
795
- 'Accept': 'application/json',
796
- 'Content-Type': 'application/json',
797
- }
798
- };
799
- return apiRequest(endpoint, options, onSuccess, onError, onRedirect, 'query', handleUnauthorized);
800
- };
801
- const backendPost = (endpoint, postContent, onJsonResponse, onHelpMessage, handleUnauthorized) => {
802
- post(endpoint, postContent, onJsonResponse, onHelpMessage, null, 'command', handleUnauthorized);
803
- };
804
- const backendGet = (endpoint, params, onJsonResponse, onHelpMessage, handleUnauthorized) => {
805
- if (params) {
806
- endpoint += '?';
807
- Object.keys(params).forEach((k, i) => {
808
- if (i > 0)
809
- endpoint += '&';
810
- endpoint += `${k}=${params[k]}`;
811
- });
616
+ return apiRequest(endpoint, options, onSuccess, onError, onRedirect, requestType, handleUnauthorized);
617
+ };
618
+ const get = (endpoint, onSuccess, onError, onRedirect, handleUnauthorized) => {
619
+ const options = {
620
+ method: 'GET',
621
+ headers: {
622
+ 'Accept': 'application/json',
623
+ 'Content-Type': 'application/json',
624
+ }
625
+ };
626
+ return apiRequest(endpoint, options, onSuccess, onError, onRedirect, 'query', handleUnauthorized);
627
+ };
628
+ const backendPost = (endpoint, postContent, onJsonResponse, onHelpMessage, handleUnauthorized) => {
629
+ post(endpoint, postContent, onJsonResponse, onHelpMessage, null, 'command', handleUnauthorized);
630
+ };
631
+ const backendGet = (endpoint, params, onJsonResponse, onHelpMessage, handleUnauthorized) => {
632
+ if (params) {
633
+ endpoint += '?';
634
+ Object.keys(params).forEach((k, i) => {
635
+ if (i > 0)
636
+ endpoint += '&';
637
+ endpoint += `${k}=${params[k]}`;
638
+ });
639
+ }
640
+ get(endpoint, onJsonResponse, onHelpMessage, null, handleUnauthorized);
641
+ };
642
+ const requestMagicLink = (data, onJsonResponse, onUnauthorized) => {
643
+ const { email, token } = data;
644
+ const url = getMagicAuthReqUrl() +
645
+ encodeURIComponent(email) +
646
+ '&destination=' +
647
+ extractTokenFromWindowLocation('destination') +
648
+ '&token=' +
649
+ token;
650
+ const options = {};
651
+ backendRequest(url, options, onJsonResponse, console.error, undefined, // ← use undefined instead of null
652
+ 'query', onUnauthorized);
653
+ };
654
+ const openMagicLink = (magicToken, onJsonResponse, onHelpMessage) => {
655
+ backendRequest(getMagicAuthUrl() + magicToken, {}, onJsonResponse, onHelpMessage);
656
+ };
657
+ const googleLogin = (token, onLogin, onLoginError) => {
658
+ const url = getBackendUrl() + 'auth-google?google-id-token=' + token;
659
+ backendRequest(url, {}, onLogin, onLoginError);
660
+ };
661
+ const microsoftLogin = (token, onLogin, onLoginError) => {
662
+ const url = getBackendUrl() + 'auth-microsoft?access-token=' + token;
663
+ backendRequest(url, {}, onLogin, onLoginError);
664
+ };
665
+ const getSSOAuthorizationEndpoint = (onSuccess, onError) => {
666
+ backendGet('openid/authorization-endpoint', null, onSuccess, onError);
667
+ };
668
+ const validateSSOToken = (token, redirectUri, onLogin, onError, handleUnauthorized) => {
669
+ backendGet('openid/callback', { code: token, 'redirect-uri': redirectUri }, onLogin, onError, handleUnauthorized);
670
+ };
671
+ const authPasswordLogin = (credentials, onLogin, onLoginError, handleUnauthorized) => {
672
+ backendPost('auth-password', credentials, onLogin, onLoginError, handleUnauthorized);
673
+ };
674
+ const getMXData = (domainName) => __awaiter$2(void 0, void 0, void 0, function* () {
675
+ try {
676
+ const records = yield fetch(`https://dns.google.com/resolve?name=${domainName}&type=MX`, {
677
+ method: 'GET',
678
+ mode: 'cors'
679
+ });
680
+ if (!records.ok) {
681
+ console.log('response was not ok');
682
+ return null;
683
+ }
684
+ const recordsJson = yield records.json();
685
+ const answer = recordsJson.Answer;
686
+ if (!answer) {
687
+ return null;
688
+ }
689
+ return Array.from(answer).map((a) => a.data);
690
+ }
691
+ catch (error) {
692
+ console.error('Fetch failed: ', error);
693
+ return null;
694
+ }
695
+ });
696
+
697
+ // TODO: find a better way to import external scripts
698
+ const script$1 = document.createElement('script');
699
+ script$1.type = 'text/javascript';
700
+ script$1.src = 'https://www.google.com/recaptcha/api.js?render=explicit';
701
+ document.head.appendChild(script$1);
702
+ const googleCaptcha = () => {
703
+ const result = document.createElement('google-captcha');
704
+ const notifyCaptchaChange = () => result.dispatchEvent(new Event('captcha-change'));
705
+ const renderCaptcha = () => grecaptcha.ready(() => {
706
+ grecaptcha.render(result, {
707
+ sitekey: process.env.GOOGLE_CAPTCHA_KEY,
708
+ callback: (token) => {
709
+ result.setAttribute('token', token);
710
+ notifyCaptchaChange();
711
+ },
712
+ 'expired-callback': () => {
713
+ result.removeAttribute('token');
714
+ notifyCaptchaChange();
715
+ }
716
+ });
717
+ result.dispatchEvent(new Event('captcha-rendered'));
718
+ });
719
+ if (typeof grecaptcha === 'undefined') {
720
+ script$1.addEventListener('load', renderCaptcha, { once: true });
721
+ }
722
+ else {
723
+ renderCaptcha();
724
+ }
725
+ return result;
726
+ };
727
+
728
+ const juxWhite = '#FFF';
729
+ const juxDarkGrey = '#282828';
730
+ const juxDimmedBlack = '#00000088';
731
+ const juxLightGreen = '#20B03D';
732
+ const juxMediumGreen = '#0D631E';
733
+
734
+ const onKeyDown = (ev, el, persitent, onClose) => {
735
+ if (ev.key === 'Escape') {
736
+ if (persitent) {
737
+ return;
738
+ }
739
+ close(el, onClose);
740
+ }
741
+ else if (ev.key === 'Enter') {
742
+ if (ev.target.id === 'members-textarea') {
743
+ return;
744
+ }
745
+ el.dispatchEvent(new CustomEvent('ok'));
746
+ }
747
+ };
748
+ const close = (el, onClose) => {
749
+ el.remove();
750
+ if (onClose) {
751
+ onClose();
752
+ }
753
+ };
754
+ const attachEventHandlers = (el, persitent, onClose) => {
755
+ const cancelButton = el.querySelector('.cancel');
756
+ cancelButton === null || cancelButton === void 0 ? void 0 : cancelButton.addEventListener('click', (_e) => {
757
+ el.dispatchEvent(new CustomEvent('cancel'));
758
+ if (el.hasAttribute('dont-remove')) {
759
+ return;
760
+ }
761
+ close(el, onClose);
762
+ });
763
+ const closeButton = el.querySelector('.close');
764
+ if (closeButton) {
765
+ closeButton.addEventListener('click', (_e) => {
766
+ close(el, onClose);
767
+ });
768
+ }
769
+ const okButton = el.querySelector('.ok');
770
+ okButton === null || okButton === void 0 ? void 0 : okButton.addEventListener('click', (_e) => {
771
+ el.dispatchEvent(new CustomEvent('ok'));
772
+ });
773
+ el.removeEventListener('keydown', (ev) => onKeyDown(ev, el, persitent, () => onClose));
774
+ el.addEventListener('keydown', (ev) => onKeyDown(ev, el, persitent, () => onClose));
775
+ el === null || el === void 0 ? void 0 : el.addEventListener('click', (e) => {
776
+ if (e.target === el) {
777
+ if (persitent) {
778
+ return;
779
+ }
780
+ close(el, onClose);
781
+ }
782
+ });
783
+ };
784
+ // TODO: Mudar construtor para usar essa interface
785
+ // interface JuxModalOptions {
786
+ // title?: string,
787
+ // btns?: string[],
788
+ // content?: string,
789
+ // isPersistent?: boolean,
790
+ // isBusy?: boolean,
791
+ // body?: HTMLElement,
792
+ // okTxt?: string,
793
+ // cancelTxt?: string,
794
+ // onClose?: any,
795
+ // titleStyle?: string,
796
+ // }
797
+ const applyWrapperStyle = (wrapper) => {
798
+ wrapper.style.alignItems = 'center';
799
+ wrapper.style.backgroundColor = juxWhite;
800
+ wrapper.style.borderRadius = '.3125rem';
801
+ wrapper.style.boxShadow = `0 0 1.33rem ${juxDimmedBlack}`;
802
+ wrapper.style.boxSizing = 'content-box';
803
+ wrapper.style.display = 'flex';
804
+ wrapper.style.flexDirection = 'column';
805
+ wrapper.style.justifyContent = 'flex-start';
806
+ wrapper.style.margin = '0';
807
+ wrapper.style.maxHeight = '88%';
808
+ wrapper.style.maxWidth = '40%';
809
+ wrapper.style.minWidth = '20%';
810
+ wrapper.style.padding = '2.8rem';
811
+ wrapper.style.position = 'relative';
812
+ };
813
+ const applyModalStyle = (modal) => {
814
+ modal.style.alignItems = 'center';
815
+ modal.style.backgroundColor = juxDimmedBlack;
816
+ modal.style.display = 'flex';
817
+ modal.style.height = '100vh';
818
+ modal.style.justifyContent = 'center';
819
+ modal.style.left = '0';
820
+ modal.style.overflow = 'hidden';
821
+ modal.style.position = 'fixed';
822
+ modal.style.top = '0';
823
+ modal.style.width = '100%';
824
+ modal.style.zIndex = '9999';
825
+ };
826
+ const applyHeaderStyle = (header) => {
827
+ header.style.alignItems = 'center';
828
+ header.style.color = juxMediumGreen;
829
+ header.style.flexDirection = 'row';
830
+ header.style.fontSize = '16px';
831
+ header.style.fontWeight = '700';
832
+ header.style.justifyContent = 'space-between';
833
+ header.style.margin = '0';
834
+ header.style.display = 'flex';
835
+ header.style.position = 'relative';
836
+ header.style.width = '100%';
837
+ };
838
+ const applyJuxModalContentStyle = (el) => {
839
+ el.style.display = 'flex';
840
+ el.style.position = 'relative';
841
+ el.style.width = '100%';
842
+ el.style.flexFlow = 'column';
843
+ el.style.marginTop = '1.33rem';
844
+ el.style.fontSize = '14px';
845
+ el.style.rowGap = '8px';
846
+ };
847
+ const applyFooterStyle = (footer) => {
848
+ footer.style.display = 'flex';
849
+ footer.style.width = '100%';
850
+ footer.style.justifyContent = 'flex-end';
851
+ footer.style.alignItems = 'center';
852
+ footer.style.gap = '0.75rem';
853
+ footer.style.marginTop = '1.5rem';
854
+ footer.style.paddingTop = '1rem';
855
+ };
856
+ const applyModalButtonStyle = (btn) => {
857
+ btn.style.display = 'inline-flex';
858
+ btn.style.alignItems = 'center';
859
+ btn.style.justifyContent = 'center';
860
+ btn.style.height = '36px';
861
+ btn.style.minWidth = '88px';
862
+ btn.style.padding = '0 16px';
863
+ btn.style.background = 'rgb(255, 255, 255)';
864
+ btn.style.border = '1px solid rgb(160, 160, 160)';
865
+ btn.style.borderRadius = '4px';
866
+ btn.style.boxShadow = '0 1px 2px rgba(0,0,0,0.15)';
867
+ btn.style.color = 'rgb(68, 68, 68)';
868
+ btn.style.fontSize = '14px';
869
+ btn.style.fontWeight = '500';
870
+ btn.style.whiteSpace = 'nowrap';
871
+ btn.style.cursor = 'pointer';
872
+ };
873
+ const juxModal = (title, btns = ['ok', 'cancel'], content = '', persistent = false, busy = false, body, okTxt = getTranslation('alert-ok-btn-txt'), cancelTxt = getTranslation('alert-cancel-btn-txt'), onClose, titleStyle) => {
874
+ const result = document.createElement('jux-modal');
875
+ applyModalStyle(result);
876
+ const wrapper = document.createElement('modal-wrapper');
877
+ applyWrapperStyle(wrapper);
878
+ // START close
879
+ const closeBtn = document.createElement('button');
880
+ if (!persistent) {
881
+ closeBtn.className = 'close';
882
+ closeBtn.setAttribute('aria-label', getTranslation('close'));
883
+ }
884
+ // END close
885
+ // START header
886
+ const header = document.createElement('modal-header');
887
+ applyHeaderStyle(header);
888
+ if (title) {
889
+ if (titleStyle) {
890
+ header.classList.add(titleStyle);
891
+ }
892
+ const h3 = document.createElement('h3');
893
+ h3.textContent = title;
894
+ h3.style.margin = '0';
895
+ header.appendChild(h3);
896
+ }
897
+ // END header
898
+ // START content
899
+ const modalContent = document.createElement('jux-modal-content');
900
+ applyJuxModalContentStyle(modalContent);
901
+ if (content) {
902
+ modalContent.innerHTML = content;
903
+ }
904
+ else if (body) {
905
+ modalContent.appendChild(body);
906
+ }
907
+ // END content
908
+ // START footer
909
+ const footer = document.createElement('modal-footer');
910
+ applyFooterStyle(footer);
911
+ if (btns.includes('cancel')) {
912
+ const cancelBtn = document.createElement('button');
913
+ cancelBtn.classList.add('secondary-button', 'modal-button', 'action', 'cancel-btn', 'cancel');
914
+ applyModalButtonStyle(cancelBtn);
915
+ const cancelText = document.createElement('button-text');
916
+ cancelText.textContent = cancelTxt;
917
+ const cancelSpinner = document.createElement('loading-spinner');
918
+ if (!busy)
919
+ cancelSpinner.style.display = 'none';
920
+ cancelBtn.append(cancelText, cancelSpinner);
921
+ footer.appendChild(cancelBtn);
922
+ }
923
+ if (btns.includes('ok')) {
924
+ const okBtn = document.createElement('button');
925
+ okBtn.classList.add('primary-button', 'modal-button', 'action', 'ok');
926
+ applyModalButtonStyle(okBtn);
927
+ const okText = document.createElement('button-text');
928
+ okText.textContent = okTxt;
929
+ const okSpinner = document.createElement('loading-spinner');
930
+ if (!busy)
931
+ okSpinner.style.display = 'none';
932
+ okBtn.append(okText, okSpinner);
933
+ footer.appendChild(okBtn);
934
+ }
935
+ // END footer
936
+ if (footer.hasChildNodes()) {
937
+ wrapper.append(closeBtn, header, modalContent, footer);
812
938
  }
813
- get(endpoint, onJsonResponse, onHelpMessage, null, handleUnauthorized);
814
- };
815
- const requestMagicLink = (data, onJsonResponse, onUnauthorized) => {
816
- const { email, token } = data;
817
- const url = getMagicAuthReqUrl() +
818
- encodeURIComponent(email) +
819
- '&destination=' +
820
- extractTokenFromWindowLocation('destination') +
821
- '&token=' +
822
- token;
823
- const options = {};
824
- backendRequest(url, options, onJsonResponse, console.error, undefined, // ← use undefined instead of null
825
- 'query', onUnauthorized);
826
- };
827
- const openMagicLink = (magicToken, onJsonResponse, onHelpMessage) => {
828
- backendRequest(getMagicAuthUrl() + magicToken, {}, onJsonResponse, onHelpMessage);
829
- };
830
- const googleLogin = (token, onLogin, onLoginError) => {
831
- const url = getBackendUrl() + 'auth-google?google-id-token=' + token;
832
- backendRequest(url, {}, onLogin, onLoginError);
833
- };
834
- const microsoftLogin = (token, onLogin, onLoginError) => {
835
- const url = getBackendUrl() + 'auth-microsoft?access-token=' + token;
836
- backendRequest(url, {}, onLogin, onLoginError);
837
- };
838
- const getSSOAuthorizationEndpoint = (onSuccess, onError) => {
839
- backendGet('openid/authorization-endpoint', null, onSuccess, onError);
840
- };
841
- const validateSSOToken = (token, redirectUri, onLogin, onError, handleUnauthorized) => {
842
- backendGet('openid/callback', { code: token, 'redirect-uri': redirectUri }, onLogin, onError, handleUnauthorized);
843
- };
844
- const authPasswordLogin = (credentials, onLogin, onLoginError, handleUnauthorized) => {
845
- backendPost('auth-password', credentials, onLogin, onLoginError, handleUnauthorized);
846
- };
847
- const getMXData = (domainName) => __awaiter$2(void 0, void 0, void 0, function* () {
848
- try {
849
- const records = yield fetch(`https://dns.google.com/resolve?name=${domainName}&type=MX`, {
850
- method: 'GET',
851
- mode: 'cors'
852
- });
853
- if (!records.ok) {
854
- console.log('response was not ok');
855
- return null;
856
- }
857
- const recordsJson = yield records.json();
858
- const answer = recordsJson.Answer;
859
- if (!answer) {
860
- return null;
861
- }
862
- return Array.from(answer).map((a) => a.data);
939
+ else {
940
+ wrapper.append(closeBtn, header, modalContent);
863
941
  }
864
- catch (error) {
865
- console.error('Fetch failed: ', error);
866
- return null;
942
+ result.appendChild(wrapper);
943
+ if (!persistent) {
944
+ result.querySelector('.close').focus();
867
945
  }
868
- });
869
-
870
- // TODO: find a better way to import external scripts
871
- const script$1 = document.createElement('script');
872
- script$1.type = 'text/javascript';
873
- script$1.src = 'https://www.google.com/recaptcha/api.js';
874
- document.head.appendChild(script$1);
875
- const googleCaptcha = () => {
876
- const result = document.createElement('google-captcha');
877
- window.submitCaptcha = (token) => result.setAttribute('token', token);
878
- result.className = 'g-recaptcha';
879
- // @ts-ignore
880
- result.setAttribute('data-sitekey', process.env.GOOGLE_CAPTCHA_KEY);
881
- result.setAttribute('data-callback', 'submitCaptcha');
882
- grecaptcha.render(result);
946
+ else {
947
+ if (btns.includes('ok')) {
948
+ result.querySelector('.ok').focus();
949
+ }
950
+ }
951
+ attachEventHandlers(result, persistent, onClose);
883
952
  return result;
884
953
  };
885
954
 
955
+ const loginFieldClass = 'jux-login-field';
956
+ const addLoginFieldStyles = () => {
957
+ if (document.querySelector(`style[data-${loginFieldClass}]`))
958
+ return;
959
+ const style = document.createElement('style');
960
+ style.setAttribute(`data-${loginFieldClass}`, '');
961
+ style.textContent = `
962
+ .${loginFieldClass}:hover {
963
+ outline: 1px solid #999 !important;
964
+ outline-offset: -1px;
965
+ }
966
+ .${loginFieldClass}:focus {
967
+ outline: 2px solid #3943B7 !important;
968
+ outline-offset: -2px;
969
+ }
970
+ `;
971
+ document.head.appendChild(style);
972
+ };
973
+ const applyTextFieldStyle = (field) => {
974
+ addLoginFieldStyles();
975
+ field.classList.add(loginFieldClass);
976
+ field.style.backgroundColor = '#fff';
977
+ field.style.border = '1px solid #ccc';
978
+ field.style.borderRadius = '8px';
979
+ field.style.boxShadow = 'inset 0 2px 3px #28282822';
980
+ field.style.boxSizing = 'border-box';
981
+ field.style.color = '#444';
982
+ field.style.cursor = 'text';
983
+ field.style.fontFamily = 'Helvetica, Arial, sans-serif';
984
+ field.style.fontSize = '16px';
985
+ field.style.height = '40px';
986
+ field.style.lineHeight = '38px';
987
+ field.style.margin = '0';
988
+ field.style.outline = 'none';
989
+ field.style.overflow = 'hidden';
990
+ field.style.padding = '0 12px';
991
+ field.style.width = '100%';
992
+ };
993
+
886
994
  const socialLoginModal = (userEmail, mailExchanger, token, onUserLogin, onReturn, backgroundImage, currentPage, clientBody, closeMagicLinkModal, handlers) => {
887
995
  const result = document.createElement('social-login-modal');
888
996
  const email = document.createElement('p');
@@ -956,77 +1064,118 @@ var juxWeb = (function (exports) {
956
1064
  closeMagicLinkModal();
957
1065
  });
958
1066
  const applyMagicEmailFieldStyle = (el) => {
1067
+ el.style.alignItems = 'center';
959
1068
  el.style.display = 'flex';
960
- el.style.flexFlow = 'column';
1069
+ el.style.flexFlow = 'row';
1070
+ el.style.gap = '0';
961
1071
  el.style.marginTop = '8px';
962
- el.style.rowGap = '8px';
963
1072
  el.style.width = '100%';
964
1073
  };
965
- const applyInputStyle = (input) => {
966
- input.style.border = `1px solid ${juxDarkGrey}`;
967
- input.style.maxWidth = '390px';
968
- input.style.padding = '10px';
969
- input.style.width = '100%';
970
- input.style.backgroundColor = juxWhite;
971
- input.style.borderRadius = '8px';
972
- input.style.boxShadow = `inset 0 2px 3px ${juxDimmedDarkGrey}`;
973
- input.style.height = '40px';
974
- input.style.whiteSpace = 'pre-line';
975
- input.style.boxSizing = 'border-box';
1074
+ const applyCaptchaStyle = (captcha) => {
1075
+ const captchaFrameWidth = 304;
1076
+ const captchaHeight = 78;
1077
+ const contentWidth = 356;
1078
+ const horizontalInset = 1;
1079
+ const scale = (contentWidth - horizontalInset * 2) / captchaFrameWidth;
1080
+ captcha.style.height = `${captchaHeight * scale}px`;
1081
+ captcha.style.overflow = 'visible';
1082
+ captcha.style.width = '100%';
1083
+ const applyWidgetStyle = () => {
1084
+ const widget = captcha.firstElementChild;
1085
+ widget.style.marginLeft = `${horizontalInset}px`;
1086
+ widget.style.width = `${captchaFrameWidth}px`;
1087
+ widget.style.transform = `scale(${scale})`;
1088
+ widget.style.transformOrigin = 'top left';
1089
+ };
1090
+ if (captcha.firstElementChild) {
1091
+ applyWidgetStyle();
1092
+ }
1093
+ else {
1094
+ captcha.addEventListener('captcha-rendered', applyWidgetStyle, { once: true });
1095
+ }
976
1096
  };
977
1097
  const applyBtnStyle = (btn) => {
978
- btn.style.alignSelf = 'flex-end';
979
1098
  btn.style.backgroundColor = juxLightGreen;
980
1099
  btn.style.border = `2px solid ${juxLightGreen}`;
981
1100
  btn.style.borderRadius = '8px';
1101
+ btn.style.boxSizing = 'border-box';
982
1102
  btn.style.color = juxWhite;
983
1103
  btn.style.cursor = 'pointer';
984
1104
  btn.style.display = 'inline-block';
1105
+ btn.style.fontSize = '16px';
985
1106
  btn.style.fontWeight = '700';
986
1107
  btn.style.height = '40px';
987
1108
  btn.style.lineHeight = '1';
988
- btn.style.marginLeft = '10px';
989
1109
  btn.style.padding = '10px 16px';
990
1110
  btn.style.position = 'relative';
991
1111
  };
1112
+ const captchaModal = (onVerified, onClose) => {
1113
+ const body = document.createElement('captcha-modal-body');
1114
+ body.style.width = '356px';
1115
+ const modal = juxModal('Security check', [], '', false, false, body, '', '', onClose);
1116
+ modal.id = 'magic-email-captcha-modal';
1117
+ const wrapper = modal.querySelector('modal-wrapper');
1118
+ wrapper.style.maxWidth = '356px';
1119
+ wrapper.style.minWidth = '356px';
1120
+ wrapper.style.width = '356px';
1121
+ modal.querySelector('modal-header h3').style.color = juxDarkGrey;
1122
+ document.body.appendChild(modal);
1123
+ const captcha = googleCaptcha();
1124
+ applyCaptchaStyle(captcha);
1125
+ captcha.addEventListener('captcha-change', () => {
1126
+ const token = captcha.getAttribute('token');
1127
+ if (!token)
1128
+ return;
1129
+ modal.remove();
1130
+ onVerified(token);
1131
+ });
1132
+ body.appendChild(captcha);
1133
+ return modal;
1134
+ };
992
1135
  const magicEmailField = (onUserLogin, onReturn, backgroundImage, currentPage, clientBody, closeMagicLinkModal, loginTypes, handlers) => {
993
1136
  const result = document.createElement('magic-email-field');
994
1137
  applyMagicEmailFieldStyle(result);
995
- const captcha = googleCaptcha();
996
1138
  const input = document.createElement('input');
997
1139
  input.id = 'mail-magic';
998
1140
  input.type = 'text';
999
- input.className = 'textfield';
1000
- input.placeholder = 'e-mail';
1001
- applyInputStyle(input);
1002
- input.addEventListener('keydown', (ev) => {
1003
- if (ev.key === 'Enter') {
1004
- onMagicLinkRequest(captcha.getAttribute('token'), input, onUserLogin, onReturn, backgroundImage, currentPage, clientBody, closeMagicLinkModal, loginTypes, handlers);
1005
- }
1006
- });
1141
+ input.placeholder = 'Email';
1142
+ applyTextFieldStyle(input);
1143
+ input.style.borderRadius = '8px 0 0 8px';
1144
+ input.style.flex = '1';
1145
+ input.style.minWidth = '0';
1007
1146
  const btn = document.createElement('button');
1008
1147
  applyBtnStyle(btn);
1009
1148
  btn.id = 'send-magic';
1010
1149
  btn.className = 'primary-button';
1011
- btn.addEventListener('click', (_ev) => onMagicLinkRequest(captcha.getAttribute('token'), input, onUserLogin, onReturn, backgroundImage, currentPage, clientBody, closeMagicLinkModal, loginTypes, handlers));
1012
1150
  btn.textContent = getTranslation('send-magic');
1013
- result.append(input, captcha, btn);
1014
- return result;
1015
- };
1016
-
1017
- const magicLinkModal = (onUserLogin, onReturn, backgroundImage, currentPage, clientBody, loginTypes, handlers) => {
1018
- const result = document.createElement('magic-link-modal');
1019
- const subtitle = document.createElement('magic-link-request-subtitle');
1020
- subtitle.style.fontSize = '14px';
1021
- subtitle.textContent = getTranslation('magic-link-request-subtitle');
1022
- const body = document.createElement('magic-link-request-body');
1023
- body.append(subtitle, magicEmailField(onUserLogin, onReturn, backgroundImage, currentPage, clientBody, () => result.remove(), loginTypes, handlers));
1024
- const modal = juxModal(getTranslation('magic-link-request-title'), [], '', false, false, body, '', '', () => result.remove());
1025
- modal.setAttribute('data-cy', 'magic-link-modal');
1026
- result.appendChild(modal);
1027
- const wrapper = modal.querySelector('modal-wrapper');
1028
- wrapper.style.maxWidth = '360px';
1029
- wrapper.style.width = '100%';
1151
+ btn.style.borderRadius = '0 8px 8px 0';
1152
+ btn.style.margin = '0';
1153
+ let openCaptchaModal = null;
1154
+ const requestMagicLink = (token) => onMagicLinkRequest(token, input, onUserLogin, onReturn, backgroundImage, currentPage, clientBody, closeMagicLinkModal, loginTypes, handlers);
1155
+ btn.addEventListener('click', () => {
1156
+ if (input.value.includes('-test@')) {
1157
+ requestMagicLink(null);
1158
+ return;
1159
+ }
1160
+ if (openCaptchaModal === null || openCaptchaModal === void 0 ? void 0 : openCaptchaModal.isConnected)
1161
+ return;
1162
+ openCaptchaModal = captchaModal((token) => {
1163
+ openCaptchaModal = null;
1164
+ requestMagicLink(token);
1165
+ }, () => openCaptchaModal = null);
1166
+ });
1167
+ const updateButtonState = () => {
1168
+ btn.disabled = !isValidEmail(input.value);
1169
+ btn.style.cursor = btn.disabled ? 'not-allowed' : 'pointer';
1170
+ btn.style.opacity = btn.disabled ? '0.5' : '1';
1171
+ };
1172
+ input.addEventListener('input', updateButtonState);
1173
+ input.addEventListener('keydown', (ev) => {
1174
+ if (ev.key === 'Enter' && !btn.disabled)
1175
+ btn.click();
1176
+ });
1177
+ updateButtonState();
1178
+ result.append(input, btn);
1030
1179
  return result;
1031
1180
  };
1032
1181
 
@@ -1045,6 +1194,8 @@ var juxWeb = (function (exports) {
1045
1194
  container.style.justifyContent = 'center';
1046
1195
  container.style.margin = '0 auto';
1047
1196
  container.style.paddingTop = '0';
1197
+ container.style.paddingInline = '36px';
1198
+ container.style.boxSizing = 'border-box';
1048
1199
  };
1049
1200
  const applyBtnsContainerStyles = (container) => {
1050
1201
  container.style.display = 'flex';
@@ -1072,6 +1223,78 @@ var juxWeb = (function (exports) {
1072
1223
  return result;
1073
1224
  };
1074
1225
 
1226
+ class JuxWebGlobal {
1227
+ setCurrentUser(newUser) {
1228
+ this._currentUser = newUser;
1229
+ }
1230
+ getCurrentUser() {
1231
+ return this._currentUser;
1232
+ }
1233
+ setBackendToken(token) {
1234
+ this._backendToken = token;
1235
+ }
1236
+ getBackendToken() {
1237
+ return this._backendToken;
1238
+ }
1239
+ }
1240
+
1241
+ const resetJuxWebGlobal = () => {
1242
+ window.juxWebGlobal = new JuxWebGlobal();
1243
+ };
1244
+ const getCurrentUser = () => {
1245
+ var _a;
1246
+ return (_a = window.juxWebGlobal) === null || _a === void 0 ? void 0 : _a.getCurrentUser();
1247
+ };
1248
+
1249
+ const setBackendToken = (token) => {
1250
+ var _a;
1251
+ (_a = window.juxWebGlobal) === null || _a === void 0 ? void 0 : _a.setBackendToken(token);
1252
+ if (token)
1253
+ setLocalStorageItem('token', token);
1254
+ };
1255
+ const clearSession = () => {
1256
+ removeLocalStorageItem('token');
1257
+ resetJuxWebGlobal();
1258
+ };
1259
+ const onTokenAcquired = (token, onUserLogin) => {
1260
+ setBackendToken(token);
1261
+ onUserLogin();
1262
+ };
1263
+
1264
+ const applyLoginTokenStyle = (textarea) => {
1265
+ applyTextFieldStyle(textarea);
1266
+ textarea.style.resize = 'none';
1267
+ textarea.style.setProperty('-webkit-text-security', 'disc');
1268
+ };
1269
+ const loginTokenTextarea = (onUserLogin, onLoginError) => {
1270
+ const juxWebGlobal = window.juxWebGlobal;
1271
+ const textarea = document.createElement('textarea');
1272
+ textarea.id = 'input-login-token';
1273
+ textarea.autocomplete = 'off';
1274
+ textarea.placeholder = 'Paste sign-in token';
1275
+ textarea.rows = 1;
1276
+ textarea.spellcheck = false;
1277
+ textarea.setAttribute('aria-label', 'Sign-in token');
1278
+ applyLoginTokenStyle(textarea);
1279
+ let loginInProgress = false;
1280
+ textarea.addEventListener('input', () => {
1281
+ const loginToken = textarea.value.trim();
1282
+ if (loginToken.length !== 16 || loginInProgress)
1283
+ return;
1284
+ loginInProgress = true;
1285
+ juxWebGlobal.setBackendToken(loginToken);
1286
+ backendGet('profile', null, (res) => {
1287
+ juxWebGlobal.setCurrentUser(res);
1288
+ onTokenAcquired(loginToken, onUserLogin);
1289
+ }, (err) => {
1290
+ loginInProgress = false;
1291
+ juxWebGlobal.setBackendToken('');
1292
+ onLoginError(err);
1293
+ });
1294
+ });
1295
+ return textarea;
1296
+ };
1297
+
1075
1298
  const authPasswordRow = (type, autoCompleteValue) => {
1076
1299
  const result = document.createElement(`${type}-row`);
1077
1300
  result.style.display = 'flex';
@@ -1135,20 +1358,56 @@ var juxWeb = (function (exports) {
1135
1358
  });
1136
1359
  return result;
1137
1360
  };
1138
- const onEmailLoginRequest = (clientApp, loginPage, onUserLogin, backgroundImage, loginTypes, handlers) => {
1139
- const onReturn = () => clientApp.appendChild(loginPage);
1140
- loginPage.appendChild(magicLinkModal(onUserLogin, onReturn, backgroundImage, loginPage, clientApp, loginTypes, handlers));
1141
- };
1142
1361
  const appendLoginTypes = (loginPage, btnsContainer, clientApp, onUserLogin, onLoginError, loginTypes, backgroundImg, explanation, handleSSOLogin, handlers) => {
1143
1362
  const upperLoginTypes = loginTypes.map(str => str.toUpperCase());
1363
+ if (upperLoginTypes.length === 0) {
1364
+ applyFormattedTranslation(explanation, 'sign-in-no-login-type');
1365
+ btnsContainer.replaceChildren();
1366
+ return;
1367
+ }
1144
1368
  const onlyAuth = upperLoginTypes.length > 0 &&
1145
1369
  upperLoginTypes.every(type => type === 'AUTH-PASSWORD');
1146
- explanation.textContent = getTranslation('sign-in-explanation');
1370
+ const onlyEmail = upperLoginTypes.every(type => type === 'EMAIL');
1371
+ applyFormattedTranslation(explanation, 'sign-in-explanation');
1372
+ const showLoginTypes = () => {
1373
+ btnsContainer.replaceChildren();
1374
+ appendLoginTypes(loginPage, btnsContainer, clientApp, onUserLogin, onLoginError, loginTypes, backgroundImg, explanation, handleSSOLogin, handlers);
1375
+ };
1376
+ const showEmailLogin = () => {
1377
+ btnsContainer.replaceChildren();
1378
+ applyFormattedTranslation(explanation, 'magic-link-request-title');
1379
+ const subtitle = document.createElement('magic-link-request-subtitle');
1380
+ subtitle.style.fontSize = '14px';
1381
+ subtitle.style.lineHeight = '20px';
1382
+ subtitle.style.marginLeft = '2px';
1383
+ subtitle.style.marginTop = '12px';
1384
+ subtitle.style.width = 'calc(100% - 2px)';
1385
+ subtitle.textContent = getTranslation('magic-link-request-subtitle');
1386
+ const onReturn = () => {
1387
+ showLoginTypes();
1388
+ clientApp.appendChild(loginPage);
1389
+ };
1390
+ const content = document.createElement('email-login-content');
1391
+ content.style.alignItems = 'center';
1392
+ content.style.display = 'flex';
1393
+ content.style.flexDirection = 'column';
1394
+ content.style.width = '356px';
1395
+ const tokenInput = loginTokenTextarea(onUserLogin, onLoginError);
1396
+ tokenInput.style.marginTop = '24px';
1397
+ content.append(subtitle, magicEmailField(onUserLogin, onReturn, backgroundImg, loginPage, clientApp, () => { }, upperLoginTypes, handlers), tokenInput);
1398
+ btnsContainer.appendChild(content);
1399
+ if (!onlyEmail)
1400
+ btnsContainer.appendChild(backButton('Back', showLoginTypes));
1401
+ };
1402
+ if (onlyEmail) {
1403
+ showEmailLogin();
1404
+ return;
1405
+ }
1147
1406
  if (upperLoginTypes.includes('AUTH-PASSWORD2') || onlyAuth) {
1148
1407
  btnsContainer.replaceChildren();
1149
1408
  btnsContainer.appendChild(authPasswordForm(clientApp, loginPage, onUserLogin, onLoginError, backgroundImg, handlers));
1150
1409
  upperLoginTypes.pop();
1151
- explanation.textContent = getTranslation('sign-in-email');
1410
+ applyFormattedTranslation(explanation, 'sign-in-email');
1152
1411
  if (!onlyAuth) {
1153
1412
  btnsContainer.appendChild(backButton('Back', () => {
1154
1413
  btnsContainer.replaceChildren();
@@ -1172,7 +1431,7 @@ var juxWeb = (function (exports) {
1172
1431
  btnsContainer.appendChild(loginButton('LINKEDIN', () => console.log('login with Linkedin')));
1173
1432
  }
1174
1433
  if (upperLoginTypes.includes('EMAIL')) {
1175
- btnsContainer.appendChild(loginButton('EMAIL', () => onEmailLoginRequest(clientApp, loginPage, onUserLogin, backgroundImg, upperLoginTypes, handlers)));
1434
+ btnsContainer.appendChild(loginButton('EMAIL', showEmailLogin));
1176
1435
  }
1177
1436
  if (upperLoginTypes.includes('AUTH-PASSWORD')) {
1178
1437
  upperLoginTypes.push('AUTH-PASSWORD2');
@@ -1187,7 +1446,7 @@ var juxWeb = (function (exports) {
1187
1446
  const salutation = document.createElement('sign-in-salutation');
1188
1447
  salutation.textContent = getTranslation('sign-in-salutation');
1189
1448
  const explanation = document.createElement('sign-in-explanation');
1190
- explanation.textContent = getTranslation('sign-in-explanation');
1449
+ applyFormattedTranslation(explanation, 'sign-in-explanation');
1191
1450
  const msg = document.createElement('sign-in-msg');
1192
1451
  msg.textContent = getTranslation('sign-in-msg-general');
1193
1452
  content.push(salutation, explanation, msg);
@@ -14565,44 +14824,6 @@ var juxWeb = (function (exports) {
14565
14824
  document.body.style.pointerEvents = 'auto';
14566
14825
  };
14567
14826
 
14568
- class JuxWebGlobal {
14569
- setCurrentUser(newUser) {
14570
- this._currentUser = newUser;
14571
- }
14572
- getCurrentUser() {
14573
- return this._currentUser;
14574
- }
14575
- setBackendToken(token) {
14576
- this._backendToken = token;
14577
- }
14578
- getBackendToken() {
14579
- return this._backendToken;
14580
- }
14581
- }
14582
-
14583
- const resetJuxWebGlobal = () => {
14584
- window.juxWebGlobal = new JuxWebGlobal();
14585
- };
14586
- const getCurrentUser = () => {
14587
- var _a;
14588
- return (_a = window.juxWebGlobal) === null || _a === void 0 ? void 0 : _a.getCurrentUser();
14589
- };
14590
-
14591
- const setBackendToken = (token) => {
14592
- var _a;
14593
- (_a = window.juxWebGlobal) === null || _a === void 0 ? void 0 : _a.setBackendToken(token);
14594
- if (token)
14595
- setLocalStorageItem('token', token);
14596
- };
14597
- const clearSession = () => {
14598
- removeLocalStorageItem('token');
14599
- resetJuxWebGlobal();
14600
- };
14601
- const onTokenAcquired = (token, onUserLogin) => {
14602
- setBackendToken(token);
14603
- onUserLogin();
14604
- };
14605
-
14606
14827
  // TODO: find a better way to import external scripts
14607
14828
  const script = document.createElement('script');
14608
14829
  script.type = 'text/javascript';
@@ -14838,7 +15059,7 @@ var juxWeb = (function (exports) {
14838
15059
  email.style.marginBottom = '10px';
14839
15060
  const howTo = document.createElement('p');
14840
15061
  howTo.id = 'magic-link-how-to';
14841
- howTo.innerHTML = getTranslation(howTo.id);
15062
+ applyFormattedTranslation(howTo, howTo.id);
14842
15063
  howTo.style.padding = '0 30px';
14843
15064
  howTo.style.textAlign = 'center';
14844
15065
  const btn = document.createElement('button');
@@ -14873,7 +15094,7 @@ var juxWeb = (function (exports) {
14873
15094
  email.style.marginBottom = '10px';
14874
15095
  const info = document.createElement('p');
14875
15096
  info.id = 'magic-link-fail-info';
14876
- info.innerHTML = getTranslation(failInfo || info.id);
15097
+ applyFormattedTranslation(info, failInfo || info.id);
14877
15098
  info.style.padding = '0 30px';
14878
15099
  info.style.textAlign = 'center';
14879
15100
  const back = document.createElement('a');
@@ -14951,9 +15172,9 @@ var juxWeb = (function (exports) {
14951
15172
  window.juxWebGlobal.fetchUserBackendUrl = fetchUserBackendUrl;
14952
15173
  window.juxWebGlobal.magicLinkRequestEndpoint = magicLinkRequestEndpoint;
14953
15174
  window.juxWebGlobal.magicLinkAuthEndpoint = magicLinkAuthEndpoint;
14954
- const supportedLoginTypesArray = Array.isArray(supportedLoginTypes)
15175
+ const supportedLoginTypesArray = (Array.isArray(supportedLoginTypes)
14955
15176
  ? supportedLoginTypes
14956
- : [supportedLoginTypes];
15177
+ : [supportedLoginTypes]).filter((loginType) => typeof loginType === 'string' && loginType.trim().length > 0);
14957
15178
  const signInToken = extractTokenFromWindowLocation('sign-in') || extractTokenFromWindowLocation('magic-link');
14958
15179
  if (signInToken) {
14959
15180
  const onOpenMagicLink = () => {
@@ -15080,6 +15301,22 @@ var juxWeb = (function (exports) {
15080
15301
  onRequestMagicLink();
15081
15302
  };
15082
15303
 
15304
+ const magicLinkModal = (onUserLogin, onReturn, backgroundImage, currentPage, clientBody, loginTypes, handlers) => {
15305
+ const result = document.createElement('magic-link-modal');
15306
+ const subtitle = document.createElement('magic-link-request-subtitle');
15307
+ subtitle.style.fontSize = '14px';
15308
+ subtitle.textContent = getTranslation('magic-link-request-subtitle');
15309
+ const body = document.createElement('magic-link-request-body');
15310
+ body.append(subtitle, magicEmailField(onUserLogin, onReturn, backgroundImage, currentPage, clientBody, () => result.remove(), loginTypes, handlers));
15311
+ const modal = juxModal(getTranslation('magic-link-request-title'), [], '', false, false, body, '', '', () => result.remove());
15312
+ modal.setAttribute('data-cy', 'magic-link-modal');
15313
+ result.appendChild(modal);
15314
+ const wrapper = modal.querySelector('modal-wrapper');
15315
+ wrapper.style.maxWidth = '360px';
15316
+ wrapper.style.width = '100%';
15317
+ return result;
15318
+ };
15319
+
15083
15320
  class JuxEvent extends Event {
15084
15321
  constructor(type, method, endpoint, onResult, params, onError, onRedirect, extras) {
15085
15322
  super('jux-event', { bubbles: true, cancelable: true, composed: true });
@@ -15102,6 +15339,8 @@ var juxWeb = (function (exports) {
15102
15339
 
15103
15340
  exports.clearSession = clearSession;
15104
15341
  exports.command = command;
15342
+ exports.createFormattedParagraph = createFormattedParagraph;
15343
+ exports.formatTextToMarkdown = formatTextToMarkdown;
15105
15344
  exports.getCurrentUser = getCurrentUser;
15106
15345
  exports.getTranslation = getTranslation;
15107
15346
  exports.handleJuxEvent = handleJuxEvent;