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