keycloak-angular 19.0.2 → 20.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/README.md CHANGED
@@ -52,8 +52,9 @@ Note that `keycloak-js` is a peer dependency of `keycloak-angular`. This allows
52
52
 
53
53
  | Angular | keycloak-js | keycloak-angular | Support |
54
54
  | :-----: | :---------: | :--------------: | :-----------------: |
55
- | 19.x | 18 - 26 | 19.x.x | New Features / Bugs |
56
- | 18.x | 18 - 26 | 16.x.x | Bugs |
55
+ | 20.x | 18 - 26 | 20.x.x | New Features / Bugs |
56
+ | 19.x | 18 - 26 | 19.x.x | Bugs |
57
+ | 18.x | 18 - 26 | 16.x.x | - |
57
58
  | 17.x | 18 - 25 | 15.x.x | - |
58
59
  | 16.x | 18 - 25 | 14.x.x | - |
59
60
  | 15.x | 18 - 21 | 13.x.x | - |
@@ -121,6 +122,41 @@ Create a file named `silent-check-sso.html` in the `public` or `assets` director
121
122
 
122
123
  If you want to know more about these options and various other capabilities of the Keycloak client is recommended to read the [JavaScript Adapter documentation](https://www.keycloak.org/docs/latest/securing_apps/#_javascript_adapter).
123
124
 
125
+ ## Load the keycloak configuration using an external file
126
+
127
+ To initialize the Keycloak application using a configuration file hosted externally, consider the following approach:
128
+
129
+ ```ts
130
+ const initializeApp = async () => {
131
+ // Replace the URL with the actual path to your configuration file
132
+ const { config, initOptions } = await fetch('/config.json').then((res) => res?.json());
133
+
134
+ const appConfig: ApplicationConfig = {
135
+ providers: [
136
+ provideKeycloakAngular(config, {
137
+ ...initOptions,
138
+ silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html',
139
+ redirectUri: window.location.origin + '/'
140
+ }),
141
+ provideZoneChangeDetection({ eventCoalescing: true }),
142
+ provideRouter(routes),
143
+ provideHttpClient(withInterceptors([includeBearerTokenInterceptor]))
144
+ ]
145
+ };
146
+
147
+ await bootstrapApplication(AppComponent, appConfig);
148
+ };
149
+
150
+ initializeApp().catch((error) => console.error(`Failed to initialize the application. ${error.message || error}`));
151
+ ```
152
+
153
+ **Notes**:
154
+
155
+ - This approach uses fetch to load the Keycloak configuration (config.json) before bootstrapping the Angular application.
156
+ - Make sure that config.json is accessible at runtime and contains the correct structure for Keycloak configuration and initOptions.
157
+
158
+ For a working example, refer to the [Fetch Config Example Project](projects/examples/fetch_config/README.md).
159
+
124
160
  > **Note About NgModules:**
125
161
  > Since Keycloak-Angular v19, the KeycloakAngularModule, KeycloakService, KeycloakBearerInterceptor and KeycloakAuthGuard are deprecated.
126
162
  > If your application relies on NgModules, the library still has support for it. See more information on how to configure a [NgModule the application](https://github.com/mauriciovigolo/keycloak-angular/docs/ngmodule.md).
@@ -243,6 +279,7 @@ import {
243
279
  provideKeycloak,
244
280
  createInterceptorCondition,
245
281
  IncludeBearerTokenCondition,
282
+ includeBearerTokenInterceptor,
246
283
  INCLUDE_BEARER_TOKEN_INTERCEPTOR_CONFIG
247
284
  } from 'keycloak-angular';
248
285
 
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, NgModule, signal, InjectionToken, inject, effect, Directive, Input, computed, PLATFORM_ID, provideAppInitializer, EnvironmentInjector, runInInjectionContext, makeEnvironmentProviders } from '@angular/core';
2
+ import { Injectable, inject, NgModule, signal, InjectionToken, TemplateRef, ViewContainerRef, effect, Input, Directive, NgZone, computed, PLATFORM_ID, provideAppInitializer, EnvironmentInjector, runInInjectionContext, makeEnvironmentProviders } from '@angular/core';
3
3
  import { HttpHeaders, HTTP_INTERCEPTORS } from '@angular/common/http';
4
4
  import { Subject, from, combineLatest, of, fromEvent, mergeMap as mergeMap$1 } from 'rxjs';
5
5
  import { map, mergeMap, debounceTime, takeUntil } from 'rxjs/operators';
@@ -21,7 +21,7 @@ import { CommonModule, isPlatformBrowser } from '@angular/common';
21
21
  * will be removed in future versions.
22
22
  * Use the new `KEYCLOAK_EVENT_SIGNAL` injection token to listen for the keycloak
23
23
  * events.
24
- * More info: https://github.com/mauriciovigolo/keycloak-angular/docs/migration-guides/v19.md
24
+ * More info: https://github.com/mauriciovigolo/keycloak-angular/blob/main/docs/migration-guides/v19.md
25
25
  */
26
26
  var KeycloakEventTypeLegacy;
27
27
  (function (KeycloakEventTypeLegacy) {
@@ -77,7 +77,7 @@ var KeycloakEventTypeLegacy;
77
77
  *
78
78
  * @deprecated Class based guards are deprecated in Keycloak Angular and will be removed in future versions.
79
79
  * Use the new `createAuthGuard` function to create a Guard for your application.
80
- * More info: https://github.com/mauriciovigolo/keycloak-angular/docs/migration-guides/v19.md
80
+ * More info: https://github.com/mauriciovigolo/keycloak-angular/blob/main/docs/migration-guides/v19.md
81
81
  */
82
82
  class KeycloakAuthGuard {
83
83
  constructor(router, keycloakAngular) {
@@ -119,7 +119,7 @@ class KeycloakAuthGuard {
119
119
  *
120
120
  * @deprecated This service is deprecated and will be removed in future versions.
121
121
  * Use the new `provideKeycloak` function to load Keycloak in an Angular application.
122
- * More info: https://github.com/mauriciovigolo/keycloak-angular/docs/migration-guides/v19.md
122
+ * More info: https://github.com/mauriciovigolo/keycloak-angular/blob/main/docs/migration-guides/v19.md
123
123
  */
124
124
  class KeycloakService {
125
125
  constructor() {
@@ -538,10 +538,10 @@ class KeycloakService {
538
538
  get keycloakEvents$() {
539
539
  return this._keycloakEvents$;
540
540
  }
541
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: KeycloakService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
542
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: KeycloakService }); }
541
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: KeycloakService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
542
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: KeycloakService }); }
543
543
  }
544
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: KeycloakService, decorators: [{
544
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: KeycloakService, decorators: [{
545
545
  type: Injectable
546
546
  }] });
547
547
 
@@ -560,11 +560,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImpor
560
560
  *
561
561
  * @deprecated KeycloakBearerInterceptor is deprecated and will be removed in future versions.
562
562
  * Use the new functional interceptor such as `includeBearerTokenInterceptor`.
563
- * More info: https://github.com/mauriciovigolo/keycloak-angular/docs/migration-guides/v19.md
563
+ * More info: https://github.com/mauriciovigolo/keycloak-angular/blob/main/docs/migration-guides/v19.md
564
564
  */
565
565
  class KeycloakBearerInterceptor {
566
- constructor(keycloak) {
567
- this.keycloak = keycloak;
566
+ constructor() {
567
+ this.keycloak = inject(KeycloakService);
568
568
  }
569
569
  /**
570
570
  * Calls to update the keycloak token if the request should update the token.
@@ -623,12 +623,12 @@ class KeycloakBearerInterceptor {
623
623
  return next.handle(kcReq);
624
624
  }));
625
625
  }
626
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: KeycloakBearerInterceptor, deps: [{ token: KeycloakService }], target: i0.ɵɵFactoryTarget.Injectable }); }
627
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: KeycloakBearerInterceptor }); }
626
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: KeycloakBearerInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
627
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: KeycloakBearerInterceptor }); }
628
628
  }
629
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: KeycloakBearerInterceptor, decorators: [{
629
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: KeycloakBearerInterceptor, decorators: [{
630
630
  type: Injectable
631
- }], ctorParameters: () => [{ type: KeycloakService }] });
631
+ }] });
632
632
 
633
633
  /**
634
634
  * @license
@@ -640,12 +640,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImpor
640
640
  /**
641
641
  * @deprecated NgModules are deprecated in Keycloak Angular and will be removed in future versions.
642
642
  * Use the new `provideKeycloak` function to load Keycloak in an Angular application.
643
- * More info: https://github.com/mauriciovigolo/keycloak-angular/docs/migration-guides/v19.md
643
+ * More info: https://github.com/mauriciovigolo/keycloak-angular/blob/main/docs/migration-guides/v19.md
644
644
  */
645
645
  class CoreModule {
646
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: CoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
647
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.4", ngImport: i0, type: CoreModule, imports: [CommonModule] }); }
648
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: CoreModule, providers: [
646
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: CoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
647
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.3", ngImport: i0, type: CoreModule, imports: [CommonModule] }); }
648
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: CoreModule, providers: [
649
649
  KeycloakService,
650
650
  {
651
651
  provide: HTTP_INTERCEPTORS,
@@ -654,7 +654,7 @@ class CoreModule {
654
654
  }
655
655
  ], imports: [CommonModule] }); }
656
656
  }
657
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: CoreModule, decorators: [{
657
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: CoreModule, decorators: [{
658
658
  type: NgModule,
659
659
  args: [{
660
660
  imports: [CommonModule],
@@ -679,14 +679,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImpor
679
679
  /**
680
680
  * @deprecated NgModules are deprecated in Keycloak Angular and will be removed in future versions.
681
681
  * Use the new `provideKeycloak` function to load Keycloak in an Angular application.
682
- * More info: https://github.com/mauriciovigolo/keycloak-angular/docs/migration-guides/v19.md
682
+ * More info: https://github.com/mauriciovigolo/keycloak-angular/blob/main/docs/migration-guides/v19.md
683
683
  */
684
684
  class KeycloakAngularModule {
685
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: KeycloakAngularModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
686
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.4", ngImport: i0, type: KeycloakAngularModule, imports: [CoreModule] }); }
687
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: KeycloakAngularModule, imports: [CoreModule] }); }
685
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: KeycloakAngularModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
686
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.3", ngImport: i0, type: KeycloakAngularModule, imports: [CoreModule] }); }
687
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: KeycloakAngularModule, imports: [CoreModule] }); }
688
688
  }
689
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: KeycloakAngularModule, decorators: [{
689
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: KeycloakAngularModule, decorators: [{
690
690
  type: NgModule,
691
691
  args: [{
692
692
  imports: [CoreModule]
@@ -903,10 +903,10 @@ const KEYCLOAK_EVENT_SIGNAL = new InjectionToken('Keycloak Events Signal');
903
903
  * ```
904
904
  */
905
905
  class HasRolesDirective {
906
- constructor(templateRef, viewContainer, keycloak) {
907
- this.templateRef = templateRef;
908
- this.viewContainer = viewContainer;
909
- this.keycloak = keycloak;
906
+ constructor() {
907
+ this.templateRef = inject(TemplateRef);
908
+ this.viewContainer = inject(ViewContainerRef);
909
+ this.keycloak = inject(Keycloak);
910
910
  /**
911
911
  * List of roles to validate against the resource or realm.
912
912
  */
@@ -946,15 +946,15 @@ class HasRolesDirective {
946
946
  const hasRealmRole = this.checkRealm ? this.roles.some((role) => this.keycloak.hasRealmRole(role)) : false;
947
947
  return hasResourceRole || hasRealmRole;
948
948
  }
949
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: HasRolesDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: Keycloak }], target: i0.ɵɵFactoryTarget.Directive }); }
950
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.4", type: HasRolesDirective, isStandalone: true, selector: "[kaHasRoles]", inputs: { roles: ["kaHasRoles", "roles"], resource: ["kaHasRolesResource", "resource"], checkRealm: ["kaHasRolesCheckRealm", "checkRealm"] }, ngImport: i0 }); }
949
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: HasRolesDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
950
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.3", type: HasRolesDirective, isStandalone: true, selector: "[kaHasRoles]", inputs: { roles: ["kaHasRoles", "roles"], resource: ["kaHasRolesResource", "resource"], checkRealm: ["kaHasRolesCheckRealm", "checkRealm"] }, ngImport: i0 }); }
951
951
  }
952
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: HasRolesDirective, decorators: [{
952
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: HasRolesDirective, decorators: [{
953
953
  type: Directive,
954
954
  args: [{
955
955
  selector: '[kaHasRoles]'
956
956
  }]
957
- }], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }, { type: Keycloak }], propDecorators: { roles: [{
957
+ }], ctorParameters: () => [], propDecorators: { roles: [{
958
958
  type: Input,
959
959
  args: ['kaHasRoles']
960
960
  }], resource: [{
@@ -990,8 +990,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImpor
990
990
  * if it is a browser context.
991
991
  */
992
992
  class UserActivityService {
993
- constructor(ngZone) {
994
- this.ngZone = ngZone;
993
+ constructor() {
994
+ this.ngZone = inject(NgZone);
995
995
  /**
996
996
  * Signal to store the timestamp of the last user activity.
997
997
  * The timestamp is represented as the number of milliseconds since epoch.
@@ -1059,12 +1059,12 @@ class UserActivityService {
1059
1059
  this.destroy$.next();
1060
1060
  this.destroy$.complete();
1061
1061
  }
1062
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: UserActivityService, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable }); }
1063
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: UserActivityService }); }
1062
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: UserActivityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1063
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: UserActivityService }); }
1064
1064
  }
1065
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: UserActivityService, decorators: [{
1065
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: UserActivityService, decorators: [{
1066
1066
  type: Injectable
1067
- }], ctorParameters: () => [{ type: i0.NgZone }] });
1067
+ }] });
1068
1068
 
1069
1069
  /**
1070
1070
  * @license
@@ -1085,9 +1085,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImpor
1085
1085
  * session timeout and inactivity handling.
1086
1086
  */
1087
1087
  class AutoRefreshTokenService {
1088
- constructor(keycloak, userActivity) {
1089
- this.keycloak = keycloak;
1090
- this.userActivity = userActivity;
1088
+ constructor() {
1089
+ this.keycloak = inject(Keycloak);
1090
+ this.userActivity = inject(UserActivityService);
1091
1091
  this.options = this.defaultOptions;
1092
1092
  this.initialized = false;
1093
1093
  const keycloakSignal = inject(KEYCLOAK_EVENT_SIGNAL);
@@ -1132,12 +1132,12 @@ class AutoRefreshTokenService {
1132
1132
  this.initialized = true;
1133
1133
  this.userActivity.startMonitoring();
1134
1134
  }
1135
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: AutoRefreshTokenService, deps: [{ token: Keycloak }, { token: UserActivityService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1136
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: AutoRefreshTokenService }); }
1135
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: AutoRefreshTokenService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1136
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: AutoRefreshTokenService }); }
1137
1137
  }
1138
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: AutoRefreshTokenService, decorators: [{
1138
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: AutoRefreshTokenService, decorators: [{
1139
1139
  type: Injectable
1140
- }], ctorParameters: () => [{ type: Keycloak }, { type: UserActivityService }] });
1140
+ }], ctorParameters: () => [] });
1141
1141
 
1142
1142
  /**
1143
1143
  * @license
@@ -1383,11 +1383,14 @@ const CUSTOM_BEARER_TOKEN_INTERCEPTOR_CONFIG = new InjectionToken('Include the b
1383
1383
  const customBearerTokenInterceptor = (req, next) => {
1384
1384
  const conditions = inject(CUSTOM_BEARER_TOKEN_INTERCEPTOR_CONFIG) ?? [];
1385
1385
  const keycloak = inject(Keycloak);
1386
- const matchingCondition = conditions.find(async (condition) => await condition.shouldAddToken(req, next, keycloak));
1387
- if (!matchingCondition) {
1388
- return next(req);
1389
- }
1390
- return from(conditionallyUpdateToken(req, keycloak, matchingCondition)).pipe(mergeMap$1(() => keycloak.authenticated ? addAuthorizationHeader(req, next, keycloak, matchingCondition) : next(req)));
1386
+ return from(Promise.all(conditions.map(async (condition) => await condition.shouldAddToken(req, next, keycloak)))).pipe(mergeMap$1((evaluatedConditions) => {
1387
+ const matchingConditionIndex = evaluatedConditions.findIndex(Boolean);
1388
+ const matchingCondition = conditions[matchingConditionIndex];
1389
+ if (!matchingCondition) {
1390
+ return next(req);
1391
+ }
1392
+ return from(conditionallyUpdateToken(req, keycloak, matchingCondition)).pipe(mergeMap$1(() => keycloak.authenticated ? addAuthorizationHeader(req, next, keycloak, matchingCondition) : next(req)));
1393
+ }));
1391
1394
  };
1392
1395
 
1393
1396
  /**