UNPKG

@rb-mwindh/ngx-theme-manager

Version:

Angular component to switch between different theming stylesheets

197 lines 23.9 kB
import { Inject, Injectable, InjectionToken, Optional, } from '@angular/core'; import { debounceTime, filter, map, skipWhile, Subject, take, takeUntil, takeWhile, tap, } from 'rxjs'; import * as i0 from "@angular/core"; import * as i1 from "./internal"; import * as i2 from "@angular/router"; const PREFIX = 'ngx-theme-manager'; /** * @group InjectionToken */ export const STORAGE_KEY = new InjectionToken(`${PREFIX}/STORAGE_KEY`, { factory: () => `${PREFIX}/current-theme`, }); /** * @group InjectionToken */ export const QUERY_PARAM = new InjectionToken(`${PREFIX}/QUERY_PARAM`, { factory: () => `theme`, }); /** * Service for managing and switching between different themes in an application. * * @group Services * @group Public API */ export class ThemeService { /** * Subject for triggering cleanup on service destruction. * * @private */ #destroyed; /** * Creates a new instance * * @param {ThemeRegistryService} registry - Service for registering available themes * @param {ThemeTrackingService} tracker - Service for tracking the currently selected theme * @param {ThemeStyleManagerService} manager - Service for theme discovery, activation and deactivation * @param {StorageService} storage - Service for storing the currently selected theme in the browser storage * @param {ActivatedRoute} activatedRoute - Angular service for managing the current route * @param {Router} router - Angular service for navigating between routes * @param {string} storageKey - Key for storing the currently selected theme in browser storage * @param {string} queryParam - Query parameter name for specifying the theme in the route */ constructor(registry, tracker, manager, storage, activatedRoute, router, storageKey, queryParam) { this.registry = registry; this.tracker = tracker; this.manager = manager; this.storage = storage; this.activatedRoute = activatedRoute; this.router = router; this.storageKey = storageKey; this.queryParam = queryParam; /** * Subject for triggering cleanup on service destruction. * * @private */ this.#destroyed = new Subject(); /** * Observable stream that emits the currently active theme. */ this.themes$ = this.registry.themes$; /** * Observable stream of all registered themes. */ this.currentTheme$ = this.tracker.currentTheme$; // update the current route, the browser storage and the style elements' media attribute, // when the currently selected theme changes this.tracker.currentTheme$ .pipe(filter(isNotNull), tap((theme) => this.#updateRouteParam(theme)), tap((theme) => this.#updateStoredTheme(theme)), tap((theme) => manager.use(theme)), takeUntil(this.#destroyed)) .subscribe(); // calculate the initially selected theme this.registry.themes$ .pipe(debounceTime(1), takeWhile(() => !this.tracker.currentTheme), map((themes) => themes.find((theme) => theme.defaultTheme) || themes[0]), map((defaultTheme) => ({ fromRoute: this.#themeFromRoute, fromStorage: this.#themeFromStorage, defaultTheme: defaultTheme?.id, })), map(({ fromRoute, fromStorage, defaultTheme }) => fromRoute || fromStorage || defaultTheme), tap((initialTheme) => { if (!this.tracker.currentTheme) { this.tracker.currentTheme = initialTheme; } }), take(1)) .subscribe(); // if we got a storage key, update current theme on storage changes if (!!this.storageKey) { this.storage.changes$ .pipe(skipWhile(() => !this.tracker.currentTheme), filter(({ key }) => key === this.storageKey), map(({ newValue }) => newValue), tap((theme) => (this.tracker.currentTheme = theme)), takeUntil(this.#destroyed)) .subscribe(); } // if we got a query param, update the current theme on route param changes if (!!this.queryParam) { this.activatedRoute.queryParamMap .pipe(skipWhile(() => !this.tracker.currentTheme), filter((map) => map.has(this.queryParam)), map((map) => map.get(this.queryParam)), tap((theme) => (this.tracker.currentTheme = theme)), takeUntil(this.#destroyed)) .subscribe(); } } /** * Cleanup logic to be executed when the service is destroyed. * * @internal */ ngOnDestroy() { this.#destroyed.next(); this.#destroyed.complete(); } /** * Select the theme to be used * * @param {string} theme */ selectTheme(theme) { this.tracker.currentTheme = theme; } /** * Gets the theme from the browser's storage. * * @returns {string | null} - The stored theme or null if the storage key is not provided * @private */ get #themeFromStorage() { if (!this.storageKey) { return null; } return this.storage.getItem(this.storageKey); } /** * Gets the theme from the current route's query. * * @returns {string | null} - The current theme or null if the query param is not provided * @private */ get #themeFromRoute() { if (!this.queryParam) { return null; } return this.activatedRoute.snapshot.queryParamMap.get(this.queryParam); } /** * Updates the browser's url query params with the given theme ID. * * Does nothing if the query param is not provided. * * @param {string} theme - The theme ID to update in the browser's url query params * @private */ #updateRouteParam(theme) { if (!this.queryParam) { return; } this.router.navigate([], { relativeTo: this.activatedRoute, queryParams: theme ? { [this.queryParam]: theme } : {}, queryParamsHandling: 'merge', preserveFragment: true, }); } /** * Updates the theme ID in the browser's storage. * * Does nothing if the storage key is not provided. * * @param {string} theme - The theme ID to save in the browser's storage * @private */ #updateStoredTheme(theme) { if (!this.storageKey) { return; } this.storage.setItem(this.storageKey, theme); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.1", ngImport: i0, type: ThemeService, deps: [{ token: i1.ThemeRegistryService }, { token: i1.ThemeTrackingService }, { token: i1.ThemeStyleManagerService }, { token: i1.StorageService }, { token: i2.ActivatedRoute }, { token: i2.Router }, { token: STORAGE_KEY, optional: true }, { token: QUERY_PARAM, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.1", ngImport: i0, type: ThemeService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.1", ngImport: i0, type: ThemeService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.ThemeRegistryService }, { type: i1.ThemeTrackingService }, { type: i1.ThemeStyleManagerService }, { type: i1.StorageService }, { type: i2.ActivatedRoute }, { type: i2.Router }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [STORAGE_KEY] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [QUERY_PARAM] }] }] }); /** * Type guard. * * @param {T | null]} arg - The argument to guard * @internal */ function isNotNull(arg) { return arg !== null; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGhlbWUuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL25neC10aGVtZS1tYW5hZ2VyL3NyYy9saWIvdGhlbWUuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsTUFBTSxFQUNOLFVBQVUsRUFDVixjQUFjLEVBRWQsUUFBUSxHQUNULE1BQU0sZUFBZSxDQUFDO0FBRXZCLE9BQU8sRUFDTCxZQUFZLEVBQ1osTUFBTSxFQUNOLEdBQUcsRUFDSCxTQUFTLEVBQ1QsT0FBTyxFQUNQLElBQUksRUFDSixTQUFTLEVBQ1QsU0FBUyxFQUNULEdBQUcsR0FDSixNQUFNLE1BQU0sQ0FBQzs7OztBQVFkLE1BQU0sTUFBTSxHQUFHLG1CQUFtQixDQUFDO0FBRW5DOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sV0FBVyxHQUFHLElBQUksY0FBYyxDQUMzQyxHQUFHLE1BQU0sY0FBYyxFQUN2QjtJQUNFLE9BQU8sRUFBRSxHQUFHLEVBQUUsQ0FBQyxHQUFHLE1BQU0sZ0JBQWdCO0NBQ3pDLENBQ0YsQ0FBQztBQUVGOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sV0FBVyxHQUFHLElBQUksY0FBYyxDQUMzQyxHQUFHLE1BQU0sY0FBYyxFQUN2QjtJQUNFLE9BQU8sRUFBRSxHQUFHLEVBQUUsQ0FBQyxPQUFPO0NBQ3ZCLENBQ0YsQ0FBQztBQUVGOzs7OztHQUtHO0FBRUgsTUFBTSxPQUFPLFlBQVk7SUFDdkI7Ozs7T0FJRztJQUNNLFVBQVUsQ0FBdUI7SUFZMUM7Ozs7Ozs7Ozs7O09BV0c7SUFDSCxZQUNtQixRQUE4QixFQUM5QixPQUE2QixFQUM3QixPQUFpQyxFQUNqQyxPQUF1QixFQUN2QixjQUE4QixFQUM5QixNQUFjLEVBQ21CLFVBQW1CLEVBQ25CLFVBQW1CO1FBUHBELGFBQVEsR0FBUixRQUFRLENBQXNCO1FBQzlCLFlBQU8sR0FBUCxPQUFPLENBQXNCO1FBQzdCLFlB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REMsTUFBTTs7MkZBQ25CLFlBQVk7a0JBRHhCLFVBQVU7bUJBQUMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFOzswQkFzQzdCLFFBQVE7OzBCQUFJLE1BQU07MkJBQUMsV0FBVzs7MEJBQzlCLFFBQVE7OzBCQUFJLE1BQU07MkJBQUMsV0FBVzs7QUFxSm5DOzs7OztHQUtHO0FBQ0gsU0FBUyxTQUFTLENBQUksR0FBYTtJQUNqQyxPQUFPLEdBQUcsS0FBSyxJQUFJLENBQUM7QUFDdEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIEluamVjdCxcbiAgSW5qZWN0YWJsZSxcbiAgSW5qZWN0aW9uVG9rZW4sXG4gIE9uRGVzdHJveSxcbiAgT3B0aW9uYWwsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQWN0aXZhdGVkUm91dGUsIFJvdXRlciB9IGZyb20gJ0Bhbmd1bGFyL3JvdXRlcic7XG5pbXBvcnQge1xuICBkZWJvdW5jZVRpbWUsXG4gIGZpbHRlcixcbiAgbWFwLFxuICBza2lwV2hpbGUsXG4gIFN1YmplY3QsXG4gIHRha2UsXG4gIHRha2VVbnRpbCxcbiAgdGFrZVdoaWxlLFxuICB0YXAsXG59IGZyb20gJ3J4anMnO1xuaW1wb3J0IHtcbiAgU3RvcmFnZVNlcnZpY2UsXG4gIFRoZW1lUmVnaXN0cnlTZXJ2aWNlLFxuICBUaGVtZVN0eWxlTWFuYWdlclNlcnZpY2UsXG4gIFRoZW1lVHJhY2tpbmdTZXJ2aWNlLFxufSBmcm9tICcuL2ludGVybmFsJztcblxuY29uc3QgUFJFRklYID0gJ25neC10aGVtZS1tYW5hZ2VyJztcblxuLyoqXG4gKiBAZ3JvdXAgSW5qZWN0aW9uVG9rZW5cbiAqL1xuZXhwb3J0IGNvbnN0IFNUT1JBR0VfS0VZID0gbmV3IEluamVjdGlvblRva2VuPHN0cmluZz4oXG4gIGAke1BSRUZJWH0vU1RPUkFHRV9LRVlgLFxuICB7XG4gICAgZmFjdG9yeTogKCkgPT4gYCR7UFJFRklYfS9jdXJyZW50LXRoZW1lYCxcbiAgfSxcbik7XG5cbi8qKlxuICogQGdyb3VwIEluamVjdGlvblRva2VuXG4gKi9cbmV4cG9ydCBjb25zdCBRVUVSWV9QQVJBTSA9IG5ldyBJbmplY3Rpb25Ub2tlbjxzdHJpbmc+KFxuICBgJHtQUkVGSVh9L1FVRVJZX1BBUkFNYCxcbiAge1xuICAgIGZhY3Rvcnk6ICgpID0+IGB0aGVtZWAsXG4gIH0sXG4pO1xuXG4vKipcbiAqIFNlcnZpY2UgZm9yIG1hbmFnaW5nIGFuZCBzd2l0Y2hpbmcgYmV0d2VlbiBkaWZmZXJlbnQgdGhlbWVzIGluIGFuIGFwcGxpY2F0aW9uLlxuICpcbiAqIEBncm91cCBTZXJ2aWNlc1xuICogQGdyb3VwIFB1YmxpYyBBUElcbiAqL1xuQEluamVjdGFibGUoeyBwcm92aWRlZEluOiAncm9vdCcgfSlcbmV4cG9ydCBjbGFzcyBUaGVtZVNlcnZpY2UgaW1wbGVtZW50cyBPbkRlc3Ryb3kge1xuICAvKipcbiAgICogU3ViamVjdCBmb3IgdHJpZ2dlcmluZyBjbGVhbnVwIG9uIHNlcnZpY2UgZGVzdHJ1Y3Rpb24uXG4gICAqXG4gICAqIEBwcml2YXRlXG4gICAqL1xuICByZWFkb25seSAjZGVzdHJveWVkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICAvKipcbiAgICogT2JzZXJ2YWJsZSBzdHJlYW0gdGhhdCBlbWl0cyB0aGUgY3VycmVudGx5IGFjdGl2ZSB0aGVtZS5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB0aGVtZXMkID0gdGhpcy5yZWdpc3RyeS50aGVtZXMkO1xuXG4gIC8qKlxuICAgKiBPYnNlcnZhYmxlIHN0cmVhbSBvZiBhbGwgcmVnaXN0ZXJlZCB0aGVtZXMuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgY3VycmVudFRoZW1lJCA9IHRoaXMudHJhY2tlci5jdXJyZW50VGhlbWUkO1xuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbmV3IGluc3RhbmNlXG4gICAqXG4gICAqIEBwYXJhbSB7VGhlbWVSZWdpc3RyeVNlcnZpY2V9IHJlZ2lzdHJ5IC0gU2VydmljZSBmb3IgcmVnaXN0ZXJpbmcgYXZhaWxhYmxlIHRoZW1lc1xuICAgKiBAcGFyYW0ge1RoZW1lVHJhY2tpbmdTZXJ2aWNlfSB0cmFja2VyIC0gU2VydmljZSBmb3IgdHJhY2tpbmcgdGhlIGN1cnJlbnRseSBzZWxlY3RlZCB0aGVtZVxuICAgKiBAcGFyYW0ge1RoZW1lU3R5bGVNYW5hZ2VyU2VydmljZX0gbWFuYWdlciAtIFNlcnZpY2UgZm9yIHRoZW1lIGRpc2NvdmVyeSwgYWN0aXZhdGlvbiBhbmQgZGVhY3RpdmF0aW9uXG4gICAqIEBwYXJhbSB7U3RvcmFnZVNlcnZpY2V9IHN0b3JhZ2UgLSBTZXJ2aWNlIGZvciBzdG9yaW5nIHRoZSBjdXJyZW50bHkgc2VsZWN0ZWQgdGhlbWUgaW4gdGhlIGJyb3dzZXIgc3RvcmFnZVxuICAgKiBAcGFyYW0ge0FjdGl2YXRlZFJvdXRlfSBhY3RpdmF0ZWRSb3V0ZSAtIEFuZ3VsYXIgc2VydmljZSBmb3IgbWFuYWdpbmcgdGhlIGN1cnJlbnQgcm91dGVcbiAgICogQHBhcmFtIHtSb3V0ZXJ9IHJvdXRlciAtIEFuZ3VsYXIgc2VydmljZSBmb3IgbmF2aWdhdGluZyBiZXR3ZWVuIHJvdXRlc1xuICAgKiBAcGFyYW0ge3N0cmluZ30gc3RvcmFnZUtleSAtIEtleSBmb3Igc3RvcmluZyB0aGUgY3VycmVudGx5IHNlbGVjdGVkIHRoZW1lIGluIGJyb3dzZXIgc3RvcmFnZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gcXVlcnlQYXJhbSAtIFF1ZXJ5IHBhcmFtZXRlciBuYW1lIGZvciBzcGVjaWZ5aW5nIHRoZSB0aGVtZSBpbiB0aGUgcm91dGVcbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgcmVnaXN0cnk6IFRoZW1lUmVnaXN0cnlTZXJ2aWNlLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgdHJhY2tlcjogVGhlbWVUcmFja2luZ1NlcnZpY2UsXG4gICAgcHJpdmF0ZSByZWFkb25seSBtYW5hZ2VyOiBUaGVtZVN0eWxlTWFuYWdlclNlcnZpY2UsXG4gICAgcHJpdmF0ZSByZWFkb25seSBzdG9yYWdlOiBTdG9yYWdlU2VydmljZSxcbiAgICBwcml2YXRlIHJlYWRvbmx5IGFjdGl2YXRlZFJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSxcbiAgICBwcml2YXRlIHJlYWRvbmx5IHJvdXRlcjogUm91dGVyLFxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoU1RPUkFHRV9LRVkpIHByaXZhdGUgcmVhZG9ubHkgc3RvcmFnZUtleT86IHN0cmluZyxcbiAgICBAT3B0aW9uYWwoKSBASW5qZWN0KFFVRVJZX1BBUkFNKSBwcml2YXRlIHJlYWRvbmx5IHF1ZXJ5UGFyYW0/OiBzdHJpbmcsXG4gICkge1xuICAgIC8vIHVwZGF0ZSB0aGUgY3VycmVudCByb3V0ZSwgdGhlIGJyb3dzZXIgc3RvcmFnZSBhbmQgdGhlIHN0eWxlIGVsZW1lbnRzJyBtZWRpYSBhdHRyaWJ1dGUsXG4gICAgLy8gd2hlbiB0aGUgY3VycmVudGx5IHNlbGVjdGVkIHRoZW1lIGNoYW5nZXNcbiAgICB0aGlzLnRyYWNrZXIuY3VycmVudFRoZW1lJFxuICAgICAgLnBpcGUoXG4gICAgICAgIGZpbHRlcihpc05vdE51bGwpLFxuICAgICAgICB0YXAoKHRoZW1lKSA9PiB0aGlzLiN1cGRhdGVSb3V0ZVBhcmFtKHRoZW1lKSksXG4gICAgICAgIHRhcCgodGhlbWUpID0+IHRoaXMuI3VwZGF0ZVN0b3JlZFRoZW1lKHRoZW1lKSksXG4gICAgICAgIHRhcCgodGhlbWUpID0+IG1hbmFnZXIudXNlKHRoZW1lKSksXG4gICAgICAgIHRha2VVbnRpbCh0aGlzLiNkZXN0cm95ZWQpLFxuICAgICAgKVxuICAgICAgLnN1YnNjcmliZSgpO1xuXG4gICAgLy8gY2FsY3VsYXRlIHRoZSBpbml0aWFsbHkgc2VsZWN0ZWQgdGhlbWVcbiAgICB0aGlzLnJlZ2lzdHJ5LnRoZW1lcyRcbiAgICAgIC5waXBlKFxuICAgICAgICBkZWJvdW5jZVRpbWUoMSksXG4gICAgICAgIHRha2VXaGlsZSgoKSA9PiAhdGhpcy50cmFja2VyLmN1cnJlbnRUaGVtZSksXG4gICAgICAgIG1hcChcbiAgICAgICAgICAodGhlbWVzKSA9PlxuICAgICAgICAgICAgdGhlbWVzLmZpbmQoKHRoZW1lKSA9PiB0aGVtZS5kZWZhdWx0VGhlbWUpIHx8IHRoZW1lc1swXSxcbiAgICAgICAgKSxcbiAgICAgICAgbWFwKChkZWZhdWx0VGhlbWUpID0+ICh7XG4gICAgICAgICAgZnJvbVJvdXRlOiB0aGlzLiN0aGVtZUZyb21Sb3V0ZSxcbiAgICAgICAgICBmcm9tU3RvcmFnZTogdGhpcy4jdGhlbWVGcm9tU3RvcmFnZSxcbiAgICAgICAgICBkZWZhdWx0VGhlbWU6IGRlZmF1bHRUaGVtZT8uaWQsXG4gICAgICAgIH0pKSxcbiAgICAgICAgbWFwKFxuICAgICAgICAgICh7IGZyb21Sb3V0ZSwgZnJvbVN0b3JhZ2UsIGRlZmF1bHRUaGVtZSB9KSA9PlxuICAgICAgICAgICAgZnJvbVJvdXRlIHx8IGZyb21TdG9yYWdlIHx8IGRlZmF1bHRUaGVtZSxcbiAgICAgICAgKSxcbiAgICAgICAgdGFwKChpbml0aWFsVGhlbWUpID0+IHtcbiAgICAgICAgICBpZiAoIXRoaXMudHJhY2tlci5jdXJyZW50VGhlbWUpIHtcbiAgICAgICAgICAgIHRoaXMudHJhY2tlci5jdXJyZW50VGhlbWUgPSBpbml0aWFsVGhlbWU7XG4gICAgICAgICAgfVxuICAgICAgICB9KSxcbiAgICAgICAgdGFrZSgxKSxcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUoKTtcblxuICAgIC8vIGlmIHdlIGdvdCBhIHN0b3JhZ2Uga2V5LCB1cGRhdGUgY3VycmVudCB0aGVtZSBvbiBzdG9yYWdlIGNoYW5nZXNcbiAgICBpZiAoISF0aGlzLnN0b3JhZ2VLZXkpIHtcbiAgICAgIHRoaXMuc3RvcmFnZS5jaGFuZ2VzJFxuICAgICAgICAucGlwZShcbiAgICAgICAgICBza2lwV2hpbGUoKCkgPT4gIXRoaXMudHJhY2tlci5jdXJyZW50VGhlbWUpLFxuICAgICAgICAgIGZpbHRlcigoeyBrZXkgfSkgPT4ga2V5ID09PSB0aGlzLnN0b3JhZ2VLZXkpLFxuICAgICAgICAgIG1hcCgoeyBuZXdWYWx1ZSB9KSA9PiBuZXdWYWx1ZSksXG4gICAgICAgICAgdGFwKCh0aGVtZSkgPT4gKHRoaXMudHJhY2tlci5jdXJyZW50VGhlbWUgPSB0aGVtZSkpLFxuICAgICAgICAgIHRha2VVbnRpbCh0aGlzLiNkZXN0cm95ZWQpLFxuICAgICAgICApXG4gICAgICAgIC5zdWJzY3JpYmUoKTtcbiAgICB9XG5cbiAgICAvLyBpZiB3ZSBnb3QgYSBxdWVyeSBwYXJhbSwgdXBkYXRlIHRoZSBjdXJyZW50IHRoZW1lIG9uIHJvdXRlIHBhcmFtIGNoYW5nZXNcbiAgICBpZiAoISF0aGlzLnF1ZXJ5UGFyYW0pIHtcbiAgICAgIHRoaXMuYWN0aXZhdGVkUm91dGUucXVlcnlQYXJhbU1hcFxuICAgICAgICAucGlwZShcbiAgICAgICAgICBza2lwV2hpbGUoKCkgPT4gIXRoaXMudHJhY2tlci5jdXJyZW50VGhlbWUpLFxuICAgICAgICAgIGZpbHRlcigobWFwKSA9PiBtYXAuaGFzKHRoaXMucXVlcnlQYXJhbSEpKSxcbiAgICAgICAgICBtYXAoKG1hcCkgPT4gbWFwLmdldCh0aGlzLnF1ZXJ5UGFyYW0hKSksXG4gICAgICAgICAgdGFwKCh0aGVtZSkgPT4gKHRoaXMudHJhY2tlci5jdXJyZW50VGhlbWUgPSB0aGVtZSkpLFxuICAgICAgICAgIHRha2VVbnRpbCh0aGlzLiNkZXN0cm95ZWQpLFxuICAgICAgICApXG4gICAgICAgIC5zdWJzY3JpYmUoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2xlYW51cCBsb2dpYyB0byBiZSBleGVjdXRlZCB3aGVuIHRoZSBzZXJ2aWNlIGlzIGRlc3Ryb3llZC5cbiAgICpcbiAgICogQGludGVybmFsXG4gICAqL1xuICBuZ09uRGVzdHJveSgpIHtcbiAgICB0aGlzLiNkZXN0cm95ZWQubmV4dCgpO1xuICAgIHRoaXMuI2Rlc3Ryb3llZC5jb21wbGV0ZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNlbGVjdCB0aGUgdGhlbWUgdG8gYmUgdXNlZFxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdGhlbWVcbiAgICovXG4gIHNlbGVjdFRoZW1lKHRoZW1lOiBzdHJpbmcpIHtcbiAgICB0aGlzLnRyYWNrZXIuY3VycmVudFRoZW1lID0gdGhlbWU7XG4gIH1cblxuICAvKipcbiAgICogR2V0cyB0aGUgdGhlbWUgZnJvbSB0aGUgYnJvd3NlcidzIHN0b3JhZ2UuXG4gICAqXG4gICAqIEByZXR1cm5zIHtzdHJpbmcgfCBudWxsfSAtIFRoZSBzdG9yZWQgdGhlbWUgb3IgbnVsbCBpZiB0aGUgc3RvcmFnZSBrZXkgaXMgbm90IHByb3ZpZGVkXG4gICAqIEBwcml2YXRlXG4gICAqL1xuICBnZXQgI3RoZW1lRnJvbVN0b3JhZ2UoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgaWYgKCF0aGlzLnN0b3JhZ2VLZXkpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5zdG9yYWdlLmdldEl0ZW0odGhpcy5zdG9yYWdlS2V5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXRzIHRoZSB0aGVtZSBmcm9tIHRoZSBjdXJyZW50IHJvdXRlJ3MgcXVlcnkuXG4gICAqXG4gICAqIEByZXR1cm5zIHtzdHJpbmcgfCBudWxsfSAtIFRoZSBjdXJyZW50IHRoZW1lIG9yIG51bGwgaWYgdGhlIHF1ZXJ5IHBhcmFtIGlzIG5vdCBwcm92aWRlZFxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgZ2V0ICN0aGVtZUZyb21Sb3V0ZSgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICBpZiAoIXRoaXMucXVlcnlQYXJhbSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmFjdGl2YXRlZFJvdXRlLnNuYXBzaG90LnF1ZXJ5UGFyYW1NYXAuZ2V0KHRoaXMucXVlcnlQYXJhbSk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlcyB0aGUgYnJvd3NlcidzIHVybCBxdWVyeSBwYXJhbXMgd2l0aCB0aGUgZ2l2ZW4gdGhlbWUgSUQuXG4gICAqXG4gICAqIERvZXMgbm90aGluZyBpZiB0aGUgcXVlcnkgcGFyYW0gaXMgbm90IHByb3ZpZGVkLlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdGhlbWUgLSBUaGUgdGhlbWUgSUQgdG8gdXBkYXRlIGluIHRoZSBicm93c2VyJ3MgdXJsIHF1ZXJ5IHBhcmFtc1xuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgI3VwZGF0ZVJvdXRlUGFyYW0odGhlbWU6IHN0cmluZykge1xuICAgIGlmICghdGhpcy5xdWVyeVBhcmFtKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMucm91dGVyLm5hdmlnYXRlKFtdLCB7XG4gICAgICByZWxhdGl2ZVRvOiB0aGlzLmFjdGl2YXRlZFJvdXRlLFxuICAgICAgcXVlcnlQYXJhbXM6IHRoZW1lID8geyBbdGhpcy5xdWVyeVBhcmFtXTogdGhlbWUgfSA6IHt9LFxuICAgICAgcXVlcnlQYXJhbXNIYW5kbGluZzogJ21lcmdlJyxcbiAgICAgIHByZXNlcnZlRnJhZ21lbnQ6IHRydWUsXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlcyB0aGUgdGhlbWUgSUQgaW4gdGhlIGJyb3dzZXIncyBzdG9yYWdlLlxuICAgKlxuICAgKiBEb2VzIG5vdGhpbmcgaWYgdGhlIHN0b3JhZ2Uga2V5IGlzIG5vdCBwcm92aWRlZC5cbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IHRoZW1lIC0gVGhlIHRoZW1lIElEIHRvIHNhdmUgaW4gdGhlIGJyb3dzZXIncyBzdG9yYWdlXG4gICAqIEBwcml2YXRlXG4gICAqL1xuICAjdXBkYXRlU3RvcmVkVGhlbWUodGhlbWU6IHN0cmluZykge1xuICAgIGlmICghdGhpcy5zdG9yYWdlS2V5KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuc3RvcmFnZS5zZXRJdGVtKHRoaXMuc3RvcmFnZUtleSwgdGhlbWUpO1xuICB9XG59XG5cbi8qKlxuICogVHlwZSBndWFyZC5cbiAqXG4gKiBAcGFyYW0ge1QgfCBudWxsXX0gYXJnIC0gVGhlIGFyZ3VtZW50IHRvIGd1YXJkXG4gKiBAaW50ZXJuYWxcbiAqL1xuZnVuY3Rpb24gaXNOb3ROdWxsPFQ+KGFyZzogVCB8IG51bGwpOiBhcmcgaXMgVCB7XG4gIHJldHVybiBhcmcgIT09IG51bGw7XG59XG4iXX0=