UNPKG

@rb-mwindh/ngx-theme-manager

Version:

Angular component to switch between different theming stylesheets

186 lines 20.2 kB
import { Inject, Injectable } from '@angular/core'; import { DOCUMENT } from '@angular/common'; import { filter, map, timer } from 'rxjs'; import * as i0 from "@angular/core"; import * as i1 from "@angular/cdk/observers"; import * as i2 from "./theme-registry.service"; /** * A service that manages the activation and deactivation of themes. * * The service uses the `ContentObserver` service to observe changes in the `<head>` element * and updates the {@link ThemeRegistryService internal theme registry} * when new `<style>` elements are added to the DOM. * * Themes are identified by the `data-theme` attribute on the `<style>` element. * * The service provides a method `use` to activate a theme with a given ID * and deactivate all other themes. * * @internal * @group Services */ export class ThemeStyleManagerService { /** * Creates a new instance. * * Subscribes to the `ContentObserver` to listen for new `<style>` elements * added to the document head. If a new `<style>` element is added, the * `#updateRegistry()` method is called. * * @param {ContentObserver} observer - The Angular ContentObserver service * @param {ThemeRegistryService} themeRegistry - A service to register new themes * @param {Document} document - A reference to the current document */ constructor(observer, themeRegistry, document) { this.observer = observer; this.themeRegistry = themeRegistry; this.document = document; this.observer .observe(document.head) .pipe(map((mutations) => mutations.some((mutation) => Array.from(mutation.addedNodes).some((node) => node.nodeName === 'STYLE'))), filter((newStyles) => !!newStyles)) .subscribe(() => this.#updateRegistry()); } /** * Activates the theme with the given ID and deactivates all other themes. * * @param {string} theme - The theme to activate * @see turnOn * @see turnOff * @remarks A theme may consist of 1 or more `<style>` elements. */ use(theme) { // INFO: [author: NWD8FE, since: 2023/01/26] // running asynchronously, to give #updateRegistries // the chance to run first. timer(0).subscribe(() => { const styles = this.#getAllThemeStyles(); styles.forEach((el) => { const id = el.getAttribute('data-theme'); (theme === id ? turnOn : turnOff)(el); }); }); } /** * Get all theme `<style>` elements in the document head. * * @private */ #getAllThemeStyles() { return Array.from(this.document.head.querySelectorAll('style[data-theme]')); } /** * Updates the internal theme registry. * * Identifies all `<style>` elements without the `data-no-theme` and `data-theme` attributes. * Extracts the theme annotations from the elements' text content and applies the * `data-theme` or `data-no-theme` attribute depending on the discovered theme id. * * @private * @see extractThemeAnnotations * @see applyThemeIdentifier */ #updateRegistry() { Array.from(this.document.head.querySelectorAll('style:not([data-no-theme]):not([data-theme])')) .map((el) => ({ el, meta: extractThemeAnnotations(el.textContent) })) .forEach(({ el, meta }) => { if (applyThemeIdentifier(el, meta?.id)) { turnOff(el); this.themeRegistry.register(meta); } }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.1", ngImport: i0, type: ThemeStyleManagerService, deps: [{ token: i1.ContentObserver }, { token: i2.ThemeRegistryService }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.1", ngImport: i0, type: ThemeStyleManagerService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.1", ngImport: i0, type: ThemeStyleManagerService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: () => [{ type: i1.ContentObserver }, { type: i2.ThemeRegistryService }, { type: Document, decorators: [{ type: Inject, args: [DOCUMENT] }] }] }); /** * Applies a theme identifier to a `<style>` element. * * If the provided `id` is truthy, the element receives the attribute * `data-theme` set to the given `id`. Otherwise, the element * will get the attribute `data-no-theme` without any value. * * @param {HTMLStyleElement} el - The `<style>` element to apply the identifier to * @param {string | undefined} id - The theme identifier * @returns {boolean} true, if the style element belongs to a theme * @group Functions * @internal */ export function applyThemeIdentifier(el, id) { if (!!id) { el.setAttribute('data-theme', id); return true; } else { el.toggleAttribute('data-no-theme', true); return false; } } /** * Turn off a style element by setting its `media` attribute to `none`. * * @param {HTMLStyleElement} el - The style element to turn off. * @group Functions * @internal */ export function turnOff(el) { el.media = 'none'; } /** * Turn on a style element by removing its `media` attribute. * * @param {HTMLStyleElement} el - The style element to turn on. * @group Functions * @internal */ export function turnOn(el) { el.removeAttribute('media'); } /** * Extracts theme annotations from a given string. * * **Format:** `@@<annotationName> value` (until end of line) * * Possible annotation names: * - `id`: a unique identifier for the theme * - `displayName`: a human-readable name for the theme * - `description`: a short description of the theme * - `defaultTheme`: a boolean flag indicating if this is the default theme * * @param {string} s The string to extract annotations from. * @returns {Theme | null} An object containing the extracted annotations, or null if no annotations were found. * @group Functions * @internal */ export function extractThemeAnnotations(s) { if (!s) { return null; } const id = unwrap(/@@id\s+([^\r\n]+)$/m.exec(s)); if (!id) { return null; } const displayName = unwrap(/@@displayName\s+([^\r\n]+)$/m.exec(s)) || id; const description = unwrap(/@@description\s+([^\r\n]+)$/m.exec(s)); const defaultTheme = /@@default/m.test(s); return { id, displayName, description, defaultTheme }; } /** * Unwrap a match from a regular expression, returning the first captured group as a string. * * @param {RegExpExecArray} match - The match to unwrap. * @group Functions * @internal */ export function unwrap(match) { return (match && match[1] && match[1].trim()) || undefined; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGhlbWUtc3R5bGUtbWFuYWdlci5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LXRoZW1lLW1hbmFnZXIvc3JjL2xpYi9pbnRlcm5hbC90aGVtZS1zdHlsZS1tYW5hZ2VyLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDbkQsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRTNDLE9BQU8sRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxNQUFNLE1BQU0sQ0FBQzs7OztBQUkxQzs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUlILE1BQU0sT0FBTyx3QkFBd0I7SUFDbkM7Ozs7Ozs7Ozs7T0FVRztJQUNILFlBQ21CLFFBQXlCLEVBQ3pCLGFBQW1DLEVBQ2pCLFFBQWtCO1FBRnBDLGFBQVEsR0FBUixRQUFRLENBQWlCO1FBQ3pCLGtCQUFhLEdBQWIsYUFBYSxDQUFzQjtRQUNqQixhQUFRLEdBQVIsUUFBUSxDQUFVO1FBRXJELElBQUksQ0FBQyxRQUFRO2FBQ1YsT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7YUFDdEIsSUFBSSxDQUNILEdBQUcsQ0FBQyxDQUFDLFNBQVMsRUFBRSxFQUFFLENBQ2hCLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUMxQixLQUFLLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxJQUFJLENBQ2xDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxLQUFLLE9BQU8sQ0FDcEMsQ0FDRixDQUNGLEVBQ0QsTUFBTSxDQUFDLENBQUMsU0FBUyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQ25DO2FBQ0EsU0FBUyxDQUFDLEdB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QUNkLENBQUM7SUFDRCxNQUFNLEVBQUUsR0FBRyxNQUFNLENBQUMscUJBQXFCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDakQsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ1IsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBQ0QsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLDhCQUE4QixDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN6RSxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsOEJBQThCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDbkUsTUFBTSxZQUFZLEdBQUcsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMxQyxPQUFPLEVBQUUsRUFBRSxFQUFFLFdBQVcsRUFBRSxXQUFXLEVBQUUsWUFBWSxFQUFFLENBQUM7QUFDeEQsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILE1BQU0sVUFBVSxNQUFNLENBQUMsS0FBNkI7SUFDbEQsT0FBTyxDQUFDLEtBQUssSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLElBQUksU0FBUyxDQUFDO0FBQzdELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3QsIEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IERPQ1VNRU5UIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbmltcG9ydCB7IENvbnRlbnRPYnNlcnZlciB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9vYnNlcnZlcnMnO1xuaW1wb3J0IHsgZmlsdGVyLCBtYXAsIHRpbWVyIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBUaGVtZSB9IGZyb20gJy4uL3RoZW1lJztcbmltcG9ydCB7IFRoZW1lUmVnaXN0cnlTZXJ2aWNlIH0gZnJvbSAnLi90aGVtZS1yZWdpc3RyeS5zZXJ2aWNlJztcblxuLyoqXG4gKiBBIHNlcnZpY2UgdGhhdCBtYW5hZ2VzIHRoZSBhY3RpdmF0aW9uIGFuZCBkZWFjdGl2YXRpb24gb2YgdGhlbWVzLlxuICpcbiAqIFRoZSBzZXJ2aWNlIHVzZXMgdGhlIGBDb250ZW50T2JzZXJ2ZXJgIHNlcnZpY2UgdG8gb2JzZXJ2ZSBjaGFuZ2VzIGluIHRoZSBgPGhlYWQ+YCBlbGVtZW50XG4gKiBhbmQgdXBkYXRlcyB0aGUge0BsaW5rIFRoZW1lUmVnaXN0cnlTZXJ2aWNlIGludGVybmFsIHRoZW1lIHJlZ2lzdHJ5fVxuICogd2hlbiBuZXcgYDxzdHlsZT5gIGVsZW1lbnRzIGFyZSBhZGRlZCB0byB0aGUgRE9NLlxuICpcbiAqIFRoZW1lcyBhcmUgaWRlbnRpZmllZCBieSB0aGUgYGRhdGEtdGhlbWVgIGF0dHJpYnV0ZSBvbiB0aGUgYDxzdHlsZT5gIGVsZW1lbnQuXG4gKlxuICogVGhlIHNlcnZpY2UgcHJvdmlkZXMgYSBtZXRob2QgYHVzZWAgdG8gYWN0aXZhdGUgYSB0aGVtZSB3aXRoIGEgZ2l2ZW4gSURcbiAqIGFuZCBkZWFjdGl2YXRlIGFsbCBvdGhlciB0aGVtZXMuXG4gKlxuICogQGludGVybmFsXG4gKiBAZ3JvdXAgU2VydmljZXNcbiAqL1xuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCcsXG59KVxuZXhwb3J0IGNsYXNzIFRoZW1lU3R5bGVNYW5hZ2VyU2VydmljZSB7XG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbmV3IGluc3RhbmNlLlxuICAgKlxuICAgKiBTdWJzY3JpYmVzIHRvIHRoZSBgQ29udGVudE9ic2VydmVyYCB0byBsaXN0ZW4gZm9yIG5ldyBgPHN0eWxlPmAgZWxlbWVudHNcbiAgICogYWRkZWQgdG8gdGhlIGRvY3VtZW50IGhlYWQuIElmIGEgbmV3IGA8c3R5bGU+YCBlbGVtZW50IGlzIGFkZGVkLCB0aGVcbiAgICogYCN1cGRhdGVSZWdpc3RyeSgpYCBtZXRob2QgaXMgY2FsbGVkLlxuICAgKlxuICAgKiBAcGFyYW0ge0NvbnRlbnRPYnNlcnZlcn0gb2JzZXJ2ZXIgLSBUaGUgQW5ndWxhciBDb250ZW50T2JzZXJ2ZXIgc2VydmljZVxuICAgKiBAcGFyYW0ge1RoZW1lUmVnaXN0cnlTZXJ2aWNlfSB0aGVtZVJlZ2lzdHJ5IC0gQSBzZXJ2aWNlIHRvIHJlZ2lzdGVyIG5ldyB0aGVtZXNcbiAgICogQHBhcmFtIHtEb2N1bWVudH0gZG9jdW1lbnQgLSBBIHJlZmVyZW5jZSB0byB0aGUgY3VycmVudCBkb2N1bWVudFxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBvYnNlcnZlcjogQ29udGVudE9ic2VydmVyLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgdGhlbWVSZWdpc3RyeTogVGhlbWVSZWdpc3RyeVNlcnZpY2UsXG4gICAgQEluamVjdChET0NVTUVOVCkgcHJpdmF0ZSByZWFkb25seSBkb2N1bWVudDogRG9jdW1lbnQsXG4gICkge1xuICAgIHRoaXMub2JzZXJ2ZXJcbiAgICAgIC5vYnNlcnZlKGRvY3VtZW50LmhlYWQpXG4gICAgICAucGlwZShcbiAgICAgICAgbWFwKChtdXRhdGlvbnMpID0+XG4gICAgICAgICAgbXV0YXRpb25zLnNvbWUoKG11dGF0aW9uKSA9PlxuICAgICAgICAgICAgQXJyYXkuZnJvbShtdXRhdGlvbi5hZGRlZE5vZGVzKS5zb21lKFxuICAgICAgICAgICAgICAobm9kZSkgPT4gbm9kZS5ub2RlTmFtZSA9PT0gJ1NUWUxFJyxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgKSxcbiAgICAgICAgKSxcbiAgICAgICAgZmlsdGVyKChuZXdTdHlsZXMpID0+ICEhbmV3U3R5bGVzKSxcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUoKCkgPT4gdGhpcy4jdXBkYXRlUmVnaXN0cnkoKSk7XG4gIH1cblxuICAvKipcbiAgICogQWN0aXZhdGVzIHRoZSB0aGVtZSB3aXRoIHRoZSBnaXZlbiBJRCBhbmQgZGVhY3RpdmF0ZXMgYWxsIG90aGVyIHRoZW1lcy5cbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IHRoZW1lIC0gVGhlIHRoZW1lIHRvIGFjdGl2YXRlXG4gICAqIEBzZWUgdHVybk9uXG4gICAqIEBzZWUgdHVybk9mZlxuICAgKiBAcmVtYXJrcyBBIHRoZW1lIG1heSBjb25zaXN0IG9mIDEgb3IgbW9yZSBgPHN0eWxlPmAgZWxlbWVudHMuXG4gICAqL1xuICB1c2UodGhlbWU6IHN0cmluZyk6IHZvaWQge1xuICAgIC8vIElORk86IFthdXRob3I6IE5XRDhGRSwgc2luY2U6IDIwMjMvMDEvMjZdXG4gICAgLy8gIHJ1bm5pbmcgYXN5bmNocm9ub3VzbHksIHRvIGdpdmUgI3VwZGF0ZVJlZ2lzdHJpZXNcbiAgICAvLyAgdGhlIGNoYW5jZSB0byBydW4gZmlyc3QuXG4gICAgdGltZXIoMCkuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgIGNvbnN0IHN0eWxlcyA9IHRoaXMuI2dldEFsbFRoZW1lU3R5bGVzKCk7XG4gICAgICBzdHlsZXMuZm9yRWFjaCgoZWwpID0+IHtcbiAgICAgICAgY29uc3QgaWQgPSBlbC5nZXRBdHRyaWJ1dGUoJ2RhdGEtdGhlbWUnKTtcbiAgICAgICAgKHRoZW1lID09PSBpZCA/IHR1cm5PbiA6IHR1cm5PZmYpKGVsKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhbGwgdGhlbWUgYDxzdHlsZT5gIGVsZW1lbnRzIGluIHRoZSBkb2N1bWVudCBoZWFkLlxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgI2dldEFsbFRoZW1lU3R5bGVzKCk6IEhUTUxTdHlsZUVsZW1lbnRbXSB7XG4gICAgcmV0dXJuIEFycmF5LmZyb20oXG4gICAgICB0aGlzLmRvY3VtZW50LmhlYWQucXVlcnlTZWxlY3RvckFsbCgnc3R5bGVbZGF0YS10aGVtZV0nKSxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgdGhlIGludGVybmFsIHRoZW1lIHJlZ2lzdHJ5LlxuICAgKlxuICAgKiBJZGVudGlmaWVzIGFsbCBgPHN0eWxlPmAgZWxlbWVudHMgd2l0aG91dCB0aGUgYGRhdGEtbm8tdGhlbWVgIGFuZCBgZGF0YS10aGVtZWAgYXR0cmlidXRlcy5cbiAgICogRXh0cmFjdHMgdGhlIHRoZW1lIGFubm90YXRpb25zIGZyb20gdGhlIGVsZW1lbnRzJyB0ZXh0IGNvbnRlbnQgYW5kIGFwcGxpZXMgdGhlXG4gICAqIGBkYXRhLXRoZW1lYCBvciBgZGF0YS1uby10aGVtZWAgYXR0cmlidXRlIGRlcGVuZGluZyBvbiB0aGUgZGlzY292ZXJlZCB0aGVtZSBpZC5cbiAgICpcbiAgICogQHByaXZhdGVcbiAgICogQHNlZSBleHRyYWN0VGhlbWVBbm5vdGF0aW9uc1xuICAgKiBAc2VlIGFwcGx5VGhlbWVJZGVudGlmaWVyXG4gICAqL1xuICAjdXBkYXRlUmVnaXN0cnkoKSB7XG4gICAgQXJyYXkuZnJvbShcbiAgICAgIHRoaXMuZG9jdW1lbnQuaGVhZC5xdWVyeVNlbGVjdG9yQWxsPEhUTUxTdHlsZUVsZW1lbnQ+KFxuICAgICAgICAnc3R5bGU6bm90KFtkYXRhLW5vLXRoZW1lXSk6bm90KFtkYXRhLXRoZW1lXSknLFxuICAgICAgKSxcbiAgICApXG4gICAgICAubWFwKChlbCkgPT4gKHsgZWwsIG1ldGE6IGV4dHJhY3RUaGVtZUFubm90YXRpb25zKGVsLnRleHRDb250ZW50KSB9KSlcbiAgICAgIC5mb3JFYWNoKCh7IGVsLCBtZXRhIH0pID0+IHtcbiAgICAgICAgaWYgKGFwcGx5VGhlbWVJZGVudGlmaWVyKGVsLCBtZXRhPy5pZCkpIHtcbiAgICAgICAgICB0dXJuT2ZmKGVsKTtcbiAgICAgICAgICB0aGlzLnRoZW1lUmVnaXN0cnkucmVnaXN0ZXIobWV0YSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICB9XG59XG5cbi8qKlxuICogQXBwbGllcyBhIHRoZW1lIGlkZW50aWZpZXIgdG8gYSBgPHN0eWxlPmAgZWxlbWVudC5cbiAqXG4gKiBJZiB0aGUgcHJvdmlkZWQgYGlkYCBpcyB0cnV0aHksIHRoZSBlbGVtZW50IHJlY2VpdmVzIHRoZSBhdHRyaWJ1dGVcbiAqIGBkYXRhLXRoZW1lYCBzZXQgdG8gdGhlIGdpdmVuIGBpZGAuIE90aGVyd2lzZSwgdGhlIGVsZW1lbnRcbiAqIHdpbGwgZ2V0IHRoZSBhdHRyaWJ1dGUgYGRhdGEtbm8tdGhlbWVgIHdpdGhvdXQgYW55IHZhbHVlLlxuICpcbiAqIEBwYXJhbSB7SFRNTFN0eWxlRWxlbWVudH0gZWwgLSBUaGUgYDxzdHlsZT5gIGVsZW1lbnQgdG8gYXBwbHkgdGhlIGlkZW50aWZpZXIgdG9cbiAqIEBwYXJhbSB7c3RyaW5nIHwgdW5kZWZpbmVkfSBpZCAtIFRoZSB0aGVtZSBpZGVudGlmaWVyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gdHJ1ZSwgaWYgdGhlIHN0eWxlIGVsZW1lbnQgYmVsb25ncyB0byBhIHRoZW1lXG4gKiBAZ3JvdXAgRnVuY3Rpb25zXG4gKiBAaW50ZXJuYWxcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFwcGx5VGhlbWVJZGVudGlmaWVyKFxuICBlbDogSFRNTFN0eWxlRWxlbWVudCxcbiAgaWQ/OiBzdHJpbmcsXG4pOiBib29sZWFuIHtcbiAgaWYgKCEhaWQpIHtcbiAgICBlbC5zZXRBdHRyaWJ1dGUoJ2RhdGEtdGhlbWUnLCBpZCk7XG4gICAgcmV0dXJuIHRydWU7XG4gIH0gZWxzZSB7XG4gICAgZWwudG9nZ2xlQXR0cmlidXRlKCdkYXRhLW5vLXRoZW1lJywgdHJ1ZSk7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbi8qKlxuICogVHVybiBvZmYgYSBzdHlsZSBlbGVtZW50IGJ5IHNldHRpbmcgaXRzIGBtZWRpYWAgYXR0cmlidXRlIHRvIGBub25lYC5cbiAqXG4gKiBAcGFyYW0ge0hUTUxTdHlsZUVsZW1lbnR9IGVsIC0gVGhlIHN0eWxlIGVsZW1lbnQgdG8gdHVybiBvZmYuXG4gKiBAZ3JvdXAgRnVuY3Rpb25zXG4gKiBAaW50ZXJuYWxcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHR1cm5PZmYoZWw6IEhUTUxTdHlsZUVsZW1lbnQpOiB2b2lkIHtcbiAgZWwubWVkaWEgPSAnbm9uZSc7XG59XG5cbi8qKlxuICogVHVybiBvbiBhIHN0eWxlIGVsZW1lbnQgYnkgcmVtb3ZpbmcgaXRzIGBtZWRpYWAgYXR0cmlidXRlLlxuICpcbiAqIEBwYXJhbSB7SFRNTFN0eWxlRWxlbWVudH0gZWwgLSBUaGUgc3R5bGUgZWxlbWVudCB0byB0dXJuIG9uLlxuICogQGdyb3VwIEZ1bmN0aW9uc1xuICogQGludGVybmFsXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0dXJuT24oZWw6IEhUTUxTdHlsZUVsZW1lbnQpOiB2b2lkIHtcbiAgZWwucmVtb3ZlQXR0cmlidXRlKCdtZWRpYScpO1xufVxuXG4vKipcbiAqIEV4dHJhY3RzIHRoZW1lIGFubm90YXRpb25zIGZyb20gYSBnaXZlbiBzdHJpbmcuXG4gKlxuICogKipGb3JtYXQ6KiogYEBAPGFubm90YXRpb25OYW1lPiB2YWx1ZWAgKHVudGlsIGVuZCBvZiBsaW5lKVxuICpcbiAqIFBvc3NpYmxlIGFubm90YXRpb24gbmFtZXM6XG4gKiAtIGBpZGA6IGEgdW5pcXVlIGlkZW50aWZpZXIgZm9yIHRoZSB0aGVtZVxuICogLSBgZGlzcGxheU5hbWVgOiBhIGh1bWFuLXJlYWRhYmxlIG5hbWUgZm9yIHRoZSB0aGVtZVxuICogLSBgZGVzY3JpcHRpb25gOiBhIHNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSB0aGVtZVxuICogLSBgZGVmYXVsdFRoZW1lYDogYSBib29sZWFuIGZsYWcgaW5kaWNhdGluZyBpZiB0aGlzIGlzIHRoZSBkZWZhdWx0IHRoZW1lXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHMgVGhlIHN0cmluZyB0byBleHRyYWN0IGFubm90YXRpb25zIGZyb20uXG4gKiBAcmV0dXJucyB7VGhlbWUgfCBudWxsfSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgZXh0cmFjdGVkIGFubm90YXRpb25zLCBvciBudWxsIGlmIG5vIGFubm90YXRpb25zIHdlcmUgZm91bmQuXG4gKiBAZ3JvdXAgRnVuY3Rpb25zXG4gKiBAaW50ZXJuYWxcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3RUaGVtZUFubm90YXRpb25zKFxuICBzOiB1bmRlZmluZWQgfCBudWxsIHwgc3RyaW5nLFxuKTogVGhlbWUgfCBudWxsIHtcbiAgaWYgKCFzKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY29uc3QgaWQgPSB1bndyYXAoL0BAaWRcXHMrKFteXFxyXFxuXSspJC9tLmV4ZWMocykpO1xuICBpZiAoIWlkKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY29uc3QgZGlzcGxheU5hbWUgPSB1bndyYXAoL0BAZGlzcGxheU5hbWVcXHMrKFteXFxyXFxuXSspJC9tLmV4ZWMocykpIHx8IGlkO1xuICBjb25zdCBkZXNjcmlwdGlvbiA9IHVud3JhcCgvQEBkZXNjcmlwdGlvblxccysoW15cXHJcXG5dKykkL20uZXhlYyhzKSk7XG4gIGNvbnN0IGRlZmF1bHRUaGVtZSA9IC9AQGRlZmF1bHQvbS50ZXN0KHMpO1xuICByZXR1cm4geyBpZCwgZGlzcGxheU5hbWUsIGRlc2NyaXB0aW9uLCBkZWZhdWx0VGhlbWUgfTtcbn1cblxuLyoqXG4gKiBVbndyYXAgYSBtYXRjaCBmcm9tIGEgcmVndWxhciBleHByZXNzaW9uLCByZXR1cm5pbmcgdGhlIGZpcnN0IGNhcHR1cmVkIGdyb3VwIGFzIGEgc3RyaW5nLlxuICpcbiAqIEBwYXJhbSB7UmVnRXhwRXhlY0FycmF5fSBtYXRjaCAtIFRoZSBtYXRjaCB0byB1bndyYXAuXG4gKiBAZ3JvdXAgRnVuY3Rpb25zXG4gKiBAaW50ZXJuYWxcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVud3JhcChtYXRjaDogUmVnRXhwRXhlY0FycmF5IHwgbnVsbCk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIHJldHVybiAobWF0Y2ggJiYgbWF0Y2hbMV0gJiYgbWF0Y2hbMV0udHJpbSgpKSB8fCB1bmRlZmluZWQ7XG59XG4iXX0=