nmce-func 1.2.0 → 1.3.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 (35) hide show
  1. package/_func/addressFunc.d.ts +31 -31
  2. package/_func/authentication.service.d.ts +27 -27
  3. package/_func/currencyFunc.d.ts +20 -20
  4. package/_func/dateFunc.d.ts +121 -121
  5. package/_func/htmlPrintFunc.d.ts +18 -18
  6. package/_func/index.d.ts +10 -10
  7. package/_func/javascriptFunc.d.ts +10 -9
  8. package/_func/jsonFunc.d.ts +31 -31
  9. package/_func/stringAusFunc.d.ts +40 -40
  10. package/_func/stringFunc.d.ts +41 -41
  11. package/_func/uuidFunc.d.ts +7 -7
  12. package/{esm2020 → esm2022}/_func/addressFunc.mjs +48 -48
  13. package/esm2022/_func/authentication.service.mjs +48 -0
  14. package/esm2022/_func/currencyFunc.mjs +72 -0
  15. package/{esm2020 → esm2022}/_func/dateFunc.mjs +315 -315
  16. package/{esm2020 → esm2022}/_func/htmlPrintFunc.mjs +39 -39
  17. package/{esm2020 → esm2022}/_func/index.mjs +10 -10
  18. package/esm2022/_func/javascriptFunc.mjs +21 -0
  19. package/{esm2020 → esm2022}/_func/jsonFunc.mjs +66 -66
  20. package/esm2022/_func/stringAusFunc.mjs +200 -0
  21. package/{esm2020 → esm2022}/_func/stringFunc.mjs +92 -92
  22. package/{esm2020 → esm2022}/_func/uuidFunc.mjs +16 -16
  23. package/{esm2020 → esm2022}/nmce-func.mjs +4 -4
  24. package/{esm2020 → esm2022}/public-api.mjs +4 -4
  25. package/{fesm2020 → fesm2022}/nmce-func.mjs +902 -895
  26. package/{fesm2020 → fesm2022}/nmce-func.mjs.map +1 -1
  27. package/index.d.ts +5 -5
  28. package/package.json +5 -11
  29. package/public-api.d.ts +1 -1
  30. package/esm2020/_func/authentication.service.mjs +0 -45
  31. package/esm2020/_func/currencyFunc.mjs +0 -72
  32. package/esm2020/_func/javascriptFunc.mjs +0 -17
  33. package/esm2020/_func/stringAusFunc.mjs +0 -200
  34. package/fesm2015/nmce-func.mjs +0 -933
  35. package/fesm2015/nmce-func.mjs.map +0 -1
@@ -5,926 +5,933 @@ import * as i1 from '@angular/common/http';
5
5
  import moment from 'moment';
6
6
  import { v4 } from 'uuid';
7
7
 
