@visa/nova-angular
Version:
Visa Product Design System Nova Angular library
315 lines • 47.7 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 { Injectable, RendererFactory2 } from '@angular/core';
import { AppReadyService } from '../_utilities/services/app-stable-check.service';
import { DOWN_ARROW_KEY, END_KEY, ENTER_KEY, HOME_KEY, SPACE_CODE, SPACE_KEY, TAB_KEY, UP_ARROW_KEY } from '../nova-lib.constants';
import { NovaLibService } from '../nova-lib.service';
import * as i0 from "@angular/core";
import * as i1 from "../nova-lib.service";
import * as i2 from "../_utilities/services/app-stable-check.service";
/**
* Service used to create listbox behavior. Some functions are used within listbox component, others are optional additions.
*/
export class ListboxService {
constructor(rendererFactory, novaLibService, appReadyService) {
this.rendererFactory = rendererFactory;
this.novaLibService = novaLibService;
this.appReadyService = appReadyService;
this.renderer = rendererFactory.createRenderer(null, null);
}
/**
* The setUpListbox method configures the listbox with the expected keyboard behaviors and sets the aria-activedescendant attribute. <br>
* This method is called by default within <code>ListboxDirective</code>.
* @param listbox Listbox to set up.
*/
setUpListbox(listbox) {
this.listbox = listbox;
this.listenForActiveIndex(listbox);
this.novaLibService.addArrowKeyNavigation(listbox.listItems.toArray(), true);
this.addShortcuts(listbox);
}
/**
* The listenForActiveIndex method sets the <code>aria-activedescendant</code> attribute of the listbox to the ID of the active listbox item and is called by default within <code>setUpListbox</code>.
* @param listbox Listbox to listen to for active index.
*/
listenForActiveIndex(listbox) {
listbox.listItems.forEach((item) => {
this.renderer.listen(item.el.nativeElement, 'focus', (event) => {
listbox.ariaActiveDescendant.set(item.id);
});
});
}
/**
* The scrollItemIntoView method scrolls given item to top of listbox. <br>
* It’s called by default in <code>ListBoxDirective</code> unless <code>customScrollControl</code> is present. <br>
* This method can also call to scroll to a custom index. By default it will scroll to the first active item, but you can provide a specific item if desired.
* @param listbox Listbox to scroll.
* @param indexToScrollTo Optional specific index to scroll to top (othwerwise, scrolls to first active item).
*/
scrollItemIntoView(listbox, indexToScrollTo) {
const targetItem = indexToScrollTo || indexToScrollTo === 0
? listbox.listItems.toArray()[indexToScrollTo]
: listbox.multiselect
? listbox.listItems
.toArray()
.reverse()
.find((item) => item.active)
: listbox.listItems.toArray().find((item) => item.active);
if (targetItem && this.appReadyService.isBrowserAndDomAvailable()) {
if (!listbox._listboxScrollStylesSet) {
// gather variable information
listbox._listboxHeight = listbox.el.nativeElement.offsetHeight;
listbox._listboxItemHeight = listbox.listItems.toArray()[0].el.nativeElement.offsetHeight;
listbox._listboxGap = parseInt(window.getComputedStyle(listbox.el.nativeElement).gap, 10);
listbox._listboxGap = isNaN(listbox._listboxGap) ? 4 : listbox._listboxGap;
}
let amountScrolledAlready = listbox.el.nativeElement.scrollTop;
// determine distance between top of listbox and active item
this.renderer.setStyle(listbox.el.nativeElement, 'position', 'relative'); // set position to relative to allow for correct offsetTop of item
const fullItemDistanceFromTop = targetItem.el.nativeElement.offsetTop + listbox._listboxItemHeight;
// if item is in the top view of the listbox..
if (fullItemDistanceFromTop < listbox._listboxHeight) {
if (!amountScrolledAlready) {
return; // do nothing, item is in view (user hasn't scrolled)
}
else {
// scroll to top of listbox (where item is)
listbox.el.nativeElement.scrollTo(0, 0);
}
}
else if (
/**
* Do nothing if the item is between the amount already scrolled and the bottom of the listbox
* ie it is in view and the user has scrolled
*/
amountScrolledAlready &&
fullItemDistanceFromTop < listbox._listboxHeight + amountScrolledAlready &&
fullItemDistanceFromTop > amountScrolledAlready) {
return;
}
else {
// either scroll to top of listbox or scroll item to top
listbox.el.nativeElement.scrollBy({
top: fullItemDistanceFromTop - listbox._listboxGap - amountScrolledAlready - listbox._listboxItemHeight
});
}
}
}
/**
* Select all items between currently focused item and closest selected item.
* @param items List of items that contains the items you want to select.
* @param index Index of the focused item.
*/
selectFromCloserSelectedToFocused(items, index) {
let [left, right] = [index, index];
while (left !== 0 || right !== items.length - 1) {
if (items[left]['active'] || items[right]['active'])
break;
if (left !== 0)
left--;
if (right !== items.length - 1)
right++;
}
this.novaLibService.selectItems(items, items[left]['active'] ? left : index, items[left]['active'] ? index : right);
}
/* ======= KEYBOARD SHORTCUTS ======= */
/**
* The addShortcuts method adds keyboard shortcuts to the listbox and is called by default within <code>setUpListbox</code>.
* @param listbox Listbox to add shortcuts to.
*/
addShortcuts(listbox) {
this.renderer.listen(listbox.el.nativeElement, 'keydown', (event) => {
this.handleKeyDown(event, listbox);
});
this.renderer.listen(listbox.el.nativeElement, 'keyup', (event) => {
this.handleKeyUp(event, listbox);
});
}
/**
* The handleKeyup method handles item selection on keyup events for the listbox and is called by default within <code>addShortcuts</code>.
* @param event Keyup event
* @param listbox Listbox to handle keyup event for.
*/
handleKeyUp(event, listbox) {
if (!listbox._isRoleListboxVariant)
return;
if (event.key === ENTER_KEY || event.key === SPACE_KEY || event.key === HOME_KEY || event.key === END_KEY) {
event.preventDefault(); // prevent scrolling
if (event.key === SPACE_KEY) {
listbox._recentSelectedIndex = listbox._highlightIndex;
}
}
}
/**
* The handleKeydown handles keydown events for the listbox and is called by default within <code>addShortcuts</code>.
* @param event Keydown event
* @param listbox Listbox to handle keyup event for.
*/
handleKeyDown(event, listbox) {
if (!listbox._isRoleListboxVariant)
return;
listbox._isHotkeyEvent = true;
if (event.key !== TAB_KEY) {
event.preventDefault();
}
/**
* Meta + Shift + Home or End
* Select the focused option and all options up/down to the first option
* &
* Home or End
* Move the focus to the first/last listbox option
*/
if (event.key === HOME_KEY || event.key === END_KEY)
this.updateFocusItem(event, listbox);
/**
* A-Z OR a-z
* Typing a character will move the focus to the next item with a name that starts with that character
* Typing in rapid succession moves the focus to the next item with a name that reflectes the set of characters just typed
*/
if (!event.metaKey &&
event.key.length === 1 &&
((event.key >= 'a' && event.key <= 'z') ||
(event.key >= 'A' && event.key <= 'Z') ||
(event.key >= '0' && event.key <= '9')))
this.searchKeyword(event, listbox);
/**
* Shift + ↑/↓
* Move the focus to and toggle the selected state of the next/previous option
*/
if (event.key === UP_ARROW_KEY || event.key === DOWN_ARROW_KEY)
this.toggleSelectedState(event, listbox);
/**
* Meta + A
* Select all or deselect all items
*/
if (event.metaKey && (event.key === 'A' || event.key === 'a') && listbox.multiselect)
this.selectAll(listbox);
/**
* Shift + Space
* Select contiguous items from the most recently selected item to ths focused item
*/
if (event.code === SPACE_CODE && event.shiftKey === true)
this.selectContiguousItems(listbox);
listbox._isHotkeyEvent = false;
}
/**
* The updateFocusItem method is activated by Meta + Shift + Home/End and selects all items between the focused item and the first or last item, then sets focus to the first or last item. <br>
* This method is called by default within <code>handleKeyDown</code>.
* @param event Keyboard event
* @param listbox Listbox to update focus item for.
*/
updateFocusItem(event, listbox) {
const listItemsArray = listbox.listItems.toArray();
if (event.metaKey && event.shiftKey && listbox.multiselect) {
listItemsArray.forEach((item, i) => {
if (listbox._highlightIndex !== null && item.disabled !== true) {
if (event.key === HOME_KEY)
listbox._highlightIndex >= i ? (item.active = true) : (item.active = false);
if (event.key === END_KEY)
listbox._highlightIndex <= i ? (item.active = true) : (item.active = false);
}
});
}
listbox._highlightIndex =
event.key === HOME_KEY
? this.novaLibService.firstEnabledItem(listItemsArray)
: this.novaLibService.lastEnabledItem(listItemsArray);
listbox.updateValueFromItems();
listItemsArray[listbox._highlightIndex].el.nativeElement.focus();
}
/**
* The searchKeyword method handles keys A-Z, a-z, and 0-9, moving the focus to the next item with a name starting with the typed character. <br>
* In the case of rapid succession, it moves the focus to the next item whose name matches the sequence of characters typed. <br />
* This method is called by default within <code>handleKeyDown</code>.
* @param event Keyboard event
* @param listbox Listbox to search for items in.
*/
searchKeyword(event, listbox) {
const listItemsArray = listbox.listItems.toArray();
listbox._keyword = listbox._keyword + event.key.toLowerCase();
clearTimeout(listbox._timeoutId);
const bounce = window.setTimeout(() => {
if (listbox._keyword !== '') {
listbox._keyword = '';
}
}, 1000);
listbox._timeoutId = bounce;
const selectedIndex = listItemsArray.findIndex((item) => item.el.nativeElement.innerText?.toLowerCase().includes(listbox._keyword));
if (selectedIndex >= 0 && !listItemsArray[selectedIndex].disabled) {
listItemsArray[selectedIndex].el.nativeElement.focus();
listbox._highlightIndex = selectedIndex;
}
}
/**
* The selectAll method is activated by the Meta + A keys and selects or deselects all items in the listbox. <br>
* This method is called by default within <code>handleKeyDown</code>.
* @param listbox Listbox to select all items in.
*/
selectAll(listbox) {
if (listbox.multiselect) {
const listItemsArray = listbox.listItems.toArray();
this.novaLibService.detectAllItemsSelected(listItemsArray)
? this.novaLibService.deselectItems(listItemsArray)
: this.novaLibService.selectItems(listItemsArray);
}
}
/**
* The selectFromCloserSelectedToFocused method selects all items between the currently focused item and the closest selected item. <br>
* This method is called by default within <code>handleKeyDown</code>.
* @param index Index of the focused item.
*/
selectContiguousItems(listbox) {
if (listbox._highlightIndex !== null && listbox._recentSelectedIndex !== null) {
const listItemsArray = listbox.listItems.toArray();
const isRecentLarger = listbox._recentSelectedIndex > listbox._highlightIndex;
this.novaLibService.selectItems(listItemsArray, isRecentLarger ? listbox._highlightIndex : listbox._recentSelectedIndex, isRecentLarger ? listbox._recentSelectedIndex : listbox._highlightIndex);
listbox.updateValueFromItems();
}
}
/**
* The toggleSelectedState method is activated by Shift + ↑/↓ and moves the focus to, and toggles the selected state of, the next or previous option. <br>
* This method is called by default within <code>handleKeyDown</code>.
* @param event Keyboard event
* @param listbox Listbox to toggle selected state for.
*/
toggleSelectedState(event, listbox) {
const listItemsArray = listbox.listItems.toArray();
if (event.shiftKey === true && listbox.multiselect && listbox._highlightIndex !== null) {
if (listItemsArray[listbox._highlightIndex].active) {
this.novaLibService.deselectItem(listItemsArray, listbox._highlightIndex);
}
else {
this.novaLibService.selectItem(listItemsArray, listbox._highlightIndex);
listbox._recentSelectedIndex = listbox._highlightIndex;
}
listbox.updateValueFromItems();
}
listbox._highlightIndex =
event.key === UP_ARROW_KEY
? this.novaLibService.previousEnabledItem(listItemsArray, listbox._highlightIndex)
: this.novaLibService.nextEnabledItem(listItemsArray, listbox._highlightIndex);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ListboxService, deps: [{ token: i0.RendererFactory2 }, { token: i1.NovaLibService }, { token: i2.AppReadyService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ListboxService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ListboxService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i0.RendererFactory2 }, { type: i1.NovaLibService }, { type: i2.AppReadyService }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlzdGJveC5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy9ub3ZhLWxpYi9zcmMvbGliL2xpc3Rib3gvbGlzdGJveC5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7SUFlSTtBQUNKLE9BQU8sRUFBRSxVQUFVLEVBQWEsZ0JBQWdCLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDeEUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGlEQUFpRCxDQUFDO0FBRWxGLE9BQU8sRUFDTCxjQUFjLEVBQ2QsT0FBTyxFQUNQLFNBQVMsRUFDVCxRQUFRLEVBQ1IsVUFBVSxFQUNWLFNBQVMsRUFDVCxPQUFPLEVBQ1AsWUFBWSxFQUNiLE1BQU0sdUJBQXVCLENBQUM7QUFDL0IsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHFCQUFxQixDQUFDOzs7O0FBR3JEOztHQUVHO0FBSUgsTUFBTSxPQUFPLGNBQWM7SUFDekIsWUFDVSxlQUFpQyxFQUNqQyxjQUE4QixFQUM5QixlQUFnQztRQUZoQyxvQkFBZSxHQUFmLGVBQWUsQ0FBa0I7UUFDakMsbUJBQWMsR0FBZCxjQUFjLENBQWdCO1FBQzlCLG9CQUFlLEdBQWYsZUFBZSxDQUFpQjtRQUV4QyxJQUFJLENBQUMsUUFBUSxHQUFHLGVBQWUsQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzdELENBQUM7SUFNRDs7OztPQUlHO0lBQ0gsWUFBWSxDQUFDLE9BQXlCO1FBQ3BDLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNuQyxJQUFJLENBQUMsY0FBYyxDQUFDLHFCQUFxQixDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDN0UsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsb0JBQW9CLENBQUMsT0FBeUI7UUFDNUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRTtZQUNqQyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLGFBQWEsRUFBRSxPQUFPLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtnQkFDN0QsT0FBTyxDQUFDLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDNUMsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxrQkFBa0IsQ0FBQyxPQUF5QixFQUFFLGVBQXdCO1FBQ3BFLE1BQU0sVUFBVSxHQUNkLGVBQWUsSUFBSSxlQUFlLEtBQUssQ0FBQztZQUN0QyxDQUFDLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxlQUFlLENBQUM7WUFDOUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxXQUFXO2dCQUNuQixDQUFDLENBQUMsT0FBTyxDQUFDLFNBQVM7cUJBQ2QsT0FBTyxFQUFFO3FCQUNULE9BQU8sRUFBRTtxQkFDVCxJQUFJLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7Z0JBQ2hDLENBQUMsQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRWhFLElBQUksVUFBVSxJQUFJLElBQUksQ0FBQyxlQUFlLENBQUMsd0JBQXdCLEVBQUUsRUFBRSxDQUFDO1lBQ2xFLElBQUksQ0FBQyxPQUFPLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztnQkFDckMsOEJBQThCO2dCQUM5QixPQUFPLENBQUMsY0FBYyxHQUFHLE9BQU8sQ0FBQyxFQUFFLENBQUMsYUFBYSxDQUFDLFlBQVksQ0FBQztnQkFDL0QsT0FBTyxDQUFDLGtCQUFrQixHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxZQUFZLENBQUM7Z0JBQzFGLE9BQU8sQ0FBQyxXQUFXLEdBQUcsUUFBUSxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQztnQkFDMUYsT0FBTyxDQUFDLFdBQVcsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUM7WUFDN0UsQ0FBQztZQUVELElBQUkscUJBQXFCLEdBQUcsT0FBTyxDQUFDLEVBQUUsQ0FBQyxhQUFhLENBQUMsU0FBUyxDQUFDO1lBQy9ELDREQUE0RDtZQUM1RCxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLGFBQWEsRUFBRSxVQUFVLEVBQUUsVUFBVSxDQUFDLENBQUMsQ0FBQyxrRUFBa0U7WUFDNUksTUFBTSx1QkFBdUIsR0FBRyxVQUFVLENBQUMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDLGtCQUFrQixDQUFDO1lBRW5HLDhDQUE4QztZQUM5QyxJQUFJLHVCQUF1QixHQUFHLE9BQU8sQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFDckQsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7b0JBQzNCLE9BQU8sQ0FBQyxxREFBcUQ7Z0JBQy9ELENBQUM7cUJBQU0sQ0FBQztvQkFDTiwyQ0FBMkM7b0JBQzNDLE9BQU8sQ0FBQyxFQUFFLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQzFDLENBQUM7WUFDSCxDQUFDO2lCQUFNO1lBQ0w7OztlQUdHO1lBQ0gscUJBQXFCO2dCQUNyQix1QkFBdUIsR0FBRyxPQUFPLENBQUMsY0FBYyxHQUFHLHFCQUFxQjtnQkFDeEUsdUJBQXVCLEdBQUcscUJBQXFCLEVBQy9DLENBQUM7Z0JBQ0QsT0FBTztZQUNULENBQUM7aUJBQU0sQ0FBQztnQkFDTix3REFBd0Q7Z0JBQ3hELE9BQU8sQ0FBQyxFQUFFLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQztvQkFDaEMsR0FBRyxFQUFFLHVCQUF1QixHQUFHLE9BQU8sQ0FBQyxXQUFXLEdBQUcscUJBQXFCLEdBQUcsT0FBTyxDQUFDLGtCQUFrQjtpQkFDeEcsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILGlDQUFpQyxDQUFDLEtBQTZCLEVBQUUsS0FBYTtRQUM1RSxJQUFJLENBQUMsSUFBSSxFQUFFLEtBQUssQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ25DLE9BQU8sSUFBSSxLQUFLLENBQUMsSUFBSSxLQUFLLEtBQUssS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUNoRCxJQUFJLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFRLENBQUMsSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsUUFBUSxDQUFDO2dCQUFFLE1BQU07WUFDM0QsSUFBSSxJQUFJLEtBQUssQ0FBQztnQkFBRSxJQUFJLEVBQUUsQ0FBQztZQUN2QixJQUFJLEtBQUssS0FBSyxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUM7Z0JBQUUsS0FBSyxFQUFFLENBQUM7UUFDMUMsQ0FBQztRQUNELElBQUksQ0FBQyxjQUFjLENBQUMsV0FBVyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUN0SCxDQUFDO0lBRUQsd0NBQXdDO0lBRXhDOzs7T0FHRztJQUNILFlBQVksQ0FBQyxPQUF5QjtRQUNwQyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLGFBQWEsRUFBRSxTQUFTLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtZQUNsRSxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNyQyxDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsYUFBYSxFQUFFLE9BQU8sRUFBRSxDQUFDLEtBQUssRUFBRSxFQUFFO1lBQ2hFLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ25DLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxXQUFXLENBQUMsS0FBb0IsRUFBRSxPQUF5QjtRQUN6RCxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQjtZQUFFLE9BQU87UUFDM0MsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLFNBQVMsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLFNBQVMsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLE9BQU8sRUFBRSxDQUFDO1lBQzFHLEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFDLG9CQUFvQjtZQUM1QyxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssU0FBUyxFQUFFLENBQUM7Z0JBQzVCLE9BQU8sQ0FBQyxvQkFBb0IsR0FBRyxPQUFPLENBQUMsZUFBZSxDQUFDO1lBQ3pELENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxhQUFhLENBQUMsS0FBb0IsRUFBRSxPQUF5QjtRQUMzRCxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQjtZQUFFLE9BQU87UUFDM0MsT0FBTyxDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUM7UUFDOUIsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLE9BQU8sRUFBRSxDQUFDO1lBQzFCLEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUN6QixDQUFDO1FBRUQ7Ozs7OztXQU1HO1FBQ0gsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLE9BQU87WUFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQztRQUUxRjs7OztXQUlHO1FBQ0gsSUFDRSxDQUFDLEtBQUssQ0FBQyxPQUFPO1lBQ2QsS0FBSyxDQUFDLEdBQUcsQ0FBQyxNQUFNLEtBQUssQ0FBQztZQUN0QixDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsSUFBSSxHQUFHLElBQUksS0FBSyxDQUFDLEdBQUcsSUFBSSxHQUFHLENBQUM7Z0JBQ3JDLENBQUMsS0FBSyxDQUFDLEdBQUcsSUFBSSxHQUFHLElBQUksS0FBSyxDQUFDLEdBQUcsSUFBSSxHQUFHLENBQUM7Z0JBQ3RDLENBQUMsS0FBSyxDQUFDLEdBQUcsSUFBSSxHQUFHLElBQUksS0FBSyxDQUFDLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQztZQUV6QyxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQztRQUVyQzs7O1dBR0c7UUFDSCxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssWUFBWSxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssY0FBYztZQUFFLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFekc7OztXQUdHO1FBQ0gsSUFBSSxLQUFLLENBQUMsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsS0FBSyxHQUFHLElBQUksS0FBSyxDQUFDLEdBQUcsS0FBSyxHQUFHLENBQUMsSUFBSSxPQUFPLENBQUMsV0FBVztZQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFOUc7OztXQUdHO1FBQ0gsSUFBSSxLQUFLLENBQUMsSUFBSSxLQUFLLFVBQVUsSUFBSSxLQUFLLENBQUMsUUFBUSxLQUFLLElBQUk7WUFBRSxJQUFJLENBQUMscUJBQXFCLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDOUYsT0FBTyxDQUFDLGNBQWMsR0FBRyxLQUFLLENBQUM7SUFDakMsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsZUFBZSxDQUFDLEtBQW9CLEVBQUUsT0FBeUI7UUFDN0QsTUFBTSxjQUFjLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNuRCxJQUFJLEtBQUssQ0FBQyxPQUFPLElBQUksS0FBSyxDQUFDLFFBQVEsSUFBSSxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDM0QsY0FBYyxDQUFDLE9BQU8sQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLEVBQUUsRUFBRTtnQkFDakMsSUFBSSxPQUFPLENBQUMsZUFBZSxLQUFLLElBQUksSUFBSSxJQUFJLENBQUMsUUFBUSxLQUFLLElBQUksRUFBRSxDQUFDO29CQUMvRCxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssUUFBUTt3QkFBRSxPQUFPLENBQUMsZUFBZSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDLENBQUM7b0JBQ3hHLElBQUksS0FBSyxDQUFDLEdBQUcsS0FBSyxPQUFPO3dCQUFFLE9BQU8sQ0FBQyxlQUFlLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUMsQ0FBQztnQkFDekcsQ0FBQztZQUNILENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUNELE9BQU8sQ0FBQyxlQUFlO1lBQ3JCLEtBQUssQ0FBQyxHQUFHLEtBQUssUUFBUTtnQkFDcEIsQ0FBQyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsZ0JBQWdCLENBQUMsY0FBYyxDQUFDO2dCQUN0RCxDQUFDLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxlQUFlLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDMUQsT0FBTyxDQUFDLG9CQUFvQixFQUFFLENBQUM7UUFDL0IsY0FBYyxDQUFDLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQyxFQUFFLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ25FLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxhQUFhLENBQUMsS0FBb0IsRUFBRSxPQUF5QjtRQUMzRCxNQUFNLGNBQWMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ25ELE9BQU8sQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQzlELFlBQVksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDakMsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDcEMsSUFBSSxPQUFPLENBQUMsUUFBUSxLQUFLLEVBQUUsRUFBRSxDQUFDO2dCQUM1QixPQUFPLENBQUMsUUFBUSxHQUFHLEVBQUUsQ0FBQztZQUN4QixDQUFDO1FBQ0gsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ1QsT0FBTyxDQUFDLFVBQVUsR0FBRyxNQUFNLENBQUM7UUFDNUIsTUFBTSxhQUFhLEdBQUcsY0FBYyxDQUFDLFNBQVMsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQ3RELElBQUksQ0FBQyxFQUFFLENBQUMsYUFBYSxDQUFDLFNBQVMsRUFBRSxXQUFXLEVBQUUsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUMxRSxDQUFDO1FBRUYsSUFBSSxhQUFhLElBQUksQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLGFBQWEsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ2xFLGNBQWMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxFQUFFLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ3ZELE9BQU8sQ0FBQyxlQUFlLEdBQUcsYUFBYSxDQUFDO1FBQzFDLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILFNBQVMsQ0FBQyxPQUF5QjtRQUNqQyxJQUFJLE9BQU8sQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUN4QixNQUFNLGNBQWMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ25ELElBQUksQ0FBQyxjQUFjLENBQUMsc0JBQXNCLENBQUMsY0FBYyxDQUFDO2dCQUN4RCxDQUFDLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxhQUFhLENBQUMsY0FBYyxDQUFDO2dCQUNuRCxDQUFDLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxXQUFXLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDdEQsQ0FBQztJQUNILENBQUM7SUFFRDs7OztPQUlHO0lBQ0gscUJBQXFCLENBQUMsT0FBeUI7UUFDN0MsSUFBSSxPQUFPLENBQUMsZUFBZSxLQUFLLElBQUksSUFBSSxPQUFPLENBQUMsb0JBQW9CLEtBQUssSUFBSSxFQUFFLENBQUM7WUFDOUUsTUFBTSxjQUFjLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNuRCxNQUFNLGNBQWMsR0FBRyxPQUFPLENBQUMsb0JBQW9CLEdBQUcsT0FBTyxDQUFDLGVBQWUsQ0FBQztZQUM5RSxJQUFJLENBQUMsY0FBYyxDQUFDLFdBQVcsQ0FDN0IsY0FBYyxFQUNkLGNBQWMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLG9CQUFvQixFQUN2RSxjQUFjLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FDeEUsQ0FBQztZQUNGLE9BQU8sQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1FBQ2pDLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCxtQkFBbUIsQ0FBQyxLQUFvQixFQUFFLE9BQXlCO1FBQ2pFLE1BQU0sY0FBYyxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDbkQsSUFBSSxLQUFLLENBQUMsUUFBUSxLQUFLLElBQUksSUFBSSxPQUFPLENBQUMsV0FBVyxJQUFJLE9BQU8sQ0FBQyxlQUFlLEtBQUssSUFBSSxFQUFFLENBQUM7WUFDdkYsSUFBSSxjQUFjLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDO2dCQUNuRCxJQUFJLENBQUMsY0FBYyxDQUFDLFlBQVksQ0FBQyxjQUFjLEVBQUUsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO1lBQzVFLENBQUM7aUJBQU0sQ0FBQztnQkFDTixJQUFJLENBQUMsY0FBYyxDQUFDLFVBQVUsQ0FBQyxjQUFjLEVBQUUsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO2dCQUN4RSxPQUFPLENBQUMsb0JBQW9CLEdBQUcsT0FBTyxDQUFDLGVBQWUsQ0FBQztZQUN6RCxDQUFDO1lBQ0QsT0FBTyxDQUFDLG9CQUFvQixFQUFFLENBQUM7UUFDakMsQ0FBQztRQUNELE9BQU8sQ0FBQyxlQUFlO1lBQ3JCLEtBQUssQ0FBQyxHQUFHLEtBQUssWUFBWTtnQkFDeEIsQ0FBQyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsbUJBQW1CLENBQUMsY0FBYyxFQUFFLE9BQU8sQ0FBQyxlQUFnQixDQUFDO2dCQUNuRixDQUFDLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxlQUFlLENBQUMsY0FBYyxFQUFFLE9BQU8sQ0FBQyxlQUFnQixDQUFDLENBQUM7SUFDdEYsQ0FBQzsrR0E1U1UsY0FBYzttSEFBZCxjQUFjLGNBRmIsTUFBTTs7NEZBRVAsY0FBYztrQkFIMUIsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkIiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqICAgICAgICAgICAgICDCqSAyMDI1IFZpc2FcbiAqXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICAgICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICpcbiAqKi9cbmltcG9ydCB7IEluamVjdGFibGUsIFJlbmRlcmVyMiwgUmVuZGVyZXJGYWN0b3J5MiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQXBwUmVhZHlTZXJ2aWNlIH0gZnJvbSAnLi4vX3V0aWxpdGllcy9zZXJ2aWNlcy9hcHAtc3RhYmxlLWNoZWNrLnNlcnZpY2UnO1xuaW1wb3J0IHsgTGlzdGJveEl0ZW1Db21wb25lbnQgfSBmcm9tICcuLi9saXN0Ym94LWl0ZW0vbGlzdGJveC1pdGVtLmNvbXBvbmVudCc7XG5pbXBvcnQge1xuICBET1dOX0FSUk9XX0tFWSxcbiAgRU5EX0tFWSxcbiAgRU5URVJfS0VZLFxuICBIT01FX0tFWSxcbiAgU1BBQ0VfQ09ERSxcbiAgU1BBQ0VfS0VZLFxuICBUQUJfS0VZLFxuICBVUF9BUlJPV19LRVlcbn0gZnJvbSAnLi4vbm92YS1saWIuY29uc3RhbnRzJztcbmltcG9ydCB7IE5vdmFMaWJTZXJ2aWNlIH0gZnJvbSAnLi4vbm92YS1saWIuc2VydmljZSc7XG5pbXBvcnQgeyBMaXN0Ym94RGlyZWN0aXZlIH0gZnJvbSAnLi9saXN0Ym94LmRpcmVjdGl2ZSc7XG5cbi8qKlxuICogU2VydmljZSB1c2VkIHRvIGNyZWF0ZSBsaXN0Ym94IGJlaGF2aW9yLiBTb21lIGZ1bmN0aW9ucyBhcmUgdXNlZCB3aXRoaW4gbGlzdGJveCBjb21wb25lbnQsIG90aGVycyBhcmUgb3B0aW9uYWwgYWRkaXRpb25zLlxuICovXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBMaXN0Ym94U2VydmljZSB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVuZGVyZXJGYWN0b3J5OiBSZW5kZXJlckZhY3RvcnkyLFxuICAgIHByaXZhdGUgbm92YUxpYlNlcnZpY2U6IE5vdmFMaWJTZXJ2aWNlLFxuICAgIHByaXZhdGUgYXBwUmVhZHlTZXJ2aWNlOiBBcHBSZWFkeVNlcnZpY2VcbiAgKSB7XG4gICAgdGhpcy5yZW5kZXJlciA9IHJlbmRlcmVyRmFjdG9yeS5jcmVhdGVSZW5kZXJlcihudWxsLCBudWxsKTtcbiAgfVxuICAvKiogQGlnbm9yZSAqL1xuICBwcml2YXRlIHJlbmRlcmVyOiBSZW5kZXJlcjI7XG4gIC8qKiBAaWdub3JlICovXG4gIGxpc3Rib3g6IExpc3Rib3hEaXJlY3RpdmU7XG5cbiAgLyoqXG4gICAqIFRoZcKgc2V0VXBMaXN0Ym94wqBtZXRob2QgY29uZmlndXJlcyB0aGUgbGlzdGJveCB3aXRoIHRoZSBleHBlY3RlZCBrZXlib2FyZCBiZWhhdmlvcnMgYW5kIHNldHMgdGhlwqBhcmlhLWFjdGl2ZWRlc2NlbmRhbnTCoGF0dHJpYnV0ZS4gPGJyPlxuICAgKiBUaGlzIG1ldGhvZCBpcyBjYWxsZWQgYnkgZGVmYXVsdCB3aXRoaW4gPGNvZGU+TGlzdGJveERpcmVjdGl2ZTwvY29kZT4uXG4gICAqIEBwYXJhbSBsaXN0Ym94IExpc3Rib3ggdG8gc2V0IHVwLlxuICAgKi9cbiAgc2V0VXBMaXN0Ym94KGxpc3Rib3g6IExpc3Rib3hEaXJlY3RpdmUpIHtcbiAgICB0aGlzLmxpc3Rib3ggPSBsaXN0Ym94O1xuICAgIHRoaXMubGlzdGVuRm9yQWN0aXZlSW5kZXgobGlzdGJveCk7XG4gICAgdGhpcy5ub3ZhTGliU2VydmljZS5hZGRBcnJvd0tleU5hdmlnYXRpb24obGlzdGJveC5saXN0SXRlbXMudG9BcnJheSgpLCB0cnVlKTtcbiAgICB0aGlzLmFkZFNob3J0Y3V0cyhsaXN0Ym94KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGXCoGxpc3RlbkZvckFjdGl2ZUluZGV4wqBtZXRob2Qgc2V0cyB0aGXCoDxjb2RlPmFyaWEtYWN0aXZlZGVzY2VuZGFudDwvY29kZT7CoGF0dHJpYnV0ZSBvZiB0aGUgbGlzdGJveCB0byB0aGUgSUQgb2YgdGhlIGFjdGl2ZSBsaXN0Ym94IGl0ZW0gYW5kIGlzIGNhbGxlZCBieSBkZWZhdWx0IHdpdGhpbiA8Y29kZT5zZXRVcExpc3Rib3g8L2NvZGU+LlxuICAgKiBAcGFyYW0gbGlzdGJveCBMaXN0Ym94IHRvIGxpc3RlbiB0byBmb3IgYWN0aXZlIGluZGV4LlxuICAgKi9cbiAgbGlzdGVuRm9yQWN0aXZlSW5kZXgobGlzdGJveDogTGlzdGJveERpcmVjdGl2ZSkge1xuICAgIGxpc3Rib3gubGlzdEl0ZW1zLmZvckVhY2goKGl0ZW0pID0+IHtcbiAgICAgIHRoaXMucmVuZGVyZXIubGlzdGVuKGl0ZW0uZWwubmF0aXZlRWxlbWVudCwgJ2ZvY3VzJywgKGV2ZW50KSA9PiB7XG4gICAgICAgIGxpc3Rib3guYXJpYUFjdGl2ZURlc2NlbmRhbnQuc2V0KGl0ZW0uaWQpO1xuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogVGhlIHNjcm9sbEl0ZW1JbnRvVmlldyBtZXRob2Qgc2Nyb2xscyBnaXZlbiBpdGVtIHRvIHRvcCBvZiBsaXN0Ym94LiA8YnI+XG4gICAqIEl04oCZcyBjYWxsZWQgYnkgZGVmYXVsdCBpbiA8Y29kZT5MaXN0Qm94RGlyZWN0aXZlPC9jb2RlPiB1bmxlc3MgPGNvZGU+Y3VzdG9tU2Nyb2xsQ29udHJvbDwvY29kZT4gaXMgcHJlc2VudC4gPGJyPlxuICAgKiBUaGlzIG1ldGhvZCBjYW4gYWxzbyBjYWxsIHRvIHNjcm9sbCB0byBhIGN1c3RvbSBpbmRleC4gQnkgZGVmYXVsdCBpdCB3aWxsIHNjcm9sbCB0byB0aGUgZmlyc3QgYWN0aXZlIGl0ZW0sIGJ1dCB5b3UgY2FuIHByb3ZpZGUgYSBzcGVjaWZpYyBpdGVtIGlmIGRlc2lyZWQuXG4gICAqIEBwYXJhbSBsaXN0Ym94IExpc3Rib3ggdG8gc2Nyb2xsLlxuICAgKiBAcGFyYW0gaW5kZXhUb1Njcm9sbFRvIE9wdGlvbmFsIHNwZWNpZmljIGluZGV4IHRvIHNjcm9sbCB0byB0b3AgKG90aHdlcndpc2UsIHNjcm9sbHMgdG8gZmlyc3QgYWN0aXZlIGl0ZW0pLlxuICAgKi9cbiAgc2Nyb2xsSXRlbUludG9WaWV3KGxpc3Rib3g6IExpc3Rib3hEaXJlY3RpdmUsIGluZGV4VG9TY3JvbGxUbz86IG51bWJlcik6IHZvaWQge1xuICAgIGNvbnN0IHRhcmdldEl0ZW06IExpc3Rib3hJdGVtQ29tcG9uZW50IHwgdW5kZWZpbmVkID1cbiAgICAgIGluZGV4VG9TY3JvbGxUbyB8fCBpbmRleFRvU2Nyb2xsVG8gPT09IDBcbiAgICAgICAgPyBsaXN0Ym94Lmxpc3RJdGVtcy50b0FycmF5KClbaW5kZXhUb1Njcm9sbFRvXVxuICAgICAgICA6IGxpc3Rib3gubXVsdGlzZWxlY3RcbiAgICAgICAgICA/IGxpc3Rib3gubGlzdEl0ZW1zXG4gICAgICAgICAgICAgIC50b0FycmF5KClcbiAgICAgICAgICAgICAgLnJldmVyc2UoKVxuICAgICAgICAgICAgICAuZmluZCgoaXRlbSkgPT4gaXRlbS5hY3RpdmUpXG4gICAgICAgICAgOiBsaXN0Ym94Lmxpc3RJdGVtcy50b0FycmF5KCkuZmluZCgoaXRlbSkgPT4gaXRlbS5hY3RpdmUpO1xuXG4gICAgaWYgKHRhcmdldEl0ZW0gJiYgdGhpcy5hcHBSZWFkeVNlcnZpY2UuaXNCcm93c2VyQW5kRG9tQXZhaWxhYmxlKCkpIHtcbiAgICAgIGlmICghbGlzdGJveC5fbGlzdGJveFNjcm9sbFN0eWxlc1NldCkge1xuICAgICAgICAvLyBnYXRoZXIgdmFyaWFibGUgaW5mb3JtYXRpb25cbiAgICAgICAgbGlzdGJveC5fbGlzdGJveEhlaWdodCA9IGxpc3Rib3guZWwubmF0aXZlRWxlbWVudC5vZmZzZXRIZWlnaHQ7XG4gICAgICAgIGxpc3Rib3guX2xpc3Rib3hJdGVtSGVpZ2h0ID0gbGlzdGJveC5saXN0SXRlbXMudG9BcnJheSgpWzBdLmVsLm5hdGl2ZUVsZW1lbnQub2Zmc2V0SGVpZ2h0O1xuICAgICAgICBsaXN0Ym94Ll9saXN0Ym94R2FwID0gcGFyc2VJbnQod2luZG93LmdldENvbXB1dGVkU3R5bGUobGlzdGJveC5lbC5uYXRpdmVFbGVtZW50KS5nYXAsIDEwKTtcbiAgICAgICAgbGlzdGJveC5fbGlzdGJveEdhcCA9IGlzTmFOKGxpc3Rib3guX2xpc3Rib3hHYXApID8gNCA6IGxpc3Rib3guX2xpc3Rib3hHYXA7XG4gICAgICB9XG5cbiAgICAgIGxldCBhbW91bnRTY3JvbGxlZEFscmVhZHkgPSBsaXN0Ym94LmVsLm5hdGl2ZUVsZW1lbnQuc2Nyb2xsVG9wO1xuICAgICAgLy8gZGV0ZXJtaW5lIGRpc3RhbmNlIGJldHdlZW4gdG9wIG9mIGxpc3Rib3ggYW5kIGFjdGl2ZSBpdGVtXG4gICAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKGxpc3Rib3guZWwubmF0aXZlRWxlbWVudCwgJ3Bvc2l0aW9uJywgJ3JlbGF0aXZlJyk7IC8vIHNldCBwb3NpdGlvbiB0byByZWxhdGl2ZSB0byBhbGxvdyBmb3IgY29ycmVjdCBvZmZzZXRUb3Agb2YgaXRlbVxuICAgICAgY29uc3QgZnVsbEl0ZW1EaXN0YW5jZUZyb21Ub3AgPSB0YXJnZXRJdGVtLmVsLm5hdGl2ZUVsZW1lbnQub2Zmc2V0VG9wICsgbGlzdGJveC5fbGlzdGJveEl0ZW1IZWlnaHQ7XG5cbiAgICAgIC8vIGlmIGl0ZW0gaXMgaW4gdGhlIHRvcCB2aWV3IG9mIHRoZSBsaXN0Ym94Li5cbiAgICAgIGlmIChmdWxsSXRlbURpc3RhbmNlRnJvbVRvcCA8IGxpc3Rib3guX2xpc3Rib3hIZWlnaHQpIHtcbiAgICAgICAgaWYgKCFhbW91bnRTY3JvbGxlZEFscmVhZHkpIHtcbiAgICAgICAgICByZXR1cm47IC8vIGRvIG5vdGhpbmcsIGl0ZW0gaXMgaW4gdmlldyAodXNlciBoYXNuJ3Qgc2Nyb2xsZWQpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gc2Nyb2xsIHRvIHRvcCBvZiBsaXN0Ym94ICh3aGVyZSBpdGVtIGlzKVxuICAgICAgICAgIGxpc3Rib3guZWwubmF0aXZlRWxlbWVudC5zY3JvbGxUbygwLCAwKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgLyoqXG4gICAgICAgICAqIERvIG5vdGhpbmcgaWYgdGhlIGl0ZW0gaXMgYmV0d2VlbiB0aGUgYW1vdW50IGFscmVhZHkgc2Nyb2xsZWQgYW5kIHRoZSBib3R0b20gb2YgdGhlIGxpc3Rib3hcbiAgICAgICAgICogaWUgaXQgaXMgaW4gdmlldyBhbmQgdGhlIHVzZXIgaGFzIHNjcm9sbGVkXG4gICAgICAgICAqL1xuICAgICAgICBhbW91bnRTY3JvbGxlZEFscmVhZHkgJiZcbiAgICAgICAgZnVsbEl0ZW1EaXN0YW5jZUZyb21Ub3AgPCBsaXN0Ym94Ll9saXN0Ym94SGVpZ2h0ICsgYW1vdW50U2Nyb2xsZWRBbHJlYWR5ICYmXG4gICAgICAgIGZ1bGxJdGVtRGlzdGFuY2VGcm9tVG9wID4gYW1vdW50U2Nyb2xsZWRBbHJlYWR5XG4gICAgICApIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gZWl0aGVyIHNjcm9sbCB0byB0b3Agb2YgbGlzdGJveCBvciBzY3JvbGwgaXRlbSB0byB0b3BcbiAgICAgICAgbGlzdGJveC5lbC5uYXRpdmVFbGVtZW50LnNjcm9sbEJ5KHtcbiAgICAgICAgICB0b3A6IGZ1bGxJdGVtRGlzdGFuY2VGcm9tVG9wIC0gbGlzdGJveC5fbGlzdGJveEdhcCAtIGFtb3VudFNjcm9sbGVkQWxyZWFkeSAtIGxpc3Rib3guX2xpc3Rib3hJdGVtSGVpZ2h0XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTZWxlY3QgYWxsIGl0ZW1zIGJldHdlZW4gY3VycmVudGx5IGZvY3VzZWQgaXRlbSBhbmQgY2xvc2VzdCBzZWxlY3RlZCBpdGVtLlxuICAgKiBAcGFyYW0gaXRlbXMgTGlzdCBvZiBpdGVtcyB0aGF0IGNvbnRhaW5zIHRoZSBpdGVtcyB5b3Ugd2FudCB0byBzZWxlY3QuXG4gICAqIEBwYXJhbSBpbmRleCBJbmRleCBvZiB0aGUgZm9jdXNlZCBpdGVtLlxuICAgKi9cbiAgc2VsZWN0RnJvbUNsb3NlclNlbGVjdGVkVG9Gb2N1c2VkKGl0ZW1zOiBMaXN0Ym94SXRlbUNvbXBvbmVudFtdLCBpbmRleDogbnVtYmVyKSB7XG4gICAgbGV0IFtsZWZ0LCByaWdodF0gPSBbaW5kZXgsIGluZGV4XTtcbiAgICB3aGlsZSAobGVmdCAhPT0gMCB8fCByaWdodCAhPT0gaXRlbXMubGVuZ3RoIC0gMSkge1xuICAgICAgaWYgKGl0ZW1zW2xlZnRdWydhY3RpdmUnXSB8fCBpdGVtc1tyaWdodF1bJ2FjdGl2ZSddKSBicmVhaztcbiAgICAgIGlmIChsZWZ0ICE9PSAwKSBsZWZ0LS07XG4gICAgICBpZiAocmlnaHQgIT09IGl0ZW1zLmxlbmd0aCAtIDEpIHJpZ2h0Kys7XG4gICAgfVxuICAgIHRoaXMubm92YUxpYlNlcnZpY2Uuc2VsZWN0SXRlbXMoaXRlbXMsIGl0ZW1zW2xlZnRdWydhY3RpdmUnXSA/IGxlZnQgOiBpbmRleCwgaXRlbXNbbGVmdF1bJ2FjdGl2ZSddID8gaW5kZXggOiByaWdodCk7XG4gIH1cblxuICAvKiA9PT09PT09IEtFWUJPQVJEIFNIT1JUQ1VUUyA9PT09PT09ICovXG5cbiAgLyoqXG4gICAqIFRoZcKgYWRkU2hvcnRjdXRzwqBtZXRob2QgYWRkcyBrZXlib2FyZCBzaG9ydGN1dHMgdG8gdGhlIGxpc3Rib3ggYW5kIGlzIGNhbGxlZCBieSBkZWZhdWx0IHdpdGhpbiA8Y29kZT5zZXRVcExpc3Rib3g8L2NvZGU+LlxuICAgKiBAcGFyYW0gbGlzdGJveCBMaXN0Ym94IHRvIGFkZCBzaG9ydGN1dHMgdG8uXG4gICAqL1xuICBhZGRTaG9ydGN1dHMobGlzdGJveDogTGlzdGJveERpcmVjdGl2ZSkge1xuICAgIHRoaXMucmVuZGVyZXIubGlzdGVuKGxpc3Rib3guZWwubmF0aXZlRWxlbWVudCwgJ2tleWRvd24nLCAoZXZlbnQpID0+IHtcbiAgICAgIHRoaXMuaGFuZGxlS2V5RG93bihldmVudCwgbGlzdGJveCk7XG4gICAgfSk7XG4gICAgdGhpcy5yZW5kZXJlci5saXN0ZW4obGlzdGJveC5lbC5uYXRpdmVFbGVtZW50LCAna2V5dXAnLCAoZXZlbnQpID0+IHtcbiAgICAgIHRoaXMuaGFuZGxlS2V5VXAoZXZlbnQsIGxpc3Rib3gpO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZcKgaGFuZGxlS2V5dXDCoG1ldGhvZCBoYW5kbGVzIGl0ZW0gc2VsZWN0aW9uIG9uIGtleXVwIGV2ZW50cyBmb3IgdGhlIGxpc3Rib3ggYW5kIGlzIGNhbGxlZCBieSBkZWZhdWx0wqB3aXRoaW4gPGNvZGU+YWRkU2hvcnRjdXRzPC9jb2RlPi5cbiAgICogQHBhcmFtIGV2ZW50IEtleXVwIGV2ZW50XG4gICAqIEBwYXJhbSBsaXN0Ym94IExpc3Rib3ggdG8gaGFuZGxlIGtleXVwIGV2ZW50IGZvci5cbiAgICovXG4gIGhhbmRsZUtleVVwKGV2ZW50OiBLZXlib2FyZEV2ZW50LCBsaXN0Ym94OiBMaXN0Ym94RGlyZWN0aXZlKSB7XG4gICAgaWYgKCFsaXN0Ym94Ll9pc1JvbGVMaXN0Ym94VmFyaWFudCkgcmV0dXJuO1xuICAgIGlmIChldmVudC5rZXkgPT09IEVOVEVSX0tFWSB8fCBldmVudC5rZXkgPT09IFNQQUNFX0tFWSB8fCBldmVudC5rZXkgPT09IEhPTUVfS0VZIHx8IGV2ZW50LmtleSA9PT0gRU5EX0tFWSkge1xuICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTsgLy8gcHJldmVudCBzY3JvbGxpbmdcbiAgICAgIGlmIChldmVudC5rZXkgPT09IFNQQUNFX0tFWSkge1xuICAgICAgICBsaXN0Ym94Ll9yZWNlbnRTZWxlY3RlZEluZGV4ID0gbGlzdGJveC5faGlnaGxpZ2h0SW5kZXg7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRoZcKgaGFuZGxlS2V5ZG93biBoYW5kbGVzIGtleWRvd24gZXZlbnRzIGZvciB0aGUgbGlzdGJveCBhbmQgaXMgY2FsbGVkIGJ5IGRlZmF1bHQgd2l0aGluIDxjb2RlPmFkZFNob3J0Y3V0czwvY29kZT4uXG4gICAqIEBwYXJhbSBldmVudCBLZXlkb3duIGV2ZW50XG4gICAqIEBwYXJhbSBsaXN0Ym94IExpc3Rib3ggdG8gaGFuZGxlIGtleXVwIGV2ZW50IGZvci5cbiAgICovXG4gIGhhbmRsZUtleURvd24oZXZlbnQ6IEtleWJvYXJkRXZlbnQsIGxpc3Rib3g6IExpc3Rib3hEaXJlY3RpdmUpIHtcbiAgICBpZiAoIWxpc3Rib3guX2lzUm9sZUxpc3Rib3hWYXJpYW50KSByZXR1cm47XG4gICAgbGlzdGJveC5faXNIb3RrZXlFdmVudCA9IHRydWU7XG4gICAgaWYgKGV2ZW50LmtleSAhPT0gVEFCX0tFWSkge1xuICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBNZXRhICsgU2hpZnQgKyBIb21lIG9yIEVuZFxuICAgICAqIFNlbGVjdCB0aGUgZm9jdXNlZCBvcHRpb24gYW5kIGFsbCBvcHRpb25zIHVwL2Rvd24gdG8gdGhlIGZpcnN0IG9wdGlvblxuICAgICAqICZcbiAgICAgKiBIb21lIG9yIEVuZFxuICAgICAqIE1vdmUgdGhlIGZvY3VzIHRvIHRoZSBmaXJzdC9sYXN0IGxpc3Rib3ggb3B0aW9uXG4gICAgICovXG4gICAgaWYgKGV2ZW50LmtleSA9PT0gSE9NRV9LRVkgfHwgZXZlbnQua2V5ID09PSBFTkRfS0VZKSB0aGlzLnVwZGF0ZUZvY3VzSXRlbShldmVudCwgbGlzdGJveCk7XG5cbiAgICAvKipcbiAgICAgKiBBLVogT1IgYS16XG4gICAgICogVHlwaW5nIGEgY2hhcmFjdGVyIHdpbGwgbW92ZSB0aGUgZm9jdXMgdG8gdGhlIG5leHQgaXRlbSB3aXRoIGEgbmFtZSB0aGF0IHN0YXJ0cyB3aXRoIHRoYXQgY2hhcmFjdGVyXG4gICAgICogVHlwaW5nIGluIHJhcGlkIHN1Y2Nlc3Npb24gbW92ZXMgdGhlIGZvY3VzIHRvIHRoZSBuZXh0IGl0ZW0gd2l0aCBhIG5hbWUgdGhhdCByZWZsZWN0ZXMgdGhlIHNldCBvZiBjaGFyYWN0ZXJzIGp1c3QgdHlwZWRcbiAgICAgKi9cbiAgICBpZiAoXG4gICAgICAhZXZlbnQubWV0YUtleSAmJlxuICAgICAgZXZlbnQua2V5Lmxlbmd0aCA9PT0gMSAmJlxuICAgICAgKChldmVudC5rZXkgPj0gJ2EnICYmIGV2ZW50LmtleSA8PSAneicpIHx8XG4gICAgICAgIChldmVudC5rZXkgPj0gJ0EnICYmIGV2ZW50LmtleSA8PSAnWicpIHx8XG4gICAgICAgIChldmVudC5rZXkgPj0gJzAnICYmIGV2ZW50LmtleSA8PSAnOScpKVxuICAgIClcbiAgICAgIHRoaXMuc2VhcmNoS2V5d29yZChldmVudCwgbGlzdGJveCk7XG5cbiAgICAvKipcbiAgICAgKiBTaGlmdCArIOKGkS/ihpNcbiAgICAgKiBNb3ZlIHRoZSBmb2N1cyB0byBhbmQgdG9nZ2xlIHRoZSBzZWxlY3RlZCBzdGF0ZSBvZiB0aGUgbmV4dC9wcmV2aW91cyBvcHRpb25cbiAgICAgKi9cbiAgICBpZiAoZXZlbnQua2V5ID09PSBVUF9BUlJPV19LRVkgfHwgZXZlbnQua2V5ID09PSBET1dOX0FSUk9XX0tFWSkgdGhpcy50b2dnbGVTZWxlY3RlZFN0YXRlKGV2ZW50LCBsaXN0Ym94KTtcblxuICAgIC8qKlxuICAgICAqIE1ldGEgKyBBXG4gICAgICogU2VsZWN0IGFsbCBvciBkZXNlbGVjdCBhbGwgaXRlbXNcbiAgICAgKi9cbiAgICBpZiAoZXZlbnQubWV0YUtleSAmJiAoZXZlbnQua2V5ID09PSAnQScgfHwgZXZlbnQua2V5ID09PSAnYScpICYmIGxpc3Rib3gubXVsdGlzZWxlY3QpIHRoaXMuc2VsZWN0QWxsKGxpc3Rib3gpO1xuXG4gICAgLyoqXG4gICAgICogU2hpZnQgKyBTcGFjZVxuICAgICAqIFNlbGVjdCBjb250aWd1b3VzIGl0ZW1zIGZyb20gdGhlIG1vc3QgcmVjZW50bHkgc2VsZWN0ZWQgaXRlbSB0byB0aHMgZm9jdXNlZCBpdGVtXG4gICAgICovXG4gICAgaWYgKGV2ZW50LmNvZGUgPT09IFNQQUNFX0NPREUgJiYgZXZlbnQuc2hpZnRLZXkgPT09IHRydWUpIHRoaXMuc2VsZWN0Q29udGlndW91c0l0ZW1zKGxpc3Rib3gpO1xuICAgIGxpc3Rib3guX2lzSG90a2V5RXZlbnQgPSBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGXCoHVwZGF0ZUZvY3VzSXRlbcKgbWV0aG9kICBpcyBhY3RpdmF0ZWQgYnkgTWV0YSArIFNoaWZ0ICsgSG9tZS9FbmQgYW5kIHNlbGVjdHMgYWxsIGl0ZW1zIGJldHdlZW4gdGhlIGZvY3VzZWQgaXRlbSBhbmQgdGhlIGZpcnN0IG9yIGxhc3QgaXRlbSwgdGhlbiBzZXRzIGZvY3VzIHRvIHRoZSBmaXJzdCBvciBsYXN0IGl0ZW0uIDxicj5cbiAgICogVGhpcyBtZXRob2QgaXMgY2FsbGVkIGJ5IGRlZmF1bHQgd2l0aGluIDxjb2RlPmhhbmRsZUtleURvd248L2NvZGU+LlxuICAgKiBAcGFyYW0gZXZlbnQgS2V5Ym9hcmQgZXZlbnRcbiAgICogQHBhcmFtIGxpc3Rib3ggTGlzdGJveCB0byB1cGRhdGUgZm9jdXMgaXRlbSBmb3IuXG4gICAqL1xuICB1cGRhdGVGb2N1c0l0ZW0oZXZlbnQ6IEtleWJvYXJkRXZlbnQsIGxpc3Rib3g6IExpc3Rib3hEaXJlY3RpdmUpIHtcbiAgICBjb25zdCBsaXN0SXRlbXNBcnJheSA9IGxpc3Rib3gubGlzdEl0ZW1zLnRvQXJyYXkoKTtcbiAgICBpZiAoZXZlbnQubWV0YUtleSAmJiBldmVudC5zaGlmdEtleSAmJiBsaXN0Ym94Lm11bHRpc2VsZWN0KSB7XG4gICAgICBsaXN0SXRlbXNBcnJheS5mb3JFYWNoKChpdGVtLCBpKSA9PiB7XG4gICAgICAgIGlmIChsaXN0Ym94Ll9oaWdobGlnaHRJbmRleCAhPT0gbnVsbCAmJiBpdGVtLmRpc2FibGVkICE9PSB0cnVlKSB7XG4gICAgICAgICAgaWYgKGV2ZW50LmtleSA9PT0gSE9NRV9LRVkpIGxpc3Rib3guX2hpZ2hsaWdodEluZGV4ID49IGkgPyAoaXRlbS5hY3RpdmUgPSB0cnVlKSA6IChpdGVtLmFjdGl2ZSA9IGZhbHNlKTtcbiAgICAgICAgICBpZiAoZXZlbnQua2V5ID09PSBFTkRfS0VZKSBsaXN0Ym94Ll9oaWdobGlnaHRJbmRleCA8PSBpID8gKGl0ZW0uYWN0aXZlID0gdHJ1ZSkgOiAoaXRlbS5hY3RpdmUgPSBmYWxzZSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cbiAgICBsaXN0Ym94Ll9oaWdobGlnaHRJbmRleCA9XG4gICAgICBldmVudC5rZXkgPT09IEhPTUVfS0VZXG4gICAgICAgID8gdGhpcy5ub3ZhTGliU2VydmljZS5maXJzdEVuYWJsZWRJdGVtKGxpc3RJdGVtc0FycmF5KVxuICAgICAgICA6IHRoaXMubm92YUxpYlNlcnZpY2UubGFzdEVuYWJsZWRJdGVtKGxpc3RJdGVtc0FycmF5KTtcbiAgICBsaXN0Ym94LnVwZGF0ZVZhbHVlRnJvbUl0ZW1zKCk7XG4gICAgbGlzdEl0ZW1zQXJyYXlbbGlzdGJveC5faGlnaGxpZ2h0SW5kZXhdLmVsLm5hdGl2ZUVsZW1lbnQuZm9jdXMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGXCoHNlYXJjaEtleXdvcmTCoG1ldGhvZCAgaGFuZGxlcyBrZXlzIEEtWiwgYS16LCBhbmQgMC05LCBtb3ZpbmcgdGhlIGZvY3VzIHRvIHRoZSBuZXh0IGl0ZW0gd2l0aCBhIG5hbWUgc3RhcnRpbmcgd2l0aCB0aGUgdHlwZWQgY2hhcmFjdGVyLiA8YnI+XG4gICAqIEluIHRoZSBjYXNlIG9mIHJhcGlkIHN1Y2Nlc3Npb24sIGl0IG1vdmVzIHRoZSBmb2N1cyB0byB0aGUgbmV4dCBpdGVtIHdob3NlIG5hbWUgbWF0Y2hlcyB0aGUgc2VxdWVuY2Ugb2YgY2hhcmFjdGVycyB0eXBlZC4gPGJyIC8+XG4gICAqIFRoaXMgbWV0aG9kIGlzIGNhbGxlZCBieSBkZWZhdWx0IHdpdGhpbiA8Y29kZT5oYW5kbGVLZXlEb3duPC9jb2RlPi5cbiAgICogQHBhcmFtIGV2ZW50IEtleWJvYXJkIGV2ZW50XG4gICAqIEBwYXJhbSBsaXN0Ym94IExpc3Rib3ggdG8gc2VhcmNoIGZvciBpdGVtcyBpbi5cbiAgICovXG4gIHNlYXJjaEtleXdvcmQoZXZlbnQ6IEtleWJvYXJkRXZlbnQsIGxpc3Rib3g6IExpc3Rib3hEaXJlY3RpdmUpIHtcbiAgICBjb25zdCBsaXN0SXRlbXNBcnJheSA9IGxpc3Rib3gubGlzdEl0ZW1zLnRvQXJyYXkoKTtcbiAgICBsaXN0Ym94Ll9rZXl3b3JkID0gbGlzdGJveC5fa2V5d29yZCArIGV2ZW50LmtleS50b0xvd2VyQ2FzZSgpO1xuICAgIGNsZWFyVGltZW91dChsaXN0Ym94Ll90aW1lb3V0SWQpO1xuICAgIGNvbnN0IGJvdW5jZSA9IHdpbmRvdy5zZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIGlmIChsaXN0Ym94Ll9rZXl3b3JkICE9PSAnJykge1xuICAgICAgICBsaXN0Ym94Ll9rZXl3b3JkID0gJyc7XG4gICAgICB9XG4gICAgfSwgMTAwMCk7XG4gICAgbGlzdGJveC5fdGltZW91dElkID0gYm91bmNlO1xuICAgIGNvbnN0IHNlbGVjdGVkSW5kZXggPSBsaXN0SXRlbXNBcnJheS5maW5kSW5kZXgoKGl0ZW0pID0+XG4gICAgICBpdGVtLmVsLm5hdGl2ZUVsZW1lbnQuaW5uZXJUZXh0Py50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKGxpc3Rib3guX2tleXdvcmQpXG4gICAgKTtcblxuICAgIGlmIChzZWxlY3RlZEluZGV4ID49IDAgJiYgIWxpc3RJdGVtc0FycmF5W3NlbGVjdGVkSW5kZXhdLmRpc2FibGVkKSB7XG4gICAgICBsaXN0SXRlbXNBcnJheVtzZWxlY3RlZEluZGV4XS5lbC5uYXRpdmVFbGVtZW50LmZvY3VzKCk7XG4gICAgICBsaXN0Ym94Ll9oaWdobGlnaHRJbmRleCA9IHNlbGVjdGVkSW5kZXg7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRoZcKgc2VsZWN0QWxswqBtZXRob2QgaXMgYWN0aXZhdGVkIGJ5IHRoZSBNZXRhICsgQSBrZXlzIGFuZCBzZWxlY3RzIG9yIGRlc2VsZWN0cyBhbGwgaXRlbXMgaW4gdGhlIGxpc3Rib3guIDxicj5cbiAgICogVGhpcyBtZXRob2QgaXMgY2FsbGVkIGJ5IGRlZmF1bHQgd2l0aGluIDxjb2RlPmhhbmRsZUtleURvd248L2NvZGU+LlxuICAgKiBAcGFyYW0gbGlzdGJveCBMaXN0Ym94IHRvIHNlbGVjdCBhbGwgaXRlbXMgaW4uXG4gICAqL1xuICBzZWxlY3RBbGwobGlzdGJveDogTGlzdGJveERpcmVjdGl2ZSkge1xuICAgIGlmIChsaXN0Ym94Lm11bHRpc2VsZWN0KSB7XG4gICAgICBjb25zdCBsaXN0SXRlbXNBcnJheSA9IGxpc3Rib3gubGlzdEl0ZW1zLnRvQXJyYXkoKTtcbiAgICAgIHRoaXMubm92YUxpYlNlcnZpY2UuZGV0ZWN0QWxsSXRlbXNTZWxlY3RlZChsaXN0SXRlbXNBcnJheSlcbiAgICAgICAgPyB0aGlzLm5vdmFMaWJTZXJ2aWNlLmRlc2VsZWN0SXRlbXMobGlzdEl0ZW1zQXJyYXkpXG4gICAgICAgIDogdGhpcy5ub3ZhTGliU2VydmljZS5zZWxlY3RJdGVtcyhsaXN0SXRlbXNBcnJheSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRoZcKgc2VsZWN0RnJvbUNsb3NlclNlbGVjdGVkVG9Gb2N1c2VkwqBtZXRob2Qgc2VsZWN0cyBhbGwgaXRlbXMgYmV0d2VlbiB0aGUgY3VycmVudGx5IGZvY3VzZWQgaXRlbSBhbmQgdGhlIGNsb3Nlc3Qgc2VsZWN0ZWQgaXRlbS4gPGJyPlxuICAgKiBUaGlzIG1ldGhvZCBpcyBjYWxsZWQgYnkgZGVmYXVsdCB3aXRoaW4gPGNvZGU+aGFuZGxlS2V5RG93bjwvY29kZT4uXG4gICAqIEBwYXJhbSBpbmRleCBJbmRleCBvZiB0aGUgZm9jdXNlZCBpdGVtLlxuICAgKi9cbiAgc2VsZWN0Q29udGlndW91c0l0ZW1zKGxpc3Rib3g6IExpc3Rib3hEaXJlY3RpdmUpIHtcbiAgICBpZiAobGlzdGJveC5faGlnaGxpZ2h0SW5kZXggIT09IG51bGwgJiYgbGlzdGJveC5fcmVjZW50U2VsZWN0ZWRJbmRleCAhPT0gbnVsbCkge1xuICAgICAgY29uc3QgbGlzdEl0ZW1zQXJyYXkgPSBsaXN0Ym94Lmxpc3RJdGVtcy50b0FycmF5KCk7XG4gICAgICBjb25zdCBpc1JlY2VudExhcmdlciA9IGxpc3Rib3guX3JlY2VudFNlbGVjdGVkSW5kZXggPiBsaXN0Ym94Ll9oaWdobGlnaHRJbmRleDtcbiAgICAgIHRoaXMubm92YUxpYlNlcnZpY2Uuc2VsZWN0SXRlbXMoXG4gICAgICAgIGxpc3RJdGVtc0FycmF5LFxuICAgICAgICBpc1JlY2VudExhcmdlciA/IGxpc3Rib3guX2hpZ2hsaWdodEluZGV4IDogbGlzdGJveC5fcmVjZW50U2VsZWN0ZWRJbmRleCxcbiAgICAgICAgaXNSZWNlbnRMYXJnZXIgPyBsaXN0Ym94Ll9yZWNlbnRTZWxlY3RlZEluZGV4IDogbGlzdGJveC5faGlnaGxpZ2h0SW5kZXhcbiAgICAgICk7XG4gICAgICBsaXN0Ym94LnVwZGF0ZVZhbHVlRnJvbUl0ZW1zKCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRoZcKgdG9nZ2xlU2VsZWN0ZWRTdGF0ZcKgbWV0aG9kIGlzIGFjdGl2YXRlZCBieSBTaGlmdCArIOKGkS/ihpMgYW5kIG1vdmVzIHRoZSBmb2N1cyB0bywgYW5kIHRvZ2dsZXMgdGhlIHNlbGVjdGVkIHN0YXRlIG9mLCB0aGUgbmV4dCBvciBwcmV2aW91cyBvcHRpb24uIDxicj5cbiAgICogVGhpcyBtZXRob2QgaXMgY2FsbGVkIGJ5IGRlZmF1bHQgd2l0aGluIDxjb2RlPmhhbmRsZUtleURvd248L2NvZGU+LlxuICAgKiBAcGFyYW0gZXZlbnQgS2V5Ym9hcmQgZXZlbnRcbiAgICogQHBhcmFtIGxpc3Rib3ggTGlzdGJveCB0byB0b2dnbGUgc2VsZWN0ZWQgc3RhdGUgZm9yLlxuICAgKi9cbiAgdG9nZ2xlU2VsZWN0ZWRTdGF0ZShldmVudDogS2V5Ym9hcmRFdmVudCwgbGlzdGJveDogTGlzdGJveERpcmVjdGl2ZSkge1xuICAgIGNvbnN0IGxpc3RJdGVtc0FycmF5ID0gbGlzdGJveC5saXN0SXRlbXMudG9BcnJheSgpO1xuICAgIGlmIChldmVudC5zaGlmdEtleSA9PT0gdHJ1ZSAmJiBsaXN0Ym94Lm11bHRpc2VsZWN0ICYmIGxpc3Rib3guX2hpZ2hsaWdodEluZGV4ICE9PSBudWxsKSB7XG4gICAgICBpZiAobGlzdEl0ZW1zQXJyYXlbbGlzdGJveC5faGlnaGxpZ2h0SW5kZXhdLmFjdGl2ZSkge1xuICAgICAgICB0aGlzLm5vdmFMaWJTZXJ2aWNlLmRlc2VsZWN0SXRlbShsaXN0SXRlbXNBcnJheSwgbGlzdGJveC5faGlnaGxpZ2h0SW5kZXgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5ub3ZhTGliU2VydmljZS5zZWxlY3RJdGVtKGxpc3RJdGVtc0FycmF5LCBsaXN0Ym94Ll9oaWdobGlnaHRJbmRleCk7XG4gICAgICAgIGxpc3Rib3guX3JlY2VudFNlbGVjdGVkSW5kZXggPSBsaXN0Ym94Ll9oaWdobGlnaHRJbmRleDtcbiAgICAgIH1cbiAgICAgIGxpc3Rib3gudXBkYXRlVmFsdWVGcm9tSXRlbXMoKTtcbiAgICB9XG4gICAgbGlzdGJveC5faGlnaGxpZ2h0SW5kZXggPVxuICAgICAgZXZlbnQua2V5ID09PSBVUF9BUlJPV19LRVlcbiAgICAgICAgPyB0aGlzLm5vdmFMaWJTZXJ2aWNlLnByZXZpb3VzRW5hYmxlZEl0ZW0obGlzdEl0ZW1zQXJyYXksIGxpc3Rib3guX2hpZ2hsaWdodEluZGV4ISlcbiAgICAgICAgOiB0aGlzLm5vdmFMaWJTZXJ2aWNlLm5leHRFbmFibGVkSXRlbShsaXN0SXRlbXNBcnJheSwgbGlzdGJveC5faGlnaGxpZ2h0SW5kZXghKTtcbiAgfVxufVxuIl19