@visa/nova-angular
Version:
Visa Product Design System Nova Angular library
428 lines • 61.3 kB
JavaScript
/**
* 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