UNPKG

@visa/nova-angular

Version:

Visa Product Design System Nova Angular library

428 lines 61.2 kB
/** * © 2025 Visa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **/ import { EventEmitter, inject, Injectable, Output, RendererFactory2 } from '@angular/core'; import { NavigationEnd, Router } from '@angular/router'; import { UUIDService } from '../lib/_utilities/services/uuid.service'; import { filter } from 'rxjs'; import { SafeSubscriber } from 'rxjs/internal/Subscriber'; import { AppReadyService } from './_utilities/services/app-stable-check.service'; import { ButtonDirective } from './button/button.directive'; import { CheckboxDirective } from './checkbox/checkbox.directive'; import { ListboxItemComponent } from './listbox-item/listbox-item.component'; import { DOWN_ARROW_KEY, LEFT_ARROW_KEY, RIGHT_ARROW_KEY, TAB_KEY, UP_ARROW_KEY } from './nova-lib.constants'; import { TabItemDirective } from './tab-item/tab-item.directive'; import * as i0 from "@angular/core"; import * as i1 from "@angular/router"; import * as i2 from "./_utilities/services/app-stable-check.service"; /** * This service manages navigation states within the application, ensuring smooth transitions and a consistent user experience. <br /> * It’s primarily used internally by the library but can also be leveraged directly for custom implementations. */ export class NovaLibService { constructor(router, rendererFactory, appReadyService) { this.router = router; this.rendererFactory = rendererFactory; this.appReadyService = appReadyService; /** @ignore */ this._idService = inject(UUIDService); /** * Emits new url on initial page load and page navigation; use alongside <code>getCurrentRoute</code>. */ this.routeChange = new EventEmitter(); this.renderer = rendererFactory.createRenderer(null, null); } /** * The getUUID generates a random ID. * @deprecated Please use the equivalent method from our unique ID generator instead. * @param name Optional string to start the ID. * @returns string */ getUUID(name) { return this._idService.getUUID(name); } /** * The check UUID method verifies that the given ID has not already been generated by the getUUID method. <br /> * @deprecated Please use the equivalent method from our unique ID generator instead. * @param uuid String ID to check. * @param name Optional string to start the ID. * @returns uuid */ checkUUID(uuid, name) { return this._idService.checkUUID(uuid, name); } /** * The getCurrentRoute method can be used to retrieve the current route or to get updated URLs when routes change by subscribing to the <code>routeChange</code> event. * @returns router.url as string */ getCurrentRoute() { this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((event) => { this.routeChange.emit(this.router.url); }); return this.router.url; } /** * The addArrowKeyNavigation method adds arrow key navigation to an array of elements of type <code>ButtonDirective</code>, <code>ListboxItemComponent</code>, <code>or CheckboxDirective</code>. * @param itemsArray Array of items to add arrow key navigation to. * @param arrowDirections Optionally specify which arrow key directions to use for navigation. * @param removeTabNavigation Optionally remove tab navigation from an array you're adding arrow navigation to. */ addArrowKeyNavigation(itemsArray, removeTabNavigation = false, arrowDirections = 'both') { if (!itemsArray || !itemsArray.length) return; itemsArray.forEach((item, index) => { if (!item.el || !item.el.nativeElement) return; item.listeners.push( // for each button, add an event listener for arrow "keydown" this.renderer.listen(item.el.nativeElement, 'keydown', (event) => { // right and down arrow keys should go to next focusable item if ((event.key === DOWN_ARROW_KEY && arrowDirections !== 'horizontal') || (event.key === RIGHT_ARROW_KEY && arrowDirections !== 'vertical')) { event.preventDefault(); const focusableIndex = this.nextEnabledItem(itemsArray, index); if (focusableIndex > -1) itemsArray[focusableIndex].el.nativeElement.focus(); } else if ((event.key === UP_ARROW_KEY && arrowDirections !== 'horizontal') || (event.key === LEFT_ARROW_KEY && arrowDirections !== 'vertical')) { // left and up arrow keys should go to previous focusable item event.preventDefault(); const focusableIndex = this.previousEnabledItem(itemsArray, index); if (focusableIndex > -1) itemsArray[focusableIndex].el.nativeElement.focus(); } })); }); if (removeTabNavigation) this.removeTabNavigation(itemsArray); } /** * The removeTabNavigation method removes tab navigation for an array of elements of type ButtonDirective, ListboxItemComponent, or CheckboxDirective. <br /> * When this functionality is added, it will disable tabbing between these specified elements. * @param itemsArray Array of items to remove tab navigation from. */ removeTabNavigation(itemsArray) { if (!itemsArray || !itemsArray.length) return; this.findStartingFocus(itemsArray); itemsArray.forEach((item, index) => { if (!item.el || !item.el.nativeElement) return; item.listeners.push(this.renderer.listen(item.el.nativeElement, 'keydown', (event) => { // as you loop through the items with arrow keys, tabIndex must be updated so that only the item with focus has tabIndex = 0 if (event.key === DOWN_ARROW_KEY || event.key === RIGHT_ARROW_KEY) { event.preventDefault(); // prevent scrolling const focusableIndex = this.nextEnabledItem(itemsArray, index); if (focusableIndex > -1) { this.renderer.setAttribute(item.el.nativeElement, 'tabIndex', '-1'); this.renderer.setAttribute(itemsArray[focusableIndex].el.nativeElement, 'tabIndex', '0'); } } else if (event.key === UP_ARROW_KEY || event.key === LEFT_ARROW_KEY) { event.preventDefault(); // prevent scrolling const focusableIndex = this.previousEnabledItem(itemsArray, index); if (focusableIndex > -1) { this.renderer.setAttribute(item.el.nativeElement, 'tabIndex', '-1'); this.renderer.setAttribute(itemsArray[focusableIndex].el.nativeElement, 'tabIndex', '0'); } } else if (event.key === TAB_KEY) { // if you tab out of the array, reset the starting index this.findStartingFocus(itemsArray); } })); }); } /** * This method resets navigation behaviors for an array of elements of type <code>ButtonDirective</code>, <code>ListboxItemComponent</code>, or <code>CheckboxDirective</code>. <br /> * It removes any tabindex or event listeners added by <code>addArrowKeyNavigation</code> or <code>removeTabNavigation</code>. * @param itemsArray Array of items to reset navigation behaviors for. */ resetNavigationBehaviors(itemsArray) { if (!itemsArray || !itemsArray.length) return; itemsArray.forEach((item, index) => { if (!item.el || !item.el.nativeElement) return; this.renderer.removeAttribute(item.el.nativeElement, 'tabIndex'); item.listeners.forEach((listener) => { if (listener instanceof SafeSubscriber) { // unsubscribe from subscription listener.unsubscribe(); } else { // remove event listener (if used with renderer.listen) listener(); } }); item.listeners = []; }); } /** * The findStartingFocus method finds the item to start navigation on. <br /> * The starting focusable item is either the first item or the currently selected item. * @param itemsArray Array of items to find starting focus for. */ findStartingFocus(itemsArray) { if (!itemsArray || !itemsArray.length) return; // item.ariaSelected = button, item.active = listboxItem, item.checked = checkbox let selectedItemIndex = itemsArray.findIndex((item) => ((item instanceof ButtonDirective && item.ariaSelected) || (item instanceof ListboxItemComponent && item.active) || (item instanceof CheckboxDirective && item.checked)) === true); if (selectedItemIndex === -1) selectedItemIndex = this.nextEnabledItem(itemsArray); itemsArray.forEach((item, index) => { if (!item.el || !item.el.nativeElement) return; // if an item is the first / selected item, allow tab focus if (index === selectedItemIndex) { this.renderer.setAttribute(item.el.nativeElement, 'tabIndex', '0'); } else { // otherwise remove tab focus ability this.renderer.setAttribute(item.el.nativeElement, 'tabIndex', '-1'); } }); } /** * The nextEnabledItem method finds the next item in the array that is not disabled. <br /> * When this functionality is added, it will automatically skip over disabled items to locate the next enabled one. * @param items Array of items to search for next enabled item. * @param currentIndex Index to start searching from. * @returns Index of next item that is not disabled. */ nextEnabledItem(items, currentIndex) { if (!items || !items.length) return -1; let index = currentIndex || currentIndex === 0 ? currentIndex + 1 : 0; let count = 0; while (count < items.length && index !== currentIndex) { if (index >= items.length) { index = 0; } if (!items[index].disabled) { return index; } index++; count++; } return index; } /** * The lastEnabledItem method retrieves the last item in an array that is not disabled. * @param items Array of items to search for last enabled item. * @returns Index of most last item that is not disabled. */ lastEnabledItem(items) { if (!items || !items.length) return -1; let count = items.length - 1; while (count >= 0) { if (items[count].disabled !== true) return count; count--; } return count; } /** * The firstEnabledItem methods retrieves the first item in array that is not disabled. * @param items Array of items to search for first enabled item. * @returns Index of first item that is not disabled. */ firstEnabledItem(items) { if (!items || !items.length) return -1; let count = 0; while (count < items.length) { if (items[count].disabled !== true) return count; count++; } return count; } /** * The previousEnabledItem method finds the previous item in the array that is not disabled. <br /> * When this functionality is added, it will automatically skip over disabled items to locate the previous enabled one. * @param items Array of items to search for previous enabled item. * @param currentIndex Index to start reverse searching from. * @returns Index of first previous item that is not disabled. */ previousEnabledItem(items, currentIndex) { if (!items || !items.length) return -1; let index = currentIndex || currentIndex === 0 ? currentIndex - 1 : items.length - 1; let count = 0; while (count < items.length && index !== currentIndex) { if (index === -1) { index = items.length - 1; } if (!items[index].disabled) { return index; } index--; count++; } return index; } /** * The addAutomaticActivation method enables automatic tab activation for the specified tab or listbox list. <br /> * When this functionality is added, focusing on an item will also select and activate it. * @param items Array of items to add automatic activation to. */ addAutomaticActivation(items) { if (!items || !items.length) return; items.forEach((item, index) => { if (item instanceof TabItemDirective && item.button?.el?.nativeElement) { // tab button this.renderer.listen(item.button.el.nativeElement, 'focus', (event) => { this.selectItem(items, index); }); } else if (item instanceof ListboxItemComponent && item._isRoleOptionVariant) { // standard list item this.renderer.listen(item.el.nativeElement, 'focus', (event) => { this.selectItem(items, index); }); } }); } /** * The setAriaCurrent method sets the aria-current="true" attribute on the element with the specified ID. * @param id ID of element to set aria-current="true" on. */ setAriaCurrent(id) { if (this._currentLink) { this.renderer.setAttribute(this._currentLink, 'aria-current', 'false'); } const doc = this.appReadyService.checkDocumentExists(); if (doc) { this._currentLink = document.getElementById(id); if (!this._currentLink) { console.warn('NovaLibService: setAriaCurrent could not find element with id: ' + id); return; } this.renderer.setAttribute(this._currentLink, 'aria-current', 'true'); } } /** * The handleAriaCurrent method handles the aria-current value on click for a list of links. * @param links List of links to add aria-current functionality to. */ handleAriaCurrent(links) { links.forEach((link) => { this.renderer.listen(link.el.nativeElement, 'click', () => { this.renderer.setAttribute(link.el.nativeElement, 'aria-current', 'true'); links.forEach((item) => { if (item === link) return; this.renderer.setAttribute(item.el.nativeElement, 'aria-current', 'false'); }); }); }); } /** * The selectItems method selects items from start_index to end_index in a list of tab or listbox items. By default, it selects all items if no indices are specified. * @param items Array of listbox items that you want to manipulate. * @param start Index of the first item you want to manipulate. * @param end Index of the last item you want to manipulate. * @param prop The property name you want to set to true (ie. active, highlighted, etc.). Defaults to 'active'. */ selectItems(items, start = 0, end = items.length - 1, prop = 'active') { if (!items || !items.length) return; if (start < 0 || end >= items.length || start > end) return; for (let i = start; i <= end; i++) { const item = items[i]; if (item instanceof TabItemDirective) { if (item.button && !item.button.disabled) item.button[prop] = true; } else if (item instanceof ListboxItemComponent) { if (!item.disabled) item[prop] = true; } } } /** * The selectItem method selects an item from a list of tab or listbox items. * @param items List of items that contains the item you want to manipulate. * @param index Index of the item you want to manipulate. * @param prop The property name you want to set to true (ie. active, highlighted, etc.). Defaults to 'active'. */ selectItem(items, index, prop = 'active') { if (!items || !items.length) return; if (items[index]) { items[index][prop] = true; } } /** * The deselectItems method deselect items out of a list of tab or listbox items. * @param items List of items that contains the items you want to manipulate. * @param index Optional index of the item you want to manipulate. * @param prop The property name you want to set to false (ie. active, highlighted, etc.). Defaults to 'active'. */ deselectItems(items, index, prop = 'active') { if (!items || !items.length) return; items.forEach((item, i) => { if ((index || index === 0) && i === index) return; item[prop] = false; }); } /** * The deselectItem method deselects a specific item from a list of tab or listbox items. * @param items List of items that contains the items you want to manipulate. * @param index Index of the item you want to manipulate. * @param prop The property name you want to set to false (ie. active, highlighted, etc.). Defaults to 'active'. */ deselectItem(items, index, prop = 'active') { if (!items || !items.length) return; items[index][prop] = false; } /** * The detectAllItemsSelected method checks whether all items in a given list of tab or listbox items are selected. * @param items List of items that contains the items you want to check. * @returns true if all items are selected, false if not. */ detectAllItemsSelected(items) { if (!items || !items.length) return; let unSelectedItems = 0; items.forEach((item) => { if (!item.active && !item.disabled) unSelectedItems++; }); return unSelectedItems == 0; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NovaLibService, deps: [{ token: i1.Router }, { token: i0.RendererFactory2 }, { token: i2.AppReadyService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NovaLibService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NovaLibService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.Router }, { type: i0.RendererFactory2 }, { type: i2.AppReadyService }], propDecorators: { routeChange: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm92YS1saWIuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL2xpYnMvbm92YS1saWIvc3JjL2xpYi9ub3ZhLWxpYi5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7SUFlSTtBQUNKLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQXdCLGdCQUFnQixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2pILE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDeEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQ3RFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDOUIsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzFELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxnREFBZ0QsQ0FBQztBQUNqRixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDNUQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFFbEUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sdUNBQXVDLENBQUM7QUFDN0UsT0FBTyxFQUFFLGNBQWMsRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUM5RyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQzs7OztBQUVqRTs7O0dBR0c7QUFJSCxNQUFNLE9BQU8sY0FBYztJQUN6QixZQUNVLE1BQWMsRUFDZCxlQUFpQyxFQUNqQyxlQUFnQztRQUZoQyxXQUFNLEdBQU4sTUFBTSxDQUFRO1FBQ2Qsb0JBQWUsR0FBZixlQUFlLENBQWtCO1FBQ2pDLG9CQUFlLEdBQWYsZUFBZSxDQUFpQjtR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O0lBRUQ7Ozs7T0FJRztJQUNILHNCQUFzQixDQUFDLEtBQTZCO1FBQ2xELElBQUksQ0FBQyxLQUFLLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTTtZQUFFLE9BQU87UUFDcEMsSUFBSSxlQUFlLEdBQUcsQ0FBQyxDQUFDO1FBQ3hCLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRTtZQUNyQixJQUFJLENBQUUsSUFBWSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRO2dCQUFFLGVBQWUsRUFBRSxDQUFDO1FBQ2pFLENBQUMsQ0FBQyxDQUFDO1FBQ0gsT0FBTyxlQUFlLElBQUksQ0FBQyxDQUFDO0lBQzlCLENBQUM7K0dBdlpVLGNBQWM7bUhBQWQsY0FBYyxjQUZiLE1BQU07OzRGQUVQLGNBQWM7a0JBSDFCLFVBQVU7bUJBQUM7b0JBQ1YsVUFBVSxFQUFFLE1BQU07aUJBQ25CO3dJQXVDVyxXQUFXO3NCQUFwQixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiAgICAgICAgICAgICAgwqkgMjAyNSBWaXNhXG4gKlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqICAgICAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqXG4gKiovXG5pbXBvcnQgeyBFdmVudEVtaXR0ZXIsIGluamVjdCwgSW5qZWN0YWJsZSwgT3V0cHV0LCBRdWVyeUxpc3QsIFJlbmRlcmVyMiwgUmVuZGVyZXJGYWN0b3J5MiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgTmF2aWdhdGlvbkVuZCwgUm91dGVyIH0gZnJvbSAnQGFuZ3VsYXIvcm91dGVyJztcbmltcG9ydCB7IFVVSURTZXJ2aWNlIH0gZnJvbSAnLi4vbGliL191dGlsaXRpZXMvc2VydmljZXMvdXVpZC5zZXJ2aWNlJztcbmltcG9ydCB7IGZpbHRlciB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgU2FmZVN1YnNjcmliZXIgfSBmcm9tICdyeGpzL2ludGVybmFsL1N1YnNjcmliZXInO1xuaW1wb3J0IHsgQXBwUmVhZHlTZXJ2aWNlIH0gZnJvbSAnLi9fdXRpbGl0aWVzL3NlcnZpY2VzL2FwcC1zdGFibGUtY2hlY2suc2VydmljZSc7XG5pbXBvcnQgeyBCdXR0b25EaXJlY3RpdmUgfSBmcm9tICcuL2J1dHRvbi9idXR0b24uZGlyZWN0aXZlJztcbmltcG9ydCB7IENoZWNrYm94RGlyZWN0aXZlIH0gZnJvbSAnLi9jaGVja2JveC9jaGVja2JveC5kaXJlY3RpdmUnO1xuaW1wb3J0IHsgTGlua0RpcmVjdGl2ZSB9IGZyb20gJy4vbGluay9saW5rLmRpcmVjdGl2ZSc7XG5pbXBvcnQgeyBMaXN0Ym94SXRlbUNvbXBvbmVudCB9IGZyb20gJy4vbGlzdGJveC1pdGVtL2xpc3Rib3gtaXRlbS5jb21wb25lbnQnO1xuaW1wb3J0IHsgRE9XTl9BUlJPV19LRVksIExFRlRfQVJST1dfS0VZLCBSSUdIVF9BUlJPV19LRVksIFRBQl9LRVksIFVQX0FSUk9XX0tFWSB9IGZyb20gJy4vbm92YS1saWIuY29uc3RhbnRzJztcbmltcG9ydCB7IFRhYkl0ZW1EaXJlY3RpdmUgfSBmcm9tICcuL3RhYi1pdGVtL3RhYi1pdGVtLmRpcmVjdGl2ZSc7XG5cbi8qKlxuICogVGhpcyBzZXJ2aWNlIG1hbmFnZXMgbmF2aWdhdGlvbiBzdGF0ZXMgd2l0aGluIHRoZSBhcHBsaWNhdGlvbiwgZW5zdXJpbmcgc21vb3RoIHRyYW5zaXRpb25zIGFuZCBhIGNvbnNpc3RlbnQgdXNlciBleHBlcmllbmNlLiA8YnIgLz5cbiAqIEl04oCZcyBwcmltYXJpbHkgdXNlZCBpbnRlcm5hbGx5IGJ5IHRoZSBsaWJyYXJ5IGJ1dCBjYW4gYWxzbyBiZSBsZXZlcmFnZWQgZGlyZWN0bHkgZm9yIGN1c3RvbSBpbXBsZW1lbnRhdGlvbnMuXG4gKi9cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIE5vdmFMaWJTZXJ2aWNlIHtcbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByb3V0ZXI6IFJvdXRlcixcbiAgICBwcml2YXRlIHJlbmRlcmVyRmFjdG9yeTogUmVuZGVyZXJGYWN0b3J5MixcbiAgICBwcml2YXRlIGFwcFJlYWR5U2VydmljZTogQXBwUmVhZHlTZXJ2aWNlXG4gICkge1xuICAgIHRoaXMucmVuZGVyZXIgPSByZW5kZXJlckZhY3RvcnkuY3JlYXRlUmVuZGVyZXIobnVsbCwgbnVsbCk7XG4gIH1cblxuICAvKiogQGlnbm9yZSAqL1xuICBwcml2YXRlIF9pZFNlcnZpY2UgPSBpbmplY3QoVVVJRFNlcnZpY2UpO1xuXG4gIC8qKiBAaWdub3JlICovXG4gIHByaXZhdGUgcmVuZGVyZXI6IFJlbmRlcmVyMjtcbiAgLyoqXG4gICAqIFRoZSBnZXRVVUlEIGdlbmVyYXRlcyBhIHJhbmRvbSBJRC5cbiAgICogQGRlcHJlY2F0ZWQgUGxlYXNlIHVzZSB0aGUgZXF1aXZhbGVudCBtZXRob2QgZnJvbSBvdXIgdW5pcXVlIElEIGdlbmVyYXRvciBpbnN0ZWFkLlxuICAgKiBAcGFyYW0gbmFtZSBPcHRpb25hbCBzdHJpbmcgdG8gc3RhcnQgdGhlIElELlxuICAgKiBAcmV0dXJucyBzdHJpbmdcbiAgICovXG4gIGdldFVVSUQobmFtZT86IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuX2lkU2VydmljZS5nZXRVVUlEKG5hbWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBjaGVjayBVVUlEIG1ldGhvZCB2ZXJpZmllcyB0aGF0IHRoZSBnaXZlbiBJRCBoYXMgbm90IGFscmVhZHkgYmVlbiBnZW5lcmF0ZWQgYnkgdGhlIGdldFVVSUQgbWV0aG9kLiA8YnIgLz5cbiAgICogQGRlcHJlY2F0ZWQgUGxlYXNlIHVzZSB0aGUgZXF1aXZhbGVudCBtZXRob2QgZnJvbSBvdXIgdW5pcXVlIElEIGdlbmVyYXRvciBpbnN0ZWFkLlxuICAgKiBAcGFyYW0gdXVpZCBTdHJpbmcgSUQgdG8gY2hlY2suXG4gICAqIEBwYXJhbSBuYW1lIE9wdGlvbmFsIHN0cmluZyB0byBzdGFydCB0aGUgSUQuXG4gICAqIEByZXR1cm5zIHV1aWRcbiAgICovXG4gIGNoZWNrVVVJRCh1dWlkOiBzdHJpbmcsIG5hbWU/OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9pZFNlcnZpY2UuY2hlY2tVVUlEKHV1aWQsIG5hbWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIEVtaXRzIG5ldyB1cmwgb24gaW5pdGlhbCBwYWdlIGxvYWQgYW5kIHBhZ2UgbmF2aWdhdGlvbjsgdXNlIGFsb25nc2lkZSA8Y29kZT5nZXRDdXJyZW50Um91dGU8L2NvZGU+LlxuICAgKi9cbiAgQE91dHB1dCgpIHJvdXRlQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG5cbiAgLyoqXG4gICAqIFRoZSBnZXRDdXJyZW50Um91dGUgbWV0aG9kIGNhbiBiZSB1c2VkIHRvIHJldHJpZXZlIHRoZSBjdXJyZW50IHJvdXRlIG9yIHRvIGdldCB1cGRhdGVkIFVSTHMgd2hlbiByb3V0ZXMgY2hhbmdlIGJ5IHN1YnNjcmliaW5nIHRvIHRoZSA8Y29kZT5yb3V0ZUNoYW5nZTwvY29kZT4gZXZlbnQuXG4gICAqIEByZXR1cm5zIHJvdXRlci51cmwgYXMgc3RyaW5nXG4gICAqL1xuICBnZXRDdXJyZW50Um91dGUoKTogc3RyaW5nIHtcbiAgICB0aGlzLnJvdXRlci5ldmVudHMucGlwZShmaWx0ZXIoKGV2ZW50KSA9PiBldmVudCBpbnN0YW5jZW9mIE5hdmlnYXRpb25FbmQpKS5zdWJzY3JpYmUoKGV2ZW50KSA9PiB7XG4gICAgICB0aGlzLnJvdXRlQ2hhbmdlLmVtaXQodGhpcy5yb3V0ZXIudXJsKTtcbiAgICB9KTtcbiAgICByZXR1cm4gdGhpcy5yb3V0ZXIudXJsO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBhZGRBcnJvd0tleU5hdmlnYXRpb24gbWV0aG9kIGFkZHMgYXJyb3cga2V5IG5hdmlnYXRpb24gdG8gYW4gYXJyYXkgb2YgZWxlbWVudHMgb2YgdHlwZSA8Y29kZT5CdXR0b25EaXJlY3RpdmU8L2NvZGU+LCA8Y29kZT5MaXN0Ym94SXRlbUNvbXBvbmVudDwvY29kZT4sIDxjb2RlPm9yIENoZWNrYm94RGlyZWN0aXZlPC9jb2RlPi5cbiAgICogQHBhcmFtIGl0ZW1zQXJyYXkgQXJyYXkgb2YgaXRlbXMgdG8gYWRkIGFycm93IGtleSBuYXZpZ2F0aW9uIHRvLlxuICAgKiBAcGFyYW0gYXJyb3dEaXJlY3Rpb25zIE9wdGlvbmFsbHkgc3BlY2lmeSB3aGljaCBhcnJvdyBrZXkgZGlyZWN0aW9ucyB0byB1c2UgZm9yIG5hdmlnYXRpb24uXG4gICAqIEBwYXJhbSByZW1vdmVUYWJOYXZpZ2F0aW9uIE9wdGlvbmFsbHkgcmVtb3ZlIHRhYiBuYXZpZ2F0aW9uIGZyb20gYW4gYXJyYXkgeW91J3JlIGFkZGluZyBhcnJvdyBuYXZpZ2F0aW9uIHRvLlxuICAgKi9cbiAgYWRkQXJyb3dLZXlOYXZpZ2F0aW9uKFxuICAgIGl0ZW1zQXJyYXk6IEJ1dHRvbkRpcmVjdGl2ZVtdIHwgTGlzdGJveEl0ZW1Db21wb25lbnRbXSB8IENoZWNrYm94RGlyZWN0aXZlW10sXG4gICAgcmVtb3ZlVGFiTmF2aWdhdGlvbjogYm9vbGVhbiA9IGZhbHNlLFxuICAgIGFycm93RGlyZWN0aW9uczogJ2JvdGgnIHwgJ2hvcml6b250YWwnIHwgJ3ZlcnRpY2FsJyA9ICdib3RoJ1xuICApIHtcbiAgICBpZiAoIWl0ZW1zQXJyYXkgfHwgIWl0ZW1zQXJyYXkubGVuZ3RoKSByZXR1cm47XG4gICAgaXRlbXNBcnJheS5mb3JFYWNoKChpdGVtOiBCdXR0b25EaXJlY3RpdmUgfCBMaXN0Ym94SXRlbUNvbXBvbmVudCB8IENoZWNrYm94RGlyZWN0aXZlLCBpbmRleDogbnVtYmVyKSA9PiB7XG4gICAgICBpZiAoIWl0ZW0uZWwgfHwgIWl0ZW0uZWwubmF0aXZlRWxlbWVudCkgcmV0dXJuO1xuXG4gICAgICBpdGVtLmxpc3RlbmVycy5wdXNoKFxuICAgICAgICAvLyBmb3IgZWFjaCBidXR0b24sIGFkZCBhbiBldmVudCBsaXN0ZW5lciBmb3IgYXJyb3cgXCJrZXlkb3duXCJcbiAgICAgICAgdGhpcy5yZW5kZXJlci5saXN0ZW4oaXRlbS5lbC5uYXRpdmVFbGVtZW50LCAna2V5ZG93bicsIChldmVudCkgPT4ge1xuICAgICAgICAgIC8vIHJpZ2h0IGFuZCBkb3duIGFycm93IGtleXMgc2hvdWxkIGdvIHRvIG5leHQgZm9jdXNhYmxlIGl0ZW1cbiAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAoZXZlbnQua2V5ID09PSBET1dOX0FSUk9XX0tFWSAmJiBhcnJvd0RpcmVjdGlvbnMgIT09ICdob3Jpem9udGFsJykgfHxcbiAgICAgICAgICAgIChldmVudC5rZXkgPT09IFJJR0hUX0FSUk9XX0tFWSAmJiBhcnJvd0RpcmVjdGlvbnMgIT09ICd2ZXJ0aWNhbCcpXG4gICAgICAgICAgKSB7XG4gICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgY29uc3QgZm9jdXNhYmxlSW5kZXggPSB0aGlzLm5leHRFbmFibGVkSXRlbShpdGVtc0FycmF5LCBpbmRleCk7XG4gICAgICAgICAgICBpZiAoZm9jdXNhYmxlSW5kZXggPiAtMSkgaXRlbXNBcnJheVtmb2N1c2FibGVJbmRleF0uZWwubmF0aXZlRWxlbWVudC5mb2N1cygpO1xuICAgICAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgICAgICAoZXZlbnQua2V5ID09PSBVUF9BUlJPV19LRVkgJiYgYXJyb3dEaXJlY3Rpb25zICE9PSAnaG9yaXpvbnRhbCcpIHx8XG4gICAgICAgICAgICAoZXZlbnQua2V5ID09PSBMRUZUX0FSUk9XX0tFWSAmJiBhcnJvd0RpcmVjdGlvbnMgIT09ICd2ZXJ0aWNhbCcpXG4gICAgICAgICAgKSB7XG4gICAgICAgICAgICAvLyBsZWZ0IGFuZCB1cCBhcnJvdyBrZXlzIHNob3VsZCBnbyB0byBwcmV2aW91cyBmb2N1c2FibGUgaXRlbVxuICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgIGNvbnN0IGZvY3VzYWJsZUluZGV4ID0gdGhpcy5wcmV2aW91c0VuYWJsZWRJdGVtKGl0ZW1zQXJyYXksIGluZGV4KTtcbiAgICAgICAgICAgIGlmIChmb2N1c2FibGVJbmRleCA+IC0xKSBpdGVtc0FycmF5W2ZvY3VzYWJsZUluZGV4XS5lbC5uYXRpdmVFbGVtZW50LmZvY3VzKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgKTtcbiAgICB9KTtcblxuICAgIGlmIChyZW1vdmVUYWJOYXZpZ2F0aW9uKSB0aGlzLnJlbW92ZVRhYk5hdmlnYXRpb24oaXRlbXNBcnJheSk7XG4gIH1cblxuICAvKipcbiAgICogVGhlIHJlbW92ZVRhYk5hdmlnYXRpb24gbWV0aG9kIHJlbW92ZXMgdGFiIG5hdmlnYXRpb24gZm9yIGFuIGFycmF5IG9mIGVsZW1lbnRzIG9mIHR5cGUgQnV0dG9uRGlyZWN0aXZlLCBMaXN0Ym94SXRlbUNvbXBvbmVudCwgb3IgQ2hlY2tib3hEaXJlY3RpdmUuIDxiciAvPlxuICAgKiBXaGVuIHRoaXMgZnVuY3Rpb25hbGl0eSBpcyBhZGRlZCwgaXQgd2lsbCBkaXNhYmxlIHRhYmJpbmcgYmV0d2VlbiB0aGVzZSBzcGVjaWZpZWQgZWxlbWVudHMuXG4gICAqIEBwYXJhbSBpdGVtc0FycmF5IEFycmF5IG9mIGl0ZW1zIHRvIHJlbW92ZSB0YWIgbmF2aWdhdGlvbiBmcm9tLlxuICAgKi9cbiAgcmVtb3ZlVGFiTmF2aWdhdGlvbihpdGVtc0FycmF5OiBCdXR0b25EaXJlY3RpdmVbXSB8IExpc3Rib3hJdGVtQ29tcG9uZW50W10gfCBDaGVja2JveERpcmVjdGl2ZVtdKSB7XG4gICAgaWYgKCFpdGVtc0FycmF5IHx8ICFpdGVtc0FycmF5Lmxlbmd0aCkgcmV0dXJuO1xuICAgIHRoaXMuZmluZFN0YXJ0aW5nRm9jdXMoaXRlbXNBcnJheSk7XG5cbiAgICBpdGVtc0FycmF5LmZvckVhY2goKGl0ZW06IEJ1dHRvbkRpcmVjdGl2ZSB8IExpc3Rib3hJdGVtQ29tcG9uZW50IHwgQ2hlY2tib3hEaXJlY3RpdmUsIGluZGV4OiBudW1iZXIpID0+IHtcbiAgICAgIGlmICghaXRlbS5lbCB8fCAhaXRlbS5lbC5uYXRpdmVFbGVtZW50KSByZXR1cm47XG5cbiAgICAgIGl0ZW0ubGlzdGVuZXJzLnB1c2goXG4gICAgICAgIHRoaXMucmVuZGVyZXIubGlzdGVuKGl0ZW0uZWwubmF0aXZlRWxlbWVudCwgJ2tleWRvd24nLCAoZXZlbnQpID0+IHtcbiAgICAgICAgICAvLyBhcyB5b3UgbG9vcCB0aHJvdWdoIHRoZSBpdGVtcyB3aXRoIGFycm93IGtleXMsIHRhYkluZGV4IG11c3QgYmUgdXBkYXRlZCBzbyB0aGF0IG9ubHkgdGhlIGl0ZW0gd2l0aCBmb2N1cyBoYXMgdGFiSW5kZXggPSAwXG4gICAgICAgICAgaWYgKGV2ZW50LmtleSA9PT0gRE9XTl9BUlJPV19LRVkgfHwgZXZlbnQua2V5ID09PSBSSUdIVF9BUlJPV19LRVkpIHtcbiAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7IC8vIHByZXZlbnQgc2Nyb2xsaW5nXG4gICAgICAgICAgICBjb25zdCBmb2N1c2FibGVJbmRleCA9IHRoaXMubmV4dEVuYWJsZWRJdGVtKGl0ZW1zQXJyYXksIGluZGV4KTtcbiAgICAgICAgICAgIGlmIChmb2N1c2FibGVJbmRleCA+IC0xKSB7XG4gICAgICAgICAgICAgIHRoaXMucmVuZGVyZXIuc2V0QXR0cmlidXRlKGl0ZW0uZWwubmF0aXZlRWxlbWVudCwgJ3RhYkluZGV4JywgJy0xJyk7XG4gICAgICAgICAgICAgIHRoaXMucmVuZGVyZXIuc2V0QXR0cmlidXRlKGl0ZW1zQXJyYXlbZm9jdXNhYmxlSW5kZXhdLmVsLm5hdGl2ZUVsZW1lbnQsICd0YWJJbmRleCcsICcwJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIGlmIChldmVudC5rZXkgPT09IFVQX0FSUk9XX0tFWSB8fCBldmVudC5rZXkgPT09IExFRlRfQVJST1dfS0VZKSB7XG4gICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpOyAvLyBwcmV2ZW50IHNjcm9sbGluZ1xuICAgICAgICAgICAgY29uc3QgZm9jdXNhYmxlSW5kZXggPSB0aGlzLnByZXZpb3VzRW5hYmxlZEl0ZW0oaXRlbXNBcnJheSwgaW5kZXgpO1xuICAgICAgICAgICAgaWYgKGZvY3VzYWJsZUluZGV4ID4gLTEpIHtcbiAgICAgICAgICAgICAgdGhpcy5yZW5kZXJlci5zZXRBdHRyaWJ1dGUoaXRlbS5lbC5uYXRpdmVFbGVtZW50LCAndGFiSW5kZXgnLCAnLTEnKTtcbiAgICAgICAgICAgICAgdGhpcy5yZW5kZXJlci5zZXRBdHRyaWJ1dGUoaXRlbXNBcnJheVtmb2N1c2FibGVJbmRleF0uZWwubmF0aXZlRWxlbWVudCwgJ3RhYkluZGV4JywgJzAnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gVEFCX0tFWSkge1xuICAgICAgICAgICAgLy8gaWYgeW91IHRhYiBvdXQgb2YgdGhlIGFycmF5LCByZXNldCB0aGUgc3RhcnRpbmcgaW5kZXhcbiAgICAgICAgICAgIHRoaXMuZmluZFN0YXJ0aW5nRm9jdXMoaXRlbXNBcnJheSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGlzIG1ldGhvZCByZXNldHMgbmF2aWdhdGlvbiBiZWhhdmlvcnMgZm9yIGFuIGFycmF5IG9mIGVsZW1lbnRzIG9mIHR5cGUgPGNvZGU+QnV0dG9uRGlyZWN0aXZlPC9jb2RlPiwgPGNvZGU+TGlzdGJveEl0ZW1Db21wb25lbnQ8L2NvZGU+LCBvciA8Y29kZT5DaGVja2JveERpcmVjdGl2ZTwvY29kZT4uIDxiciAvPlxuICAgKiBJdCByZW1vdmVzIGFueSB0YWJpbmRleCBvciBldmVudCBsaXN0ZW5lcnMgYWRkZWQgYnkgPGNvZGU+YWRkQXJyb3dLZXlOYXZpZ2F0aW9uPC9jb2RlPiBvciA8Y29kZT5yZW1vdmVUYWJOYXZpZ2F0aW9uPC9jb2RlPi5cbiAgICogQHBhcmFtIGl0ZW1zQXJyYXkgQXJyYXkgb2YgaXRlbXMgdG8gcmVzZXQgbmF2aWdhdGlvbiBiZWhhdmlvcnMgZm9yLlxuICAgKi9cbiAgcmVzZXROYXZpZ2F0aW9uQmVoYXZpb3JzKGl0ZW1zQXJyYXk6IEJ1dHRvbkRpcmVjdGl2ZVtdIHwgTGlzdGJveEl0ZW1Db21wb25lbnRbXSB8IENoZWNrYm94RGlyZWN0aXZlW10pIHtcbiAgICBpZiAoIWl0ZW1zQXJyYXkgfHwgIWl0ZW1zQXJyYXkubGVuZ3RoKSByZXR1cm47XG4gICAgaXRlbXNBcnJheS5mb3JFYWNoKChpdGVtOiBCdXR0b25EaXJlY3RpdmUgfCBMaXN0Ym94SXRlbUNvbXBvbmVudCB8IENoZWNrYm94RGlyZWN0aXZlLCBpbmRleDogbnVtYmVyKSA9PiB7XG4gICAgICBpZiAoIWl0ZW0uZWwgfHwgIWl0ZW0uZWwubmF0aXZlRWxlbWVudCkgcmV0dXJuO1xuICAgICAgdGhpcy5yZW5kZXJlci5yZW1vdmVBdHRyaWJ1dGUoaXRlbS5lbC5uYXRpdmVFbGVtZW50LCAndGFiSW5kZXgnKTtcbiAgICAgIGl0ZW0ubGlzdGVuZXJzLmZvckVhY2goKGxpc3RlbmVyKSA9PiB7XG4gICAgICAgIGlmIChsaXN0ZW5lciBpbnN0YW5jZW9mIFNhZmVTdWJzY3JpYmVyKSB7XG4gICAgICAgICAgLy8gdW5zdWJzY3JpYmUgZnJvbSBzdWJzY3JpcHRpb25cbiAgICAgICAgICBsaXN0ZW5lci51bnN1YnNjcmliZSgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIHJlbW92ZSBldmVudCBsaXN0ZW5lciAoaWYgdXNlZCB3aXRoIHJlbmRlcmVyLmxpc3RlbilcbiAgICAgICAgICBsaXN0ZW5lcigpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIGl0ZW0ubGlzdGVuZXJzID0gW107XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogVGhlIGZpbmRTdGFydGluZ0ZvY3VzIG1ldGhvZCBmaW5kcyB0aGUgaXRlbSB0byBzdGFydCBuYXZpZ2F0aW9uIG9uLiA8YnIgLz5cbiAgICogVGhlIHN0YXJ0aW5nIGZvY3VzYWJsZSBpdGVtIGlzIGVpdGhlciB0aGUgZmlyc3QgaXRlbSBvciB0aGUgY3VycmVudGx5IHNlbGVjdGVkIGl0ZW0uXG4gICAqIEBwYXJhbSBpdGVtc0FycmF5IEFycmF5IG9mIGl0ZW1zIHRvIGZpbmQgc3RhcnRpbmcgZm9jdXMgZm9yLlxuICAgKi9cbiAgZmluZFN0YXJ0aW5nRm9jdXMoaXRlbXNBcnJheTogQnV0dG9uRGlyZWN0aXZlW10gfCBMaXN0Ym94SXRlbUNvbXBvbmVudFtdIHwgQ2hlY2tib3hEaXJlY3RpdmVbXSkge1xuICAgIGlmICghaXRlbXNBcnJheSB8fCAhaXRlbXNBcnJheS5sZW5ndGgpIHJldHVybjtcbiAgICAvLyBpdGVtLmFyaWFTZWxlY3RlZCA9IGJ1dHRvbiwgaXRlbS5hY3RpdmUgPSBsaXN0Ym94SXRlbSwgaXRlbS5jaGVja2VkID0gY2hlY2tib3hcbiAgICBsZXQgc2VsZWN0ZWRJdGVtSW5kZXggPSBpdGVtc0FycmF5LmZpbmRJbmRleChcbiAgICAgIChpdGVtOiBCdXR0b25EaXJlY3RpdmUgfCBMaXN0Ym94SXRlbUNvbXBvbmVudCB8IENoZWNrYm94RGlyZWN0aXZlKSA9PlxuICAgICAgICAoKGl0ZW0gaW5zdGFuY2VvZiBCdXR0b25EaXJlY3RpdmUgJiYgaXRlbS5hcmlhU2VsZWN0ZWQpIHx8XG4gICAgICAgICAgKGl0ZW0gaW5zdGFuY2VvZiBMaXN0Ym94SXRlbUNvbXBvbmVudCAmJiBpdGVtLmFjdGl2ZSkgfHxcbiAgICAgICAgICAoaXRlbSBpbnN0YW5jZW9mIENoZWNrYm94RGlyZWN0aXZlICYmIGl0ZW0uY2hlY2tlZCkpID09PSB0cnVlXG4gICAgKTtcbiAgICBpZiAoc2VsZWN0ZWRJdGVtSW5kZXggPT09IC0xKSBzZWxlY3RlZEl0ZW1JbmRleCA9IHRoaXMubmV4dEVuYWJsZWRJdGVtKGl0ZW1zQXJyYXkpO1xuICAgIGl0ZW1zQXJyYXkuZm9yRWFjaCgoaXRlbTogQnV0dG9uRGlyZWN0aXZlIHwgTGlzdGJveEl0ZW1Db21wb25lbnQgfCBDaGVja2JveERpcmVjdGl2ZSwgaW5kZXg6IG51bWJlcikgPT4ge1xuICAgICAgaWYgKCFpdGVtLmVsIHx8ICFpdGVtLmVsLm5hdGl2ZUVsZW1lbnQpIHJldHVybjtcbiAgICAgIC8vIGlmIGFuIGl0ZW0gaXMgdGhlIGZpcnN0IC8gc2VsZWN0ZWQgaXRlbSwgYWxsb3cgdGFiIGZvY3VzXG4gICAgICBpZiAoaW5kZXggPT09IHNlbGVjdGVkSXRlbUluZGV4KSB7XG4gICAgICAgIHRoaXMucmVuZGVyZXIuc2V0QXR0cmlidXRlKGl0ZW0uZWwubmF0aXZlRWxlbWVudCwgJ3RhYkluZGV4JywgJzAnKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSByZW1vdmUgdGFiIGZvY3VzIGFiaWxpdHlcbiAgICAgICAgdGhpcy5yZW5kZXJlci5zZXRBdHRyaWJ1dGUoaXRlbS5lbC5uYXRpdmVFbGVtZW50LCAndGFiSW5kZXgnLCAnLTEnKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgbmV4dEVuYWJsZWRJdGVtIG1ldGhvZCBmaW5kcyB0aGUgbmV4dCBpdGVtIGluIHRoZSBhcnJheSB0aGF0IGlzIG5vdCBkaXNhYmxlZC4gPGJyIC8+XG4gICAqIFdoZW4gdGhpcyBmdW5jdGlvbmFsaXR5IGlzIGFkZGVkLCBpdCB3aWxsIGF1dG9tYXRpY2FsbHkgc2tpcCBvdmVyIGRpc2FibGVkIGl0ZW1zIHRvIGxvY2F0ZSB0aGUgbmV4dCBlbmFibGVkIG9uZS5cbiAgICogQHBhcmFtIGl0ZW1zIEFycmF5IG9mIGl0ZW1zIHRvIHNlYXJjaCBmb3IgbmV4dCBlbmFibGVkIGl0ZW0uXG4gICAqIEBwYXJhbSBjdXJyZW50SW5kZXggSW5kZXggdG8gc3RhcnQgc2VhcmNoaW5nIGZyb20uXG4gICAqIEByZXR1cm5zIEluZGV4IG9mIG5leHQgaXRlbSB0aGF0IGlzIG5vdCBkaXNhYmxlZC5cbiAgICovXG4gIG5leHRFbmFibGVkSXRlbShcbiAgICBpdGVtczogQnV0dG9uRGlyZWN0aXZlW10gfCBMaXN0Ym94SXRlbUNvbXBvbmVudFtdIHwgQ2hlY2tib3hEaXJlY3RpdmVbXSxcbiAgICBjdXJyZW50SW5kZXg/OiBudW1iZXJcbiAgKTogbnVtYmVyIHtcbiAgICBpZiAoIWl0ZW1zIHx8ICFpdGVtcy5sZW5ndGgpIHJldHVybiAtMTtcbiAgICBsZXQgaW5kZXggPSBjdXJyZW50SW5kZXggfHwgY3VycmVudEluZGV4ID09PSAwID8gY3VycmVudEluZGV4ICsgMSA6IDA7XG4gICAgbGV0IGNvdW50ID0gMDtcbiAgICB3aGlsZSAoY291bnQgPCBpdGVtcy5sZW5ndGggJiYgaW5kZXggIT09IGN1cnJlbnRJbmRleCkge1xuICAgICAgaWYgKGluZGV4ID49IGl0ZW1zLmxlbmd0