UNPKG

@visa/nova-angular

Version:

Visa Product Design System Nova Angular library

428 lines 61.3 kB
/** * Copyright (c) 2025 Visa, Inc. * * 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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O0lBRUQ7Ozs7T0FJRztJQUNILHNCQUFzQixDQUFDLEtBQTZCO1FBQ2xELElBQUksQ0FBQyxLQUFLLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTTtZQUFFLE9BQU87UUFDcEMsSUFBSSxlQUFlLEdBQUcsQ0FBQyxDQUFDO1FBQ3hCLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRTtZQUNyQixJQUFJLENBQUUsSUFBWSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRO2dCQUFFLGVBQWUsRUFBRSxDQUFDO1FBQ2pFLENBQUMsQ0FBQyxDQUFDO1FBQ0gsT0FBTyxlQUFlLElBQUksQ0FBQyxDQUFDO0lBQzlCLENBQUM7K0dBdlpVLGNBQWM7bUhBQWQsY0FBYyxjQUZiLE1BQU07OzRGQUVQLGNBQWM7a0JBSDFCLFVBQVU7bUJBQUM7b0JBQ1YsVUFBVSxFQUFFLE1BQU07aUJBQ25CO3dJQXVDVyxXQUFXO3NCQUFwQixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiAgICAgICAgICAgICAgQ29weXJpZ2h0IChjKSAyMDI1IFZpc2EsIEluYy5cbiAqXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICAgICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICpcbiAqKi9cbmltcG9ydCB7IEV2ZW50RW1pdHRlciwgaW5qZWN0LCBJbmplY3RhYmxlLCBPdXRwdXQsIFF1ZXJ5TGlzdCwgUmVuZGVyZXIyLCBSZW5kZXJlckZhY3RvcnkyIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBOYXZpZ2F0aW9uRW5kLCBSb3V0ZXIgfSBmcm9tICdAYW5ndWxhci9yb3V0ZXInO1xuaW1wb3J0IHsgVVVJRFNlcnZpY2UgfSBmcm9tICcuLi9saWIvX3V0aWxpdGllcy9zZXJ2aWNlcy91dWlkLnNlcnZpY2UnO1xuaW1wb3J0IHsgZmlsdGVyIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBTYWZlU3Vic2NyaWJlciB9IGZyb20gJ3J4anMvaW50ZXJuYWwvU3Vic2NyaWJlcic7XG5pbXBvcnQgeyBBcHBSZWFkeVNlcnZpY2UgfSBmcm9tICcuL191dGlsaXRpZXMvc2VydmljZXMvYXBwLXN0YWJsZS1jaGVjay5zZXJ2aWNlJztcbmltcG9ydCB7IEJ1dHRvbkRpcmVjdGl2ZSB9IGZyb20gJy4vYnV0dG9uL2J1dHRvbi5kaXJlY3RpdmUnO1xuaW1wb3J0IHsgQ2hlY2tib3hEaXJlY3RpdmUgfSBmcm9tICcuL2NoZWNrYm94L2NoZWNrYm94LmRpcmVjdGl2ZSc7XG5pbXBvcnQgeyBMaW5rRGlyZWN0aXZlIH0gZnJvbSAnLi9saW5rL2xpbmsuZGlyZWN0aXZlJztcbmltcG9ydCB7IExpc3Rib3hJdGVtQ29tcG9uZW50IH0gZnJvbSAnLi9saXN0Ym94LWl0ZW0vbGlzdGJveC1pdGVtLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBET1dOX0FSUk9XX0tFWSwgTEVGVF9BUlJPV19LRVksIFJJR0hUX0FSUk9XX0tFWSwgVEFCX0tFWSwgVVBfQVJST1dfS0VZIH0gZnJvbSAnLi9ub3ZhLWxpYi5jb25zdGFudHMnO1xuaW1wb3J0IHsgVGFiSXRlbURpcmVjdGl2ZSB9IGZyb20gJy4vdGFiLWl0ZW0vdGFiLWl0ZW0uZGlyZWN0aXZlJztcblxuLyoqXG4gKiBUaGlzIHNlcnZpY2UgbWFuYWdlcyBuYXZpZ2F0aW9uIHN0YXRlcyB3aXRoaW4gdGhlIGFwcGxpY2F0aW9uLCBlbnN1cmluZyBzbW9vdGggdHJhbnNpdGlvbnMgYW5kIGEgY29uc2lzdGVudCB1c2VyIGV4cGVyaWVuY2UuIDxiciAvPlxuICogSXTigJlzIHByaW1hcmlseSB1c2VkIGludGVybmFsbHkgYnkgdGhlIGxpYnJhcnkgYnV0IGNhbiBhbHNvIGJlIGxldmVyYWdlZCBkaXJlY3RseSBmb3IgY3VzdG9tIGltcGxlbWVudGF0aW9ucy5cbiAqL1xuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgTm92YUxpYlNlcnZpY2Uge1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJvdXRlcjogUm91dGVyLFxuICAgIHByaXZhdGUgcmVuZGVyZXJGYWN0b3J5OiBSZW5kZXJlckZhY3RvcnkyLFxuICAgIHByaXZhdGUgYXBwUmVhZHlTZXJ2aWNlOiBBcHBSZWFkeVNlcnZpY2VcbiAgKSB7XG4gICAgdGhpcy5yZW5kZXJlciA9IHJlbmRlcmVyRmFjdG9yeS5jcmVhdGVSZW5kZXJlcihudWxsLCBudWxsKTtcbiAgfVxuXG4gIC8qKiBAaWdub3JlICovXG4gIHByaXZhdGUgX2lkU2VydmljZSA9IGluamVjdChVVUlEU2VydmljZSk7XG5cbiAgLyoqIEBpZ25vcmUgKi9cbiAgcHJpdmF0ZSByZW5kZXJlcjogUmVuZGVyZXIyO1xuICAvKipcbiAgICogVGhlIGdldFVVSUQgZ2VuZXJhdGVzIGEgcmFuZG9tIElELlxuICAgKiBAZGVwcmVjYXRlZCBQbGVhc2UgdXNlIHRoZSBlcXVpdmFsZW50IG1ldGhvZCBmcm9tIG91ciB1bmlxdWUgSUQgZ2VuZXJhdG9yIGluc3RlYWQuXG4gICAqIEBwYXJhbSBuYW1lIE9wdGlvbmFsIHN0cmluZyB0byBzdGFydCB0aGUgSUQuXG4gICAqIEByZXR1cm5zIHN0cmluZ1xuICAgKi9cbiAgZ2V0VVVJRChuYW1lPzogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5faWRTZXJ2aWNlLmdldFVVSUQobmFtZSk7XG4gIH1cblxuICAvKipcbiAgICogVGhlIGNoZWNrIFVVSUQgbWV0aG9kIHZlcmlmaWVzIHRoYXQgdGhlIGdpdmVuIElEIGhhcyBub3QgYWxyZWFkeSBiZWVuIGdlbmVyYXRlZCBieSB0aGUgZ2V0VVVJRCBtZXRob2QuIDxiciAvPlxuICAgKiBAZGVwcmVjYXRlZCBQbGVhc2UgdXNlIHRoZSBlcXVpdmFsZW50IG1ldGhvZCBmcm9tIG91ciB1bmlxdWUgSUQgZ2VuZXJhdG9yIGluc3RlYWQuXG4gICAqIEBwYXJhbSB1dWlkIFN0cmluZyBJRCB0byBjaGVjay5cbiAgICogQHBhcmFtIG5hbWUgT3B0aW9uYWwgc3RyaW5nIHRvIHN0YXJ0IHRoZSBJRC5cbiAgICogQHJldHVybnMgdXVpZFxuICAgKi9cbiAgY2hlY2tVVUlEKHV1aWQ6IHN0cmluZywgbmFtZT86IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuX2lkU2VydmljZS5jaGVja1VVSUQodXVpZCwgbmFtZSk7XG4gIH1cblxuICAvKipcbiAgICogRW1pdHMgbmV3IHVybCBvbiBpbml0aWFsIHBhZ2UgbG9hZCBhbmQgcGFnZSBuYXZpZ2F0aW9uOyB1c2UgYWxvbmdzaWRlIDxjb2RlPmdldEN1cnJlbnRSb3V0ZTwvY29kZT4uXG4gICAqL1xuICBAT3V0cHV0KCkgcm91dGVDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcblxuICAvKipcbiAgICogVGhlIGdldEN1cnJlbnRSb3V0ZSBtZXRob2QgY2FuIGJlIHVzZWQgdG8gcmV0cmlldmUgdGhlIGN1cnJlbnQgcm91dGUgb3IgdG8gZ2V0IHVwZGF0ZWQgVVJMcyB3aGVuIHJvdXRlcyBjaGFuZ2UgYnkgc3Vic2NyaWJpbmcgdG8gdGhlIDxjb2RlPnJvdXRlQ2hhbmdlPC9jb2RlPiBldmVudC5cbiAgICogQHJldHVybnMgcm91dGVyLnVybCBhcyBzdHJpbmdcbiAgICovXG4gIGdldEN1cnJlbnRSb3V0ZSgpOiBzdHJpbmcge1xuICAgIHRoaXMucm91dGVyLmV2ZW50cy5waXBlKGZpbHRlcigoZXZlbnQpID0+IGV2ZW50IGluc3RhbmNlb2YgTmF2aWdhdGlvbkVuZCkpLnN1YnNjcmliZSgoZXZlbnQpID0+IHtcbiAgICAgIHRoaXMucm91dGVDaGFuZ2UuZW1pdCh0aGlzLnJvdXRlci51cmwpO1xuICAgIH0pO1xuICAgIHJldHVybiB0aGlzLnJvdXRlci51cmw7XG4gIH1cblxuICAvKipcbiAgICogVGhlIGFkZEFycm93S2V5TmF2aWdhdGlvbiBtZXRob2QgYWRkcyBhcnJvdyBrZXkgbmF2aWdhdGlvbiB0byBhbiBhcnJheSBvZiBlbGVtZW50cyBvZiB0eXBlIDxjb2RlPkJ1dHRvbkRpcmVjdGl2ZTwvY29kZT4sIDxjb2RlPkxpc3Rib3hJdGVtQ29tcG9uZW50PC9jb2RlPiwgPGNvZGU+b3IgQ2hlY2tib3hEaXJlY3RpdmU8L2NvZGU+LlxuICAgKiBAcGFyYW0gaXRlbXNBcnJheSBBcnJheSBvZiBpdGVtcyB0byBhZGQgYXJyb3cga2V5IG5hdmlnYXRpb24gdG8uXG4gICAqIEBwYXJhbSBhcnJvd0RpcmVjdGlvbnMgT3B0aW9uYWxseSBzcGVjaWZ5IHdoaWNoIGFycm93IGtleSBkaXJlY3Rpb25zIHRvIHVzZSBmb3IgbmF2aWdhdGlvbi5cbiAgICogQHBhcmFtIHJlbW92ZVRhYk5hdmlnYXRpb24gT3B0aW9uYWxseSByZW1vdmUgdGFiIG5hdmlnYXRpb24gZnJvbSBhbiBhcnJheSB5b3UncmUgYWRkaW5nIGFycm93IG5hdmlnYXRpb24gdG8uXG4gICAqL1xuICBhZGRBcnJvd0tleU5hdmlnYXRpb24oXG4gICAgaXRlbXNBcnJheTogQnV0dG9uRGlyZWN0aXZlW10gfCBMaXN0Ym94SXRlbUNvbXBvbmVudFtdIHwgQ2hlY2tib3hEaXJlY3RpdmVbXSxcbiAgICByZW1vdmVUYWJOYXZpZ2F0aW9uOiBib29sZWFuID0gZmFsc2UsXG4gICAgYXJyb3dEaXJlY3Rpb25zOiAnYm90aCcgfCAnaG9yaXpvbnRhbCcgfCAndmVydGljYWwnID0gJ2JvdGgnXG4gICkge1xuICAgIGlmICghaXRlbXNBcnJheSB8fCAhaXRlbXNBcnJheS5sZW5ndGgpIHJldHVybjtcbiAgICBpdGVtc0FycmF5LmZvckVhY2goKGl0ZW06IEJ1dHRvbkRpcmVjdGl2ZSB8IExpc3Rib3hJdGVtQ29tcG9uZW50IHwgQ2hlY2tib3hEaXJlY3RpdmUsIGluZGV4OiBudW1iZXIpID0+IHtcbiAgICAgIGlmICghaXRlbS5lbCB8fCAhaXRlbS5lbC5uYXRpdmVFbGVtZW50KSByZXR1cm47XG5cbiAgICAgIGl0ZW0ubGlzdGVuZXJzLnB1c2goXG4gICAgICAgIC8vIGZvciBlYWNoIGJ1dHRvbiwgYWRkIGFuIGV2ZW50IGxpc3RlbmVyIGZvciBhcnJvdyBcImtleWRvd25cIlxuICAgICAgICB0aGlzLnJlbmRlcmVyLmxpc3RlbihpdGVtLmVsLm5hdGl2ZUVsZW1lbnQsICdrZXlkb3duJywgKGV2ZW50KSA9PiB7XG4gICAgICAgICAgLy8gcmlnaHQgYW5kIGRvd24gYXJyb3cga2V5cyBzaG91bGQgZ28gdG8gbmV4dCBmb2N1c2FibGUgaXRlbVxuICAgICAgICAgIGlmIChcbiAgICAgICAgICAgIChldmVudC5rZXkgPT09IERPV05fQVJST1dfS0VZICYmIGFycm93RGlyZWN0aW9ucyAhPT0gJ2hvcml6b250YWwnKSB8fFxuICAgICAgICAgICAgKGV2ZW50LmtleSA9PT0gUklHSFRfQVJST1dfS0VZICYmIGFycm93RGlyZWN0aW9ucyAhPT0gJ3ZlcnRpY2FsJylcbiAgICAgICAgICApIHtcbiAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICBjb25zdCBmb2N1c2FibGVJbmRleCA9IHRoaXMubmV4dEVuYWJsZWRJdGVtKGl0ZW1zQXJyYXksIGluZGV4KTtcbiAgICAgICAgICAgIGlmIChmb2N1c2FibGVJbmRleCA+IC0xKSBpdGVtc0FycmF5W2ZvY3VzYWJsZUluZGV4XS5lbC5uYXRpdmVFbGVtZW50LmZvY3VzKCk7XG4gICAgICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgICAgIChldmVudC5rZXkgPT09IFVQX0FSUk9XX0tFWSAmJiBhcnJvd0RpcmVjdGlvbnMgIT09ICdob3Jpem9udGFsJykgfHxcbiAgICAgICAgICAgIChldmVudC5rZXkgPT09IExFRlRfQVJST1dfS0VZICYmIGFycm93RGlyZWN0aW9ucyAhPT0gJ3ZlcnRpY2FsJylcbiAgICAgICAgICApIHtcbiAgICAgICAgICAgIC8vIGxlZnQgYW5kIHVwIGFycm93IGtleXMgc2hvdWxkIGdvIHRvIHByZXZpb3VzIGZvY3VzYWJsZSBpdGVtXG4gICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgY29uc3QgZm9jdXNhYmxlSW5kZXggPSB0aGlzLnByZXZpb3VzRW5hYmxlZEl0ZW0oaXRlbXNBcnJheSwgaW5kZXgpO1xuICAgICAgICAgICAgaWYgKGZvY3VzYWJsZUluZGV4ID4gLTEpIGl0ZW1zQXJyYXlbZm9jdXNhYmxlSW5kZXhdLmVsLm5hdGl2ZUVsZW1lbnQuZm9jdXMoKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgICApO1xuICAgIH0pO1xuXG4gICAgaWYgKHJlbW92ZVRhYk5hdmlnYXRpb24pIHRoaXMucmVtb3ZlVGFiTmF2aWdhdGlvbihpdGVtc0FycmF5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgcmVtb3ZlVGFiTmF2aWdhdGlvbiBtZXRob2QgcmVtb3ZlcyB0YWIgbmF2aWdhdGlvbiBmb3IgYW4gYXJyYXkgb2YgZWxlbWVudHMgb2YgdHlwZSBCdXR0b25EaXJlY3RpdmUsIExpc3Rib3hJdGVtQ29tcG9uZW50LCBvciBDaGVja2JveERpcmVjdGl2ZS4gPGJyIC8+XG4gICAqIFdoZW4gdGhpcyBmdW5jdGlvbmFsaXR5IGlzIGFkZGVkLCBpdCB3aWxsIGRpc2FibGUgdGFiYmluZyBiZXR3ZWVuIHRoZXNlIHNwZWNpZmllZCBlbGVtZW50cy5cbiAgICogQHBhcmFtIGl0ZW1zQXJyYXkgQXJyYXkgb2YgaXRlbXMgdG8gcmVtb3ZlIHRhYiBuYXZpZ2F0aW9uIGZyb20uXG4gICAqL1xuICByZW1vdmVUYWJOYXZpZ2F0aW9uKGl0ZW1zQXJyYXk6IEJ1dHRvbkRpcmVjdGl2ZVtdIHwgTGlzdGJveEl0ZW1Db21wb25lbnRbXSB8IENoZWNrYm94RGlyZWN0aXZlW10pIHtcbiAgICBpZiAoIWl0ZW1zQXJyYXkgfHwgIWl0ZW1zQXJyYXkubGVuZ3RoKSByZXR1cm47XG4gICAgdGhpcy5maW5kU3RhcnRpbmdGb2N1cyhpdGVtc0FycmF5KTtcblxuICAgIGl0ZW1zQXJyYXkuZm9yRWFjaCgoaXRlbTogQnV0dG9uRGlyZWN0aXZlIHwgTGlzdGJveEl0ZW1Db21wb25lbnQgfCBDaGVja2JveERpcmVjdGl2ZSwgaW5kZXg6IG51bWJlcikgPT4ge1xuICAgICAgaWYgKCFpdGVtLmVsIHx8ICFpdGVtLmVsLm5hdGl2ZUVsZW1lbnQpIHJldHVybjtcblxuICAgICAgaXRlbS5saXN0ZW5lcnMucHVzaChcbiAgICAgICAgdGhpcy5yZW5kZXJlci5saXN0ZW4oaXRlbS5lbC5uYXRpdmVFbGVtZW50LCAna2V5ZG93bicsIChldmVudCkgPT4ge1xuICAgICAgICAgIC8vIGFzIHlvdSBsb29wIHRocm91Z2ggdGhlIGl0ZW1zIHdpdGggYXJyb3cga2V5cywgdGFiSW5kZXggbXVzdCBiZSB1cGRhdGVkIHNvIHRoYXQgb25seSB0aGUgaXRlbSB3aXRoIGZvY3VzIGhhcyB0YWJJbmRleCA9IDBcbiAgICAgICAgICBpZiAoZXZlbnQua2V5ID09PSBET1dOX0FSUk9XX0tFWSB8fCBldmVudC5rZXkgPT09IFJJR0hUX0FSUk9XX0tFWSkge1xuICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTsgLy8gcHJldmVudCBzY3JvbGxpbmdcbiAgICAgICAgICAgIGNvbnN0IGZvY3VzYWJsZUluZGV4ID0gdGhpcy5uZXh0RW5hYmxlZEl0ZW0oaXRlbXNBcnJheSwgaW5kZXgpO1xuICAgICAgICAgICAgaWYgKGZvY3VzYWJsZUluZGV4ID4gLTEpIHtcbiAgICAgICAgICAgICAgdGhpcy5yZW5kZXJlci5zZXRBdHRyaWJ1dGUoaXRlbS5lbC5uYXRpdmVFbGVtZW50LCAndGFiSW5kZXgnLCAnLTEnKTtcbiAgICAgICAgICAgICAgdGhpcy5yZW5kZXJlci5zZXRBdHRyaWJ1dGUoaXRlbXNBcnJheVtmb2N1c2FibGVJbmRleF0uZWwubmF0aXZlRWxlbWVudCwgJ3RhYkluZGV4JywgJzAnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gVVBfQVJST1dfS0VZIHx8IGV2ZW50LmtleSA9PT0gTEVGVF9BUlJPV19LRVkpIHtcbiAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7IC8vIHByZXZlbnQgc2Nyb2xsaW5nXG4gICAgICAgICAgICBjb25zdCBmb2N1c2FibGVJbmRleCA9IHRoaXMucHJldmlvdXNFbmFibGVkSXRlbShpdGVtc0FycmF5LCBpbmRleCk7XG4gICAgICAgICAgICBpZiAoZm9jdXNhYmxlSW5kZXggPiAtMSkge1xuICAgICAgICAgICAgICB0aGlzLnJlbmRlcmVyLnNldEF0dHJpYnV0ZShpdGVtLmVsLm5hdGl2ZUVsZW1lbnQsICd0YWJJbmRleCcsICctMScpO1xuICAgICAgICAgICAgICB0aGlzLnJlbmRlcmVyLnNldEF0dHJpYnV0ZShpdGVtc0FycmF5W2ZvY3VzYWJsZUluZGV4XS5lbC5uYXRpdmVFbGVtZW50LCAndGFiSW5kZXgnLCAnMCcpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSBUQUJfS0VZKSB7XG4gICAgICAgICAgICAvLyBpZiB5b3UgdGFiIG91dCBvZiB0aGUgYXJyYXksIHJlc2V0IHRoZSBzdGFydGluZyBpbmRleFxuICAgICAgICAgICAgdGhpcy5maW5kU3RhcnRpbmdGb2N1cyhpdGVtc0FycmF5KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgICApO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoaXMgbWV0aG9kIHJlc2V0cyBuYXZpZ2F0aW9uIGJlaGF2aW9ycyBmb3IgYW4gYXJyYXkgb2YgZWxlbWVudHMgb2YgdHlwZSA8Y29kZT5CdXR0b25EaXJlY3RpdmU8L2NvZGU+LCA8Y29kZT5MaXN0Ym94SXRlbUNvbXBvbmVudDwvY29kZT4sIG9yIDxjb2RlPkNoZWNrYm94RGlyZWN0aXZlPC9jb2RlPi4gPGJyIC8+XG4gICAqIEl0IHJlbW92ZXMgYW55IHRhYmluZGV4IG9yIGV2ZW50IGxpc3RlbmVycyBhZGRlZCBieSA8Y29kZT5hZGRBcnJvd0tleU5hdmlnYXRpb248L2NvZGU+IG9yIDxjb2RlPnJlbW92ZVRhYk5hdmlnYXRpb248L2NvZGU+LlxuICAgKiBAcGFyYW0gaXRlbXNBcnJheSBBcnJheSBvZiBpdGVtcyB0byByZXNldCBuYXZpZ2F0aW9uIGJlaGF2aW9ycyBmb3IuXG4gICAqL1xuICByZXNldE5hdmlnYXRpb25CZWhhdmlvcnMoaXRlbXNBcnJheTogQnV0dG9uRGlyZWN0aXZlW10gfCBMaXN0Ym94SXRlbUNvbXBvbmVudFtdIHwgQ2hlY2tib3hEaXJlY3RpdmVbXSkge1xuICAgIGlmICghaXRlbXNBcnJheSB8fCAhaXRlbXNBcnJheS5sZW5ndGgpIHJldHVybjtcbiAgICBpdGVtc0FycmF5LmZvckVhY2goKGl0ZW06IEJ1dHRvbkRpcmVjdGl2ZSB8IExpc3Rib3hJdGVtQ29tcG9uZW50IHwgQ2hlY2tib3hEaXJlY3RpdmUsIGluZGV4OiBudW1iZXIpID0+IHtcbiAgICAgIGlmICghaXRlbS5lbCB8fCAhaXRlbS5lbC5uYXRpdmVFbGVtZW50KSByZXR1cm47XG4gICAgICB0aGlzLnJlbmRlcmVyLnJlbW92ZUF0dHJpYnV0ZShpdGVtLmVsLm5hdGl2ZUVsZW1lbnQsICd0YWJJbmRleCcpO1xuICAgICAgaXRlbS5saXN0ZW5lcnMuZm9yRWFjaCgobGlzdGVuZXIpID0+IHtcbiAgICAgICAgaWYgKGxpc3RlbmVyIGluc3RhbmNlb2YgU2FmZVN1YnNjcmliZXIpIHtcbiAgICAgICAgICAvLyB1bnN1YnNjcmliZSBmcm9tIHN1YnNjcmlwdGlvblxuICAgICAgICAgIGxpc3RlbmVyLnVuc3Vic2NyaWJlKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gcmVtb3ZlIGV2ZW50IGxpc3RlbmVyIChpZiB1c2VkIHdpdGggcmVuZGVyZXIubGlzdGVuKVxuICAgICAgICAgIGxpc3RlbmVyKCk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgaXRlbS5saXN0ZW5lcnMgPSBbXTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgZmluZFN0YXJ0aW5nRm9jdXMgbWV0aG9kIGZpbmRzIHRoZSBpdGVtIHRvIHN0YXJ0IG5hdmlnYXRpb24gb24uIDxiciAvPlxuICAgKiBUaGUgc3RhcnRpbmcgZm9jdXNhYmxlIGl0ZW0gaXMgZWl0aGVyIHRoZSBmaXJzdCBpdGVtIG9yIHRoZSBjdXJyZW50bHkgc2VsZWN0ZWQgaXRlbS5cbiAgICogQHBhcmFtIGl0ZW1zQXJyYXkgQXJyYXkgb2YgaXRlbXMgdG8gZmluZCBzdGFydGluZyBmb2N1cyBmb3IuXG4gICAqL1xuICBmaW5kU3RhcnRpbmdGb2N1cyhpdGVtc0FycmF5OiBCdXR0b25EaXJlY3RpdmVbXSB8IExpc3Rib3hJdGVtQ29tcG9uZW50W10gfCBDaGVja2JveERpcmVjdGl2ZVtdKSB7XG4gICAgaWYgKCFpdGVtc0FycmF5IHx8ICFpdGVtc0FycmF5Lmxlbmd0aCkgcmV0dXJuO1xuICAgIC8vIGl0ZW0uYXJpYVNlbGVjdGVkID0gYnV0dG9uLCBpdGVtLmFjdGl2ZSA9IGxpc3Rib3hJdGVtLCBpdGVtLmNoZWNrZWQgPSBjaGVja2JveFxuICAgIGxldCBzZWxlY3RlZEl0ZW1JbmRleCA9IGl0ZW1zQXJyYXkuZmluZEluZGV4KFxuICAgICAgKGl0ZW06IEJ1dHRvbkRpcmVjdGl2ZSB8IExpc3Rib3hJdGVtQ29tcG9uZW50IHwgQ2hlY2tib3hEaXJlY3RpdmUpID0+XG4gICAgICAgICgoaXRlbSBpbnN0YW5jZW9mIEJ1dHRvbkRpcmVjdGl2ZSAmJiBpdGVtLmFyaWFTZWxlY3RlZCkgfHxcbiAgICAgICAgICAoaXRlbSBpbnN0YW5jZW9mIExpc3Rib3hJdGVtQ29tcG9uZW50ICYmIGl0ZW0uYWN0aXZlKSB8fFxuICAgICAgICAgIChpdGVtIGluc3RhbmNlb2YgQ2hlY2tib3hEaXJlY3RpdmUgJiYgaXRlbS5jaGVja2VkKSkgPT09IHRydWVcbiAgICApO1xuICAgIGlmIChzZWxlY3RlZEl0ZW1JbmRleCA9PT0gLTEpIHNlbGVjdGVkSXRlbUluZGV4ID0gdGhpcy5uZXh0RW5hYmxlZEl0ZW0oaXRlbXNBcnJheSk7XG4gICAgaXRlbXNBcnJheS5mb3JFYWNoKChpdGVtOiBCdXR0b25EaXJlY3RpdmUgfCBMaXN0Ym94SXRlbUNvbXBvbmVudCB8IENoZWNrYm94RGlyZWN0aXZlLCBpbmRleDogbnVtYmVyKSA9PiB7XG4gICAgICBpZiAoIWl0ZW0uZWwgfHwgIWl0ZW0uZWwubmF0aXZlRWxlbWVudCkgcmV0dXJuO1xuICAgICAgLy8gaWYgYW4gaXRlbSBpcyB0aGUgZmlyc3QgLyBzZWxlY3RlZCBpdGVtLCBhbGxvdyB0YWIgZm9jdXNcbiAgICAgIGlmIChpbmRleCA9PT0gc2VsZWN0ZWRJdGVtSW5kZXgpIHtcbiAgICAgICAgdGhpcy5yZW5kZXJlci5zZXRBdHRyaWJ1dGUoaXRlbS5lbC5uYXRpdmVFbGVtZW50LCAndGFiSW5kZXgnLCAnMCcpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gb3RoZXJ3aXNlIHJlbW92ZSB0YWIgZm9jdXMgYWJpbGl0eVxuICAgICAgICB0aGlzLnJlbmRlcmVyLnNldEF0dHJpYnV0ZShpdGVtLmVsLm5hdGl2ZUVsZW1lbnQsICd0YWJJbmRleCcsICctMScpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBuZXh0RW5hYmxlZEl0ZW0gbWV0aG9kIGZpbmRzIHRoZSBuZXh0IGl0ZW0gaW4gdGhlIGFycmF5IHRoYXQgaXMgbm90IGRpc2FibGVkLiA8YnIgLz5cbiAgICogV2hlbiB0aGlzIGZ1bmN0aW9uYWxpdHkgaXMgYWRkZWQsIGl0IHdpbGwgYXV0b21hdGljYWxseSBza2lwIG92ZXIgZGlzYWJsZWQgaXRlbXMgdG8gbG9jYXRlIHRoZSBuZXh0IGVuYWJsZWQgb25lLlxuICAgKiBAcGFyYW0gaXRlbXMgQXJyYXkgb2YgaXRlbXMgdG8gc2VhcmNoIGZvciBuZXh0IGVuYWJsZWQgaXRlbS5cbiAgICogQHBhcmFtIGN1cnJlbnRJbmRleCBJbmRleCB0byBzdGFydCBzZWFyY2hpbmcgZnJvbS5cbiAgICogQHJldHVybnMgSW5kZXggb2YgbmV4dCBpdGVtIHRoYXQgaXMgbm90IGRpc2FibGVkLlxuICAgKi9cbiAgbmV4dEVuYWJsZWRJdGVtKFxuICAgIGl0ZW1zOiBCdXR0b25EaXJlY3RpdmVbXSB8IExpc3Rib3hJdGVtQ29tcG9uZW50W10gfCBDaGVja2JveERpcmVjdGl2ZVtdLFxuICAgIGN1cnJlbnRJbmRleD86IG51bWJlclxuICApOiBudW1iZXIge1xuICAgIGlmICghaXRlbXMgfHwgIWl0ZW1zLmxlbmd0aCkgcmV0dXJuIC0xO1xuICAgIGxldCBpbmRleCA9IGN1cnJlbnRJbmRleCB8fCBjdXJyZW50SW5kZXggPT09IDAgPyBjdXJyZW50SW5kZXggKyAxIDogMDtcbiAgICBsZXQgY291bnQgPSAwO1xuICAgIHdoaWxlIChjb3VudCA8IGl0ZW1zLmxlbmd0aCAmJiBpbmRleCAhPT0gY3VycmVudEluZGV4KSB7XG