@rb-mwindh/ngx-theme-manager
Version:
Angular component to switch between different theming stylesheets
197 lines • 23.9 kB
JavaScript
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=