underpost 3.2.5 → 3.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/.github/workflows/release.cd.yml +1 -2
  2. package/CHANGELOG.md +251 -1
  3. package/CLI-HELP.md +26 -13
  4. package/Dockerfile +0 -4
  5. package/README.md +3 -3
  6. package/bin/build.js +13 -3
  7. package/bin/deploy.js +570 -1
  8. package/bin/file.js +5 -0
  9. package/conf.js +11 -2
  10. package/jsconfig.json +1 -1
  11. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  12. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  13. package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
  14. package/manifests/deployment/dd-test-development/deployment.yaml +136 -66
  15. package/manifests/deployment/dd-test-development/proxy.yaml +41 -5
  16. package/package.json +20 -11
  17. package/src/api/core/core.controller.js +10 -10
  18. package/src/api/core/core.service.js +10 -10
  19. package/src/api/default/default.controller.js +10 -10
  20. package/src/api/default/default.service.js +10 -10
  21. package/src/api/document/document.controller.js +12 -12
  22. package/src/api/document/document.model.js +10 -16
  23. package/src/api/file/file.controller.js +8 -8
  24. package/src/api/file/file.model.js +10 -10
  25. package/src/api/file/file.service.js +36 -36
  26. package/src/api/test/test.controller.js +8 -8
  27. package/src/api/test/test.service.js +8 -8
  28. package/src/api/user/guest.service.js +99 -0
  29. package/src/api/user/user.controller.js +6 -6
  30. package/src/api/user/user.model.js +8 -13
  31. package/src/api/user/user.service.js +3 -20
  32. package/src/cli/deploy.js +33 -30
  33. package/src/cli/fs.js +62 -5
  34. package/src/cli/image.js +43 -1
  35. package/src/cli/index.js +5 -1
  36. package/src/cli/release.js +57 -1
  37. package/src/cli/repository.js +35 -3
  38. package/src/cli/run.js +300 -35
  39. package/src/cli/ssh.js +1 -1
  40. package/src/cli/static.js +43 -115
  41. package/src/client/Default.index.js +21 -33
  42. package/src/client/components/core/404.js +4 -4
  43. package/src/client/components/core/500.js +4 -4
  44. package/src/client/components/core/Account.js +73 -60
  45. package/src/client/components/core/AgGrid.js +23 -33
  46. package/src/client/components/core/Alert.js +12 -13
  47. package/src/client/components/core/AppStore.js +1 -1
  48. package/src/client/components/core/Auth.js +20 -32
  49. package/src/client/components/core/Badge.js +7 -13
  50. package/src/client/components/core/BtnIcon.js +15 -17
  51. package/src/client/components/core/CalendarCore.js +42 -63
  52. package/src/client/components/core/Chat.js +13 -15
  53. package/src/client/components/core/ClientEvents.js +87 -0
  54. package/src/client/components/core/ColorPaletteElement.js +309 -0
  55. package/src/client/components/core/Content.js +17 -14
  56. package/src/client/components/core/Css.js +15 -71
  57. package/src/client/components/core/CssCore.js +12 -16
  58. package/src/client/components/core/D3Chart.js +4 -4
  59. package/src/client/components/core/Docs.js +60 -59
  60. package/src/client/components/core/DropDown.js +69 -91
  61. package/src/client/components/core/EventBus.js +92 -0
  62. package/src/client/components/core/EventsUI.js +14 -17
  63. package/src/client/components/core/FileExplorer.js +102 -234
  64. package/src/client/components/core/FullScreen.js +47 -75
  65. package/src/client/components/core/Input.js +24 -69
  66. package/src/client/components/core/Keyboard.js +25 -18
  67. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  68. package/src/client/components/core/LoadingAnimation.js +25 -31
  69. package/src/client/components/core/LogIn.js +41 -41
  70. package/src/client/components/core/LogOut.js +23 -14
  71. package/src/client/components/core/Modal.js +397 -176
  72. package/src/client/components/core/NotificationManager.js +14 -18
  73. package/src/client/components/core/Panel.js +54 -50
  74. package/src/client/components/core/PanelForm.js +25 -125
  75. package/src/client/components/core/Polyhedron.js +110 -214
  76. package/src/client/components/core/PublicProfile.js +39 -32
  77. package/src/client/components/core/Recover.js +52 -48
  78. package/src/client/components/core/Responsive.js +88 -32
  79. package/src/client/components/core/RichText.js +9 -18
  80. package/src/client/components/core/Router.js +24 -3
  81. package/src/client/components/core/SearchBox.js +37 -37
  82. package/src/client/components/core/SignUp.js +39 -30
  83. package/src/client/components/core/SocketIo.js +31 -2
  84. package/src/client/components/core/SocketIoHandler.js +6 -6
  85. package/src/client/components/core/ToggleSwitch.js +8 -20
  86. package/src/client/components/core/ToolTip.js +5 -17
  87. package/src/client/components/core/Translate.js +56 -59
  88. package/src/client/components/core/Validator.js +26 -16
  89. package/src/client/components/core/Wallet.js +15 -26
  90. package/src/client/components/core/Worker.js +140 -25
  91. package/src/client/components/core/windowGetDimensions.js +7 -7
  92. package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
  93. package/src/client/components/default/CssDefault.js +12 -12
  94. package/src/client/components/default/LogInDefault.js +6 -4
  95. package/src/client/components/default/LogOutDefault.js +6 -4
  96. package/src/client/components/default/RouterDefault.js +47 -0
  97. package/src/client/components/default/SettingsDefault.js +4 -4
  98. package/src/client/components/default/SignUpDefault.js +6 -4
  99. package/src/client/components/default/TranslateDefault.js +3 -3
  100. package/src/client/services/core/core.service.js +17 -49
  101. package/src/client/services/default/default.management.js +139 -242
  102. package/src/client/services/default/default.service.js +10 -16
  103. package/src/client/services/document/document.service.js +14 -19
  104. package/src/client/services/file/file.service.js +8 -13
  105. package/src/client/services/test/test.service.js +8 -13
  106. package/src/client/services/user/guest.service.js +79 -0
  107. package/src/client/services/user/user.management.js +5 -5
  108. package/src/client/services/user/user.service.js +14 -20
  109. package/src/client/ssr/body/404.js +3 -3
  110. package/src/client/ssr/body/500.js +3 -3
  111. package/src/client/ssr/body/CacheControl.js +5 -2
  112. package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
  113. package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
  114. package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
  115. package/src/client/ssr/offline/Maintenance.js +12 -11
  116. package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
  117. package/src/client/ssr/pages/Test.js +2 -2
  118. package/src/client/sw/core.sw.js +212 -0
  119. package/src/index.js +1 -1
  120. package/src/runtime/express/Dockerfile +4 -4
  121. package/src/runtime/lampp/Dockerfile +8 -7
  122. package/src/runtime/wp/Dockerfile +11 -17
  123. package/src/server/client-build-docs.js +45 -46
  124. package/src/server/client-build.js +334 -60
  125. package/src/server/client-formatted.js +47 -16
  126. package/src/server/conf.js +5 -4
  127. package/src/server/ipfs-client.js +232 -91
  128. package/src/server/process.js +13 -27
  129. package/src/server/start.js +6 -3
  130. package/src/server/valkey.js +134 -235
  131. package/tsconfig.docs.json +15 -0
  132. package/typedoc.json +20 -0
  133. package/jsdoc.json +0 -52
  134. package/src/client/components/core/ColorPalette.js +0 -5267
  135. package/src/client/components/core/JoyStick.js +0 -80
  136. package/src/client/components/default/RoutesDefault.js +0 -49
  137. package/src/client/sw/default.sw.js +0 -127
  138. package/src/client/sw/template.sw.js +0 -84
