turbogui-angular 15.2.0 → 15.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/esm2022/main/controller/dialog.service.mjs +3 -3
  2. package/esm2022/main/controller/locales.service.mjs +492 -0
  3. package/esm2022/main/controller/turbo-api-caller.service.mjs +41 -9
  4. package/esm2022/main/controller/views.service.mjs +20 -17
  5. package/esm2022/main/model/classes/View.mjs +2 -2
  6. package/esm2022/main/view/components/busy-state-base/busy-state-base.component.mjs +1 -1
  7. package/esm2022/main/view/components/button-container/button-container.component.mjs +1 -1
  8. package/esm2022/main/view/components/button-image/button-image.component.mjs +1 -1
  9. package/esm2022/main/view/components/dialog-date-selection/dialog-date-selection.component.mjs +1 -1
  10. package/esm2022/main/view/components/dialog-error/dialog-error.component.mjs +1 -1
  11. package/esm2022/main/view/components/dialog-multiple-option/dialog-multiple-option.component.mjs +1 -1
  12. package/esm2022/main/view/components/dialog-single-input/dialog-single-input.component.mjs +25 -8
  13. package/esm2022/public_api.mjs +2 -2
  14. package/fesm2022/turbogui-angular.mjs +559 -41
  15. package/fesm2022/turbogui-angular.mjs.map +1 -1
  16. package/main/controller/dialog.service.d.ts +2 -2
  17. package/main/controller/locales.service.d.ts +295 -0
  18. package/main/controller/locales.service.d.ts.map +1 -0
  19. package/main/controller/turbo-api-caller.service.d.ts +28 -8
  20. package/main/controller/turbo-api-caller.service.d.ts.map +1 -1
  21. package/main/controller/views.service.d.ts +11 -10
  22. package/main/controller/views.service.d.ts.map +1 -1
  23. package/main/model/classes/View.d.ts +1 -1
  24. package/main/view/components/busy-state-base/busy-state-base.component.d.ts +0 -9
  25. package/main/view/components/busy-state-base/busy-state-base.component.d.ts.map +1 -1
  26. package/main/view/components/button-container/button-container.component.d.ts +0 -3
  27. package/main/view/components/button-container/button-container.component.d.ts.map +1 -1
  28. package/main/view/components/button-image/button-image.component.d.ts +0 -3
  29. package/main/view/components/button-image/button-image.component.d.ts.map +1 -1
  30. package/main/view/components/dialog-date-selection/dialog-date-selection.component.d.ts +0 -3
  31. package/main/view/components/dialog-date-selection/dialog-date-selection.component.d.ts.map +1 -1
  32. package/main/view/components/dialog-error/dialog-error.component.d.ts +0 -3
  33. package/main/view/components/dialog-error/dialog-error.component.d.ts.map +1 -1
  34. package/main/view/components/dialog-multiple-option/dialog-multiple-option.component.d.ts +0 -3
  35. package/main/view/components/dialog-multiple-option/dialog-multiple-option.component.d.ts.map +1 -1
  36. package/main/view/components/dialog-single-input/dialog-single-input.component.d.ts +5 -5
  37. package/main/view/components/dialog-single-input/dialog-single-input.component.d.ts.map +1 -1
  38. package/package.json +1 -1
  39. package/public_api.d.ts +1 -1
  40. package/public_api.d.ts.map +1 -1
  41. package/esm2022/main/controller/localization.service.mjs +0 -25
  42. package/main/controller/localization.service.d.ts +0 -10
  43. package/main/controller/localization.service.d.ts.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { EventEmitter, Directive, Output, HostListener, NgModule, inject, Injectable, Component, Inject, HostBinding, ViewContainerRef, Input, ViewChild } from '@angular/core';
3
3
  import { Subject } from 'rxjs';
4
- import { LocalizationManager, ArrayUtils, NumericUtils, HTTPManager, StringUtils, HTTPManagerGetRequest, HTTPManagerPostRequest, BrowserManager, ConversionUtils } from 'turbocommons-ts';
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';
7
7
  import * as i3 from '@angular/material/button';
@@ -379,16 +379,483 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.8", ngImpor
379
379
  /**
380
380
  * Manages application text translations and languages
381
381
  */
