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