UNPKG

@ui-tool/core

Version:
1,285 lines (1,231 loc) 178 kB
import * as i0 from '@angular/core'; import { InjectionToken, ComponentFactoryResolver, ChangeDetectorRef, ViewContainerRef, Component, ChangeDetectionStrategy, Input, ViewChild, PLATFORM_ID, NgModule, Directive, Inject, InjectFlags, Injector, Pipe, HostBinding, Injectable, Optional, ContentChildren } from '@angular/core'; import { Subscription, of, throwError, ReplaySubject, Subject, from, forkJoin } from 'rxjs'; import { Router, RouterEvent, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, RouterModule } from '@angular/router'; import { switchMap, map, filter, tap, mergeMap, retryWhen, catchError, distinctUntilChanged, debounceTime, delay } from 'rxjs/operators'; import { findLastIndex, merge, cloneDeep } from 'lodash-es'; import * as i1 from '@angular/common'; import { isPlatformBrowser, CommonModule, DOCUMENT } from '@angular/common'; import { v4 } from 'uuid'; import * as i1$1 from '@angular/forms'; import { FormControl, FormControlDirective, NgModel, AbstractControl, NgControl, FormControlName, FormArray, FormGroup } from '@angular/forms'; import { __decorate, __param } from 'tslib'; import * as i1$2 from '@angular/platform-browser'; // Multiple validation summarizer const COMMON_VALIDATOR_SERVICE = new InjectionToken('COMMON_VALIDATOR_SERVICE'); const COMMON_VALIDATOR_OPTIONS = new InjectionToken('COMMON_VALIDATOR_OPTIONS'); const COMMON_VALIDATOR_OPTIONS_PROVIDER = new InjectionToken('COMMON_VALIDATOR_OPTIONS_PROVIDER'); // Spinner providers. const SPINNER_SERVICE = new InjectionToken('SPINNER_SERVICE'); const SPINNER_METHOD_INVOKE_CALLBACK = new InjectionToken('SPINNER_METHOD_INVOKE_CALLBACK'); const SPINNER_REQUEST_ID = new InjectionToken('SPINNER_REQUEST_ID'); const SPINNER_HOST = new InjectionToken('SPINNER_HOST'); const DISPLAY_SPINNER_OPTIONS = new InjectionToken('DISPLAY_SPINNER_OPTIONS'); // Banner providers. const BANNER_SERVICE = new InjectionToken('BANNER_SERVICE'); const BANNER_BUILDER = new InjectionToken('BANNER_BUILDER'); // Smart navigator providers. const SMART_NAVIGATOR_SERVICE = new InjectionToken('SMART_NAVIGATOR_SERVICE'); const SMART_NAVIGATOR_ROUTES = new InjectionToken('SMART_NAVIGATOR_ROUTES'); const SMART_NAVIGATOR_SCREEN_CODE_RESOLVER = new InjectionToken('SMART_NAVIGATOR_SCREEN_CODE_RESOLVER'); // Dialog providers. const DIALOG_BUILDER = new InjectionToken('DIALOG_BUILDER'); const DIALOG_SERVICE = new InjectionToken('DIALOG_SERVICE'); // Windows provider. const WINDOW = new InjectionToken('WINDOW'); // Sentinel providers const REQUIREMENT_SENTINEL_SERVICE = new InjectionToken('REQUIREMENT_SENTINEL_SERVICE'); const REQUIREMENT_SENTINEL_EXCEPTION_PROCESSOR = new InjectionToken('REQUIREMENT_SENTINEL_EXCEPTION_PROCESSOR'); const REQUIREMENT_HANDLER = new InjectionToken('REQUIREMENT_HANDLER'); const FEATURE_SENTINEL_SERVICE = new InjectionToken('FEATURE_SENTINEL_SERVICE'); const ROLE_SENTINEL_SERVICE = new InjectionToken('ROLE_SENTINEL_SERVICE'); const STACK_SERVICE = new InjectionToken('STACK_SERVICE'); const ASSET_SERVICE = new InjectionToken('ASSET_SERVICE'); // Multiple validation summarizer const MULTIPLE_VALIDATION_SUMMARIZER_SERVICE = new InjectionToken('MULTIPLE_VALIDATION_SUMMARIZER_SERVICE'); const MULTIPLE_VALIDATION_SUMMARIZER_OPTIONS = new InjectionToken('MULTIPLE_VALIDATION_SUMMARIZER_OPTIONS_PROVIDER'); const MULTIPLE_VALIDATION_SUMMARIZER_OPTIONS_PROVIDER = new InjectionToken('MULTIPLE_VALIDATION_SUMMARIZER_OPTIONS_PROVIDER'); // Validation summarizer providers. const VALIDATION_SUMMARIZER_SERVICE = new InjectionToken('VALIDATION_SUMMARIZER_PROVIDER'); const VALIDATION_ITEM_TEMPLATE_BUILDERS = new InjectionToken('VALIDATION_ITEM_TEMPLATE_BUILDERS_PROVIDER'); const VALIDATION_SUMMARIZER_OPTIONS_PROVIDER = new InjectionToken('VALIDATION_SUMMARIZER_OPTION_PROVIDER'); // Key-value which is used for mapping validation error with its actual display. const builtInValidationMessages = { required: 'MSG_VALIDATION_PROPERTY_REQUIRED', min: 'MSG_VALIDATION_PROPERTY_MIN', max: 'MSG_VALIDATION_PROPERTY_MAX', minlength: 'MSG_VALIDATION_PROPERTY_MIN_LENGTH', maxlength: 'MSG_VALIDATION_PROPERTY_MAX_LENGTH', pattern: 'MSG_VALIDATION_PROPERTY_PATTERN', email: 'MSG_VALIDATION_PROPERTY_EMAIL' }; class DialogResultConstant { } //#region Properties DialogResultConstant.reject = 'DISMISS'; DialogResultConstant.resolve = 'MANUALLY_CLOSED'; class DialogBuilderExceptionConstant { } //#region Properties DialogBuilderExceptionConstant.invalidDialogSettings = 'INVALID_DIALOG_SETTINGS'; class DialogKindConstant { } //#region Properties DialogKindConstant.confirmation = 'confirmation'; DialogKindConstant.error = 'error'; DialogKindConstant.info = 'info'; DialogKindConstant.warning = 'warning'; class SmartNavigatorExceptions { } //#region Properties SmartNavigatorExceptions.invalidScreenCode = 'INVALID_SCREEN_CODE'; SmartNavigatorExceptions.invalidNavigationRequest = 'INVALID_NAVIGATION_REQUEST'; SmartNavigatorExceptions.screenCodeMappingNotFound = 'SCREEN_CODE_MAPPING_NOT_FOUND'; SmartNavigatorExceptions.urlNotFound = 'SCREEN_URL_NOT_FOUND'; class SpinnerCommands { } //#region Properties SpinnerCommands.display = 'DISPLAY_SPINNER'; SpinnerCommands.close = 'CLOSE_SPINNER'; SpinnerCommands.purge = 'PURGE_SPINNER'; var StackItemEvents; (function (StackItemEvents) { StackItemEvents["ITEM__DELETED"] = "ITEM__DELETED"; StackItemEvents["ITEM__PUSHED"] = "ITEM__PUSHED"; })(StackItemEvents || (StackItemEvents = {})); var Visibilities; (function (Visibilities) { Visibilities[Visibilities["hidden"] = 0] = "hidden"; Visibilities[Visibilities["visible"] = 1] = "visible"; })(Visibilities || (Visibilities = {})); class ConfirmationDialogSettings { //#endregion //#region Constructor constructor(message, title, icon, injector) { this.kind = DialogKindConstant.confirmation; this.message = message; this.title = title || ''; this.icon = icon; this.injector = injector; this.backdropClasses = []; this.buttons = []; this.centered = true; this.dialogClasses = []; this.disableClose = false; } } // tslint:disable-next-line:no-empty-interface class ErrorDialogSettings { //#endregion //#region Constructor constructor(message, title, icon, injector) { this.kind = DialogKindConstant.error; this.message = message; this.title = title || ''; this.icon = icon; this.injector = injector; this.backdropClasses = []; this.buttons = []; this.centered = true; this.dialogClasses = []; this.disableClose = false; } } // tslint:disable-next-line:no-empty-interface class InfoDialogSettings { //#endregion //#region Constructor constructor(message, title, icon, injector) { this.kind = DialogKindConstant.info; this.message = message; this.title = title || ''; this.icon = icon; this.injector = injector; this.backdropClasses = []; this.buttons = []; this.centered = true; this.dialogClasses = []; this.disableClose = false; } } class WarningDialogSettings { //#endregion //#region Constructor constructor(message, title, icon, injector) { this.kind = DialogKindConstant.warning; this.message = message; this.title = title || ''; this.icon = icon; this.injector = injector; this.backdropClasses = []; this.buttons = []; this.centered = true; this.dialogClasses = []; this.disableClose = false; } } class BasicDialogButton { //#endregion //#region Constructor constructor(content, clickHandler, classes) { this.content = content; this.clickHandler = clickHandler; this.classes = classes; } } class DialogResult { //#endregion //#region Constructor constructor(action, data) { this._action = action; this._data = data; } //#endregion //#region Accessor // Modal dialog action. get action() { return this._action; } // Data. get data() { return this._data; } } class TemplateDialogButton { //#endregion //#region Constructor constructor(content) { this.content = content; } } class NavigateToScreenRequest { //#endregion //#region Constructor constructor(code, routeParams, extras) { this.code = code; this.routeParams = routeParams; this.extras = extras; } } class DeleteSpinnerCommand { //#endregion //#region Constructor constructor(containerId, id) { this.kind = SpinnerCommands.close; this.containerId = containerId; this.id = id; } } class DisplaySpinnerCommand { //#endregion //#region Constructor constructor(containerId, id, options) { this.containerId = containerId; this.id = id; this.options = options; this.kind = SpinnerCommands.display; } } class DisplaySpinnerOptions { } class PurgeSpinnerCommand { //#endregion //#region Constructor constructor(containerId) { this.containerId = containerId; this.kind = SpinnerCommands.purge; } } class StackItemOptions { } class StackItemDeletedEvent { //#endregion //#region Constructor constructor(stackCode, itemIds) { this.stackCode = stackCode; this.itemIds = itemIds; this.kind = StackItemEvents.ITEM__DELETED; this.stackCode = stackCode; } } class StackItemPushedEvent { //#endregion //#region Constructor constructor(stackCode, id, itemIds) { this.stackCode = stackCode; this.id = id; this.itemIds = itemIds; this.kind = StackItemEvents.ITEM__PUSHED; this.stackCode = stackCode; this.id = id; } } class ValidationItemBuildContext { //#region Constructor constructor(containerGroupId, label, validationMessage, control) { this.containerGroupId = containerGroupId; this.label = label; this.validationMessage = validationMessage; this.control = control; } } class ValidationMessage { //#endregion //#region Constructor constructor(key, content) { this.key = key; this.content = content; this.additionalValue = {}; } } class AssetFile { //#endregion //#region Properties constructor(kind) { this.kind = kind; } } class JsScriptFile extends AssetFile { //#region Constructor constructor(link) { super('js-script'); this.link = link; } } class CssFile extends AssetFile { //#region Constructor constructor(href) { super('css'); this.href = href; } } class AssetLoadResult { //#endregion //#region Properties constructor(kind) { this.kind = kind; this.successful = false; } } class HtmlContent { //#region Constructor constructor(content) { this.content = content; } } // Implementations export. class BannerComponent { //#endregion //#region Constructor constructor(injector) { this.injector = injector; this.id = ''; this.queryMode = 'pop'; this.preserveMode = 'navigate-start-clear'; this.container = null; this._destroyBannerTimer = null; this._displayingRequest = null; this._displayRequests = []; // Service reflection. this.bannerService = this.injector.get(BANNER_SERVICE); this.componentFactoryResolver = this.injector.get(ComponentFactoryResolver); this.router = this.injector.get(Router); this.windowService = this.injector.get(WINDOW); this.bannerBuilders = this.injector.get(BANNER_BUILDER); this.changeDetectorRef = this.injector.get(ChangeDetectorRef); this._subscription = new Subscription(); } //#endregion //#region Methods ngAfterViewInit() { const id = this.id; // Subscription about banner display requested. const displayBannerSubscription = this.bannerService .addedRequestEvent .subscribe(request => { // Container is invalid. if (!this.container) { return; } // Add the request into list. this._displayRequests.push(request); // No banner has been displayed before. if (!this._displayingRequest) { this.bannerService.displayNextBanner(id); } }); this._subscription.add(displayBannerSubscription); // Subscription which raises when next banner display is requested. const nextBannerDisplayRequestSubscription = this.bannerService .nextBannerDisplayRequested .pipe(switchMap(request => { // Invalid container. if (request.containerId && request.containerId !== id) { return of(void (0)); } if (!request.containerId) { return of(void (0)); } // Get the next settings. const nextRequest = this.queryMode === 'pop' ? this.popRequest(request.containerId) : this.dequeueRequest(request.containerId); if (!nextRequest) { // Clear the host view. if (this.container) { this.container.clear(); this._displayingRequest = null; } return of(void (0)); } return this.displayBannerAsync(nextRequest) .pipe(map(_ => void (0))); })) .subscribe(() => { this.changeDetectorRef.markForCheck(); }); this._subscription.add(nextBannerDisplayRequestSubscription); // Listen to navigation event. const navigationEventSubscription = this.router .events .pipe(filter(e => e instanceof RouterEvent), filter(e => (e instanceof NavigationCancel) || (e instanceof NavigationEnd) || (e instanceof NavigationError))) .subscribe(e => { if (!this.container) { return; } if (((e instanceof NavigationCancel) || (e instanceof NavigationEnd) || (e instanceof NavigationError)) && this.preserveMode === 'navigate-end-clear') { this.container.clear(); this.changeDetectorRef.markForCheck(); } if ((e instanceof NavigationStart) && this.preserveMode === 'navigate-start-clear') { this.container.clear(); this.changeDetectorRef.markForCheck(); } }); this._subscription.add(navigationEventSubscription); // Hook delete display banner request. this.hookDeleteRequestEvent(); } // Called when component is destroyed. ngOnDestroy() { this._subscription?.unsubscribe(); // Clear the previous timeout. if (this._destroyBannerTimer) { this.windowService.clearTimeout(this._destroyBannerTimer); } } //#endregion //#region Internal methods hookDeleteRequestEvent() { const deleteRequestSubscription = this.bannerService .deleteRequestEvent .subscribe(deleteRequest => { // Container is invalid. if (!this.container) { return; } let index = 0; while (index < this._displayRequests.length) { // Container is invalid. if (deleteRequest.containerId && deleteRequest.containerId !== this.id) { index++; continue; } // Request id is invalid. if (deleteRequest.id && deleteRequest.id !== this._displayingRequest?.id) { index++; continue; } this._displayRequests.splice(index, 1); } if (!this.ableToDeleteDisplayingRequest(deleteRequest)) { return; } this.container.clear(); this._displayingRequest = null; this.changeDetectorRef.markForCheck(); }); this._subscription.add(deleteRequestSubscription); } // Display banner by handling request. displayBannerAsync(bannerDisplayRequest) { // Invalid request. if (!bannerDisplayRequest) { return of(void (0)); } // Request does not belong to the current container. if (bannerDisplayRequest.containerId && this.id && bannerDisplayRequest.containerId !== this.id) { return of(void (0)); } const settings = bannerDisplayRequest.settings; if (!settings) { return of(void (0)); } // Clear the previous timeout. if (this._destroyBannerTimer) { this.windowService.clearTimeout(this._destroyBannerTimer); } let itemIndex = 0; const builders = this.bannerBuilders; const isNotAbleToBuildException = 'IS_NOT_ABLE_TO_BE_BUILT'; const noBuilderAvailableException = 'NO_BUILDER_AVAILABLE'; const maxRetriesExceeded = 'MAX_RETRIES_EXCEEDED'; return of(void (0)) .pipe(tap(_ => { if (!builders) { throw noBuilderAvailableException; } if (itemIndex > builders.length - 1) { throw maxRetriesExceeded; } }), mergeMap(_ => builders[itemIndex].canBuildAsync(bannerDisplayRequest.settings)), mergeMap(ableToBuild => { if (!ableToBuild) { return throwError(isNotAbleToBuildException); } return builders[itemIndex].buildAsync(bannerDisplayRequest.settings) .pipe(tap((componentRef) => { if (!this.container) { return; } // Update the request. this._displayingRequest = bannerDisplayRequest; const hookDisposeRequest = componentRef.instance .disposeRequestingEvent .subscribe((_) => { this.bannerService.displayNextBanner(this.id); }); componentRef.onDestroy(() => { if (hookDisposeRequest && !hookDisposeRequest.closed) { hookDisposeRequest.unsubscribe(); } // Mark no request to be displayed. this._displayingRequest = null; }); // Detect changes. componentRef.changeDetectorRef.detectChanges(); this.container.clear(); this.container.insert(componentRef.hostView); if (settings.timeout && settings.timeout.duration && settings.timeout.action) { this._destroyBannerTimer = this.windowService .setTimeout(() => { // Do action on timeout. if (settings && settings.timeout) { settings.timeout.action(); } }, settings.timeout.duration); } }), map(_ => void (0))); }), retryWhen(exceptionObservable => { return exceptionObservable .pipe(tap(exception => { if (exception !== isNotAbleToBuildException) { throw exception; } itemIndex++; })); })); //#endregion } // Whether displaying request is removable or not. ableToDeleteDisplayingRequest(deleteRequest) { if (deleteRequest.containerId && deleteRequest.containerId !== this.id) { return false; } if (deleteRequest.id && deleteRequest.id !== this._displayingRequest?.id) { return false; } return true; } // Dequeue request. dequeueRequest(containerId) { if (!this._displayRequests || !this._displayRequests.length) { return null; } // Get first match item in the messages list. const itemIndex = this._displayRequests.findIndex(x => x.containerId === containerId); if (itemIndex < 0) { return null; } const item = this._displayRequests[itemIndex]; this._displayRequests.splice(itemIndex, 1); return item; } // Pop request. popRequest(containerId) { if (!this._displayRequests || !this._displayRequests.length) { return null; } // Find the last index of item. const lastIndex = findLastIndex(this._displayRequests, x => x.containerId === containerId); if (lastIndex < 0) { return null; } const item = this._displayRequests[lastIndex]; this._displayRequests.splice(lastIndex, 1); return item; } } BannerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: BannerComponent, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component }); BannerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: BannerComponent, selector: "cms-banner", inputs: { id: "id", queryMode: ["query-mode", "queryMode"], preserveMode: ["preserve-mode", "preserveMode"] }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "<ng-template #container></ng-template>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: BannerComponent, decorators: [{ type: Component, args: [{ selector: 'cms-banner', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template #container></ng-template>\n" }] }], ctorParameters: function () { return [{ type: i0.Injector }]; }, propDecorators: { id: [{ type: Input }], queryMode: [{ type: Input, args: ['query-mode'] }], preserveMode: [{ type: Input, args: ['preserve-mode'] }], container: [{ type: ViewChild, args: ['container', { read: ViewContainerRef }] }] } }); class BannerService { //#endregion //#region Constructor constructor() { this._bannerDisplayRequestSubject = new ReplaySubject(1); this._deleteRequestSubject = new ReplaySubject(1); this._displayNextSubject = new ReplaySubject(1); this.addedRequestEvent = this._bannerDisplayRequestSubject.asObservable(); this.deleteRequestEvent = this._deleteRequestSubject.asObservable(); this.nextBannerDisplayRequested = this._displayNextSubject.asObservable(); } //#endregion //#region Methods // Display banner using specific settings. // If container id is defined, only the contain whose id matches will display the banner. addBanner(settings, containerId) { const request = { id: settings.id, containerId, settings }; this._bannerDisplayRequestSubject.next(request); return request.id; } // Find and delete banner by id. // No id is defined, all banner will be deleted. deleteBanner(id, containerId) { const deleteRequest = { id, containerId }; this._deleteRequestSubject.next(deleteRequest); } // Get the next banner and display it inside a specific container. // If no container id is defined, display next banner into every banner. displayNextBanner(containerId) { const request = { containerId }; this._displayNextSubject.next(request); } } /* Define abstract class for obtaining reference to the global window object. */ class WindowRef { // tslint:disable-next-line:ban-types get nativeWindow() { throw new Error('Not implemented.'); } } /* Define class that implements the abstract class and returns the native window object. */ class BrowserWindowRef extends WindowRef { constructor() { super(); } // tslint:disable-next-line:ban-types get nativeWindow() { return window; } } /* Create an factory function that returns the native window object. */ // tslint:disable-next-line:ban-types function windowFactory(browserWindowRef, platformId) { if (isPlatformBrowser(platformId)) { return browserWindowRef.nativeWindow; } return {}; } /* Create a injectable provider for the WindowRef token that uses the BrowserWindowRef class. */ const browserWindowProvider = { provide: WindowRef, useValue: new BrowserWindowRef() }; /* Create an injectable provider that uses the windowFactory function for returning the native window object. */ const windowProvider = { provide: WINDOW, useFactory: windowFactory, deps: [WindowRef, PLATFORM_ID] }; /* Create an array of providers. */ const WINDOW_PROVIDERS = [ browserWindowProvider, windowProvider ]; // Child validation summarizer providers // Validation summarizer option. // tslint:disable-next-line:max-line-length const VALIDATION_SUMMARIZER_OPTIONS = new InjectionToken('VALIDATION_SUMMARIZER_OPTIONS_PROVIDER'); const NULL_VALIDATION_SUMMARIZER_PROVIDER = new InjectionToken('BLANK_VALIDATION_SUMMARIZER_PROVIDER'); // Common validator const NULL_COMMON_VALIDATOR_SERVICE = new InjectionToken('NULL_COMMON_VALIDATOR_SERVICE'); // Banner const NULL_BANNER_SERVICE_PROVIDER = new InjectionToken('NULL_BANNER_SERVICE_PROVIDER'); const NULL_BANNER_CONTENT_BUILDER_SERVICE_PROVIDER = new InjectionToken('NULL_BANNER_CONTENT_BUILDER_SERVICE_PROVIDER'); // Validation pipe const HAS_ANY_VALIDATOR_SERVICE = new InjectionToken('HAS_ANY_VALIDATOR_SERVICE'); //#region Services // Build banner service. function buildBannerService(option) { return new BannerService(); } // Build null banner service. function buildNullBannerProvider() { return { provide: NULL_BANNER_SERVICE_PROVIDER, useValue: {} }; } // Build banner provider. function buildBannerProvider() { return { provide: BANNER_SERVICE, useFactory: buildBannerService }; } //#endregion //#region Content builders function buildEmptyContentBuilderProvider() { return { provide: NULL_BANNER_CONTENT_BUILDER_SERVICE_PROVIDER, useValue: {} }; } //#endregion class BannerModule { //#region Methods static forRoot(options) { return { ngModule: BannerModule, providers: [ // Banner service registration. (options || {}).serviceProvider || buildBannerProvider(), // Banner content builder. (options || {}).contentBuilderProviders || buildEmptyContentBuilderProvider() ] }; } static forChild(options) { return { ngModule: BannerModule, providers: [ // Banner service registration. (options || {}).serviceProvider || buildNullBannerProvider(), // Banner content builder. (options || {}).contentBuilderProviders || buildEmptyContentBuilderProvider() ] }; } } BannerModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: BannerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); BannerModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: BannerModule, declarations: [BannerComponent], imports: [CommonModule, RouterModule], exports: [BannerComponent] }); BannerModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: BannerModule, providers: [ WINDOW_PROVIDERS, { provide: BANNER_SERVICE, useClass: BannerService } ], imports: [[ CommonModule, RouterModule ]] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: BannerModule, decorators: [{ type: NgModule, args: [{ declarations: [ BannerComponent ], exports: [ BannerComponent ], imports: [ CommonModule, RouterModule ], providers: [ WINDOW_PROVIDERS, { provide: BANNER_SERVICE, useClass: BannerService } ] }] }] }); var RequirementSentinelExceptionCodes; (function (RequirementSentinelExceptionCodes) { RequirementSentinelExceptionCodes["NO_REQUIREMENT_HANDLER_AT_ALL"] = "NO_REQUIREMENT_HANDLER_AT_ALL"; RequirementSentinelExceptionCodes["REQUIREMENT_NOT_FOUND_FOR_SPECIFIC_NAME"] = "REQUIREMENT_NOT_FOUND_FOR_SPECIFIC_NAME"; RequirementSentinelExceptionCodes["FAILED_WHILE_HANDLING_EXTERNAL_BUSINESS"] = "FAILED_WHILE_HANDLING_EXTERNAL_BUSINESS"; })(RequirementSentinelExceptionCodes || (RequirementSentinelExceptionCodes = {})); var RequirementSentinelExceptionTypes; (function (RequirementSentinelExceptionTypes) { RequirementSentinelExceptionTypes["INTERNAL"] = "INTERNAL"; RequirementSentinelExceptionTypes["EXTERNAL"] = "EXTERNAL"; })(RequirementSentinelExceptionTypes || (RequirementSentinelExceptionTypes = {})); class ExternalRequirementSentinelException { //#endregion //#region Constructor constructor(requirementName, code, metadata) { this.requirementName = requirementName; this.type = RequirementSentinelExceptionTypes.EXTERNAL; this.code = code; this.metadata = metadata; } } class InternalRequirementSentinelException { //#endregion //#region Constructor constructor(requirementName, code, metadata) { this.requirementName = requirementName; this.type = RequirementSentinelExceptionTypes.INTERNAL; this.code = code; this.metadata = metadata; } } class RequirementSentinelDirective { //#endregion //#region Constructor constructor(_meetRequirementService, _viewContainerRef, _templateRef) { this._meetRequirementService = _meetRequirementService; this._viewContainerRef = _viewContainerRef; this._templateRef = _templateRef; this.__requirement = undefined; this.__displayContent$ = new Subject(); this.__subscription = new Subscription(); } //#endregion //#region Accessors set requirement(value) { this.__requirement = value; this.__displayContent$.next(); } set elseTemplate(value) { this.__elseTemplateRef = value; this.__displayContent$.next(); } set context(value) { this.__context = value; this.__displayContent$.next(); } //#endregion //#region Life cycles ngOnInit() { // Display feature subscription. const displayContentSubscription = this.__displayContent$ .pipe(switchMap(() => { return this._meetRequirementService .shouldRequirementMetAsync(this.__requirement || '', this.__context) .pipe(catchError(_ => of(false))); }), distinctUntilChanged()) .subscribe(ableToAccessFeature => { this._viewContainerRef.clear(); if (!ableToAccessFeature) { if (this.__elseTemplateRef) { const elseView = this._viewContainerRef.createEmbeddedView(this.__elseTemplateRef); elseView.markForCheck(); } return; } const embeddedView = this._viewContainerRef.createEmbeddedView(this._templateRef); embeddedView.markForCheck(); }); this.__subscription.add(displayContentSubscription); // Hook validation event from service. const hookValidationEventSubscription = this._meetRequirementService .hookValidationEventAsync() .subscribe(() => { this.__displayContent$.next(); }); this.__subscription.add(hookValidationEventSubscription); // Do the first requirement check. this.__displayContent$.next(); } ngOnDestroy() { this.__subscription?.unsubscribe(); } } RequirementSentinelDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RequirementSentinelDirective, deps: [{ token: REQUIREMENT_SENTINEL_SERVICE }, { token: i0.ViewContainerRef }, { token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); RequirementSentinelDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.11", type: RequirementSentinelDirective, selector: "[meetRequirement]", inputs: { requirement: ["meetRequirement", "requirement"], elseTemplate: ["meetRequirementElse", "elseTemplate"], context: ["meetRequirementContext", "context"] }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RequirementSentinelDirective, decorators: [{ type: Directive, args: [{ selector: '[meetRequirement]' }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Inject, args: [REQUIREMENT_SENTINEL_SERVICE] }] }, { type: i0.ViewContainerRef }, { type: i0.TemplateRef }]; }, propDecorators: { requirement: [{ type: Input, args: ['meetRequirement'] }], elseTemplate: [{ type: Input, args: ['meetRequirementElse'] }], context: [{ type: Input, args: ['meetRequirementContext'] }] } }); class RequirementSentinelService { //#endregion //#region Constructor constructor(injector) { this._requirementHandlers = injector .get(REQUIREMENT_HANDLER, null, InjectFlags.Optional); this._exceptionProcessor = injector.get(REQUIREMENT_SENTINEL_EXCEPTION_PROCESSOR, null, InjectFlags.Optional); this._doValidationEvent = new ReplaySubject(1); } //#endregion //#region Methods shouldRequirementMetAsync(name, context) { // No requirement is found. if (!this._requirementHandlers || !this._requirementHandlers.length) { return this.__processExceptionAsync(new InternalRequirementSentinelException(name, RequirementSentinelExceptionCodes.NO_REQUIREMENT_HANDLER_AT_ALL)); } // Find the requirement which has the exact name with the passed one. const requirementHandler = this._requirementHandlers.find(item => item.name === name); if (!requirementHandler) { return this.__processExceptionAsync(new InternalRequirementSentinelException(name, RequirementSentinelExceptionCodes.REQUIREMENT_NOT_FOUND_FOR_SPECIFIC_NAME, { name })); } return requirementHandler.shouldRequirementMetAsync(context) .pipe(catchError(exception => { return this.__processExceptionAsync(new ExternalRequirementSentinelException(name, RequirementSentinelExceptionCodes.FAILED_WHILE_HANDLING_EXTERNAL_BUSINESS, { exception })); })); } doValidation() { this._doValidationEvent.next(); } hookValidationEventAsync() { return this._doValidationEvent.asObservable(); } //#endregion //#region Internal methods __processExceptionAsync(exceptionInstance) { if (!this._exceptionProcessor) { return of(false); } return this._exceptionProcessor.processExceptionAsync(exceptionInstance) .pipe(map(() => false)); } } function buildSentinelDirectiveService(injector) { return new RequirementSentinelService(injector); } class RequirementSentinelModule { //#region Methods static forRoot() { return { ngModule: RequirementSentinelModule, providers: [ { provide: REQUIREMENT_SENTINEL_SERVICE, useFactory: buildSentinelDirectiveService, deps: [ Injector ] } ] }; } static forChild({ providers }) { return { ngModule: RequirementSentinelModule, providers: [ ...(providers || []), { provide: REQUIREMENT_SENTINEL_SERVICE, useFactory: buildSentinelDirectiveService, deps: [ Injector ] } ] }; } } RequirementSentinelModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RequirementSentinelModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); RequirementSentinelModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RequirementSentinelModule, declarations: [RequirementSentinelDirective], exports: [RequirementSentinelDirective] }); RequirementSentinelModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RequirementSentinelModule }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RequirementSentinelModule, decorators: [{ type: NgModule, args: [{ declarations: [ RequirementSentinelDirective ], exports: [ RequirementSentinelDirective ] }] }] }); class FeatureSentinelDirective { //#endregion //#region Constructor constructor(_requireFeaturePermissionService, _viewContainerRef, _templateRef) { this._requireFeaturePermissionService = _requireFeaturePermissionService; this._viewContainerRef = _viewContainerRef; this._templateRef = _templateRef; this.__names = []; this.__displayFeatureContent$ = new ReplaySubject(); this._subscription = new Subscription(); } //#endregion //#region Accessors set name(value) { if (value instanceof Array) { this.__names = value; } else { this.__names = [value]; } this.__displayFeatureContent$.next(); } set elseTemplate(value) { this.__elseTemplateRef = value; this.__displayFeatureContent$.next(); } //#endregion //#region Methods ngOnInit() { // Display feature subscription. const displayFeatureContentSubscription = this.__displayFeatureContent$ .pipe(switchMap(() => { return this._requireFeaturePermissionService .ableToAccessFeaturesAsync(this.__names) .pipe(catchError(_ => of(false))); }), distinctUntilChanged()) .subscribe(ableToAccessFeature => { this._viewContainerRef.clear(); if (!ableToAccessFeature) { if (this.__elseTemplateRef) { const elseView = this._viewContainerRef.createEmbeddedView(this.__elseTemplateRef); elseView.markForCheck(); } return; } else { const embeddedView = this._viewContainerRef.createEmbeddedView(this._templateRef); embeddedView.markForCheck(); } }); this._subscription.add(displayFeatureContentSubscription); // Hook validation event from service. const hookValidationEventSubscription = this._requireFeaturePermissionService .hookValidationEventAsync() .subscribe(() => { this.__displayFeatureContent$.next(); }); this._subscription.add(hookValidationEventSubscription); this.__displayFeatureContent$.next(); } ngOnDestroy() { this.__displayFeatureContent$.complete(); this._subscription?.unsubscribe(); } } FeatureSentinelDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: FeatureSentinelDirective, deps: [{ token: FEATURE_SENTINEL_SERVICE }, { token: i0.ViewContainerRef }, { token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); FeatureSentinelDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.11", type: FeatureSentinelDirective, selector: "[hasFeatureAccess]", inputs: { name: ["hasFeatureAccess", "name"], elseTemplate: ["hasFeatureAccessElse", "elseTemplate"] }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: FeatureSentinelDirective, decorators: [{ type: Directive, args: [{ selector: '[hasFeatureAccess]' }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Inject, args: [FEATURE_SENTINEL_SERVICE] }] }, { type: i0.ViewContainerRef }, { type: i0.TemplateRef }]; }, propDecorators: { name: [{ type: Input, args: ['hasFeatureAccess'] }], elseTemplate: [{ type: Input, args: ['hasFeatureAccessElse'] }] } }); class FeatureSentinelModule { //#region Properties static forRoot(option) { return { ngModule: FeatureSentinelModule, providers: option.providers || [] }; } } FeatureSentinelModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: FeatureSentinelModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); FeatureSentinelModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: FeatureSentinelModule, declarations: [FeatureSentinelDirective], exports: [FeatureSentinelDirective] }); FeatureSentinelModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: FeatureSentinelModule }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: FeatureSentinelModule, decorators: [{ type: NgModule, args: [{ declarations: [ FeatureSentinelDirective ], exports: [ FeatureSentinelDirective ] }] }] }); class RoleSentinelDirective { //#endregion //#region Constructor constructor(_requireRolePermissionService, _viewContainerRef, _templateRef) { this._requireRolePermissionService = _requireRolePermissionService; this._viewContainerRef = _viewContainerRef; this._templateRef = _templateRef; this.__names = []; this.__displayRoleContent$ = new ReplaySubject(1); this._subscription = new Subscription(); } //#endregion //#region Accessors set name(value) { if (value instanceof Array) { this.__names = value; } else { this.__names = [value]; } this.__displayRoleContent$.next(); } set elseTemplate(value) { this.__elseTemplateRef = value; this.__displayRoleContent$.next(); } //#endregion //#region Methods ngOnInit() { const displayFeatureContentSubscription = this.__displayRoleContent$ .pipe(switchMap(() => { return this._requireRolePermissionService .hasAnyRoleAsync(this.__names) .pipe(catchError(_ => of(false))); }), distinctUntilChanged()) .subscribe(ableToAccessFeature => { this._viewContainerRef.clear(); if (!ableToAccessFeature) { if (this.__elseTemplateRef) { const elseView = this._viewContainerRef.createEmbeddedView(this.__elseTemplateRef); elseView.markForCheck(); } return; } const embeddedView = this._viewContainerRef.createEmbeddedView(this._templateRef); embeddedView.markForCheck(); }); // Trigger validation. this.__displayRoleContent$.next(); const hookRoleValidationSubscription = this._requireRolePermissionService .hookValidationEventAsync() .subscribe(() => { this.__displayRoleContent$.next(); }); this._subscription.add(hookRoleValidationSubscription); this._subscription.add(displayFeatureContentSubscription); } ngOnDestroy() { this._subscription?.unsubscribe(); this.__displayRoleContent$.complete(); } } RoleSentinelDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RoleSentinelDirective, deps: [{ token: ROLE_SENTINEL_SERVICE }, { token: i0.ViewContainerRef }, { token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); RoleSentinelDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.11", type: RoleSentinelDirective, selector: "[hasRoles]", inputs: { name: ["hasRoles", "name"], elseTemplate: ["hasRolesElse", "elseTemplate"] }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RoleSentinelDirective, decorators: [{ type: Directive, args: [{ selector: '[hasRoles]' }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Inject, args: [ROLE_SENTINEL_SERVICE] }] }, { type: i0.ViewContainerRef }, { type: i0.TemplateRef }]; }, propDecorators: { name: [{ type: Input, args: ['hasRoles'] }], elseTemplate: [{ type: Input, args: ['hasRolesElse'] }] } }); class RoleSentinelModule { //#region Properties static forRoot(option) { return { ngModule: RoleSentinelModule, providers: option.providers || [] }; } } RoleSentinelModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RoleSentinelModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); RoleSentinelModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RoleSentinelModule, declarations: [RoleSentinelDirective], exports: [RoleSentinelDirective] }); RoleSentinelModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RoleSentinelModule }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RoleSentinelModule, decorators: [{ type: NgModule, args: [{ declarations: [ RoleSentinelDirective ], exports: [ RoleSentinelDirective ] }] }] }); class SmartNavigatorService { //#endregion //#region Constructor constructor(injector) { this.injector = injector; const codeToUrlMappings = this.injector.get(SMART_NAVIGATOR_ROUTES); this._router = this.injector.get(Router); this._screenCodeResolvers = this.injector.get(SMART_NAVIGATOR_SCREEN_CODE_RESOLVER, null, InjectFlags.Optional); this.__codeToUrlMappings = {}; if (codeToUrlMappings) { this.__codeToUrlMappings = merge({}, codeToUrlMappings); } } //#endregion //#endregion //#region Methods // Navigate to a screen by using screen code. navigateToScreenAsync(request) { if (!request) { throw new Error(SmartNavigatorExceptions.invalidNavigationRequest); } // Get raw url from screen code. const rawUrl = this.loadRawUrl(request.code); const compiled = this.__template(rawUrl); const fullUrl = compiled(request.routeParams); return from(this._router.navigate([fullUrl], request.extras)); } // Get raw url. loadRawUrl(code) { if (!code || !code.length) { throw new Error(SmartNavigatorExceptions.invalidScreenCode); } let url = this.__codeToUrlMappings[code]; if (url) { return url; } const screenCodeResolvers = this._screenCodeResolvers; if (!screenCodeResolvers || !screenCodeResolvers.length) { throw new Error(SmartNavigatorExceptions.invalidScreenCode); } for (const screenCodeResolver of screenCodeResolvers) { url = screenCodeResolver.loadUrl(code); if (url && url.length) { return url; } } throw new Error(SmartNavigatorExceptions.invalidS