8
- class AddressFunc {
9
- /**
10
- * Compose to one line separated by comma
11
- * @param st1
12
- * @param st2
13
- * @param city
14
- * @param state
15
- * @param postcode
16
- */
17
- static composeOneLineAddress(st1, st2, city, state, postcode) {
18
- return AddressFunc.composeAddress(st1, st2, city, state, postcode, ', ');
19
- }
20
- /**
21
- * Compose to multiple separated by \n
22
- * @param st1
23
- * @param st2
24
- * @param city
25
- * @param state
26
- * @param postcode
27
- */
28
- static composeMultiLineAddress(st1, st2, city, state, postcode) {
29
- return AddressFunc.composeAddress(st1, st2, city, state, postcode, '\n');
30
- }
31
- /**
32
- * Compose with separator
33
- * @param st1
34
- * @param st2
35
- * @param city
36
- * @param state
37
- * @param postcode
38
- * @param sep
39
- */
40
- static composeAddress(st1, st2, city, state, postcode, sep) {
41
- const r = (st1 ? (st1 + sep) : '') + (st2 ? (st2 + sep) : '') + (city ? (city + sep) : '') + (state ? (state + ' ') : '') + (postcode ? postcode : '');
42
- return r;
43
- }
44
- static composeGoogleMapsAuUrl(st1, st2, city, state, country) {
45
- const googleBaseUrl = 'https://www.google.com.au/maps?hl=en&q=';
46
- const fff = (s) => {
47
- if (!s) {
48
- return '';
49
- }
50
- return '+' + encodeURIComponent(s.replace(' ', '+')) + ',';
51
- };
52
- const ss = fff(st1) + fff(st2) + fff(city) + fff(state) + fff(country);
53
- return googleBaseUrl + ss;
54
- }
8
+ class AddressFunc {
9
+ /**
10
+ * Compose to one line separated by comma
11
+ * @param st1
12
+ * @param st2
13
+ * @param city
14
+ * @param state
15
+ * @param postcode
16
+ */
17
+ static composeOneLineAddress(st1, st2, city, state, postcode) {
18
+ return AddressFunc.composeAddress(st1, st2, city, state, postcode, ', ');
19
+ }
20
+ /**
21
+ * Compose to multiple separated by \n
22
+ * @param st1
23
+ * @param st2
24
+ * @param city
25
+ * @param state
26
+ * @param postcode
27
+ */
28
+ static composeMultiLineAddress(st1, st2, city, state, postcode) {
29
+ return AddressFunc.composeAddress(st1, st2, city, state, postcode, '\n');
30
+ }
31
+ /**
32
+ * Compose with separator
33
+ * @param st1
34
+ * @param st2
35
+ * @param city
36
+ * @param state
37
+ * @param postcode
38
+ * @param sep
39
+ */
40
+ static composeAddress(st1, st2, city, state, postcode, sep) {
41
+ const r = (st1 ? (st1 + sep) : '') + (st2 ? (st2 + sep) : '') + (city ? (city + sep) : '') + (state ? (state + ' ') : '') + (postcode ? postcode : '');
42
+ return r;
43
+ }
44
+ static composeGoogleMapsAuUrl(st1, st2, city, state, country) {
45
+ const googleBaseUrl = 'https://www.google.com.au/maps?hl=en&q=';
46
+ const fff = (s) => {
47
+ if (!s) {
48
+ return '';
49
+ }
50
+ return '+' + encodeURIComponent(s.replace(' ', '+')) + ',';
51
+ };
52
+ const ss = fff(st1) + fff(st2) + fff(city) + fff(state) + fff(country);
53
+ return googleBaseUrl + ss;
54
+ }
55
55
  }
56
56
 
57
- /**
58
- * Login and saving tokens in sessionStorage.
59
- * App needs to provide constant 'auth.tokenUrl'.
60
- */
61
- class AuthenticationService {
62
- constructor(authUri, http) {
63
- this.authUri = authUri;
64
- this.http = http;
65
- }
66
- /**
67
- * Login and save tokens to sessionStorage then return an observable.
68
- * @param username
69
- * @param password
70
- */
71
- login(username, password, headers) {
72
- const body = 'username=' + username + '&password=' + password + '&grant_type=password';
73
- const contentTypeHeader = { 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8' };
74
- const mergedHeaders = headers ? { ...contentTypeHeader, ...headers } : contentTypeHeader;
75
- const options = { headers: mergedHeaders };
76
- return this.http.post(this.authUri, body, options)
77
- .pipe(map(response => {
78
- //sessionStorage.setItem('access_token', response.access_token); The client code is response to doing these.
79
- //sessionStorage.setItem('expires_in', response.expires_in.toString());
80
- //sessionStorage.setItem('token_type', response.token_type);
81
- //sessionStorage.setItem('issued', response.issued);
82
- //sessionStorage.setItem('expires', response.expires); // often up to 2 weeks by default in Asp.net identity 2.
83
- this.username = response.username;
84
- //APP_STATUSES.userName = this.userName;
85
- return response;
86
- }));
87
- }
88
- }
89
- AuthenticationService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.0", ngImport: i0, type: AuthenticationService, deps: [{ token: 'auth.tokenUrl' }, { token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
90
- AuthenticationService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.1.0", ngImport: i0, type: AuthenticationService });
91
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.0", ngImport: i0, type: AuthenticationService, decorators: [{
92
- type: Injectable
93
- }], ctorParameters: function () { return [{ type: undefined, decorators: [{
94
- type: Inject,
95
- args: ['auth.tokenUrl']
57
+ /**
58
+ * Login and saving tokens in sessionStorage.
59
+ * App needs to provide constant 'auth.tokenUrl'.
60
+ */
61
+ class AuthenticationService {
62
+ authUri;
63
+ http;
64
+ username;
65
+ constructor(authUri, http) {
66
+ this.authUri = authUri;
67
+ this.http = http;
68
+ }
69
+ /**
70
+ * Login and save tokens to sessionStorage then return an observable.
71
+ * @param username
72
+ * @param password
73
+ */
74
+ login(username, password, headers) {
75
+ const body = 'username=' + username + '&password=' + password + '&grant_type=password';
76
+ const contentTypeHeader = { 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8' };
77
+ const mergedHeaders = headers ? { ...contentTypeHeader, ...headers } : contentTypeHeader;
78
+ const options = { headers: mergedHeaders };
79
+ return this.http.post(this.authUri, body, options)
80
+ .pipe(map(response => {
81
+ //sessionStorage.setItem('access_token', response.access_token); The client code is response to doing these.
82
+ //sessionStorage.setItem('expires_in', response.expires_in.toString());
83
+ //sessionStorage.setItem('token_type', response.token_type);
84
+ //sessionStorage.setItem('issued', response.issued);
85
+ //sessionStorage.setItem('expires', response.expires); // often up to 2 weeks by default in Asp.net identity 2.
86
+ this.username = response.username;
87
+ //APP_STATUSES.userName = this.userName;
88
+ return response;
89
+ }));
90
+ }
91
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.1.3", ngImport: i0, type: AuthenticationService, deps: [{ token: 'auth.tokenUrl' }, { token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
92
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.1.3", ngImport: i0, type: AuthenticationService });
93
+ }
94
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.3", ngImport: i0, type: AuthenticationService, decorators: [{
95
+ type: Injectable
96
+ }], ctorParameters: function () { return [{ type: undefined, decorators: [{
97
+ type: Inject,
98
+ args: ['auth.tokenUrl']
96
99
  }] }, { type: i1.HttpClient }]; } });
97
100
 
98
- /**
99
- * Currency calculations. Undefined input of number is considered zero, just like null.
100
- */
101
- class CurrencyFunc {
102
- /**
103
- *
104
- * @param num
105
- * @param decimalPlaces default 0
106
- */
107
- static bankerRound(num, decimalPlaces) {
108
- if (!num) {
109
- return 0;
110
- }
111
- const d = decimalPlaces || 0;
112
- const m = Math.pow(10, d);
113
- const n = +(d ? num * m : num).toFixed(8); // Avoid rounding errors
114
- const i = Math.floor(n), f = n - i;
115
- const e = 1e-8; // Allow for rounding errors in f
116
- const r = (f > 0.5 - e && f < 0.5 + e) ?
117
- ((i % 2 === 0) ? i : i + 1) : Math.round(n);
118
- return d ? r / m : r;
119
- }
120
- static bankerRoundTo5cents(num) {
121
- if (!num) {
122
- return 0;
123
- }
124
- const r = this.bankerRound(Math.ceil(num * 20 - 0.5) / 20, 2);
125
- return r;
126
- }
127
- static ceilTo5cents(num) {
128
- if (!num) {
129
- return 0;
130
- }
131
- const r = this.bankerRound(Math.ceil(num * 20) / 20, 4);
132
- const roundup = Math.ceil(r * 10000) / 10000;
133
- return roundup;
134
- }
135
- static transformCurrency(value, fractionSize = 2) {
136
- let [integer, fraction = ''] = (value || '').toString()
137
- .split(this.DECIMAL_SEPARATOR);
138
- fraction = fractionSize > 0
139
- ? this.DECIMAL_SEPARATOR + (fraction + this.PADDING).substring(0, fractionSize)
140
- : '';
141
- integer = integer.replace(/\B(?=(\d{3})+(?!\d))/g, this.THOUSANDS_SEPARATOR);
142
- return integer + fraction;
143
- }
144
- static parseCurrency(value, fractionSize = 2) {
145
- let [integer, fraction = ''] = (value || '').split(this.DECIMAL_SEPARATOR);
146
- integer = integer.replace(new RegExp(this.THOUSANDS_SEPARATOR, 'g'), '');
147
- fraction = parseInt(fraction, 10) > 0 && fractionSize > 0
148
- ? this.DECIMAL_SEPARATOR + (fraction + this.PADDING).substring(0, fractionSize)
149
- : '';
150
- return integer + fraction;
151
- }
152
- //http://stackoverflow.com/questions/2998784/how-to-output-integers-with-leading-zeros-in-javascript
153
- static pad(num, size) {
154
- num = null;
155
- let s = num + '';
156
- while (s.length < size) {
157
- s = '0' + s;
158
- }
159
- return s;
160
- }
161
- static sum(ns) {
162
- const r = ns.reduce((a, b) => (a ?? 0) + (b ?? 0), 0);
163
- return r;
164
- }
165
- }
166
- CurrencyFunc.DECIMAL_SEPARATOR = '.';
167
- CurrencyFunc.THOUSANDS_SEPARATOR = ',';
168
- CurrencyFunc.PADDING = '000000';
101
+ /**
102
+ * Currency calculations. Undefined input of number is considered zero, just like null.
103
+ */
104
+ class CurrencyFunc {
105
+ static DECIMAL_SEPARATOR = '.';
106
+ static THOUSANDS_SEPARATOR = ',';
107
+ static PADDING = '000000';
108
+ /**
109
+ *
110
+ * @param num
111
+ * @param decimalPlaces default 0
112
+ */
113
+ static bankerRound(num, decimalPlaces) {
114
+ if (!num) {
115
+ return 0;
116
+ }
117
+ const d = decimalPlaces || 0;
118
+ const m = Math.pow(10, d);
119
+ const n = +(d ? num * m : num).toFixed(8); // Avoid rounding errors
120
+ const i = Math.floor(n), f = n - i;
121
+ const e = 1e-8; // Allow for rounding errors in f
122
+ const r = (f > 0.5 - e && f < 0.5 + e) ?
123
+ ((i % 2 === 0) ? i : i + 1) : Math.round(n);
124
+ return d ? r / m : r;
125
+ }
126
+ static bankerRoundTo5cents(num) {
127
+ if (!num) {
128
+ return 0;
129
+ }
130
+ const r = this.bankerRound(Math.ceil(num * 20 - 0.5) / 20, 2);
131
+ return r;
132
+ }
133
+ static ceilTo5cents(num) {
134
+ if (!num) {
135
+ return 0;
136
+ }
137
+ const r = this.bankerRound(Math.ceil(num * 20) / 20, 4);
138
+ const roundup = Math.ceil(r * 10000) / 10000;
139
+ return roundup;
140
+ }
141
+ static transformCurrency(value, fractionSize = 2) {
142
+ let [integer, fraction = ''] = (value || '').toString()
143
+ .split(this.DECIMAL_SEPARATOR);
144
+ fraction = fractionSize > 0
145
+ ? this.DECIMAL_SEPARATOR + (fraction + this.PADDING).substring(0, fractionSize)
146
+ : '';
147
+ integer = integer.replace(/\B(?=(\d{3})+(?!\d))/g, this.THOUSANDS_SEPARATOR);
148
+ return integer + fraction;
149
+ }
150
+ static parseCurrency(value, fractionSize = 2) {
151
+ let [integer, fraction = ''] = (value || '').split(this.DECIMAL_SEPARATOR);
152
+ integer = integer.replace(new RegExp(this.THOUSANDS_SEPARATOR, 'g'), '');
153
+ fraction = parseInt(fraction, 10) > 0 && fractionSize > 0
154
+ ? this.DECIMAL_SEPARATOR + (fraction + this.PADDING).substring(0, fractionSize)
155
+ : '';
156
+ return integer + fraction;
157
+ }
158
+ //http://stackoverflow.com/questions/2998784/how-to-output-integers-with-leading-zeros-in-javascript
159
+ static pad(num, size) {
160
+ num = null;
161
+ let s = num + '';
162
+ while (s.length < size) {
163
+ s = '0' + s;
164
+ }
165
+ return s;
166
+ }
167
+ static sum(ns) {
168
+ const r = ns.reduce((a, b) => (a ?? 0) + (b ?? 0), 0);
169
+ return r;
170
+ }
171
+ }
169
172
 
170
- class DateFunc {
171
- /**
172
- * Transform UTC DateTime to local date without H, M and S. For example, the month day of 2018-01-23T22:00:00Z is 24 in Australia.
173
- * @param dtUtc
174
- * @param offsetMinutes if not defined, it will be new Date().getTimezoneOffset(). //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset
175
- */
176
- static dateTimeUtcToLocalDateNumber(dtUtc) {
177
- if (!dtUtc) {
178
- return 0; //0 is better for calculation by the clients.
179
- }
180
- const localDt = DateFunc.dateTimeUtcToLocalDateTime(dtUtc);
181
- const localDNum = localDt.setHours(0, 0, 0, 0);
182
- return localDNum;
183
- }
184
- /**
185
- * Date only. However, the date may still be in UTC.
186
- * @param dtUtc
187
- */
188
- static dateTimeUtcToLocalDate(dtUtc) {
189
- const localDt = DateFunc.dateTimeUtcToLocalDateTime(dtUtc);
190
- const localD = localDt.setHours(0, 0, 0, 0);
191
- return new Date(localD);
192
- }
193
- static localISODateString(dtUtc) {
194
- const dt = moment(dtUtc).local();
195
- return dt.format('YYYY-MM-DD');
196
- }
197
- /**
198
- * locate date ONLY (no time) to UTC date.
199
- * @param dt if dt contain time info, it will become dt.setHours(0, 0, 0, 0)
200
- */
201
- static localDateToUtc(d) {
202
- const dt = moment(d).toDate();
203
- const n = dt.setHours(0, 0, 0, 0);
204
- const offset = dt.getTimezoneOffset() * 60000;
205
- return new Date(n + offset);
206
- }
207
- static getTimezoneOffset() {
208
- const dt = this.today;
209
- return dt.getTimezoneOffset();
210
- }
211
- /**
212
- * Transform UTC DateTime to local dateTime.
213
- * @param dtUtc
214
- * @param offsetMinutes if not defined, it will be new Date().getTimezoneOffset(). //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset
215
- */
216
- static dateTimeUtcToLocalDateTime(dtUtc) {
217
- const stillUtc = moment.utc(dtUtc).toDate();
218
- return moment(stillUtc).local().toDate();
219
- }
220
- static dateTimeUtcToLocaMoment(dtUtc) {
221
- const stillUtc = moment.utc(dtUtc);
222
- return stillUtc.local();
223
- }
224
- static getEndOfWeek(dt) {
225
- return moment(dt).endOf('isoWeek').toDate();
226
- }
227
- static getStartOfWeek(dt) {
228
- return moment(dt).startOf('isoWeek').toDate();
229
- }
230
- static getEndOfMonth(dt) {
231
- // return new Date(dt.getFullYear(), dt.getMonth() + 1, 0, 23, 59, 59, 999);
232
- return moment(dt).endOf('month').toDate();
233
- }
234
- static getStartOfMonth(dt) {
235
- return moment(dt).startOf('month').toDate();
236
- }
237
- static getDaysBetweenDates(dt1, dt2) {
238
- return this.getDaysBetween(dt1, dt2);
239
- }
240
- static getEndOfDate(dt) {
241
- return dt ? new Date(dt.setHours(23, 59, 59, 999)) :
242
- new Date(this.now.setHours(23, 59, 59, 999));
243
- }
244
- static getStartOfDate(dt) {
245
- return moment(dt).startOf('day').toDate();
246
- }
247
- static getEndOfToday() {
248
- // return new Date((new Date(Date.now())).setHours(23, 59, 59, 999));
249
- return moment(Date.now()).endOf('day').toDate();
250
- }
251
- static getStartOfToday() {
252
- // return new Date((new Date(Date.now())).setHours(0, 0, 0, 0));
253
- return moment(Date.now()).startOf('day').toDate();
254
- }
255
- //inspired https://stackoverflow.com/questions/563406/add-days-to-javascript-date
256
- static addDays(dt, days = 0) {
257
- const dat = moment(dt);
258
- dat.add(days, 'days');
259
- return dat.toDate();
260
- }
261
- static subtractDays(dt, days = 0) {
262
- const dat = moment(dt);
263
- dat.subtract(days, 'days');
264
- return dat.toDate();
265
- }
266
- /**
267
- * Start of today
268
- */
269
- static get today() {
270
- return this.getStartOfToday();
271
- }
272
- static get now() {
273
- return new Date(Date.now());
274
- }
275
- static getNext5MinuteMark() {
276
- const m = moment().set('second', 0).set('millisecond', 0);
277
- const minute = m.minute();
278
- const mod = minute % 5;
279
- if (mod) {
280
- const delta = 5 - mod;
281
- return m.add(delta, 'm').toDate();
282
- }
283
- return m.toDate();
284
- }
285
- static getYMD(d) {
286
- return moment(d).format('YYYYMMDD');
287
- }
288
- static getDMYWithSlash(d) {
289
- return moment(d).format('DD/MM/YYYY');
290
- }
291
- static getDMYHmWithSlash(d) {
292
- return moment(d).format('DD/MM/YYYY HH:mm');
293
- }
294
- static getMcpTime(dt) {
295
- return moment(dt).format('HH:mm:ss.SSSZ');
296
- }
297
- /**
298
- * In 24 hour format
299
- * @param dtUtc
300
- */
301
- static getLocalDMYHmWithSlash(dtUtc) {
302
- const d = DateFunc.dateTimeUtcToLocalDateTime(dtUtc);
303
- return moment(d).format('DD/MM/YYYY HH:mm');
304
- }
305
- /**
306
- * Offset minutes comparing with today
307
- */
308
- static getOffsetMinutes(dtUtc) {
309
- const dm1 = moment(dtUtc);
310
- const dm2 = moment(new Date().setHours(0, 0, 0, 0));
311
- return dm1.diff(dm2, 'minutes');
312
- }
313
- static getDaysBetween(d1, d2) {
314
- const dm1 = moment(d1);
315
- const dm2 = moment(d2);
316
- return dm2.diff(dm1, 'days');
317
- }
318
- /**
319
- * Get hour of the date. If Date is not defined, the hour will be current hour.
320
- * @param dtUtc
321
- */
322
- static getHour(dtUtc) {
323
- const m = moment(dtUtc);
324
- return m.hours();
325
- }
326
- static getMinute(dtUtc) {
327
- const m = moment(dtUtc);
328
- return m.minutes();
329
- }
330
- static composeDateTime(dt, h = 0, minute = 0) {
331
- const mt = moment(dt);
332
- return new Date(mt.toDate().setHours(h, minute, 0, 0));
333
- }
334
- static olderThan24Hours(d) {
335
- const m = moment(d);
336
- return moment().diff(m, 'hours') >= 24;
337
- }
338
- static olderThan24HoursUtc(dtUtc) {
339
- return DateFunc.getHourAgeUtc(dtUtc) >= 24;
340
- }
341
- static olderThanHours(d, hours) {
342
- const m = moment(d);
343
- return moment().diff(m, 'hours') >= hours;
344
- }
345
- static olderThanHoursUtc(dtUtc, hours) {
346
- return DateFunc.getHourAgeUtc(dtUtc) >= hours;
347
- }
348
- static olderThanMinutes(d, minutes) {
349
- const m = moment(d);
350
- return moment().diff(m, 'minutes') >= minutes;
351
- }
352
- /**
353
- * It could be 11PM yesterday, and 1 AM today. Actually based on local today.
354
- */
355
- static olderThan1Day(dtUtc) {
356
- return DateFunc.getDayAgeUtc(dtUtc) > 0;
357
- }
358
- static getHourAge(d) {
359
- const m = moment(d);
360
- return moment().diff(m, 'hours');
361
- }
362
- static getHourAgeUtc(dtUtc) {
363
- const m = moment.utc(dtUtc);
364
- return moment.utc().diff(m, 'hours');
365
- }
366
- /**
367
- * Compare utc date with utc now.
368
- * @param dtUtc
369
- */
370
- static getDayAgeUtc(dtUtc) {
371
- const m = moment.utc(dtUtc);
372
- return moment.utc().diff(m, 'days');
373
- }
374
- /**
375
- * How many years from now.
376
- * @param d
377
- * @returns
378
- */
379
- static getAge(d) {
380
- const m = moment(d);
381
- return moment().diff(m, 'years');
382
- }
383
- /**
384
- * Year of date.
385
- * @param d
386
- * @returns
387
- */
388
- static getYear(d) {
389
- const m = moment(d);
390
- return m.year();
391
- }
392
- static getUtcNow() {
393
- return moment.utc().toDate();
394
- }
395
- static addMinutes(d, m) {
396
- return moment(d).add(m, 'm').toDate();
397
- }
398
- static addMonth(d, m) {
399
- return moment(d).add(m, 'M').toDate();
400
- }
401
- static getDuration(d1, d2) {
402
- const md1 = moment(d1);
403
- const md2 = moment(d2);
404
- return moment.duration(md2.diff(md1));
405
- }
406
- /**
407
- * Convert minutes from midnight to HH:mm text
408
- * @param mins
409
- */
410
- static getHMFromMins(mins) {
411
- // do not include the first validation check if you want, for example,
412
- // getTimeFromMins(1530) to equal getTimeFromMins(90) (i.e. mins rollover)
413
- if (mins >= 24 * 60 || mins < 0) {
414
- throw new RangeError('Valid input should be greater than or equal to 0 and less than 1440.');
415
- }
416
- const h = mins / 60 | 0, m = mins % 60 | 0;
417
- return moment.utc().hours(h).minutes(m).format('HH:mm');
418
- }
419
- static getMinutesSinceMidnight(d) {
420
- const m = moment(d);
421
- const midnight = moment(d).startOf('day'); //Mutates the original moment by setting it to the start of a unit of time. So I have better not to use m which wil be changed by calling this function
422
- return m.diff(midnight, 'minutes');
423
- }
424
- static getMinutesBetween(start, end) {
425
- const m = moment(start);
426
- const m2 = moment(end);
427
- return m2.diff(m, 'minutes');
428
- }
429
- /**
430
- * Parse json string with date serialized into string, and get proper date object back
431
- * @param s
432
- */
433
- static dateSafeJsonParse(s) {
434
- return JSON.parse(s, this.dateReviver);
435
- }
436
- static dateReviver(key, value) {
437
- if (DateFunc.isSerializedDate(value)) {
438
- return (new Date(value));
439
- }
440
- // If it's not a date-string, we want to return the value as-is. If we fail to return
441
- // a value, it will be omitted from the resultant data structure.
442
- return (value);
443
- }
444
- // I determine if the given value is a string that matches the serialized-date pattern.
445
- static isSerializedDate(value) {
446
- // Dates are serialized in TZ format, example: '1981-12-20T04:00:14.000Z'.
447
- const datePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
448
- return (DateFunc.isString(value) && datePattern.test(value));
449
- }
450
- // I determine if the given value is a String.
451
- static isString(value) {
452
- return ({}.toString.call(value) === '[object String]');
453
- }
454
- static dateSafeParse(s) {
455
- const m = moment(s);
456
- return m.toDate();
457
- }
458
- static composeDateWithMinutes(d, minute) {
459
- const m = moment(d);
460
- const midnight = moment(d).startOf('day'); // Mutates the original moment by setting it to the start of a unit of time. So I have better not to use m which wil be changed by calling this function
461
- midnight.add(minute, 'minutes');
462
- return midnight.toDate();
463
- }
464
- /**
465
- * Safe compare since date data may be considered as string rather than date.
466
- * @param d1
467
- * @param d2
468
- */
469
- static compare(d1, d2) {
470
- if (!d1 && !d2) {
471
- return 0;
472
- }
473
- if (!d1) {
474
- return -NaN;
475
- }
476
- if (!d2) {
477
- return NaN;
478
- }
479
- const dd1 = (new Date(d1)).valueOf();
480
- const dd2 = (new Date(d2)).valueOf();
481
- return dd1 - dd2;
482
- }
173
+ class DateFunc {
174
+ /**
175
+ * Transform UTC DateTime to local date without H, M and S. For example, the month day of 2018-01-23T22:00:00Z is 24 in Australia.
176
+ * @param dtUtc
177
+ * @param offsetMinutes if not defined, it will be new Date().getTimezoneOffset(). //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset
178
+ */
179
+ static dateTimeUtcToLocalDateNumber(dtUtc) {
180
+ if (!dtUtc) {
181
+ return 0; //0 is better for calculation by the clients.
182
+ }
183
+ const localDt = DateFunc.dateTimeUtcToLocalDateTime(dtUtc);
184
+ const localDNum = localDt.setHours(0, 0, 0, 0);
185
+ return localDNum;
186
+ }
187
+ /**
188
+ * Date only. However, the date may still be in UTC.
189
+ * @param dtUtc
190
+ */
191
+ static dateTimeUtcToLocalDate(dtUtc) {
192
+ const localDt = DateFunc.dateTimeUtcToLocalDateTime(dtUtc);
193
+ const localD = localDt.setHours(0, 0, 0, 0);
194
+ return new Date(localD);
195
+ }
196
+ static localISODateString(dtUtc) {
197
+ const dt = moment(dtUtc).local();
198
+ return dt.format('YYYY-MM-DD');
199
+ }
200
+ /**
201
+ * locate date ONLY (no time) to UTC date.
202
+ * @param dt if dt contain time info, it will become dt.setHours(0, 0, 0, 0)
203
+ */
204
+ static localDateToUtc(d) {
205
+ const dt = moment(d).toDate();
206
+ const n = dt.setHours(0, 0, 0, 0);
207
+ const offset = dt.getTimezoneOffset() * 60000;
208
+ return new Date(n + offset);
209
+ }
210
+ static getTimezoneOffset() {
211
+ const dt = this.today;
212
+ return dt.getTimezoneOffset();
213
+ }
214
+ /**
215
+ * Transform UTC DateTime to local dateTime.
216
+ * @param dtUtc
217
+ * @param offsetMinutes if not defined, it will be new Date().getTimezoneOffset(). //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset
218
+ */
219
+ static dateTimeUtcToLocalDateTime(dtUtc) {
220
+ const stillUtc = moment.utc(dtUtc).toDate();
221
+ return moment(stillUtc).local().toDate();
222
+ }
223
+ static dateTimeUtcToLocaMoment(dtUtc) {
224
+ const stillUtc = moment.utc(dtUtc);
225
+ return stillUtc.local();
226
+ }
227
+ static getEndOfWeek(dt) {
228
+ return moment(dt).endOf('isoWeek').toDate();
229
+ }
230
+ static getStartOfWeek(dt) {
231
+ return moment(dt).startOf('isoWeek').toDate();
232
+ }
233
+ static getEndOfMonth(dt) {
234
+ // return new Date(dt.getFullYear(), dt.getMonth() + 1, 0, 23, 59, 59, 999);
235
+ return moment(dt).endOf('month').toDate();
236
+ }
237
+ static getStartOfMonth(dt) {
238
+ return moment(dt).startOf('month').toDate();
239
+ }
240
+ static getDaysBetweenDates(dt1, dt2) {
241
+ return this.getDaysBetween(dt1, dt2);
242
+ }
243
+ static getEndOfDate(dt) {
244
+ return dt ? new Date(dt.setHours(23, 59, 59, 999)) :
245
+ new Date(this.now.setHours(23, 59, 59, 999));
246
+ }
247
+ static getStartOfDate(dt) {
248
+ return moment(dt).startOf('day').toDate();
249
+ }
250
+ static getEndOfToday() {
251
+ // return new Date((new Date(Date.now())).setHours(23, 59, 59, 999));
252
+ return moment(Date.now()).endOf('day').toDate();
253
+ }
254
+ static getStartOfToday() {
255
+ // return new Date((new Date(Date.now())).setHours(0, 0, 0, 0));
256
+ return moment(Date.now()).startOf('day').toDate();
257
+ }
258
+ //inspired https://stackoverflow.com/questions/563406/add-days-to-javascript-date
259
+ static addDays(dt, days = 0) {
260
+ const dat = moment(dt);
261
+ dat.add(days, 'days');
262
+ return dat.toDate();
263
+ }
264
+ static subtractDays(dt, days = 0) {
265
+ const dat = moment(dt);
266
+ dat.subtract(days, 'days');
267
+ return dat.toDate();
268
+ }
269
+ /**
270
+ * Start of today
271
+ */
272
+ static get today() {
273
+ return this.getStartOfToday();
274
+ }
275
+ static get now() {
276
+ return new Date(Date.now());
277
+ }
278
+ static getNext5MinuteMark() {
279
+ const m = moment().set('second', 0).set('millisecond', 0);
280
+ const minute = m.minute();
281
+ const mod = minute % 5;
282
+ if (mod) {
283
+ const delta = 5 - mod;
284
+ return m.add(delta, 'm').toDate();
285
+ }
286
+ return m.toDate();
287
+ }
288
+ static getYMD(d) {
289
+ return moment(d).format('YYYYMMDD');
290
+ }
291
+ static getDMYWithSlash(d) {
292
+ return moment(d).format('DD/MM/YYYY');
293
+ }
294
+ static getDMYHmWithSlash(d) {
295
+ return moment(d).format('DD/MM/YYYY HH:mm');
296
+ }
297
+ static getMcpTime(dt) {
298
+ return moment(dt).format('HH:mm:ss.SSSZ');
299
+ }
300
+ /**
301
+ * In 24 hour format
302
+ * @param dtUtc
303
+ */
304
+ static getLocalDMYHmWithSlash(dtUtc) {
305
+ const d = DateFunc.dateTimeUtcToLocalDateTime(dtUtc);
306
+ return moment(d).format('DD/MM/YYYY HH:mm');
307
+ }
308
+ /**
309
+ * Offset minutes comparing with today
310
+ */
311
+ static getOffsetMinutes(dtUtc) {
312
+ const dm1 = moment(dtUtc);
313
+ const dm2 = moment(new Date().setHours(0, 0, 0, 0));
314
+ return dm1.diff(dm2, 'minutes');
315
+ }
316
+ static getDaysBetween(d1, d2) {
317
+ const dm1 = moment(d1);
318
+ const dm2 = moment(d2);
319
+ return dm2.diff(dm1, 'days');
320
+ }
321
+ /**
322
+ * Get hour of the date. If Date is not defined, the hour will be current hour.
323
+ * @param dtUtc
324
+ */
325
+ static getHour(dtUtc) {
326
+ const m = moment(dtUtc);
327
+ return m.hours();
328
+ }
329
+ static getMinute(dtUtc) {
330
+ const m = moment(dtUtc);
331
+ return m.minutes();
332
+ }
333
+ static composeDateTime(dt, h = 0, minute = 0) {
334
+ const mt = moment(dt);
335
+ return new Date(mt.toDate().setHours(h, minute, 0, 0));
336
+ }
337
+ static olderThan24Hours(d) {
338
+ const m = moment(d);
339
+ return moment().diff(m, 'hours') >= 24;
340
+ }
341
+ static olderThan24HoursUtc(dtUtc) {
342
+ return DateFunc.getHourAgeUtc(dtUtc) >= 24;
343
+ }
344
+ static olderThanHours(d, hours) {
345
+ const m = moment(d);
346
+ return moment().diff(m, 'hours') >= hours;
347
+ }
348
+ static olderThanHoursUtc(dtUtc, hours) {
349
+ return DateFunc.getHourAgeUtc(dtUtc) >= hours;
350
+ }
351
+ static olderThanMinutes(d, minutes) {
352
+ const m = moment(d);
353
+ return moment().diff(m, 'minutes') >= minutes;
354
+ }
355
+ /**
356
+ * It could be 11PM yesterday, and 1 AM today. Actually based on local today.
357
+ */
358
+ static olderThan1Day(dtUtc) {
359
+ return DateFunc.getDayAgeUtc(dtUtc) > 0;
360
+ }
361
+ static getHourAge(d) {
362
+ const m = moment(d);
363
+ return moment().diff(m, 'hours');
364
+ }
365
+ static getHourAgeUtc(dtUtc) {
366
+ const m = moment.utc(dtUtc);
367
+ return moment.utc().diff(m, 'hours');
368
+ }
369
+ /**
370
+ * Compare utc date with utc now.
371
+ * @param dtUtc
372
+ */
373
+ static getDayAgeUtc(dtUtc) {
374
+ const m = moment.utc(dtUtc);
375
+ return moment.utc().diff(m, 'days');
376
+ }
377
+ /**
378
+ * How many years from now.
379
+ * @param d
380
+ * @returns
381
+ */
382
+ static getAge(d) {
383
+ const m = moment(d);
384
+ return moment().diff(m, 'years');
385
+ }
386
+ /**
387
+ * Year of date.
388
+ * @param d
389
+ * @returns
390
+ */
391
+ static getYear(d) {
392
+ const m = moment(d);
393
+ return m.year();
394
+ }
395
+ static getUtcNow() {
396
+ return moment.utc().toDate();
397
+ }
398
+ static addMinutes(d, m) {
399
+ return moment(d).add(m, 'm').toDate();
400
+ }
401
+ static addMonth(d, m) {
402
+ return moment(d).add(m, 'M').toDate();
403
+ }
404
+ static getDuration(d1, d2) {
405
+ const md1 = moment(d1);
406
+ const md2 = moment(d2);
407
+ return moment.duration(md2.diff(md1));
408
+ }
409
+ /**
410
+ * Convert minutes from midnight to HH:mm text
411
+ * @param mins
412
+ */
413
+ static getHMFromMins(mins) {
414
+ // do not include the first validation check if you want, for example,
415
+ // getTimeFromMins(1530) to equal getTimeFromMins(90) (i.e. mins rollover)
416
+ if (mins >= 24 * 60 || mins < 0) {
417
+ throw new RangeError('Valid input should be greater than or equal to 0 and less than 1440.');
418
+ }
419
+ const h = mins / 60 | 0, m = mins % 60 | 0;
420
+ return moment.utc().hours(h).minutes(m).format('HH:mm');
421
+ }
422
+ static getMinutesSinceMidnight(d) {
423
+ const m = moment(d);
424
+ const midnight = moment(d).startOf('day'); //Mutates the original moment by setting it to the start of a unit of time. So I have better not to use m which wil be changed by calling this function
425
+ return m.diff(midnight, 'minutes');
426
+ }
427
+ static getMinutesBetween(start, end) {
428
+ const m = moment(start);
429
+ const m2 = moment(end);
430
+ return m2.diff(m, 'minutes');
431
+ }
432
+ /**
433
+ * Parse json string with date serialized into string, and get proper date object back
434
+ * @param s
435
+ */
436
+ static dateSafeJsonParse(s) {
437
+ return JSON.parse(s, this.dateReviver);
438
+ }
439
+ static dateReviver(key, value) {
440
+ if (DateFunc.isSerializedDate(value)) {
441
+ return (new Date(value));
442
+ }
443
+ // If it's not a date-string, we want to return the value as-is. If we fail to return
444
+ // a value, it will be omitted from the resultant data structure.
445
+ return (value);
446
+ }
447
+ // I determine if the given value is a string that matches the serialized-date pattern.
448
+ static isSerializedDate(value) {
449
+ // Dates are serialized in TZ format, example: '1981-12-20T04:00:14.000Z'.
450
+ const datePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
451
+ return (DateFunc.isString(value) && datePattern.test(value));
452
+ }
453
+ // I determine if the given value is a String.
454
+ static isString(value) {
455
+ return ({}.toString.call(value) === '[object String]');
456
+ }
457
+ static dateSafeParse(s) {
458
+ const m = moment(s);
459
+ return m.toDate();
460
+ }
461
+ static composeDateWithMinutes(d, minute) {
462
+ const m = moment(d);
463
+ const midnight = moment(d).startOf('day'); // Mutates the original moment by setting it to the start of a unit of time. So I have better not to use m which wil be changed by calling this function
464
+ midnight.add(minute, 'minutes');
465
+ return midnight.toDate();
466
+ }
467
+ /**
468
+ * Safe compare since date data may be considered as string rather than date.
469
+ * @param d1
470
+ * @param d2
471
+ */
472
+ static compare(d1, d2) {
473
+ if (!d1 && !d2) {
474
+ return 0;
475
+ }
476
+ if (!d1) {
477
+ return -NaN;
478
+ }
479
+ if (!d2) {
480
+ return NaN;
481
+ }
482
+ const dd1 = (new Date(d1)).valueOf();
483
+ const dd2 = (new Date(d2)).valueOf();
484
+ return dd1 - dd2;
485
+ }
483
486
  }
484
487
 
485
- class HtmlPrintFunc {
486
- /**
487
- * Print with CSS for internal reports
488
- * @param htmlTags
489
- * @param cssUrl
490
- */
491
- static printWithCSS(htmlTags, cssUrl) {
492
- if (window) {
488
+ class HtmlPrintFunc {
489
+ /**
490
+ * Print with CSS for internal reports
491
+ * @param htmlTags
492
+ * @param cssUrl
493
+ */
494
+ static printWithCSS(htmlTags, cssUrl) {
495
+ if (window) {
493
496
  const htmlToPrint = `<html><head>
494
497
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
495
498
  <link rel="stylesheet" type="text/css" href="${cssUrl}" media="screen,print"/>
496
- </head><body onload="window.print()">${htmlTags}</body></html>`;
497
- const popup = window.open('', '_blank', 'width=1024,height=768');
498
- popup?.document.open();
499
- popup?.document.write(htmlToPrint);
500
- popup?.document.close();
501
- }
502
- return true;
503
- }
504
- /**
505
- * Print for external documents.
506
- * @param htmlTags
507
- */
508
- static print(htmlTags) {
509
- if (window) {
499
+ </head><body onload="window.print()">${htmlTags}</body></html>`;
500
+ const popup = window.open('', '_blank', 'width=1024,height=768');
501
+ popup?.document.open();
502
+ popup?.document.write(htmlToPrint);
503
+ popup?.document.close();
504
+ }
505
+ return true;
506
+ }
507
+ /**
508
+ * Print for external documents.
509
+ * @param htmlTags
510
+ */
511
+ static print(htmlTags) {
512
+ if (window) {
510
513
  const htmlToPrint = `<html><head>
511
514
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
512
- </head><body onload="window.print()">${htmlTags}</body></html>`;
513
- const popup = window.open('', '_blank', 'width=1024,height=768');
514
- popup?.document.open();
515
- popup?.document.write(htmlToPrint);
516
- popup?.document.close();
517
- }
518
- return true;
519
- }
520
- /**
521
- * Print image url through html img.
522
- * @param url
523
- */
524
- static printImage(url) {
525
- const imageTags = `<img src="${url}" alt="Image from URL"/>`;
526
- HtmlPrintFunc.print(imageTags);
527
- }
515
+ </head><body onload="window.print()">${htmlTags}</body></html>`;
516
+ const popup = window.open('', '_blank', 'width=1024,height=768');
517
+ popup?.document.open();
518
+ popup?.document.write(htmlToPrint);
519
+ popup?.document.close();
520
+ }
521
+ return true;
522
+ }
523
+ /**
524
+ * Print image url through html img.
525
+ * @param url
526
+ */
527
+ static printImage(url) {
528
+ const imageTags = `<img src="${url}" alt="Image from URL"/>`;
529
+ HtmlPrintFunc.print(imageTags);
530
+ }
528
531
  }
529
532
 
530
- class JavaScriptFunc {
531
- /**
532
- * Some business functions depend on external JavaScript libraries. Lazy loading of respective business modules is good,
533
- * and this function supports lazy loading of JS libraries.
534
- * @param scriptUrl
535
- * @returns Promise for subsequent JS function calls.
536
- */
537
- static loadExternalScript(scriptUrl) {
538
- return new Promise((resolve, reject) => {
539
- const scriptElement = document.createElement('script');
540
- scriptElement.src = scriptUrl;
541
- scriptElement.onload = resolve;
542
- document.body.appendChild(scriptElement);
543
- });
544
- }
533
+ class JavaScriptFunc {
534
+ /**
535
+ * Some business functions depend on external JavaScript libraries. Lazy loading of respective business modules is good,
536
+ * and this function supports lazy loading of JS libraries.
537
+ * @param scriptUrl
538
+ * @param type things like module.
539
+ * @returns Promise for subsequent JS function calls.
540
+ */
541
+ static loadExternalScript(scriptUrl, type) {
542
+ return new Promise((resolve, reject) => {
543
+ const scriptElement = document.createElement('script');
544
+ if (type) {
545
+ scriptElement.type = type;
546
+ }
547
+ scriptElement.src = scriptUrl;
548
+ scriptElement.onload = resolve;
549
+ document.body.appendChild(scriptElement);
550
+ });
551
+ }
545
552
  }
546
553
 
547
- /**
548
- * Basic JSON functions
549
- */
550
- class JsonFunc {
551
- /**
552
- *
553
- * @param array Group by a property of array element.
554
- * @param propertyName
555
- * @returns
556
- */
557
- static groupBy(array, propertyName) {
558
- return array.reduce(function (acc, obj) {
559
- const key = obj[propertyName];
560
- if (!acc[key]) {
561
- acc[key] = [];
562
- }
563
- acc[key].push(obj);
564
- return acc;
565
- }, {});
566
- }
567
- /**
568
- * Group by a date property. The key is always of string type and representing milliseconds.
569
- * The client should convert the string to number.
570
- * Angular date pipe could actually consume such string without explicitly converting to number.
571
- * @param array
572
- * @param propertyName
573
- */
574
- static groupByDate(array, propertyName) {
575
- return array.reduce(function (acc, obj) {
576
- const key = DateFunc.dateTimeUtcToLocalDateNumber(obj[propertyName]);
577
- if (!acc[key]) {
578
- acc[key] = [];
579
- }
580
- acc[key].push(obj);
581
- return acc;
582
- }, {});
583
- }
584
- /**
585
- * Remove null or empty fields including those in nested objects.
586
- * This is useful for reducing payload of AJAX serialization.
587
- * @param obj
588
- */
589
- static removeNullOrEmptyFields(obj) {
590
- for (const f in obj) {
591
- let p = obj[f];
592
- if (p === null || p === '') {
593
- delete obj[f];
594
- }
595
- else if (typeof p === 'object' && p !== null) {
596
- this.removeNullOrEmptyFields(p);
597
- }
598
- }
599
- }
600
- /**
601
- *
602
- * @param obj Remove null fields of object at only the 1st level.
603
- */
604
- static removeNullFields(obj) {
605
- for (const f in obj) {
606
- if (obj[f] === null) {
607
- delete obj[f];
608
- }
609
- }
610
- }
554
+ /**
555
+ * Basic JSON functions
556
+ */
557
+ class JsonFunc {
558
+ /**
559
+ *
560
+ * @param array Group by a property of array element.
561
+ * @param propertyName
562
+ * @returns
563
+ */
564
+ static groupBy(array, propertyName) {
565
+ return array.reduce(function (acc, obj) {
566
+ const key = obj[propertyName];
567
+ if (!acc[key]) {
568
+ acc[key] = [];
569
+ }
570
+ acc[key].push(obj);
571
+ return acc;
572
+ }, {});
573
+ }
574
+ /**
575
+ * Group by a date property. The key is always of string type and representing milliseconds.
576
+ * The client should convert the string to number.
577
+ * Angular date pipe could actually consume such string without explicitly converting to number.
578
+ * @param array
579
+ * @param propertyName
580
+ */
581
+ static groupByDate(array, propertyName) {
582
+ return array.reduce(function (acc, obj) {
583
+ const key = DateFunc.dateTimeUtcToLocalDateNumber(obj[propertyName]);
584
+ if (!acc[key]) {
585
+ acc[key] = [];
586
+ }
587
+ acc[key].push(obj);
588
+ return acc;
589
+ }, {});
590
+ }
591
+ /**
592
+ * Remove null or empty fields including those in nested objects.
593
+ * This is useful for reducing payload of AJAX serialization.
594
+ * @param obj
595
+ */
596
+ static removeNullOrEmptyFields(obj) {
597
+ for (const f in obj) {
598
+ let p = obj[f];
599
+ if (p === null || p === '') {
600
+ delete obj[f];
601
+ }
602
+ else if (typeof p === 'object' && p !== null) {
603
+ this.removeNullOrEmptyFields(p);
604
+ }
605
+ }
606
+ }
607
+ /**
608
+ *
609
+ * @param obj Remove null fields of object at only the 1st level.
610
+ */
611
+ static removeNullFields(obj) {
612
+ for (const f in obj) {
613
+ if (obj[f] === null) {
614
+ delete obj[f];
615
+ }
616
+ }
617
+ }
611
618
  }
612
619
 
613
- /**
614
- * String functions specific to Australia
615
- */
616
- class StringAusFunc {
617
- /**
618
- * Validate medicare number
619
- * @param n
620
- * @returns validation error message
621
- */
622
- static validateMedicare(n) {
623
- if (!n) {
624
- return null;
625
- }
626
- if (n && n.length === 10) {
627
- const matches = n.match(/^(\d{8})(\d)/);
628
- if (!matches) {
629
- return {
630
- code: 2, message: 'Medicare number should be all digit.'
631
- };
632
- }
633
- const base = matches[1];
634
- const checkDigit = matches[2];
635
- const weights = [1, 3, 7, 9, 1, 3, 7, 9];
636
- let sum = 0;
637
- for (let i = 0; i < weights.length; i++) {
638
- sum += parseInt(base[i], 10) * weights[i];
639
- }
640
- //console.debug(`sum: ${sum} checkDigits: ${checkDigit}`);
641
- const isValid = sum % 10 === parseInt(checkDigit, 10);
642
- if (!isValid) {
643
- return {
644
- code: 3, message: 'Checksum is incorrect.'
645
- };
646
- }
647
- }
648
- else {
649
- return { code: 1, message: 'Length should be 10.' };
650
- }
651
- return null;
652
- }
653
- static validateMedicareProviderNumber(providerNumber) {
654
- if (!providerNumber) {
655
- return null;
656
- }
657
- if (!/^[0-9]{6}[0-9ABCDEFGHJKLMNPQRTUVWXY][ABFHJKLTWXY]/.test(providerNumber)) {
658
- return { code: 1, message: 'Not matching provider number format.' };
659
- }
660
- const practiceLocationValues = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'V', 'W', 'X', 'Y'];
661
- const remainderValues = ['Y', 'X', 'W', 'T', 'L', 'K', 'J', 'H', 'F', 'B', 'A'];
662
- const stemWeights = [3, 5, 8, 4, 2, 1];
663
- const stemNumbers = providerNumber.substr(0, 6).split('').map((char) => parseInt(char));
664
- const practiceLoc = practiceLocationValues.findIndex((e) => e === providerNumber[6]);
665
- const checksum = providerNumber[7];
666
- const zipped = stemWeights.map((x, i) => [x, stemNumbers[i]]);
667
- const sumWeights = zipped.map((y) => y[1] * y[0]).reduce((total, num) => total + num);
668
- const remainder = (sumWeights + practiceLoc * 6) % 11;
669
- const result = remainderValues[remainder];
670
- const valid = result === checksum;
671
- if (!valid) {
672
- return { code: 2, message: 'Checksum is incorrect.' };
673
- }
674
- return null;
675
- }
676
- static validateDVAFileNumber(dva) {
677
- if (!dva) {
678
- return null;
679
- }
680
- const states = ['N', 'V', 'Q', 'S', 'W', 'T'];
681
- const warCodes = [' ', 'X', 'KM', 'SR', 'SS', 'SM', 'SWP', 'AGX', 'BW', 'GW', 'CGW',
682
- 'P', 'PX', 'PAD', 'PAM', 'PCA', 'PCR', 'PCV', 'PMS', 'PSW', 'PWO', 'HKX', 'MAL',
683
- 'CN', 'CNX', 'IV', 'NF', 'NG', 'RD', 'RDX', 'SA', 'SAX', 'A',
684
- 'N', 'NX', 'NSW', 'NSM',
685
- 'BUR', 'CNK', 'CNS', 'FIJ', 'GHA', 'HKS', 'IND', 'KYA', 'MAU', 'MLS', 'MTX', 'MWI', 'NK', 'NGR', 'NRD', 'NSS', 'PK']; //British Commonwealth Countries - SP Eligibility
686
- if (!states.includes(dva.charAt(0))) {
687
- return { code: 1, message: 'State incorrect.' };
688
- }
689
- const ns = dva.match(/\d+/);
690
- if (!ns) {
691
- return { code: 2, message: 'No number.' };
692
- }
693
- const n = ns[0];
694
- const idxOfN = dva.indexOf(n);
695
- const warCode = dva.substring(1, idxOfN);
696
- if (!warCodes.includes(warCode)) {
697
- return { code: 3, message: 'War code invalid.' };
698
- }
699
- const lenOfN = n.length;
700
- const lenOfWc = warCode.length;
701
- if (lenOfN + lenOfWc > 7) {
702
- return { code: 4, message: 'File number length should not be greater 7.' };
703
- }
704
- return null;
705
- }
706
- static validateTFN(n) {
707
- if (!n) {
708
- return null;
709
- }
710
- const tfn = n.replace(/\s+/g, '').replace(/[-]/g, '');
711
- const isNumber = /^[0-9]+$/.test(tfn);
712
- if (!isNumber) {
713
- return { code: 1, message: 'Invalid TFN, only numbers are allowed.' };
714
- }
715
- const length = tfn.length;
716
- if (length !== 9) {
717
- return {
718
- code: 2, message: 'Invalid TFN, must have 9 digits.'
719
- };
720
- }
721
- const digits = tfn.split('').map(d => parseInt(d));
722
- const sum = (digits[0] * 1)
723
- + (digits[1] * 4)
724
- + (digits[2] * 3)
725
- + (digits[3] * 7)
726
- + (digits[4] * 5)
727
- + (digits[5] * 8)
728
- + (digits[6] * 6)
729
- + (digits[7] * 9)
730
- + (digits[8] * 10);
731
- const remainder = sum % 11;
732
- const valid = remainder === 0;
733
- if (!valid) {
734
- return { code: 3, message: 'Checksum is incorrect.' };
735
- }
736
- return null;
737
- }
738
- static addWeighted(p, v, i) {
739
- return p + v * StringAusFunc.weights[i];
740
- }
741
- static addAcnWeighted(p, v, i) {
742
- return p + v * StringAusFunc.acnWeights[i];
743
- }
744
- static generateLookup() {
745
- const ns = [];
746
- for (let i = 0; i < 10; ++i) {
747
- ns[i * 19 % 89] = i;
748
- }
749
- return ns;
750
- }
751
- static validateABN(abn) {
752
- if (!abn) {
753
- return null;
754
- }
755
- const digits = abn.replace(/[^\d]/g, '').split('').map(Number);
756
- if (digits.length !== 11) {
757
- return { code: 1, message: 'Expect 11-digit.' };
758
- }
759
- digits[0] -= 1;
760
- const sum = digits.reduce(StringAusFunc.addWeighted, 0);
761
- if (sum % 89 === 0) {
762
- return null;
763
- }
764
- digits[0] += 1;
765
- let sum1 = sum - digits[10] * StringAusFunc.weights[10];
766
- let digit = StringAusFunc.suggestLookup[89 - sum1 % 89];
767
- if (digit !== undefined) {
768
- return {
769
- code: 2,
770
- message: 'Checksum1 is incorrect.'
771
- };
772
- }
773
- else {
774
- const sum2 = sum1 - digits[9] * StringAusFunc.weights[9];
775
- for (let i = 0; i < 10; ++i) {
776
- sum1 = sum2 + i * StringAusFunc.weights[9];
777
- digit = StringAusFunc.suggestLookup[89 - sum1 % 89];
778
- if (digit !== undefined) {
779
- return {
780
- code: 3,
781
- message: 'Checksum2 is incorrect.'
782
- };
783
- }
784
- }
785
- }
786
- return null;
787
- }
788
- static validateACN(acn) {
789
- if (!acn) {
790
- return null;
791
- }
792
- const digits = acn.replace(/[^\d]/g, '').split('').map(Number);
793
- console.log(digits);
794
- if (digits.length !== 9) {
795
- return { code: 1, message: 'Expect 9-digit.' };
796
- }
797
- const sum = digits.slice(0, 8).reduce(StringAusFunc.addAcnWeighted, 0);
798
- const lastDigit = 10 - sum % 10;
799
- if (lastDigit === digits[8]) {
800
- return null;
801
- }
802
- return {
803
- code: 2,
804
- message: 'Checksum is incorrect.'
805
- };
806
- }
807
- }
808
- //thanks to https://github.com/sidorares/australian-business-number/blob/0591475f5978fd122b472edcdc7efe6d96d56f26/index.js
809
- StringAusFunc.acnWeights = [8, 7, 6, 5, 4, 3, 2, 1];
810
- StringAusFunc.weights = [10, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19];
811
- StringAusFunc.suggestLookup = StringAusFunc.generateLookup();
620
+ /**
621
+ * String functions specific to Australia
622
+ */
623
+ class StringAusFunc {
624
+ //thanks to https://github.com/sidorares/australian-business-number/blob/0591475f5978fd122b472edcdc7efe6d96d56f26/index.js
625
+ static acnWeights = [8, 7, 6, 5, 4, 3, 2, 1];
626
+ static weights = [10, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19];
627
+ static suggestLookup = StringAusFunc.generateLookup();
628
+ /**
629
+ * Validate medicare number
630
+ * @param n
631
+ * @returns validation error message
632
+ */
633
+ static validateMedicare(n) {
634
+ if (!n) {
635
+ return null;
636
+ }
637
+ if (n && n.length === 10) {
638
+ const matches = n.match(/^(\d{8})(\d)/);
639
+ if (!matches) {
640
+ return {
641
+ code: 2, message: 'Medicare number should be all digit.'
642
+ };
643
+ }
644
+ const base = matches[1];
645
+ const checkDigit = matches[2];
646
+ const weights = [1, 3, 7, 9, 1, 3, 7, 9];
647
+ let sum = 0;
648
+ for (let i = 0; i < weights.length; i++) {
649
+ sum += parseInt(base[i], 10) * weights[i];
650
+ }
651
+ //console.debug(`sum: ${sum} checkDigits: ${checkDigit}`);
652
+ const isValid = sum % 10 === parseInt(checkDigit, 10);
653
+ if (!isValid) {
654
+ return {
655
+ code: 3, message: 'Checksum is incorrect.'
656
+ };
657
+ }
658
+ }
659
+ else {
660
+ return { code: 1, message: 'Length should be 10.' };
661
+ }
662
+ return null;
663
+ }
664
+ static validateMedicareProviderNumber(providerNumber) {
665
+ if (!providerNumber) {
666
+ return null;
667
+ }
668
+ if (!/^[0-9]{6}[0-9ABCDEFGHJKLMNPQRTUVWXY][ABFHJKLTWXY]/.test(providerNumber)) {
669
+ return { code: 1, message: 'Not matching provider number format.' };
670
+ }
671
+ const practiceLocationValues = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'V', 'W', 'X', 'Y'];
672
+ const remainderValues = ['Y', 'X', 'W', 'T', 'L', 'K', 'J', 'H', 'F', 'B', 'A'];
673
+ const stemWeights = [3, 5, 8, 4, 2, 1];
674
+ const stemNumbers = providerNumber.substr(0, 6).split('').map((char) => parseInt(char));
675
+ const practiceLoc = practiceLocationValues.findIndex((e) => e === providerNumber[6]);
676
+ const checksum = providerNumber[7];
677
+ const zipped = stemWeights.map((x, i) => [x, stemNumbers[i]]);
678
+ const sumWeights = zipped.map((y) => y[1] * y[0]).reduce((total, num) => total + num);
679
+ const remainder = (sumWeights + practiceLoc * 6) % 11;
680
+ const result = remainderValues[remainder];
681
+ const valid = result === checksum;
682
+ if (!valid) {
683
+ return { code: 2, message: 'Checksum is incorrect.' };
684
+ }
685
+ return null;
686
+ }
687
+ static validateDVAFileNumber(dva) {
688
+ if (!dva) {
689
+ return null;
690
+ }
691
+ const states = ['N', 'V', 'Q', 'S', 'W', 'T'];
692
+ const warCodes = [' ', 'X', 'KM', 'SR', 'SS', 'SM', 'SWP', 'AGX', 'BW', 'GW', 'CGW',
693
+ 'P', 'PX', 'PAD', 'PAM', 'PCA', 'PCR', 'PCV', 'PMS', 'PSW', 'PWO', 'HKX', 'MAL',
694
+ 'CN', 'CNX', 'IV', 'NF', 'NG', 'RD', 'RDX', 'SA', 'SAX', 'A',
695
+ 'N', 'NX', 'NSW', 'NSM',
696
+ 'BUR', 'CNK', 'CNS', 'FIJ', 'GHA', 'HKS', 'IND', 'KYA', 'MAU', 'MLS', 'MTX', 'MWI', 'NK', 'NGR', 'NRD', 'NSS', 'PK']; //British Commonwealth Countries - SP Eligibility
697
+ if (!states.includes(dva.charAt(0))) {
698
+ return { code: 1, message: 'State incorrect.' };
699
+ }
700
+ const ns = dva.match(/\d+/);
701
+ if (!ns) {
702
+ return { code: 2, message: 'No number.' };
703
+ }
704
+ const n = ns[0];
705
+ const idxOfN = dva.indexOf(n);
706
+ const warCode = dva.substring(1, idxOfN);
707
+ if (!warCodes.includes(warCode)) {
708
+ return { code: 3, message: 'War code invalid.' };
709
+ }
710
+ const lenOfN = n.length;
711
+ const lenOfWc = warCode.length;
712
+ if (lenOfN + lenOfWc > 7) {
713
+ return { code: 4, message: 'File number length should not be greater 7.' };
714
+ }
715
+ return null;
716
+ }
717
+ static validateTFN(n) {
718
+ if (!n) {
719
+ return null;
720
+ }
721
+ const tfn = n.replace(/\s+/g, '').replace(/[-]/g, '');
722
+ const isNumber = /^[0-9]+$/.test(tfn);
723
+ if (!isNumber) {
724
+ return { code: 1, message: 'Invalid TFN, only numbers are allowed.' };
725
+ }
726
+ const length = tfn.length;
727
+ if (length !== 9) {
728
+ return {
729
+ code: 2, message: 'Invalid TFN, must have 9 digits.'
730
+ };
731
+ }
732
+ const digits = tfn.split('').map(d => parseInt(d));
733
+ const sum = (digits[0] * 1)
734
+ + (digits[1] * 4)
735
+ + (digits[2] * 3)
736
+ + (digits[3] * 7)
737
+ + (digits[4] * 5)
738
+ + (digits[5] * 8)
739
+ + (digits[6] * 6)
740
+ + (digits[7] * 9)
741
+ + (digits[8] * 10);
742
+ const remainder = sum % 11;
743
+ const valid = remainder === 0;
744
+ if (!valid) {
745
+ return { code: 3, message: 'Checksum is incorrect.' };
746
+ }
747
+ return null;
748
+ }
749
+ static addWeighted(p, v, i) {
750
+ return p + v * StringAusFunc.weights[i];
751
+ }
752
+ static addAcnWeighted(p, v, i) {
753
+ return p + v * StringAusFunc.acnWeights[i];
754
+ }
755
+ static generateLookup() {
756
+ const ns = [];
757
+ for (let i = 0; i < 10; ++i) {
758
+ ns[i * 19 % 89] = i;
759
+ }
760
+ return ns;
761
+ }
762
+ static validateABN(abn) {
763
+ if (!abn) {
764
+ return null;
765
+ }
766
+ const digits = abn.replace(/[^\d]/g, '').split('').map(Number);
767
+ if (digits.length !== 11) {
768
+ return { code: 1, message: 'Expect 11-digit.' };
769
+ }
770
+ digits[0] -= 1;
771
+ const sum = digits.reduce(StringAusFunc.addWeighted, 0);
772
+ if (sum % 89 === 0) {
773
+ return null;
774
+ }
775
+ digits[0] += 1;
776
+ let sum1 = sum - digits[10] * StringAusFunc.weights[10];
777
+ let digit = StringAusFunc.suggestLookup[89 - sum1 % 89];
778
+ if (digit !== undefined) {
779
+ return {
780
+ code: 2,
781
+ message: 'Checksum1 is incorrect.'
782
+ };
783
+ }
784
+ else {
785
+ const sum2 = sum1 - digits[9] * StringAusFunc.weights[9];
786
+ for (let i = 0; i < 10; ++i) {
787
+ sum1 = sum2 + i * StringAusFunc.weights[9];
788
+ digit = StringAusFunc.suggestLookup[89 - sum1 % 89];
789
+ if (digit !== undefined) {
790
+ return {
791
+ code: 3,
792
+ message: 'Checksum2 is incorrect.'
793
+ };
794
+ }
795
+ }
796
+ }
797
+ return null;
798
+ }
799
+ static validateACN(acn) {
800
+ if (!acn) {
801
+ return null;
802
+ }
803
+ const digits = acn.replace(/[^\d]/g, '').split('').map(Number);
804
+ console.log(digits);
805
+ if (digits.length !== 9) {
806
+ return { code: 1, message: 'Expect 9-digit.' };
807
+ }
808
+ const sum = digits.slice(0, 8).reduce(StringAusFunc.addAcnWeighted, 0);
809
+ const lastDigit = 10 - sum % 10;
810
+ if (lastDigit === digits[8]) {
811
+ return null;
812
+ }
813
+ return {
814
+ code: 2,
815
+ message: 'Checksum is incorrect.'
816
+ };
817
+ }
818
+ }
812
819
 
813
- class StringFunc {
814
- /**
815
- * Up to 2 letters. For John Smith, returns JS, for Huang, Zijian, returns ZH
816
- * @param s
817
- */
818
- static getAbbr(s) {
819
- if (!s) {
820
- return '';
821
- }
822
- const sArray = s.split(/[\s,]+/);
823
- const comma = s.indexOf(',') >= 0;
824
- if (sArray.length === 1) {
825
- return sArray[0][0];
826
- }
827
- return comma ? sArray[1][0] + sArray[0][0] : sArray[0][0] + sArray[1][0];
828
- }
829
- /**
830
- * A substring with line breaks replaced by space.
831
- * @param s
832
- * @param length
833
- * @returns result, or empty string if the input is empty, null or undefined
834
- */
835
- static getOneLineDigest(s, length) {
836
- if (!s) {
837
- return '';
838
- }
839
- const ss = s.substring(0, length);
840
- const st = ss.replace(new RegExp('\n', 'g'), ' ') + ((s.length > length) ? '...' : '');
841
- return st.trim();
842
- }
843
- /**
844
- * Remove line breaks and econde with encodeURI() so the data could be saved in Azure as meta. If the string is truncated, the return will have ... suffix.
845
- * @param s
846
- * @param length
847
- * @returns result, or empty string if the input is empty, null or undefined
848
- */
849
- static getOneLineDigestOfHtml(s, length) {
850
- if (!s) {
851
- return '';
852
- }
853
- const ss = s.substring(0, length);
854
- const st = ss.replace(new RegExp('\n', 'g'), ' ') + ((s.length > length) ? '...' : '');
855
- return encodeURI(st.trim()); //need to encode in order to save as meta in Azure.
856
- }
857
- /**
858
- * Pad number with zero
859
- * @param num
860
- * @param size
861
- * @returns
862
- */
863
- static pad(num, size) {
864
- if (num == null) {
865
- return '';
866
- }
867
- let s = num + '';
868
- while (s.length < size) {
869
- s = '0' + s;
870
- }
871
- return s;
872
- }
873
- /**
874
- * get plain text of HTML content
875
- * @param s
876
- * @returns result. If input is empty, null, or undefined, return the same.
877
- */
878
- static getHtmlPlainText(s) {
879
- if (!s) {
880
- return null;
881
- }
882
- const parser = new DOMParser();
883
- const html = parser.parseFromString(s, 'text/html');
884
- return html.body.textContent;
885
- }
886
- /**
887
- *
888
- * @param s
889
- * @returns result. If input is empty, null, or undefined, return the same.
890
- */
891
- static capitalizeWords(s) {
892
- if (!s) {
893
- return s;
894
- }
895
- return s.replace(/(?:^|\s)\S/g, (a) => a.toUpperCase());
896
- }
897
- static validateEmail(email) {
898
- if (!email) {
899
- return true;
900
- }
901
- const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
902
- return re.test(email.toLowerCase());
903
- }
820
+ class StringFunc {
821
+ /**
822
+ * Up to 2 letters. For John Smith, returns JS, for Huang, Zijian, returns ZH
823
+ * @param s
824
+ */
825
+ static getAbbr(s) {
826
+ if (!s) {
827
+ return '';
828
+ }
829
+ const sArray = s.split(/[\s,]+/);
830
+ const comma = s.indexOf(',') >= 0;
831
+ if (sArray.length === 1) {
832
+ return sArray[0][0];
833
+ }
834
+ return comma ? sArray[1][0] + sArray[0][0] : sArray[0][0] + sArray[1][0];
835
+ }
836
+ /**
837
+ * A substring with line breaks replaced by space.
838
+ * @param s
839
+ * @param length
840
+ * @returns result, or empty string if the input is empty, null or undefined
841
+ */
842
+ static getOneLineDigest(s, length) {
843
+ if (!s) {
844
+ return '';
845
+ }
846
+ const ss = s.substring(0, length);
847
+ const st = ss.replace(new RegExp('\n', 'g'), ' ') + ((s.length > length) ? '...' : '');
848
+ return st.trim();
849
+ }
850
+ /**
851
+ * Remove line breaks and econde with encodeURI() so the data could be saved in Azure as meta. If the string is truncated, the return will have ... suffix.
852
+ * @param s
853
+ * @param length
854
+ * @returns result, or empty string if the input is empty, null or undefined
855
+ */
856
+ static getOneLineDigestOfHtml(s, length) {
857
+ if (!s) {
858
+ return '';
859
+ }
860
+ const ss = s.substring(0, length);
861
+ const st = ss.replace(new RegExp('\n', 'g'), ' ') + ((s.length > length) ? '...' : '');
862
+ return encodeURI(st.trim()); //need to encode in order to save as meta in Azure.
863
+ }
864
+ /**
865
+ * Pad number with zero
866
+ * @param num
867
+ * @param size
868
+ * @returns
869
+ */
870
+ static pad(num, size) {
871
+ if (num == null) {
872
+ return '';
873
+ }
874
+ let s = num + '';
875
+ while (s.length < size) {
876
+ s = '0' + s;
877
+ }
878
+ return s;
879
+ }
880
+ /**
881
+ * get plain text of HTML content
882
+ * @param s
883
+ * @returns result. If input is empty, null, or undefined, return the same.
884
+ */
885
+ static getHtmlPlainText(s) {
886
+ if (!s) {
887
+ return null;
888
+ }
889
+ const parser = new DOMParser();
890
+ const html = parser.parseFromString(s, 'text/html');
891
+ return html.body.textContent;
892
+ }
893
+ /**
894
+ *
895
+ * @param s
896
+ * @returns result. If input is empty, null, or undefined, return the same.
897
+ */
898
+ static capitalizeWords(s) {
899
+ if (!s) {
900
+ return s;
901
+ }
902
+ return s.replace(/(?:^|\s)\S/g, (a) => a.toUpperCase());
903
+ }
904
+ static validateEmail(email) {
905
+ if (!email) {
906
+ return true;
907
+ }
908
+ const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
909
+ return re.test(email.toLowerCase());
910
+ }
904
911
  }
905
912
 
906
- // import { v5 as uuid } from 'uuid/v5 causes ERROR in src / app / _func / helperFunc.ts(1, 10): error TS2305: Module '"C:/VSProjects/ApsCloudTrunk/APS.WebPos.NGCli/NGSource/node_modules/@types/uuid/v5"' has no exported member 'v5'
907
- // https://github.com/DefinitelyTyped/DefinitelyTyped/pull/28439
908
- class UuidFunc {
909
- /**
910
- * 36 UUID string including 4 hyphens. MySql stores GUID as 36 bytes anyway rather than 16bytes.
911
- */
912
- static newUUID() {
913
- // return uuid('medilink.com.au', 'apscloud');
914
- return v4();
915
- }
916
- static newUUIDStartWith0() {
917
- const s = v4();
918
- return '0000' + s.slice(4);
919
- }
913
+ // import { v5 as uuid } from 'uuid/v5 causes ERROR in src / app / _func / helperFunc.ts(1, 10): error TS2305: Module '"C:/VSProjects/ApsCloudTrunk/APS.WebPos.NGCli/NGSource/node_modules/@types/uuid/v5"' has no exported member 'v5'
914
+ // https://github.com/DefinitelyTyped/DefinitelyTyped/pull/28439
915
+ class UuidFunc {
916
+ /**
917
+ * 36 UUID string including 4 hyphens. MySql stores GUID as 36 bytes anyway rather than 16bytes.
918
+ */
919
+ static newUUID() {
920
+ // return uuid('medilink.com.au', 'apscloud');
921
+ return v4();
922
+ }
923
+ static newUUIDStartWith0() {
924
+ const s = v4();
925
+ return '0000' + s.slice(4);
926
+ }
920
927
  }
921
928
 
922
- /*
923
- * Public API Surface of nmce-func
929
+ /*
930
+ * Public API Surface of nmce-func
924
931
  */
925
932
 
926
- /**
927
- * Generated bundle index. Do not edit.
933
+ /**
934
+ * Generated bundle index. Do not edit.
928
935
  */
929
936
 
930
937
  export { AddressFunc, AuthenticationService, CurrencyFunc, DateFunc, HtmlPrintFunc, JavaScriptFunc, JsonFunc, StringAusFunc, StringFunc, UuidFunc };