joopjs 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/CHANGELOG.md +678 -0
  2. package/README.md +583 -0
  3. package/dist/a11y.service-C-DQQfgO.d.mts +143 -0
  4. package/dist/a11y.service-CauEJrJe.d.ts +143 -0
  5. package/dist/adapters-B6slG6hQ.d.mts +84 -0
  6. package/dist/adapters-B6slG6hQ.d.ts +84 -0
  7. package/dist/aes.service-CkoupAww.d.mts +95 -0
  8. package/dist/aes.service-CkoupAww.d.ts +95 -0
  9. package/dist/ai/index.d.mts +99 -0
  10. package/dist/ai/index.d.ts +99 -0
  11. package/dist/ai/index.js +307 -0
  12. package/dist/ai/index.js.map +1 -0
  13. package/dist/ai/index.mjs +304 -0
  14. package/dist/ai/index.mjs.map +1 -0
  15. package/dist/analytics/index.d.mts +42 -0
  16. package/dist/analytics/index.d.ts +42 -0
  17. package/dist/analytics/index.js +139 -0
  18. package/dist/analytics/index.js.map +1 -0
  19. package/dist/analytics/index.mjs +136 -0
  20. package/dist/analytics/index.mjs.map +1 -0
  21. package/dist/angular/index.d.mts +148 -0
  22. package/dist/angular/index.d.ts +148 -0
  23. package/dist/angular/index.js +122 -0
  24. package/dist/angular/index.js.map +1 -0
  25. package/dist/angular/index.mjs +101 -0
  26. package/dist/angular/index.mjs.map +1 -0
  27. package/dist/api/index.d.mts +128 -0
  28. package/dist/api/index.d.ts +128 -0
  29. package/dist/api/index.js +1358 -0
  30. package/dist/api/index.js.map +1 -0
  31. package/dist/api/index.mjs +1332 -0
  32. package/dist/api/index.mjs.map +1 -0
  33. package/dist/auth/index.d.mts +105 -0
  34. package/dist/auth/index.d.ts +105 -0
  35. package/dist/auth/index.js +989 -0
  36. package/dist/auth/index.js.map +1 -0
  37. package/dist/auth/index.mjs +979 -0
  38. package/dist/auth/index.mjs.map +1 -0
  39. package/dist/auth.service-DNVB-L4U.d.mts +16 -0
  40. package/dist/auth.service-PjUUSUIt.d.ts +16 -0
  41. package/dist/banking/index.d.mts +1530 -0
  42. package/dist/banking/index.d.ts +1530 -0
  43. package/dist/banking/index.js +4739 -0
  44. package/dist/banking/index.js.map +1 -0
  45. package/dist/banking/index.mjs +4661 -0
  46. package/dist/banking/index.mjs.map +1 -0
  47. package/dist/cache/index.d.mts +40 -0
  48. package/dist/cache/index.d.ts +40 -0
  49. package/dist/cache/index.js +174 -0
  50. package/dist/cache/index.js.map +1 -0
  51. package/dist/cache/index.mjs +172 -0
  52. package/dist/cache/index.mjs.map +1 -0
  53. package/dist/client-profile.service-BuPeXVp5.d.mts +28 -0
  54. package/dist/client-profile.service-D5bRRYQp.d.ts +28 -0
  55. package/dist/config.models-Cqg04fAQ.d.mts +84 -0
  56. package/dist/config.models-Cqg04fAQ.d.ts +84 -0
  57. package/dist/config.service-CrCvI-JS.d.ts +31 -0
  58. package/dist/config.service-Cz4QQLlf.d.mts +31 -0
  59. package/dist/core/index.d.mts +4 -0
  60. package/dist/core/index.d.ts +4 -0
  61. package/dist/core/index.js +631 -0
  62. package/dist/core/index.js.map +1 -0
  63. package/dist/core/index.mjs +619 -0
  64. package/dist/core/index.mjs.map +1 -0
  65. package/dist/crypto-utils-DriNhLdx.d.mts +30 -0
  66. package/dist/crypto-utils-DriNhLdx.d.ts +30 -0
  67. package/dist/data-storage.service-DT6xaTxE.d.ts +51 -0
  68. package/dist/data-storage.service-LvhGRCmw.d.mts +51 -0
  69. package/dist/deeplink/index.d.mts +39 -0
  70. package/dist/deeplink/index.d.ts +39 -0
  71. package/dist/deeplink/index.js +268 -0
  72. package/dist/deeplink/index.js.map +1 -0
  73. package/dist/deeplink/index.mjs +265 -0
  74. package/dist/deeplink/index.mjs.map +1 -0
  75. package/dist/deeplink.service-Ctd5u243.d.mts +35 -0
  76. package/dist/deeplink.service-uUuTnY9_.d.ts +35 -0
  77. package/dist/dev/index.d.mts +20 -0
  78. package/dist/dev/index.d.ts +20 -0
  79. package/dist/dev/index.js +51 -0
  80. package/dist/dev/index.js.map +1 -0
  81. package/dist/dev/index.mjs +49 -0
  82. package/dist/dev/index.mjs.map +1 -0
  83. package/dist/device/index.d.mts +108 -0
  84. package/dist/device/index.d.ts +108 -0
  85. package/dist/device/index.js +960 -0
  86. package/dist/device/index.js.map +1 -0
  87. package/dist/device/index.mjs +951 -0
  88. package/dist/device/index.mjs.map +1 -0
  89. package/dist/differential-privacy-BcAv1G80.d.mts +210 -0
  90. package/dist/differential-privacy-C8mAUjZr.d.ts +210 -0
  91. package/dist/encryption/index.d.mts +75 -0
  92. package/dist/encryption/index.d.ts +75 -0
  93. package/dist/encryption/index.js +605 -0
  94. package/dist/encryption/index.js.map +1 -0
  95. package/dist/encryption/index.mjs +598 -0
  96. package/dist/encryption/index.mjs.map +1 -0
  97. package/dist/form-validator-3tkmzr_o.d.mts +72 -0
  98. package/dist/form-validator-3tkmzr_o.d.ts +72 -0
  99. package/dist/forms/index.d.mts +59 -0
  100. package/dist/forms/index.d.ts +59 -0
  101. package/dist/forms/index.js +446 -0
  102. package/dist/forms/index.js.map +1 -0
  103. package/dist/forms/index.mjs +442 -0
  104. package/dist/forms/index.mjs.map +1 -0
  105. package/dist/i18n/index.d.mts +37 -0
  106. package/dist/i18n/index.d.ts +37 -0
  107. package/dist/i18n/index.js +147 -0
  108. package/dist/i18n/index.js.map +1 -0
  109. package/dist/i18n/index.mjs +145 -0
  110. package/dist/i18n/index.mjs.map +1 -0
  111. package/dist/idempotency.service-_6LqhivP.d.mts +372 -0
  112. package/dist/idempotency.service-eOKoISRD.d.ts +372 -0
  113. package/dist/index-B_ksKpS1.d.mts +202 -0
  114. package/dist/index-CqDKWTUP.d.mts +28 -0
  115. package/dist/index-CqDKWTUP.d.ts +28 -0
  116. package/dist/index-DFqEoX_l.d.ts +202 -0
  117. package/dist/index-Dz0gOur2.d.mts +36 -0
  118. package/dist/index-Dz0gOur2.d.ts +36 -0
  119. package/dist/index.d.mts +1336 -0
  120. package/dist/index.d.ts +1336 -0
  121. package/dist/index.js +19464 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/index.mjs +19155 -0
  124. package/dist/index.mjs.map +1 -0
  125. package/dist/india/index.d.mts +75 -0
  126. package/dist/india/index.d.ts +75 -0
  127. package/dist/india/index.js +325 -0
  128. package/dist/india/index.js.map +1 -0
  129. package/dist/india/index.mjs +303 -0
  130. package/dist/india/index.mjs.map +1 -0
  131. package/dist/joop-Bx7Iwj5p.d.mts +155 -0
  132. package/dist/joop-CA3DMeOO.d.ts +155 -0
  133. package/dist/native-bridge/index.d.mts +27 -0
  134. package/dist/native-bridge/index.d.ts +27 -0
  135. package/dist/native-bridge/index.js +98 -0
  136. package/dist/native-bridge/index.js.map +1 -0
  137. package/dist/native-bridge/index.mjs +96 -0
  138. package/dist/native-bridge/index.mjs.map +1 -0
  139. package/dist/network/index.d.mts +85 -0
  140. package/dist/network/index.d.ts +85 -0
  141. package/dist/network/index.js +454 -0
  142. package/dist/network/index.js.map +1 -0
  143. package/dist/network/index.mjs +451 -0
  144. package/dist/network/index.mjs.map +1 -0
  145. package/dist/network-monitor-BIwPSXme.d.mts +179 -0
  146. package/dist/network-monitor-Bqp2hvZr.d.ts +179 -0
  147. package/dist/notification.service-Dm4fvfZf.d.mts +25 -0
  148. package/dist/notification.service-tEMKatWJ.d.ts +25 -0
  149. package/dist/observability/index.d.mts +179 -0
  150. package/dist/observability/index.d.ts +179 -0
  151. package/dist/observability/index.js +559 -0
  152. package/dist/observability/index.js.map +1 -0
  153. package/dist/observability/index.mjs +552 -0
  154. package/dist/observability/index.mjs.map +1 -0
  155. package/dist/oidc-client-DIJcClmB.d.mts +190 -0
  156. package/dist/oidc-client-DxhyE59t.d.ts +190 -0
  157. package/dist/platform/index.d.mts +73 -0
  158. package/dist/platform/index.d.ts +73 -0
  159. package/dist/platform/index.js +127 -0
  160. package/dist/platform/index.js.map +1 -0
  161. package/dist/platform/index.mjs +125 -0
  162. package/dist/platform/index.mjs.map +1 -0
  163. package/dist/pwa/index.d.mts +31 -0
  164. package/dist/pwa/index.d.ts +31 -0
  165. package/dist/pwa/index.js +247 -0
  166. package/dist/pwa/index.js.map +1 -0
  167. package/dist/pwa/index.mjs +244 -0
  168. package/dist/pwa/index.mjs.map +1 -0
  169. package/dist/react/index.d.mts +133 -0
  170. package/dist/react/index.d.ts +133 -0
  171. package/dist/react/index.js +632 -0
  172. package/dist/react/index.js.map +1 -0
  173. package/dist/react/index.mjs +630 -0
  174. package/dist/react/index.mjs.map +1 -0
  175. package/dist/router/index.d.mts +39 -0
  176. package/dist/router/index.d.ts +39 -0
  177. package/dist/router/index.js +168 -0
  178. package/dist/router/index.js.map +1 -0
  179. package/dist/router/index.mjs +166 -0
  180. package/dist/router/index.mjs.map +1 -0
  181. package/dist/security/index.d.mts +206 -0
  182. package/dist/security/index.d.ts +206 -0
  183. package/dist/security/index.js +1297 -0
  184. package/dist/security/index.js.map +1 -0
  185. package/dist/security/index.mjs +1285 -0
  186. package/dist/security/index.mjs.map +1 -0
  187. package/dist/session/index.d.mts +115 -0
  188. package/dist/session/index.d.ts +115 -0
  189. package/dist/session/index.js +297 -0
  190. package/dist/session/index.js.map +1 -0
  191. package/dist/session/index.mjs +292 -0
  192. package/dist/session/index.mjs.map +1 -0
  193. package/dist/state/index.d.mts +43 -0
  194. package/dist/state/index.d.ts +43 -0
  195. package/dist/state/index.js +156 -0
  196. package/dist/state/index.js.map +1 -0
  197. package/dist/state/index.mjs +152 -0
  198. package/dist/state/index.mjs.map +1 -0
  199. package/dist/statement-parser-BHQtXwCM.d.ts +260 -0
  200. package/dist/statement-parser-C2qNmb49.d.mts +260 -0
  201. package/dist/storage/index.d.mts +40 -0
  202. package/dist/storage/index.d.ts +40 -0
  203. package/dist/storage/index.js +256 -0
  204. package/dist/storage/index.js.map +1 -0
  205. package/dist/storage/index.mjs +252 -0
  206. package/dist/storage/index.mjs.map +1 -0
  207. package/dist/sync/index.d.mts +69 -0
  208. package/dist/sync/index.d.ts +69 -0
  209. package/dist/sync/index.js +330 -0
  210. package/dist/sync/index.js.map +1 -0
  211. package/dist/sync/index.mjs +323 -0
  212. package/dist/sync/index.mjs.map +1 -0
  213. package/dist/sync-engine-DCIMRG5s.d.ts +61 -0
  214. package/dist/sync-engine-DZqyKHkK.d.mts +61 -0
  215. package/dist/theme/index.d.mts +53 -0
  216. package/dist/theme/index.d.ts +53 -0
  217. package/dist/theme/index.js +169 -0
  218. package/dist/theme/index.js.map +1 -0
  219. package/dist/theme/index.mjs +167 -0
  220. package/dist/theme/index.mjs.map +1 -0
  221. package/dist/ui/index.d.mts +66 -0
  222. package/dist/ui/index.d.ts +66 -0
  223. package/dist/ui/index.js +811 -0
  224. package/dist/ui/index.js.map +1 -0
  225. package/dist/ui/index.mjs +803 -0
  226. package/dist/ui/index.mjs.map +1 -0
  227. package/dist/utilities/index.d.mts +199 -0
  228. package/dist/utilities/index.d.ts +199 -0
  229. package/dist/utilities/index.js +1991 -0
  230. package/dist/utilities/index.js.map +1 -0
  231. package/dist/utilities/index.mjs +1923 -0
  232. package/dist/utilities/index.mjs.map +1 -0
  233. package/dist/validation/index.d.mts +60 -0
  234. package/dist/validation/index.d.ts +60 -0
  235. package/dist/validation/index.js +460 -0
  236. package/dist/validation/index.js.map +1 -0
  237. package/dist/validation/index.mjs +455 -0
  238. package/dist/validation/index.mjs.map +1 -0
  239. package/dist/vue/index.d.mts +135 -0
  240. package/dist/vue/index.d.ts +135 -0
  241. package/dist/vue/index.js +621 -0
  242. package/dist/vue/index.js.map +1 -0
  243. package/dist/vue/index.mjs +619 -0
  244. package/dist/vue/index.mjs.map +1 -0
  245. package/dist/watermark.service-Detur5tq.d.ts +235 -0
  246. package/dist/watermark.service-QNegMeQZ.d.mts +235 -0
  247. package/dist/workers/index.d.mts +42 -0
  248. package/dist/workers/index.d.ts +42 -0
  249. package/dist/workers/index.js +359 -0
  250. package/dist/workers/index.js.map +1 -0
  251. package/dist/workers/index.mjs +356 -0
  252. package/dist/workers/index.mjs.map +1 -0
  253. package/dist/workflow/index.d.mts +99 -0
  254. package/dist/workflow/index.d.ts +99 -0
  255. package/dist/workflow/index.js +282 -0
  256. package/dist/workflow/index.js.map +1 -0
  257. package/dist/workflow/index.mjs +279 -0
  258. package/dist/workflow/index.mjs.map +1 -0
  259. package/package.json +226 -0
