x4js 2.0.20 → 2.0.21

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 (172) hide show
  1. package/lib/cjs/x4.css +1 -1
  2. package/lib/cjs/x4.js +2 -2
  3. package/lib/esm/x4.css +1 -1
  4. package/lib/esm/x4.mjs +2 -2
  5. package/lib/styles/x4.css +1 -1
  6. package/lib/types/x4js.d.ts +92 -77
  7. package/package.json +1 -1
  8. package/src/components/boxes/boxes.ts +33 -1
  9. package/src/components/combobox/combobox.module.scss +13 -0
  10. package/src/components/combobox/combobox.ts +7 -0
  11. package/src/components/form/form.ts +4 -4
  12. package/src/components/gridview/gridview.ts +12 -9
  13. package/src/components/header/header.module.scss +2 -1
  14. package/src/components/header/header.ts +17 -5
  15. package/src/components/icon/icon.module.scss +1 -0
  16. package/src/components/label/label.module.scss +6 -1
  17. package/src/components/listbox/listbox.module.scss +35 -31
  18. package/src/components/listbox/listbox.ts +50 -12
  19. package/src/components/messages/messages.ts +14 -0
  20. package/src/components/notification/notification.ts +1 -1
  21. package/src/components/popup/popup.ts +13 -2
  22. package/src/components/propgrid/propgrid.ts +4 -2
  23. package/src/components/sizers/sizer.ts +4 -4
  24. package/src/components/tabs/tabs.module.scss +2 -2
  25. package/src/components/tabs/tabs.ts +28 -4
  26. package/src/components/textedit/textedit.ts +1 -1
  27. package/src/components/treeview/treeview.module.scss +9 -1
  28. package/src/components/treeview/treeview.ts +52 -13
  29. package/src/core/component.ts +46 -9
  30. package/src/core/core_data.ts +8 -8
  31. package/src/core/core_state.ts +1 -1
  32. package/src/core/core_tools.ts +2 -0
  33. package/lib/src/components/base.scss +0 -25
  34. package/lib/src/components/boxes/boxes.module.scss +0 -54
  35. package/lib/src/components/boxes/boxes.ts +0 -370
  36. package/lib/src/components/breadcrumb/breadcrumb.scss +0 -56
  37. package/lib/src/components/breadcrumb/breadcrumb.ts +0 -93
  38. package/lib/src/components/breadcrumb/chevron-right.svg +0 -1
  39. package/lib/src/components/btngroup/btngroup.module.scss +0 -41
  40. package/lib/src/components/btngroup/btngroup.ts +0 -153
  41. package/lib/src/components/button/button.module.scss +0 -173
  42. package/lib/src/components/button/button.ts +0 -185
  43. package/lib/src/components/calendar/calendar-check-sharp-light.svg +0 -1
  44. package/lib/src/components/calendar/calendar.module.scss +0 -163
  45. package/lib/src/components/calendar/calendar.ts +0 -327
  46. package/lib/src/components/calendar/chevron-left-sharp-light.svg +0 -1
  47. package/lib/src/components/calendar/chevron-right-sharp-light.svg +0 -1
  48. package/lib/src/components/canvas/canvas.module.scss +0 -25
  49. package/lib/src/components/canvas/canvas.ts +0 -189
  50. package/lib/src/components/canvas/canvas_ex.ts +0 -276
  51. package/lib/src/components/checkbox/check.svg +0 -4
  52. package/lib/src/components/checkbox/checkbox.module.scss +0 -142
  53. package/lib/src/components/checkbox/checkbox.ts +0 -140
  54. package/lib/src/components/colorinput/colorinput.module.scss +0 -65
  55. package/lib/src/components/colorinput/colorinput.ts +0 -91
  56. package/lib/src/components/colorinput/crosshairs-simple-sharp-light.svg +0 -1
  57. package/lib/src/components/colorpicker/colorpicker.module.scss +0 -133
  58. package/lib/src/components/colorpicker/colorpicker.ts +0 -482
  59. package/lib/src/components/combobox/combobox.module.scss +0 -133
  60. package/lib/src/components/combobox/combobox.ts +0 -275
  61. package/lib/src/components/combobox/updown.svg +0 -4
  62. package/lib/src/components/components.ts +0 -42
  63. package/lib/src/components/dialog/dialog.module.scss +0 -104
  64. package/lib/src/components/dialog/dialog.ts +0 -229
  65. package/lib/src/components/dialog/xmark-sharp-light.svg +0 -1
  66. package/lib/src/components/filedrop/cloud-arrow-up.svg +0 -1
  67. package/lib/src/components/filedrop/filedrop.module.scss +0 -70
  68. package/lib/src/components/filedrop/filedrop.ts +0 -131
  69. package/lib/src/components/form/form.module.scss +0 -38
  70. package/lib/src/components/form/form.ts +0 -172
  71. package/lib/src/components/gridview/arrow-down-light.svg +0 -1
  72. package/lib/src/components/gridview/arrow-up-light.svg +0 -1
  73. package/lib/src/components/gridview/gridview.module.scss +0 -324
  74. package/lib/src/components/gridview/gridview.ts +0 -1175
  75. package/lib/src/components/header/header.module.scss +0 -40
  76. package/lib/src/components/header/header.ts +0 -130
  77. package/lib/src/components/icon/icon.module.scss +0 -31
  78. package/lib/src/components/icon/icon.ts +0 -137
  79. package/lib/src/components/image/image.module.scss +0 -28
  80. package/lib/src/components/image/image.ts +0 -168
  81. package/lib/src/components/input/input.module.scss +0 -74
  82. package/lib/src/components/input/input.ts +0 -422
  83. package/lib/src/components/keyboard/arrow-up.svg +0 -1
  84. package/lib/src/components/keyboard/delete-left.svg +0 -1
  85. package/lib/src/components/keyboard/eye-slash.svg +0 -1
  86. package/lib/src/components/keyboard/keyboard.module.scss +0 -134
  87. package/lib/src/components/keyboard/keyboard.ts +0 -526
  88. package/lib/src/components/label/label.module.scss +0 -76
  89. package/lib/src/components/label/label.ts +0 -97
  90. package/lib/src/components/link/link.ts +0 -81
  91. package/lib/src/components/listbox/listbox.module.scss +0 -161
  92. package/lib/src/components/listbox/listbox.ts +0 -539
  93. package/lib/src/components/menu/caret-right-solid.svg +0 -1
  94. package/lib/src/components/menu/menu.module.scss +0 -117
  95. package/lib/src/components/menu/menu.ts +0 -174
  96. package/lib/src/components/messages/circle-exclamation.svg +0 -1
  97. package/lib/src/components/messages/messages.module.scss +0 -92
  98. package/lib/src/components/messages/messages.ts +0 -215
  99. package/lib/src/components/messages/pen-field.svg +0 -1
  100. package/lib/src/components/normalize.scss +0 -391
  101. package/lib/src/components/notification/circle-check-solid.svg +0 -1
  102. package/lib/src/components/notification/circle-exclamation-solid.svg +0 -1
  103. package/lib/src/components/notification/circle-notch-light.svg +0 -1
  104. package/lib/src/components/notification/notification.module.scss +0 -84
  105. package/lib/src/components/notification/notification.ts +0 -107
  106. package/lib/src/components/notification/xmark-sharp-light.svg +0 -1
  107. package/lib/src/components/panel/panel.module.scss +0 -60
  108. package/lib/src/components/panel/panel.ts +0 -58
  109. package/lib/src/components/popup/popup.module.scss +0 -51
  110. package/lib/src/components/popup/popup.ts +0 -442
  111. package/lib/src/components/progress/progress.module.scss +0 -57
  112. package/lib/src/components/progress/progress.ts +0 -44
  113. package/lib/src/components/propgrid/folder-closed.svg +0 -1
  114. package/lib/src/components/propgrid/folder-open.svg +0 -1
  115. package/lib/src/components/propgrid/progrid.module.scss +0 -112
  116. package/lib/src/components/propgrid/propgrid.ts +0 -288
  117. package/lib/src/components/propgrid/updown.svg +0 -4
  118. package/lib/src/components/radio/radio.module.scss +0 -147
  119. package/lib/src/components/radio/radio.svg +0 -4
  120. package/lib/src/components/radio/radio.ts +0 -142
  121. package/lib/src/components/rating/rating.module.scss +0 -23
  122. package/lib/src/components/rating/rating.ts +0 -131
  123. package/lib/src/components/rating/star-sharp-light.svg +0 -1
  124. package/lib/src/components/rating/star-sharp-solid.svg +0 -1
  125. package/lib/src/components/select/select.module.scss +0 -9
  126. package/lib/src/components/select/select.ts +0 -134
  127. package/lib/src/components/shared.scss +0 -137
  128. package/lib/src/components/sizers/sizer.module.scss +0 -90
  129. package/lib/src/components/sizers/sizer.ts +0 -132
  130. package/lib/src/components/slider/slider.module.scss +0 -118
  131. package/lib/src/components/slider/slider.ts +0 -198
  132. package/lib/src/components/switch/switch.module.scss +0 -127
  133. package/lib/src/components/switch/switch.ts +0 -62
  134. package/lib/src/components/tabs/tabs.module.scss +0 -45
  135. package/lib/src/components/tabs/tabs.ts +0 -205
  136. package/lib/src/components/textarea/textarea.module.scss +0 -63
  137. package/lib/src/components/textarea/textarea.ts +0 -125
  138. package/lib/src/components/textedit/textedit.module.scss +0 -116
  139. package/lib/src/components/textedit/textedit.ts +0 -115
  140. package/lib/src/components/themes.scss +0 -88
  141. package/lib/src/components/tickline/tickline.module.scss +0 -26
  142. package/lib/src/components/tickline/tickline.ts +0 -82
  143. package/lib/src/components/tooltips/circle-info-sharp-light.svg +0 -1
  144. package/lib/src/components/tooltips/comments-question.svg +0 -1
  145. package/lib/src/components/tooltips/tooltips.scss +0 -72
  146. package/lib/src/components/tooltips/tooltips.ts +0 -109
  147. package/lib/src/components/treeview/chevron-down-light.svg +0 -1
  148. package/lib/src/components/treeview/treeview.module.scss +0 -185
  149. package/lib/src/components/treeview/treeview.ts +0 -445
  150. package/lib/src/components/viewport/viewport.module.scss +0 -32
  151. package/lib/src/components/viewport/viewport.ts +0 -41
  152. package/lib/src/core/component.ts +0 -1066
  153. package/lib/src/core/core_application.ts +0 -265
  154. package/lib/src/core/core_colors.ts +0 -250
  155. package/lib/src/core/core_data.ts +0 -1310
  156. package/lib/src/core/core_dom.ts +0 -471
  157. package/lib/src/core/core_dragdrop.ts +0 -201
  158. package/lib/src/core/core_element.ts +0 -115
  159. package/lib/src/core/core_events.ts +0 -177
  160. package/lib/src/core/core_i18n.ts +0 -393
  161. package/lib/src/core/core_react.ts +0 -79
  162. package/lib/src/core/core_router.ts +0 -237
  163. package/lib/src/core/core_state.ts +0 -62
  164. package/lib/src/core/core_styles.ts +0 -214
  165. package/lib/src/core/core_svg.ts +0 -712
  166. package/lib/src/core/core_tools.ts +0 -906
  167. package/lib/src/types/scss.d.ts +0 -4
  168. package/lib/src/types/svg.d.ts +0 -1
  169. package/lib/src/types/x4react.d.ts +0 -9
  170. package/lib/src/x4.scss +0 -19
  171. package/lib/src/x4.ts +0 -35
  172. package/lib/src/x4tsx.d.ts +0 -25