382
- class LocalizationService extends LocalizationManager {
383
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: LocalizationService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
384
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: LocalizationService, providedIn: 'root' }); }
382
+ class LocalesService extends SingletoneStrictClass {
383
+ /**
384
+ * Fully featured translation manager to be used with any application that requires text internationalization.
385
+ */
386
+ constructor() {
387
+ super(LocalesService);
388
+ /**
389
+ * if the class has been correctly initialized and translations have been correctly loaded
390
+ */
391
+ this._isInitialized = false;
392
+ /**
393
+ * @see getLocales()
394
+ */
395
+ this._locales = [];
396
+ /**
397
+ * @see getLanguages()
398
+ */
399
+ this._languages = [];
400
+ /**
401
+ * Stores all the loaded localization data by library name, bundle name, key and locales
402
+ */
403
+ this._loadedTranslations = {};
404
+ /**
405
+ * Stores a memory cache to improve performance when outputing translations
406
+ */
407
+ this._keyValuesCache = {};
408
+ /**
409
+ * @see setWildCardsFormat()
410
+ */
411
+ this._wildCardsFormat = '{N}';
412
+ /**
413
+ * @see setMissingKeyFormat()
414
+ */
415
+ this._missingKeyFormat = '$exception';
416
+ /**
417
+ * Stores a hash value that is used to improve the performance for translation t() methods.
418
+ * This is computed based on _wildCardsFormat plus _missingKeyFormat plus the current primary locale
419
+ * Methods that change these values will recalculate the hash string, so when calling translation methods, the
420
+ * performance will be as fast as possible.
421
+ */
422
+ this._cacheHashBaseString = '';
423
+ }
424
+ /**
425
+ * Wildcards are string fragments that are placed inside the translated texts. Their main purpose is to be replaced at
426
+ * runtime by custom values like for example a user name, a date, a numeric value, etc..
427
+ *
428
+ * This class helps with this process by including a parameter called 'toReplace' on all ->t methods which allows us
429
+ * to specify a string or list of strings that will replace the respective wildcards on the translated text. Each wildcard
430
+ * must follow the format specified here, and contain a numeric digit that will be used to find the replacement text at the
431
+ * 'toReplace' list. For example, if we define $N as the wildcard format, and we have a translation that contains $0, $1, $2,
432
+ * $0 will be replaced with the first element on toReplace, $1 with the second and so.
433
+ *
434
+ * We usually set this before initializing the class translation data
435
+ *
436
+ * Notice that N is mandayory on the wildcards format and the first index value is 0.
437
+ *
438
+ * @param value The wildcards format we want to set
439
+ *
440
+ * @returns The value that's been set
441
+ */
442
+ setWildCardsFormat(value) {
443
+ if (!value.includes('N')) {
444
+ throw new Error("N is mandatory to replace wildcards");
445
+ }
446
+ this._cacheHashBaseString = value + this._missingKeyFormat + ((this._locales.length > 0) ? this._locales[0] : '');
447
+ this._wildCardsFormat = value;
448
+ return value;
449
+ }
450
+ /**
451
+ * Defines the behaviour for get(), getStartCase(), etc... methods when a key is not found on
452
+ * a bundle or the bundle does not exist
453
+ *
454
+ * If missingKeyFormat is an empty string, all missing keys will return an empty value (not recommended)
455
+ *
456
+ * If missingKeyFormat contains a string, that string will be always returned for missing keys
457
+ *
458
+ * If missingKeyFormat contains a string with one of the following predefined wildcards:<br>
459
+ * - $key will be replaced with key name. Example: get("NAME") will output [NAME] if key is not found and missingKeyFormat = '[$key]'<br>
460
+ * - $exception (default value) will throw an exception with the problem cause description.
461
+ *
462
+ * @param value The missing key format we want to set
463
+ *
464
+ * @returns The value that's been set
465
+ */
466
+ setMissingKeyFormat(value) {
467
+ this._cacheHashBaseString = this._wildCardsFormat + value + ((this._locales.length > 0) ? this._locales[0] : '');
468
+ this._missingKeyFormat = value;
469
+ return value;
470
+ }
471
+ /**
472
+ * @see setMissingKeyFormat()
473
+ */
474
+ getMissingKeyFormat() {
475
+ return this._missingKeyFormat;
476
+ }
477
+ /**
478
+ * Initializes the translation system by loading and parsing bundle files from the specified translations path.
479
+ *
480
+ * @param translationsPath - Url where the translations Json structure of libraries/bundles/locales/keys is available.
481
+ * @param locales An array of locale codes (e.g., ['en_US', 'es_ES', 'fr_FR']) to load. These will be added to the translation
482
+ * path using the following format: translationsPath/en_US-es_ES-fr_FR. The order of this array will determine the
483
+ * translation priority
484
+ * @param parameters Any extra parameters to be attached to the translationsPath after the locales like /param1/param2/ etc
485
+ *
486
+ * @return A promise that will resolve if the translations get correctly loaded, or reject with an error if load fails
487
+ */
488
+ initialize(translationsPath, locales, parameters) {
489
+ this._isInitialized = false;
490
+ this._loadedTranslations = {};
491
+ // Validate received locales are correct
492
+ for (const locale of locales) {
493
+ this._validateLocaleString(locale);
494
+ }
495
+ let translationsFullPath = translationsPath + '/' + locales.join('-') + '/' + parameters.join('/');
496
+ return new Promise((resolve, reject) => {
497
+ fetch(translationsFullPath).then(response => {
498
+ if (!response.ok) {
499
+ throw new Error(`HTTP error! status: ${response.status}`);
500
+ }
501
+ return response.json();
502
+ }).then(data => {
503
+ this._loadedTranslations = data;
504
+ this._isInitialized = true;
505
+ this._locales = locales;
506
+ this._languages = locales.map((l) => l.substring(0, 2));
507
+ this._cacheHashBaseString = this._wildCardsFormat + this._missingKeyFormat + this._locales[0];
508
+ resolve(undefined);
509
+ }).catch(error => {
510
+ reject(new Error(`ERROR LOADING LOCALES FROM: ${translationsFullPath}\n` + error));
511
+ });
512
+ });
513
+ }
514
+ /**
515
+ * Check if the class has been correctly initialized and translations have been correctly loaded
516
+ */
517
+ isInitialized() {
518
+ return this._isInitialized;
519
+ }
520
+ /**
521
+ * Aux method to verify that this class is correctly initialized with translation data
522
+ */
523
+ _validateInitialized() {
524
+ if (!this._isInitialized) {
525
+ throw new Error('LocalesManager not initialized');
526
+ }
527
+ }
528
+ /**
529
+ * Checks if the specified locale is currently loaded for the currently defined bundles and paths.
530
+ *
531
+ * @param locale A locale to check. For example 'en_US'
532
+ *
533
+ * @return True if the locale is currently loaded on the class, false if not.
534
+ */
535
+ isLocaleLoaded(locale) {
536
+ this._validateLocaleString(locale);
537
+ return this._locales.includes(locale);
538
+ }
539
+ /**
540
+ * Aux method to validate that a locale string is correctly formatted
541
+ *
542
+ * @param string $locale A locale string
543
+ */
544
+ _validateLocaleString(locale) {
545
+ if (!/^[a-z]{2}_[A-Z]{2}$/.test(locale)) {
546
+ throw new Error('locale must be a valid xx_XX value');
547
+ }
548
+ }
549
+ /**
550
+ * Checks if the specified 2 digit language is currently loaded for the currently defined bundles and paths.
551
+ *
552
+ * @param language A language to check. For example 'en'
553
+ *
554
+ * @return True if the language is currently loaded on the class, false if not.
555
+ */
556
+ isLanguageLoaded(language) {
557
+ this._validateLanguageString(language);
558
+ return this._languages.includes(language);
559
+ }
560
+ /**
561
+ * Aux method to validate that a language string is correctly formatted
562
+ *
563
+ * @param language A 2 digit language string
564
+ */
565
+ _validateLanguageString(language) {
566
+ if (!/^[a-z]{2}$/.test(language)) {
567
+ throw new Error('language must be a valid 2 digit value');
568
+ }
569
+ }
570
+ /**
571
+ * Get the translation to the current primary locale for the given key, library and bundle
572
+ *
573
+ * @param string key The key we want to read from the specified resource bundle
574
+ * @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
575
+ * @param array replaceWildcards A list of values that will replace wildcards that may be found on the translated text. Each wildcard
576
+ * will be replaced with the element whose index on toReplace matches it. Check the documentation for this.wildCardsFormat
577
+ * property to know more about how to setup wildcards.
578
+ *
579
+ * @see setWildCardsFormat()
580
+ *
581
+ * @return The translated text
582
+ */
583
+ t(key, bundlePath, replaceWildcards = []) {
584
+ this._validateInitialized();
585
+ // Create a cache key to improve performance when requesting the same key translation several times
586
+ const cacheKey = `${this._cacheHashBaseString}${key}${bundlePath}${replaceWildcards.join('')}`;
587
+ if (!this._keyValuesCache[cacheKey]) {
588
+ this._forceNonEmptyString(key, '', 'key must be non empty string');
589
+ this._forceNonEmptyString(bundlePath, '', 'bundlePath must be non empty string');
590
+ const [library, bundle] = bundlePath.split('/');
591
+ this._forceNonEmptyString(library, '', 'no library specified on bundlePath');
592
+ this._forceNonEmptyString(bundle, '', 'no bundle specified on bundlePath');
593
+ const replacementsCount = replaceWildcards.length;
594
+ // Loop all the locales to find the first one with a value for the specified key
595
+ for (const locale of this._locales) {
596
+ if (this._loadedTranslations[library]?.[bundle]?.[locale]?.[key]) {
597
+ let result = this._loadedTranslations[library][bundle][locale][key];
598
+ // Replace all wildcards on the text with the specified replacements if any
599
+ for (let i = 0; i < replacementsCount; i++) {
600
+ result = this._replace(result, this._replace(this._wildCardsFormat, 'N', i.toString()), replaceWildcards[i]);
601
+ }
602
+ this._keyValuesCache[cacheKey] = result;
603
+ return result;
604
+ }
605
+ }
606
+ // Check if an exception needs to be thrown if the specified key is not found on this bundle
607
+ if (this._missingKeyFormat.includes('$exception')) {
608
+ throw new Error(`key <${key}> not found on ${bundlePath}`);
609
+ }
610
+ this._keyValuesCache[cacheKey] = this._replace(this._missingKeyFormat, '$key', key);
611
+ }
612
+ return this._keyValuesCache[cacheKey];
613
+ }
614
+ /**
615
+ * Get the translation for the given key and bundle as a string with all words first character capitalized
616
+ * and all the rest of the word with lower case
617
+ *
618
+ * @see t()
619
+ *
620
+ * @returns The localized and case formatted text
621
+ */
622
+ tStartCase(key, bundlePath, replaceWildcards = []) {
623
+ return this.t(key, bundlePath, replaceWildcards).split(' ')
624
+ .map((word) => word ? word[0].toUpperCase() + word.slice(1).toLowerCase() : '').join(' ');
625
+ }
626
+ /**
627
+ * Get the translation for the given key and bundle as an all upper case string
628
+ *
629
+ * @see t()
630
+ *
631
+ * @returns The localized and case formatted text
632
+ */
633
+ tAllUpperCase(key, bundlePath, replaceWildcards = []) {
634
+ return this.t(key, bundlePath, replaceWildcards).toUpperCase();
635
+ }
636
+ /**
637
+ * Get the translation for the given key and bundle as an all lower case string
638
+ *
639
+ * @see t()
640
+ *
641
+ * @returns The localized and case formatted text
642
+ */
643
+ tAllLowerCase(key, bundlePath, replaceWildcards = []) {
644
+ return this.t(key, bundlePath, replaceWildcards).toLowerCase();
645
+ }
646
+ /**
647
+ * Get the translation for the given key and bundle as a string with the first character as Upper case
648
+ * and all the rest as lower case
649
+ *
650
+ * @see t()
651
+ *
652
+ * @returns The localized and case formatted text
653
+ */
654
+ tFirstUpperRestLower(key, bundlePath, replaceWildcards = []) {
655
+ const string = this.t(key, bundlePath, replaceWildcards);
656
+ return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
657
+ }
658
+ /**
659
+ * A list of strings containing the locales that are used by this class to translate the given keys, sorted by preference.
660
+ * Each string is formatted as a standard locale code with language and country joined by an underscore, like: en_US, fr_FR
661
+ *
662
+ * When a key and bundle are requested for translation, the class will check on the first language of this
663
+ * list for a translated text. If missing, the next one will be used, and so. This list is constructed after initialize
664
+ * methods is called.
665
+ *
666
+ * @example: After loading the following list of locales ['en_US', 'es_ES', 'fr_FR'] if we call LocalesManager.t('HELLO', 'lib1/greetings')
667
+ * the localization manager will try to locate the en_US value for the HELLO tag on the greetings bundle for the library lib1.
668
+ * 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
669
+ * value is found or no more locales are defined.
670
+ */
671
+ getLocales() {
672
+ return this._locales;
673
+ }
674
+ /**
675
+ * A list of strings containing the languages that are used by this class to translate the given keys, sorted by preference.
676
+ * Each string is formatted as a 2 digit language code, like: en, fr
677
+ *
678
+ * This list is the same as the locales() one, but containing only the language part of each locale (the first two digits)
679
+ *
680
+ * @see getLocales()
681
+ */
682
+ getLanguages() {
683
+ return this._languages;
684
+ }
685
+ /**
686
+ * Get the first locale from the list of loaded locales, which is the currently used to search for translated texts.
687
+ *
688
+ * @returns The locale that is defined as the primary one. For example: en_US, es_ES, ..
689
+ */
690
+ getPrimaryLocale() {
691
+ this._validateInitialized();
692
+ return this._locales[0];
693
+ }
694
+ /**
695
+ * Get the first language from the list of loaded locales, which is the currently used to search for translated texts.
696
+ *
697
+ * @returns The 2 digit language code that is defined as the primary one. For example: en, es, ..
698
+ */
699
+ getPrimaryLanguage() {
700
+ this._validateInitialized();
701
+ return this._languages[0];
702
+ }
703
+ /**
704
+ * 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).
705
+ *
706
+ * This will be the first locale to use when trying to get a translation.
707
+ *
708
+ * @param locale A currently loaded locale that will be moved to the first position of the loaded locales list. If the specified locale
709
+ * is not currently loaded, an exception will happen.
710
+ *
711
+ * @returns void
712
+ */
713
+ setPrimaryLocale(locale) {
714
+ this._validateInitialized();
715
+ if (!this.isLocaleLoaded(locale)) {
716
+ throw new Error(locale + ' not loaded');
717
+ }
718
+ let result = [locale];
719
+ for (let l of this._locales) {
720
+ if (l !== locale) {
721
+ result.push(l);
722
+ }
723
+ }
724
+ this._locales = result;
725
+ this._languages = this._locales.map((l) => l.substring(0, 2));
726
+ this._cacheHashBaseString = this._wildCardsFormat + this._missingKeyFormat + this._locales[0];
727
+ }
728
+ /**
729
+ * Moves the specified locales to the beginning of the locales list. This also alters the translation priority by setting the first
730
+ * provided locale as the most prioritary, the second as the next one and so.
731
+ *
732
+ * This method basically works exactly the same way as setPrimaryLocale but letting us add many locales at once.
733
+ *
734
+ * @see setPrimaryLocale()
735
+ *
736
+ * @param locales A list of locales to be moved to the beginning of the translation priority. First locales item will be the prefered
737
+ * 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
738
+ * specified locales is not currently loaded, an exception will happen.
739
+ *
740
+ * @returns void
741
+ */
742
+ setPrimaryLocales(locales) {
743
+ if (!Array.isArray(locales) ||
744
+ (new Set(locales).size !== locales.length) ||
745
+ locales.length === 0) {
746
+ throw new Error('locales must be non empty string array with no duplicate elements');
747
+ }
748
+ for (let i = locales.length - 1; i >= 0; i--) {
749
+ this.setPrimaryLocale(locales[i]);
750
+ }
751
+ }
752
+ /**
753
+ * 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).
754
+ *
755
+ * This will be the first language to use when trying to get a translation.
756
+ *
757
+ * @param language A 2 digit language code that matches with any of the currently loaded locales, which will
758
+ * be moved to the first position of the loaded locales list. If the specified language does not match with
759
+ * a locale that is currently loaded, an exception will happen.
760
+ *
761
+ * @returns void
762
+ */
763
+ setPrimaryLanguage(language) {
764
+ for (let locale of this._locales) {
765
+ if (locale.substring(0, 2) === language) {
766
+ this.setPrimaryLocale(locale);
767
+ return;
768
+ }
769
+ }
770
+ throw new Error(language + ' not loaded');
771
+ }
772
+ /**
773
+ * Moves the locales that match the specified languages to the beginning of the locales list.
774
+ * Works the same as setPrimaryLocales() but with a list of the 2 digit language codes that match the respective locales.
775
+ *
776
+ * @see setPrimaryLocale()
777
+ * @see setPrimaryLanguage()
778
+ *
779
+ * @param languages A list of 2 digit language codes to be moved to the beginning of the translation priority. If any of the
780
+ * specified languages does not match with a locale that is currently loaded, an exception will happen.
781
+ *
782
+ * @returns void
783
+ */
784
+ setPrimaryLanguages(languages) {
785
+ if (!Array.isArray(languages) ||
786
+ (new Set(languages).size !== languages.length) ||
787
+ languages.length === 0) {
788
+ throw new Error('languages must be non empty string array with no duplicate elements');
789
+ }
790
+ for (let i = languages.length - 1; i >= 0; i--) {
791
+ this.setPrimaryLanguage(languages[i]);
792
+ }
793
+ }
794
+ /**
795
+ * Change the loaded locales translation preference order. The same locales that are currently loaded must be passed
796
+ * but with a different order to change the translation priority.
797
+ *
798
+ * @param locales A list with the new locales translation priority
799
+ *
800
+ * @returns void
801
+ */
802
+ setLocalesOrder(locales) {
803
+ if (locales.length !== this._locales.length) {
804
+ throw new Error('locales must contain all the currently loaded locales');
805
+ }
806
+ this._validateInitialized();
807
+ for (let locale of locales) {
808
+ if (!this.isLocaleLoaded(locale)) {
809
+ throw new Error(locale + ' not loaded');
810
+ }
811
+ }
812
+ this._locales = locales;
813
+ this._languages = this._locales.map((l) => l.substring(0, 2));
814
+ this._cacheHashBaseString = this._wildCardsFormat + this._missingKeyFormat + this._locales[0];
815
+ }
816
+ /**
817
+ * This is an aux method to implement the TurboCommons StringUtils replace method.
818
+ * It is exactly the same as the one on the library, but we implement it here to avoid having a dependency with TurboCommons
819
+ */
820
+ _replace(string, search, replacement) {
821
+ const escapedSearch = search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
822
+ return string.replace(new RegExp(escapedSearch, 'g'), replacement);
823
+ }
824
+ /**
825
+ * This is an aux method to implement the TurboCommons StringUtils isEmpty method.
826
+ * It is exactly the same as the one on the library, but we implement it here to avoid having a dependency with TurboCommons
827
+ */
828
+ _isEmpty(string) {
829
+ let isString = (typeof string === 'string' || string instanceof String);
830
+ // Throw exception if non string value was received
831
+ if (!isString) {
832
+ // Empty or null value is considered empty
833
+ if (string == null || string == '') {
834
+ return true;
835
+ }
836
+ throw new Error("value is not a string");
837
+ }
838
+ return string.replace(/[ \n\r\t]/g, '') === '';
839
+ }
840
+ /**
841
+ * This is an aux method to implement the TurboCommons StringUtils forceNonEmptyString method.
842
+ * It is exactly the same as the one on the library, but we implement it here to avoid having a dependency with TurboCommons
843
+ */
844
+ _forceNonEmptyString(value, valueName = '', errorMessage = 'must be a non empty string') {
845
+ let isString = (typeof value === 'string' || value instanceof String);
846
+ if (!isString || this._isEmpty(value)) {
847
+ throw new Error(valueName + ' ' + errorMessage);
848
+ }
849
+ }
850
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: LocalesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
851
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: LocalesService, providedIn: 'root' }); }
385
852
  }
