turbogui-angular 18.0.0 → 19.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/turbogui-angular.mjs +1408 -1410
- package/fesm2022/turbogui-angular.mjs.map +1 -1
- package/main/controller/{locales.service.d.ts → locales-base.service.d.ts} +7 -10
- package/main/controller/locales-base.service.d.ts.map +1 -0
- package/main/controller/router-base.service.d.ts +10 -4
- package/main/controller/router-base.service.d.ts.map +1 -1
- package/package.json +1 -1
- package/public_api.d.ts +1 -1
- package/public_api.d.ts.map +1 -1
- package/main/controller/locales.service.d.ts.map +0 -1
|
@@ -383,610 +383,595 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImpor
|
|
|
383
383
|
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
384
384
|
*/
|
|
385
385
|
/**
|
|
386
|
-
*
|
|
386
|
+
* This is the base class for all the dialog components that can be loaded by the dialog service class
|
|
387
387
|
*/
|
|
388
|
-
class
|
|
389
|
-
|
|
390
|
-
*
|
|
391
|
-
*/
|
|
392
|
-
constructor() {
|
|
393
|
-
super(LocalesService);
|
|
394
|
-
/**
|
|
395
|
-
* if the class has been correctly initialized and translations have been correctly loaded
|
|
396
|
-
*/
|
|
397
|
-
this._isInitialized = false;
|
|
398
|
-
/**
|
|
399
|
-
* @see getLocales()
|
|
400
|
-
*/
|
|
401
|
-
this._locales = [];
|
|
402
|
-
/**
|
|
403
|
-
* @see getLanguages()
|
|
404
|
-
*/
|
|
405
|
-
this._languages = [];
|
|
406
|
-
/**
|
|
407
|
-
* Stores all the loaded localization data by library name, bundle name, key and locales
|
|
408
|
-
*/
|
|
409
|
-
this._loadedTranslations = {};
|
|
410
|
-
/**
|
|
411
|
-
* Stores a memory cache to improve performance when outputing translations
|
|
412
|
-
*/
|
|
413
|
-
this._keyValuesCache = {};
|
|
414
|
-
/**
|
|
415
|
-
* @see setWildCardsFormat()
|
|
416
|
-
*/
|
|
417
|
-
this._wildCardsFormat = '{N}';
|
|
418
|
-
/**
|
|
419
|
-
* @see setMissingKeyFormat()
|
|
420
|
-
*/
|
|
421
|
-
this._missingKeyFormat = '$exception';
|
|
422
|
-
/**
|
|
423
|
-
* Stores a hash value that is used to improve the performance for translation t() methods.
|
|
424
|
-
* This is computed based on _wildCardsFormat plus _missingKeyFormat plus the current primary locale
|
|
425
|
-
* Methods that change these values will recalculate the hash string, so when calling translation methods, the
|
|
426
|
-
* performance will be as fast as possible.
|
|
427
|
-
*/
|
|
428
|
-
this._cacheHashBaseString = '';
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* Wildcards are string fragments that are placed inside the translated texts. Their main purpose is to be replaced at
|
|
432
|
-
* runtime by custom values like for example a user name, a date, a numeric value, etc..
|
|
433
|
-
*
|
|
434
|
-
* This class helps with this process by including a parameter called 'toReplace' on all ->t methods which allows us
|
|
435
|
-
* to specify a string or list of strings that will replace the respective wildcards on the translated text. Each wildcard
|
|
436
|
-
* must follow the format specified here, and contain a numeric digit that will be used to find the replacement text at the
|
|
437
|
-
* 'toReplace' list. For example, if we define $N as the wildcard format, and we have a translation that contains $0, $1, $2,
|
|
438
|
-
* $0 will be replaced with the first element on toReplace, $1 with the second and so.
|
|
439
|
-
*
|
|
440
|
-
* We usually set this before initializing the class translation data
|
|
441
|
-
*
|
|
442
|
-
* Notice that N is mandayory on the wildcards format and the first index value is 0.
|
|
443
|
-
*
|
|
444
|
-
* @param value The wildcards format we want to set
|
|
445
|
-
*
|
|
446
|
-
* @returns The value that's been set
|
|
447
|
-
*/
|
|
448
|
-
setWildCardsFormat(value) {
|
|
449
|
-
if (!value.includes('N')) {
|
|
450
|
-
throw new Error("N is mandatory to replace wildcards");
|
|
451
|
-
}
|
|
452
|
-
this._cacheHashBaseString = value + this._missingKeyFormat + ((this._locales.length > 0) ? this._locales[0] : '');
|
|
453
|
-
this._wildCardsFormat = value;
|
|
454
|
-
return value;
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* Defines the behaviour for t(), tStartCase(), etc... methods when a key is not found on
|
|
458
|
-
* a bundle or the bundle does not exist
|
|
459
|
-
*
|
|
460
|
-
* If missingKeyFormat is an empty string, all missing keys will return an empty value (not recommended)
|
|
461
|
-
*
|
|
462
|
-
* If missingKeyFormat contains a string, that string will be always returned for missing keys
|
|
463
|
-
*
|
|
464
|
-
* If missingKeyFormat contains a string with one of the following predefined wildcards:<br>
|
|
465
|
-
* - $key will be replaced with key name. Example: get("NAME") will output [NAME] if key is not found and missingKeyFormat = '[$key]'<br>
|
|
466
|
-
* - $exception (default value) will throw an exception with the problem cause description.
|
|
388
|
+
class DialogBaseComponent {
|
|
389
|
+
/*
|
|
390
|
+
* The name of the superclass must be set into this constant as it is required by the dialog service to identify dialogs as different.
|
|
467
391
|
*
|
|
468
|
-
*
|
|
392
|
+
* When you extend the dialog base class, simply declare this static constant with the exact same name as your class and you're done.
|
|
393
|
+
* If this value is not set on the extended dialog component, a runtime exception will happen when trying to show the dialog.
|
|
469
394
|
*
|
|
470
|
-
*
|
|
471
|
-
|
|
472
|
-
setMissingKeyFormat(value) {
|
|
473
|
-
this._cacheHashBaseString = this._wildCardsFormat + value + ((this._locales.length > 0) ? this._locales[0] : '');
|
|
474
|
-
this._missingKeyFormat = value;
|
|
475
|
-
return value;
|
|
476
|
-
}
|
|
477
|
-
/**
|
|
478
|
-
* @see setMissingKeyFormat()
|
|
395
|
+
* The root cause of this requirement is that when apps are compiled for production, class names are minified and this causes problems
|
|
396
|
+
* when creating a dialog hash to uniquely identify a dialog instance. Therefore, a hardcoded class name is necesary.
|
|
479
397
|
*/
|
|
480
|
-
|
|
481
|
-
|
|
398
|
+
static { this.DIALOG_CLASS_NAME = ''; }
|
|
399
|
+
constructor(elementRef, dialogRef) {
|
|
400
|
+
this.elementRef = elementRef;
|
|
401
|
+
this.dialogRef = dialogRef;
|
|
482
402
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
* @param translations A JSON object containing the translation data. The structure must be as follows:
|
|
488
|
-
* { library_name: { bundle_name: { locale_code: { key1: "translation1", key2: "translation2" } } } ... }
|
|
489
|
-
*
|
|
490
|
-
* @param locales An array of locale codes (e.g., ['en_US', 'es_ES', 'fr_FR']) to load into this class. The order of this array
|
|
491
|
-
* will determine the translation priority
|
|
492
|
-
*
|
|
493
|
-
* @return True if the translations get correctly loaded. Any unsuccessful initialization will throw an exception
|
|
494
|
-
*/
|
|
495
|
-
initializeFromJson(translations, locales) {
|
|
496
|
-
this._isInitialized = false;
|
|
497
|
-
// Validate received locales are correct
|
|
498
|
-
for (const locale of locales) {
|
|
499
|
-
this._validateLocaleString(locale);
|
|
500
|
-
}
|
|
501
|
-
// Validate the translations object follows the right structure
|
|
502
|
-
let isTranslationsValid = false;
|
|
503
|
-
for (const library in translations) {
|
|
504
|
-
for (const bundle in translations[library]) {
|
|
505
|
-
for (const locale in translations[library][bundle]) {
|
|
506
|
-
this._validateLocaleString(locale);
|
|
507
|
-
isTranslationsValid = true;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
if (!isTranslationsValid) {
|
|
512
|
-
throw new Error('translations must be a non empty object with the structure: { library: { bundle: { xx_XX: { key: translation } } } }');
|
|
403
|
+
ngAfterViewInit() {
|
|
404
|
+
// Assign the component HTML identifier if it is specifically assigned to the dialogref instance
|
|
405
|
+
if (this.dialogRef.id !== undefined && this.dialogRef.id !== '') {
|
|
406
|
+
this.elementRef.nativeElement.id = this.dialogRef.id;
|
|
513
407
|
}
|
|
514
|
-
this._loadedTranslations = translations;
|
|
515
|
-
this._locales = locales;
|
|
516
|
-
this._languages = locales.map((l) => l.substring(0, 2));
|
|
517
|
-
this._cacheHashBaseString = this._wildCardsFormat + this._missingKeyFormat + this._locales[0];
|
|
518
|
-
return this._isInitialized = true;
|
|
519
408
|
}
|
|
520
409
|
/**
|
|
521
|
-
*
|
|
522
|
-
*
|
|
523
|
-
*
|
|
410
|
+
* Method to be called by the dialogs that extend this base component when they want to close themselves.
|
|
411
|
+
* It will perform the close of that dialog and also send an object to the addDialog() callback with the index and value
|
|
412
|
+
* that the user may have selected.
|
|
524
413
|
*
|
|
525
|
-
* @param
|
|
526
|
-
* @param
|
|
527
|
-
* path using the following format: translationsPath/en_US-es_ES-fr_FR. The order of this array will determine the
|
|
528
|
-
* translation priority
|
|
529
|
-
* @param parameters Any extra parameters to be attached to the translationsPath after the locales like /param1/param2/ etc
|
|
414
|
+
* @param index The numeric index of the user option selection. It will be specific for each dialog and it's different available options
|
|
415
|
+
* @param value Any value that may be provided to the callback regarding the user selected option.
|
|
530
416
|
*
|
|
531
|
-
* @return
|
|
532
|
-
*/
|
|
533
|
-
initializeFromUrl(translationsPath, locales, parameters) {
|
|
534
|
-
this._isInitialized = false;
|
|
535
|
-
const translationsFullPath = translationsPath + '/' + locales.join('-') + '/' + parameters.join('/');
|
|
536
|
-
return new Promise((resolve, reject) => {
|
|
537
|
-
fetch(translationsFullPath).then(response => {
|
|
538
|
-
if (!response.ok) {
|
|
539
|
-
throw new Error(`HTTP error! status: ${response.status}`);
|
|
540
|
-
}
|
|
541
|
-
return response.json();
|
|
542
|
-
}).then(data => {
|
|
543
|
-
this.initializeFromJson(data, locales);
|
|
544
|
-
resolve(undefined);
|
|
545
|
-
}).catch(error => {
|
|
546
|
-
reject(new Error(`ERROR LOADING LOCALES FROM: ${translationsFullPath}\n` + error));
|
|
547
|
-
});
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
/**
|
|
551
|
-
* Check if the class has been correctly initialized and translations have been correctly loaded
|
|
417
|
+
* @return void
|
|
552
418
|
*/
|
|
553
|
-
|
|
554
|
-
|
|
419
|
+
closeDialog(index, value = null) {
|
|
420
|
+
this.dialogRef.close({ index: index, value: value });
|
|
555
421
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
422
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DialogBaseComponent, deps: [{ token: i0.ElementRef }, { token: i1.MatDialogRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
423
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: DialogBaseComponent, isStandalone: false, selector: "ng-component", ngImport: i0, template: '', isInline: true }); }
|
|
424
|
+
}
|
|
425
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DialogBaseComponent, decorators: [{
|
|
426
|
+
type: Component,
|
|
427
|
+
args: [{
|
|
428
|
+
template: '',
|
|
429
|
+
standalone: false
|
|
430
|
+
}]
|
|
431
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.MatDialogRef }] });
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
435
|
+
*
|
|
436
|
+
* Website : -> http://www.turbogui.org
|
|
437
|
+
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
438
|
+
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
439
|
+
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
440
|
+
*/
|
|
441
|
+
/**
|
|
442
|
+
* A dialog component with a single option button, to be used for error notifications
|
|
443
|
+
*/
|
|
444
|
+
class DialogErrorComponent extends DialogBaseComponent {
|
|
445
|
+
static { this.DIALOG_CLASS_NAME = 'DialogErrorComponent'; }
|
|
446
|
+
constructor(elementRef, dialogRef, data) {
|
|
447
|
+
super(elementRef, dialogRef);
|
|
448
|
+
this.elementRef = elementRef;
|
|
449
|
+
this.dialogRef = dialogRef;
|
|
450
|
+
this.data = data;
|
|
451
|
+
if (data.texts.length < 1) {
|
|
452
|
+
throw new Error('DialogErrorComponent expects 2 texts: The title and optionally a description');
|
|
562
453
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
*
|
|
569
|
-
* @return True if the locale is currently loaded on the class, false if not.
|
|
570
|
-
*/
|
|
571
|
-
isLocaleLoaded(locale) {
|
|
572
|
-
this._validateLocaleString(locale);
|
|
573
|
-
return this._locales.includes(locale);
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* Aux method to validate that a locale string is correctly formatted
|
|
577
|
-
*
|
|
578
|
-
* @param string $locale A locale string
|
|
579
|
-
*/
|
|
580
|
-
_validateLocaleString(locale) {
|
|
581
|
-
if (!/^[a-z]{2}_[A-Z]{2}$/.test(locale)) {
|
|
582
|
-
throw new Error('locale must be a valid xx_XX value');
|
|
454
|
+
if (data.options.length !== 1) {
|
|
455
|
+
throw new Error('DialogErrorComponent expects only one option');
|
|
456
|
+
}
|
|
457
|
+
if (data.texts.length > 1) {
|
|
458
|
+
data.texts[1] = data.texts[1].replace(/\n/g, "<br/>");
|
|
583
459
|
}
|
|
584
460
|
}
|
|
461
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DialogErrorComponent, deps: [{ token: i0.ElementRef }, { token: i1.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
462
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: DialogErrorComponent, isStandalone: true, selector: "tg-dialog-error", providers: [], usesInheritance: true, ngImport: i0, template: "<div class=\"titleAndIconContainer\">\r\n <h3>\r\n {{data.texts[0]}}\r\n </h3>\r\n \r\n <!-- error icon -->\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\">\r\n <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\r\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\" fill=\"#ff0000\" />\r\n </svg>\r\n \r\n</div>\r\n\r\n<div class=\"textContainer\">\r\n <p *ngIf=\"data.texts.length > 1\" [innerHTML]=\"data.texts[1]\">\r\n </p>\r\n</div>\r\n\r\n<button mat-raised-button color=\"warn\"\r\n (click)=\"closeDialog(0)\">\r\n \r\n {{data.options[0]}}\r\n \r\n</button>", styles: [":host{position:relative;min-height:300px}.titleAndIconContainer{display:flex;flex-direction:row;align-items:center;justify-content:space-between}svg{height:10vh;width:auto}.textContainer{margin-top:5px;max-height:70vh;overflow:auto}p{-webkit-user-select:text;user-select:text}h3{margin-top:0;margin-bottom:25px;width:82%}button{width:100%;margin-top:5px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }] }); }
|
|
463
|
+
}
|
|
464
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DialogErrorComponent, decorators: [{
|
|
465
|
+
type: Component,
|
|
466
|
+
args: [{ selector: 'tg-dialog-error', imports: [CommonModule, MatButtonModule], providers: [], template: "<div class=\"titleAndIconContainer\">\r\n <h3>\r\n {{data.texts[0]}}\r\n </h3>\r\n \r\n <!-- error icon -->\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\">\r\n <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\r\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\" fill=\"#ff0000\" />\r\n </svg>\r\n \r\n</div>\r\n\r\n<div class=\"textContainer\">\r\n <p *ngIf=\"data.texts.length > 1\" [innerHTML]=\"data.texts[1]\">\r\n </p>\r\n</div>\r\n\r\n<button mat-raised-button color=\"warn\"\r\n (click)=\"closeDialog(0)\">\r\n \r\n {{data.options[0]}}\r\n \r\n</button>", styles: [":host{position:relative;min-height:300px}.titleAndIconContainer{display:flex;flex-direction:row;align-items:center;justify-content:space-between}svg{height:10vh;width:auto}.textContainer{margin-top:5px;max-height:70vh;overflow:auto}p{-webkit-user-select:text;user-select:text}h3{margin-top:0;margin-bottom:25px;width:82%}button{width:100%;margin-top:5px}\n"] }]
|
|
467
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.MatDialogRef }, { type: undefined, decorators: [{
|
|
468
|
+
type: Inject,
|
|
469
|
+
args: [MAT_DIALOG_DATA]
|
|
470
|
+
}] }] });
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
474
|
+
*
|
|
475
|
+
* Website : -> http://www.turbogui.org
|
|
476
|
+
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
477
|
+
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
478
|
+
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
479
|
+
*/
|
|
480
|
+
/**
|
|
481
|
+
* Fade animations
|
|
482
|
+
*/
|
|
483
|
+
class FadeAnimationClass {
|
|
585
484
|
/**
|
|
586
|
-
*
|
|
587
|
-
*
|
|
588
|
-
* @param language A language to check. For example 'en'
|
|
485
|
+
* Get a custom trigger to create fade animations when components are added or removed from the application
|
|
589
486
|
*
|
|
590
|
-
* @
|
|
487
|
+
* @param triggerName The name for the trigger we want to create
|
|
488
|
+
* @param enter The time and easing that we want to use for the enter state
|
|
489
|
+
* @param leave The time and easing that we want to use for the leave state
|
|
591
490
|
*/
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
491
|
+
static getTrigger(triggerName, enter = '1s ease', leave = '300ms ease') {
|
|
492
|
+
return trigger(triggerName, [
|
|
493
|
+
transition('void => *', [style({ opacity: 0 }), animate(enter, style({ opacity: 1 }))]),
|
|
494
|
+
transition('* => void', [animate(leave, style({ opacity: 0 }))])
|
|
495
|
+
]);
|
|
595
496
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
497
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: FadeAnimationClass, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
498
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: FadeAnimationClass }); }
|
|
499
|
+
}
|
|
500
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: FadeAnimationClass, decorators: [{
|
|
501
|
+
type: Injectable
|
|
502
|
+
}] });
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
506
|
+
*
|
|
507
|
+
* Website : -> http://www.turbogui.org
|
|
508
|
+
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
509
|
+
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
510
|
+
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
511
|
+
*/
|
|
512
|
+
/**
|
|
513
|
+
* This component is used by turboGUI angular library to show the busy state to the user.
|
|
514
|
+
* It is used to block all the user input and progressively shows a busy cursor to notify that the application
|
|
515
|
+
* is waiting for something.
|
|
516
|
+
*
|
|
517
|
+
* We can (should) override this component with our own one to adapt its visual appearance to our application.
|
|
518
|
+
* We can then set dialogService.busyStateComponentClass to our component class at the application start to to
|
|
519
|
+
* override the default one.
|
|
520
|
+
*/
|
|
521
|
+
class BusyStateBaseComponent {
|
|
522
|
+
constructor() {
|
|
523
|
+
/**
|
|
524
|
+
* This is used to attach the fade animation directly to this component so it fades in and out when created and removed from the app
|
|
525
|
+
*/
|
|
526
|
+
this.busyStateBaseFade = true;
|
|
605
527
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
for (let i = 0; i < replacementsCount; i++) {
|
|
636
|
-
result = this._replace(result, this._replace(this._wildCardsFormat, 'N', i.toString()), replaceWildcards[i]);
|
|
637
|
-
}
|
|
638
|
-
this._keyValuesCache[cacheKey] = result;
|
|
639
|
-
return result;
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
// Check if an exception needs to be thrown if the specified key is not found on this bundle
|
|
643
|
-
if (this._missingKeyFormat.includes('$exception')) {
|
|
644
|
-
throw new Error(`Translation key <${key}> not found on <${bundlePath}>`);
|
|
645
|
-
}
|
|
646
|
-
this._keyValuesCache[cacheKey] = this._replace(this._missingKeyFormat, '$key', key);
|
|
647
|
-
}
|
|
648
|
-
return this._keyValuesCache[cacheKey];
|
|
528
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BusyStateBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
529
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: BusyStateBaseComponent, isStandalone: true, selector: "tg-busy-state-base", host: { properties: { "@busyStateBaseFade": "this.busyStateBaseFade" } }, providers: [], ngImport: i0, template: "<div class=\"darkBg\">\r\n\r\n</div>\r\n\r\n<svg width=\"38\" height=\"38\" viewBox=\"0 0 38 38\" xmlns=\"http://www.w3.org/2000/svg\" stroke=\"#fff\">\r\n <g fill=\"none\" fill-rule=\"evenodd\">\r\n <g transform=\"translate(1 1)\" stroke-width=\"1\">\r\n <circle stroke-opacity=\".2\" cx=\"18\" cy=\"18\" r=\"18\"/>\r\n <path d=\"M36 18c0-9.94-8.06-18-18-18\">\r\n <animateTransform\r\n attributeName=\"transform\"\r\n type=\"rotate\"\r\n from=\"0 18 18\"\r\n to=\"360 18 18\"\r\n dur=\"1s\"\r\n repeatCount=\"indefinite\"/>\r\n </path>\r\n </g>\r\n </g>\r\n</svg>\r\n", styles: [":host{position:fixed;inset:0;display:flex;justify-content:center;align-items:center;z-index:999999999}.darkBg{position:absolute;cursor:progress;width:100%;height:100%;display:flex;justify-content:center;align-items:center;background-color:#00000096;animation-name:bgfadein;animation-duration:30s;animation-timing-function:ease}svg{z-index:5000;width:40%;height:40%;max-width:130px;max-height:130px;animation-name:loadingfadein;animation-duration:3s;animation-timing-function:ease-in}@keyframes loadingfadein{0%{opacity:0}25%{opacity:0}to{opacity:1}}@keyframes bgfadein{0%{opacity:0}4%{opacity:0}14%{opacity:.2}35%{opacity:.5}to{opacity:1}}\n"], animations: [FadeAnimationClass.getTrigger('busyStateBaseFade', '1s ease', '400ms ease')] }); }
|
|
530
|
+
}
|
|
531
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BusyStateBaseComponent, decorators: [{
|
|
532
|
+
type: Component,
|
|
533
|
+
args: [{ selector: 'tg-busy-state-base', imports: [], providers: [], animations: [FadeAnimationClass.getTrigger('busyStateBaseFade', '1s ease', '400ms ease')], template: "<div class=\"darkBg\">\r\n\r\n</div>\r\n\r\n<svg width=\"38\" height=\"38\" viewBox=\"0 0 38 38\" xmlns=\"http://www.w3.org/2000/svg\" stroke=\"#fff\">\r\n <g fill=\"none\" fill-rule=\"evenodd\">\r\n <g transform=\"translate(1 1)\" stroke-width=\"1\">\r\n <circle stroke-opacity=\".2\" cx=\"18\" cy=\"18\" r=\"18\"/>\r\n <path d=\"M36 18c0-9.94-8.06-18-18-18\">\r\n <animateTransform\r\n attributeName=\"transform\"\r\n type=\"rotate\"\r\n from=\"0 18 18\"\r\n to=\"360 18 18\"\r\n dur=\"1s\"\r\n repeatCount=\"indefinite\"/>\r\n </path>\r\n </g>\r\n </g>\r\n</svg>\r\n", styles: [":host{position:fixed;inset:0;display:flex;justify-content:center;align-items:center;z-index:999999999}.darkBg{position:absolute;cursor:progress;width:100%;height:100%;display:flex;justify-content:center;align-items:center;background-color:#00000096;animation-name:bgfadein;animation-duration:30s;animation-timing-function:ease}svg{z-index:5000;width:40%;height:40%;max-width:130px;max-height:130px;animation-name:loadingfadein;animation-duration:3s;animation-timing-function:ease-in}@keyframes loadingfadein{0%{opacity:0}25%{opacity:0}to{opacity:1}}@keyframes bgfadein{0%{opacity:0}4%{opacity:0}14%{opacity:.2}35%{opacity:.5}to{opacity:1}}\n"] }]
|
|
534
|
+
}], propDecorators: { busyStateBaseFade: [{
|
|
535
|
+
type: HostBinding,
|
|
536
|
+
args: ['@busyStateBaseFade']
|
|
537
|
+
}] } });
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
541
|
+
*
|
|
542
|
+
* Website : -> http://www.turbogui.org
|
|
543
|
+
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
544
|
+
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
545
|
+
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
546
|
+
*/
|
|
547
|
+
/**
|
|
548
|
+
* A dialog component with a calendar that allows us to select a single date value
|
|
549
|
+
*/
|
|
550
|
+
class DialogDateSelectionComponent extends DialogBaseComponent {
|
|
551
|
+
static { this.DIALOG_CLASS_NAME = 'DialogDateSelectionComponent'; }
|
|
552
|
+
constructor(elementRef, dialogRef, data) {
|
|
553
|
+
super(elementRef, dialogRef);
|
|
554
|
+
this.elementRef = elementRef;
|
|
555
|
+
this.dialogRef = dialogRef;
|
|
556
|
+
this.data = data;
|
|
649
557
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
558
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DialogDateSelectionComponent, deps: [{ token: i0.ElementRef }, { token: i1.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
559
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: DialogDateSelectionComponent, isStandalone: true, selector: "tg-dialog-date-selection", providers: [], usesInheritance: true, ngImport: i0, template: "<h2>{{data.texts[0]}}</h2>\r\n\r\n<mat-calendar #calendar\r\n (selectedChange)=\"closeDialog(0, $event)\">\r\n</mat-calendar>", styles: [":host{position:relative;min-height:300px}h1{margin-top:0;margin-bottom:25px;width:82%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i2$1.MatCalendar, selector: "mat-calendar", inputs: ["headerComponent", "startAt", "startView", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "comparisonStart", "comparisonEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["selectedChange", "yearSelected", "monthSelected", "viewChanged", "_userSelection", "_userDragDrop"], exportAs: ["matCalendar"] }, { kind: "ngmodule", type: MatNativeDateModule }] }); }
|
|
560
|
+
}
|
|
561
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DialogDateSelectionComponent, decorators: [{
|
|
562
|
+
type: Component,
|
|
563
|
+
args: [{ selector: 'tg-dialog-date-selection', imports: [CommonModule, MatDatepickerModule, MatNativeDateModule], providers: [], template: "<h2>{{data.texts[0]}}</h2>\r\n\r\n<mat-calendar #calendar\r\n (selectedChange)=\"closeDialog(0, $event)\">\r\n</mat-calendar>", styles: [":host{position:relative;min-height:300px}h1{margin-top:0;margin-bottom:25px;width:82%}\n"] }]
|
|
564
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.MatDialogRef }, { type: undefined, decorators: [{
|
|
565
|
+
type: Inject,
|
|
566
|
+
args: [MAT_DIALOG_DATA]
|
|
567
|
+
}] }] });
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
571
|
+
*
|
|
572
|
+
* Website : -> http://www.turbogui.org
|
|
573
|
+
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
574
|
+
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
575
|
+
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
576
|
+
*/
|
|
577
|
+
/**
|
|
578
|
+
* Manages the application modal and non modal floating elements
|
|
579
|
+
*/
|
|
580
|
+
class DialogService extends SingletoneStrictClass {
|
|
581
|
+
constructor(rendererFactory, matSnackBar, matDialog, injector, applicationRef, componentFactoryResolver) {
|
|
582
|
+
super(DialogService);
|
|
583
|
+
this.matSnackBar = matSnackBar;
|
|
584
|
+
this.matDialog = matDialog;
|
|
585
|
+
this.injector = injector;
|
|
586
|
+
this.applicationRef = applicationRef;
|
|
587
|
+
this.componentFactoryResolver = componentFactoryResolver;
|
|
588
|
+
/**
|
|
589
|
+
* Used to modify the busy state component that is shown by default by the addModalBusyState() method.
|
|
590
|
+
*
|
|
591
|
+
* @see this.addModalBusyState()
|
|
592
|
+
*/
|
|
593
|
+
this.customBusyStateComponentClass = BusyStateBaseComponent;
|
|
594
|
+
/**
|
|
595
|
+
* Check public getter for docs
|
|
596
|
+
*/
|
|
597
|
+
this._isEnabled = true;
|
|
598
|
+
/**
|
|
599
|
+
* Tells if the main application is currently showing a busy state that blocks all user interaction
|
|
600
|
+
*/
|
|
601
|
+
this._isShowingBusyState = false;
|
|
602
|
+
/**
|
|
603
|
+
* A reference to the modal busy state component that is initialized only the first time it is called.
|
|
604
|
+
*
|
|
605
|
+
* (To append the busy state dynamically, we use the Portal concept from the angular material library)
|
|
606
|
+
*/
|
|
607
|
+
this._componentPortal = null;
|
|
608
|
+
/**
|
|
609
|
+
* A reference to the modal busy state container where the component will be added
|
|
610
|
+
*/
|
|
611
|
+
this._modalBusyStateHost = null;
|
|
612
|
+
/**
|
|
613
|
+
* Tells if the manager is currently showing a snackbar element or not
|
|
614
|
+
*/
|
|
615
|
+
this._isShowingSnackBar = false;
|
|
616
|
+
/**
|
|
617
|
+
* Contains a list of the dialogs that are currently visible to the user.
|
|
618
|
+
* Each item in this list is a hash that is computed when dialog is created to uniquely identify it.
|
|
619
|
+
*
|
|
620
|
+
* Empty list means no dialogs are currently visible
|
|
621
|
+
*/
|
|
622
|
+
this._activeDialogs = [];
|
|
623
|
+
/**
|
|
624
|
+
* Contains a list with all the MatDialog instances that are currently visible to the user.
|
|
625
|
+
* The list uses the same order as the list of _activeDialogs hash values
|
|
626
|
+
*/
|
|
627
|
+
this._activeDialogInstances = [];
|
|
628
|
+
/**
|
|
629
|
+
* Method that is used to delete the window beforeunload event listener once not used anymore
|
|
630
|
+
*/
|
|
631
|
+
this._windowBeforeUnloadUnListen = null;
|
|
632
|
+
/**
|
|
633
|
+
* Method that is used to delete the document keydown event listener once not used anymore
|
|
634
|
+
*/
|
|
635
|
+
this._documentKeydownUnlisten = null;
|
|
636
|
+
/**
|
|
637
|
+
* Method that is used to delete the document mousedown event listener once not used anymore
|
|
638
|
+
*/
|
|
639
|
+
this._documentMousedownUnlisten = null;
|
|
640
|
+
/**
|
|
641
|
+
* Method that is used to delete the document pointerdown event listener once not used anymore
|
|
642
|
+
*/
|
|
643
|
+
this._documentPointerdownUnlisten = null;
|
|
644
|
+
this._renderer = rendererFactory.createRenderer(null, null);
|
|
661
645
|
}
|
|
662
646
|
/**
|
|
663
|
-
*
|
|
664
|
-
*
|
|
665
|
-
* @see t()
|
|
647
|
+
* Tells if this dialog service is enabled or not. If dialog service is disabled, none of it's features will work
|
|
648
|
+
* This is used to block all dialog features normally when shutting down the application
|
|
666
649
|
*
|
|
667
|
-
*
|
|
650
|
+
* Use with caution. When this is set to false, dialog service stops working.
|
|
668
651
|
*/
|
|
669
|
-
|
|
670
|
-
|
|
652
|
+
set isEnabled(v) {
|
|
653
|
+
if (v === this._isEnabled) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
this._isEnabled = v;
|
|
671
657
|
}
|
|
672
658
|
/**
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
*
|
|
659
|
+
* Enables a warning that will be shown to the user when he/she tries to close the application.
|
|
660
|
+
* This warning will prompt the user to continue with the exit process or cancel it.
|
|
661
|
+
* The specific texts of this message are a generic ones and cannot be changed.
|
|
676
662
|
*
|
|
677
|
-
*
|
|
663
|
+
* IMPORTANT: This method must be called after the main application has been initialized in order to work,
|
|
664
|
+
* otherwise it will do nothing.
|
|
678
665
|
*/
|
|
679
|
-
|
|
680
|
-
|
|
666
|
+
addCloseApplicationWarning() {
|
|
667
|
+
if (this._windowBeforeUnloadUnListen === null) {
|
|
668
|
+
this._windowBeforeUnloadUnListen = this._renderer.listen('window', 'beforeunload', (event) => event.returnValue = true);
|
|
669
|
+
}
|
|
681
670
|
}
|
|
682
671
|
/**
|
|
683
|
-
*
|
|
684
|
-
* and all the rest as lower case
|
|
685
|
-
*
|
|
686
|
-
* @see t()
|
|
687
|
-
*
|
|
688
|
-
* @returns The localized and case formatted text
|
|
672
|
+
* Remove the close application warning message if previously assigned
|
|
689
673
|
*/
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
674
|
+
removeCloseApplicationWarning() {
|
|
675
|
+
if (this._windowBeforeUnloadUnListen !== null) {
|
|
676
|
+
this._windowBeforeUnloadUnListen();
|
|
677
|
+
this._windowBeforeUnloadUnListen = null;
|
|
678
|
+
}
|
|
693
679
|
}
|
|
694
680
|
/**
|
|
695
|
-
*
|
|
696
|
-
*
|
|
681
|
+
* Change the application visual appearance so the user perceives that the application is
|
|
682
|
+
* currently busy. While modal busy state is enabled, no user input is possible neither via
|
|
683
|
+
* keyboard, mouse or touch. Use this state when performing server requests or operations that
|
|
684
|
+
* must block the user interaction with the application. To allow user interaction again, you must
|
|
685
|
+
* call removeModalBusyState()
|
|
697
686
|
*
|
|
698
|
-
*
|
|
699
|
-
*
|
|
700
|
-
*
|
|
687
|
+
* Notice: We can modify the busy state visual component that is shown by this method. To do it, we must
|
|
688
|
+
* set this.customBusyStateComponentClass property with our own custom busy state component class. (We can do it at
|
|
689
|
+
* our main application component constructor for example). Our custom component must extend the
|
|
690
|
+
* BusyStateBaseComponent one to add its own visual appearance.
|
|
701
691
|
*
|
|
702
|
-
* @
|
|
703
|
-
* the localization manager will try to locate the en_US value for the HELLO tag on the greetings bundle for the library lib1.
|
|
704
|
-
* If the tag is not found for the specified locale and bundle, the same search will be performed for the es_ES locale, and so, till a
|
|
705
|
-
* value is found or no more locales are defined.
|
|
692
|
+
* @see this.customBusyStateComponentClass
|
|
706
693
|
*/
|
|
707
|
-
|
|
708
|
-
|
|
694
|
+
addModalBusyState() {
|
|
695
|
+
if (!this._isEnabled || this._isShowingBusyState) {
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
this._disableUserInteraction();
|
|
699
|
+
// Dynamically create the busy state component reference if this is the first time
|
|
700
|
+
if (this._componentPortal === null) {
|
|
701
|
+
this._componentPortal = new ComponentPortal(this.customBusyStateComponentClass);
|
|
702
|
+
// Create a PortalHost with document.body as its anchor element
|
|
703
|
+
this._modalBusyStateHost = new DomPortalOutlet(document.body, this.componentFactoryResolver, this.applicationRef, this.injector);
|
|
704
|
+
}
|
|
705
|
+
this._modalBusyStateHost.attach(this._componentPortal);
|
|
706
|
+
this._isShowingBusyState = true;
|
|
709
707
|
}
|
|
710
708
|
/**
|
|
711
|
-
*
|
|
712
|
-
* Each string is formatted as a 2 digit language code, like: en, fr
|
|
713
|
-
*
|
|
714
|
-
* This list is the same as the locales() one, but containing only the language part of each locale (the first two digits)
|
|
715
|
-
*
|
|
716
|
-
* @see getLocales()
|
|
709
|
+
* Tells if the application is currently locked in a modal busy state (caused by an addModalBusyState() call)
|
|
717
710
|
*/
|
|
718
|
-
|
|
719
|
-
return this.
|
|
711
|
+
get isShowingBusyState() {
|
|
712
|
+
return this._isShowingBusyState;
|
|
720
713
|
}
|
|
721
714
|
/**
|
|
722
|
-
*
|
|
723
|
-
*
|
|
724
|
-
* @returns The locale that is defined as the primary one. For example: en_US, es_ES, ..
|
|
715
|
+
* Cancel the application busy state and restore it back to normal so user interaction is allowed again
|
|
725
716
|
*/
|
|
726
|
-
|
|
727
|
-
this.
|
|
728
|
-
|
|
717
|
+
removeModalBusyState() {
|
|
718
|
+
if (!this._isEnabled || !this._isShowingBusyState) {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
if (this._componentPortal !== null) {
|
|
722
|
+
this._modalBusyStateHost.detach();
|
|
723
|
+
}
|
|
724
|
+
this._enableUserInteraction();
|
|
725
|
+
this._isShowingBusyState = false;
|
|
729
726
|
}
|
|
730
727
|
/**
|
|
731
|
-
*
|
|
732
|
-
*
|
|
733
|
-
* @returns The 2 digit language code that is defined as the primary one. For example: en, es, ..
|
|
728
|
+
* TODO - adapt from TS version
|
|
734
729
|
*/
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
return this._languages[0];
|
|
730
|
+
addToolTip() {
|
|
731
|
+
// TODO - adapt from TS version
|
|
738
732
|
}
|
|
739
733
|
/**
|
|
740
|
-
*
|
|
734
|
+
* Show a non modal snackbar notification to the user (Only one snack-bar can ever be opened at the same time).
|
|
741
735
|
*
|
|
742
|
-
*
|
|
736
|
+
* Snackbars inform users of a process that an app has performed or will perform. They appear temporarily, towards the bottom or top of the screen.
|
|
737
|
+
* They shouldn’t interrupt the user experience, and they don’t require user input to disappear.
|
|
743
738
|
*
|
|
744
|
-
* @param
|
|
745
|
-
*
|
|
739
|
+
* @param config A MatSnackBarConfig instance with the customizations we want for this snackbar
|
|
740
|
+
* @param message The message to show on the snackbar
|
|
741
|
+
* @param action If not empty, the text to place on the snackbar confirmation button
|
|
742
|
+
* @param actionCallback A method to execute once the user clicks into the action button.
|
|
746
743
|
*
|
|
747
|
-
* @
|
|
744
|
+
* @return A promise that will be resolved once the snackbar is closed.
|
|
748
745
|
*/
|
|
749
|
-
|
|
750
|
-
this.
|
|
751
|
-
|
|
752
|
-
throw new Error(locale + ' not loaded');
|
|
746
|
+
addSnackBar(config, message, action = '') {
|
|
747
|
+
if (!this._isEnabled) {
|
|
748
|
+
return Promise.reject(new Error('Dialog service is disabled'));
|
|
753
749
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
if (l !== locale) {
|
|
757
|
-
result.push(l);
|
|
758
|
-
}
|
|
750
|
+
if (this._isShowingSnackBar) {
|
|
751
|
+
throw new Error('Trying to show a snackbar while another one is still visible');
|
|
759
752
|
}
|
|
760
|
-
this.
|
|
761
|
-
|
|
762
|
-
|
|
753
|
+
this._isShowingSnackBar = true;
|
|
754
|
+
return new Promise((resolve) => {
|
|
755
|
+
const snackBarRef = this.matSnackBar.open(message, action === '' ? undefined : action, config);
|
|
756
|
+
// Handle action button click
|
|
757
|
+
snackBarRef.onAction().subscribe(() => {
|
|
758
|
+
this._isShowingSnackBar = false;
|
|
759
|
+
resolve(true);
|
|
760
|
+
});
|
|
761
|
+
// Handle dismiss
|
|
762
|
+
snackBarRef.afterDismissed().subscribe(() => {
|
|
763
|
+
this._isShowingSnackBar = false;
|
|
764
|
+
resolve(false);
|
|
765
|
+
});
|
|
766
|
+
});
|
|
763
767
|
}
|
|
764
768
|
/**
|
|
765
|
-
*
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
+
* Tells if the application is currently showing a snackbar or not
|
|
770
|
+
*/
|
|
771
|
+
get isShowingSnackBar() {
|
|
772
|
+
return this._isShowingSnackBar;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Force the removal of the snack bar dialog if it exists.
|
|
769
776
|
*
|
|
770
|
-
*
|
|
777
|
+
* If no snackbar is currently visible, this method will do nothing
|
|
778
|
+
*/
|
|
779
|
+
removeSnackBar() {
|
|
780
|
+
if (!this._isEnabled || !this._isShowingSnackBar) {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
this.matSnackBar.dismiss();
|
|
784
|
+
this._isShowingSnackBar = false;
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Show a dialog with one or more options that can be used to close it. We can use any of the predefined dialog types that are bundled with
|
|
788
|
+
* this library or extend DialogBaseComponent to create our own custom ones.
|
|
771
789
|
*
|
|
772
|
-
* @param
|
|
773
|
-
*
|
|
774
|
-
*
|
|
790
|
+
* @param dialogComponentClass A class for a component that extends DialogBaseComponent, which will be the dialog that is shown to the user.
|
|
791
|
+
* @param properties An object containing the different visual and textual options that this dialog allows:
|
|
792
|
+
* - id: The html unique identifier that the dialog will have once created. If not specified, no id will be explicitly set
|
|
793
|
+
* - width: 50% by default. Specify the css value for the default dialog width. As the dialog is responsive, the value will be automatically
|
|
794
|
+
* reduced if the available screen is not enough, and will reach the desired value otherwise. We can set any css unit like pixels,
|
|
795
|
+
* %, vh, vw, or any other. For example: '400px', '50%', etc.
|
|
796
|
+
* - maxWidth: Defines the maximum width that the dialog will have regarding the viewport. We can specify it in % or vw, just like is done in
|
|
797
|
+
* css. By default it is defined as 96vw, which will fit 96% of the viewport on small devices
|
|
798
|
+
* - height: TODO docs
|
|
799
|
+
* - maxHeight: TODO docs
|
|
800
|
+
* - modal: True (default) if selecting an option is mandatory to close the dialog, false if the dialog can be closed
|
|
801
|
+
* by the user clicking outside it
|
|
802
|
+
* - texts: A list with strings containing the dialog texts, sorted by importance. When dialog has a title, this should
|
|
803
|
+
* be placed first, subtitle second and so (Each dialog may accept a different custom number of texts).
|
|
804
|
+
* - options: A list of strings that will be used as button captions for each one of the accepted dialog options
|
|
805
|
+
* - data: An object that we can use to pass any extra data that we want to the dialog
|
|
806
|
+
* - viewContainerRef: This is important if we want to propagate providers from a parent component to this dialog. We must specify
|
|
807
|
+
* this reference to make sure the same services injected on the parent are available too at the child dialog
|
|
775
808
|
*
|
|
776
|
-
* @
|
|
809
|
+
* @return A promise that will be resolved once the dialog is closed.
|
|
810
|
+
* The promise will receive a selection object with two properties which will correspond to the index and value from the options
|
|
811
|
+
* array that's selected by the user. If no option selected, index will be -1 and value null
|
|
777
812
|
*/
|
|
778
|
-
|
|
779
|
-
if (!
|
|
780
|
-
(new
|
|
781
|
-
locales.length === 0) {
|
|
782
|
-
throw new Error('locales must be non empty string array with no duplicate elements');
|
|
783
|
-
}
|
|
784
|
-
for (let i = locales.length - 1; i >= 0; i--) {
|
|
785
|
-
this.setPrimaryLocale(locales[i]);
|
|
813
|
+
addDialog(dialogComponentClass, properties) {
|
|
814
|
+
if (!this._isEnabled) {
|
|
815
|
+
return Promise.reject(new Error('Dialog service is disabled'));
|
|
786
816
|
}
|
|
817
|
+
return new Promise((resolve) => {
|
|
818
|
+
// Set the default values for non specified properties
|
|
819
|
+
properties.modal = properties.modal ?? true;
|
|
820
|
+
properties.texts = properties.texts ?? [];
|
|
821
|
+
properties.options = properties.options ?? [];
|
|
822
|
+
properties.data = properties.data ?? {};
|
|
823
|
+
// Generate a string to uniquely identify this dialog on the list of active dialogs
|
|
824
|
+
// A dialog is considered as unique if the dialog id and texts are exactly the same. We do not take options into consideration
|
|
825
|
+
// as there may be dialogs with a big amount of options available.
|
|
826
|
+
let className = dialogComponentClass.DIALOG_CLASS_NAME;
|
|
827
|
+
if (className === '') {
|
|
828
|
+
throw new Error(`The static property DIALOG_CLASS_NAME is not defined or is empty for this dialog component (${dialogComponentClass})`);
|
|
829
|
+
}
|
|
830
|
+
const dialogHash = className + properties.texts.join('');
|
|
831
|
+
// identical dialogs won't be allowed at the same time
|
|
832
|
+
if (this._activeDialogs.includes(dialogHash)) {
|
|
833
|
+
return resolve({ index: -1 });
|
|
834
|
+
}
|
|
835
|
+
const dialogRef = this.matDialog.open(dialogComponentClass, {
|
|
836
|
+
width: properties.width ?? "50%",
|
|
837
|
+
maxWidth: properties.maxWidth ?? "96vw",
|
|
838
|
+
disableClose: properties.modal,
|
|
839
|
+
autoFocus: false,
|
|
840
|
+
closeOnNavigation: !properties.modal,
|
|
841
|
+
viewContainerRef: properties.viewContainerRef,
|
|
842
|
+
data: { texts: properties.texts, options: properties.options, data: properties.data }
|
|
843
|
+
});
|
|
844
|
+
// Assign the dialog ID only if specifically set on properties
|
|
845
|
+
if (properties.id && properties.id !== undefined) {
|
|
846
|
+
dialogRef.id = properties.id;
|
|
847
|
+
}
|
|
848
|
+
this._activeDialogs.push(dialogHash);
|
|
849
|
+
this._activeDialogInstances.push(dialogRef);
|
|
850
|
+
dialogRef.beforeClosed().subscribe((selection) => {
|
|
851
|
+
this._activeDialogs = ArrayUtils.removeElement(this._activeDialogs, dialogHash);
|
|
852
|
+
this._activeDialogInstances = ArrayUtils.removeElement(this._activeDialogInstances, dialogRef);
|
|
853
|
+
if (!properties.modal && selection === undefined) {
|
|
854
|
+
selection = { index: -1 };
|
|
855
|
+
}
|
|
856
|
+
else if (!NumericUtils.isInteger(selection.index)) {
|
|
857
|
+
throw new Error(`closeDialog() expects index to be an integer`);
|
|
858
|
+
}
|
|
859
|
+
if (selection.index >= 0 && selection.value === null) {
|
|
860
|
+
selection.value = properties.options[selection.index];
|
|
861
|
+
}
|
|
862
|
+
resolve(selection);
|
|
863
|
+
});
|
|
864
|
+
});
|
|
787
865
|
}
|
|
788
866
|
/**
|
|
789
|
-
*
|
|
790
|
-
*
|
|
791
|
-
* This will be the first language to use when trying to get a translation.
|
|
867
|
+
* Show a dialog with a calendar to let the user pick a date.
|
|
792
868
|
*
|
|
793
|
-
* @param
|
|
794
|
-
*
|
|
795
|
-
*
|
|
869
|
+
* @param properties An object containing the different visual and textual options that this dialog allows:
|
|
870
|
+
* - id: The html unique identifier that the dialog will have once created. If not specified, no id will be explicitly set
|
|
871
|
+
* - width: Specify the css value for the default dialog width. As the dialog is responsive, the value will be automatically
|
|
872
|
+
* reduced if the available screen is not enough, and will reach the desired value otherwise. We can set any css unit like pixels,
|
|
873
|
+
* %, vh, vw, or any other. For example: '400px', '50%', etc.
|
|
874
|
+
* - maxWidth: Defines the maximum width that the dialog will have regarding the viewport. We can specify it in % or vw, just like is done in
|
|
875
|
+
* css. By default it is defined as 96vw, which will fit 96% of the viewport on small devices
|
|
876
|
+
* - height: TODO docs
|
|
877
|
+
* - maxHeight: TODO docs
|
|
878
|
+
* - modal: True (default) if selecting an option is mandatory to close the dialog, false if the dialog can be closed
|
|
879
|
+
* by the user clicking outside it
|
|
880
|
+
* - title: An optional dialog title
|
|
881
|
+
* - viewContainerRef: This is important to propagate providers from a parent component to this dialog. We must specify
|
|
882
|
+
* this reference to make sure the same services injected on the parent are available too at the child dialog
|
|
796
883
|
*
|
|
797
|
-
* @returns
|
|
884
|
+
* @returns A Promise that resolves to a Date() object selected by the user or null if no selection was made
|
|
798
885
|
*/
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
this.setPrimaryLocale(locale);
|
|
803
|
-
return;
|
|
804
|
-
}
|
|
886
|
+
async addDateSelectionDialog(properties) {
|
|
887
|
+
if (!this._isEnabled) {
|
|
888
|
+
return null;
|
|
805
889
|
}
|
|
806
|
-
|
|
890
|
+
const selection = await this.addDialog(DialogDateSelectionComponent, {
|
|
891
|
+
id: properties.id ?? undefined,
|
|
892
|
+
width: properties.width ?? "50%",
|
|
893
|
+
maxWidth: properties.maxWidth ?? "96vw",
|
|
894
|
+
height: properties.height ?? "50%",
|
|
895
|
+
maxHeight: properties.maxHeight ?? "92vw",
|
|
896
|
+
modal: properties.modal ?? false,
|
|
897
|
+
texts: [properties.title ?? ''],
|
|
898
|
+
viewContainerRef: properties.viewContainerRef
|
|
899
|
+
});
|
|
900
|
+
return selection.index === -1 ? null : selection.value;
|
|
807
901
|
}
|
|
808
902
|
/**
|
|
809
|
-
*
|
|
810
|
-
* Works the same as setPrimaryLocales() but with a list of the 2 digit language codes that match the respective locales.
|
|
811
|
-
*
|
|
812
|
-
* @see setPrimaryLocale()
|
|
813
|
-
* @see setPrimaryLanguage()
|
|
814
|
-
*
|
|
815
|
-
* @param languages A list of 2 digit language codes to be moved to the beginning of the translation priority. If any of the
|
|
816
|
-
* specified languages does not match with a locale that is currently loaded, an exception will happen.
|
|
903
|
+
* Force the removal of all the dialogs that are currently visible.
|
|
817
904
|
*
|
|
818
|
-
*
|
|
905
|
+
* If no dialogs are currently visible, this method will do nothing
|
|
819
906
|
*/
|
|
820
|
-
|
|
821
|
-
if (!
|
|
822
|
-
|
|
823
|
-
languages.length === 0) {
|
|
824
|
-
throw new Error('languages must be non empty string array with no duplicate elements');
|
|
907
|
+
removeAllDialogs() {
|
|
908
|
+
if (!this._isEnabled) {
|
|
909
|
+
return;
|
|
825
910
|
}
|
|
826
|
-
for (
|
|
827
|
-
|
|
911
|
+
for (const dialogRef of this._activeDialogInstances) {
|
|
912
|
+
dialogRef.close({ index: -1 });
|
|
828
913
|
}
|
|
914
|
+
this._activeDialogs = [];
|
|
915
|
+
this._activeDialogInstances = [];
|
|
829
916
|
}
|
|
830
917
|
/**
|
|
831
|
-
*
|
|
832
|
-
* but with a different order to change the translation priority.
|
|
833
|
-
*
|
|
834
|
-
* @param locales A list with the new locales translation priority
|
|
835
|
-
*
|
|
836
|
-
* @returns void
|
|
918
|
+
* TODO - translate from TS version
|
|
837
919
|
*/
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
}
|
|
842
|
-
this._validateInitialized();
|
|
843
|
-
for (let locale of locales) {
|
|
844
|
-
if (!this.isLocaleLoaded(locale)) {
|
|
845
|
-
throw new Error(locale + ' not loaded');
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
this._locales = locales;
|
|
849
|
-
this._languages = this._locales.map((l) => l.substring(0, 2));
|
|
850
|
-
this._cacheHashBaseString = this._wildCardsFormat + this._missingKeyFormat + this._locales[0];
|
|
851
|
-
}
|
|
920
|
+
// addSideNav(){
|
|
921
|
+
//
|
|
922
|
+
// }
|
|
852
923
|
/**
|
|
853
|
-
*
|
|
854
|
-
* It is exactly the same as the one on the library, but we implement it here to avoid having a dependency with TurboCommons
|
|
924
|
+
* TODO - translate from TS version
|
|
855
925
|
*/
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
}
|
|
926
|
+
// get isShowingSideNav(){
|
|
927
|
+
//
|
|
928
|
+
// }
|
|
860
929
|
/**
|
|
861
|
-
*
|
|
862
|
-
* It is exactly the same as the one on the library, but we implement it here to avoid having a dependency with TurboCommons
|
|
930
|
+
* TODO - translate from TS version
|
|
863
931
|
*/
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
if (!isString) {
|
|
868
|
-
// Empty or null value is considered empty
|
|
869
|
-
if (string == null || string == '') {
|
|
870
|
-
return true;
|
|
871
|
-
}
|
|
872
|
-
throw new Error("value is not a string");
|
|
873
|
-
}
|
|
874
|
-
return string.replace(/[ \n\r\t]/g, '') === '';
|
|
875
|
-
}
|
|
932
|
+
// removeSideNav(){
|
|
933
|
+
//
|
|
934
|
+
// }
|
|
876
935
|
/**
|
|
877
|
-
*
|
|
878
|
-
* It is exactly the same as the one on the library, but we implement it here to avoid having a dependency with TurboCommons
|
|
936
|
+
* Block all the user interactions with the application (keyboard, touch, mouse, ...)
|
|
879
937
|
*/
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
throw new Error(valueName + ' ' + errorMessage);
|
|
938
|
+
_disableUserInteraction() {
|
|
939
|
+
if (this._documentKeydownUnlisten === null) {
|
|
940
|
+
this._documentKeydownUnlisten = this._renderer.listen('document', 'keydown', (event) => event.preventDefault());
|
|
884
941
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
type: Injectable,
|
|
891
|
-
args: [{
|
|
892
|
-
providedIn: 'root',
|
|
893
|
-
}]
|
|
894
|
-
}], ctorParameters: () => [] });
|
|
895
|
-
|
|
896
|
-
/**
|
|
897
|
-
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
898
|
-
*
|
|
899
|
-
* Website : -> http://www.turbogui.org
|
|
900
|
-
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
901
|
-
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
902
|
-
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
903
|
-
*/
|
|
904
|
-
/**
|
|
905
|
-
* This is the base class for all the dialog components that can be loaded by the dialog service class
|
|
906
|
-
*/
|
|
907
|
-
class DialogBaseComponent {
|
|
908
|
-
/*
|
|
909
|
-
* The name of the superclass must be set into this constant as it is required by the dialog service to identify dialogs as different.
|
|
910
|
-
*
|
|
911
|
-
* When you extend the dialog base class, simply declare this static constant with the exact same name as your class and you're done.
|
|
912
|
-
* If this value is not set on the extended dialog component, a runtime exception will happen when trying to show the dialog.
|
|
913
|
-
*
|
|
914
|
-
* The root cause of this requirement is that when apps are compiled for production, class names are minified and this causes problems
|
|
915
|
-
* when creating a dialog hash to uniquely identify a dialog instance. Therefore, a hardcoded class name is necesary.
|
|
916
|
-
*/
|
|
917
|
-
static { this.DIALOG_CLASS_NAME = ''; }
|
|
918
|
-
constructor(elementRef, dialogRef) {
|
|
919
|
-
this.elementRef = elementRef;
|
|
920
|
-
this.dialogRef = dialogRef;
|
|
921
|
-
}
|
|
922
|
-
ngAfterViewInit() {
|
|
923
|
-
// Assign the component HTML identifier if it is specifically assigned to the dialogref instance
|
|
924
|
-
if (this.dialogRef.id !== undefined && this.dialogRef.id !== '') {
|
|
925
|
-
this.elementRef.nativeElement.id = this.dialogRef.id;
|
|
942
|
+
if (this._documentMousedownUnlisten === null) {
|
|
943
|
+
this._documentMousedownUnlisten = this._renderer.listen('document', 'mousedown', (event) => event.preventDefault());
|
|
944
|
+
}
|
|
945
|
+
if (this._documentPointerdownUnlisten === null) {
|
|
946
|
+
this._documentPointerdownUnlisten = this._renderer.listen('document', 'pointerdown', (event) => event.preventDefault());
|
|
926
947
|
}
|
|
927
948
|
}
|
|
928
949
|
/**
|
|
929
|
-
*
|
|
930
|
-
* It will perform the close of that dialog and also send an object to the addDialog() callback with the index and value
|
|
931
|
-
* that the user may have selected.
|
|
932
|
-
*
|
|
933
|
-
* @param index The numeric index of the user option selection. It will be specific for each dialog and it's different available options
|
|
934
|
-
* @param value Any value that may be provided to the callback regarding the user selected option.
|
|
935
|
-
*
|
|
936
|
-
* @return void
|
|
950
|
+
* Restore the user interactions that were previously disabled with _disableUserInteraction method
|
|
937
951
|
*/
|
|
938
|
-
|
|
939
|
-
this.
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: DialogBaseComponent, isStandalone: false, selector: "ng-component", ngImport: i0, template: '', isInline: true }); }
|
|
943
|
-
}
|
|
944
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DialogBaseComponent, decorators: [{
|
|
945
|
-
type: Component,
|
|
946
|
-
args: [{
|
|
947
|
-
template: '',
|
|
948
|
-
standalone: false
|
|
949
|
-
}]
|
|
950
|
-
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.MatDialogRef }] });
|
|
951
|
-
|
|
952
|
-
/**
|
|
953
|
-
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
954
|
-
*
|
|
955
|
-
* Website : -> http://www.turbogui.org
|
|
956
|
-
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
957
|
-
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
958
|
-
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
959
|
-
*/
|
|
960
|
-
/**
|
|
961
|
-
* A dialog component with a single option button, to be used for error notifications
|
|
962
|
-
*/
|
|
963
|
-
class DialogErrorComponent extends DialogBaseComponent {
|
|
964
|
-
static { this.DIALOG_CLASS_NAME = 'DialogErrorComponent'; }
|
|
965
|
-
constructor(elementRef, dialogRef, data) {
|
|
966
|
-
super(elementRef, dialogRef);
|
|
967
|
-
this.elementRef = elementRef;
|
|
968
|
-
this.dialogRef = dialogRef;
|
|
969
|
-
this.data = data;
|
|
970
|
-
if (data.texts.length < 1) {
|
|
971
|
-
throw new Error('DialogErrorComponent expects 2 texts: The title and optionally a description');
|
|
952
|
+
_enableUserInteraction() {
|
|
953
|
+
if (this._documentKeydownUnlisten !== null) {
|
|
954
|
+
this._documentKeydownUnlisten();
|
|
955
|
+
this._documentKeydownUnlisten = null;
|
|
972
956
|
}
|
|
973
|
-
if (
|
|
974
|
-
|
|
957
|
+
if (this._documentMousedownUnlisten !== null) {
|
|
958
|
+
this._documentMousedownUnlisten();
|
|
959
|
+
this._documentMousedownUnlisten = null;
|
|
975
960
|
}
|
|
976
|
-
if (
|
|
977
|
-
|
|
961
|
+
if (this._documentPointerdownUnlisten !== null) {
|
|
962
|
+
this._documentPointerdownUnlisten();
|
|
963
|
+
this._documentMousedownUnlisten = null;
|
|
978
964
|
}
|
|
979
965
|
}
|
|
980
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type:
|
|
981
|
-
static { this.ɵ
|
|
966
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DialogService, deps: [{ token: i0.RendererFactory2 }, { token: i1$1.MatSnackBar }, { token: i1.MatDialog }, { token: i0.Injector }, { token: i0.ApplicationRef }, { token: i0.ComponentFactoryResolver }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
967
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DialogService, providedIn: 'root' }); }
|
|
982
968
|
}
|
|
983
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type:
|
|
984
|
-
type:
|
|
985
|
-
args: [{
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
}] }] });
|
|
969
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DialogService, decorators: [{
|
|
970
|
+
type: Injectable,
|
|
971
|
+
args: [{
|
|
972
|
+
providedIn: 'root',
|
|
973
|
+
}]
|
|
974
|
+
}], ctorParameters: () => [{ type: i0.RendererFactory2 }, { type: i1$1.MatSnackBar }, { type: i1.MatDialog }, { type: i0.Injector }, { type: i0.ApplicationRef }, { type: i0.ComponentFactoryResolver }] });
|
|
990
975
|
|
|
991
976
|
/**
|
|
992
977
|
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
@@ -997,28 +982,62 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImpor
|
|
|
997
982
|
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
998
983
|
*/
|
|
999
984
|
/**
|
|
1000
|
-
*
|
|
985
|
+
* Manages application http communications
|
|
1001
986
|
*/
|
|
1002
|
-
class
|
|
987
|
+
class HTTPService extends HTTPManager {
|
|
988
|
+
constructor(dialogService) {
|
|
989
|
+
super(true);
|
|
990
|
+
this.dialogService = dialogService;
|
|
991
|
+
}
|
|
1003
992
|
/**
|
|
1004
|
-
*
|
|
993
|
+
* The same method as HTTPManager.execute but with the ability to enable several options which are specific to this service:
|
|
1005
994
|
*
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
*
|
|
995
|
+
* - options:
|
|
996
|
+
* busyState: Set it to false to prevent the default behaviour of locking the UI while the request is running
|
|
997
|
+
* handleErrors: Set it to false to prevent the default behaviour of showing a detailed error dialog when a request fails
|
|
998
|
+
*
|
|
999
|
+
* @see HTTPManager.execute()
|
|
1009
1000
|
*/
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1001
|
+
execute(requests, finishedCallback = null, progressCallback = null, options = {}) {
|
|
1002
|
+
// Set the default values for non specified properties
|
|
1003
|
+
options.busyState = options.busyState ?? true;
|
|
1004
|
+
options.handleErrors = options.handleErrors ?? true;
|
|
1005
|
+
if (options.busyState) {
|
|
1006
|
+
this.dialogService.addModalBusyState();
|
|
1007
|
+
}
|
|
1008
|
+
super.execute(requests, (results, anyError) => {
|
|
1009
|
+
if (options.busyState) {
|
|
1010
|
+
this.dialogService.removeModalBusyState();
|
|
1011
|
+
}
|
|
1012
|
+
if (options.handleErrors && anyError) {
|
|
1013
|
+
for (let result of results) {
|
|
1014
|
+
if (result.isError) {
|
|
1015
|
+
let errorMsg = result.errorMsg + '\n\n' + result.response;
|
|
1016
|
+
if (StringUtils.isEmpty(errorMsg)) {
|
|
1017
|
+
errorMsg = 'Unknown error. Make sure Internet connection is available';
|
|
1018
|
+
}
|
|
1019
|
+
this.dialogService.addDialog(DialogErrorComponent, {
|
|
1020
|
+
width: '50vw',
|
|
1021
|
+
texts: ['Error: ' + result.code, errorMsg],
|
|
1022
|
+
options: ['Ok']
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
if (finishedCallback !== null) {
|
|
1028
|
+
finishedCallback(results, anyError);
|
|
1029
|
+
}
|
|
1030
|
+
}, progressCallback);
|
|
1015
1031
|
}
|
|
1016
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type:
|
|
1017
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type:
|
|
1032
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: HTTPService, deps: [{ token: DialogService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1033
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: HTTPService, providedIn: 'root' }); }
|
|
1018
1034
|
}
|
|
1019
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type:
|
|
1020
|
-
type: Injectable
|
|
1021
|
-
|
|
1035
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: HTTPService, decorators: [{
|
|
1036
|
+
type: Injectable,
|
|
1037
|
+
args: [{
|
|
1038
|
+
providedIn: 'root',
|
|
1039
|
+
}]
|
|
1040
|
+
}], ctorParameters: () => [{ type: DialogService }] });
|
|
1022
1041
|
|
|
1023
1042
|
/**
|
|
1024
1043
|
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
@@ -1029,31 +1048,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImpor
|
|
|
1029
1048
|
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
1030
1049
|
*/
|
|
1031
1050
|
/**
|
|
1032
|
-
*
|
|
1033
|
-
|
|
1034
|
-
|
|
1051
|
+
* Class that defines a GET http request, to be used by HttpService
|
|
1052
|
+
*/
|
|
1053
|
+
class HTTPServiceGetRequest extends HTTPManagerGetRequest {
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
1035
1058
|
*
|
|
1036
|
-
*
|
|
1037
|
-
*
|
|
1038
|
-
*
|
|
1059
|
+
* Website : -> http://www.turbogui.org
|
|
1060
|
+
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
1061
|
+
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
1062
|
+
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
1039
1063
|
*/
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
*/
|
|
1045
|
-
this.busyStateBaseFade = true;
|
|
1046
|
-
}
|
|
1047
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BusyStateBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1048
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: BusyStateBaseComponent, isStandalone: true, selector: "tg-busy-state-base", host: { properties: { "@busyStateBaseFade": "this.busyStateBaseFade" } }, providers: [], ngImport: i0, template: "<div class=\"darkBg\">\r\n\r\n</div>\r\n\r\n<svg width=\"38\" height=\"38\" viewBox=\"0 0 38 38\" xmlns=\"http://www.w3.org/2000/svg\" stroke=\"#fff\">\r\n <g fill=\"none\" fill-rule=\"evenodd\">\r\n <g transform=\"translate(1 1)\" stroke-width=\"1\">\r\n <circle stroke-opacity=\".2\" cx=\"18\" cy=\"18\" r=\"18\"/>\r\n <path d=\"M36 18c0-9.94-8.06-18-18-18\">\r\n <animateTransform\r\n attributeName=\"transform\"\r\n type=\"rotate\"\r\n from=\"0 18 18\"\r\n to=\"360 18 18\"\r\n dur=\"1s\"\r\n repeatCount=\"indefinite\"/>\r\n </path>\r\n </g>\r\n </g>\r\n</svg>\r\n", styles: [":host{position:fixed;inset:0;display:flex;justify-content:center;align-items:center;z-index:999999999}.darkBg{position:absolute;cursor:progress;width:100%;height:100%;display:flex;justify-content:center;align-items:center;background-color:#00000096;animation-name:bgfadein;animation-duration:30s;animation-timing-function:ease}svg{z-index:5000;width:40%;height:40%;max-width:130px;max-height:130px;animation-name:loadingfadein;animation-duration:3s;animation-timing-function:ease-in}@keyframes loadingfadein{0%{opacity:0}25%{opacity:0}to{opacity:1}}@keyframes bgfadein{0%{opacity:0}4%{opacity:0}14%{opacity:.2}35%{opacity:.5}to{opacity:1}}\n"], animations: [FadeAnimationClass.getTrigger('busyStateBaseFade', '1s ease', '400ms ease')] }); }
|
|
1064
|
+
/**
|
|
1065
|
+
* Class that defines a POST http request, to be used by HttpService
|
|
1066
|
+
*/
|
|
1067
|
+
class HTTPServicePostRequest extends HTTPManagerPostRequest {
|
|
1049
1068
|
}
|
|
1050
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BusyStateBaseComponent, decorators: [{
|
|
1051
|
-
type: Component,
|
|
1052
|
-
args: [{ selector: 'tg-busy-state-base', imports: [], providers: [], animations: [FadeAnimationClass.getTrigger('busyStateBaseFade', '1s ease', '400ms ease')], template: "<div class=\"darkBg\">\r\n\r\n</div>\r\n\r\n<svg width=\"38\" height=\"38\" viewBox=\"0 0 38 38\" xmlns=\"http://www.w3.org/2000/svg\" stroke=\"#fff\">\r\n <g fill=\"none\" fill-rule=\"evenodd\">\r\n <g transform=\"translate(1 1)\" stroke-width=\"1\">\r\n <circle stroke-opacity=\".2\" cx=\"18\" cy=\"18\" r=\"18\"/>\r\n <path d=\"M36 18c0-9.94-8.06-18-18-18\">\r\n <animateTransform\r\n attributeName=\"transform\"\r\n type=\"rotate\"\r\n from=\"0 18 18\"\r\n to=\"360 18 18\"\r\n dur=\"1s\"\r\n repeatCount=\"indefinite\"/>\r\n </path>\r\n </g>\r\n </g>\r\n</svg>\r\n", styles: [":host{position:fixed;inset:0;display:flex;justify-content:center;align-items:center;z-index:999999999}.darkBg{position:absolute;cursor:progress;width:100%;height:100%;display:flex;justify-content:center;align-items:center;background-color:#00000096;animation-name:bgfadein;animation-duration:30s;animation-timing-function:ease}svg{z-index:5000;width:40%;height:40%;max-width:130px;max-height:130px;animation-name:loadingfadein;animation-duration:3s;animation-timing-function:ease-in}@keyframes loadingfadein{0%{opacity:0}25%{opacity:0}to{opacity:1}}@keyframes bgfadein{0%{opacity:0}4%{opacity:0}14%{opacity:.2}35%{opacity:.5}to{opacity:1}}\n"] }]
|
|
1053
|
-
}], propDecorators: { busyStateBaseFade: [{
|
|
1054
|
-
type: HostBinding,
|
|
1055
|
-
args: ['@busyStateBaseFade']
|
|
1056
|
-
}] } });
|
|
1057
1069
|
|
|
1058
1070
|
/**
|
|
1059
1071
|
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
@@ -1064,26 +1076,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImpor
|
|
|
1064
1076
|
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
1065
1077
|
*/
|
|
1066
1078
|
/**
|
|
1067
|
-
*
|
|
1079
|
+
* An abstraction of the browser entity an all its related operations and properties
|
|
1068
1080
|
*/
|
|
1069
|
-
class
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
this.elementRef = elementRef;
|
|
1074
|
-
this.dialogRef = dialogRef;
|
|
1075
|
-
this.data = data;
|
|
1081
|
+
class BrowserService extends BrowserManager {
|
|
1082
|
+
constructor(location) {
|
|
1083
|
+
super();
|
|
1084
|
+
this.location = location;
|
|
1076
1085
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1086
|
+
/**
|
|
1087
|
+
* Modify the current browser URI without reloading the current page document
|
|
1088
|
+
*
|
|
1089
|
+
* @param path The uri value we want to set
|
|
1090
|
+
* @param query The query url parameters part we want to specify if any
|
|
1091
|
+
*
|
|
1092
|
+
* @returns void
|
|
1093
|
+
*/
|
|
1094
|
+
setCurrentUrlURI(path, query) {
|
|
1095
|
+
this.location.go(path, query);
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Obtain a subscription to get notified on any changes at the browser url
|
|
1099
|
+
*
|
|
1100
|
+
* @param onNext A method to be executed every time the url changes on the browser. The url will be available inside the value parameter
|
|
1101
|
+
*
|
|
1102
|
+
* @returns Subscribed events. Make sure to unsubscribe when not needed
|
|
1103
|
+
*/
|
|
1104
|
+
subscribeToUrlChange(onNext) {
|
|
1105
|
+
return this.location.subscribe(onNext);
|
|
1106
|
+
}
|
|
1107
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BrowserService, deps: [{ token: i2.Location }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1108
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BrowserService, providedIn: 'root' }); }
|
|
1079
1109
|
}
|
|
1080
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type:
|
|
1081
|
-
type:
|
|
1082
|
-
args: [{
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
}] }] });
|
|
1110
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BrowserService, decorators: [{
|
|
1111
|
+
type: Injectable,
|
|
1112
|
+
args: [{
|
|
1113
|
+
providedIn: 'root',
|
|
1114
|
+
}]
|
|
1115
|
+
}], ctorParameters: () => [{ type: i2.Location }] });
|
|
1087
1116
|
|
|
1088
1117
|
/**
|
|
1089
1118
|
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
@@ -1094,403 +1123,428 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImpor
|
|
|
1094
1123
|
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
1095
1124
|
*/
|
|
1096
1125
|
/**
|
|
1097
|
-
*
|
|
1126
|
+
* Allows us to easily perform requests to a remote API that is developed with Turbo framework.
|
|
1098
1127
|
*/
|
|
1099
|
-
class
|
|
1100
|
-
constructor(
|
|
1101
|
-
super(
|
|
1102
|
-
this.
|
|
1103
|
-
this.
|
|
1104
|
-
this.injector = injector;
|
|
1105
|
-
this.applicationRef = applicationRef;
|
|
1106
|
-
this.componentFactoryResolver = componentFactoryResolver;
|
|
1107
|
-
/**
|
|
1108
|
-
* Used to modify the busy state component that is shown by default by the addModalBusyState() method.
|
|
1109
|
-
*
|
|
1110
|
-
* @see this.addModalBusyState()
|
|
1111
|
-
*/
|
|
1112
|
-
this.customBusyStateComponentClass = BusyStateBaseComponent;
|
|
1113
|
-
/**
|
|
1114
|
-
* Check public getter for docs
|
|
1115
|
-
*/
|
|
1116
|
-
this._isEnabled = true;
|
|
1128
|
+
class TurboApiCallerService extends SingletoneStrictClass {
|
|
1129
|
+
constructor(dialogService, browserService) {
|
|
1130
|
+
super(TurboApiCallerService);
|
|
1131
|
+
this.dialogService = dialogService;
|
|
1132
|
+
this.browserService = browserService;
|
|
1117
1133
|
/**
|
|
1118
|
-
*
|
|
1134
|
+
* URI Path to the web service that performs the user login
|
|
1119
1135
|
*/
|
|
1120
|
-
this.
|
|
1136
|
+
this.loginServiceURI = 'users/login';
|
|
1121
1137
|
/**
|
|
1122
|
-
*
|
|
1123
|
-
*
|
|
1124
|
-
* (To append the busy state dynamically, we use the Portal concept from the angular material library)
|
|
1138
|
+
* URI Path to the web service that allows us to create extra user tokens
|
|
1125
1139
|
*/
|
|
1126
|
-
this.
|
|
1140
|
+
this.tokenCreationServiceURI = 'users/user-token-create';
|
|
1127
1141
|
/**
|
|
1128
|
-
*
|
|
1142
|
+
* URI Path to the web service that performs the user log out
|
|
1129
1143
|
*/
|
|
1130
|
-
this.
|
|
1144
|
+
this.logOutServiceURI = 'users/logout';
|
|
1131
1145
|
/**
|
|
1132
|
-
*
|
|
1146
|
+
* URI Path to the web service that performs the verification for a user email
|
|
1133
1147
|
*/
|
|
1134
|
-
this.
|
|
1148
|
+
this.mailVerifyServiceURI = 'users/user-mail-verify';
|
|
1135
1149
|
/**
|
|
1136
|
-
*
|
|
1137
|
-
* Each item in this list is a hash that is computed when dialog is created to uniquely identify it.
|
|
1138
|
-
*
|
|
1139
|
-
* Empty list means no dialogs are currently visible
|
|
1150
|
+
* The username that is currently defined and will be used by the login methods
|
|
1140
1151
|
*/
|
|
1141
|
-
this.
|
|
1152
|
+
this._userName = '';
|
|
1142
1153
|
/**
|
|
1143
|
-
*
|
|
1144
|
-
* The list uses the same order as the list of _activeDialogs hash values
|
|
1154
|
+
* The password for the user that is currently defined and will be used by the login methods
|
|
1145
1155
|
*/
|
|
1146
|
-
this.
|
|
1156
|
+
this._password = '';
|
|
1147
1157
|
/**
|
|
1148
|
-
*
|
|
1158
|
+
* Contains the last email account that has been verified (or tried to verify) by this service, if any
|
|
1149
1159
|
*/
|
|
1150
|
-
this.
|
|
1160
|
+
this._lastVerifiedMail = '';
|
|
1151
1161
|
/**
|
|
1152
|
-
*
|
|
1162
|
+
* Check public getter for docs
|
|
1153
1163
|
*/
|
|
1154
|
-
this.
|
|
1164
|
+
this._isLogged = false;
|
|
1155
1165
|
/**
|
|
1156
|
-
*
|
|
1166
|
+
* @see token() getter for more info
|
|
1157
1167
|
*/
|
|
1158
|
-
this.
|
|
1168
|
+
this._token = '';
|
|
1159
1169
|
/**
|
|
1160
|
-
*
|
|
1170
|
+
* List of operations that are allowed to the currently loged user. It gets filled just after login is performed
|
|
1161
1171
|
*/
|
|
1162
|
-
this.
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
* Tells if this dialog service is enabled or not. If dialog service is disabled, none of it's features will work
|
|
1167
|
-
* This is used to block all dialog features normally when shutting down the application
|
|
1168
|
-
*
|
|
1169
|
-
* Use with caution. When this is set to false, dialog service stops working.
|
|
1170
|
-
*/
|
|
1171
|
-
set isEnabled(v) {
|
|
1172
|
-
if (v === this._isEnabled) {
|
|
1173
|
-
return;
|
|
1174
|
-
}
|
|
1175
|
-
this._isEnabled = v;
|
|
1172
|
+
this._operations = [];
|
|
1173
|
+
// Create a fresh instance of the http service so we can use it independently
|
|
1174
|
+
this.httpManager = new HTTPManager();
|
|
1175
|
+
this._clearUserAndToken();
|
|
1176
1176
|
}
|
|
1177
1177
|
/**
|
|
1178
|
-
*
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
*/
|
|
1185
|
-
addCloseApplicationWarning() {
|
|
1186
|
-
if (this._windowBeforeUnloadUnListen === null) {
|
|
1187
|
-
this._windowBeforeUnloadUnListen = this._renderer.listen('window', 'beforeunload', (event) => event.returnValue = true);
|
|
1188
|
-
}
|
|
1178
|
+
* The username that is currently defined and will be used by the login methods
|
|
1179
|
+
*/
|
|
1180
|
+
set userName(v) {
|
|
1181
|
+
this._isLogged = false;
|
|
1182
|
+
this._token = '';
|
|
1183
|
+
this._userName = v;
|
|
1189
1184
|
}
|
|
1190
1185
|
/**
|
|
1191
|
-
*
|
|
1186
|
+
* The username that is currently defined and will be used by the login methods
|
|
1192
1187
|
*/
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
this._windowBeforeUnloadUnListen();
|
|
1196
|
-
this._windowBeforeUnloadUnListen = null;
|
|
1197
|
-
}
|
|
1188
|
+
get userName() {
|
|
1189
|
+
return this._userName;
|
|
1198
1190
|
}
|
|
1199
1191
|
/**
|
|
1200
|
-
*
|
|
1201
|
-
* currently busy. While modal busy state is enabled, no user input is possible neither via
|
|
1202
|
-
* keyboard, mouse or touch. Use this state when performing server requests or operations that
|
|
1203
|
-
* must block the user interaction with the application. To allow user interaction again, you must
|
|
1204
|
-
* call removeModalBusyState()
|
|
1205
|
-
*
|
|
1206
|
-
* Notice: We can modify the busy state visual component that is shown by this method. To do it, we must
|
|
1207
|
-
* set this.customBusyStateComponentClass property with our own custom busy state component class. (We can do it at
|
|
1208
|
-
* our main application component constructor for example). Our custom component must extend the
|
|
1209
|
-
* BusyStateBaseComponent one to add its own visual appearance.
|
|
1210
|
-
*
|
|
1211
|
-
* @see this.customBusyStateComponentClass
|
|
1192
|
+
* The password for the user that is currently defined and will be used by the login methods
|
|
1212
1193
|
*/
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
this._disableUserInteraction();
|
|
1218
|
-
// Dynamically create the busy state component reference if this is the first time
|
|
1219
|
-
if (this._componentPortal === null) {
|
|
1220
|
-
this._componentPortal = new ComponentPortal(this.customBusyStateComponentClass);
|
|
1221
|
-
// Create a PortalHost with document.body as its anchor element
|
|
1222
|
-
this._modalBusyStateHost = new DomPortalOutlet(document.body, this.componentFactoryResolver, this.applicationRef, this.injector);
|
|
1223
|
-
}
|
|
1224
|
-
this._modalBusyStateHost.attach(this._componentPortal);
|
|
1225
|
-
this._isShowingBusyState = true;
|
|
1194
|
+
set password(v) {
|
|
1195
|
+
this._isLogged = false;
|
|
1196
|
+
this._token = '';
|
|
1197
|
+
this._password = v;
|
|
1226
1198
|
}
|
|
1227
1199
|
/**
|
|
1228
|
-
*
|
|
1200
|
+
* The password for the user that is currently defined and will be used by the login methods
|
|
1229
1201
|
*/
|
|
1230
|
-
get
|
|
1231
|
-
return this.
|
|
1202
|
+
get password() {
|
|
1203
|
+
return this._password;
|
|
1232
1204
|
}
|
|
1233
1205
|
/**
|
|
1234
|
-
*
|
|
1206
|
+
* Define the root url that will be used for all the API calls
|
|
1235
1207
|
*/
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
return;
|
|
1239
|
-
}
|
|
1240
|
-
if (this._componentPortal !== null) {
|
|
1241
|
-
this._modalBusyStateHost.detach();
|
|
1242
|
-
}
|
|
1243
|
-
this._enableUserInteraction();
|
|
1244
|
-
this._isShowingBusyState = false;
|
|
1208
|
+
set baseUrl(url) {
|
|
1209
|
+
this.httpManager.baseUrl = url;
|
|
1245
1210
|
}
|
|
1246
1211
|
/**
|
|
1247
|
-
*
|
|
1212
|
+
* Define the root url that will be used for all the API calls
|
|
1248
1213
|
*/
|
|
1249
|
-
|
|
1250
|
-
|
|
1214
|
+
get baseUrl() {
|
|
1215
|
+
return this.httpManager.baseUrl;
|
|
1251
1216
|
}
|
|
1252
1217
|
/**
|
|
1253
|
-
*
|
|
1254
|
-
*
|
|
1255
|
-
* Snackbars inform users of a process that an app has performed or will perform. They appear temporarily, towards the bottom or top of the screen.
|
|
1256
|
-
* They shouldn’t interrupt the user experience, and they don’t require user input to disappear.
|
|
1218
|
+
* If the current browser URL contains a hash fragment (The part after the # character) that contains encoded user or password
|
|
1219
|
+
* values, this method will parse and automatically set them as the credentials to use.
|
|
1257
1220
|
*
|
|
1258
|
-
*
|
|
1259
|
-
* @param message The message to show on the snackbar
|
|
1260
|
-
* @param action If not empty, the text to place on the snackbar confirmation button
|
|
1261
|
-
* @param actionCallback A method to execute once the user clicks into the action button.
|
|
1221
|
+
* The URL hash is not altered in any way by this method.
|
|
1262
1222
|
*
|
|
1263
|
-
* @
|
|
1223
|
+
* @returns True if any credentials were found and loaded, false if nothing happened
|
|
1264
1224
|
*/
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1225
|
+
loadCredentialsFromURLHashFragment() {
|
|
1226
|
+
// If the hash fragment is empty, nothing to do
|
|
1227
|
+
if (!this.browserService.isCurrentUrlWithHashFragment()) {
|
|
1228
|
+
return false;
|
|
1268
1229
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1230
|
+
let valuesFound = false;
|
|
1231
|
+
// Split the hash fragment to obtain the different values that contains
|
|
1232
|
+
let hashData = this.browserService.getCurrentUrlHashFragment().split('/');
|
|
1233
|
+
// User name is the first element of the hash fragment. If we found a value,
|
|
1234
|
+
// we will fill it automatically
|
|
1235
|
+
if (hashData.length > 0) {
|
|
1236
|
+
valuesFound = true;
|
|
1237
|
+
this.userName = ConversionUtils.base64ToString(hashData[0]);
|
|
1271
1238
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
resolve(true);
|
|
1279
|
-
});
|
|
1280
|
-
// Handle dismiss
|
|
1281
|
-
snackBarRef.afterDismissed().subscribe(() => {
|
|
1282
|
-
this._isShowingSnackBar = false;
|
|
1283
|
-
resolve(false);
|
|
1284
|
-
});
|
|
1285
|
-
});
|
|
1239
|
+
// Auto fill the password if it is received
|
|
1240
|
+
if (hashData.length > 3) {
|
|
1241
|
+
valuesFound = true;
|
|
1242
|
+
this.password = ConversionUtils.base64ToString(hashData[3]);
|
|
1243
|
+
}
|
|
1244
|
+
return valuesFound;
|
|
1286
1245
|
}
|
|
1287
1246
|
/**
|
|
1288
|
-
*
|
|
1247
|
+
* If the current browser URL contains a hash fragment (The part after the # character) that contains encoded user, mail and
|
|
1248
|
+
* has verification code values, this method will perform the request to server to verify that email account for the user.
|
|
1249
|
+
*
|
|
1250
|
+
* The URI path to the mail verification service must be correctly defined at this.mailVerifyServiceURI property.
|
|
1251
|
+
*
|
|
1252
|
+
* @returns A promise that resolves with the server response if the verification is successful,
|
|
1253
|
+
* or rejects with an error if the verification fails. Response will have 4 possible values:
|
|
1254
|
+
* undefined - meaning that the URL hash fragment didn't contain valid verification values
|
|
1255
|
+
* -1 - meaning verification failed
|
|
1256
|
+
* 0 - meaning verification succeeded
|
|
1257
|
+
* 1 - meaning the email was already previouly verified
|
|
1289
1258
|
*/
|
|
1290
|
-
|
|
1291
|
-
|
|
1259
|
+
verifyUserMailFromURLHashFragment() {
|
|
1260
|
+
// If the hash fragment is empty, nothing to do
|
|
1261
|
+
if (this.browserService.isCurrentUrlWithHashFragment()) {
|
|
1262
|
+
let hashData = this.browserService.getCurrentUrlHashFragment().split('/');
|
|
1263
|
+
if (hashData.length >= 3) {
|
|
1264
|
+
// Call for the user mail verification if user, mail and hash are received
|
|
1265
|
+
let user = ConversionUtils.base64ToString(hashData[0]);
|
|
1266
|
+
let mail = ConversionUtils.base64ToString(hashData[1]);
|
|
1267
|
+
let hash = ConversionUtils.base64ToString(hashData[2]);
|
|
1268
|
+
if (!StringUtils.isEmpty(user) && !StringUtils.isEmpty(mail) && !StringUtils.isEmpty(hash)) {
|
|
1269
|
+
this._lastVerifiedMail = mail;
|
|
1270
|
+
return this.call(this.mailVerifyServiceURI, { userName: user, mail: mail, hash: hash });
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
return Promise.resolve(undefined);
|
|
1292
1275
|
}
|
|
1293
1276
|
/**
|
|
1294
|
-
*
|
|
1295
|
-
*
|
|
1296
|
-
* If no snackbar is currently visible, this method will do nothing
|
|
1277
|
+
* Obtain the lat user mail account that has been verified (or attempted to verify) by this service.
|
|
1297
1278
|
*/
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
return;
|
|
1301
|
-
}
|
|
1302
|
-
this.matSnackBar.dismiss();
|
|
1303
|
-
this._isShowingSnackBar = false;
|
|
1279
|
+
getLastVerifiedMail() {
|
|
1280
|
+
return this._lastVerifiedMail;
|
|
1304
1281
|
}
|
|
1305
1282
|
/**
|
|
1306
|
-
*
|
|
1307
|
-
*
|
|
1283
|
+
* Authenticates the userName and password that are currently defined at the respective properties of this service.
|
|
1284
|
+
* Returns a promise that resolves with the server's response or rejects with an error if the login fails.
|
|
1285
|
+
* Path to the login service must be correctly defined at this.loginWebService
|
|
1308
1286
|
*
|
|
1309
|
-
*
|
|
1310
|
-
*
|
|
1311
|
-
* - id: The html unique identifier that the dialog will have once created. If not specified, no id will be explicitly set
|
|
1312
|
-
* - width: 50% by default. Specify the css value for the default dialog width. As the dialog is responsive, the value will be automatically
|
|
1313
|
-
* reduced if the available screen is not enough, and will reach the desired value otherwise. We can set any css unit like pixels,
|
|
1314
|
-
* %, vh, vw, or any other. For example: '400px', '50%', etc.
|
|
1315
|
-
* - maxWidth: Defines the maximum width that the dialog will have regarding the viewport. We can specify it in % or vw, just like is done in
|
|
1316
|
-
* css. By default it is defined as 96vw, which will fit 96% of the viewport on small devices
|
|
1317
|
-
* - height: TODO docs
|
|
1318
|
-
* - maxHeight: TODO docs
|
|
1319
|
-
* - modal: True (default) if selecting an option is mandatory to close the dialog, false if the dialog can be closed
|
|
1320
|
-
* by the user clicking outside it
|
|
1321
|
-
* - texts: A list with strings containing the dialog texts, sorted by importance. When dialog has a title, this should
|
|
1322
|
-
* be placed first, subtitle second and so (Each dialog may accept a different custom number of texts).
|
|
1323
|
-
* - options: A list of strings that will be used as button captions for each one of the accepted dialog options
|
|
1324
|
-
* - data: An object that we can use to pass any extra data that we want to the dialog
|
|
1325
|
-
* - viewContainerRef: This is important if we want to propagate providers from a parent component to this dialog. We must specify
|
|
1326
|
-
* this reference to make sure the same services injected on the parent are available too at the child dialog
|
|
1287
|
+
* The authentication process is performed by sending an encoded credentials request to the login web service using the
|
|
1288
|
+
* currently defined user name and psw.
|
|
1327
1289
|
*
|
|
1328
|
-
*
|
|
1329
|
-
*
|
|
1330
|
-
*
|
|
1290
|
+
* Here's an example of a call:
|
|
1291
|
+
*
|
|
1292
|
+
* login().then(response => {
|
|
1293
|
+
* console.log('Login successful:', response);
|
|
1294
|
+
* }).catch(() => {
|
|
1295
|
+
* console.error('Login failed:');
|
|
1296
|
+
* });
|
|
1297
|
+
*
|
|
1298
|
+
* @returns A promise that resolves with the server response if the login is successful,
|
|
1299
|
+
* or rejects with an error if the login fails.
|
|
1331
1300
|
*/
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
throw new Error(`The static property DIALOG_CLASS_NAME is not defined or is empty for this dialog component (${dialogComponentClass})`);
|
|
1348
|
-
}
|
|
1349
|
-
const dialogHash = className + properties.texts.join('');
|
|
1350
|
-
// identical dialogs won't be allowed at the same time
|
|
1351
|
-
if (this._activeDialogs.includes(dialogHash)) {
|
|
1352
|
-
return resolve({ index: -1 });
|
|
1353
|
-
}
|
|
1354
|
-
const dialogRef = this.matDialog.open(dialogComponentClass, {
|
|
1355
|
-
width: properties.width ?? "50%",
|
|
1356
|
-
maxWidth: properties.maxWidth ?? "96vw",
|
|
1357
|
-
disableClose: properties.modal,
|
|
1358
|
-
autoFocus: false,
|
|
1359
|
-
closeOnNavigation: !properties.modal,
|
|
1360
|
-
viewContainerRef: properties.viewContainerRef,
|
|
1361
|
-
data: { texts: properties.texts, options: properties.options, data: properties.data }
|
|
1362
|
-
});
|
|
1363
|
-
// Assign the dialog ID only if specifically set on properties
|
|
1364
|
-
if (properties.id && properties.id !== undefined) {
|
|
1365
|
-
dialogRef.id = properties.id;
|
|
1366
|
-
}
|
|
1367
|
-
this._activeDialogs.push(dialogHash);
|
|
1368
|
-
this._activeDialogInstances.push(dialogRef);
|
|
1369
|
-
dialogRef.beforeClosed().subscribe((selection) => {
|
|
1370
|
-
this._activeDialogs = ArrayUtils.removeElement(this._activeDialogs, dialogHash);
|
|
1371
|
-
this._activeDialogInstances = ArrayUtils.removeElement(this._activeDialogInstances, dialogRef);
|
|
1372
|
-
if (!properties.modal && selection === undefined) {
|
|
1373
|
-
selection = { index: -1 };
|
|
1374
|
-
}
|
|
1375
|
-
else if (!NumericUtils.isInteger(selection.index)) {
|
|
1376
|
-
throw new Error(`closeDialog() expects index to be an integer`);
|
|
1301
|
+
login() {
|
|
1302
|
+
this.dialogService.addModalBusyState();
|
|
1303
|
+
return new Promise((resolve, reject) => {
|
|
1304
|
+
const request = new HTTPManagerPostRequest(this.loginServiceURI);
|
|
1305
|
+
request.ignoreGlobalPostParams = true;
|
|
1306
|
+
const encodedCredentials = ConversionUtils.stringToBase64(ConversionUtils.stringToBase64(this._userName) + ',' + ConversionUtils.stringToBase64(this._password));
|
|
1307
|
+
request.parameters = { data: encodedCredentials };
|
|
1308
|
+
request.successCallback = (response) => {
|
|
1309
|
+
if (response !== '') {
|
|
1310
|
+
response = JSON.parse(response);
|
|
1311
|
+
this._isLogged = true;
|
|
1312
|
+
this._token = response.token;
|
|
1313
|
+
this._operations = response.operations;
|
|
1314
|
+
this.httpManager.setGlobalPostParam('token', response.token);
|
|
1315
|
+
resolve(response);
|
|
1377
1316
|
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1317
|
+
else {
|
|
1318
|
+
this._clearUserAndToken();
|
|
1319
|
+
reject(new Error());
|
|
1380
1320
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1321
|
+
};
|
|
1322
|
+
request.errorCallback = () => {
|
|
1323
|
+
this._clearUserAndToken();
|
|
1324
|
+
reject(new Error());
|
|
1325
|
+
};
|
|
1326
|
+
request.finallyCallback = () => {
|
|
1327
|
+
this.dialogService.removeModalBusyState();
|
|
1328
|
+
};
|
|
1329
|
+
this.httpManager.execute(request);
|
|
1383
1330
|
});
|
|
1384
1331
|
}
|
|
1385
1332
|
/**
|
|
1386
|
-
*
|
|
1333
|
+
* Perform a request to the API service to create a new token for the user that is currently logged in.
|
|
1387
1334
|
*
|
|
1388
|
-
* @param
|
|
1389
|
-
*
|
|
1390
|
-
* - width: Specify the css value for the default dialog width. As the dialog is responsive, the value will be automatically
|
|
1391
|
-
* reduced if the available screen is not enough, and will reach the desired value otherwise. We can set any css unit like pixels,
|
|
1392
|
-
* %, vh, vw, or any other. For example: '400px', '50%', etc.
|
|
1393
|
-
* - maxWidth: Defines the maximum width that the dialog will have regarding the viewport. We can specify it in % or vw, just like is done in
|
|
1394
|
-
* css. By default it is defined as 96vw, which will fit 96% of the viewport on small devices
|
|
1395
|
-
* - height: TODO docs
|
|
1396
|
-
* - maxHeight: TODO docs
|
|
1397
|
-
* - modal: True (default) if selecting an option is mandatory to close the dialog, false if the dialog can be closed
|
|
1398
|
-
* by the user clicking outside it
|
|
1399
|
-
* - title: An optional dialog title
|
|
1400
|
-
* - viewContainerRef: This is important to propagate providers from a parent component to this dialog. We must specify
|
|
1401
|
-
* this reference to make sure the same services injected on the parent are available too at the child dialog
|
|
1335
|
+
* @param options The parameters that will affect the token behaviour. To learn more about each option, please
|
|
1336
|
+
* refer to the turbodepot UsersManager class createToken method documentation
|
|
1402
1337
|
*
|
|
1403
|
-
* @returns A
|
|
1338
|
+
* @returns A promise that resolves with the created token string if the request is successful, or rejects
|
|
1339
|
+
* with an error (containing all the error response details) if the request fails.
|
|
1404
1340
|
*/
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
return null;
|
|
1408
|
-
}
|
|
1409
|
-
const selection = await this.addDialog(DialogDateSelectionComponent, {
|
|
1410
|
-
id: properties.id ?? undefined,
|
|
1411
|
-
width: properties.width ?? "50%",
|
|
1412
|
-
maxWidth: properties.maxWidth ?? "96vw",
|
|
1413
|
-
height: properties.height ?? "50%",
|
|
1414
|
-
maxHeight: properties.maxHeight ?? "92vw",
|
|
1415
|
-
modal: properties.modal ?? false,
|
|
1416
|
-
texts: [properties.title ?? ''],
|
|
1417
|
-
viewContainerRef: properties.viewContainerRef
|
|
1418
|
-
});
|
|
1419
|
-
return selection.index === -1 ? null : selection.value;
|
|
1341
|
+
createUserToken(options) {
|
|
1342
|
+
return this.call(this.tokenCreationServiceURI, { options: options }, { resultFormat: 'STRING' });
|
|
1420
1343
|
}
|
|
1421
1344
|
/**
|
|
1422
|
-
*
|
|
1423
|
-
*
|
|
1424
|
-
* If no dialogs are currently visible, this method will do nothing
|
|
1345
|
+
* Checks if the user and password credentials are filled and not empty.
|
|
1346
|
+
* If any of the two values is empty, false will be returned
|
|
1425
1347
|
*/
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
return;
|
|
1429
|
-
}
|
|
1430
|
-
for (const dialogRef of this._activeDialogInstances) {
|
|
1431
|
-
dialogRef.close({ index: -1 });
|
|
1432
|
-
}
|
|
1433
|
-
this._activeDialogs = [];
|
|
1434
|
-
this._activeDialogInstances = [];
|
|
1348
|
+
get isUserAndPswDefined() {
|
|
1349
|
+
return !StringUtils.isEmpty(this._userName) && !StringUtils.isEmpty(this._password);
|
|
1435
1350
|
}
|
|
1436
1351
|
/**
|
|
1437
|
-
*
|
|
1352
|
+
* Tells if the user name and psw that are specified on this service are currently logged or not. This means
|
|
1353
|
+
* also a token is active.
|
|
1438
1354
|
*/
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1355
|
+
get isLogged() {
|
|
1356
|
+
return this._isLogged;
|
|
1357
|
+
}
|
|
1442
1358
|
/**
|
|
1443
|
-
*
|
|
1359
|
+
* Gives the value for the currently active user authentication token or an empty string if no user logged
|
|
1444
1360
|
*/
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1361
|
+
get token() {
|
|
1362
|
+
return this._token;
|
|
1363
|
+
}
|
|
1448
1364
|
/**
|
|
1449
|
-
*
|
|
1365
|
+
* Checks if the currently logged user is allowed to perform the specified operation.
|
|
1366
|
+
* This will check the user permisions as defined on server side and return true if the operation is allowed
|
|
1367
|
+
* or false if not. We can then decide what to do (hide an app section, block a button, etc)
|
|
1368
|
+
*
|
|
1369
|
+
* @param operation The name of the operation to check
|
|
1370
|
+
*
|
|
1371
|
+
* @returns True if the operation is allowed, false otherwise
|
|
1450
1372
|
*/
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1373
|
+
isUserAllowedTo(operation) {
|
|
1374
|
+
return this._operations.includes(operation);
|
|
1375
|
+
}
|
|
1454
1376
|
/**
|
|
1455
|
-
*
|
|
1377
|
+
* Performs a standard request to an API service and returns a promise that resolves with the response data.
|
|
1378
|
+
*
|
|
1379
|
+
* Following is an example of a call:
|
|
1380
|
+
*
|
|
1381
|
+
* this.apiService.call('users/user-create', this.userData, {handleErrors: false}).then(response => {
|
|
1382
|
+
*
|
|
1383
|
+
* console.log('Success:', response);
|
|
1384
|
+
*
|
|
1385
|
+
* }).catch(error => {
|
|
1386
|
+
*
|
|
1387
|
+
* console.error('Error:', error.message);
|
|
1388
|
+
* });
|
|
1389
|
+
*
|
|
1390
|
+
* @param apiPath - A relative URL (based on the defined base root url) that defines the path to the service to call.
|
|
1391
|
+
* For example: 'users/login'
|
|
1392
|
+
* @param parameters - An object containing key-value pairs that are sent as the request body.
|
|
1393
|
+
* token parameter is not necessary, it is automatically appended
|
|
1394
|
+
* @param options An object defining several options for the request:
|
|
1395
|
+
* resultFormat: 'JSON' by default. The expected format of the response.
|
|
1396
|
+
* busyState: Enabled by default. Enables or disables the busy state to lock user interaction while performing the http calls.
|
|
1397
|
+
* handleErrors: Enabled by default. If set to true, an error dialog will be automatically shown when the call fails.
|
|
1398
|
+
* If set to false, the promise will generate a reject error that must be handled by our code.
|
|
1399
|
+
*
|
|
1400
|
+
* @returns A promise that resolves with the response data correctly parsed if the request is successful, or rejects
|
|
1401
|
+
* with an error (containing all the error response details) if the request fails.
|
|
1456
1402
|
*/
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1403
|
+
call(apiPath, parameters = {}, options = {}) {
|
|
1404
|
+
// Set the default values for non specified properties
|
|
1405
|
+
options.resultFormat = options.resultFormat ?? 'JSON';
|
|
1406
|
+
options.busyState = options.busyState ?? true;
|
|
1407
|
+
options.handleErrors = options.handleErrors ?? true;
|
|
1408
|
+
if (options.busyState) {
|
|
1409
|
+
this.dialogService.addModalBusyState();
|
|
1460
1410
|
}
|
|
1461
|
-
|
|
1462
|
-
|
|
1411
|
+
return new Promise((resolve, reject) => {
|
|
1412
|
+
// Create the request object
|
|
1413
|
+
const request = new HTTPManagerPostRequest(apiPath, options.resultFormat === 'STRING' ? HTTPManagerPostRequest.STRING : HTTPManagerPostRequest.JSON);
|
|
1414
|
+
request.parameters = parameters;
|
|
1415
|
+
request.successCallback = (response) => {
|
|
1416
|
+
resolve(response);
|
|
1417
|
+
};
|
|
1418
|
+
request.errorCallback = (errorMsg, errorCode, response) => {
|
|
1419
|
+
if (options.handleErrors) {
|
|
1420
|
+
this.showErrorDialog(errorMsg, errorCode, response);
|
|
1421
|
+
}
|
|
1422
|
+
else {
|
|
1423
|
+
reject(new Error(errorMsg + ' ' + errorCode + ' ' + response));
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
request.finallyCallback = () => {
|
|
1427
|
+
if (options.busyState) {
|
|
1428
|
+
this.dialogService.removeModalBusyState();
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
this.httpManager.execute(request);
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Performs a request to chain several api calls into a single http request, via the chain services mechanism of the turboframework API.
|
|
1436
|
+
* Returns a promise that resolves with an array of response objects. One for each of the request calls.
|
|
1437
|
+
*
|
|
1438
|
+
* @param apiPath - A relative URL (based on the defined base root url) that defines the path to the root of the chain services call.
|
|
1439
|
+
* For example: 'turbosite/chain/chain-services'
|
|
1440
|
+
* @param services - An array of objects, were each object contains a valid structure for the chain services call.
|
|
1441
|
+
* Token parameter is not necessary, it is automatically appended
|
|
1442
|
+
* @param options An object defining several options for the request:
|
|
1443
|
+
* busyState: Enables or disables the busy state to lock user interaction while performing the http calls. Enabled by default
|
|
1444
|
+
* handleErrors: Enabled by default. If set to true, an error dialog will be automatically shown when the call fails.
|
|
1445
|
+
* If set to false, the promise will generate a reject error that must be handled by our code.
|
|
1446
|
+
*
|
|
1447
|
+
* @returns A promise that resolves with the response data correctly parsed if the request is successful, or rejects
|
|
1448
|
+
* with an error (containing all the error response details) if the request fails.
|
|
1449
|
+
*/
|
|
1450
|
+
callChain(apiPath, services, options = {}) {
|
|
1451
|
+
// Set the default values for non specified properties
|
|
1452
|
+
options.busyState = options.busyState ?? true;
|
|
1453
|
+
options.handleErrors = options.handleErrors ?? true;
|
|
1454
|
+
if (options.busyState) {
|
|
1455
|
+
this.dialogService.addModalBusyState();
|
|
1463
1456
|
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1457
|
+
return new Promise((resolve, reject) => {
|
|
1458
|
+
const request = new HTTPManagerPostRequest(apiPath, HTTPManagerPostRequest.JSON);
|
|
1459
|
+
request.ignoreGlobalPostParams = true;
|
|
1460
|
+
request.parameters = { services: services };
|
|
1461
|
+
request.successCallback = (response) => {
|
|
1462
|
+
resolve(response);
|
|
1463
|
+
};
|
|
1464
|
+
request.errorCallback = (errorMsg, errorCode, response) => {
|
|
1465
|
+
if (options.handleErrors) {
|
|
1466
|
+
this.showErrorDialog(errorMsg, errorCode, response);
|
|
1467
|
+
}
|
|
1468
|
+
else {
|
|
1469
|
+
reject(new Error(errorMsg + ' ' + errorCode + ' ' + response));
|
|
1470
|
+
}
|
|
1471
|
+
};
|
|
1472
|
+
request.finallyCallback = () => {
|
|
1473
|
+
if (options.busyState) {
|
|
1474
|
+
this.dialogService.removeModalBusyState();
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
this.httpManager.execute(request);
|
|
1478
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Aux method to show an error dialog when a request fails and error handling is enabled
|
|
1482
|
+
*/
|
|
1483
|
+
showErrorDialog(errorMsg, errorCode, response) {
|
|
1484
|
+
errorMsg = errorMsg + '\n\n' + response;
|
|
1485
|
+
if (StringUtils.isEmpty(errorMsg)) {
|
|
1486
|
+
errorMsg = 'Unknown error. Make sure Internet connection is available';
|
|
1466
1487
|
}
|
|
1488
|
+
this.dialogService.addDialog(DialogErrorComponent, {
|
|
1489
|
+
width: '50vw',
|
|
1490
|
+
texts: ['Error: ' + errorCode, errorMsg],
|
|
1491
|
+
options: ['Ok']
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* Perform the logout for the currently logged user
|
|
1496
|
+
*
|
|
1497
|
+
* @param options An object defining several options for the request:
|
|
1498
|
+
* handleErrors: Enabled by default. If set to true, an error dialog will be automatically shown when the call fails.
|
|
1499
|
+
* If set to false, the promise will generate a reject error that must be handled by our code.
|
|
1500
|
+
*
|
|
1501
|
+
* @returns A promise that resolves correctly if the logout is correct, or rejects with an error (containing all the error
|
|
1502
|
+
* response details) if the request fails.
|
|
1503
|
+
*/
|
|
1504
|
+
logout(options = {}) {
|
|
1505
|
+
// Set the default values for non specified properties
|
|
1506
|
+
options.handleErrors = options.handleErrors ?? true;
|
|
1507
|
+
this.dialogService.addModalBusyState();
|
|
1508
|
+
return new Promise((resolve, reject) => {
|
|
1509
|
+
const request = new HTTPManagerPostRequest(this.logOutServiceURI);
|
|
1510
|
+
request.parameters = { token: this._token };
|
|
1511
|
+
request.successCallback = () => {
|
|
1512
|
+
this._clearUserAndToken();
|
|
1513
|
+
resolve(undefined);
|
|
1514
|
+
};
|
|
1515
|
+
request.errorCallback = (errorMsg, errorCode, response) => {
|
|
1516
|
+
if (options.handleErrors) {
|
|
1517
|
+
this.showErrorDialog(errorMsg, errorCode, response);
|
|
1518
|
+
}
|
|
1519
|
+
else {
|
|
1520
|
+
reject(new Error(errorMsg + ' ' + errorCode + ' ' + response));
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
request.finallyCallback = () => {
|
|
1524
|
+
this.dialogService.removeModalBusyState();
|
|
1525
|
+
};
|
|
1526
|
+
this.httpManager.execute(request);
|
|
1527
|
+
});
|
|
1467
1528
|
}
|
|
1468
1529
|
/**
|
|
1469
|
-
*
|
|
1530
|
+
* Aux methot to clear all the currently logged user data
|
|
1470
1531
|
*/
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
this._documentMousedownUnlisten();
|
|
1478
|
-
this._documentMousedownUnlisten = null;
|
|
1479
|
-
}
|
|
1480
|
-
if (this._documentPointerdownUnlisten !== null) {
|
|
1481
|
-
this._documentPointerdownUnlisten();
|
|
1482
|
-
this._documentMousedownUnlisten = null;
|
|
1483
|
-
}
|
|
1532
|
+
_clearUserAndToken() {
|
|
1533
|
+
this._userName = '';
|
|
1534
|
+
this._password = '';
|
|
1535
|
+
this._isLogged = false;
|
|
1536
|
+
this._token = '';
|
|
1537
|
+
this.httpManager.setGlobalPostParam('token', '-');
|
|
1484
1538
|
}
|
|
1485
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type:
|
|
1486
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type:
|
|
1539
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: TurboApiCallerService, deps: [{ token: DialogService }, { token: BrowserService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1540
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: TurboApiCallerService, providedIn: 'root' }); }
|
|
1487
1541
|
}
|
|
1488
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type:
|
|
1542
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: TurboApiCallerService, decorators: [{
|
|
1489
1543
|
type: Injectable,
|
|
1490
1544
|
args: [{
|
|
1491
1545
|
providedIn: 'root',
|
|
1492
1546
|
}]
|
|
1493
|
-
}], ctorParameters: () => [{ type:
|
|
1547
|
+
}], ctorParameters: () => [{ type: DialogService }, { type: BrowserService }] });
|
|
1494
1548
|
|
|
1495
1549
|
/**
|
|
1496
1550
|
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
@@ -1501,137 +1555,122 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImpor
|
|
|
1501
1555
|
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
1502
1556
|
*/
|
|
1503
1557
|
/**
|
|
1504
|
-
*
|
|
1558
|
+
* Global service that helps with application routing and related functionalities.
|
|
1559
|
+
* It is defined as an abstract class so it must be extended in our application to be used.
|
|
1560
|
+
* We must declare a static array of routes that will be used to define the routes of the application.
|
|
1505
1561
|
*/
|
|
1506
|
-
class
|
|
1507
|
-
constructor(
|
|
1508
|
-
|
|
1509
|
-
this.
|
|
1562
|
+
class RouterBaseService {
|
|
1563
|
+
constructor(router, titleService) {
|
|
1564
|
+
this.router = router;
|
|
1565
|
+
this.titleService = titleService;
|
|
1566
|
+
/**
|
|
1567
|
+
* Indicates whether the title manager has been initialized.
|
|
1568
|
+
* This should only be done once, typically at application startup.
|
|
1569
|
+
*/
|
|
1570
|
+
this._isTitleManagerInitialized = false;
|
|
1571
|
+
/**
|
|
1572
|
+
* BehaviorSubject that holds the current route URL.
|
|
1573
|
+
* This allows components to reactively subscribe to route changes.
|
|
1574
|
+
*/
|
|
1575
|
+
this._currentRoute = new BehaviorSubject(this.router.url);
|
|
1576
|
+
// Initial update in case the service loads after the first NavigationEnd
|
|
1577
|
+
this._updateCurrentRoute();
|
|
1510
1578
|
}
|
|
1511
1579
|
/**
|
|
1512
|
-
*
|
|
1580
|
+
* Updates the current route URL in the BehaviorSubject.
|
|
1581
|
+
* This is called internally after each navigation event.
|
|
1582
|
+
*/
|
|
1583
|
+
_updateCurrentRoute() {
|
|
1584
|
+
this._currentRoute.next(this.router.url);
|
|
1585
|
+
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Checks if the current route matches the specified route.
|
|
1513
1588
|
*
|
|
1514
|
-
*
|
|
1515
|
-
* busyState: Set it to false to prevent the default behaviour of locking the UI while the request is running
|
|
1516
|
-
* handleErrors: Set it to false to prevent the default behaviour of showing a detailed error dialog when a request fails
|
|
1589
|
+
* @param route The route to check against the current route.
|
|
1517
1590
|
*
|
|
1518
|
-
* @
|
|
1591
|
+
* @returns True if we are actually at the specified route, false otherwise.
|
|
1519
1592
|
*/
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
options.busyState = options.busyState ?? true;
|
|
1523
|
-
options.handleErrors = options.handleErrors ?? true;
|
|
1524
|
-
if (options.busyState) {
|
|
1525
|
-
this.dialogService.addModalBusyState();
|
|
1526
|
-
}
|
|
1527
|
-
super.execute(requests, (results, anyError) => {
|
|
1528
|
-
if (options.busyState) {
|
|
1529
|
-
this.dialogService.removeModalBusyState();
|
|
1530
|
-
}
|
|
1531
|
-
if (options.handleErrors && anyError) {
|
|
1532
|
-
for (let result of results) {
|
|
1533
|
-
if (result.isError) {
|
|
1534
|
-
let errorMsg = result.errorMsg + '\n\n' + result.response;
|
|
1535
|
-
if (StringUtils.isEmpty(errorMsg)) {
|
|
1536
|
-
errorMsg = 'Unknown error. Make sure Internet connection is available';
|
|
1537
|
-
}
|
|
1538
|
-
this.dialogService.addDialog(DialogErrorComponent, {
|
|
1539
|
-
width: '50vw',
|
|
1540
|
-
texts: ['Error: ' + result.code, errorMsg],
|
|
1541
|
-
options: ['Ok']
|
|
1542
|
-
});
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
if (finishedCallback !== null) {
|
|
1547
|
-
finishedCallback(results, anyError);
|
|
1548
|
-
}
|
|
1549
|
-
}, progressCallback);
|
|
1593
|
+
isRouteCurrent(route) {
|
|
1594
|
+
return this.getCurrentRoute() === '/' + route;
|
|
1550
1595
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
args: [{
|
|
1557
|
-
providedIn: 'root',
|
|
1558
|
-
}]
|
|
1559
|
-
}], ctorParameters: () => [{ type: DialogService }] });
|
|
1560
|
-
|
|
1561
|
-
/**
|
|
1562
|
-
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
1563
|
-
*
|
|
1564
|
-
* Website : -> http://www.turbogui.org
|
|
1565
|
-
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
1566
|
-
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
1567
|
-
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
1568
|
-
*/
|
|
1569
|
-
/**
|
|
1570
|
-
* Class that defines a GET http request, to be used by HttpService
|
|
1571
|
-
*/
|
|
1572
|
-
class HTTPServiceGetRequest extends HTTPManagerGetRequest {
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
/**
|
|
1576
|
-
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
1577
|
-
*
|
|
1578
|
-
* Website : -> http://www.turbogui.org
|
|
1579
|
-
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
1580
|
-
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
1581
|
-
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
1582
|
-
*/
|
|
1583
|
-
/**
|
|
1584
|
-
* Class that defines a POST http request, to be used by HttpService
|
|
1585
|
-
*/
|
|
1586
|
-
class HTTPServicePostRequest extends HTTPManagerPostRequest {
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
/**
|
|
1590
|
-
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
1591
|
-
*
|
|
1592
|
-
* Website : -> http://www.turbogui.org
|
|
1593
|
-
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
1594
|
-
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
1595
|
-
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
1596
|
-
*/
|
|
1597
|
-
/**
|
|
1598
|
-
* An abstraction of the browser entity an all its related operations and properties
|
|
1599
|
-
*/
|
|
1600
|
-
class BrowserService extends BrowserManager {
|
|
1601
|
-
constructor(location) {
|
|
1602
|
-
super();
|
|
1603
|
-
this.location = location;
|
|
1596
|
+
/**
|
|
1597
|
+
* Gets the current value of the route URL synchronously.
|
|
1598
|
+
*/
|
|
1599
|
+
getCurrentRoute() {
|
|
1600
|
+
return this._currentRoute.getValue();
|
|
1604
1601
|
}
|
|
1605
1602
|
/**
|
|
1606
|
-
*
|
|
1603
|
+
* Initializes the title management feature to automatically refresh the browser title based on the current
|
|
1604
|
+
* URL route. It Must be called once, typically at application startup
|
|
1607
1605
|
*
|
|
1608
|
-
*
|
|
1609
|
-
* @param query The query url parameters part we want to specify if any
|
|
1606
|
+
* To correctly translate the route title, We expect the route definitions to have the following properties:
|
|
1610
1607
|
*
|
|
1611
|
-
*
|
|
1608
|
+
* - titleKey: The key to be used to get the title from the translation bundle.
|
|
1609
|
+
* - titleBundle: The bundle to be used to get the title from the translation bundle.
|
|
1610
|
+
*
|
|
1611
|
+
* (Translations will be done using the LocalesService from this same library).
|
|
1612
|
+
*
|
|
1613
|
+
* Example of a Route using this feature:
|
|
1614
|
+
* // Home route
|
|
1615
|
+
* { path: '', component: HomePageComponent,
|
|
1616
|
+
* data: { titleKey: 'HOME', titleBundle: 'turbodepot/user-interface'} },
|
|
1617
|
+
*
|
|
1618
|
+
* @param prefix A text to be added before the computed title.
|
|
1619
|
+
* @param sufix A text to be added after the computed title.
|
|
1612
1620
|
*/
|
|
1613
|
-
|
|
1614
|
-
this.
|
|
1621
|
+
initializeAutoTranslateTitleByRoute(localesService, prefix = '', sufix = '') {
|
|
1622
|
+
this._localesService = localesService;
|
|
1623
|
+
if (this._isTitleManagerInitialized) {
|
|
1624
|
+
throw new Error('Title refresh from routes has already been initialized. Can only be done once.');
|
|
1625
|
+
}
|
|
1626
|
+
// Set initial title based on current route data immediately
|
|
1627
|
+
this.updateTitleFromCurrentRoute(prefix, sufix);
|
|
1628
|
+
// Subscribe to future navigation events
|
|
1629
|
+
this._routerSubscription = this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => {
|
|
1630
|
+
this._updateCurrentRoute();
|
|
1631
|
+
this.updateTitleFromCurrentRoute(prefix, sufix);
|
|
1632
|
+
});
|
|
1633
|
+
this._isTitleManagerInitialized = true;
|
|
1615
1634
|
}
|
|
1616
1635
|
/**
|
|
1617
|
-
*
|
|
1636
|
+
* Aux method: Updates the browser title based on the current route's data properties.
|
|
1637
|
+
* This is called after each navigation event to ensure the title is always up-to-date.
|
|
1618
1638
|
*
|
|
1619
|
-
* @param
|
|
1639
|
+
* @param prefix A text to be added before the computed title.
|
|
1640
|
+
* @param sufix A text to be added after the computed title.
|
|
1641
|
+
*/
|
|
1642
|
+
updateTitleFromCurrentRoute(prefix, sufix) {
|
|
1643
|
+
let currentRoute = this.router.routerState.snapshot.root;
|
|
1644
|
+
while (currentRoute.firstChild) {
|
|
1645
|
+
currentRoute = currentRoute.firstChild;
|
|
1646
|
+
}
|
|
1647
|
+
const data = currentRoute.data;
|
|
1648
|
+
if (data['titleKey'] && data['titleBundle']) {
|
|
1649
|
+
this.titleService.setTitle(prefix + this._localesService.t(data['titleKey'], data['titleBundle']) + sufix);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
/**
|
|
1653
|
+
* Navigates to the specified route.
|
|
1620
1654
|
*
|
|
1621
|
-
* @
|
|
1655
|
+
* @param route The route to navigate to.
|
|
1622
1656
|
*/
|
|
1623
|
-
|
|
1624
|
-
|
|
1657
|
+
navigateTo(route) {
|
|
1658
|
+
this.router.navigate(['/' + route]);
|
|
1625
1659
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1660
|
+
ngOnDestroy() {
|
|
1661
|
+
this._routerSubscription?.unsubscribe();
|
|
1662
|
+
// Clean up BehaviorSubject
|
|
1663
|
+
this._currentRoute.complete();
|
|
1664
|
+
}
|
|
1665
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: RouterBaseService, deps: [{ token: i1$2.Router }, { token: i2$2.Title }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1666
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: RouterBaseService, providedIn: 'root' }); }
|
|
1628
1667
|
}
|
|
1629
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type:
|
|
1668
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: RouterBaseService, decorators: [{
|
|
1630
1669
|
type: Injectable,
|
|
1631
1670
|
args: [{
|
|
1632
1671
|
providedIn: 'root',
|
|
1633
1672
|
}]
|
|
1634
|
-
}], ctorParameters: () => [{ type: i2.
|
|
1673
|
+
}], ctorParameters: () => [{ type: i1$2.Router }, { type: i2$2.Title }] });
|
|
1635
1674
|
|
|
1636
1675
|
/**
|
|
1637
1676
|
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
@@ -1642,554 +1681,513 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImpor
|
|
|
1642
1681
|
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
1643
1682
|
*/
|
|
1644
1683
|
/**
|
|
1645
|
-
*
|
|
1684
|
+
* Fully featured translation manager to be used with any application that requires text internationalization.
|
|
1685
|
+
* It is defined as an abstract class so it must be extended in our application. This way we can
|
|
1686
|
+
* write custom methods to extend the functionality of this class if needed.
|
|
1646
1687
|
*/
|
|
1647
|
-
class
|
|
1648
|
-
constructor(
|
|
1649
|
-
super(TurboApiCallerService);
|
|
1650
|
-
this.dialogService = dialogService;
|
|
1651
|
-
this.browserService = browserService;
|
|
1652
|
-
/**
|
|
1653
|
-
* URI Path to the web service that performs the user login
|
|
1654
|
-
*/
|
|
1655
|
-
this.loginServiceURI = 'users/login';
|
|
1656
|
-
/**
|
|
1657
|
-
* URI Path to the web service that allows us to create extra user tokens
|
|
1658
|
-
*/
|
|
1659
|
-
this.tokenCreationServiceURI = 'users/user-token-create';
|
|
1688
|
+
class LocalesBaseService {
|
|
1689
|
+
constructor() {
|
|
1660
1690
|
/**
|
|
1661
|
-
*
|
|
1691
|
+
* if the class has been correctly initialized and translations have been correctly loaded
|
|
1662
1692
|
*/
|
|
1663
|
-
this.
|
|
1693
|
+
this._isInitialized = false;
|
|
1664
1694
|
/**
|
|
1665
|
-
*
|
|
1695
|
+
* @see getLocales()
|
|
1666
1696
|
*/
|
|
1667
|
-
this.
|
|
1697
|
+
this._locales = [];
|
|
1668
1698
|
/**
|
|
1669
|
-
*
|
|
1699
|
+
* @see getLanguages()
|
|
1670
1700
|
*/
|
|
1671
|
-
this.
|
|
1701
|
+
this._languages = [];
|
|
1672
1702
|
/**
|
|
1673
|
-
*
|
|
1703
|
+
* Stores all the loaded localization data by library name, bundle name, key and locales
|
|
1674
1704
|
*/
|
|
1675
|
-
this.
|
|
1705
|
+
this._loadedTranslations = {};
|
|
1676
1706
|
/**
|
|
1677
|
-
*
|
|
1707
|
+
* Stores a memory cache to improve performance when outputing translations
|
|
1678
1708
|
*/
|
|
1679
|
-
this.
|
|
1709
|
+
this._keyValuesCache = {};
|
|
1680
1710
|
/**
|
|
1681
|
-
*
|
|
1711
|
+
* @see setWildCardsFormat()
|
|
1682
1712
|
*/
|
|
1683
|
-
this.
|
|
1713
|
+
this._wildCardsFormat = '{N}';
|
|
1684
1714
|
/**
|
|
1685
|
-
* @see
|
|
1715
|
+
* @see setMissingKeyFormat()
|
|
1686
1716
|
*/
|
|
1687
|
-
this.
|
|
1717
|
+
this._missingKeyFormat = '$exception';
|
|
1688
1718
|
/**
|
|
1689
|
-
*
|
|
1719
|
+
* Stores a hash value that is used to improve the performance for translation t() methods.
|
|
1720
|
+
* This is computed based on _wildCardsFormat plus _missingKeyFormat plus the current primary locale
|
|
1721
|
+
* Methods that change these values will recalculate the hash string, so when calling translation methods, the
|
|
1722
|
+
* performance will be as fast as possible.
|
|
1690
1723
|
*/
|
|
1691
|
-
this.
|
|
1692
|
-
// Create a fresh instance of the http service so we can use it independently
|
|
1693
|
-
this.httpManager = new HTTPManager();
|
|
1694
|
-
this._clearUserAndToken();
|
|
1695
|
-
}
|
|
1696
|
-
/**
|
|
1697
|
-
* The username that is currently defined and will be used by the login methods
|
|
1698
|
-
*/
|
|
1699
|
-
set userName(v) {
|
|
1700
|
-
this._isLogged = false;
|
|
1701
|
-
this._token = '';
|
|
1702
|
-
this._userName = v;
|
|
1703
|
-
}
|
|
1704
|
-
/**
|
|
1705
|
-
* The username that is currently defined and will be used by the login methods
|
|
1706
|
-
*/
|
|
1707
|
-
get userName() {
|
|
1708
|
-
return this._userName;
|
|
1709
|
-
}
|
|
1710
|
-
/**
|
|
1711
|
-
* The password for the user that is currently defined and will be used by the login methods
|
|
1712
|
-
*/
|
|
1713
|
-
set password(v) {
|
|
1714
|
-
this._isLogged = false;
|
|
1715
|
-
this._token = '';
|
|
1716
|
-
this._password = v;
|
|
1724
|
+
this._cacheHashBaseString = '';
|
|
1717
1725
|
}
|
|
1718
1726
|
/**
|
|
1719
|
-
*
|
|
1727
|
+
* Wildcards are string fragments that are placed inside the translated texts. Their main purpose is to be replaced at
|
|
1728
|
+
* runtime by custom values like for example a user name, a date, a numeric value, etc..
|
|
1729
|
+
*
|
|
1730
|
+
* This class helps with this process by including a parameter called 'toReplace' on all ->t methods which allows us
|
|
1731
|
+
* to specify a string or list of strings that will replace the respective wildcards on the translated text. Each wildcard
|
|
1732
|
+
* must follow the format specified here, and contain a numeric digit that will be used to find the replacement text at the
|
|
1733
|
+
* 'toReplace' list. For example, if we define $N as the wildcard format, and we have a translation that contains $0, $1, $2,
|
|
1734
|
+
* $0 will be replaced with the first element on toReplace, $1 with the second and so.
|
|
1735
|
+
*
|
|
1736
|
+
* We usually set this before initializing the class translation data
|
|
1737
|
+
*
|
|
1738
|
+
* Notice that N is mandayory on the wildcards format and the first index value is 0.
|
|
1739
|
+
*
|
|
1740
|
+
* @param value The wildcards format we want to set
|
|
1741
|
+
*
|
|
1742
|
+
* @returns The value that's been set
|
|
1720
1743
|
*/
|
|
1721
|
-
|
|
1722
|
-
|
|
1744
|
+
setWildCardsFormat(value) {
|
|
1745
|
+
if (!value.includes('N')) {
|
|
1746
|
+
throw new Error("N is mandatory to replace wildcards");
|
|
1747
|
+
}
|
|
1748
|
+
this._cacheHashBaseString = value + this._missingKeyFormat + ((this._locales.length > 0) ? this._locales[0] : '');
|
|
1749
|
+
this._wildCardsFormat = value;
|
|
1750
|
+
return value;
|
|
1723
1751
|
}
|
|
1724
1752
|
/**
|
|
1725
|
-
*
|
|
1753
|
+
* Defines the behaviour for t(), tStartCase(), etc... methods when a key is not found on
|
|
1754
|
+
* a bundle or the bundle does not exist
|
|
1755
|
+
*
|
|
1756
|
+
* If missingKeyFormat is an empty string, all missing keys will return an empty value (not recommended)
|
|
1757
|
+
*
|
|
1758
|
+
* If missingKeyFormat contains a string, that string will be always returned for missing keys
|
|
1759
|
+
*
|
|
1760
|
+
* If missingKeyFormat contains a string with one of the following predefined wildcards:<br>
|
|
1761
|
+
* - $key will be replaced with key name. Example: get("NAME") will output [NAME] if key is not found and missingKeyFormat = '[$key]'<br>
|
|
1762
|
+
* - $exception (default value) will throw an exception with the problem cause description.
|
|
1763
|
+
*
|
|
1764
|
+
* @param value The missing key format we want to set
|
|
1765
|
+
*
|
|
1766
|
+
* @returns The value that's been set
|
|
1726
1767
|
*/
|
|
1727
|
-
|
|
1728
|
-
this.
|
|
1768
|
+
setMissingKeyFormat(value) {
|
|
1769
|
+
this._cacheHashBaseString = this._wildCardsFormat + value + ((this._locales.length > 0) ? this._locales[0] : '');
|
|
1770
|
+
this._missingKeyFormat = value;
|
|
1771
|
+
return value;
|
|
1729
1772
|
}
|
|
1730
1773
|
/**
|
|
1731
|
-
*
|
|
1774
|
+
* @see setMissingKeyFormat()
|
|
1732
1775
|
*/
|
|
1733
|
-
|
|
1734
|
-
return this.
|
|
1776
|
+
getMissingKeyFormat() {
|
|
1777
|
+
return this._missingKeyFormat;
|
|
1735
1778
|
}
|
|
1736
1779
|
/**
|
|
1737
|
-
*
|
|
1738
|
-
*
|
|
1780
|
+
* Initializes the translation system by loading and parsing bundle files from the specified JSON object.
|
|
1781
|
+
* After the method finishes, the class will contain all the translation data and will be ready to translate any provided key.
|
|
1739
1782
|
*
|
|
1740
|
-
*
|
|
1783
|
+
* @param translations A JSON object containing the translation data. The structure must be as follows:
|
|
1784
|
+
* { library_name: { bundle_name: { locale_code: { key1: "translation1", key2: "translation2" } } } ... }
|
|
1741
1785
|
*
|
|
1742
|
-
* @
|
|
1786
|
+
* @param locales An array of locale codes (e.g., ['en_US', 'es_ES', 'fr_FR']) to load into this class. The order of this array
|
|
1787
|
+
* will determine the translation priority
|
|
1788
|
+
*
|
|
1789
|
+
* @return True if the translations get correctly loaded. Any unsuccessful initialization will throw an exception
|
|
1743
1790
|
*/
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1791
|
+
initializeFromJson(translations, locales) {
|
|
1792
|
+
this._isInitialized = false;
|
|
1793
|
+
// Validate received locales are correct
|
|
1794
|
+
for (const locale of locales) {
|
|
1795
|
+
this._validateLocaleString(locale);
|
|
1748
1796
|
}
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1797
|
+
// Validate the translations object follows the right structure
|
|
1798
|
+
let isTranslationsValid = false;
|
|
1799
|
+
for (const library in translations) {
|
|
1800
|
+
for (const bundle in translations[library]) {
|
|
1801
|
+
for (const locale in translations[library][bundle]) {
|
|
1802
|
+
this._validateLocaleString(locale);
|
|
1803
|
+
isTranslationsValid = true;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1757
1806
|
}
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
valuesFound = true;
|
|
1761
|
-
this.password = ConversionUtils.base64ToString(hashData[3]);
|
|
1807
|
+
if (!isTranslationsValid) {
|
|
1808
|
+
throw new Error('translations must be a non empty object with the structure: { library: { bundle: { xx_XX: { key: translation } } } }');
|
|
1762
1809
|
}
|
|
1763
|
-
|
|
1810
|
+
this._loadedTranslations = translations;
|
|
1811
|
+
this._locales = locales;
|
|
1812
|
+
this._languages = locales.map((l) => l.substring(0, 2));
|
|
1813
|
+
this._cacheHashBaseString = this._wildCardsFormat + this._missingKeyFormat + this._locales[0];
|
|
1814
|
+
return this._isInitialized = true;
|
|
1764
1815
|
}
|
|
1765
1816
|
/**
|
|
1766
|
-
*
|
|
1767
|
-
*
|
|
1817
|
+
* Initializes the translation system by loading and parsing bundle files from the specified translations path.
|
|
1818
|
+
* After the promise finishes, the class will contain all the translation data and will be ready to translate any
|
|
1819
|
+
* provided key.
|
|
1768
1820
|
*
|
|
1769
|
-
*
|
|
1821
|
+
* @param translationsPath - Url where the translations Json structure of libraries/bundles/locales/keys is available.
|
|
1822
|
+
* @param locales An array of locale codes (e.g., ['en_US', 'es_ES', 'fr_FR']) to load. These will be added to the translation
|
|
1823
|
+
* path using the following format: translationsPath/en_US-es_ES-fr_FR. The order of this array will determine the
|
|
1824
|
+
* translation priority
|
|
1825
|
+
* @param parameters Any extra parameters to be attached to the translationsPath after the locales like /param1/param2/ etc
|
|
1770
1826
|
*
|
|
1771
|
-
* @
|
|
1772
|
-
* or rejects with an error if the verification fails. Response will have 4 possible values:
|
|
1773
|
-
* undefined - meaning that the URL hash fragment didn't contain valid verification values
|
|
1774
|
-
* -1 - meaning verification failed
|
|
1775
|
-
* 0 - meaning verification succeeded
|
|
1776
|
-
* 1 - meaning the email was already previouly verified
|
|
1827
|
+
* @return A promise that will resolve if the translations get correctly loaded, or reject with an error if load fails
|
|
1777
1828
|
*/
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
let mail = ConversionUtils.base64ToString(hashData[1]);
|
|
1786
|
-
let hash = ConversionUtils.base64ToString(hashData[2]);
|
|
1787
|
-
if (!StringUtils.isEmpty(user) && !StringUtils.isEmpty(mail) && !StringUtils.isEmpty(hash)) {
|
|
1788
|
-
this._lastVerifiedMail = mail;
|
|
1789
|
-
return this.call(this.mailVerifyServiceURI, { userName: user, mail: mail, hash: hash });
|
|
1829
|
+
initializeFromUrl(translationsPath, locales, parameters) {
|
|
1830
|
+
this._isInitialized = false;
|
|
1831
|
+
const translationsFullPath = translationsPath + '/' + locales.join('-') + '/' + parameters.join('/');
|
|
1832
|
+
return new Promise((resolve, reject) => {
|
|
1833
|
+
fetch(translationsFullPath).then(response => {
|
|
1834
|
+
if (!response.ok) {
|
|
1835
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
1790
1836
|
}
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1837
|
+
return response.json();
|
|
1838
|
+
}).then(data => {
|
|
1839
|
+
this.initializeFromJson(data, locales);
|
|
1840
|
+
resolve(undefined);
|
|
1841
|
+
}).catch(error => {
|
|
1842
|
+
reject(new Error(`ERROR LOADING LOCALES FROM: ${translationsFullPath}\n` + error));
|
|
1843
|
+
});
|
|
1844
|
+
});
|
|
1794
1845
|
}
|
|
1795
1846
|
/**
|
|
1796
|
-
*
|
|
1847
|
+
* Check if the class has been correctly initialized and translations have been correctly loaded
|
|
1797
1848
|
*/
|
|
1798
|
-
|
|
1799
|
-
return this.
|
|
1849
|
+
isInitialized() {
|
|
1850
|
+
return this._isInitialized;
|
|
1800
1851
|
}
|
|
1801
1852
|
/**
|
|
1802
|
-
*
|
|
1803
|
-
* Returns a promise that resolves with the server's response or rejects with an error if the login fails.
|
|
1804
|
-
* Path to the login service must be correctly defined at this.loginWebService
|
|
1805
|
-
*
|
|
1806
|
-
* The authentication process is performed by sending an encoded credentials request to the login web service using the
|
|
1807
|
-
* currently defined user name and psw.
|
|
1808
|
-
*
|
|
1809
|
-
* Here's an example of a call:
|
|
1810
|
-
*
|
|
1811
|
-
* login().then(response => {
|
|
1812
|
-
* console.log('Login successful:', response);
|
|
1813
|
-
* }).catch(() => {
|
|
1814
|
-
* console.error('Login failed:');
|
|
1815
|
-
* });
|
|
1816
|
-
*
|
|
1817
|
-
* @returns A promise that resolves with the server response if the login is successful,
|
|
1818
|
-
* or rejects with an error if the login fails.
|
|
1853
|
+
* Aux method to verify that this class is correctly initialized with translation data
|
|
1819
1854
|
*/
|
|
1820
|
-
|
|
1821
|
-
this.
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
request.ignoreGlobalPostParams = true;
|
|
1825
|
-
const encodedCredentials = ConversionUtils.stringToBase64(ConversionUtils.stringToBase64(this._userName) + ',' + ConversionUtils.stringToBase64(this._password));
|
|
1826
|
-
request.parameters = { data: encodedCredentials };
|
|
1827
|
-
request.successCallback = (response) => {
|
|
1828
|
-
if (response !== '') {
|
|
1829
|
-
response = JSON.parse(response);
|
|
1830
|
-
this._isLogged = true;
|
|
1831
|
-
this._token = response.token;
|
|
1832
|
-
this._operations = response.operations;
|
|
1833
|
-
this.httpManager.setGlobalPostParam('token', response.token);
|
|
1834
|
-
resolve(response);
|
|
1835
|
-
}
|
|
1836
|
-
else {
|
|
1837
|
-
this._clearUserAndToken();
|
|
1838
|
-
reject(new Error());
|
|
1839
|
-
}
|
|
1840
|
-
};
|
|
1841
|
-
request.errorCallback = () => {
|
|
1842
|
-
this._clearUserAndToken();
|
|
1843
|
-
reject(new Error());
|
|
1844
|
-
};
|
|
1845
|
-
request.finallyCallback = () => {
|
|
1846
|
-
this.dialogService.removeModalBusyState();
|
|
1847
|
-
};
|
|
1848
|
-
this.httpManager.execute(request);
|
|
1849
|
-
});
|
|
1855
|
+
_validateInitialized() {
|
|
1856
|
+
if (!this._isInitialized) {
|
|
1857
|
+
throw new Error('Translation service not initialized');
|
|
1858
|
+
}
|
|
1850
1859
|
}
|
|
1851
1860
|
/**
|
|
1852
|
-
*
|
|
1861
|
+
* Checks if the specified locale is currently loaded for the currently defined bundles and paths.
|
|
1853
1862
|
*
|
|
1854
|
-
* @param
|
|
1855
|
-
* refer to the turbodepot UsersManager class createToken method documentation
|
|
1863
|
+
* @param locale A locale to check. For example 'en_US'
|
|
1856
1864
|
*
|
|
1857
|
-
* @
|
|
1858
|
-
* with an error (containing all the error response details) if the request fails.
|
|
1865
|
+
* @return True if the locale is currently loaded on the class, false if not.
|
|
1859
1866
|
*/
|
|
1860
|
-
|
|
1861
|
-
|
|
1867
|
+
isLocaleLoaded(locale) {
|
|
1868
|
+
this._validateLocaleString(locale);
|
|
1869
|
+
return this._locales.includes(locale);
|
|
1862
1870
|
}
|
|
1863
1871
|
/**
|
|
1864
|
-
*
|
|
1865
|
-
*
|
|
1872
|
+
* Aux method to validate that a locale string is correctly formatted
|
|
1873
|
+
*
|
|
1874
|
+
* @param string $locale A locale string
|
|
1866
1875
|
*/
|
|
1867
|
-
|
|
1868
|
-
|
|
1876
|
+
_validateLocaleString(locale) {
|
|
1877
|
+
if (!/^[a-z]{2}_[A-Z]{2}$/.test(locale)) {
|
|
1878
|
+
throw new Error('locale must be a valid xx_XX value');
|
|
1879
|
+
}
|
|
1869
1880
|
}
|
|
1870
1881
|
/**
|
|
1871
|
-
*
|
|
1872
|
-
*
|
|
1882
|
+
* Checks if the specified 2 digit language is currently loaded for the currently defined bundles and paths.
|
|
1883
|
+
*
|
|
1884
|
+
* @param language A language to check. For example 'en'
|
|
1885
|
+
*
|
|
1886
|
+
* @return True if the language is currently loaded on the class, false if not.
|
|
1873
1887
|
*/
|
|
1874
|
-
|
|
1875
|
-
|
|
1888
|
+
isLanguageLoaded(language) {
|
|
1889
|
+
this._validateLanguageString(language);
|
|
1890
|
+
return this._languages.includes(language);
|
|
1876
1891
|
}
|
|
1877
1892
|
/**
|
|
1878
|
-
*
|
|
1893
|
+
* Aux method to validate that a language string is correctly formatted
|
|
1894
|
+
*
|
|
1895
|
+
* @param language A 2 digit language string
|
|
1879
1896
|
*/
|
|
1880
|
-
|
|
1881
|
-
|
|
1897
|
+
_validateLanguageString(language) {
|
|
1898
|
+
if (!/^[a-z]{2}$/.test(language)) {
|
|
1899
|
+
throw new Error('language must be a valid 2 digit value');
|
|
1900
|
+
}
|
|
1882
1901
|
}
|
|
1883
1902
|
/**
|
|
1884
|
-
*
|
|
1885
|
-
* This will check the user permisions as defined on server side and return true if the operation is allowed
|
|
1886
|
-
* or false if not. We can then decide what to do (hide an app section, block a button, etc)
|
|
1903
|
+
* Get the translation to the current primary locale for the given key, library and bundle
|
|
1887
1904
|
*
|
|
1888
|
-
* @param
|
|
1905
|
+
* @param string key The key we want to read from the specified resource bundle
|
|
1906
|
+
* @param string bundlePath A string with the format 'library_name/bundle_name' that is used to locate the bundle were the key to translate is found
|
|
1907
|
+
* @param array replaceWildcards A list of values that will replace wildcards that may be found on the translated text. Each wildcard
|
|
1908
|
+
* will be replaced with the element whose index on replaceWildcards matches it. Check the documentation for this.wildCardsFormat
|
|
1909
|
+
* property to know more about how to setup wildcards on your translations.
|
|
1889
1910
|
*
|
|
1890
|
-
* @
|
|
1911
|
+
* @see setWildCardsFormat()
|
|
1912
|
+
*
|
|
1913
|
+
* @return The translated text
|
|
1891
1914
|
*/
|
|
1892
|
-
|
|
1893
|
-
|
|
1915
|
+
t(key, bundlePath, replaceWildcards = []) {
|
|
1916
|
+
this._validateInitialized();
|
|
1917
|
+
// Create a cache key to improve performance when requesting the same key translation several times
|
|
1918
|
+
const cacheKey = `${this._cacheHashBaseString}${key}${bundlePath}${replaceWildcards.join('')}`;
|
|
1919
|
+
if (!this._keyValuesCache[cacheKey]) {
|
|
1920
|
+
this._forceNonEmptyString(key, '', 'key must be non empty string');
|
|
1921
|
+
this._forceNonEmptyString(bundlePath, '', 'bundlePath must be non empty string');
|
|
1922
|
+
const [library, bundle] = bundlePath.split('/');
|
|
1923
|
+
this._forceNonEmptyString(library, '', 'no library specified on bundlePath');
|
|
1924
|
+
this._forceNonEmptyString(bundle, '', 'no bundle specified on bundlePath');
|
|
1925
|
+
const replacementsCount = replaceWildcards.length;
|
|
1926
|
+
// Loop all the locales to find the first one with a value for the specified key
|
|
1927
|
+
for (const locale of this._locales) {
|
|
1928
|
+
if (this._loadedTranslations[library]?.[bundle]?.[locale]?.[key]) {
|
|
1929
|
+
let result = this._loadedTranslations[library][bundle][locale][key];
|
|
1930
|
+
// Replace all wildcards on the text with the specified replacements if any
|
|
1931
|
+
for (let i = 0; i < replacementsCount; i++) {
|
|
1932
|
+
result = this._replace(result, this._replace(this._wildCardsFormat, 'N', i.toString()), replaceWildcards[i]);
|
|
1933
|
+
}
|
|
1934
|
+
this._keyValuesCache[cacheKey] = result;
|
|
1935
|
+
return result;
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
// Check if an exception needs to be thrown if the specified key is not found on this bundle
|
|
1939
|
+
if (this._missingKeyFormat.includes('$exception')) {
|
|
1940
|
+
throw new Error(`Translation key <${key}> not found on <${bundlePath}>`);
|
|
1941
|
+
}
|
|
1942
|
+
this._keyValuesCache[cacheKey] = this._replace(this._missingKeyFormat, '$key', key);
|
|
1943
|
+
}
|
|
1944
|
+
return this._keyValuesCache[cacheKey];
|
|
1894
1945
|
}
|
|
1895
1946
|
/**
|
|
1896
|
-
*
|
|
1897
|
-
*
|
|
1898
|
-
* Following is an example of a call:
|
|
1899
|
-
*
|
|
1900
|
-
* this.apiService.call('users/user-create', this.userData, {handleErrors: false}).then(response => {
|
|
1901
|
-
*
|
|
1902
|
-
* console.log('Success:', response);
|
|
1903
|
-
*
|
|
1904
|
-
* }).catch(error => {
|
|
1905
|
-
*
|
|
1906
|
-
* console.error('Error:', error.message);
|
|
1907
|
-
* });
|
|
1947
|
+
* Get the translation for the given key and bundle as a string with all words first character capitalized
|
|
1948
|
+
* and all the rest of the word with lower case
|
|
1908
1949
|
*
|
|
1909
|
-
* @
|
|
1910
|
-
* For example: 'users/login'
|
|
1911
|
-
* @param parameters - An object containing key-value pairs that are sent as the request body.
|
|
1912
|
-
* token parameter is not necessary, it is automatically appended
|
|
1913
|
-
* @param options An object defining several options for the request:
|
|
1914
|
-
* resultFormat: 'JSON' by default. The expected format of the response.
|
|
1915
|
-
* busyState: Enabled by default. Enables or disables the busy state to lock user interaction while performing the http calls.
|
|
1916
|
-
* handleErrors: Enabled by default. If set to true, an error dialog will be automatically shown when the call fails.
|
|
1917
|
-
* If set to false, the promise will generate a reject error that must be handled by our code.
|
|
1950
|
+
* @see t()
|
|
1918
1951
|
*
|
|
1919
|
-
* @returns
|
|
1920
|
-
* with an error (containing all the error response details) if the request fails.
|
|
1952
|
+
* @returns The localized and case formatted text
|
|
1921
1953
|
*/
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
options.busyState = options.busyState ?? true;
|
|
1926
|
-
options.handleErrors = options.handleErrors ?? true;
|
|
1927
|
-
if (options.busyState) {
|
|
1928
|
-
this.dialogService.addModalBusyState();
|
|
1929
|
-
}
|
|
1930
|
-
return new Promise((resolve, reject) => {
|
|
1931
|
-
// Create the request object
|
|
1932
|
-
const request = new HTTPManagerPostRequest(apiPath, options.resultFormat === 'STRING' ? HTTPManagerPostRequest.STRING : HTTPManagerPostRequest.JSON);
|
|
1933
|
-
request.parameters = parameters;
|
|
1934
|
-
request.successCallback = (response) => {
|
|
1935
|
-
resolve(response);
|
|
1936
|
-
};
|
|
1937
|
-
request.errorCallback = (errorMsg, errorCode, response) => {
|
|
1938
|
-
if (options.handleErrors) {
|
|
1939
|
-
this.showErrorDialog(errorMsg, errorCode, response);
|
|
1940
|
-
}
|
|
1941
|
-
else {
|
|
1942
|
-
reject(new Error(errorMsg + ' ' + errorCode + ' ' + response));
|
|
1943
|
-
}
|
|
1944
|
-
};
|
|
1945
|
-
request.finallyCallback = () => {
|
|
1946
|
-
if (options.busyState) {
|
|
1947
|
-
this.dialogService.removeModalBusyState();
|
|
1948
|
-
}
|
|
1949
|
-
};
|
|
1950
|
-
this.httpManager.execute(request);
|
|
1951
|
-
});
|
|
1954
|
+
tStartCase(key, bundlePath, replaceWildcards = []) {
|
|
1955
|
+
return this.t(key, bundlePath, replaceWildcards).split(' ')
|
|
1956
|
+
.map((word) => word ? word[0].toUpperCase() + word.slice(1).toLowerCase() : '').join(' ');
|
|
1952
1957
|
}
|
|
1953
1958
|
/**
|
|
1954
|
-
*
|
|
1955
|
-
* Returns a promise that resolves with an array of response objects. One for each of the request calls.
|
|
1959
|
+
* Get the translation for the given key and bundle as an all upper case string
|
|
1956
1960
|
*
|
|
1957
|
-
* @
|
|
1958
|
-
* For example: 'turbosite/chain/chain-services'
|
|
1959
|
-
* @param services - An array of objects, were each object contains a valid structure for the chain services call.
|
|
1960
|
-
* Token parameter is not necessary, it is automatically appended
|
|
1961
|
-
* @param options An object defining several options for the request:
|
|
1962
|
-
* busyState: Enables or disables the busy state to lock user interaction while performing the http calls. Enabled by default
|
|
1963
|
-
* handleErrors: Enabled by default. If set to true, an error dialog will be automatically shown when the call fails.
|
|
1964
|
-
* If set to false, the promise will generate a reject error that must be handled by our code.
|
|
1961
|
+
* @see t()
|
|
1965
1962
|
*
|
|
1966
|
-
* @returns
|
|
1967
|
-
* with an error (containing all the error response details) if the request fails.
|
|
1963
|
+
* @returns The localized and case formatted text
|
|
1968
1964
|
*/
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
options.busyState = options.busyState ?? true;
|
|
1972
|
-
options.handleErrors = options.handleErrors ?? true;
|
|
1973
|
-
if (options.busyState) {
|
|
1974
|
-
this.dialogService.addModalBusyState();
|
|
1975
|
-
}
|
|
1976
|
-
return new Promise((resolve, reject) => {
|
|
1977
|
-
const request = new HTTPManagerPostRequest(apiPath, HTTPManagerPostRequest.JSON);
|
|
1978
|
-
request.ignoreGlobalPostParams = true;
|
|
1979
|
-
request.parameters = { services: services };
|
|
1980
|
-
request.successCallback = (response) => {
|
|
1981
|
-
resolve(response);
|
|
1982
|
-
};
|
|
1983
|
-
request.errorCallback = (errorMsg, errorCode, response) => {
|
|
1984
|
-
if (options.handleErrors) {
|
|
1985
|
-
this.showErrorDialog(errorMsg, errorCode, response);
|
|
1986
|
-
}
|
|
1987
|
-
else {
|
|
1988
|
-
reject(new Error(errorMsg + ' ' + errorCode + ' ' + response));
|
|
1989
|
-
}
|
|
1990
|
-
};
|
|
1991
|
-
request.finallyCallback = () => {
|
|
1992
|
-
if (options.busyState) {
|
|
1993
|
-
this.dialogService.removeModalBusyState();
|
|
1994
|
-
}
|
|
1995
|
-
};
|
|
1996
|
-
this.httpManager.execute(request);
|
|
1997
|
-
});
|
|
1965
|
+
tAllUpperCase(key, bundlePath, replaceWildcards = []) {
|
|
1966
|
+
return this.t(key, bundlePath, replaceWildcards).toUpperCase();
|
|
1998
1967
|
}
|
|
1999
1968
|
/**
|
|
2000
|
-
*
|
|
1969
|
+
* Get the translation for the given key and bundle as an all lower case string
|
|
1970
|
+
*
|
|
1971
|
+
* @see t()
|
|
1972
|
+
*
|
|
1973
|
+
* @returns The localized and case formatted text
|
|
2001
1974
|
*/
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
if (StringUtils.isEmpty(errorMsg)) {
|
|
2005
|
-
errorMsg = 'Unknown error. Make sure Internet connection is available';
|
|
2006
|
-
}
|
|
2007
|
-
this.dialogService.addDialog(DialogErrorComponent, {
|
|
2008
|
-
width: '50vw',
|
|
2009
|
-
texts: ['Error: ' + errorCode, errorMsg],
|
|
2010
|
-
options: ['Ok']
|
|
2011
|
-
});
|
|
1975
|
+
tAllLowerCase(key, bundlePath, replaceWildcards = []) {
|
|
1976
|
+
return this.t(key, bundlePath, replaceWildcards).toLowerCase();
|
|
2012
1977
|
}
|
|
2013
1978
|
/**
|
|
2014
|
-
*
|
|
1979
|
+
* Get the translation for the given key and bundle as a string with the first character as Upper case
|
|
1980
|
+
* and all the rest as lower case
|
|
2015
1981
|
*
|
|
2016
|
-
* @
|
|
2017
|
-
* handleErrors: Enabled by default. If set to true, an error dialog will be automatically shown when the call fails.
|
|
2018
|
-
* If set to false, the promise will generate a reject error that must be handled by our code.
|
|
1982
|
+
* @see t()
|
|
2019
1983
|
*
|
|
2020
|
-
* @returns
|
|
2021
|
-
* response details) if the request fails.
|
|
1984
|
+
* @returns The localized and case formatted text
|
|
2022
1985
|
*/
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
request.finallyCallback = () => {
|
|
2043
|
-
this.dialogService.removeModalBusyState();
|
|
2044
|
-
};
|
|
2045
|
-
this.httpManager.execute(request);
|
|
2046
|
-
});
|
|
1986
|
+
tFirstUpperRestLower(key, bundlePath, replaceWildcards = []) {
|
|
1987
|
+
const string = this.t(key, bundlePath, replaceWildcards);
|
|
1988
|
+
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
|
|
1989
|
+
}
|
|
1990
|
+
/**
|
|
1991
|
+
* A list of strings containing the locales that are used by this class to translate the given keys, sorted by preference.
|
|
1992
|
+
* Each string is formatted as a standard locale code with language and country joined by an underscore, like: en_US, fr_FR
|
|
1993
|
+
*
|
|
1994
|
+
* When a key and bundle are requested for translation, the class will check on the first language of this
|
|
1995
|
+
* list for a translated text. If missing, the next one will be used, and so. This list is constructed after initialize
|
|
1996
|
+
* methods is called.
|
|
1997
|
+
*
|
|
1998
|
+
* @example: After loading the following list of locales ['en_US', 'es_ES', 'fr_FR'] if we call t('HELLO', 'lib1/greetings')
|
|
1999
|
+
* the localization manager will try to locate the en_US value for the HELLO tag on the greetings bundle for the library lib1.
|
|
2000
|
+
* If the tag is not found for the specified locale and bundle, the same search will be performed for the es_ES locale, and so, till a
|
|
2001
|
+
* value is found or no more locales are defined.
|
|
2002
|
+
*/
|
|
2003
|
+
getLocales() {
|
|
2004
|
+
return this._locales;
|
|
2047
2005
|
}
|
|
2048
2006
|
/**
|
|
2049
|
-
*
|
|
2007
|
+
* A list of strings containing the languages that are used by this class to translate the given keys, sorted by preference.
|
|
2008
|
+
* Each string is formatted as a 2 digit language code, like: en, fr
|
|
2009
|
+
*
|
|
2010
|
+
* This list is the same as the locales() one, but containing only the language part of each locale (the first two digits)
|
|
2011
|
+
*
|
|
2012
|
+
* @see getLocales()
|
|
2050
2013
|
*/
|
|
2051
|
-
|
|
2052
|
-
this.
|
|
2053
|
-
this._password = '';
|
|
2054
|
-
this._isLogged = false;
|
|
2055
|
-
this._token = '';
|
|
2056
|
-
this.httpManager.setGlobalPostParam('token', '-');
|
|
2014
|
+
getLanguages() {
|
|
2015
|
+
return this._languages;
|
|
2057
2016
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
}], ctorParameters: () => [{ type: DialogService }, { type: BrowserService }] });
|
|
2067
|
-
|
|
2068
|
-
/**
|
|
2069
|
-
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
2070
|
-
*
|
|
2071
|
-
* Website : -> http://www.turbogui.org
|
|
2072
|
-
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
2073
|
-
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
2074
|
-
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
2075
|
-
*/
|
|
2076
|
-
/**
|
|
2077
|
-
* Global service that helps with application routing and related functionalities.
|
|
2078
|
-
* It is defined as an abstract class so it must be extended in our application to be used.
|
|
2079
|
-
* We must declare a static array of routes that will be used to define the routes of the application.
|
|
2080
|
-
*/
|
|
2081
|
-
class RouterBaseService {
|
|
2082
|
-
constructor(router, titleService, ls) {
|
|
2083
|
-
this.router = router;
|
|
2084
|
-
this.titleService = titleService;
|
|
2085
|
-
this.ls = ls;
|
|
2086
|
-
/**
|
|
2087
|
-
* Indicates whether the title manager has been initialized.
|
|
2088
|
-
* This should only be done once, typically at application startup.
|
|
2089
|
-
*/
|
|
2090
|
-
this._isTitleManagerInitialized = false;
|
|
2091
|
-
/**
|
|
2092
|
-
* BehaviorSubject that holds the current route URL.
|
|
2093
|
-
* This allows components to reactively subscribe to route changes.
|
|
2094
|
-
*/
|
|
2095
|
-
this._currentRoute = new BehaviorSubject(this.router.url);
|
|
2096
|
-
// Initial update in case the service loads after the first NavigationEnd
|
|
2097
|
-
this._updateCurrentRoute();
|
|
2017
|
+
/**
|
|
2018
|
+
* Get the first locale from the list of loaded locales, which is the currently used to search for translated texts.
|
|
2019
|
+
*
|
|
2020
|
+
* @returns The locale that is defined as the primary one. For example: en_US, es_ES, ..
|
|
2021
|
+
*/
|
|
2022
|
+
getPrimaryLocale() {
|
|
2023
|
+
this._validateInitialized();
|
|
2024
|
+
return this._locales[0];
|
|
2098
2025
|
}
|
|
2099
2026
|
/**
|
|
2100
|
-
*
|
|
2101
|
-
*
|
|
2027
|
+
* Get the first language from the list of loaded locales, which is the currently used to search for translated texts.
|
|
2028
|
+
*
|
|
2029
|
+
* @returns The 2 digit language code that is defined as the primary one. For example: en, es, ..
|
|
2102
2030
|
*/
|
|
2103
|
-
|
|
2104
|
-
this.
|
|
2031
|
+
getPrimaryLanguage() {
|
|
2032
|
+
this._validateInitialized();
|
|
2033
|
+
return this._languages[0];
|
|
2105
2034
|
}
|
|
2106
2035
|
/**
|
|
2107
|
-
*
|
|
2036
|
+
* Define the locale that will be placed at the front of the currently loaded locales list (moving all the others one position to the right).
|
|
2108
2037
|
*
|
|
2109
|
-
*
|
|
2038
|
+
* This will be the first locale to use when trying to get a translation.
|
|
2110
2039
|
*
|
|
2111
|
-
* @
|
|
2040
|
+
* @param locale A currently loaded locale that will be moved to the first position of the loaded locales list. If the specified locale
|
|
2041
|
+
* is not currently loaded, an exception will happen.
|
|
2042
|
+
*
|
|
2043
|
+
* @returns void
|
|
2112
2044
|
*/
|
|
2113
|
-
|
|
2114
|
-
|
|
2045
|
+
setPrimaryLocale(locale) {
|
|
2046
|
+
this._validateInitialized();
|
|
2047
|
+
if (!this.isLocaleLoaded(locale)) {
|
|
2048
|
+
throw new Error(locale + ' not loaded');
|
|
2049
|
+
}
|
|
2050
|
+
let result = [locale];
|
|
2051
|
+
for (let l of this._locales) {
|
|
2052
|
+
if (l !== locale) {
|
|
2053
|
+
result.push(l);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
this._locales = result;
|
|
2057
|
+
this._languages = this._locales.map((l) => l.substring(0, 2));
|
|
2058
|
+
this._cacheHashBaseString = this._wildCardsFormat + this._missingKeyFormat + this._locales[0];
|
|
2115
2059
|
}
|
|
2116
2060
|
/**
|
|
2117
|
-
*
|
|
2061
|
+
* Moves the specified locales to the beginning of the locales list. This also alters the translation priority by setting the first
|
|
2062
|
+
* provided locale as the most prioritary, the second as the next one and so.
|
|
2063
|
+
*
|
|
2064
|
+
* This method basically works exactly the same way as setPrimaryLocale but letting us add many locales at once.
|
|
2065
|
+
*
|
|
2066
|
+
* @see setPrimaryLocale()
|
|
2067
|
+
*
|
|
2068
|
+
* @param locales A list of locales to be moved to the beginning of the translation priority. First locales item will be the prefered
|
|
2069
|
+
* locale for translation, second will be the next one in case some key is not translated for the first one and so. If any of the
|
|
2070
|
+
* specified locales is not currently loaded, an exception will happen.
|
|
2071
|
+
*
|
|
2072
|
+
* @returns void
|
|
2118
2073
|
*/
|
|
2119
|
-
|
|
2120
|
-
|
|
2074
|
+
setPrimaryLocales(locales) {
|
|
2075
|
+
if (!Array.isArray(locales) ||
|
|
2076
|
+
(new Set(locales).size !== locales.length) ||
|
|
2077
|
+
locales.length === 0) {
|
|
2078
|
+
throw new Error('locales must be non empty string array with no duplicate elements');
|
|
2079
|
+
}
|
|
2080
|
+
for (let i = locales.length - 1; i >= 0; i--) {
|
|
2081
|
+
this.setPrimaryLocale(locales[i]);
|
|
2082
|
+
}
|
|
2121
2083
|
}
|
|
2122
2084
|
/**
|
|
2123
|
-
*
|
|
2124
|
-
* URL route. It Must be called once, typically at application startup
|
|
2085
|
+
* Define the 2 digit language that will be placed at the front of the currently loaded locales list (moving all the others one position to the right).
|
|
2125
2086
|
*
|
|
2126
|
-
*
|
|
2087
|
+
* This will be the first language to use when trying to get a translation.
|
|
2127
2088
|
*
|
|
2128
|
-
*
|
|
2129
|
-
*
|
|
2089
|
+
* @param language A 2 digit language code that matches with any of the currently loaded locales, which will
|
|
2090
|
+
* be moved to the first position of the loaded locales list. If the specified language does not match with
|
|
2091
|
+
* a locale that is currently loaded, an exception will happen.
|
|
2130
2092
|
*
|
|
2131
|
-
*
|
|
2093
|
+
* @returns void
|
|
2094
|
+
*/
|
|
2095
|
+
setPrimaryLanguage(language) {
|
|
2096
|
+
for (let locale of this._locales) {
|
|
2097
|
+
if (locale.substring(0, 2) === language) {
|
|
2098
|
+
this.setPrimaryLocale(locale);
|
|
2099
|
+
return;
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
throw new Error(language + ' not loaded');
|
|
2103
|
+
}
|
|
2104
|
+
/**
|
|
2105
|
+
* Moves the locales that match the specified languages to the beginning of the locales list.
|
|
2106
|
+
* Works the same as setPrimaryLocales() but with a list of the 2 digit language codes that match the respective locales.
|
|
2132
2107
|
*
|
|
2133
|
-
*
|
|
2134
|
-
*
|
|
2135
|
-
* { path: '', component: HomePageComponent,
|
|
2136
|
-
* data: { titleKey: 'HOME', titleBundle: 'turbodepot/user-interface'} },
|
|
2108
|
+
* @see setPrimaryLocale()
|
|
2109
|
+
* @see setPrimaryLanguage()
|
|
2137
2110
|
*
|
|
2138
|
-
* @param
|
|
2139
|
-
*
|
|
2111
|
+
* @param languages A list of 2 digit language codes to be moved to the beginning of the translation priority. If any of the
|
|
2112
|
+
* specified languages does not match with a locale that is currently loaded, an exception will happen.
|
|
2113
|
+
*
|
|
2114
|
+
* @returns void
|
|
2140
2115
|
*/
|
|
2141
|
-
|
|
2142
|
-
if (
|
|
2143
|
-
|
|
2116
|
+
setPrimaryLanguages(languages) {
|
|
2117
|
+
if (!Array.isArray(languages) ||
|
|
2118
|
+
(new Set(languages).size !== languages.length) ||
|
|
2119
|
+
languages.length === 0) {
|
|
2120
|
+
throw new Error('languages must be non empty string array with no duplicate elements');
|
|
2121
|
+
}
|
|
2122
|
+
for (let i = languages.length - 1; i >= 0; i--) {
|
|
2123
|
+
this.setPrimaryLanguage(languages[i]);
|
|
2144
2124
|
}
|
|
2145
|
-
// Set initial title based on current route data immediately
|
|
2146
|
-
this.updateTitleFromCurrentRoute(prefix, sufix);
|
|
2147
|
-
// Subscribe to future navigation events
|
|
2148
|
-
this._routerSubscription = this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => {
|
|
2149
|
-
this._updateCurrentRoute();
|
|
2150
|
-
this.updateTitleFromCurrentRoute(prefix, sufix);
|
|
2151
|
-
});
|
|
2152
|
-
this._isTitleManagerInitialized = true;
|
|
2153
2125
|
}
|
|
2154
2126
|
/**
|
|
2155
|
-
*
|
|
2156
|
-
*
|
|
2127
|
+
* Change the loaded locales translation preference order. The same locales that are currently loaded must be passed
|
|
2128
|
+
* but with a different order to change the translation priority.
|
|
2157
2129
|
*
|
|
2158
|
-
* @param
|
|
2159
|
-
*
|
|
2130
|
+
* @param locales A list with the new locales translation priority
|
|
2131
|
+
*
|
|
2132
|
+
* @returns void
|
|
2160
2133
|
*/
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
currentRoute = currentRoute.firstChild;
|
|
2134
|
+
setLocalesOrder(locales) {
|
|
2135
|
+
if (locales.length !== this._locales.length) {
|
|
2136
|
+
throw new Error('locales must contain all the currently loaded locales');
|
|
2165
2137
|
}
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2138
|
+
this._validateInitialized();
|
|
2139
|
+
for (let locale of locales) {
|
|
2140
|
+
if (!this.isLocaleLoaded(locale)) {
|
|
2141
|
+
throw new Error(locale + ' not loaded');
|
|
2142
|
+
}
|
|
2169
2143
|
}
|
|
2144
|
+
this._locales = locales;
|
|
2145
|
+
this._languages = this._locales.map((l) => l.substring(0, 2));
|
|
2146
|
+
this._cacheHashBaseString = this._wildCardsFormat + this._missingKeyFormat + this._locales[0];
|
|
2170
2147
|
}
|
|
2171
2148
|
/**
|
|
2172
|
-
*
|
|
2173
|
-
*
|
|
2174
|
-
* @param route The route to navigate to.
|
|
2149
|
+
* This is an aux method to implement the TurboCommons StringUtils replace method.
|
|
2150
|
+
* It is exactly the same as the one on the library, but we implement it here to avoid having a dependency with TurboCommons
|
|
2175
2151
|
*/
|
|
2176
|
-
|
|
2177
|
-
|
|
2152
|
+
_replace(string, search, replacement) {
|
|
2153
|
+
const escapedSearch = search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
2154
|
+
return string.replace(new RegExp(escapedSearch, 'g'), replacement);
|
|
2178
2155
|
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2156
|
+
/**
|
|
2157
|
+
* This is an aux method to implement the TurboCommons StringUtils isEmpty method.
|
|
2158
|
+
* It is exactly the same as the one on the library, but we implement it here to avoid having a dependency with TurboCommons
|
|
2159
|
+
*/
|
|
2160
|
+
_isEmpty(string) {
|
|
2161
|
+
let isString = (typeof string === 'string' || string instanceof String);
|
|
2162
|
+
// Throw exception if non string value was received
|
|
2163
|
+
if (!isString) {
|
|
2164
|
+
// Empty or null value is considered empty
|
|
2165
|
+
if (string == null || string == '') {
|
|
2166
|
+
return true;
|
|
2167
|
+
}
|
|
2168
|
+
throw new Error("value is not a string");
|
|
2169
|
+
}
|
|
2170
|
+
return string.replace(/[ \n\r\t]/g, '') === '';
|
|
2183
2171
|
}
|
|
2184
|
-
|
|
2185
|
-
|
|
2172
|
+
/**
|
|
2173
|
+
* This is an aux method to implement the TurboCommons StringUtils forceNonEmptyString method.
|
|
2174
|
+
* It is exactly the same as the one on the library, but we implement it here to avoid having a dependency with TurboCommons
|
|
2175
|
+
*/
|
|
2176
|
+
_forceNonEmptyString(value, valueName = '', errorMessage = 'must be a non empty string') {
|
|
2177
|
+
let isString = (typeof value === 'string' || value instanceof String);
|
|
2178
|
+
if (!isString || this._isEmpty(value)) {
|
|
2179
|
+
throw new Error(valueName + ' ' + errorMessage);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: LocalesBaseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2183
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: LocalesBaseService, providedIn: 'root' }); }
|
|
2186
2184
|
}
|
|
2187
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type:
|
|
2185
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: LocalesBaseService, decorators: [{
|
|
2188
2186
|
type: Injectable,
|
|
2189
2187
|
args: [{
|
|
2190
2188
|
providedIn: 'root',
|
|
2191
2189
|
}]
|
|
2192
|
-
}]
|
|
2190
|
+
}] });
|
|
2193
2191
|
|
|
2194
2192
|
/**
|
|
2195
2193
|
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
@@ -2942,5 +2940,5 @@ class ValidatorsPlus extends Validators {
|
|
|
2942
2940
|
* Generated bundle index. Do not edit.
|
|
2943
2941
|
*/
|
|
2944
2942
|
|
|
2945
|
-
export { AutoFocusOnDisplayDirective, AutoSelectTextOnFocusDirective, BrowserService, BusyStateBaseComponent, ButtonContainerComponent, ButtonImageComponent, DelayedMethodCallManager, DialogBaseComponent, DialogDateSelectionComponent, DialogErrorComponent, DialogMultipleOptionComponent, DialogService, DialogSingleInputComponent, DialogSingleOptionComponent, DialogSingleSelectionListComponent, DialogTwoOptionComponent, ElementCreatedDirective, ElementDestroyedDirective, FadeAnimationClass, GUINotification, GlobalErrorService, HTTPService, HTTPServiceGetRequest, HTTPServicePostRequest,
|
|
2943
|
+
export { AutoFocusOnDisplayDirective, AutoSelectTextOnFocusDirective, BrowserService, BusyStateBaseComponent, ButtonContainerComponent, ButtonImageComponent, DelayedMethodCallManager, DialogBaseComponent, DialogDateSelectionComponent, DialogErrorComponent, DialogMultipleOptionComponent, DialogService, DialogSingleInputComponent, DialogSingleOptionComponent, DialogSingleSelectionListComponent, DialogTwoOptionComponent, ElementCreatedDirective, ElementDestroyedDirective, FadeAnimationClass, GUINotification, GlobalErrorService, HTTPService, HTTPServiceGetRequest, HTTPServicePostRequest, LocalesBaseService, NotificationService, RouterBaseService, SingletoneStrictClass, TurboApiCallerService, TurboGuiAngularModule, ValidatorsPlus, View, ViewService };
|
|
2946
2944
|
//# sourceMappingURL=turbogui-angular.mjs.map
|