@@ -7,17 +7,18 @@ import { Modal } from './Modal.js';
7
7
  import { getId } from './CommonJs.js';
8
8
  import { setPath, getProxyPath, getQueryParams, extractUsernameFromPath, RouterEvents } from './Router.js';
9
9
 
10
- const PublicProfile = {
11
- Data: {},
12
- currentUsername: null, // Track the currently displayed username for back/forward navigation
10
+ class PublicProfile {
11
+ static Data = {};
12
+ /** @type {string|null} Track the currently displayed username for back/forward navigation */
13
+ static currentUsername = null;
13
14
 
14
- Update: async function (options = { idModal: '', user: {} }) {
15
+ static async Update(options = { idModal: '', user: {} }) {
15
16
  const { idModal, user } = options;
16
17
  const username = user.username || 'Unknown User';
17
18
 
18
19
  // Check if modal exists and is registered in Modal.Data
19
20
  if (!Modal.Data[idModal]) {
20
- return await this.Render(options);
21
+ return await this.instance(options);
21
22
  }
22
23
 
23
24
  try {
@@ -35,7 +36,7 @@ const PublicProfile = {
35
36
  this._cleanupProfileData({ idModal });
36
37
 
37
38
  // Re-render the profile content with new user
38
- const newContent = await this.Render({ ...options, disableUpdate: true });
39
+ const newContent = await this.instance({ ...options, disableUpdate: true });
39
40
 
40
41
  // Update modal content using Modal.writeHTML with smooth transition
41
42
  Modal.writeHTML({ idModal, html: newContent });
@@ -54,9 +55,9 @@ const PublicProfile = {
54
55
 
55
56
  throw error;
56
57
  }
57
- },
58
+ }
58
59
 
59
- _getLoadingHtml: function (username) {
60
+ static _getLoadingHtml(username) {
60
61
  return html`
61
62
  <div class="profile-loading-container">
62
63
  <div class="profile-loading-spinner"></div>
@@ -94,9 +95,9 @@ const PublicProfile = {
94
95
  </style>
95
96
  </div>
96
97
  `;
97
- },
98
+ }
98
99
 
99
- _addTransitionEffect: function (idModal) {
100
+ static _addTransitionEffect(idModal) {
100
101
  // Add smooth transition effect to modal content
101
102
  const modalContent = s(`.html-${idModal}`);
102
103
  if (modalContent) {
@@ -108,9 +109,9 @@ const PublicProfile = {
108
109
  modalContent.style.opacity = '1';
109
110
  }, 50);
110
111
  }
111
- },
112
+ }
112
113
 
113
- _getErrorHtml: function (username, errorMessage) {
114
+ static _getErrorHtml(username, errorMessage) {
114
115
  return html`
115
116
  <div class="profile-error-container">
116
117
  <div class="profile-error-icon">
@@ -169,24 +170,27 @@ const PublicProfile = {
169
170
  </style>
170
171
  </div>
171
172
  `;
172
- },
173
+ }
173
174
 
174
- _cleanupProfileData: function ({ idModal }) {
175
+ static _cleanupProfileData({ idModal }) {
175
176
  delete ThemeEvents[`error-state-${idModal}`];
176
177
  delete ThemeEvents[`profile-${idModal}`];
177
178
  delete this.Data[idModal];
178
- },
179
+ }
179
180
 
180
- _ensureModalState: function (idModal) {
181
+ static _ensureModalState(idModal) {
181
182
  // Ensure modal is in the correct state for content updates
182
183
  if (Modal.Data[idModal]) {
183
184
  // Reset any modal-specific states that might interfere
184
185
  Modal.Data[idModal].updated = true;
185
186
  Modal.Data[idModal].lastUpdated = Date.now();
186
187
  }
187
- },
188
+ }
188
189
 
189
- Render: async function (
190
+ /**
191
+ * @param {{ idModal?: string, user: { _id?: string, username?: string } }} options
192
+ */
193
+ static async instance(
190
194
  options = {
191
195
  idModal: '',
192
196
  user: {},
@@ -436,7 +440,7 @@ const PublicProfile = {
436
440
  onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 4px 12px ${colors.buttonShadow}';"
437
441
  >
438
442
  <i class="fa-solid fa-home"></i>
439
- ${Translate.Render('go-home')}
443
+ ${Translate.instance('go-home')}
440
444
  </a>
441
445
  <a
442
446
  href="javascript:history.back()"
@@ -459,7 +463,7 @@ const PublicProfile = {
459
463
  onmouseout="this.style.background='transparent'; this.style.borderColor='${colors.primaryColor}40';"
460
464
  >
461
465
  <i class="fa-solid fa-arrow-left"></i>
462
- ${Translate.Render('go-back')}
466
+ ${Translate.instance('go-back')}
463
467
  </a>
464
468
  </div>
465
469
  </div>
@@ -468,7 +472,7 @@ const PublicProfile = {
468
472
 
469
473
  if (!userId && !username) {
470
474
  return renderErrorState(
471
- Translate.Render('user-not-found'),
475
+ Translate.instance('user-not-found'),
472
476
  'fa-user-slash',
473
477
  "The user you're looking for could not be found. Please check the username and try again.",
474
478
  );
@@ -503,13 +507,13 @@ const PublicProfile = {
503
507
  } else {
504
508
  if (result.message && result.message.toLowerCase().match('private'))
505
509
  return renderErrorState(
506
- Translate.Render('profile-is-private'),
510
+ Translate.instance('profile-is-private'),
507
511
  'fa-lock',
508
512
  'This user has chosen to keep their profile private. Respect their privacy and check back later if they change their settings.',
509
513
  );
510
514
  else
511
515
  return renderErrorState(
512
- Translate.Render('user-not-found'),
516
+ Translate.instance('user-not-found'),
513
517
  'fa-user-slash',
514
518
  'This user profile does not exist or has been removed. Please verify the username.',
515
519
  );
@@ -517,7 +521,7 @@ const PublicProfile = {
517
521
  } catch (error) {
518
522
  console.error('Error fetching public profile:', error);
519
523
  return renderErrorState(
520
- Translate.Render('error-loading-profile'),
524
+ Translate.instance('error-loading-profile'),
521
525
  'fa-circle-exclamation',
522
526
  'We encountered an issue loading this profile. Please try again in a moment or contact support if the problem persists.',
523
527
  );
@@ -526,7 +530,7 @@ const PublicProfile = {
526
530
  // If user doesn't have public profile enabled
527
531
  if (!userData.publicProfile) {
528
532
  return renderErrorState(
529
- Translate.Render('profile-is-private'),
533
+ Translate.instance('profile-is-private'),
530
534
  'fa-lock',
531
535
  'This user has chosen to keep their profile private. Respect their privacy and check back later if they change their settings.',
532
536
  );
@@ -765,7 +769,7 @@ const PublicProfile = {
765
769
  transition: color 0.3s ease-in-out;
766
770
  "
767
771
  >
768
- ${Translate.Render('no-description')}
772
+ ${Translate.instance('no-description')}
769
773
  </p>`}
770
774
  </div>
771
775
 
@@ -792,7 +796,7 @@ const PublicProfile = {
792
796
  "
793
797
  >
794
798
  <i class="fa-solid fa-calendar" style="margin-right: 6px;"></i>
795
- ${Translate.Render('member-since')}: ${new Date(userData.createdAt).toLocaleDateString()}
799
+ ${Translate.instance('member-since')}: ${new Date(userData.createdAt).toLocaleDateString()}
796
800
  </span>
797
801
  </div>
798
802
 
@@ -817,7 +821,7 @@ const PublicProfile = {
817
821
  <a
818
822
  class="${profileId}-button"
819
823
  href="javascript:void(0)"
820
- title="${Translate.Render('view-profile')}"
824
+ title="${Translate.instance('view-profile')}"
821
825
  style="
822
826
  display: inline-flex;
823
827
  align-items: center;
@@ -850,9 +854,12 @@ const PublicProfile = {
850
854
  </div>
851
855
  </div>
852
856
  `;
853
- },
857
+ }
854
858
 
855
- Router: async function (options = { idModal: '' }) {
859
+ /**
860
+ * @param {{ idModal?: string }} options
861
+ */
862
+ static async Router(options = { idModal: '' }) {
856
863
  const idModal = options.idModal || 'modal-public-profile';
857
864
  // Register RouterEvents listener for back/forward navigation between profiles
858
865
  // This ensures the profile updates when the user navigates through browser history
@@ -882,7 +889,7 @@ const PublicProfile = {
882
889
  }
883
890
  }
884
891
  };
885
- },
886
- };
892
+ }
893
+ }
887
894
 
888
895
  export { PublicProfile };
@@ -8,13 +8,25 @@ import { Translate } from './Translate.js';
8
8
  import { Validator } from './Validator.js';
9
9
  import { s } from './VanillaJs.js';
10
10
  import { getProxyPath, getQueryParams } from './Router.js';
11
-
12
- const Recover = {
13
- Event: {},
14
- Trigger: async function (options) {
15
- for (const eventKey of Object.keys(this.Event)) await this.Event[eventKey](options);
16
- },
17
- Render: async function (options = { idModal: '', user: {}, bottomRender: async () => '' }) {
11
+ import { RecoverEventType, recoverEvents } from './ClientEvents.js';
12
+ class Recover {
13
+ static Event = {};
14
+ static onTriggered(listener, options = {}) {
15
+ if (options.key) Recover.Event[options.key] = listener;
16
+ return recoverEvents.on(RecoverEventType.triggered, listener, options);
17
+ }
18
+ static offTriggered(key) {
19
+ delete Recover.Event[key];
20
+ return recoverEvents.off(key);
21
+ }
22
+ static hasTriggeredListener(key) {
23
+ return recoverEvents.has(key);
24
+ }
25
+ static async Trigger(options) {
26
+ await recoverEvents.emit(RecoverEventType.triggered, options);
27
+ for (const eventKey of Object.keys(Recover.Event)) await Recover.Event[eventKey](options);
28
+ }
29
+ static async instance(options = { idModal: '', user: {}, bottomRender: async () => '' }) {
18
30
  const { idModal, user } = options;
19
31
  let mode = 'recover-verify-email';
20
32
  const recoverToken = getQueryParams().payload;
@@ -25,7 +37,7 @@ const Recover = {
25
37
  rules: [{ type: 'isEmpty' }, { type: 'isLength', options: { min: 2, max: 20 } }],
26
38
  show: () => false,
27
39
  disable: function () {
28
- return !this.show();
40
+ return !Recover.show();
29
41
  },
30
42
  },
31
43
  'recover-email': {
@@ -34,7 +46,7 @@ const Recover = {
34
46
  rules: [{ type: 'isEmpty' }, { type: 'isEmail' }],
35
47
  show: () => mode === 'recover-verify-email',
36
48
  disable: function () {
37
- return !this.show();
49
+ return !Recover.show();
38
50
  },
39
51
  },
40
52
  'recover-password': {
@@ -43,7 +55,7 @@ const Recover = {
43
55
  rules: [{ type: 'isStrongPassword' }],
44
56
  show: () => mode === 'change-password',
45
57
  disable: function () {
46
- return !this.show();
58
+ return !Recover.show();
47
59
  },
48
60
  },
49
61
  'recover-repeat-password': {
@@ -51,23 +63,19 @@ const Recover = {
51
63
  rules: [{ type: 'isEmpty' }, { type: 'passwordMismatch', options: `recover-password` }],
52
64
  show: () => mode === 'change-password',
53
65
  disable: function () {
54
- return !this.show();
66
+ return !Recover.show();
55
67
  },
56
68
  },
57
69
  };
58
-
59
70
  if (recoverToken) {
60
71
  mode = 'change-password';
61
72
  }
62
-
63
73
  setTimeout(async () => {
64
74
  if (user && user.email) {
65
75
  s(`.recover-email`).value = user.role === 'guest' ? '' : user.email;
66
76
  if (user.emailConfirmed) s(`.recover-email`).setAttribute('disabled', '');
67
77
  }
68
-
69
78
  const validators = await Validator.instance(formData);
70
-
71
79
  EventsUI.onClick(`.btn-recover`, async (e) => {
72
80
  e.preventDefault();
73
81
  s(`.recover-resend-btn-container`).classList.add('hide');
@@ -86,7 +94,9 @@ const Recover = {
86
94
  });
87
95
  NotificationManager.Push({
88
96
  html:
89
- result.status === 'error' ? result.message : Translate.Render(`${result.status}-recover-verify-email`),
97
+ result.status === 'error'
98
+ ? result.message
99
+ : Translate.instance(`${result.status}-recover-verify-email`),
90
100
  status: result.status,
91
101
  });
92
102
  if (result.status === 'success') {
@@ -99,7 +109,7 @@ const Recover = {
99
109
  const result = await UserService.put({ id: `recover/${recoverToken}`, body });
100
110
  NotificationManager.Push({
101
111
  html:
102
- typeof result.data === 'string' ? result.data : Translate.Render(`${result.status}-recover-password`),
112
+ typeof result.data === 'string' ? result.data : Translate.instance(`${result.status}-recover-password`),
103
113
  status: result.status,
104
114
  });
105
115
  if (result.status === 'success') {
@@ -112,7 +122,7 @@ const Recover = {
112
122
  s(`.input-container-recover-repeat-password`).classList.add('hide');
113
123
  s(`.btn-recover-log-in`).classList.remove('hide');
114
124
  }
115
- this.Trigger({ user: result.data });
125
+ Recover.Trigger({ user: result.data });
116
126
  }
117
127
  break;
118
128
  }
@@ -128,80 +138,74 @@ const Recover = {
128
138
  };
129
139
  });
130
140
  return html`
131
- ${await BtnIcon.Render({
141
+ ${await BtnIcon.instance({
132
142
  class: 'in section-mp form-button btn-recover-log-in hide',
133
- label: Translate.Render('log-in'),
143
+ label: Translate.instance('log-in'),
134
144
  type: 'button',
135
145
  })}
136
146
  <form class="in">
137
147
  <div class="in">
138
- ${await Input.Render({
148
+ ${await Input.instance({
139
149
  id: `recover-username`,
140
150
  type: 'text',
141
- label: html`<i class="fa-solid fa-pen-to-square"></i> ${Translate.Render('username')}`,
142
- containerClass: `inl section-mp width-mini-box input-container ${
143
- formData[`recover-username`].show() ? '' : 'hide'
144
- }`,
151
+ label: html`<i class="fa-solid fa-pen-to-square"></i> ${Translate.instance('username')}`,
152
+ containerClass: `inl section-mp width-mini-box input-container ${formData[`recover-username`].show() ? '' : 'hide'}`,
145
153
  placeholder: true,
146
154
  })}
147
155
  </div>
148
156
  <div class="in">
149
- ${await Input.Render({
157
+ ${await Input.instance({
150
158
  id: `recover-email`,
151
159
  type: 'email',
152
- label: html`<i class="fa-solid fa-envelope"></i> ${Translate.Render('email')}`,
153
- containerClass: `inl section-mp width-mini-box input-container ${
154
- formData[`recover-email`].show() ? '' : 'hide'
155
- }`,
160
+ label: html`<i class="fa-solid fa-envelope"></i> ${Translate.instance('email')}`,
161
+ containerClass: `inl section-mp width-mini-box input-container ${formData[`recover-email`].show() ? '' : 'hide'}`,
156
162
  placeholder: true,
157
163
  autocomplete: 'email',
158
164
  })}
159
165
  </div>
160
166
  <div class="in">
161
- ${await Input.Render({
167
+ ${await Input.instance({
162
168
  id: `recover-password`,
163
169
  type: 'password',
164
170
  autocomplete: 'new-password',
165
- label: html`<i class="fa-solid fa-lock"></i> ${Translate.Render('password')}`,
166
- containerClass: `inl section-mp width-mini-box input-container ${
167
- formData[`recover-password`].show() ? '' : 'hide'
168
- }`,
171
+ label: html`<i class="fa-solid fa-lock"></i> ${Translate.instance('password')}`,
172
+ containerClass: `inl section-mp width-mini-box input-container ${formData[`recover-password`].show() ? '' : 'hide'}`,
169
173
  placeholder: true,
170
174
  })}
171
175
  </div>
172
176
  <div class="in">
173
- ${await Input.Render({
177
+ ${await Input.instance({
174
178
  id: `recover-repeat-password`,
175
179
  type: 'password',
176
180
  autocomplete: 'new-password',
177
- label: html`<i class="fa-solid fa-lock"></i> ${Translate.Render('repeat')} ${Translate.Render('password')}`,
178
- containerClass: `inl section-mp width-mini-box input-container ${
179
- formData[`recover-repeat-password`].show() ? '' : 'hide'
180
- }`,
181
+ label: html`<i class="fa-solid fa-lock"></i> ${Translate.instance('repeat')}
182
+ ${Translate.instance('password')}`,
183
+ containerClass: `inl section-mp width-mini-box input-container ${formData[`recover-repeat-password`].show() ? '' : 'hide'}`,
181
184
  placeholder: true,
182
185
  })}
183
186
  </div>
184
187
  ${options?.bottomRender ? await options.bottomRender() : ``}
185
188
  <div class="in recover-send-btn-container">
186
- ${await BtnIcon.Render({
189
+ ${await BtnIcon.instance({
187
190
  class: 'in section-mp form-button btn-recover',
188
- label: Translate.Render(mode === 'recover-verify-email' ? 'send-recover-verify-email' : 'change-password'),
191
+ label: Translate.instance(
192
+ mode === 'recover-verify-email' ? 'send-recover-verify-email' : 'change-password',
193
+ ),
189
194
  type: 'button',
190
195
  })}
191
196
  </div>
192
197
  <div class="in recover-resend-btn-container hide">
193
198
  <div class="in section-mp form-button" style="color: #ed9d0f">
194
- <i class="fa-solid fa-triangle-exclamation"></i> ${Translate.Render('15-min-valid-recover-email')}
199
+ <i class="fa-solid fa-triangle-exclamation"></i> ${Translate.instance('15-min-valid-recover-email')}
195
200
  </div>
196
- ${await BtnIcon.Render({
201
+ ${await BtnIcon.instance({
197
202
  class: 'in section-mp form-button btn-recover-resend',
198
- label: html`${Translate.Render('resend')} ${Translate.Render('recover-verify-email')}`,
203
+ label: html`${Translate.instance('resend')} ${Translate.instance('recover-verify-email')}`,
199
204
  type: 'submit',
200
205
  })}
201
206
  </div>
202
207
  </form>
203
208
  `;
204
- },
205
- };
206
-
209
+ }
210
+ }
207
211
  export { Recover };
@@ -1,35 +1,41 @@
1
1
  import { newInstance } from './CommonJs.js';
2
+ import {
3
+ ResponsiveEventType,
4
+ responsiveChangeEvents,
5
+ responsiveOrientationEvents,
6
+ responsiveOrientationSettledEvents,
7
+ responsiveSettledEvents,
8
+ } from './ClientEvents.js';
2
9
  import { loggerFactory } from './Logger.js';
3
10
  import { getResponsiveData } from './VanillaJs.js';
4
-
5
11
  const logger = loggerFactory(import.meta);
6
-
7
- const Responsive = {
8
- Data: {},
9
- Event: {},
10
- DelayEvent: {},
11
- Observer: ResizeObserver,
12
- getResponsiveData: function () {
13
- return newInstance(this.Data);
14
- },
15
- getResponsiveDataAmplitude: function (options) {
12
+ class Responsive {
13
+ static Data = {};
14
+ static Event = {};
15
+ static DelayEvent = {};
16
+ static Observer = ResizeObserver;
17
+ static getResponsiveData() {
18
+ return newInstance(Responsive.Data);
19
+ }
20
+ static getResponsiveDataAmplitude(options) {
16
21
  const { dimAmplitude } = options;
17
- const ResponsiveDataAmplitude = newInstance(this.Data);
22
+ const ResponsiveDataAmplitude = newInstance(Responsive.Data);
18
23
  ResponsiveDataAmplitude.minValue = ResponsiveDataAmplitude.minValue * dimAmplitude;
19
24
  ResponsiveDataAmplitude.maxValue = ResponsiveDataAmplitude.maxValue * dimAmplitude;
20
25
  ResponsiveDataAmplitude.width = ResponsiveDataAmplitude.width * dimAmplitude;
21
26
  ResponsiveDataAmplitude.height = ResponsiveDataAmplitude.height * dimAmplitude;
22
27
  return ResponsiveDataAmplitude;
23
- },
24
- resizeCallback: function (force) {
28
+ }
29
+ static resizeCallback(force) {
25
30
  const Data = getResponsiveData();
26
31
  if (force === true || Data.minValue !== Responsive.Data.minValue || Data.maxValue !== Responsive.Data.maxValue) {
27
32
  Responsive.Data = Data;
33
+ Responsive.emitChanged(Data);
28
34
  Responsive.triggerEvents();
29
35
  }
30
- },
31
- resize: 0,
32
- Init: async function () {
36
+ }
37
+ static resize = 0;
38
+ static async instance() {
33
39
  Responsive.resizeCallback();
34
40
  window.onresize = (e, force) => {
35
41
  Responsive.resize++;
@@ -39,6 +45,7 @@ const Responsive = {
39
45
  if (resize === Responsive.resize) {
40
46
  Responsive.resizeCallback(force);
41
47
  Responsive.resize = 0;
48
+ Responsive.emitSettled(Responsive.Data);
42
49
  for (const event of Object.keys(Responsive.DelayEvent)) Responsive.DelayEvent[event]();
43
50
  }
44
51
  }, 750);
@@ -46,7 +53,6 @@ const Responsive = {
46
53
  // alternative option
47
54
  // this.Observer = new ResizeObserver(this.resizeCallback);
48
55
  // this.Observer.observe(document.documentElement);
49
-
50
56
  // Check if screen.orientation is available before adding event listener
51
57
  if (
52
58
  typeof screen !== 'undefined' &&
@@ -62,7 +68,6 @@ const Responsive = {
62
68
  });
63
69
  }
64
70
  Responsive.matchMediaOrientationInstance = matchMedia('screen and (orientation:portrait)');
65
-
66
71
  Responsive.matchMediaOrientationInstance.onchange = (e) => {
67
72
  console.log('orientation change', Responsive.matchMediaOrientationInstance.matches ? 'portrait' : 'landscape');
68
73
  // though beware square will be marked as landscape here,
@@ -71,20 +76,71 @@ const Responsive = {
71
76
  setTimeout(() => window.onresize({}, true));
72
77
  Responsive.triggerEventsOrientation();
73
78
  };
74
- },
75
- triggerEventsOrientation: function () {
76
- for (const event of Object.keys(this.orientationEvent)) this.orientationEvent[event]();
79
+ }
80
+ static onChanged(listener, options = {}) {
81
+ if (options.key) Responsive.Event[options.key] = listener;
82
+ return responsiveChangeEvents.on(ResponsiveEventType.changed, listener, options);
83
+ }
84
+ static offChanged(key) {
85
+ delete Responsive.Event[key];
86
+ return responsiveChangeEvents.off(key);
87
+ }
88
+ static hasChangedListener(key) {
89
+ return responsiveChangeEvents.has(key) || Boolean(Responsive.Event[key]);
90
+ }
91
+ static onSettled(listener, options = {}) {
92
+ if (options.key) Responsive.DelayEvent[options.key] = listener;
93
+ return responsiveSettledEvents.on(ResponsiveEventType.settled, listener, options);
94
+ }
95
+ static offSettled(key) {
96
+ delete Responsive.DelayEvent[key];
97
+ return responsiveSettledEvents.off(key);
98
+ }
99
+ static onOrientationChanged(listener, options = {}) {
100
+ if (options.key) Responsive.orientationEvent[options.key] = listener;
101
+ return responsiveOrientationEvents.on(ResponsiveEventType.orientationChanged, listener, options);
102
+ }
103
+ static offOrientationChanged(key) {
104
+ delete Responsive.orientationEvent[key];
105
+ return responsiveOrientationEvents.off(key);
106
+ }
107
+ static onOrientationSettled(listener, options = {}) {
108
+ if (options.key) Responsive.orientationDelayEvent[options.key] = listener;
109
+ return responsiveOrientationSettledEvents.on(ResponsiveEventType.orientationSettled, listener, options);
110
+ }
111
+ static offOrientationSettled(key) {
112
+ delete Responsive.orientationDelayEvent[key];
113
+ return responsiveOrientationSettledEvents.off(key);
114
+ }
115
+ static async emitChanged(detail = Responsive.Data) {
116
+ await responsiveChangeEvents.emit(ResponsiveEventType.changed, detail);
117
+ }
118
+ static async emitSettled(detail = Responsive.Data) {
119
+ await responsiveSettledEvents.emit(ResponsiveEventType.settled, detail);
120
+ }
121
+ static async emitOrientationChanged(detail = Responsive.Data) {
122
+ await responsiveOrientationEvents.emit(ResponsiveEventType.orientationChanged, detail);
123
+ }
124
+ static async emitOrientationSettled(detail = Responsive.Data) {
125
+ await responsiveOrientationSettledEvents.emit(ResponsiveEventType.orientationSettled, detail);
126
+ }
127
+ static triggerChanged(keyEvent) {
128
+ return Responsive.triggerEvents(keyEvent);
129
+ }
130
+ static triggerEventsOrientation() {
131
+ Responsive.emitOrientationChanged(Responsive.Data);
132
+ for (const event of Object.keys(Responsive.orientationEvent)) Responsive.orientationEvent[event]();
77
133
  setTimeout(() => {
78
134
  window.onresize();
79
- for (const event of Object.keys(this.orientationDelayEvent)) this.orientationDelayEvent[event]();
135
+ Responsive.emitOrientationSettled(Responsive.Data);
136
+ for (const event of Object.keys(Responsive.orientationDelayEvent)) Responsive.orientationDelayEvent[event]();
80
137
  }, 1500);
81
- },
82
- triggerEvents: function (keyEvent) {
83
- if (keyEvent) return this.Event[keyEvent]();
84
- return Object.keys(this.Event).map((key) => this.Event[key]());
85
- },
86
- orientationEvent: {},
87
- orientationDelayEvent: {},
88
- };
89
-
138
+ }
139
+ static triggerEvents(keyEvent) {
140
+ if (keyEvent) return Responsive.Event[keyEvent]();
141
+ return Object.keys(Responsive.Event).map((key) => Responsive.Event[key]());
142
+ }
143
+ static orientationEvent = {};
144
+ static orientationDelayEvent = {};
145
+ }
90
146
  export { Responsive };
@@ -1,12 +1,11 @@
1
1
  import { getId, newInstance } from './CommonJs.js';
2
2
  import { Modal } from './Modal.js';
3
3
  import { s } from './VanillaJs.js';
4
-
5
- const RichText = {
6
- Tokens: {},
7
- Render: async function (options = { id: '', parentIdModal: '' }) {
8
- const id = options?.id ? options.id : getId(this.Tokens, 'rich-text-');
9
- this.Tokens[id] = {};
4
+ class RichText {
5
+ static Tokens = {};
6
+ static async instance(options = { id: '', parentIdModal: '' }) {
7
+ const id = options?.id ? options.id : getId(RichText.Tokens, 'rich-text-');
8
+ RichText.Tokens[id] = {};
10
9
  setTimeout(() => {
11
10
  const easyMDE = new EasyMDE({
12
11
  element: s(`.${id}`),
@@ -23,20 +22,12 @@ const RichText = {
23
22
  }
24
23
  },
25
24
  });
26
- this.Tokens[id].easyMDE = easyMDE;
25
+ RichText.Tokens[id].easyMDE = easyMDE;
27
26
  // easyMDE.value();
28
27
  // easyMDE.value(val);
29
28
  });
30
- return html` <style>
31
- .md-container {
32
- background: white;
33
- }
34
- .md-container button {
35
- color: black;
36
- }
37
- </style>
29
+ return html` <style></style>
38
30
  <div class="in md-container"><textarea class="${id}"></textarea></div>`;
39
- },
40
- };
41
-
31
+ }
32
+ }
42
33
  export { RichText };