386
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: LocalizationService, decorators: [{
853
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: LocalesService, decorators: [{
387
854
  type: Injectable,
388
855
  args: [{
389
856
  providedIn: 'root',
390
857
  }]
391
- }] });
858
+ }], ctorParameters: () => [] });
392
859
 
393
860
  /**
394
861
  * TurboGUI is A library that helps with the most common and generic UI elements and functionalities
@@ -811,8 +1278,8 @@ class DialogService extends SingletoneStrictClass {
811
1278
  * - viewContainerRef: This is important if we want to propagate providers from a parent component to this dialog. We must specify
812
1279
  * this reference to make sure the same services injected on the parent are available too at the child dialog
813
1280
  *
814
- * @param callback A function that will be called after the dialog is closed and will receive a selection object with the numeric index and value for
815
- * the option that's been selected by the user. if no option was selected, index will be -1 and value null
1281
+ * @param callback A function that will be called after the dialog is closed. It will receive a selection object with two properties: index and value. Those
1282
+ * 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
816
1283
  */
817
1284
  addDialog(dialogComponentClass, properties, callback = null) {
818
1285
  if (!this._isEnabled) {
@@ -1148,11 +1615,11 @@ class TurboApiCallerService extends SingletoneStrictClass {
1148
1615
  /**
1149
1616
  * The username that is currently defined and will be used by the login methods
1150
1617
  */
1151
- this.userName = '';
1618
+ this._userName = '';
1152
1619
  /**
1153
1620
  * The password for the user that is currently defined and will be used by the login methods
1154
1621
  */
1155
- this.password = '';
1622
+ this._password = '';
1156
1623
  /**
1157
1624
  * Contains the last email account that has been verified (or tried to verify) by this service, if any
1158
1625
  */
@@ -1173,6 +1640,34 @@ class TurboApiCallerService extends SingletoneStrictClass {
1173
1640
  this.httpManager = new HTTPManager();
1174
1641
  this._clearUserAndToken();
1175
1642
  }
1643
+ /**
1644
+ * The username that is currently defined and will be used by the login methods
1645
+ */
1646
+ set userName(v) {
1647
+ this._isLogged = false;
1648
+ this._token = '';
1649
+ this._userName = v;
1650
+ }
1651
+ /**
1652
+ * The username that is currently defined and will be used by the login methods
1653
+ */
1654
+ get userName() {
1655
+ return this._userName;
1656
+ }
1657
+ /**
1658
+ * The password for the user that is currently defined and will be used by the login methods
1659
+ */
1660
+ set password(v) {
1661
+ this._isLogged = false;
1662
+ this._token = '';
1663
+ this._password = v;
1664
+ }
1665
+ /**
1666
+ * The password for the user that is currently defined and will be used by the login methods
1667
+ */
1668
+ get password() {
1669
+ return this._password;
1670
+ }
1176
1671
  /**
1177
1672
  * Define the root url that will be used for all the API calls
1178
1673
  */
@@ -1252,10 +1747,13 @@ class TurboApiCallerService extends SingletoneStrictClass {
1252
1747
  ;
1253
1748
  }
1254
1749
  /**
1255
- * Authenticates the user by sending an encoded credentials request to the login web service.
1750
+ * Authenticates the userName and password that are currently defined at the respective properties of this service.
1256
1751
  * Returns a promise that resolves with the server's response or rejects with an error if the login fails.
1257
1752
  * Path to the login service must be correctly defined at this.loginWebService
1258
1753
  *
1754
+ * The authentication process is performed by sending an encoded credentials request to the login web service using the
1755
+ * currently defined user name and psw.
1756
+ *
1259
1757
  * Here's an example of a call:
1260
1758
  *
1261
1759
  * login().then(response => {
@@ -1272,7 +1770,7 @@ class TurboApiCallerService extends SingletoneStrictClass {
1272
1770
  return new Promise((resolve, reject) => {
1273
1771
  const request = new HTTPManagerPostRequest(this.loginServiceURI);
1274
1772
  request.ignoreGlobalPostParams = true;
1275
- const encodedCredentials = ConversionUtils.stringToBase64(ConversionUtils.stringToBase64(this.userName) + ',' + ConversionUtils.stringToBase64(this.password));
1773
+ const encodedCredentials = ConversionUtils.stringToBase64(ConversionUtils.stringToBase64(this._userName) + ',' + ConversionUtils.stringToBase64(this._password));
1276
1774
  request.parameters = { data: encodedCredentials };
1277
1775
  request.successCallback = (response) => {
1278
1776
  if (response !== '') {
@@ -1303,10 +1801,11 @@ class TurboApiCallerService extends SingletoneStrictClass {
1303
1801
  * If any of the two values is empty, false will be returned
1304
1802
  */
1305
1803
  get isUserAndPswDefined() {
1306
- return !StringUtils.isEmpty(this.userName) && !StringUtils.isEmpty(this.password);
1804
+ return !StringUtils.isEmpty(this._userName) && !StringUtils.isEmpty(this._password);
1307
1805
  }
1308
1806
  /**
1309
- * Tells if exists a user that is currently logged or not
1807
+ * Tells if the user name and psw that are specified on this service are currently logged or not. This means
1808
+ * also a token is active.
1310
1809
  */
1311
1810
  get isLogged() {
1312
1811
  return this._isLogged;
@@ -1486,8 +1985,8 @@ class TurboApiCallerService extends SingletoneStrictClass {
1486
1985
  * Aux methot to clear all the currently logged user data
1487
1986
  */
1488
1987
  _clearUserAndToken() {
1489
- this.userName = '';
1490
- this.password = '';
1988
+ this._userName = '';
1989
+ this._password = '';
1491
1990
  this._isLogged = false;
1492
1991
  this._token = '';
1493
1992
  this.httpManager.setGlobalPostParam('token', '-');
@@ -1529,6 +2028,10 @@ class ViewsService extends SingletoneStrictClass {
1529
2028
  * Contains the reference to the currently loaded view component
1530
2029
  */
1531
2030
  this._currentComponentRef = null;
2031
+ /**
2032
+ * Flag that stores if any view is in the process of being loaded
2033
+ */
2034
+ this._isLoadingView = false;
1532
2035
  }
1533
2036
  /**
1534
2037
  * Tells if there's any view currently loaded on the application views container
@@ -1550,20 +2053,25 @@ class ViewsService extends SingletoneStrictClass {
1550
2053
  }
1551
2054
  /**
1552
2055
  * Create a new view instance with the specified type and add it to the current application views container. Any currently loaded
1553
- * view will be removed
2056
+ * view will be removed.
1554
2057
  *
1555
- * Make sure this method is called when all the visual components of the application have been created (ngAfterViewInit)
2058
+ * If we push a view while another one is in the process of being loaded, the new push will be ignored.
1556
2059
  *
1557
- * @param view The classname for the view that we want to create and add to the views container (must extend View base class).
2060
+ * Make sure this method is called when all the visual components of the application have been created (ngAfterViewInit)
1558
2061
  *
1559
- * @return The instance of the newly added and created view.
2062
+ * @param view The classname for the view that we want to create (must extend View base class). A new angular object
2063
+ * will be instantiated and loaded into the views container.
1560
2064
  */
1561
2065
  async pushView(view) {
1562
- this.verifyViewsContainerExist();
1563
2066
  // If the loaded view is the same as the specified one, we will do nothing
1564
2067
  if (this._loadedViewClass === view) {
1565
- return this._currentComponentRef;
2068
+ return;
1566
2069
  }
2070
+ // If a view is already being loaded, nothing to do
2071
+ if (this._isLoadingView) {
2072
+ return;
2073
+ }
2074
+ this._isLoadingView = true;
1567
2075
  // If a view is already loaded, we will unload it first
1568
2076
  if (this._loadedViewClass !== null) {
1569
2077
  await this.removeCurrentView();
@@ -1582,18 +2090,17 @@ class ViewsService extends SingletoneStrictClass {
1582
2090
  fadeInPlayer.onDone(() => {
1583
2091
  this._currentComponentRef = newComponentRef;
1584
2092
  this._loadedViewClass = view;
2093
+ this._isLoadingView = false;
1585
2094
  resolve();
1586
2095
  });
1587
2096
  fadeInPlayer.play();
1588
2097
  });
1589
- return this._currentComponentRef;
1590
2098
  }
1591
2099
  /**
1592
2100
  * Delete the currently loaded view from the views container, and leave it empty.
1593
2101
  * If no view is currently loaded, this method will do nothing
1594
2102
  */
1595
2103
  async popView() {
1596
- this.verifyViewsContainerExist();
1597
2104
  if (this._loadedViewClass !== null && this._currentComponentRef) {
1598
2105
  await this.removeCurrentView();
1599
2106
  }
@@ -1602,6 +2109,9 @@ class ViewsService extends SingletoneStrictClass {
1602
2109
  * Aux method to remove the currently loaded view from the views container using a promise
1603
2110
  */
1604
2111
  removeCurrentView() {
2112
+ if (this._viewContainerRef === null) {
2113
+ throw new Error('Views container not defined. Please declare a <views-container> element in your application');
2114
+ }
1605
2115
  return new Promise((resolve) => {
1606
2116
  if (this._currentComponentRef) {
1607
2117
  const element = this._currentComponentRef.location.nativeElement;
@@ -1624,14 +2134,6 @@ class ViewsService extends SingletoneStrictClass {
1624
2134
  }
1625
2135
  });
1626
2136
  }
1627
- /**
1628
- * Auxiliary method to test if the views container instance is available on the application
1629
- */
1630
- verifyViewsContainerExist() {
1631
- if (this._viewContainerRef === null) {
1632
- throw new Error('Views container not defined. Please declare a <views-container> element in your application');
1633
- }
1634
- }
1635
2137
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: ViewsService, deps: [{ token: i1$2.AnimationBuilder }], target: i0.ɵɵFactoryTarget.Injectable }); }
1636
2138
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: ViewsService, providedIn: 'root' }); }
1637
2139
  }
@@ -1882,6 +2384,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.8", ngImpor
1882
2384
  /**
1883
2385
  * A dialog component with a single input element and an accept button, to be used with dialog service
1884
2386
  * It lets us easily ask the user for any arbitrary text we may need.
2387
+ *
2388
+ * 1st text is the dialog title
2389
+ * 2nd text is the dialog subtitle (leave it empty to hide the subtitle)
2390
+ * 3rd text is the input prompt caption
2391
+ * 4rd text is the default value of the input contents
2392
+ *
2393
+ * 1st option will be the text on the unique save button that exists on the dialog
1885
2394
  */
1886
2395
  class DialogSingleInputComponent extends DialogBaseComponent {
1887
2396
  static { this.DIALOG_CLASS_NAME = 'DialogSingleInputComponent'; }
@@ -1894,28 +2403,37 @@ class DialogSingleInputComponent extends DialogBaseComponent {
1894
2403
  * Contains the text that the user types on the dialog input
1895
2404
  */
1896
2405
  this.inputText = '';
2406
+ /**
2407
+ * Contains the default text that's been specified for the input
2408
+ */
2409
+ this.defaultTextValue = '';
1897
2410
  if (data.texts.length < 1) {
1898
- throw new Error('DialogSingleInputComponent expects 2 texts: The title and optionally a description');
2411
+ throw new Error('DialogSingleInputComponent expects 1 text: The dialog title');
1899
2412
  }
1900
2413
  if (data.options.length !== 1) {
1901
2414
  throw new Error('DialogSingleInputComponent expects only one option');
1902
2415
  }
2416
+ if (data.texts.length > 3) {
2417
+ this.inputText = data.texts[3];
2418
+ this.defaultTextValue = data.texts[3];
2419
+ }
1903
2420
  }
1904
- isInputEmpty() {
2421
+ isButtonDisabled() {
2422
+ if (this.defaultTextValue !== '' && this.inputText === this.defaultTextValue) {
2423
+ return true;
2424
+ }
1905
2425
  return StringUtils.isEmpty(this.inputText);
1906
2426
  }
1907
2427
  closeDialog() {
1908
- if (!this.isInputEmpty()) {
1909
- super.closeDialog(0, this.inputText);
1910
- }
2428
+ super.closeDialog(0, this.inputText);
1911
2429
  }
1912
2430
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: DialogSingleInputComponent, deps: [{ token: i0.ElementRef }, { token: i1.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
1913
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.0.8", type: DialogSingleInputComponent, isStandalone: true, selector: "tg-dialog-single-input", providers: [], usesInheritance: true, ngImport: i0, template: "<h3>\r\n {{data.texts[0]}}\r\n</h3>\r\n\r\n<p *ngIf=\"data.texts.length > 2\">\r\n {{data.texts[2]}}\r\n</p>\r\n\r\n<mat-form-field>\r\n\r\n <mat-label>{{data.texts[1]}}</mat-label>\r\n <input matInput autoFocusOnDisplay\r\n (keyup.enter)=\"closeDialog()\"\r\n [(ngModel)]=\"inputText\">\r\n \r\n</mat-form-field>\r\n\r\n<button mat-raised-button color=\"primary\"\r\n [disabled]=\"isInputEmpty()\"\r\n (click)=\"closeDialog()\">\r\n \r\n {{data.options[0]}}\r\n \r\n</button>", styles: [":host{min-height:300px}h3{margin-bottom:25px}p{margin-bottom:52px}button{float:right}\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"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i6.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TurboGuiAngularModule }, { kind: "directive", type: AutoFocusOnDisplayDirective, selector: "[autoFocusOnDisplay]" }] }); }
2431
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.0.8", type: DialogSingleInputComponent, isStandalone: true, selector: "tg-dialog-single-input", providers: [], usesInheritance: true, ngImport: i0, template: "<h3>\r\n {{data.texts[0]}}\r\n</h3>\r\n\r\n<p *ngIf=\"data.texts.length > 1 && data.texts[1] !== ''\">\r\n {{data.texts[1]}}\r\n</p>\r\n\r\n<mat-form-field>\r\n\r\n <mat-label>{{data.texts[2]}}</mat-label>\r\n <input matInput autoFocusOnDisplay autoSelectTextOnFocus\r\n (keyup.enter)=\"closeDialog()\"\r\n [(ngModel)]=\"inputText\">\r\n \r\n</mat-form-field>\r\n\r\n<button mat-raised-button color=\"primary\"\r\n [disabled]=\"isButtonDisabled()\"\r\n (click)=\"closeDialog()\">\r\n \r\n {{data.options[0]}}\r\n \r\n</button>", styles: [":host{min-height:300px}h3{margin-top:1vh;margin-bottom:3vh}p{margin-top:0;margin-bottom:5vh}button{min-height:5vh}\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"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i6.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TurboGuiAngularModule }, { kind: "directive", type: AutoFocusOnDisplayDirective, selector: "[autoFocusOnDisplay]" }, { kind: "directive", type: AutoSelectTextOnFocusDirective, selector: "[autoSelectTextOnFocus]" }] }); }
1914
2432
  }