@@ -1,906 +0,0 @@
1
- /**
2
- * ___ ___ __
3
- * \ \/ / / _
4
- * \ / /_| |_
5
- * / \____ _|
6
- * /__/\__\ |_|
7
- *
8
- * @file core_tools.ts
9
- * @author Etienne Cochard
10
- *
11
- * @copyright (c) 2024 R-libre ingenierie
12
- *
13
- * Use of this source code is governed by an MIT-style license
14
- * that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
15
- **/
16
-
17
- import { _tr } from "./core_i18n.js";
18
-
19
- /**
20
- * @returns true if object is a string
21
- */
22
- export function isString(val: any): val is string {
23
- return typeof val === 'string';
24
- }
25
-
26
- /**
27
- * @returns true if object is a number
28
- */
29
-
30
- export function isNumber(v: any): v is number {
31
- return typeof v === 'number' && isFinite(v);
32
- }
33
-
34
- /**
35
- * @returns true if object is an array
36
- */
37
- export function isArray(val: any): val is any[] {
38
- return val instanceof Array;
39
- }
40
-
41
- /**
42
- * @returns true if object is a function
43
- */
44
-
45
- export function isFunction(val: any): val is Function {
46
- return val instanceof Function;
47
- }
48
-
49
- /**
50
- * generic constructor
51
- */
52
-
53
- export type Constructor<P> = {
54
- new(...params: any[]): P;
55
- };
56
-
57
-
58
- /**
59
- * a way to explain that the given string may be unsafe but must be treated a sstring
60
- * @example
61
- * label.setText( unsafehtml`<b>Bold</b> text` );
62
- * label.setText( new UnsafeHtml("<b>Bold</b> text`" ) );
63
- */
64
-
65
- export class UnsafeHtml extends String {
66
- constructor(value: string) {
67
- super(value);
68
- }
69
- }
70
-
71
- export function unsafeHtml(x: string): UnsafeHtml {
72
- return new UnsafeHtml(x);
73
- }
74
-
75
- export function unsafe(strings: TemplateStringsArray, ...values: any[]): UnsafeHtml {
76
- const result = strings.reduce((acc, str, i) => {
77
- return acc + str + (values[i] || '');
78
- }, '');
79
- return unsafeHtml(result);
80
- }
81
-
82
- /**
83
- *
84
- */
85
-
86
- export function clamp<T>(v: T, min: T, max: T): T {
87
- if (v < min) { return min; }
88
- if (v > max) { return max; }
89
- return v;
90
- }
91
-
92
-
93
- /**
94
- * generic Rectangle
95
- */
96
-
97
- export interface IRect {
98
- left: number;
99
- top: number;
100
- height: number;
101
- width: number;
102
- }
103
-
104
- /**
105
- *
106
- */
107
-
108
- export class Rect implements IRect {
109
- left: number;
110
- top: number;
111
- height: number;
112
- width: number;
113
-
114
- constructor();
115
- constructor(l: number, t: number, w: number, h: number);
116
- constructor(l: Rect);
117
- constructor(l?: number | IRect, t?: number, w?: number, h?: number) {
118
- if (l !== undefined) {
119
- if (isNumber(l)) {
120
- this.left = l;
121
- this.top = t;
122
- this.width = w;
123
- this.height = h;
124
- }
125
- else {
126
- Object.assign(this, l);
127
- }
128
- }
129
- }
130
-
131
- get right() {
132
- return this.left + this.width;
133
- }
134
-
135
- get bottom() {
136
- return this.top + this.height;
137
- }
138
-
139
- contains(pt: Point): boolean;
140
- contains(rc: Rect): boolean;
141
- contains(arg: any): boolean {
142
- if (arg instanceof Rect) {
143
- return arg.left >= this.left && arg.right <= this.right && arg.top >= this.top && arg.bottom <= this.bottom;
144
- }
145
- else {
146
- return arg.x >= this.left && arg.x < this.right && arg.y >= this.top && arg.y < this.bottom;
147
- }
148
- }
149
-
150
- touches(rc: Rect): boolean {
151
- if (this.left > rc.right || this.right < rc.left || this.top > rc.bottom || this.bottom < rc.top) {
152
- return false;
153
- }
154
-
155
- return true;
156
- }
157
-
158
- normalize(): this {
159
- let w = this.width,
160
- h = this.height;
161
-
162
- if (w < 0) {
163
- this.left += w;
164
- this.width = -w;
165
- }
166
-
167
- if (h < 0) {
168
- this.top += h;
169
- this.height = -h;
170
- }
171
-
172
- return this;
173
- }
174
- }
175
-
176
- /**
177
- * generic size
178
- */
179
-
180
- export interface Size {
181
- w: number;
182
- h: number;
183
- }
184
-
185
-
186
- /**
187
- * generic Point
188
- * TODO: IPoint
189
- */
190
-
191
- export interface Point {
192
- x: number;
193
- y: number;
194
- }
195
-
196
-
197
- /**
198
- * generic Size
199
- * TODO: ISize
200
- */
201
-
202
- export interface Size {
203
- w: number;
204
- h: number;
205
- }
206
-
207
- /**
208
- * center one rect inside another
209
- */
210
-
211
-
212
- export function centerRect(innerRect: IRect, outerRect: IRect, margin: number = 0): IRect {
213
-
214
- const owidth = outerRect.width - 2 * margin;
215
- const oheight = outerRect.height - 2 * margin;
216
-
217
- const ratio = innerRect.width / innerRect.height;
218
-
219
- let nwidth = owidth;
220
- let nheight = owidth / ratio;
221
-
222
- if (nheight > oheight) {
223
- nheight = oheight;
224
- nwidth = oheight * ratio;
225
- }
226
-
227
- const newLeft = outerRect.left + (outerRect.width - nwidth) / 2;
228
- const newTop = outerRect.top + (outerRect.height - nheight) / 2;
229
-
230
- return { left: newLeft, top: newTop, width: nwidth, height: nheight };
231
- }
232
-
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
-
242
-
243
-
244
-
245
-
246
- /**
247
- * @see queryInterface
248
- */
249
-
250
- export interface IComponentInterface {
251
- }
252
-
253
- // form-element
254
- export interface IFormElement extends IComponentInterface {
255
- getRawValue(): any;
256
- setRawValue(v: any): void;
257
- isValid(): boolean;
258
- }
259
-
260
- // tab-handler
261
- export interface ITabHandler extends IComponentInterface {
262
- focusNext(next: boolean): boolean; // return true to stop event
263
- }
264
-
265
- // tip-handler
266
- export interface ITipHandler extends IComponentInterface {
267
- getTip(): string;
268
- }
269
-
270
- /**
271
- *
272
- */
273
-
274
- interface Features {
275
- eyedropper: 1,
276
- }
277
-
278
- export function isFeatureAvailable(name: keyof Features): boolean {
279
- switch (name) {
280
- case "eyedropper": return "EyeDropper" in window;
281
- }
282
-
283
- return false;
284
- }
285
-
286
- export class Timer {
287
-
288
- protected _timers: Map<string, any>;
289
-
290
- /**
291
- *
292
- */
293
-
294
- setTimeout(name: string, time: number, callback: Function) {
295
- if (!this._timers) {
296
- this._timers = new Map();
297
- }
298
- else {
299
- this.clearTimeout(name);
300
- }
301
-
302
- const tm = setTimeout(callback, time);
303
- this._timers.set(name, tm);
304
-
305
- return tm;
306
- }
307
-
308
- clearTimeout(name: string) {
309
- if (this._timers && this._timers.has(name)) {
310
- clearTimeout(this._timers.get(name));
311
- this._timers.delete(name);
312
- }
313
- }
314
-
315
- /**
316
- *
317
- */
318
-
319
- setInterval(name: string, time: number, callback: Function) {
320
- if (!this._timers) {
321
- this._timers = new Map();
322
- }
323
- else {
324
- this.clearInterval(name);
325
- }
326
-
327
- const tm = setInterval(callback, time);
328
- this._timers.set(name, tm);
329
-
330
- return tm;
331
- }
332
-
333
- clearInterval(name: string) {
334
- if (this._timers && this._timers.has(name)) {
335
- clearInterval(this._timers.get(name));
336
- this._timers.delete(name);
337
- }
338
- }
339
-
340
- clearAllTimeouts() {
341
- this._timers?.forEach(t => {
342
- clearTimeout(t);
343
- });
344
-
345
- this._timers = null;
346
- }
347
- }
348
-
349
- /**
350
- *
351
- */
352
-
353
- export function asap(callback: () => void) {
354
- return requestAnimationFrame(callback);
355
- }
356
-
357
- export function oneshot(callback: () => void, ms = 0) {
358
- return setTimeout(callback, ms);
359
- }
360
-
361
-
362
- // :: STRING UTILS ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
363
-
364
-
365
- /**
366
- * prepend 0 to a value to a given length
367
- * @param value
368
- * @param length
369
- */
370
-
371
- export function pad(what: any, size: number, ch: string = '0') {
372
-
373
- let value: string;
374
-
375
- if (!isString(what)) {
376
- value = '' + what;
377
- }
378
- else {
379
- value = what;
380
- }
381
-
382
- if (size > 0) {
383
- return value.padEnd(size, ch);
384
- }
385
- else {
386
- return value.padStart(-size, ch);
387
- }
388
- }
389
-
390
- /**
391
- * replace {0..9} by given arguments
392
- * @param format string
393
- * @param args
394
- *
395
- * @example ```ts
396
- *
397
- * console.log( sprintf( 'here is arg 1 {1} and arg 0 {0}', 'argument 0', 'argument 1' ) )
398
- */
399
-
400
- export function sprintf(format: string, ...args: any[]) {
401
- return format.replace(/{(\d+)}/g, function (match, index) {
402
- return typeof args[index] != 'undefined' ? args[index] : match;
403
- });
404
- }
405
-
406
- /**
407
- * inverse of camel case
408
- * theThingToCase -> the-thing-to-case
409
- * @param {String} str
410
- */
411
-
412
- export function pascalCase(string: string): string {
413
-
414
- let result = string;
415
-
416
- result = result.replace(/([a-z])([A-Z])/g, "$1 $2");
417
- result = result.toLowerCase();
418
- result = result.replace(/[^- a-z0-9]+/g, ' ');
419
-
420
- if (result.indexOf(' ') < 0) {
421
- return result;
422
- }
423
-
424
- result = result.trim();
425
- return result.replace(/ /g, '-');
426
- }
427
-
428
- export function camelCase(text: string) {
429
- let result = text.toLowerCase();
430
- result = result.replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => {
431
- return chr.toUpperCase();
432
- });
433
- return result;
434
- }
435
-
436
- // :: DATES ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
437
-
438
- let cur_locale: string = 'fr-FR';
439
-
440
- /**
441
- * change the current locale for misc translations (date...)
442
- * @param locale
443
- */
444
-
445
- export function _date_set_locale(locale: string) {
446
- cur_locale = locale;
447
- }
448
-
449
- /**
450
- *
451
- * @param date
452
- * @param options
453
- * @example
454
- * let date = new Date( );
455
- * let options = { day: 'numeric', month: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric' };
456
- * let text = date_format( date, options );
457
- */
458
-
459
- export function date_format(date: Date, options?: any): string {
460
- //return new Intl.DateTimeFormat(cur_locale, options).format( date );
461
- return formatIntlDate(date);
462
- }
463
-
464
- /**
465
- *
466
- * @param date
467
- * @param options
468
- */
469
-
470
- export function date_diff(date1: Date, date2: Date, options?: any): string {
471
-
472
- let dt = (date1.getTime() - date2.getTime()) / 1000;
473
-
474
- // seconds
475
- let sec = dt;
476
- if (sec < 60) {
477
- return sprintf(_tr.global.diff_date_seconds, Math.round(sec));
478
- }
479
-
480
- // minutes
481
- let min = Math.floor(sec / 60);
482
- if (min < 60) {
483
- return sprintf(_tr.global.diff_date_minutes, Math.round(min));
484
- }
485
-
486
- // hours
487
- let hrs = Math.floor(min / 60);
488
- return sprintf(_tr.global.diff_date_hours, hrs, min % 60);
489
- }
490
-
491
- export function date_to_sql(date: Date, withHours: boolean) {
492
-
493
- if (withHours) {
494
- return formatIntlDate(date, 'Y-M-D H:I:S');
495
- }
496
- else {
497
- return formatIntlDate(date, 'Y-M-D');
498
- }
499
- }
500
-
501
- /**
502
- * construct a date from an utc date time (sql format)
503
- * YYYY-MM-DD HH:MM:SS
504
- */
505
-
506
- export function date_sql_utc(date: string): Date {
507
- let result = new Date(date + ' GMT');
508
- return result;
509
- }
510
-
511
-
512
-
513
- /**
514
- * return a number that is a representation of the date
515
- * this number can be compared with another hash
516
- */
517
-
518
- export function date_hash(date: Date): number {
519
- return date.getFullYear() << 16 | date.getMonth() << 8 | date.getDate();
520
- }
521
-
522
- /**
523
- * return a copy of a date
524
- */
525
-
526
- export function date_clone(date: Date): Date {
527
- return new Date(date.getTime());
528
- }
529
-
530
- /**
531
- * return the week number of a date
532
- */
533
-
534
- export function date_calc_weeknum(date: Date): number {
535
- const firstDayOfYear = new Date(date.getFullYear(), 0, 1);
536
- const pastDaysOfYear = (date.valueOf() - firstDayOfYear.valueOf()) / 86400000;
537
- return Math.floor((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
538
- }
539
-
540
-
541
-
542
- /**
543
- * parse a date according to the given format
544
- * @param value - string date to parse
545
- * @param fmts - format list - i18 tranlation by default
546
- * allowed format specifiers:
547
- * d: date (1 or 2 digits)
548
- * D: date (2 digits)
549
- * m: month (1 or 2 digits)
550
- * M: month (2 digits)
551
- * y: year (2 to 4 digits)
552
- * Y: year (2 digits)
553
- * YY: year (4 digits)
554
- * h: hours (1 or 2 digits)
555
- * H: hours (2 digits)
556
- * i: minutes (1 or 2 digits)
557
- * I: minutes (2 digits)
558
- * s: seconds (1 or 2 digits)
559
- * S: seconds (2 digits)
560
- * <space>: 1 or more spaces
561
- * any other char: <0 or more spaces><the char><0 or more spaces>
562
- * each specifiers is separated from other by a pipe (|)
563
- * more specific at first
564
- * @example
565
- * 'd/m/y|d m Y|dmy|y-m-d h:i:s|y-m-d|YY-M-D'
566
- */
567
-
568
- export function parseIntlDate(value: string, fmts: string = _tr.global.date_input_formats): Date {
569
-
570
- let formats = fmts.split('|');
571
- for (let fmatch of formats) {
572
-
573
- //review: could do that only once & keep result
574
- //review: add hours, minutes, seconds
575
-
576
- let smatch = '';
577
- for (let i = 0; i < fmatch.length; i++) {
578
- const c = fmatch[i];
579
-
580
- if (c == 'd') {
581
- smatch += '(?<day>\\d{1,2})';
582
- }
583
- else if (c == 'D') {
584
- smatch += '(?<day>\\d{2})';
585
- }
586
- else if (c == 'm') {
587
- smatch += '(?<month>\\d{1,2})';
588
- }
589
- else if (c == 'M') {
590
- smatch += '(?<month>\\d{2})';
591
- }
592
- else if (c == 'y') {
593
- smatch += '(?<year>\\d{1,4})';
594
- }
595
- else if (c == 'Y') {
596
- if (fmatch[i + 1] == 'Y') {
597
- smatch += '(?<year>\\d{4})';
598
- i++;
599
- }
600
- else {
601
- smatch += '(?<year>\\d{2})';
602
- }
603
- }
604
- else if (c == 'h') {
605
- smatch += '(?<hour>\\d{1,2})';
606
- }
607
- else if (c == 'H') {
608
- smatch += '(?<hour>\\d{2})';
609
- }
610
- else if (c == 'i') {
611
- smatch += '(?<min>\\d{1,2})';
612
- }
613
- else if (c == 'I') {
614
- smatch += '(?<min>\\d{2})';
615
- }
616
- else if (c == 's') {
617
- smatch += '(?<sec>\\d{1,2})';
618
- }
619
- else if (c == 'S') {
620
- smatch += '(?<sec>\\d{2})';
621
- }
622
- else if (c == ' ') {
623
- smatch += '\\s+';
624
- }
625
- else {
626
- smatch += '\\s*\\' + c + '\\s*';
627
- }
628
- }
629
-
630
- let rematch = new RegExp('^' + smatch + '$', 'm');
631
-
632
- let match = rematch.exec(value);
633
-
634
- if (match) {
635
- const now = new Date();
636
-
637
- let d = parseInt(match.groups.day ?? '1');
638
- let m = parseInt(match.groups.month ?? '1');
639
- let y = parseInt(match.groups.year ?? now.getFullYear() + '');
640
- let h = parseInt(match.groups.hour ?? '0');
641
- let i = parseInt(match.groups.min ?? '0');
642
- let s = parseInt(match.groups.sec ?? '0');
643
-
644
- if (y > 0 && y < 100) {
645
- y += 2000;
646
- }
647
-
648
- let result = new Date(y, m - 1, d, h, i, s, 0);
649
-
650
- // we test the vdate validity (without adjustments)
651
- // without this test, date ( 0, 0, 0) is accepted and transformed to 1969/11/31 (not fun)
652
- let ty = result.getFullYear(),
653
- tm = result.getMonth() + 1,
654
- td = result.getDate();
655
-
656
- if (ty != y || tm != m || td != d) {
657
- //debugger;
658
- return null;
659
- }
660
-
661
- return result;
662
- }
663
- }
664
-
665
- return null;
666
- }
667
-
668
- /**
669
- * format a date as string
670
- * @param date - date to format
671
- * @param fmt - format
672
- * format specifiers:
673
- * - d: date (no pad)
674
- * - D: 2 digits date padded with 0
675
- * - j: day of week short mode 'mon'
676
- * - J: day of week long mode 'monday'
677
- * - w: week number
678
- * - m: month (no pad)
679
- * - M: 2 digits month padded with 0
680
- * - o: month short mode 'jan'
681
- * - O: month long mode 'january'
682
- * - y or Y: year
683
- * - h: hour (24 format)
684
- * - H: 2 digits hour (24 format) padded with 0
685
- * - i: minutes
686
- * - I: 2 digits minutes padded with 0
687
- * - s: seconds
688
- * - S: 2 digits seconds padded with 0
689
- * - a: am or pm
690
- * - anything else is inserted
691
- * - if you need to insert some text, put it between {}
692
- *
693
- * @example
694
- *
695
- * 01/01/1970 11:25:00 with '{this is my demo date formatter: }H-i*M'
696
- * "this is my demo date formatter: 11-25*january"
697
- */
698
-
699
- export function formatIntlDate(date: Date, fmt: string = _tr.global.date_format) {
700
-
701
- if (!date) {
702
- return '';
703
- }
704
-
705
- let now = {
706
- year: date.getFullYear(),
707
- month: date.getMonth() + 1,
708
- day: date.getDate(),
709
- wday: date.getDay(),
710
- hours: date.getHours(),
711
- minutes: date.getMinutes(),
712
- seconds: date.getSeconds(),
713
- milli: date.getMilliseconds()
714
- };
715
-
716
-
717
- let result = '';
718
- let esc = 0;
719
-
720
- for (let c of fmt) {
721
-
722
- if (c == '{') {
723
- if (++esc == 1) {
724
- continue;
725
- }
726
- }
727
- else if (c == '}') {
728
- if (--esc == 0) {
729
- continue;
730
- }
731
- }
732
-
733
- if (esc) {
734
- result += c;
735
- continue;
736
- }
737
-
738
- if (c == 'd') {
739
- result += now.day;
740
- }
741
- else if (c == 'D') {
742
- result += pad(now.day, -2);
743
- }
744
- else if (c == 'j') { // day short
745
- result += _tr.global.day_short[now.wday];
746
- }
747
- else if (c == 'J') { // day long
748
- result += _tr.global.day_long[now.wday];
749
- }
750
- else if (c == 'w') { // week
751
- result += date_calc_weeknum(date);
752
- }
753
- else if (c == 'W') { // week
754
- result += pad(date_calc_weeknum(date), -2);
755
- }
756
- else if (c == 'm') {
757
- result += now.month;
758
- }
759
- else if (c == 'M') {
760
- result += pad(now.month, -2);
761
- }
762
- else if (c == 'o') { // month short
763
- result += _tr.global.month_short[now.month - 1];
764
- }
765
- else if (c == 'O') { // month long
766
- result += _tr.global.month_long[now.month - 1];
767
- }
768
- else if (c == 'y' || c == 'Y') {
769
- result += pad(now.year, -4);
770
- }
771
- else if (c == 'a' || c == 'A') {
772
- result += now.hours < 12 ? 'am' : 'pm';
773
- }
774
- else if (c == 'h') {
775
- result += now.hours;
776
- }
777
- else if (c == 'H') {
778
- result += pad(now.hours, -2);
779
- }
780
- else if (c == 'i') {
781
- result += now.minutes;
782
- }
783
- else if (c == 'I') {
784
- result += pad(now.minutes, -2);
785
- }
786
- else if (c == 's') {
787
- result += now.seconds;
788
- }
789
- else if (c == 'S') {
790
- result += pad(now.seconds, -2);
791
- }
792
- else if (c == 'l') {
793
- result += now.milli;
794
- }
795
- else if (c == 'L') {
796
- result += pad(now.milli, -3);
797
- }
798
- else {
799
- result += c;
800
- }
801
- }
802
-
803
- return result;
804
- }
805
-
806
- export function calcAge(birth: Date, ref?: Date) {
807
- if (ref === undefined) {
808
- ref = new Date();
809
- }
810
-
811
- if (!birth) {
812
- return 0;
813
- }
814
-
815
- let age = ref.getFullYear() - birth.getFullYear();
816
- if (ref.getMonth() < birth.getMonth() || (ref.getMonth() == birth.getMonth() && ref.getDate() < birth.getDate())) {
817
- age--;
818
- }
819
-
820
- return age;
821
- }
822
-
823
-
824
- // :: MISC ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
825
-
826
- export function beep() {
827
- const snd = new Audio("data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=");
828
- snd.play();
829
- }
830
-
831
- let sb_width_cache = -1;
832
-
833
- /**
834
- * compute scrollbar size
835
- */
836
-
837
- export function getScrollbarSize() {
838
-
839
- if (sb_width_cache < 0) {
840
- let outerDiv = document.createElement('div');
841
- outerDiv.style.cssText = 'overflow:auto;position:absolute;top:0;width:100px;height:100px';
842
-
843
- let innerDiv = document.createElement('div');
844
- innerDiv.style.width = '200px';
845
- innerDiv.style.height = '200px';
846
-
847
- outerDiv.appendChild(innerDiv);
848
- document.body.appendChild(outerDiv);
849
-
850
- sb_width_cache = outerDiv.offsetWidth - outerDiv.clientWidth;
851
- document.body.removeChild(outerDiv);
852
- }
853
-
854
- return sb_width_cache;
855
- }
856
-
857
- /**
858
- *
859
- */
860
-
861
- export const x4_class_ns_sym = Symbol("class-ns");
862
- export function class_ns(ns: string) {
863
- return function (constructor: Function) {
864
- (constructor as any)[x4_class_ns_sym] = ns;
865
- }
866
- }
867
-
868
- /**
869
- *
870
- */
871
-
872
- export function setWaitCursor(wait: boolean) {
873
- document.body.style.cursor = wait ? "wait" : "default";
874
- }
875
-
876
- /**
877
- * return the focusable elements from a given node
878
- */
879
-
880
- export function getFocusableElements(root: Element) {
881
- const els = [
882
- 'button:not([tabindex="-1"]):not([disabled])',
883
- '[href]',
884
- 'input:not([disabled])',
885
- 'select:not([disabled])',
886
- 'textarea:not([disabled])',
887
- '[tabindex]:not([tabindex="-1"])'
888
- ]
889
-
890
- const focusable = Array.from(root.querySelectorAll(els.join(',')));
891
- return focusable.filter(x => (x as HTMLElement).offsetParent != null); // check visibility
892
- }
893
-
894
- /**
895
- *
896
- */
897
-
898
- export enum kbNav {
899
- first,
900
- prev,
901
- pgdn,
902
- pgup,
903
- next,
904
- last,
905
- }
906
-