@@ -0,0 +1,1923 @@
1
+ // src/utilities/common.utility.ts
2
+ function removeTrailingSlash(url) {
3
+ return url?.endsWith("/") ? url.slice(0, -1) : url;
4
+ }
5
+ function objectToQueryString(obj) {
6
+ return Object.entries(obj).filter(([, v]) => v !== void 0 && v !== null).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
7
+ }
8
+ function deepClone(obj) {
9
+ return JSON.parse(JSON.stringify(obj));
10
+ }
11
+ function isPlainObject(value) {
12
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13
+ }
14
+ function randomString(length, charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") {
15
+ let result = "";
16
+ for (let i = 0; i < length; i++) {
17
+ result += charset[Math.floor(Math.random() * charset.length)];
18
+ }
19
+ return result;
20
+ }
21
+ function stringToHex(input) {
22
+ let result = "";
23
+ for (let i = 0; i < input.length; i++) {
24
+ result += input.charCodeAt(i).toString(16).slice(-4);
25
+ }
26
+ return result;
27
+ }
28
+ function bufferToHex(buffer) {
29
+ return [...new Uint8Array(buffer)].map((b) => b.toString(16).padStart(2, "0")).join("");
30
+ }
31
+ function flattenObject(obj, prefix = "") {
32
+ const result = {};
33
+ for (const [key, value] of Object.entries(obj)) {
34
+ const fullKey = prefix ? `${prefix}.${key}` : key;
35
+ if (isPlainObject(value)) {
36
+ Object.assign(result, flattenObject(value, fullKey));
37
+ } else {
38
+ result[fullKey] = String(value);
39
+ }
40
+ }
41
+ return result;
42
+ }
43
+
44
+ // src/utilities/device.utility.ts
45
+ function getDeviceInfo() {
46
+ const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
47
+ const platform = typeof navigator !== "undefined" ? navigator.platform ?? "" : "";
48
+ const language = typeof navigator !== "undefined" ? navigator.language ?? "en" : "en";
49
+ const isIOS = /iphone|ipad|ipod/i.test(ua);
50
+ const isAndroid = /android/i.test(ua);
51
+ const isMobile = isIOS || isAndroid || /mobi/i.test(ua);
52
+ return {
53
+ userAgent: ua,
54
+ platform,
55
+ isIOS,
56
+ isAndroid,
57
+ isMobile,
58
+ isDesktop: !isMobile,
59
+ language
60
+ };
61
+ }
62
+ function isBrowser() {
63
+ return typeof window !== "undefined" && typeof document !== "undefined";
64
+ }
65
+ function isSSR() {
66
+ return !isBrowser();
67
+ }
68
+
69
+ // src/utilities/sanitizer.utility.ts
70
+ var SQL_KEYWORDS = /\b(SELECT|INSERT|UPDATE|DELETE|DROP|TRUNCATE|ALTER|EXEC|UNION|SCRIPT)\b/gi;
71
+ function escapeHtml(input) {
72
+ return input.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
73
+ }
74
+ function stripHtml(input) {
75
+ return input.replace(/<[^>]*>/g, "");
76
+ }
77
+ function sanitizeInput(input) {
78
+ return escapeHtml(input.trim());
79
+ }
80
+ function stripSqlKeywords(input) {
81
+ return input.replace(/['";\\]/g, "").replace(SQL_KEYWORDS, "");
82
+ }
83
+ function sanitizeFileName(name) {
84
+ return name.replace(/[^a-zA-Z0-9._\-]/g, "_").replace(/_{2,}/g, "_");
85
+ }
86
+ function isValidUrl(url) {
87
+ try {
88
+ const { protocol } = new URL(url);
89
+ return protocol === "http:" || protocol === "https:";
90
+ } catch {
91
+ return false;
92
+ }
93
+ }
94
+ function isValidEmail(email) {
95
+ return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(email);
96
+ }
97
+ function isValidPhone(phone) {
98
+ return /^\+?[\d\s\-()]{7,20}$/.test(phone);
99
+ }
100
+ function isAlphanumeric(value) {
101
+ return /^[a-zA-Z0-9]+$/.test(value);
102
+ }
103
+ function truncate(value, maxLength, suffix = "\u2026") {
104
+ return value.length > maxLength ? value.slice(0, maxLength - suffix.length) + suffix : value;
105
+ }
106
+
107
+ // src/utilities/connection.utility.ts
108
+ function flattenObj(obj, prefix = "", result = {}) {
109
+ for (const key of Object.keys(obj)) {
110
+ const fullKey = prefix ? `${prefix}.${key}` : key;
111
+ const val = obj[key];
112
+ if (val !== null && typeof val === "object" && !Array.isArray(val)) {
113
+ flattenObj(val, fullKey, result);
114
+ } else {
115
+ result[fullKey] = val == null ? "" : String(val);
116
+ }
117
+ }
118
+ return result;
119
+ }
120
+ function buildQueryString(params, encode = true) {
121
+ const flat = flattenObj(params);
122
+ return Object.entries(flat).filter(([, v]) => v !== "").map(([k, v]) => `${encode ? encodeURIComponent(k) : k}=${encode ? encodeURIComponent(v) : v}`).join("&");
123
+ }
124
+ function buildFormData(params, files) {
125
+ const flat = flattenObj(params);
126
+ const fd = new FormData();
127
+ for (const [k, v] of Object.entries(flat)) {
128
+ fd.append(k, v);
129
+ }
130
+ if (files) {
131
+ for (const { field, file, name } of files) {
132
+ if (name) {
133
+ fd.append(field, file, name);
134
+ } else {
135
+ fd.append(field, file);
136
+ }
137
+ }
138
+ }
139
+ return fd;
140
+ }
141
+ function urlEncode(params) {
142
+ return Object.entries(params).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
143
+ }
144
+ function getRandomNum(min, max) {
145
+ return Math.floor(Math.random() * (max - min + 1)) + min;
146
+ }
147
+
148
+ // src/events/index.ts
149
+ var JoopSubject = class {
150
+ _listeners = [];
151
+ subscribe(listener) {
152
+ this._listeners.push(listener);
153
+ return () => {
154
+ this._listeners = this._listeners.filter((l) => l !== listener);
155
+ };
156
+ }
157
+ next(value) {
158
+ for (const listener of this._listeners) {
159
+ listener(value);
160
+ }
161
+ }
162
+ asObservable() {
163
+ return new JoopObservable((listener) => this.subscribe(listener));
164
+ }
165
+ };
166
+ var JoopBehaviorSubject = class extends JoopSubject {
167
+ _value;
168
+ constructor(initialValue) {
169
+ super();
170
+ this._value = initialValue;
171
+ }
172
+ getValue() {
173
+ return this._value;
174
+ }
175
+ next(value) {
176
+ this._value = value;
177
+ super.next(value);
178
+ }
179
+ subscribe(listener) {
180
+ listener(this._value);
181
+ return super.subscribe(listener);
182
+ }
183
+ asObservable() {
184
+ return new JoopObservable((listener) => this.subscribe(listener));
185
+ }
186
+ };
187
+ var JoopObservable = class {
188
+ constructor(_subscribeFn) {
189
+ this._subscribeFn = _subscribeFn;
190
+ }
191
+ _subscribeFn;
192
+ subscribe(listener) {
193
+ return this._subscribeFn(listener);
194
+ }
195
+ /** Returns the current value without subscribing (only meaningful for BehaviorSubject-backed observables). */
196
+ getOnce() {
197
+ let result;
198
+ const unsub = this.subscribe((v) => {
199
+ result = v;
200
+ });
201
+ unsub();
202
+ return result;
203
+ }
204
+ };
205
+
206
+ // src/utilities/clipboard.utility.ts
207
+ var JoopClipboardService = class {
208
+ _events$ = new JoopSubject();
209
+ /** Copy text to clipboard. Falls back to execCommand for older browsers. */
210
+ async copy(text) {
211
+ try {
212
+ if (navigator?.clipboard?.writeText) {
213
+ await navigator.clipboard.writeText(text);
214
+ this._events$.next({ type: "copy", text });
215
+ return true;
216
+ }
217
+ return this._execCopy(text);
218
+ } catch {
219
+ return this._execCopy(text);
220
+ }
221
+ }
222
+ /** Read text from clipboard */
223
+ async paste() {
224
+ try {
225
+ if (navigator?.clipboard?.readText) {
226
+ const text = await navigator.clipboard.readText();
227
+ this._events$.next({ type: "paste", text });
228
+ return text;
229
+ }
230
+ return null;
231
+ } catch {
232
+ return null;
233
+ }
234
+ }
235
+ /** Copy a DOM element's innerText */
236
+ async copyElement(elementOrId) {
237
+ const el = typeof elementOrId === "string" ? document.getElementById(elementOrId) : elementOrId;
238
+ return el ? this.copy(el.innerText ?? el.textContent ?? "") : false;
239
+ }
240
+ /** Observe copy/paste events */
241
+ events$() {
242
+ return this._events$.asObservable();
243
+ }
244
+ _execCopy(text) {
245
+ if (typeof document === "undefined") return false;
246
+ const ta = document.createElement("textarea");
247
+ ta.value = text;
248
+ ta.style.cssText = "position:fixed;top:-9999px;left:-9999px;opacity:0";
249
+ document.body.appendChild(ta);
250
+ ta.focus();
251
+ ta.select();
252
+ try {
253
+ const ok = document.execCommand("copy");
254
+ if (ok) this._events$.next({ type: "copy", text });
255
+ return ok;
256
+ } finally {
257
+ document.body.removeChild(ta);
258
+ }
259
+ }
260
+ };
261
+ var clipboard = new JoopClipboardService();
262
+
263
+ // src/utilities/image.utility.ts
264
+ var JoopImageService = class {
265
+ /** Compress and optionally resize an image file */
266
+ compress(file, options = {}) {
267
+ const { maxWidthOrHeight = 1920, quality = 0.8, outputType = "image/jpeg" } = options;
268
+ return new Promise((resolve, reject) => {
269
+ const url = URL.createObjectURL(file);
270
+ const img = new Image();
271
+ img.onload = () => {
272
+ URL.revokeObjectURL(url);
273
+ const canvas = document.createElement("canvas");
274
+ let { width, height } = img;
275
+ if (width > maxWidthOrHeight || height > maxWidthOrHeight) {
276
+ if (width > height) {
277
+ height = Math.round(height * maxWidthOrHeight / width);
278
+ width = maxWidthOrHeight;
279
+ } else {
280
+ width = Math.round(width * maxWidthOrHeight / height);
281
+ height = maxWidthOrHeight;
282
+ }
283
+ }
284
+ canvas.width = width;
285
+ canvas.height = height;
286
+ canvas.getContext("2d").drawImage(img, 0, 0, width, height);
287
+ canvas.toBlob((b) => b ? resolve(b) : reject(new Error("compression failed")), outputType, quality);
288
+ };
289
+ img.onerror = reject;
290
+ img.src = url;
291
+ });
292
+ }
293
+ /** Resize to exact dimensions */
294
+ resize(file, width, height, outputType = "image/jpeg") {
295
+ return new Promise((resolve, reject) => {
296
+ const url = URL.createObjectURL(file);
297
+ const img = new Image();
298
+ img.onload = () => {
299
+ URL.revokeObjectURL(url);
300
+ const canvas = document.createElement("canvas");
301
+ canvas.width = width;
302
+ canvas.height = height;
303
+ canvas.getContext("2d").drawImage(img, 0, 0, width, height);
304
+ canvas.toBlob((b) => b ? resolve(b) : reject(new Error("resize failed")), outputType, 0.9);
305
+ };
306
+ img.onerror = reject;
307
+ img.src = url;
308
+ });
309
+ }
310
+ /** Convert a blob to base64 data URL */
311
+ toBase64(file) {
312
+ return new Promise((resolve, reject) => {
313
+ const reader = new FileReader();
314
+ reader.onload = () => resolve(reader.result);
315
+ reader.onerror = reject;
316
+ reader.readAsDataURL(file);
317
+ });
318
+ }
319
+ /** Get image dimensions and metadata */
320
+ getInfo(file) {
321
+ return new Promise((resolve, reject) => {
322
+ const url = URL.createObjectURL(file);
323
+ const img = new Image();
324
+ img.onload = () => {
325
+ URL.revokeObjectURL(url);
326
+ resolve({
327
+ width: img.naturalWidth,
328
+ height: img.naturalHeight,
329
+ size: file.size,
330
+ type: file.type,
331
+ name: file instanceof File ? file.name : "",
332
+ aspectRatio: parseFloat((img.naturalWidth / img.naturalHeight).toFixed(4))
333
+ });
334
+ };
335
+ img.onerror = reject;
336
+ img.src = url;
337
+ });
338
+ }
339
+ /** Validate an image against size/type/dimension constraints */
340
+ async validate(file, options = {}) {
341
+ const errors = [];
342
+ if (options.maxSizeBytes && file.size > options.maxSizeBytes) {
343
+ errors.push(`File too large: ${(file.size / 1024).toFixed(1)} KB exceeds ${(options.maxSizeBytes / 1024).toFixed(1)} KB`);
344
+ }
345
+ if (options.allowedTypes && !options.allowedTypes.includes(file.type)) {
346
+ errors.push(`Invalid type: ${file.type}. Allowed: ${options.allowedTypes.join(", ")}`);
347
+ }
348
+ if (options.maxWidth || options.maxHeight || options.minWidth || options.minHeight) {
349
+ try {
350
+ const info = await this.getInfo(file);
351
+ if (options.maxWidth && info.width > options.maxWidth) errors.push(`Width ${info.width}px exceeds max ${options.maxWidth}px`);
352
+ if (options.maxHeight && info.height > options.maxHeight) errors.push(`Height ${info.height}px exceeds max ${options.maxHeight}px`);
353
+ if (options.minWidth && info.width < options.minWidth) errors.push(`Width ${info.width}px below min ${options.minWidth}px`);
354
+ if (options.minHeight && info.height < options.minHeight) errors.push(`Height ${info.height}px below min ${options.minHeight}px`);
355
+ } catch {
356
+ errors.push("Could not read image dimensions");
357
+ }
358
+ }
359
+ return { valid: errors.length === 0, errors };
360
+ }
361
+ /** Create thumbnail as data URL */
362
+ async thumbnail(file, size = 128) {
363
+ const thumb = await this.compress(file, { maxWidthOrHeight: size, quality: 0.7 });
364
+ return this.toBase64(thumb);
365
+ }
366
+ };
367
+
368
+ // src/utilities/date.utility.ts
369
+ function isBusinessDay(date, holidays = [], weekend = [0, 6]) {
370
+ const dow = date.getDay();
371
+ if (weekend.includes(dow)) return false;
372
+ const iso = _toIso(date);
373
+ return !holidays.some((h) => _toIso(h) === iso);
374
+ }
375
+ function addBusinessDays(date, days, holidays = [], weekend = [0, 6]) {
376
+ const d = new Date(date);
377
+ let remaining = Math.abs(days);
378
+ const step = days >= 0 ? 1 : -1;
379
+ while (remaining > 0) {
380
+ d.setDate(d.getDate() + step);
381
+ if (isBusinessDay(d, holidays, weekend)) remaining--;
382
+ }
383
+ return d;
384
+ }
385
+ function getBusinessDaysBetween(start, end, holidays = [], weekend = [0, 6]) {
386
+ const s = new Date(Math.min(start.getTime(), end.getTime()));
387
+ const e = new Date(Math.max(start.getTime(), end.getTime()));
388
+ let count = 0;
389
+ const cur = new Date(s);
390
+ cur.setDate(cur.getDate() + 1);
391
+ while (cur <= e) {
392
+ if (isBusinessDay(cur, holidays, weekend)) count++;
393
+ cur.setDate(cur.getDate() + 1);
394
+ }
395
+ return count;
396
+ }
397
+ function daysBetween(a, b) {
398
+ return Math.round(Math.abs(b.getTime() - a.getTime()) / 864e5);
399
+ }
400
+ function isDateInRange(date, start, end) {
401
+ return date >= start && date <= end;
402
+ }
403
+ function parseDate(value) {
404
+ if (!value) return null;
405
+ const d = new Date(value);
406
+ return isNaN(d.getTime()) ? null : d;
407
+ }
408
+ function formatDate(date, format, locale = "en-US") {
409
+ const p = {
410
+ YYYY: String(date.getFullYear()),
411
+ MM: String(date.getMonth() + 1).padStart(2, "0"),
412
+ DD: String(date.getDate()).padStart(2, "0"),
413
+ HH: String(date.getHours()).padStart(2, "0"),
414
+ mm: String(date.getMinutes()).padStart(2, "0"),
415
+ ss: String(date.getSeconds()).padStart(2, "0"),
416
+ M: String(date.getMonth() + 1),
417
+ D: String(date.getDate()),
418
+ // locale-aware month name
419
+ MMM: date.toLocaleDateString(locale, { month: "short" }),
420
+ MMMM: date.toLocaleDateString(locale, { month: "long" })
421
+ };
422
+ return format.replace(/MMMM|MMM|YYYY|MM|DD|HH|mm|ss|M|D/g, (k) => p[k] ?? k);
423
+ }
424
+ var HIJRI_MONTHS = [
425
+ "Muharram",
426
+ "Safar",
427
+ "Rabi al-Awwal",
428
+ "Rabi al-Thani",
429
+ "Jumada al-Awwal",
430
+ "Jumada al-Thani",
431
+ "Rajab",
432
+ "Sha'ban",
433
+ "Ramadan",
434
+ "Shawwal",
435
+ "Dhu al-Qi'dah",
436
+ "Dhu al-Hijjah"
437
+ ];
438
+ function toHijri(date) {
439
+ const jd = _toJulian(date.getFullYear(), date.getMonth() + 1, date.getDate());
440
+ let l = jd - 1948440 + 10632;
441
+ const n = Math.floor((l - 1) / 10631);
442
+ l = l - 10631 * n + 354;
443
+ const j = Math.floor((10985 - l) / 5316) * Math.floor(50 * l / 17719) + Math.floor(l / 5670) * Math.floor(43 * l / 15238);
444
+ l = l - Math.floor((30 - j) / 15) * Math.floor(17719 * j / 50) - Math.floor(j / 16) * Math.floor(15238 * j / 43) + 29;
445
+ const month = Math.floor(24 * l / 709);
446
+ const day = l - Math.floor(709 * month / 24);
447
+ const year = 30 * n + j - 30;
448
+ return { year, month, day, monthName: HIJRI_MONTHS[month - 1] ?? "" };
449
+ }
450
+ function fromHijri(year, month, day) {
451
+ const jd = Math.floor((11 * year + 3) / 30) + 354 * year + 30 * month - Math.floor((month - 1) / 2) + day + 1948440 - 385;
452
+ return _fromJulian(jd);
453
+ }
454
+ function formatHijri(date, includeMonthName = true) {
455
+ const d = String(date.day).padStart(2, "0");
456
+ const m = String(date.month).padStart(2, "0");
457
+ return includeMonthName ? `${d} ${date.monthName} ${date.year} AH` : `${d}/${m}/${date.year}`;
458
+ }
459
+ function timeAgo(date, locale = "en") {
460
+ const diff = Date.now() - date.getTime();
461
+ const abs = Math.abs(diff);
462
+ const future = diff < 0;
463
+ const pre = future ? "in " : "";
464
+ const suf = future ? "" : " ago";
465
+ if (abs < 6e4) return "just now";
466
+ if (abs < 36e5) return `${pre}${Math.floor(abs / 6e4)}m${suf}`;
467
+ if (abs < 864e5) return `${pre}${Math.floor(abs / 36e5)}h${suf}`;
468
+ if (abs < 2592e6) return `${pre}${Math.floor(abs / 864e5)}d${suf}`;
469
+ return formatDate(date, "DD MMM YYYY", locale);
470
+ }
471
+ function _toIso(date) {
472
+ return date.toISOString().slice(0, 10);
473
+ }
474
+ function _toJulian(y, m, d) {
475
+ if (m <= 2) {
476
+ y--;
477
+ m += 12;
478
+ }
479
+ const A = Math.floor(y / 100);
480
+ const B = 2 - A + Math.floor(A / 4);
481
+ return Math.floor(365.25 * (y + 4716)) + Math.floor(30.6001 * (m + 1)) + d + B - 1524;
482
+ }
483
+ function _fromJulian(jd) {
484
+ const z = Math.floor(jd + 0.5);
485
+ const a = Math.floor((z - 186721625e-2) / 36524.25);
486
+ const A = z + 1 + a - Math.floor(a / 4);
487
+ const B = A + 1524;
488
+ const C = Math.floor((B - 122.1) / 365.25);
489
+ const D = Math.floor(365.25 * C);
490
+ const E = Math.floor((B - D) / 30.6001);
491
+ const day = B - D - Math.floor(30.6001 * E);
492
+ const month = E < 14 ? E - 1 : E - 13;
493
+ const year = month > 2 ? C - 4716 : C - 4715;
494
+ return new Date(year, month - 1, day);
495
+ }
496
+
497
+ // src/utilities/qr.utility.ts
498
+ var _GFe = new Uint8Array(512);
499
+ var _GFl = new Uint8Array(256);
500
+ (function() {
501
+ let x = 1;
502
+ for (let i = 0; i < 255; i++) {
503
+ _GFe[i] = x;
504
+ _GFl[x] = i;
505
+ x = x << 1;
506
+ if (x & 256) x ^= 285;
507
+ }
508
+ for (let i = 255; i < 512; i++) _GFe[i] = _GFe[i - 255];
509
+ })();
510
+ var _gm = (a, b) => a && b ? _GFe[(_GFl[a] + _GFl[b]) % 255] : 0;
511
+ function _gen(n) {
512
+ let g = [1];
513
+ for (let i = 0; i < n; i++) {
514
+ const next = new Array(g.length + 1).fill(0);
515
+ for (let j = 0; j < g.length; j++) {
516
+ next[j] ^= g[j];
517
+ next[j + 1] ^= _gm(g[j], _GFe[i]);
518
+ }
519
+ g = next;
520
+ }
521
+ return g;
522
+ }
523
+ function _rsEnc(data, nec) {
524
+ const g = _gen(nec);
525
+ const msg = [...data, ...new Array(nec).fill(0)];
526
+ for (let i = 0; i < data.length; i++) {
527
+ const c = msg[i];
528
+ if (c) for (let j = 0; j < g.length; j++) msg[i + j] ^= _gm(g[j], c);
529
+ }
530
+ return msg.slice(data.length);
531
+ }
532
+ var VT = [
533
+ { aln: [], L: [7, [[1, 19]]], M: [10, [[1, 16]]], Q: [13, [[1, 13]]], H: [17, [[1, 9]]] },
534
+ { aln: [6, 18], L: [10, [[1, 34]]], M: [16, [[1, 28]]], Q: [22, [[1, 22]]], H: [28, [[1, 16]]] },
535
+ { aln: [6, 22], L: [15, [[1, 55]]], M: [26, [[1, 44]]], Q: [18, [[2, 17]]], H: [22, [[2, 13]]] },
536
+ { aln: [6, 26], L: [20, [[1, 80]]], M: [18, [[2, 32]]], Q: [26, [[2, 24]]], H: [16, [[4, 9]]] },
537
+ { aln: [6, 30], L: [26, [[1, 108]]], M: [24, [[2, 43]]], Q: [18, [[2, 15], [2, 16]]], H: [22, [[2, 11], [2, 12]]] },
538
+ { aln: [6, 34], L: [18, [[2, 68]]], M: [16, [[4, 27]]], Q: [24, [[4, 19]]], H: [28, [[4, 15]]] },
539
+ { aln: [6, 22, 38], L: [20, [[2, 78]]], M: [18, [[4, 31]]], Q: [18, [[2, 14], [4, 15]]], H: [26, [[4, 13], [1, 14]]] },
540
+ { aln: [6, 24, 42], L: [24, [[2, 97]]], M: [22, [[2, 38], [2, 39]]], Q: [22, [[4, 18], [2, 19]]], H: [26, [[4, 14], [2, 15]]] },
541
+ { aln: [6, 26, 46], L: [30, [[2, 116]]], M: [22, [[3, 36], [2, 37]]], Q: [20, [[4, 16], [4, 17]]], H: [24, [[4, 12], [4, 13]]] },
542
+ { aln: [6, 28, 50], L: [18, [[2, 68], [2, 69]]], M: [26, [[4, 43], [1, 44]]], Q: [24, [[6, 19], [2, 20]]], H: [28, [[6, 15], [2, 16]]] }
543
+ ];
544
+ var ECI = { L: 1, M: 0, Q: 3, H: 2 };
545
+ function _cap(v, ec) {
546
+ return VT[v - 1][ec][1].reduce((s, [c, d]) => s + c * d, 0);
547
+ }
548
+ function _bestV(len, ec) {
549
+ for (let v = 1; v <= 10; v++) if (_cap(v, ec) >= len + 3) return v;
550
+ throw new Error(`JoopQr: text too long (${len} bytes) for versions 1-10`);
551
+ }
552
+ function _encData(text, ver, ec) {
553
+ const bytes = new TextEncoder().encode(text);
554
+ const total = _cap(ver, ec);
555
+ const bits = [];
556
+ const push = (v, n) => {
557
+ for (let i = n - 1; i >= 0; i--) bits.push(v >> i & 1);
558
+ };
559
+ push(4, 4);
560
+ push(bytes.length, 8);
561
+ for (const b of bytes) push(b, 8);
562
+ push(0, Math.min(4, total * 8 - bits.length));
563
+ while (bits.length % 8) bits.push(0);
564
+ const cw = [];
565
+ for (let i = 0; i < bits.length; i += 8) {
566
+ let b = 0;
567
+ for (let j = 0; j < 8; j++) b = b << 1 | (bits[i + j] ?? 0);
568
+ cw.push(b);
569
+ }
570
+ const pad = [236, 17];
571
+ while (cw.length < total) cw.push(pad[cw.length % 2]);
572
+ return cw;
573
+ }
574
+ function _buildCW(data, ver, ec) {
575
+ const [ecpb, groups] = VT[ver - 1][ec];
576
+ const db = [], eb = [];
577
+ let di = 0;
578
+ for (const [cnt, dpb] of groups) for (let b = 0; b < cnt; b++) {
579
+ const bl = data.slice(di, di + dpb);
580
+ di += dpb;
581
+ db.push(bl);
582
+ eb.push(_rsEnc(bl, ecpb));
583
+ }
584
+ const r = [];
585
+ const md = Math.max(...db.map((b) => b.length));
586
+ for (let i = 0; i < md; i++) for (const b of db) if (i < b.length) r.push(b[i]);
587
+ for (let i = 0; i < ecpb; i++) for (const b of eb) r.push(b[i]);
588
+ return r;
589
+ }
590
+ function _mat(sz) {
591
+ return Array.from({ length: sz }, () => new Int8Array(sz).fill(-1));
592
+ }
593
+ function _finder(m, r, c) {
594
+ for (let dr = -1; dr <= 7; dr++) for (let dc = -1; dc <= 7; dc++) {
595
+ const row = r + dr, col = c + dc;
596
+ if (row < 0 || col < 0 || row >= m.length || col >= m.length) continue;
597
+ const inF = dr >= 0 && dr <= 6 && dc >= 0 && dc <= 6;
598
+ m[row][col] = inF ? dr === 0 || dr === 6 || dc === 0 || dc === 6 || dr >= 2 && dr <= 4 && dc >= 2 && dc <= 4 ? 1 : 0 : 0;
599
+ }
600
+ }
601
+ function _align(m, r, c) {
602
+ for (let dr = -2; dr <= 2; dr++) for (let dc = -2; dc <= 2; dc++) {
603
+ m[r + dr][c + dc] = Math.abs(dr) === 2 || Math.abs(dc) === 2 || dr === 0 && dc === 0 ? 1 : 0;
604
+ }
605
+ }
606
+ function _funcPat(m, ver) {
607
+ const sz = m.length;
608
+ _finder(m, 0, 0);
609
+ _finder(m, 0, sz - 7);
610
+ _finder(m, sz - 7, 0);
611
+ for (let i = 8; i < sz - 8; i++) {
612
+ if (m[6][i] === -1) m[6][i] = i % 2 ? 0 : 1;
613
+ if (m[i][6] === -1) m[i][6] = i % 2 ? 0 : 1;
614
+ }
615
+ const aln = VT[ver - 1].aln;
616
+ for (const r of aln) for (const c of aln) if (m[r][c] === -1) _align(m, r, c);
617
+ m[4 * ver + 9][8] = 1;
618
+ for (let i = 0; i <= 8; i++) {
619
+ if (m[8][i] === -1) m[8][i] = 0;
620
+ if (m[i][8] === -1) m[i][8] = 0;
621
+ }
622
+ for (let i = sz - 8; i < sz; i++) {
623
+ if (m[8][i] === -1) m[8][i] = 0;
624
+ if (m[i][8] === -1) m[i][8] = 0;
625
+ }
626
+ }
627
+ function _placeData(m, cw) {
628
+ const sz = m.length;
629
+ const bits = [];
630
+ for (const b of cw) for (let i = 7; i >= 0; i--) bits.push(b >> i & 1);
631
+ let bi = 0, up = true;
632
+ for (let col = sz - 1; col >= 1; col -= 2) {
633
+ if (col === 6) col--;
634
+ for (let ri = 0; ri < sz; ri++) {
635
+ const row = up ? sz - 1 - ri : ri;
636
+ for (let dc = 0; dc < 2; dc++) {
637
+ const c = col - dc;
638
+ if (m[row][c] === -1) m[row][c] = bi < bits.length ? bits[bi++] : 0;
639
+ }
640
+ }
641
+ up = !up;
642
+ }
643
+ }
644
+ var MFN = [
645
+ (r, c) => (r + c) % 2 === 0,
646
+ (r) => r % 2 === 0,
647
+ (_, c) => c % 3 === 0,
648
+ (r, c) => (r + c) % 3 === 0,
649
+ (r, c) => (Math.floor(r / 2) + Math.floor(c / 3)) % 2 === 0,
650
+ (r, c) => r * c % 2 + r * c % 3 === 0,
651
+ (r, c) => (r * c % 2 + r * c % 3) % 2 === 0,
652
+ (r, c) => ((r + c) % 2 + r * c % 3) % 2 === 0
653
+ ];
654
+ function _mask(m, pat, isF) {
655
+ const sz = m.length;
656
+ const copy = m.map((r) => new Int8Array(r));
657
+ const fn = MFN[pat];
658
+ for (let r = 0; r < sz; r++) for (let c = 0; c < sz; c++) if (!isF[r][c] && copy[r][c] !== -1) {
659
+ if (fn(r, c)) copy[r][c] ^= 1;
660
+ }
661
+ return copy;
662
+ }
663
+ function _pen(m) {
664
+ const sz = m.length;
665
+ let p = 0;
666
+ for (let r = 0; r < sz; r++) for (const hor of [true, false]) {
667
+ let run = 1, prev = hor ? m[r][0] : m[0][r];
668
+ for (let i = 1; i < sz; i++) {
669
+ const cur = hor ? m[r][i] : m[i][r];
670
+ if (cur === prev) {
671
+ run++;
672
+ if (run === 5) p += 3;
673
+ else if (run > 5) p++;
674
+ } else {
675
+ run = 1;
676
+ prev = cur;
677
+ }
678
+ }
679
+ }
680
+ for (let r = 0; r < sz - 1; r++) for (let c = 0; c < sz - 1; c++) {
681
+ const v = m[r][c];
682
+ if (v === m[r][c + 1] && v === m[r + 1][c] && v === m[r + 1][c + 1]) p += 3;
683
+ }
684
+ let dark = 0;
685
+ for (let r = 0; r < sz; r++) for (let c = 0; c < sz; c++) if (m[r][c]) dark++;
686
+ p += Math.abs(Math.floor(dark / (sz * sz) * 20) - 10) * 10;
687
+ return p;
688
+ }
689
+ function _fmtBits(ec, mask) {
690
+ const data = ECI[ec] << 3 | mask;
691
+ let rem = data << 10;
692
+ for (let i = 14; i >= 10; i--) if (rem & 1 << i) rem ^= 1335 << i - 10;
693
+ return (data << 10 | rem) ^ 21522;
694
+ }
695
+ function _placeFmt(m, fmt) {
696
+ const sz = m.length;
697
+ const bits = [];
698
+ for (let i = 14; i >= 0; i--) bits.push(fmt >> i & 1);
699
+ let bi = 0;
700
+ for (let c = 0; c <= 5; c++) m[8][c] = bits[bi++];
701
+ m[8][7] = bits[bi++];
702
+ m[8][8] = bits[bi++];
703
+ m[7][8] = bits[bi++];
704
+ for (let r = 5; r >= 0; r--) m[r][8] = bits[bi++];
705
+ bi = 0;
706
+ for (let r = sz - 1; r >= sz - 7; r--) m[r][8] = bits[bi++];
707
+ m[8][sz - 8] = bits[bi++];
708
+ for (let c = sz - 7; c < sz; c++) m[8][c] = bits[bi++];
709
+ }
710
+ function _svg(m, o) {
711
+ const sz = m.length, tot = sz + 2 * o.margin, mod = o.size / tot;
712
+ let rects = "";
713
+ for (let r = 0; r < sz; r++) for (let c = 0; c < sz; c++) {
714
+ if (m[r][c] === 1) {
715
+ const x = ((c + o.margin) * mod).toFixed(2), y = ((r + o.margin) * mod).toFixed(2), s = (mod + 0.1).toFixed(2);
716
+ rects += `<rect x="${x}" y="${y}" width="${s}" height="${s}"/>`;
717
+ }
718
+ }
719
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${o.size} ${o.size}" shape-rendering="crispEdges"><rect width="${o.size}" height="${o.size}" fill="${o.background}"/><g fill="${o.foreground}">${rects}</g></svg>`;
720
+ }
721
+ function generateQr(text, options = {}) {
722
+ const ec = options.ecLevel ?? "M";
723
+ const opts = {
724
+ size: options.size ?? 300,
725
+ margin: options.margin ?? 4,
726
+ foreground: options.foreground ?? "#000000",
727
+ background: options.background ?? "#ffffff"
728
+ };
729
+ const bytes = new TextEncoder().encode(text);
730
+ const ver = _bestV(bytes.length, ec);
731
+ const sz = 4 * ver + 17;
732
+ const dataCW = _encData(text, ver, ec);
733
+ const allCW = _buildCW(dataCW, ver, ec);
734
+ const m = _mat(sz);
735
+ _funcPat(m, ver);
736
+ const isF = Array.from({ length: sz }, (_, r) => Array.from({ length: sz }, (_2, c) => m[r][c] !== -1));
737
+ _placeData(m, allCW);
738
+ let bm = 0, bp = Infinity;
739
+ for (let mp = 0; mp < 8; mp++) {
740
+ const masked = _mask(m, mp, isF);
741
+ _placeFmt(masked, _fmtBits(ec, mp));
742
+ const pen = _pen(masked);
743
+ if (pen < bp) {
744
+ bp = pen;
745
+ bm = mp;
746
+ }
747
+ }
748
+ const final = _mask(m, bm, isF);
749
+ _placeFmt(final, _fmtBits(ec, bm));
750
+ return _svg(final, opts);
751
+ }
752
+ function generateQrDataUrl(text, options = {}) {
753
+ const svg = generateQr(text, options);
754
+ return "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(svg)));
755
+ }
756
+ function generatePaymentQr(amount, currency, reference, options) {
757
+ const uri = `payment:${currency}:${amount}:${reference}`;
758
+ return generateQr(uri, options);
759
+ }
760
+
761
+ // src/utilities/barcode.utility.ts
762
+ var C128_SYMBOLS = [
763
+ "11011001100",
764
+ "11001101100",
765
+ "11001100110",
766
+ "10010011000",
767
+ "10010001100",
768
+ "10001001100",
769
+ "10011001000",
770
+ "10011000100",
771
+ "10001100100",
772
+ "11001001000",
773
+ "11001000100",
774
+ "11000100100",
775
+ "10110011100",
776
+ "10011011100",
777
+ "10011001110",
778
+ "10111001100",
779
+ "10011101100",
780
+ "10011100110",
781
+ "11001110010",
782
+ "11001011100",
783
+ "11001001110",
784
+ "11011100100",
785
+ "11001110100",
786
+ "11101101110",
787
+ "11101001100",
788
+ "11100101100",
789
+ "11100100110",
790
+ "11101100100",
791
+ "11100110100",
792
+ "11100110010",
793
+ "11011011000",
794
+ "11011000110",
795
+ "11000110110",
796
+ "10100011000",
797
+ "10001011000",
798
+ "10001000110",
799
+ "10110001000",
800
+ "10001101000",
801
+ "10001100010",
802
+ "11010001000",
803
+ "11000101000",
804
+ "11000100010",
805
+ "10110111000",
806
+ "10110001110",
807
+ "10001101110",
808
+ "10111011000",
809
+ "10111000110",
810
+ "10001110110",
811
+ "11101110110",
812
+ "11010001110",
813
+ "11000101110",
814
+ "11011101000",
815
+ "11011100010",
816
+ "11011101110",
817
+ "11101011000",
818
+ "11101000110",
819
+ "11100010110",
820
+ "11101101000",
821
+ "11101100010",
822
+ "11100011010",
823
+ "11101111010",
824
+ "11001000010",
825
+ "11110001010",
826
+ "10100110000",
827
+ "10100001100",
828
+ "10010110000",
829
+ "10010000110",
830
+ "10000101100",
831
+ "10000100110",
832
+ "10110010000",
833
+ "10110000100",
834
+ "10011010000",
835
+ "10011000010",
836
+ "10000110100",
837
+ "10000110010",
838
+ "11000010010",
839
+ "11001010000",
840
+ "11110111010",
841
+ "11000010100",
842
+ "10001111010",
843
+ "10100111100",
844
+ "10010111100",
845
+ "10010011110",
846
+ "10111100100",
847
+ "10011110100",
848
+ "10011110010",
849
+ "11110100100",
850
+ "11110010100",
851
+ "11110010010",
852
+ "11011011110",
853
+ "11011110110",
854
+ "11110110110",
855
+ "10101111000",
856
+ "10100011110",
857
+ "10001011110",
858
+ "10111101000",
859
+ "10111100010",
860
+ "11110101000",
861
+ "11110100010",
862
+ "10111011110",
863
+ "10111101110",
864
+ "11101011110",
865
+ "11110101110",
866
+ "11010000100",
867
+ "11010010000",
868
+ "11010011100",
869
+ "11000111010"
870
+ ];
871
+ var C128_START_B = "11010010000";
872
+ var C128_STOP = "11000111010";
873
+ function _code128Bars(text) {
874
+ const codes = [];
875
+ let checksum = 104;
876
+ for (let i = 0; i < text.length; i++) {
877
+ const v = text.charCodeAt(i) - 32;
878
+ if (v < 0 || v > 95) throw new Error(`Code128: unsupported char '${text[i]}'`);
879
+ codes.push(v);
880
+ checksum += v * (i + 1);
881
+ }
882
+ codes.push(checksum % 103);
883
+ return C128_START_B + codes.map((c) => C128_SYMBOLS[c]).join("") + C128_STOP + "11";
884
+ }
885
+ function generateCode128(text, opts = {}) {
886
+ const bars = _code128Bars(text);
887
+ const mw = opts.width ? Math.floor((opts.width - 2 * (opts.margin ?? 10)) / bars.length) : 2;
888
+ const margin = opts.margin ?? 10;
889
+ const barH = (opts.height ?? 80) - (opts.showText !== false ? (opts.fontSize ?? 12) + 4 : 0) - margin;
890
+ const totalW = bars.length * mw + margin * 2;
891
+ const totalH = opts.height ?? 80;
892
+ const fg = opts.foreground ?? "#000000";
893
+ const bg = opts.background ?? "#ffffff";
894
+ let rects = "";
895
+ for (let i = 0; i < bars.length; i++) {
896
+ if (bars[i] === "1") rects += `<rect x="${margin + i * mw}" y="${margin}" width="${mw}" height="${barH}" fill="${fg}"/>`;
897
+ }
898
+ const label = opts.showText !== false ? `<text x="${totalW / 2}" y="${totalH - 2}" text-anchor="middle" font-family="monospace" font-size="${opts.fontSize ?? 12}" fill="${fg}">${text}</text>` : "";
899
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${totalW}" height="${totalH}" viewBox="0 0 ${totalW} ${totalH}"><rect width="${totalW}" height="${totalH}" fill="${bg}"/>${rects}${label}</svg>`;
900
+ }
901
+ var EAN_L = ["0001101", "0011001", "0010011", "0111101", "0100011", "0110001", "0101111", "0111011", "0110111", "0001011"];
902
+ var EAN_G = ["0100111", "0110011", "0011011", "0100001", "0011101", "0111001", "0000101", "0010001", "0001001", "0010111"];
903
+ var EAN_R = ["1110010", "1100110", "1101100", "1000010", "1011100", "1001110", "1010000", "1000100", "1001000", "1110100"];
904
+ var EAN_PARITY = ["LLLLLL", "LLGLGG", "LLGGLG", "LLGGGL", "LGLLGG", "LGGLLG", "LGGGLL", "LGLGLG", "LGLGGL", "LGGLGL"];
905
+ function _ean13Bars(digits) {
906
+ if (!/^\d{13}$/.test(digits)) throw new Error("EAN-13: need exactly 13 digits");
907
+ const d = digits.split("").map(Number);
908
+ const parity = EAN_PARITY[d[0]];
909
+ let bars = "101";
910
+ for (let i = 0; i < 6; i++) bars += parity[i] === "G" ? EAN_G[d[i + 1]] : EAN_L[d[i + 1]];
911
+ bars += "01010";
912
+ for (let i = 7; i < 13; i++) bars += EAN_R[d[i]];
913
+ bars += "101";
914
+ return bars;
915
+ }
916
+ function validateEan13(digits) {
917
+ if (!/^\d{12,13}$/.test(digits)) return false;
918
+ const d = digits.slice(0, 12).split("").map(Number);
919
+ const check = (10 - d.reduce((s, v, i) => s + (i % 2 === 0 ? v : v * 3), 0) % 10) % 10;
920
+ return digits.length === 12 ? true : check === Number(digits[12]);
921
+ }
922
+ function generateEan13(digits, opts = {}) {
923
+ const bars = _ean13Bars(digits);
924
+ const mw = 2;
925
+ const margin = opts.margin ?? 10;
926
+ const barH = (opts.height ?? 70) - (opts.showText !== false ? (opts.fontSize ?? 12) + 4 : 0) - margin;
927
+ const totalW = bars.length * mw + margin * 2 + 8;
928
+ const totalH = opts.height ?? 70;
929
+ const fg = opts.foreground ?? "#000000";
930
+ const bg = opts.background ?? "#ffffff";
931
+ let rects = "";
932
+ const xOff = margin + 8;
933
+ for (let i = 0; i < bars.length; i++) {
934
+ if (bars[i] === "1") rects += `<rect x="${xOff + i * mw}" y="${margin}" width="${mw}" height="${barH}" fill="${fg}"/>`;
935
+ }
936
+ const label = opts.showText !== false ? `<text x="${margin}" y="${margin + barH - 2}" font-family="monospace" font-size="${opts.fontSize ?? 10}" fill="${fg}">${digits[0]}</text><text x="${totalW / 2}" y="${totalH - 2}" text-anchor="middle" font-family="monospace" font-size="${opts.fontSize ?? 10}" fill="${fg}">${digits.slice(1, 7)} ${digits.slice(7)}</text>` : "";
937
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${totalW}" height="${totalH}" viewBox="0 0 ${totalW} ${totalH}"><rect width="${totalW}" height="${totalH}" fill="${bg}"/>${rects}${label}</svg>`;
938
+ }
939
+ var C39_MAP = {
940
+ "0": "101001101101",
941
+ "1": "110100101011",
942
+ "2": "101100101011",
943
+ "3": "110110010101",
944
+ "4": "101001101011",
945
+ "5": "110100110101",
946
+ "6": "101100110101",
947
+ "7": "101001011011",
948
+ "8": "110100101101",
949
+ "9": "101100101101",
950
+ "A": "110101001011",
951
+ "B": "101101001011",
952
+ "C": "110110100101",
953
+ "D": "101011001011",
954
+ "E": "110101100101",
955
+ "F": "101101100101",
956
+ "G": "101010011011",
957
+ "H": "110101001101",
958
+ "I": "101101001101",
959
+ "J": "101011001101",
960
+ "K": "110101010011",
961
+ "L": "101101010011",
962
+ "M": "110110101001",
963
+ "N": "101011010011",
964
+ "O": "110101101001",
965
+ "P": "101101101001",
966
+ "Q": "101010110011",
967
+ "R": "110101011001",
968
+ "S": "101101011001",
969
+ "T": "101011011001",
970
+ "U": "110010101011",
971
+ "V": "100110101011",
972
+ "W": "110011010101",
973
+ "X": "100101101011",
974
+ "Y": "110010110101",
975
+ "Z": "100110110101",
976
+ "-": "100101011011",
977
+ ".": "110010101101",
978
+ " ": "100110101101",
979
+ "*": "100101101101"
980
+ };
981
+ function generateCode39(text, opts = {}) {
982
+ const upper = text.toUpperCase();
983
+ const barsStr = ["*", ...upper.split(""), "*"].map((c) => {
984
+ if (!C39_MAP[c]) throw new Error(`Code39: unsupported char '${c}'`);
985
+ return C39_MAP[c];
986
+ }).join("0");
987
+ const mw = 2;
988
+ const margin = opts.margin ?? 10;
989
+ const barH = (opts.height ?? 70) - (opts.showText !== false ? (opts.fontSize ?? 12) + 4 : 0) - margin;
990
+ const totalW = barsStr.length * mw + margin * 2;
991
+ const totalH = opts.height ?? 70;
992
+ const fg = opts.foreground ?? "#000000";
993
+ const bg = opts.background ?? "#ffffff";
994
+ let rects = "";
995
+ for (let i = 0; i < barsStr.length; i++) {
996
+ if (barsStr[i] === "1") rects += `<rect x="${margin + i * mw}" y="${margin}" width="${mw}" height="${barH}" fill="${fg}"/>`;
997
+ }
998
+ const label = opts.showText !== false ? `<text x="${totalW / 2}" y="${totalH - 2}" text-anchor="middle" font-family="monospace" font-size="${opts.fontSize ?? 12}" fill="${fg}">${text}</text>` : "";
999
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${totalW}" height="${totalH}" viewBox="0 0 ${totalW} ${totalH}"><rect width="${totalW}" height="${totalH}" fill="${bg}"/>${rects}${label}</svg>`;
1000
+ }
1001
+ function generateBarcodeDataUrl(svg) {
1002
+ return `data:image/svg+xml;base64,${btoa(svg)}`;
1003
+ }
1004
+
1005
+ // src/utilities/pdf.utility.ts
1006
+ var PT = { W: 595.28, H: 841.89 };
1007
+ var _PdfBuilder = class {
1008
+ _objs = [];
1009
+ _offsets = [];
1010
+ _stream = "";
1011
+ _pw;
1012
+ _ph;
1013
+ _fonts = /* @__PURE__ */ new Set();
1014
+ constructor(w = PT.W, h = PT.H) {
1015
+ this._pw = w;
1016
+ this._ph = h;
1017
+ }
1018
+ _addObj(body) {
1019
+ const id = this._objs.length + 1;
1020
+ this._objs.push(body);
1021
+ return id;
1022
+ }
1023
+ _y(y) {
1024
+ return this._ph - y;
1025
+ }
1026
+ // flip Y axis
1027
+ text(x, y, content, opts = {}) {
1028
+ const font = opts.font ?? "Helvetica";
1029
+ const size = opts.size ?? 10;
1030
+ const [r, g, b] = opts.color ?? [0, 0, 0];
1031
+ this._fonts.add(font);
1032
+ const safe = content.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
1033
+ this._stream += `BT
1034
+ /${font.replace("-", "")} ${size} Tf
1035
+ ${r} ${g} ${b} rg
1036
+ ${x} ${this._y(y) - size} Td
1037
+ (${safe}) Tj
1038
+ ET
1039
+ `;
1040
+ return this;
1041
+ }
1042
+ line(x1, y1, x2, y2, opts = {}) {
1043
+ const [r, g, b] = opts.color ?? [0, 0, 0];
1044
+ const w = opts.width ?? 0.5;
1045
+ const dash = opts.dash ? `[${opts.dash[0]} ${opts.dash[1]}] 0 d
1046
+ ` : "[] 0 d\n";
1047
+ this._stream += `${r} ${g} ${b} RG
1048
+ ${w} w
1049
+ ${dash}${x1} ${this._y(y1)} m
1050
+ ${x2} ${this._y(y2)} l
1051
+ S
1052
+ `;
1053
+ return this;
1054
+ }
1055
+ rect(x, y, w, h, fill, stroke) {
1056
+ if (fill) this._stream += `${fill[0]} ${fill[1]} ${fill[2]} rg
1057
+ ${x} ${this._y(y) - h} ${w} ${h} re
1058
+ f
1059
+ `;
1060
+ if (stroke) this._stream += `${stroke[0]} ${stroke[1]} ${stroke[2]} RG
1061
+ ${x} ${this._y(y) - h} ${w} ${h} re
1062
+ S
1063
+ `;
1064
+ return this;
1065
+ }
1066
+ table(x, y, cols, rows, opts = {}) {
1067
+ const rh = opts.rowHeight ?? 18;
1068
+ const fs = opts.fontSize ?? 9;
1069
+ const hbg = opts.headerBg ?? [0.2, 0.4, 0.7];
1070
+ const totalW = cols.reduce((s, c) => s + c.width, 0);
1071
+ this.rect(x, y, totalW, rh, hbg);
1072
+ let cx = x;
1073
+ for (const col of cols) {
1074
+ this.text(cx + 3, y + 4, col.header, { font: "Helvetica-Bold", size: fs, color: [1, 1, 1] });
1075
+ cx += col.width;
1076
+ }
1077
+ y += rh;
1078
+ for (let i = 0; i < rows.length; i++) {
1079
+ if (i % 2 === 1) this.rect(x, y, totalW, rh, [0.95, 0.95, 0.95]);
1080
+ cx = x;
1081
+ for (const col of cols) {
1082
+ const val = String(rows[i][col.key] ?? "");
1083
+ const textX = col.align === "right" ? cx + col.width - 3 - val.length * fs * 0.5 : col.align === "center" ? cx + col.width / 2 - val.length * fs * 0.25 : cx + 3;
1084
+ this.text(textX, y + 4, val, { size: fs });
1085
+ cx += col.width;
1086
+ }
1087
+ this.line(x, y + rh, x + totalW, y + rh, { width: 0.3, color: [0.8, 0.8, 0.8] });
1088
+ y += rh;
1089
+ }
1090
+ return y;
1091
+ }
1092
+ build() {
1093
+ const fontNames = ["Helvetica", "HelveticaBold", "TimesRoman", "Courier"];
1094
+ const fontObjs = [];
1095
+ for (const fn of fontNames) {
1096
+ const baseName = fn === "HelveticaBold" ? "Helvetica-Bold" : fn === "TimesRoman" ? "Times-Roman" : fn;
1097
+ fontObjs.push(this._addObj(`<< /Type /Font /Subtype /Type1 /BaseFont /${baseName} /Encoding /WinAnsiEncoding >>`));
1098
+ }
1099
+ const fontDict = fontNames.map((n, i) => `/${n} ${fontObjs[i]} 0 R`).join("\n");
1100
+ const resources = this._addObj(`<< /Font << ${fontDict} >> >>`);
1101
+ const streamBytes = new TextEncoder().encode(this._stream);
1102
+ const contentObj = this._addObj(`<< /Length ${streamBytes.length} >>
1103
+ stream
1104
+ ${this._stream}endstream`);
1105
+ const pageObj = this._addObj(`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${this._pw} ${this._ph}] /Contents ${contentObj} 0 R /Resources ${resources} 0 R >>`);
1106
+ const ordered = [
1107
+ `<< /Type /Catalog /Pages 2 0 R >>`,
1108
+ `<< /Type /Pages /Kids [${pageObj} 0 R] /Count 1 >>`,
1109
+ ...this._objs.slice(2)
1110
+ ];
1111
+ let body = "%PDF-1.4\n";
1112
+ const offsets = [];
1113
+ for (let i = 0; i < ordered.length; i++) {
1114
+ offsets.push(body.length);
1115
+ body += `${i + 1} 0 obj
1116
+ ${ordered[i]}
1117
+ endobj
1118
+ `;
1119
+ }
1120
+ const xrefOffset = body.length;
1121
+ body += `xref
1122
+ 0 ${ordered.length + 1}
1123
+ 0000000000 65535 f
1124
+ `;
1125
+ for (const off of offsets) body += `${String(off).padStart(10, "0")} 00000 n
1126
+ `;
1127
+ body += `trailer
1128
+ << /Size ${ordered.length + 1} /Root 1 0 R >>
1129
+ startxref
1130
+ ${xrefOffset}
1131
+ %%EOF`;
1132
+ return new TextEncoder().encode(body);
1133
+ }
1134
+ };
1135
+ var JoopPdfService = class {
1136
+ _opts;
1137
+ constructor(opts = {}) {
1138
+ this._opts = opts;
1139
+ }
1140
+ _builder() {
1141
+ return new _PdfBuilder(this._opts.pageWidth ?? PT.W, this._opts.pageHeight ?? PT.H);
1142
+ }
1143
+ /** Generate a basic receipt PDF */
1144
+ receipt(title, lines, footer) {
1145
+ const m = this._opts.margin ?? 60;
1146
+ const pw = this._opts.pageWidth ?? PT.W;
1147
+ const doc = this._builder();
1148
+ let y = m;
1149
+ doc.text(m, y, title, { font: "Helvetica-Bold", size: 16 });
1150
+ y += 30;
1151
+ doc.line(m, y, pw - m, y);
1152
+ y += 12;
1153
+ for (const line of lines) {
1154
+ const font = line.bold ? "Helvetica-Bold" : "Helvetica";
1155
+ doc.text(m, y, line.label, { font, size: 10 });
1156
+ doc.text(pw - m - line.value.length * 6, y, line.value, { font, size: 10 });
1157
+ y += 16;
1158
+ }
1159
+ doc.line(m, y, pw - m, y);
1160
+ y += 14;
1161
+ if (footer) doc.text(m, y, footer, { size: 8, color: [0.5, 0.5, 0.5] });
1162
+ return doc.build();
1163
+ }
1164
+ /** Generate a statement with a table */
1165
+ statement(title, subtitle, columns, rows, summary) {
1166
+ const m = this._opts.margin ?? 40;
1167
+ const pw = this._opts.pageWidth ?? PT.W;
1168
+ const doc = this._builder();
1169
+ let y = m;
1170
+ doc.text(m, y, title, { font: "Helvetica-Bold", size: 14 });
1171
+ y += 20;
1172
+ doc.text(m, y, subtitle, { size: 9, color: [0.4, 0.4, 0.4] });
1173
+ y += 16;
1174
+ doc.line(m, y, pw - m, y);
1175
+ y += 12;
1176
+ y = doc.table(m, y, columns, rows) + 10;
1177
+ if (summary) {
1178
+ for (const line of summary) {
1179
+ const font = line.bold ? "Helvetica-Bold" : "Helvetica";
1180
+ doc.text(m, y, line.label, { font, size: 10 });
1181
+ doc.text(pw - m - line.value.length * 6, y, line.value, { font, size: 10 });
1182
+ y += 16;
1183
+ }
1184
+ }
1185
+ return doc.build();
1186
+ }
1187
+ /** Wrap raw bytes as a base64 data URL */
1188
+ toDataUrl(bytes) {
1189
+ let bin = "";
1190
+ for (const b of bytes) bin += String.fromCharCode(b);
1191
+ return `data:application/pdf;base64,${btoa(bin)}`;
1192
+ }
1193
+ /** Trigger browser download */
1194
+ download(bytes, filename = "document.pdf") {
1195
+ const blob = new Blob([new Uint8Array(bytes)], { type: "application/pdf" });
1196
+ const url = URL.createObjectURL(blob);
1197
+ const a = document.createElement("a");
1198
+ a.href = url;
1199
+ a.download = filename;
1200
+ a.click();
1201
+ URL.revokeObjectURL(url);
1202
+ }
1203
+ /** Low-level builder for custom documents */
1204
+ builder() {
1205
+ return this._builder();
1206
+ }
1207
+ };
1208
+
1209
+ // src/utilities/notification.service.ts
1210
+ var JoopNotificationService = class {
1211
+ _permission$ = new JoopBehaviorSubject(
1212
+ typeof Notification !== "undefined" ? Notification.permission : "default"
1213
+ );
1214
+ _messageHandlers = [];
1215
+ _swReg = null;
1216
+ isSupported() {
1217
+ return typeof Notification !== "undefined";
1218
+ }
1219
+ getPermission() {
1220
+ return this._permission$.getValue();
1221
+ }
1222
+ permission$() {
1223
+ return this._permission$.asObservable();
1224
+ }
1225
+ async requestPermission() {
1226
+ if (!this.isSupported()) return "denied";
1227
+ const result = await Notification.requestPermission();
1228
+ this._permission$.next(result);
1229
+ return result;
1230
+ }
1231
+ async show(title, options = {}) {
1232
+ if (this._permission$.getValue() !== "granted") {
1233
+ const p = await this.requestPermission();
1234
+ if (p !== "granted") return null;
1235
+ }
1236
+ if (this._swReg) {
1237
+ await this._swReg.showNotification(title, options);
1238
+ return null;
1239
+ }
1240
+ return new Notification(title, options);
1241
+ }
1242
+ async setServiceWorker(reg) {
1243
+ this._swReg = reg;
1244
+ }
1245
+ async subscribe(vapidPublicKey) {
1246
+ if (!this._swReg) throw new Error("JoopNotification: ServiceWorkerRegistration required");
1247
+ const key = _urlBase64ToUint8Array(vapidPublicKey);
1248
+ return this._swReg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: key });
1249
+ }
1250
+ async unsubscribe() {
1251
+ if (!this._swReg) return false;
1252
+ const sub = await this._swReg.pushManager.getSubscription();
1253
+ if (!sub) return false;
1254
+ return sub.unsubscribe();
1255
+ }
1256
+ async getSubscription() {
1257
+ if (!this._swReg) return null;
1258
+ return this._swReg.pushManager.getSubscription();
1259
+ }
1260
+ onMessage(handler) {
1261
+ this._messageHandlers.push(handler);
1262
+ navigator.serviceWorker?.addEventListener("message", handler);
1263
+ return () => {
1264
+ this._messageHandlers = this._messageHandlers.filter((h) => h !== handler);
1265
+ navigator.serviceWorker?.removeEventListener("message", handler);
1266
+ };
1267
+ }
1268
+ destroy() {
1269
+ for (const h of this._messageHandlers) navigator.serviceWorker?.removeEventListener("message", h);
1270
+ this._messageHandlers = [];
1271
+ }
1272
+ };
1273
+ function _urlBase64ToUint8Array(base64String) {
1274
+ const padding = "=".repeat((4 - base64String.length % 4) % 4);
1275
+ const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
1276
+ const raw = atob(base64);
1277
+ return new Uint8Array(Array.from(raw, (c) => c.charCodeAt(0)));
1278
+ }
1279
+
1280
+ // src/utilities/geo.service.ts
1281
+ var JoopGeoService = class {
1282
+ isSupported() {
1283
+ return "geolocation" in navigator;
1284
+ }
1285
+ async getPosition(opts = {}) {
1286
+ return new Promise((resolve, reject) => {
1287
+ if (!this.isSupported()) {
1288
+ reject(new Error("Geolocation not supported"));
1289
+ return;
1290
+ }
1291
+ navigator.geolocation.getCurrentPosition(
1292
+ (p) => resolve(this._map(p)),
1293
+ (e) => reject(e),
1294
+ opts
1295
+ );
1296
+ });
1297
+ }
1298
+ watchPosition(onSuccess, onError, opts = {}) {
1299
+ return navigator.geolocation.watchPosition(
1300
+ (p) => onSuccess(this._map(p)),
1301
+ onError,
1302
+ opts
1303
+ );
1304
+ }
1305
+ clearWatch(id) {
1306
+ navigator.geolocation.clearWatch(id);
1307
+ }
1308
+ async getPermissionState() {
1309
+ if (!("permissions" in navigator)) return "prompt";
1310
+ const status = await navigator.permissions.query({ name: "geolocation" });
1311
+ return status.state;
1312
+ }
1313
+ /** Haversine distance in kilometers */
1314
+ distance(from, to) {
1315
+ const R = 6371;
1316
+ const dLat = _rad(to.latitude - from.latitude);
1317
+ const dLon = _rad(to.longitude - from.longitude);
1318
+ const a = Math.sin(dLat / 2) ** 2 + Math.cos(_rad(from.latitude)) * Math.cos(_rad(to.latitude)) * Math.sin(dLon / 2) ** 2;
1319
+ return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1320
+ }
1321
+ isInRadius(point, center, radiusKm) {
1322
+ return this.distance(point, center) <= radiusKm;
1323
+ }
1324
+ /** Compute compass bearing from `from` to `to` in degrees (0–360) */
1325
+ bearing(from, to) {
1326
+ const dLon = _rad(to.longitude - from.longitude);
1327
+ const lat1 = _rad(from.latitude), lat2 = _rad(to.latitude);
1328
+ const y = Math.sin(dLon) * Math.cos(lat2);
1329
+ const x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
1330
+ return (_deg(Math.atan2(y, x)) + 360) % 360;
1331
+ }
1332
+ formatCoords(lat, lon, format = "decimal") {
1333
+ if (format === "decimal") return `${lat.toFixed(6)}, ${lon.toFixed(6)}`;
1334
+ const fmt = (d, posLabel, negLabel) => {
1335
+ const abs = Math.abs(d);
1336
+ const deg = Math.floor(abs), min = Math.floor((abs - deg) * 60);
1337
+ const sec = ((abs - deg - min / 60) * 3600).toFixed(2);
1338
+ return `${deg}\xB0 ${min}' ${sec}" ${d >= 0 ? posLabel : negLabel}`;
1339
+ };
1340
+ return `${fmt(lat, "N", "S")}, ${fmt(lon, "E", "W")}`;
1341
+ }
1342
+ _map(p) {
1343
+ return {
1344
+ latitude: p.coords.latitude,
1345
+ longitude: p.coords.longitude,
1346
+ accuracy: p.coords.accuracy,
1347
+ altitude: p.coords.altitude,
1348
+ speed: p.coords.speed,
1349
+ heading: p.coords.heading,
1350
+ timestamp: p.timestamp
1351
+ };
1352
+ }
1353
+ };
1354
+ function _rad(d) {
1355
+ return d * Math.PI / 180;
1356
+ }
1357
+ function _deg(r) {
1358
+ return r * 180 / Math.PI;
1359
+ }
1360
+
1361
+ // src/utilities/media.service.ts
1362
+ var JoopMediaRecorder = class {
1363
+ _recorder;
1364
+ _chunks = [];
1365
+ constructor(stream, opts = {}) {
1366
+ const mimeType = opts.mimeType ?? (MediaRecorder.isTypeSupported("video/webm") ? "video/webm" : "");
1367
+ this._recorder = new MediaRecorder(stream, { mimeType, audioBitsPerSecond: opts.audioBitsPerSecond, videoBitsPerSecond: opts.videoBitsPerSecond });
1368
+ this._recorder.ondataavailable = (e) => {
1369
+ if (e.data.size > 0) this._chunks.push(e.data);
1370
+ };
1371
+ }
1372
+ start(timeslice) {
1373
+ this._recorder.start(timeslice);
1374
+ }
1375
+ stop() {
1376
+ return new Promise((resolve) => {
1377
+ this._recorder.onstop = () => {
1378
+ const mimeType = this._recorder.mimeType || "video/webm";
1379
+ resolve(new Blob(this._chunks, { type: mimeType }));
1380
+ };
1381
+ this._recorder.stop();
1382
+ });
1383
+ }
1384
+ pause() {
1385
+ this._recorder.pause();
1386
+ }
1387
+ resume() {
1388
+ this._recorder.resume();
1389
+ }
1390
+ getState() {
1391
+ return this._recorder.state;
1392
+ }
1393
+ };
1394
+ var JoopMediaService = class {
1395
+ isSupported() {
1396
+ return "mediaDevices" in navigator;
1397
+ }
1398
+ async requestStream(constraints) {
1399
+ if (!this.isSupported()) throw new Error("MediaDevices not supported");
1400
+ return navigator.mediaDevices.getUserMedia(constraints);
1401
+ }
1402
+ async requestCamera(constraints = {}) {
1403
+ return this.requestStream({ video: Object.keys(constraints).length ? constraints : true });
1404
+ }
1405
+ async requestMicrophone(constraints = {}) {
1406
+ return this.requestStream({ audio: Object.keys(constraints).length ? constraints : true });
1407
+ }
1408
+ async requestScreenShare(withAudio = false) {
1409
+ if (!("getDisplayMedia" in navigator.mediaDevices)) throw new Error("Screen capture not supported");
1410
+ return navigator.mediaDevices.getDisplayMedia({ video: true, audio: withAudio });
1411
+ }
1412
+ stopStream(stream) {
1413
+ stream.getTracks().forEach((t) => t.stop());
1414
+ }
1415
+ async capturePhoto(stream, quality = 0.92) {
1416
+ const video = document.createElement("video");
1417
+ video.srcObject = stream;
1418
+ video.muted = true;
1419
+ await video.play();
1420
+ const canvas = document.createElement("canvas");
1421
+ canvas.width = video.videoWidth;
1422
+ canvas.height = video.videoHeight;
1423
+ canvas.getContext("2d").drawImage(video, 0, 0);
1424
+ return new Promise((resolve, reject) => canvas.toBlob((b) => b ? resolve(b) : reject(new Error("Capture failed")), "image/jpeg", quality));
1425
+ }
1426
+ createRecorder(stream, opts) {
1427
+ return new JoopMediaRecorder(stream, opts);
1428
+ }
1429
+ async getDevices(kind) {
1430
+ const devices = await navigator.mediaDevices.enumerateDevices();
1431
+ return kind ? devices.filter((d) => d.kind === kind) : devices;
1432
+ }
1433
+ async checkPermission(kind) {
1434
+ if (!("permissions" in navigator)) return "prompt";
1435
+ const name = kind === "camera" ? "camera" : "microphone";
1436
+ const status = await navigator.permissions.query({ name });
1437
+ return status.state;
1438
+ }
1439
+ /** Attach a stream to a <video> element */
1440
+ attachStream(stream, videoEl, muted = true) {
1441
+ videoEl.srcObject = stream;
1442
+ videoEl.muted = muted;
1443
+ videoEl.play().catch(() => {
1444
+ });
1445
+ }
1446
+ };
1447
+
1448
+ // src/encryption/crypto-utils.ts
1449
+ function _hex(buf) {
1450
+ return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
1451
+ }
1452
+ function _hexToBytes(hex) {
1453
+ return new Uint8Array(hex.match(/.{2}/g).map((h) => parseInt(h, 16)));
1454
+ }
1455
+ var JoopCryptoUtils = class {
1456
+ // ── Hashing ──────────────────────────────────────────────────────────────
1457
+ async sha256(data) {
1458
+ const buf = typeof data === "string" ? new TextEncoder().encode(data) : data;
1459
+ return _hex(await crypto.subtle.digest("SHA-256", buf));
1460
+ }
1461
+ async sha512(data) {
1462
+ const buf = typeof data === "string" ? new TextEncoder().encode(data) : data;
1463
+ return _hex(await crypto.subtle.digest("SHA-512", buf));
1464
+ }
1465
+ async sha1(data) {
1466
+ const buf = typeof data === "string" ? new TextEncoder().encode(data) : data;
1467
+ return _hex(await crypto.subtle.digest("SHA-1", buf));
1468
+ }
1469
+ // ── HMAC ─────────────────────────────────────────────────────────────────
1470
+ async hmac(data, secret, algo = "SHA-256") {
1471
+ const key = await crypto.subtle.importKey(
1472
+ "raw",
1473
+ new TextEncoder().encode(secret),
1474
+ { name: "HMAC", hash: algo },
1475
+ false,
1476
+ ["sign"]
1477
+ );
1478
+ return _hex(await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(data)));
1479
+ }
1480
+ async hmacVerify(data, secret, signature, algo = "SHA-256") {
1481
+ const expected = await this.hmac(data, secret, algo);
1482
+ return this.timingSafeEqual(expected, signature);
1483
+ }
1484
+ // ── PBKDF2 ───────────────────────────────────────────────────────────────
1485
+ async pbkdf2(password, salt, iterations = 1e5, keyLen = 32, hash = "SHA-256") {
1486
+ const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveBits"]);
1487
+ const bits = await crypto.subtle.deriveBits(
1488
+ { name: "PBKDF2", salt: new TextEncoder().encode(salt), iterations, hash },
1489
+ key,
1490
+ keyLen * 8
1491
+ );
1492
+ return _hex(bits);
1493
+ }
1494
+ // ── Password hashing ──────────────────────────────────────────────────────
1495
+ async hashPassword(password, salt, iterations = 1e5) {
1496
+ const s = salt ?? this.generateSalt();
1497
+ const hash = await this.pbkdf2(password, s, iterations);
1498
+ return { hash, salt: s, iterations, keyLen: 32 };
1499
+ }
1500
+ async verifyPassword(password, record) {
1501
+ const { hash } = await this.hashPassword(password, record.salt, record.iterations);
1502
+ return this.timingSafeEqual(hash, record.hash);
1503
+ }
1504
+ // ── Utilities ──────────────────────────────────────────────────────────────
1505
+ generateSalt(bytes = 32) {
1506
+ return _hex(crypto.getRandomValues(new Uint8Array(bytes)).buffer);
1507
+ }
1508
+ randomBytes(length) {
1509
+ return new Uint8Array(crypto.getRandomValues(new Uint8Array(length)));
1510
+ }
1511
+ randomHex(bytes = 16) {
1512
+ return _hex(this.randomBytes(bytes).buffer);
1513
+ }
1514
+ uuid() {
1515
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
1516
+ bytes[6] = bytes[6] & 15 | 64;
1517
+ bytes[8] = bytes[8] & 63 | 128;
1518
+ const hex = _hex(bytes.buffer);
1519
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
1520
+ }
1521
+ /** Constant-time string comparison to prevent timing attacks */
1522
+ timingSafeEqual(a, b) {
1523
+ if (a.length !== b.length) return false;
1524
+ let diff = 0;
1525
+ for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
1526
+ return diff === 0;
1527
+ }
1528
+ hexToBytes(hex) {
1529
+ return _hexToBytes(hex);
1530
+ }
1531
+ bytesToHex(bytes) {
1532
+ return _hex(bytes.buffer);
1533
+ }
1534
+ base64ToHex(b64) {
1535
+ return this.bytesToHex(new Uint8Array(Array.from(atob(b64), (c) => c.charCodeAt(0))));
1536
+ }
1537
+ hexToBase64(hex) {
1538
+ return btoa(String.fromCharCode(..._hexToBytes(hex)));
1539
+ }
1540
+ /** Derive an AES-GCM key from a password via PBKDF2 */
1541
+ async deriveKey(password, salt, iterations = 1e5) {
1542
+ const base = await crypto.subtle.importKey("raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveKey"]);
1543
+ return crypto.subtle.deriveKey(
1544
+ { name: "PBKDF2", salt: new TextEncoder().encode(salt), iterations, hash: "SHA-256" },
1545
+ base,
1546
+ { name: "AES-GCM", length: 256 },
1547
+ false,
1548
+ ["encrypt", "decrypt"]
1549
+ );
1550
+ }
1551
+ };
1552
+
1553
+ // src/utilities/chart-data.utility.ts
1554
+ function _intervalMs(interval) {
1555
+ const MS = { hour: 36e5, day: 864e5, week: 6048e5, month: 2592e6, year: 31536e6 };
1556
+ return MS[interval];
1557
+ }
1558
+ function _bucketLabel(ts, interval) {
1559
+ const d = new Date(ts);
1560
+ if (interval === "hour") return `${d.toLocaleDateString()} ${d.getHours().toString().padStart(2, "0")}:00`;
1561
+ if (interval === "day") return d.toLocaleDateString();
1562
+ if (interval === "week") return `W${_isoWeek(d)} ${d.getFullYear()}`;
1563
+ if (interval === "month") return `${d.toLocaleString("default", { month: "short" })} ${d.getFullYear()}`;
1564
+ return String(d.getFullYear());
1565
+ }
1566
+ function _isoWeek(d) {
1567
+ const jan4 = new Date(d.getFullYear(), 0, 4);
1568
+ return Math.ceil(((d.getTime() - jan4.getTime()) / 864e5 + jan4.getDay() + 1) / 7);
1569
+ }
1570
+ function bucketByTime(data, interval, valueKey, dateKey = "date") {
1571
+ if (!data.length) return [];
1572
+ const ms = _intervalMs(interval);
1573
+ const buckets = /* @__PURE__ */ new Map();
1574
+ for (const row of data) {
1575
+ const ts = new Date(row[dateKey]).getTime();
1576
+ const key = Math.floor(ts / ms) * ms;
1577
+ const b = buckets.get(key) ?? { sum: 0, count: 0 };
1578
+ b.sum += Number(row[valueKey] ?? 0);
1579
+ b.count++;
1580
+ buckets.set(key, b);
1581
+ }
1582
+ return Array.from(buckets.entries()).sort(([a], [b]) => a - b).map(([key, b]) => ({
1583
+ label: _bucketLabel(key, interval),
1584
+ value: b.sum,
1585
+ count: b.count,
1586
+ start: key,
1587
+ end: key + ms
1588
+ }));
1589
+ }
1590
+ function rollingAverage(data, window2) {
1591
+ return data.map((_, i) => {
1592
+ const slice = data.slice(Math.max(0, i - window2 + 1), i + 1);
1593
+ return slice.reduce((a, b) => a + b, 0) / slice.length;
1594
+ });
1595
+ }
1596
+ function normalize(data, min, max) {
1597
+ const mn = min ?? Math.min(...data);
1598
+ const mx = max ?? Math.max(...data);
1599
+ const range = mx - mn || 1;
1600
+ return data.map((v) => (v - mn) / range);
1601
+ }
1602
+ function cumulative(data) {
1603
+ let sum = 0;
1604
+ return data.map((v) => sum += v);
1605
+ }
1606
+ function movingSum(data, window2) {
1607
+ return data.map((_, i) => data.slice(Math.max(0, i - window2 + 1), i + 1).reduce((a, b) => a + b, 0));
1608
+ }
1609
+ function aggregateByCategory(data, categoryKey, valueKey) {
1610
+ const map = /* @__PURE__ */ new Map();
1611
+ for (const row of data) {
1612
+ const cat = String(row[categoryKey] ?? "");
1613
+ const val = Number(row[valueKey] ?? 0);
1614
+ if (!map.has(cat)) map.set(cat, []);
1615
+ map.get(cat).push(val);
1616
+ }
1617
+ return Array.from(map.entries()).map(([category, vals]) => ({
1618
+ category,
1619
+ total: vals.reduce((a, b) => a + b, 0),
1620
+ count: vals.length,
1621
+ average: vals.reduce((a, b) => a + b, 0) / vals.length,
1622
+ min: Math.min(...vals),
1623
+ max: Math.max(...vals)
1624
+ }));
1625
+ }
1626
+ function percentChange(values) {
1627
+ return values.map((v, i) => i === 0 ? 0 : (v - values[i - 1]) / (Math.abs(values[i - 1]) || 1) * 100);
1628
+ }
1629
+ function detectTrend(data, threshold = 0.05) {
1630
+ if (data.length < 2) return "flat";
1631
+ const n = data.length;
1632
+ const xMean = (n - 1) / 2;
1633
+ const yMean = data.reduce((a, b) => a + b, 0) / n;
1634
+ let num = 0, den = 0;
1635
+ data.forEach((y, x) => {
1636
+ num += (x - xMean) * (y - yMean);
1637
+ den += (x - xMean) ** 2;
1638
+ });
1639
+ const slope = den ? num / den : 0;
1640
+ const relativeSlope = Math.abs(slope) / (Math.abs(yMean) || 1);
1641
+ return relativeSlope < threshold ? "flat" : slope > 0 ? "up" : "down";
1642
+ }
1643
+ function sparklinePoints(data, width, height) {
1644
+ if (!data.length) return "";
1645
+ const mn = Math.min(...data), mx = Math.max(...data), range = mx - mn || 1;
1646
+ return data.map((v, i) => {
1647
+ const x = i / (data.length - 1 || 1) * width;
1648
+ const y = height - (v - mn) / range * height;
1649
+ return `${x.toFixed(2)},${y.toFixed(2)}`;
1650
+ }).join(" ");
1651
+ }
1652
+ function sparklineSvg(data, width = 100, height = 30, color = "#2563eb") {
1653
+ if (!data.length) return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"/>`;
1654
+ const pts = sparklinePoints(data, width, height);
1655
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><polyline points="${pts}" fill="none" stroke="${color}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
1656
+ }
1657
+
1658
+ // src/utilities/watermark.service.ts
1659
+ var JoopWatermarkService = class {
1660
+ _watermarkedElements = /* @__PURE__ */ new WeakMap();
1661
+ /**
1662
+ * Apply a visible tiled watermark overlay to an element.
1663
+ */
1664
+ apply(element, text, opts = {}) {
1665
+ this.remove(element);
1666
+ const { opacity = 0.08, fontSize = 16, color = "#000000", rotation = -30, repeat = true } = opts;
1667
+ const overlay = document.createElement("div");
1668
+ Object.assign(overlay.style, {
1669
+ position: "absolute",
1670
+ top: "0",
1671
+ left: "0",
1672
+ width: "100%",
1673
+ height: "100%",
1674
+ pointerEvents: "none",
1675
+ zIndex: "9999",
1676
+ overflow: "hidden",
1677
+ opacity: String(opacity)
1678
+ });
1679
+ if (repeat) {
1680
+ const canvas = document.createElement("canvas");
1681
+ canvas.width = 200;
1682
+ canvas.height = 100;
1683
+ const ctx = canvas.getContext("2d");
1684
+ ctx.clearRect(0, 0, 200, 100);
1685
+ ctx.save();
1686
+ ctx.translate(100, 50);
1687
+ ctx.rotate(rotation * Math.PI / 180);
1688
+ ctx.font = `${fontSize}px sans-serif`;
1689
+ ctx.fillStyle = color;
1690
+ ctx.textAlign = "center";
1691
+ ctx.textBaseline = "middle";
1692
+ ctx.fillText(text, 0, 0);
1693
+ ctx.restore();
1694
+ overlay.style.backgroundImage = `url(${canvas.toDataURL()})`;
1695
+ overlay.style.backgroundRepeat = "repeat";
1696
+ } else {
1697
+ overlay.style.display = "flex";
1698
+ overlay.style.alignItems = "center";
1699
+ overlay.style.justifyContent = "center";
1700
+ const span = document.createElement("span");
1701
+ span.textContent = text;
1702
+ span.style.transform = `rotate(${rotation}deg)`;
1703
+ span.style.fontSize = `${fontSize}px`;
1704
+ span.style.color = color;
1705
+ span.style.whiteSpace = "nowrap";
1706
+ overlay.appendChild(span);
1707
+ }
1708
+ if (getComputedStyle(element).position === "static") element.style.position = "relative";
1709
+ element.appendChild(overlay);
1710
+ this._watermarkedElements.set(element, overlay);
1711
+ }
1712
+ remove(element) {
1713
+ const overlay = this._watermarkedElements.get(element);
1714
+ if (overlay && overlay.parentNode === element) element.removeChild(overlay);
1715
+ this._watermarkedElements.delete(element);
1716
+ }
1717
+ /**
1718
+ * Embed a payload invisibly into an ImageData using LSB steganography.
1719
+ * Modifies the least significant bit of the R channel of each pixel.
1720
+ */
1721
+ embedInImage(imageData, payload) {
1722
+ const bytes = new TextEncoder().encode(payload + "\0");
1723
+ const bits = _toBits(bytes);
1724
+ const data = new Uint8ClampedArray(imageData.data);
1725
+ const lenBits = _uint32ToBits(bytes.length);
1726
+ for (let i = 0; i < 32 && i < data.length / 4; i++) {
1727
+ data[i * 4] = data[i * 4] & 254 | lenBits[i];
1728
+ }
1729
+ for (let i = 0; i < bits.length; i++) {
1730
+ const pixelIdx = (i + 32) * 4;
1731
+ if (pixelIdx >= data.length) break;
1732
+ data[pixelIdx] = data[pixelIdx] & 254 | bits[i];
1733
+ }
1734
+ return new ImageData(data, imageData.width, imageData.height);
1735
+ }
1736
+ /**
1737
+ * Extract a payload from an ImageData that was embedded with embedInImage.
1738
+ * Returns null if no valid payload is found.
1739
+ */
1740
+ extractFromImage(imageData) {
1741
+ const data = imageData.data;
1742
+ const lenBits = [];
1743
+ for (let i = 0; i < 32; i++) lenBits.push(data[i * 4] & 1);
1744
+ const len = _bitsToUint32(lenBits);
1745
+ if (len <= 0 || len > (data.length / 4 - 32) / 8) return null;
1746
+ const totalBits = len * 8;
1747
+ const bits = [];
1748
+ for (let i = 0; i < totalBits; i++) {
1749
+ const pixelIdx = (i + 32) * 4;
1750
+ if (pixelIdx >= data.length) break;
1751
+ bits.push(data[pixelIdx] & 1);
1752
+ }
1753
+ const bytes = _fromBits(bits);
1754
+ try {
1755
+ const text = new TextDecoder().decode(bytes);
1756
+ const nullIdx = text.indexOf("\0");
1757
+ return nullIdx >= 0 ? text.slice(0, nullIdx) : text;
1758
+ } catch {
1759
+ return null;
1760
+ }
1761
+ }
1762
+ /** Apply invisible LSB watermark to a canvas element */
1763
+ applyToCanvas(canvas, payload) {
1764
+ const ctx = canvas.getContext("2d");
1765
+ if (!ctx) return;
1766
+ const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
1767
+ const modified = this.embedInImage(imgData, payload);
1768
+ ctx.putImageData(modified, 0, 0);
1769
+ }
1770
+ };
1771
+ function _toBits(bytes) {
1772
+ const bits = [];
1773
+ for (const byte of bytes) for (let i = 7; i >= 0; i--) bits.push(byte >> i & 1);
1774
+ return bits;
1775
+ }
1776
+ function _fromBits(bits) {
1777
+ const bytes = new Uint8Array(Math.ceil(bits.length / 8));
1778
+ for (let i = 0; i < bytes.length; i++) {
1779
+ let val = 0;
1780
+ for (let j = 0; j < 8 && i * 8 + j < bits.length; j++) val = val << 1 | bits[i * 8 + j];
1781
+ bytes[i] = val;
1782
+ }
1783
+ return bytes;
1784
+ }
1785
+ function _uint32ToBits(n) {
1786
+ const bits = [];
1787
+ for (let i = 31; i >= 0; i--) bits.push(n >> i & 1);
1788
+ return bits;
1789
+ }
1790
+ function _bitsToUint32(bits) {
1791
+ let n = 0;
1792
+ for (const b of bits) n = n << 1 | b;
1793
+ return n >>> 0;
1794
+ }
1795
+
1796
+ // src/utilities/qr-scanner.ts
1797
+ var JoopQrScanner = class {
1798
+ _stream = null;
1799
+ _video = null;
1800
+ _canvas = null;
1801
+ _ctx = null;
1802
+ _timer = null;
1803
+ _active = false;
1804
+ _onResult = null;
1805
+ _onError = null;
1806
+ _cfg;
1807
+ constructor(config) {
1808
+ this._cfg = {
1809
+ decoder: config.decoder,
1810
+ facingMode: config.facingMode ?? "environment",
1811
+ fps: config.fps ?? 10,
1812
+ stopOnScan: config.stopOnScan ?? true,
1813
+ width: config.width ?? 1280,
1814
+ height: config.height ?? 720
1815
+ };
1816
+ }
1817
+ onResult(fn) {
1818
+ this._onResult = fn;
1819
+ return this;
1820
+ }
1821
+ onError(fn) {
1822
+ this._onError = fn;
1823
+ return this;
1824
+ }
1825
+ async start(container) {
1826
+ if (this._active) return;
1827
+ if (!navigator.mediaDevices?.getUserMedia) throw new Error("Camera not available");
1828
+ this._stream = await navigator.mediaDevices.getUserMedia({
1829
+ video: {
1830
+ facingMode: this._cfg.facingMode,
1831
+ width: { ideal: this._cfg.width },
1832
+ height: { ideal: this._cfg.height }
1833
+ }
1834
+ });
1835
+ this._video = document.createElement("video");
1836
+ this._video.setAttribute("playsinline", "true");
1837
+ this._video.muted = true;
1838
+ this._video.srcObject = this._stream;
1839
+ if (container) {
1840
+ Object.assign(this._video.style, { width: "100%", height: "100%", objectFit: "cover", display: "block" });
1841
+ container.appendChild(this._video);
1842
+ }
1843
+ await new Promise((res) => {
1844
+ this._video.onloadedmetadata = () => {
1845
+ this._video.play();
1846
+ res();
1847
+ };
1848
+ });
1849
+ this._canvas = document.createElement("canvas");
1850
+ this._ctx = this._canvas.getContext("2d");
1851
+ this._active = true;
1852
+ this._scheduleFrames();
1853
+ }
1854
+ stop() {
1855
+ this._active = false;
1856
+ if (this._timer) {
1857
+ clearInterval(this._timer);
1858
+ this._timer = null;
1859
+ }
1860
+ this._stream?.getTracks().forEach((t) => t.stop());
1861
+ this._stream = null;
1862
+ this._video?.remove();
1863
+ this._video = null;
1864
+ }
1865
+ isActive() {
1866
+ return this._active;
1867
+ }
1868
+ /** Scan a static image File / Blob instead of the camera. */
1869
+ async scanFile(file) {
1870
+ return new Promise((resolve) => {
1871
+ const img = new Image();
1872
+ const url = URL.createObjectURL(file);
1873
+ img.onload = () => {
1874
+ const canvas = document.createElement("canvas");
1875
+ canvas.width = img.width;
1876
+ canvas.height = img.height;
1877
+ const ctx = canvas.getContext("2d");
1878
+ ctx.drawImage(img, 0, 0);
1879
+ const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);
1880
+ const text = this._cfg.decoder(data, canvas.width, canvas.height);
1881
+ URL.revokeObjectURL(url);
1882
+ resolve(text ? { text, timestamp: Date.now() } : null);
1883
+ };
1884
+ img.src = url;
1885
+ });
1886
+ }
1887
+ _scheduleFrames() {
1888
+ const interval = Math.round(1e3 / this._cfg.fps);
1889
+ this._timer = setInterval(() => this._processFrame(), interval);
1890
+ }
1891
+ _processFrame() {
1892
+ if (!this._active || !this._video || !this._canvas || !this._ctx) return;
1893
+ if (this._video.readyState < 2) return;
1894
+ this._canvas.width = this._video.videoWidth;
1895
+ this._canvas.height = this._video.videoHeight;
1896
+ this._ctx.drawImage(this._video, 0, 0);
1897
+ try {
1898
+ const imageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height);
1899
+ const text = this._cfg.decoder(imageData.data, this._canvas.width, this._canvas.height);
1900
+ if (text) {
1901
+ if (this._cfg.stopOnScan) this.stop();
1902
+ this._onResult?.({ text, timestamp: Date.now() });
1903
+ }
1904
+ } catch (err) {
1905
+ this._onError?.(err instanceof Error ? err : new Error(String(err)));
1906
+ }
1907
+ }
1908
+ };
1909
+ function generateTotpUri(options) {
1910
+ const { secret, account, issuer, period = 30, digits = 6, algorithm = "SHA1" } = options;
1911
+ const params = new URLSearchParams({
1912
+ secret,
1913
+ issuer,
1914
+ algorithm,
1915
+ digits: String(digits),
1916
+ period: String(period)
1917
+ });
1918
+ return `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(account)}?${params}`;
1919
+ }
1920
+
1921
+ export { JoopClipboardService, JoopCryptoUtils, JoopGeoService, JoopImageService, JoopMediaRecorder, JoopMediaService, JoopNotificationService, JoopPdfService, JoopQrScanner, JoopWatermarkService, addBusinessDays, aggregateByCategory, bucketByTime, bufferToHex, buildFormData, buildQueryString, clipboard, cumulative, daysBetween, deepClone, detectTrend, escapeHtml, flattenObj, flattenObject, formatDate, formatHijri, fromHijri, generateBarcodeDataUrl, generateCode128, generateCode39, generateEan13, generatePaymentQr, generateQr, generateQrDataUrl, generateTotpUri, getBusinessDaysBetween, getDeviceInfo, getRandomNum, isAlphanumeric, isBrowser, isBusinessDay, isDateInRange, isPlainObject, isSSR, isValidEmail, isValidPhone, isValidUrl, movingSum, normalize, objectToQueryString, parseDate, percentChange, randomString, removeTrailingSlash, rollingAverage, sanitizeFileName, sanitizeInput, sparklinePoints, sparklineSvg, stringToHex, stripHtml, stripSqlKeywords, timeAgo, toHijri, truncate, urlEncode, validateEan13 };
1922
+ //# sourceMappingURL=index.mjs.map
1923
+ //# sourceMappingURL=index.mjs.map