1915
2433
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: DialogSingleInputComponent, decorators: [{
1916
2434
  type: Component,
1917
2435
  args: [{ selector: 'tg-dialog-single-input', standalone: true, imports: [CommonModule, MatButtonModule, MatInputModule, FormsModule,
1918
- TurboGuiAngularModule], providers: [], template: "<h3>\r\n {{data.texts[0]}}\r\n</h3>\r\n\r\n<p *ngIf=\"data.texts.length > 2\">\r\n {{data.texts[2]}}\r\n</p>\r\n\r\n<mat-form-field>\r\n\r\n <mat-label>{{data.texts[1]}}</mat-label>\r\n <input matInput autoFocusOnDisplay\r\n (keyup.enter)=\"closeDialog()\"\r\n [(ngModel)]=\"inputText\">\r\n \r\n</mat-form-field>\r\n\r\n<button mat-raised-button color=\"primary\"\r\n [disabled]=\"isInputEmpty()\"\r\n (click)=\"closeDialog()\">\r\n \r\n {{data.options[0]}}\r\n \r\n</button>", styles: [":host{min-height:300px}h3{margin-bottom:25px}p{margin-bottom:52px}button{float:right}\n"] }]
2436
+ TurboGuiAngularModule], providers: [], template: "<h3>\r\n {{data.texts[0]}}\r\n</h3>\r\n\r\n<p *ngIf=\"data.texts.length > 1 && data.texts[1] !== ''\">\r\n {{data.texts[1]}}\r\n</p>\r\n\r\n<mat-form-field>\r\n\r\n <mat-label>{{data.texts[2]}}</mat-label>\r\n <input matInput autoFocusOnDisplay autoSelectTextOnFocus\r\n (keyup.enter)=\"closeDialog()\"\r\n [(ngModel)]=\"inputText\">\r\n \r\n</mat-form-field>\r\n\r\n<button mat-raised-button color=\"primary\"\r\n [disabled]=\"isButtonDisabled()\"\r\n (click)=\"closeDialog()\">\r\n \r\n {{data.options[0]}}\r\n \r\n</button>", styles: [":host{min-height:300px}h3{margin-top:1vh;margin-bottom:3vh}p{margin-top:0;margin-bottom:5vh}button{min-height:5vh}\n"] }]
1919
2437
  }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.MatDialogRef }, { type: undefined, decorators: [{
1920
2438
  type: Inject,
1921
2439
  args: [MAT_DIALOG_DATA]
@@ -2281,7 +2799,7 @@ class GUINotification {
2281
2799
  */
2282
2800
  /**
2283
2801
  * Defines an application view.
2284
- * Our View components must extend this class so they can be manipulated via ViewsService and be correctly linked to a view service.
2802
+ * Our View components must extend this class so they can be manipulated via ViewsService and be correctly linked to a views-container.
2285
2803
  */
2286
2804
  class View {
2287
2805
  /**
@@ -2425,5 +2943,5 @@ class ValidatorsPlus extends Validators {
2425
2943
  * Generated bundle index. Do not edit.
2426
2944
  */
2427
2945
 
2428
- 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, LocalizationService, NotificationService, SingletoneStrictClass, TurboApiCallerService, TurboGuiAngularModule, ValidatorsPlus, View, ViewService, ViewsContainerComponent, ViewsService };
2946
+ 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, LocalesService, NotificationService, SingletoneStrictClass, TurboApiCallerService, TurboGuiAngularModule, ValidatorsPlus, View, ViewService, ViewsContainerComponent, ViewsService };
2429
2947
  //# sourceMappingURL=turbogui-angular.mjs.map