x4js 2.3.20 → 2.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/aicontext.md +127 -32
- package/package.json +1 -1
- package/src/components/autocomplete/autocomplete.ts +3 -0
- package/src/components/boxes/boxes.ts +1 -1
- package/src/components/chart/chart.ts +12 -3
- package/src/components/colorinput/colorinput.module.scss +11 -2
- package/src/components/colorinput/colorinput.ts +37 -4
- package/src/components/colorpicker/colorpicker.module.scss +14 -2
- package/src/components/colorpicker/colorpicker.ts +57 -11
- package/src/components/components.ts +1 -0
- package/src/components/dialog/dialog.ts +16 -5
- package/src/components/form/form.ts +3 -4
- package/src/components/gridview/gridview.module.scss +18 -16
- package/src/components/gridview/gridview.ts +5 -0
- package/src/components/icon/icon.ts +34 -18
- package/src/components/keyboard/keyboard.ts +31 -5
- package/src/components/link/link.ts +18 -7
- package/src/components/messages/messages.ts +1 -1
- package/src/components/panel/panel.ts +3 -2
- package/src/components/settingsview/settingsview.module.scss +178 -0
- package/src/components/settingsview/settingsview.ts +289 -0
- package/src/components/spreadsheet/spreadsheet.module.scss +18 -16
- package/src/components/textarea/textarea.module.scss +10 -3
- package/src/components/video/video.ts +17 -3
- package/src/core/core_colors.ts +21 -10
- package/src/core/core_dragdrop.ts +5 -4
- package/src/core/core_i18n.ts +191 -54
- package/src/core/core_pdf.ts +15 -3
- package/src/core/core_shortcuts.ts +5 -1
- package/src/core/core_styles.ts +46 -25
- package/src/core/core_svg.ts +24 -16
- package/src/core/core_tools.ts +18 -9
package/src/core/core_i18n.ts
CHANGED
|
@@ -85,7 +85,12 @@ export function addTranslation( name: string, ...parts: any[] ) {
|
|
|
85
85
|
_patch( lang.src_translations, p );
|
|
86
86
|
} );
|
|
87
87
|
|
|
88
|
-
lang.translations = _proxyfy( lang.src_translations, lang.base,
|
|
88
|
+
lang.translations = _proxyfy( lang.src_translations, lang.base, [] );
|
|
89
|
+
|
|
90
|
+
// the current language: _tr sees the new translations at once
|
|
91
|
+
if( name==current.name ) {
|
|
92
|
+
current.translations = lang.translations;
|
|
93
|
+
}
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
/**
|
|
@@ -117,96 +122,71 @@ function _patch( obj: any, by: any ) {
|
|
|
117
122
|
/**
|
|
118
123
|
* create a proxy for all sub objects
|
|
119
124
|
* (deep traverse)
|
|
125
|
+
* @param path where obj is, from _tr: [] for _tr itself, ["global"] for _tr.global
|
|
120
126
|
*/
|
|
121
127
|
|
|
122
|
-
function _proxyfy( obj: any, base:
|
|
128
|
+
function _proxyfy( obj: any, base: string, path: string[] ) {
|
|
123
129
|
|
|
124
130
|
const result: any = {}
|
|
125
131
|
|
|
126
132
|
for( const n in obj ) {
|
|
127
133
|
if( typeof obj[n]!=="string" && !Array.isArray(obj[n])) {
|
|
128
|
-
result[n] = _proxyfy( obj[n], base,
|
|
134
|
+
result[n] = _proxyfy( obj[n], base, [...path, n] );
|
|
129
135
|
}
|
|
130
136
|
else {
|
|
131
137
|
result[n] = obj[n];
|
|
132
138
|
}
|
|
133
139
|
}
|
|
134
140
|
|
|
135
|
-
return _mk_proxy( result, base,
|
|
141
|
+
return _mk_proxy( result, base, path );
|
|
136
142
|
}
|
|
137
143
|
|
|
138
144
|
|
|
139
145
|
/**
|
|
140
146
|
* create a proxy for the given object
|
|
147
|
+
* what is not found is asked to the base language, at the same path
|
|
141
148
|
*/
|
|
142
149
|
|
|
143
|
-
function _mk_proxy( obj: any, base: string,
|
|
150
|
+
function _mk_proxy( obj: any, base: string, path: string[] ) : any {
|
|
144
151
|
return new Proxy( obj, {
|
|
145
152
|
get: (target, prop) => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
153
|
+
const value = target[prop];
|
|
154
|
+
|
|
155
|
+
// a symbol is not a translation
|
|
156
|
+
if( value!==undefined || typeof prop==="symbol" ) {
|
|
157
|
+
return value;
|
|
151
158
|
}
|
|
152
159
|
|
|
153
|
-
|
|
154
|
-
if( value===undefined ) {
|
|
155
|
-
if( base ) {
|
|
156
|
-
value = _findBaseTrans( base );
|
|
157
|
-
}
|
|
160
|
+
const asked = [...path, prop];
|
|
158
161
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
// not in this language: in its base, which looks in its own base
|
|
163
|
+
if( base ) {
|
|
164
|
+
return _findBaseTrans( base, asked );
|
|
162
165
|
}
|
|
163
166
|
|
|
164
|
-
|
|
167
|
+
console.error( "I18N error: unable to find", '_tr.'+asked.join('.') );
|
|
168
|
+
return undefined;
|
|
165
169
|
}
|
|
166
170
|
});
|
|
167
171
|
}
|
|
168
172
|
|
|
169
|
-
|
|
170
173
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* then when we try to get _tr.xxx.yyy
|
|
175
|
-
* reqpath is [xxx,yyy]
|
|
176
|
-
* if yyy is not found, we try with base langage for the full reqpath
|
|
177
|
-
* until no base found
|
|
174
|
+
* the translation at path in the language base
|
|
175
|
+
* its proxies look in their own base: the last language of the chain tells what is missing
|
|
178
176
|
*/
|
|
179
177
|
|
|
180
|
-
|
|
178
|
+
function _findBaseTrans( base: string, path: string[] ) {
|
|
181
179
|
|
|
182
|
-
|
|
183
|
-
*
|
|
184
|
-
*/
|
|
180
|
+
let trans = languages[base].translations;
|
|
185
181
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
let trans = lang.translations;
|
|
191
|
-
let value;
|
|
192
|
-
|
|
193
|
-
for( const p of req_path ) {
|
|
194
|
-
value = trans[p];
|
|
195
|
-
if( value===undefined ) {
|
|
196
|
-
break;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
trans = value;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if( value!==undefined ) {
|
|
203
|
-
return trans;
|
|
182
|
+
for( const p of path ) {
|
|
183
|
+
trans = trans[p];
|
|
184
|
+
if( trans===undefined ) {
|
|
185
|
+
return undefined;
|
|
204
186
|
}
|
|
205
|
-
|
|
206
|
-
base = lang.base;
|
|
207
187
|
}
|
|
208
188
|
|
|
209
|
-
return
|
|
189
|
+
return trans;
|
|
210
190
|
}
|
|
211
191
|
|
|
212
192
|
/**
|
|
@@ -320,7 +300,7 @@ let fr = {
|
|
|
320
300
|
|
|
321
301
|
diff_date_seconds: '{0} secondes',
|
|
322
302
|
diff_date_minutes: '{0} minutes',
|
|
323
|
-
diff_date_hours: '{0} heures',
|
|
303
|
+
diff_date_hours: '{0} heures {1} minutes',
|
|
324
304
|
|
|
325
305
|
invalid_date: 'Date non reconnue ({0})',
|
|
326
306
|
empty_list: 'Liste vide',
|
|
@@ -351,6 +331,11 @@ let fr = {
|
|
|
351
331
|
numeric: '123',
|
|
352
332
|
alpha: 'Abc'
|
|
353
333
|
},
|
|
334
|
+
|
|
335
|
+
settings: {
|
|
336
|
+
search: 'Rechercher un paramètre',
|
|
337
|
+
nomatch: 'Aucun paramètre trouvé',
|
|
338
|
+
},
|
|
354
339
|
}
|
|
355
340
|
};
|
|
356
341
|
|
|
@@ -385,7 +370,7 @@ let en = {
|
|
|
385
370
|
|
|
386
371
|
diff_date_seconds: '{0} seconds',
|
|
387
372
|
diff_date_minutes: '{0} minutes',
|
|
388
|
-
diff_date_hours: '{0} hours',
|
|
373
|
+
diff_date_hours: '{0} hours {1} minutes',
|
|
389
374
|
|
|
390
375
|
invalid_date: 'Unrecognized date({0})',
|
|
391
376
|
empty_list: 'Empty list',
|
|
@@ -416,6 +401,151 @@ let en = {
|
|
|
416
401
|
numeric: '123',
|
|
417
402
|
alpha: 'Abc'
|
|
418
403
|
},
|
|
404
|
+
|
|
405
|
+
settings: {
|
|
406
|
+
search: 'Search settings',
|
|
407
|
+
nomatch: 'No setting found',
|
|
408
|
+
},
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
/** @ignore */
|
|
413
|
+
|
|
414
|
+
let es = {
|
|
415
|
+
global: {
|
|
416
|
+
ok: 'Aceptar',
|
|
417
|
+
cancel: 'Cancelar',
|
|
418
|
+
ignore: 'Ignorar',
|
|
419
|
+
yes: 'Sí',
|
|
420
|
+
no: 'No',
|
|
421
|
+
abort: "Anular",
|
|
422
|
+
retry: "Reintentar",
|
|
423
|
+
|
|
424
|
+
error: "Error",
|
|
425
|
+
today: "Hoy",
|
|
426
|
+
|
|
427
|
+
open: 'Abrir',
|
|
428
|
+
new: 'Nuevo',
|
|
429
|
+
delete: 'Eliminar',
|
|
430
|
+
close: 'Cerrar',
|
|
431
|
+
save: 'Guardar',
|
|
432
|
+
|
|
433
|
+
search: 'Buscar',
|
|
434
|
+
search_tip: 'Escriba el texto que desea buscar. <b>Intro</b> para iniciar la búsqueda. <b>Esc</b> para cancelar.',
|
|
435
|
+
|
|
436
|
+
required_field: "dato obligatorio",
|
|
437
|
+
invalid_format: "formato no válido",
|
|
438
|
+
invalid_email: 'dirección de correo no válida',
|
|
439
|
+
invalid_number: 'valor numérico no válido',
|
|
440
|
+
|
|
441
|
+
diff_date_seconds: '{0} segundos',
|
|
442
|
+
diff_date_minutes: '{0} minutos',
|
|
443
|
+
diff_date_hours: '{0} horas {1} minutos',
|
|
444
|
+
|
|
445
|
+
invalid_date: 'Fecha no reconocida ({0})',
|
|
446
|
+
empty_list: 'Lista vacía',
|
|
447
|
+
|
|
448
|
+
date_input_formats: 'd/m/y|d.m.y|d m y|d-m-y|dmy',
|
|
449
|
+
date_format: 'D/M/Y',
|
|
450
|
+
date_time_format: 'D/M/Y H:I:S',
|
|
451
|
+
|
|
452
|
+
day_short: [ 'dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb' ],
|
|
453
|
+
day_long: [ 'domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado' ],
|
|
454
|
+
|
|
455
|
+
month_short: [ 'ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic' ],
|
|
456
|
+
month_long: [ 'enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre' ],
|
|
457
|
+
|
|
458
|
+
property: 'Propiedad',
|
|
459
|
+
value: 'Valor',
|
|
460
|
+
|
|
461
|
+
err_403: `No tiene permisos suficientes para realizar esta acción`,
|
|
462
|
+
|
|
463
|
+
copy: 'Copiar',
|
|
464
|
+
cut: 'Cortar',
|
|
465
|
+
paste: 'Pegar',
|
|
466
|
+
|
|
467
|
+
filedrop: 'Suelte un archivo',
|
|
468
|
+
|
|
469
|
+
keyboard: {
|
|
470
|
+
next: 'Siguiente',
|
|
471
|
+
numeric: '123',
|
|
472
|
+
alpha: 'Abc'
|
|
473
|
+
},
|
|
474
|
+
|
|
475
|
+
settings: {
|
|
476
|
+
search: 'Buscar un ajuste',
|
|
477
|
+
nomatch: 'Ningún ajuste encontrado',
|
|
478
|
+
},
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
/** @ignore */
|
|
483
|
+
|
|
484
|
+
let de = {
|
|
485
|
+
global: {
|
|
486
|
+
ok: 'OK',
|
|
487
|
+
cancel: 'Abbrechen',
|
|
488
|
+
ignore: 'Ignorieren',
|
|
489
|
+
yes: 'Ja',
|
|
490
|
+
no: 'Nein',
|
|
491
|
+
abort: "Abbrechen",
|
|
492
|
+
retry: "Wiederholen",
|
|
493
|
+
|
|
494
|
+
error: "Fehler",
|
|
495
|
+
today: "Heute",
|
|
496
|
+
|
|
497
|
+
open: 'Öffnen',
|
|
498
|
+
new: 'Neu',
|
|
499
|
+
delete: 'Löschen',
|
|
500
|
+
close: 'Schließen',
|
|
501
|
+
save: 'Speichern',
|
|
502
|
+
|
|
503
|
+
search: 'Suchen',
|
|
504
|
+
search_tip: 'Geben Sie den zu suchenden Text ein. <b>Enter</b> startet die Suche. <b>Esc</b> bricht ab.',
|
|
505
|
+
|
|
506
|
+
required_field: "Pflichtangabe",
|
|
507
|
+
invalid_format: "ungültiges Format",
|
|
508
|
+
invalid_email: 'ungültige E-Mail-Adresse',
|
|
509
|
+
invalid_number: 'ungültiger Zahlenwert',
|
|
510
|
+
|
|
511
|
+
diff_date_seconds: '{0} Sekunden',
|
|
512
|
+
diff_date_minutes: '{0} Minuten',
|
|
513
|
+
diff_date_hours: '{0} Stunden {1} Minuten',
|
|
514
|
+
|
|
515
|
+
invalid_date: 'Datum nicht erkannt ({0})',
|
|
516
|
+
empty_list: 'Leere Liste',
|
|
517
|
+
|
|
518
|
+
date_input_formats: 'd.m.y|d/m/y|d m y|d-m-y|dmy',
|
|
519
|
+
date_format: 'D.M.Y',
|
|
520
|
+
date_time_format: 'D.M.Y H:I:S',
|
|
521
|
+
|
|
522
|
+
day_short: [ 'So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa' ],
|
|
523
|
+
day_long: [ 'Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag' ],
|
|
524
|
+
|
|
525
|
+
month_short: [ 'Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez' ],
|
|
526
|
+
month_long: [ 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ],
|
|
527
|
+
|
|
528
|
+
property: 'Eigenschaft',
|
|
529
|
+
value: 'Wert',
|
|
530
|
+
|
|
531
|
+
err_403: `Sie haben nicht die erforderlichen Rechte für diese Aktion`,
|
|
532
|
+
|
|
533
|
+
copy: 'Kopieren',
|
|
534
|
+
cut: 'Ausschneiden',
|
|
535
|
+
paste: 'Einfügen',
|
|
536
|
+
|
|
537
|
+
filedrop: 'Datei hier ablegen',
|
|
538
|
+
|
|
539
|
+
keyboard: {
|
|
540
|
+
next: 'Weiter',
|
|
541
|
+
numeric: '123',
|
|
542
|
+
alpha: 'Abc'
|
|
543
|
+
},
|
|
544
|
+
|
|
545
|
+
settings: {
|
|
546
|
+
search: 'Einstellungen durchsuchen',
|
|
547
|
+
nomatch: 'Keine Einstellung gefunden',
|
|
548
|
+
},
|
|
419
549
|
}
|
|
420
550
|
};
|
|
421
551
|
|
|
@@ -425,6 +555,13 @@ addTranslation( 'fr', fr );
|
|
|
425
555
|
createLanguage( 'en', 'fr' );
|
|
426
556
|
addTranslation( 'en', en );
|
|
427
557
|
|
|
558
|
+
// a key they lack is taken in english
|
|
559
|
+
createLanguage( 'es', 'en' );
|
|
560
|
+
addTranslation( 'es', es );
|
|
561
|
+
|
|
562
|
+
createLanguage( 'de', 'en' );
|
|
563
|
+
addTranslation( 'de', de );
|
|
564
|
+
|
|
428
565
|
selectLanguage( 'fr' ); // by default
|
|
429
566
|
|
|
430
567
|
|
package/src/core/core_pdf.ts
CHANGED
|
@@ -835,7 +835,13 @@ function transformSegments( segs: PathSegment[], m: Matrix ): PathSegment[] {
|
|
|
835
835
|
|
|
836
836
|
function svgSource( icon: string ): string {
|
|
837
837
|
while (icon.startsWith('var:')) {
|
|
838
|
-
|
|
838
|
+
// "var:home" or "var:--home": the variable --home, set on <html>
|
|
839
|
+
let name = icon.substring(4)
|
|
840
|
+
if (!name.startsWith('--')) {
|
|
841
|
+
name = '--' + name
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
icon = document.documentElement.style.getPropertyValue(name).trim()
|
|
839
845
|
}
|
|
840
846
|
|
|
841
847
|
if (icon.startsWith('data:image/svg+xml;base64,')) {
|
|
@@ -1297,13 +1303,19 @@ export class X4PDFBuilder {
|
|
|
1297
1303
|
}
|
|
1298
1304
|
|
|
1299
1305
|
const image = (jpegBytes: Uint8Array, x: number, y: number, w: number, h: number) => {
|
|
1306
|
+
// the size is in the frame header (SOF): the segments are followed, an EXIF
|
|
1307
|
+
// thumbnail inside a segment is skipped
|
|
1300
1308
|
let imgWidth = 0, imgHeight = 0
|
|
1301
|
-
|
|
1302
|
-
|
|
1309
|
+
let i = 2
|
|
1310
|
+
while (i + 9 < jpegBytes.length && jpegBytes[i] === 0xFF) {
|
|
1311
|
+
const marker = jpegBytes[i + 1]
|
|
1312
|
+
if (marker >= 0xC0 && marker <= 0xCF && marker !== 0xC4 && marker !== 0xC8 && marker !== 0xCC) {
|
|
1303
1313
|
imgHeight = (jpegBytes[i + 5] << 8) | jpegBytes[i + 6]
|
|
1304
1314
|
imgWidth = (jpegBytes[i + 7] << 8) | jpegBytes[i + 8]
|
|
1305
1315
|
break
|
|
1306
1316
|
}
|
|
1317
|
+
|
|
1318
|
+
i += 2 + ((jpegBytes[i + 2] << 8) | jpegBytes[i + 3])
|
|
1307
1319
|
}
|
|
1308
1320
|
|
|
1309
1321
|
const imgName = `/Im${imageCount++}`
|
|
@@ -91,6 +91,11 @@ function sequenceOf( ev: KeyboardEvent ): string {
|
|
|
91
91
|
key = ev.code[3].toLowerCase( );
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
// the space bar ( " " ): written "Space" in a sequence
|
|
95
|
+
if( key===" " ) {
|
|
96
|
+
key = "space";
|
|
97
|
+
}
|
|
98
|
+
|
|
94
99
|
return join( { shift: ev.shiftKey, ctrl: ev.ctrlKey, cmd: ev.metaKey, alt: ev.altKey, key } );
|
|
95
100
|
}
|
|
96
101
|
|
|
@@ -158,7 +163,6 @@ const key_names: Record<string,string> = {
|
|
|
158
163
|
arrowdown: "↓",
|
|
159
164
|
escape: "Esc",
|
|
160
165
|
delete: "Del",
|
|
161
|
-
" ": "Space",
|
|
162
166
|
};
|
|
163
167
|
|
|
164
168
|
/**
|
package/src/core/core_styles.ts
CHANGED
|
@@ -121,33 +121,42 @@ export function isUnitLess( name: string ) {
|
|
|
121
121
|
return unitless[name] ? true : false;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
// one sheet for the page, shared by all the Stylesheet: a name is one rule,
|
|
125
|
+
// whatever instance sets it
|
|
126
|
+
let page_sheet: CSSStyleSheet = null;
|
|
127
|
+
const page_rules = new Map<string, CSSRule>( );
|
|
128
|
+
|
|
129
|
+
function pageSheet( ): CSSStyleSheet {
|
|
130
|
+
if( !page_sheet ) {
|
|
131
|
+
// found by its id: a title would make it a "preferred" style sheet,
|
|
132
|
+
// of which a page applies only one
|
|
133
|
+
let dom = document.getElementById( 'x4-dynamic-css' ) as HTMLStyleElement;
|
|
134
|
+
if( !dom ) {
|
|
135
|
+
dom = document.createElement( 'style' );
|
|
136
|
+
dom.id = 'x4-dynamic-css';
|
|
137
|
+
document.head.appendChild( dom );
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
page_sheet = dom.sheet;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return page_sheet;
|
|
144
|
+
}
|
|
145
|
+
|
|
124
146
|
/**
|
|
125
|
-
*
|
|
147
|
+
* Rules added to the page while it runs, in one <style id="x4-dynamic-css">.
|
|
148
|
+
* @deprecated the CSS variables do it: setStyleVariable( ) on a component,
|
|
149
|
+
* var( --name ) in its stylesheet
|
|
126
150
|
*/
|
|
127
151
|
|
|
128
152
|
export class Stylesheet {
|
|
129
153
|
|
|
130
154
|
readonly _sheet: CSSStyleSheet;
|
|
131
|
-
readonly _rules: Map<string,
|
|
155
|
+
readonly _rules: Map<string, CSSRule>;
|
|
132
156
|
|
|
133
157
|
constructor() {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
for(let i=0; i<document.styleSheets.length; i++) {
|
|
137
|
-
let sheet = document.styleSheets[i];
|
|
138
|
-
if(sheet.title === name ) {
|
|
139
|
-
return sheet;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
this._sheet = getStyleSheet( 'x4-dynamic-css' );
|
|
145
|
-
if( !this._sheet ) {
|
|
146
|
-
const dom = document.createElement( 'style' );
|
|
147
|
-
dom.setAttribute('id', 'x4-dynamic-css' );
|
|
148
|
-
document.head.appendChild(dom);
|
|
149
|
-
this._sheet = dom.sheet
|
|
150
|
-
}
|
|
158
|
+
this._sheet = pageSheet( );
|
|
159
|
+
this._rules = page_rules;
|
|
151
160
|
}
|
|
152
161
|
|
|
153
162
|
/**
|
|
@@ -156,20 +165,31 @@ export class Stylesheet {
|
|
|
156
165
|
* @param {string} definition - css definition of the rule
|
|
157
166
|
* @example
|
|
158
167
|
* setRule('xbody', "body { background-color: #ff0000; }" );
|
|
168
|
+
* @deprecated a CSS variable: setStyleVariable( ) on a component, var( --name ) in its stylesheet
|
|
159
169
|
*/
|
|
160
170
|
|
|
161
171
|
public setRule(name: string, definition: any ) {
|
|
162
172
|
|
|
163
173
|
if( isString(definition) ) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
174
|
+
const sheet = this._sheet;
|
|
175
|
+
const old = this._rules.get( name );
|
|
176
|
+
|
|
177
|
+
// in the place of the rule of that name, else at the end
|
|
178
|
+
let index = sheet.cssRules.length;
|
|
179
|
+
if( old ) {
|
|
180
|
+
const at = Array.prototype.indexOf.call( sheet.cssRules, old );
|
|
181
|
+
if( at>=0 ) {
|
|
182
|
+
index = at;
|
|
183
|
+
}
|
|
167
184
|
}
|
|
168
|
-
|
|
169
|
-
|
|
185
|
+
|
|
186
|
+
// inserted first: a definition that is not valid throws, the old rule stays
|
|
187
|
+
sheet.insertRule( definition, index );
|
|
188
|
+
if( old && sheet.cssRules[index+1]===old ) {
|
|
189
|
+
sheet.deleteRule( index+1 );
|
|
170
190
|
}
|
|
171
191
|
|
|
172
|
-
this._rules.set( name,
|
|
192
|
+
this._rules.set( name, sheet.cssRules[index] );
|
|
173
193
|
}
|
|
174
194
|
else {
|
|
175
195
|
let idx = 1;
|
|
@@ -203,6 +223,7 @@ export class Stylesheet {
|
|
|
203
223
|
* ```
|
|
204
224
|
* let color = Component.getCss( ).getVar( 'button-color' );
|
|
205
225
|
* ```
|
|
226
|
+
* @deprecated getStyleVariable( ) of a component, or getComputedStyle( document.documentElement ).getPropertyValue( "--name" )
|
|
206
227
|
*/
|
|
207
228
|
|
|
208
229
|
public static getVar( name: string ) : any {
|
package/src/core/core_svg.ts
CHANGED
|
@@ -156,7 +156,7 @@ class SvgItem {
|
|
|
156
156
|
* @param cap
|
|
157
157
|
*/
|
|
158
158
|
|
|
159
|
-
strokeCap( cap: "butt" | "round" | "
|
|
159
|
+
strokeCap( cap: "butt" | "round" | "square" ) {
|
|
160
160
|
return this.setAttr( "stroke-linecap", cap );
|
|
161
161
|
}
|
|
162
162
|
|
|
@@ -218,12 +218,13 @@ class SvgItem {
|
|
|
218
218
|
*/
|
|
219
219
|
|
|
220
220
|
getNumAttr( name: string ) {
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
// not set or empty: 0; "1.5" is 1.5
|
|
222
|
+
const a = this._dom.getAttribute( name );
|
|
223
|
+
if( !a ) {
|
|
223
224
|
return 0;
|
|
224
225
|
}
|
|
225
226
|
|
|
226
|
-
return
|
|
227
|
+
return parseFloat( a );
|
|
227
228
|
}
|
|
228
229
|
|
|
229
230
|
/**
|
|
@@ -271,7 +272,9 @@ class SvgItem {
|
|
|
271
272
|
*/
|
|
272
273
|
|
|
273
274
|
addClass( cls: string ): this {
|
|
274
|
-
if( !cls )
|
|
275
|
+
if( !cls ) {
|
|
276
|
+
return this;
|
|
277
|
+
}
|
|
275
278
|
|
|
276
279
|
cls = cls.trim();
|
|
277
280
|
if( cls.indexOf(' ')>=0 ) {
|
|
@@ -484,12 +487,13 @@ export class SvgPath extends SvgItem {
|
|
|
484
487
|
|
|
485
488
|
/**
|
|
486
489
|
* draw an arc
|
|
487
|
-
*
|
|
490
|
+
* A circular arc around (x, y), from the angle start to end in degrees: 0 is the top,
|
|
491
|
+
* 90 the right (clockwise). It starts a new subpath at its first point.
|
|
488
492
|
*
|
|
489
|
-
* @param x - The x-coordinate of the center
|
|
490
|
-
* @param y - The y-coordinate of the center
|
|
493
|
+
* @param x - The x-coordinate of the center.
|
|
494
|
+
* @param y - The y-coordinate of the center.
|
|
491
495
|
* @param r - The radius of the arc.
|
|
492
|
-
* @param start - The start angle of the arc in degrees (0 is
|
|
496
|
+
* @param start - The start angle of the arc in degrees (0 is the top, 90 is the right).
|
|
493
497
|
* @param end - The end angle of the arc in degrees.
|
|
494
498
|
* @param clockwise - If `true`, the arc is drawn clockwise; otherwise, counter-clockwise. Defaults to `true`.
|
|
495
499
|
*
|
|
@@ -612,12 +616,16 @@ export class SvgIcon extends SvgItem {
|
|
|
612
616
|
constructor( svg: string ) {
|
|
613
617
|
super( "svg" );
|
|
614
618
|
|
|
619
|
+
// a data url is decoded when it is encoded ("data:image/svg+xml,%3Csvg..."), a raw svg never
|
|
615
620
|
if( svg.startsWith("data:image/svg+xml,") ) {
|
|
616
621
|
svg = svg.substring( 19 );
|
|
622
|
+
if( !svg.startsWith( "<" ) ) {
|
|
623
|
+
svg = decodeURIComponent( svg );
|
|
624
|
+
}
|
|
617
625
|
}
|
|
618
626
|
|
|
619
627
|
const parser = new DOMParser();
|
|
620
|
-
const doc = parser.parseFromString(
|
|
628
|
+
const doc = parser.parseFromString( svg, "image/svg+xml");
|
|
621
629
|
|
|
622
630
|
const parserErrorElement = doc.querySelector("parsererror");
|
|
623
631
|
if( parserErrorElement ) {
|
|
@@ -629,11 +637,9 @@ export class SvgIcon extends SvgItem {
|
|
|
629
637
|
this._dom.setAttribute( svgRoot.attributes[i].name, svgRoot.attributes[i].value );
|
|
630
638
|
}
|
|
631
639
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
this._dom.appendChild(child);
|
|
636
|
-
}
|
|
640
|
+
// the elements, all of them: each one moved leaves the list
|
|
641
|
+
for( const child of Array.from( svgRoot.children ) ) {
|
|
642
|
+
this._dom.appendChild( child );
|
|
637
643
|
}
|
|
638
644
|
}
|
|
639
645
|
}
|
|
@@ -707,7 +713,9 @@ export class SvgGradient extends SvgItem {
|
|
|
707
713
|
* @returns The current `SvgGradient` instance for chaining.
|
|
708
714
|
*/
|
|
709
715
|
addStop( offset: number_or_perc, color: string ): this {
|
|
710
|
-
|
|
716
|
+
// a number is a percentage: 20 is "20%"
|
|
717
|
+
const o = isString( offset ) ? offset : offset + "%";
|
|
718
|
+
this._dom.insertAdjacentHTML( "beforeend", `<stop offset="${o}" stop-color="${color}"></stop>`);
|
|
711
719
|
return this;
|
|
712
720
|
}
|
|
713
721
|
}
|
package/src/core/core_tools.ts
CHANGED
|
@@ -124,8 +124,8 @@ export function unsafeHtml(x: string): UnsafeHtml {
|
|
|
124
124
|
export function unsafe(strings: TemplateStringsArray, ...values: any[]): UnsafeHtml {
|
|
125
125
|
|
|
126
126
|
const resolve = ( value: any ) : string | UnsafeHtml => {
|
|
127
|
-
|
|
128
|
-
if(
|
|
127
|
+
// nothing to write: null, undefined (after the last part of the template)
|
|
128
|
+
if( value===undefined || value===null ) {
|
|
129
129
|
return "";
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -137,7 +137,8 @@ export function unsafe(strings: TemplateStringsArray, ...values: any[]): UnsafeH
|
|
|
137
137
|
return value.toString();
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
// a number, a boolean...: its text, escaped
|
|
141
|
+
return sanitizeHtml( String( value ) );
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
const result = strings.reduce((acc, str, i) => {
|
|
@@ -561,7 +562,8 @@ export function date_format(date: Date, options?: any): string {
|
|
|
561
562
|
|
|
562
563
|
export function date_diff(date1: Date, date2: Date, options?: any): string {
|
|
563
564
|
|
|
564
|
-
|
|
565
|
+
// the gap, whichever date comes first
|
|
566
|
+
let dt = Math.abs(date1.getTime() - date2.getTime()) / 1000;
|
|
565
567
|
|
|
566
568
|
// seconds
|
|
567
569
|
let sec = dt;
|
|
@@ -575,7 +577,7 @@ export function date_diff(date1: Date, date2: Date, options?: any): string {
|
|
|
575
577
|
return sprintf(_tr.global.diff_date_minutes, Math.round(min));
|
|
576
578
|
}
|
|
577
579
|
|
|
578
|
-
// hours
|
|
580
|
+
// hours, and the minutes left
|
|
579
581
|
let hrs = Math.floor(min / 60);
|
|
580
582
|
return sprintf(_tr.global.diff_date_hours, hrs, min % 60);
|
|
581
583
|
}
|
|
@@ -626,13 +628,20 @@ export function date_clone(date: Date): Date {
|
|
|
626
628
|
}
|
|
627
629
|
|
|
628
630
|
/**
|
|
629
|
-
* return the week number of a date
|
|
631
|
+
* return the week number of a date, ISO 8601: weeks from monday,
|
|
632
|
+
* the week 1 holds the first thursday of the year (the 29/12/2025 is in the week 1 of 2026)
|
|
630
633
|
*/
|
|
631
634
|
|
|
632
635
|
export function date_calc_weeknum(date: Date): number {
|
|
633
|
-
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
+
// in UTC: a day of 23 or 25 hours (summer time) does not move the weeks
|
|
637
|
+
const d = new Date( Date.UTC( date.getFullYear( ), date.getMonth( ), date.getDate( ) ) );
|
|
638
|
+
|
|
639
|
+
// the thursday of its week gives the year of the week
|
|
640
|
+
const day = d.getUTCDay( ) || 7; // monday 1 .. sunday 7
|
|
641
|
+
d.setUTCDate( d.getUTCDate( ) + 4 - day );
|
|
642
|
+
|
|
643
|
+
const jan1 = Date.UTC( d.getUTCFullYear( ), 0, 1 );
|
|
644
|
+
return Math.ceil( ( ( d.getTime( ) - jan1 ) / 86400000 + 1 ) / 7 );
|
|
636
645
|
}
|
|
637
646
|
|
|
638
647
|
|