@visa/nova-angular
Version:
Visa Product Design System Nova Angular library
315 lines • 47.7 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 { 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlzdGJveC5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy9ub3ZhLWxpYi9zcmMvbGliL2xpc3Rib3gvbGlzdGJveC5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7SUFlSTtBQUNKLE9BQU8sRUFBRSxVQUFVLEVBQWEsZ0JBQWdCLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDeEUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGlEQUFpRCxDQUFDO0FBRWxGLE9BQU8sRUFDTCxjQUFjLEVBQ2QsT0FBTyxFQUNQLFNBQVMsRUFDVCxRQUFRLEVBQ1IsVUFBVSxFQUNWLFNBQVMsRUFDVCxPQUFPLEVBQ1AsWUFBWSxFQUNiLE1BQU0sdUJBQXVCLENBQUM7QUFDL0IsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHFCQUFxQixDQUFDOzs7O0FBR3JEOztHQUVHO0FBSUgsTUFBTSxPQUFPLGNBQWM7SUFDekIsWUFDVSxlQUFpQyxFQUNqQyxjQUE4QixFQUM5QixlQUFnQztRQUZoQyxvQkFBZSxHQUFmLGVBQWUsQ0FBa0I7UUFDakMsbUJBQWMsR0FBZCxjQUFjLENBQWdCO1FBQzlCLG9CQUFlLEdBQWYsZUFBZSxDQUFpQjtRQUV4QyxJQUFJLENBQUMsUUFBUSxHQUFHLGVBQWUsQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzdELENBQUM7SUFNRDs7OztPQUlHO0lBQ0gsWUFBWSxDQUFDLE9BQXlCO1FBQ3BDLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNuQyxJQUFJLENBQUMsY0FBYyxDQUFDLHFCQUFxQixDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDN0UsSUFBSSxDQUFDLFlBQVks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RVAsY0FBYztrQkFIMUIsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkIiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqICAgICAgICAgICAgICBDb3B5cmlnaHQgKGMpIDIwMjUgVmlzYSwgSW5jLlxuICpcbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKlxuICoqL1xuaW1wb3J0IHsgSW5qZWN0YWJsZSwgUmVuZGVyZXIyLCBSZW5kZXJlckZhY3RvcnkyIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBBcHBSZWFkeVNlcnZpY2UgfSBmcm9tICcuLi9fdXRpbGl0aWVzL3NlcnZpY2VzL2FwcC1zdGFibGUtY2hlY2suc2VydmljZSc7XG5pbXBvcnQgeyBMaXN0Ym94SXRlbUNvbXBvbmVudCB9IGZyb20gJy4uL2xpc3Rib3gtaXRlbS9saXN0Ym94LWl0ZW0uY29tcG9uZW50JztcbmltcG9ydCB7XG4gIERPV05fQVJST1dfS0VZLFxuICBFTkRfS0VZLFxuICBFTlRFUl9LRVksXG4gIEhPTUVfS0VZLFxuICBTUEFDRV9DT0RFLFxuICBTUEFDRV9LRVksXG4gIFRBQl9LRVksXG4gIFVQX0FSUk9XX0tFWVxufSBmcm9tICcuLi9ub3ZhLWxpYi5jb25zdGFudHMnO1xuaW1wb3J0IHsgTm92YUxpYlNlcnZpY2UgfSBmcm9tICcuLi9ub3ZhLWxpYi5zZXJ2aWNlJztcbmltcG9ydCB7IExpc3Rib3hEaXJlY3RpdmUgfSBmcm9tICcuL2xpc3Rib3guZGlyZWN0aXZlJztcblxuLyoqXG4gKiBTZXJ2aWNlIHVzZWQgdG8gY3JlYXRlIGxpc3Rib3ggYmVoYXZpb3IuIFNvbWUgZnVuY3Rpb25zIGFyZSB1c2VkIHdpdGhpbiBsaXN0Ym94IGNvbXBvbmVudCwgb3RoZXJzIGFyZSBvcHRpb25hbCBhZGRpdGlvbnMuXG4gKi9cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIExpc3Rib3hTZXJ2aWNlIHtcbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZW5kZXJlckZhY3Rvcnk6IFJlbmRlcmVyRmFjdG9yeTIsXG4gICAgcHJpdmF0ZSBub3ZhTGliU2VydmljZTogTm92YUxpYlNlcnZpY2UsXG4gICAgcHJpdmF0ZSBhcHBSZWFkeVNlcnZpY2U6IEFwcFJlYWR5U2VydmljZVxuICApIHtcbiAgICB0aGlzLnJlbmRlcmVyID0gcmVuZGVyZXJGYWN0b3J5LmNyZWF0ZVJlbmRlcmVyKG51bGwsIG51bGwpO1xuICB9XG4gIC8qKiBAaWdub3JlICovXG4gIHByaXZhdGUgcmVuZGVyZXI6IFJlbmRlcmVyMjtcbiAgLyoqIEBpZ25vcmUgKi9cbiAgbGlzdGJveDogTGlzdGJveERpcmVjdGl2ZTtcblxuICAvKipcbiAgICogVGhlwqBzZXRVcExpc3Rib3jCoG1ldGhvZCBjb25maWd1cmVzIHRoZSBsaXN0Ym94IHdpdGggdGhlIGV4cGVjdGVkIGtleWJvYXJkIGJlaGF2aW9ycyBhbmQgc2V0cyB0aGXCoGFyaWEtYWN0aXZlZGVzY2VuZGFudMKgYXR0cmlidXRlLiA8YnI+XG4gICAqIFRoaXMgbWV0aG9kIGlzIGNhbGxlZCBieSBkZWZhdWx0IHdpdGhpbiA8Y29kZT5MaXN0Ym94RGlyZWN0aXZlPC9jb2RlPi5cbiAgICogQHBhcmFtIGxpc3Rib3ggTGlzdGJveCB0byBzZXQgdXAuXG4gICAqL1xuICBzZXRVcExpc3Rib3gobGlzdGJveDogTGlzdGJveERpcmVjdGl2ZSkge1xuICAgIHRoaXMubGlzdGJveCA9IGxpc3Rib3g7XG4gICAgdGhpcy5saXN0ZW5Gb3JBY3RpdmVJbmRleChsaXN0Ym94KTtcbiAgICB0aGlzLm5vdmFMaWJTZXJ2aWNlLmFkZEFycm93S2V5TmF2aWdhdGlvbihsaXN0Ym94Lmxpc3RJdGVtcy50b0FycmF5KCksIHRydWUpO1xuICAgIHRoaXMuYWRkU2hvcnRjdXRzKGxpc3Rib3gpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZcKgbGlzdGVuRm9yQWN0aXZlSW5kZXjCoG1ldGhvZCBzZXRzIHRoZcKgPGNvZGU+YXJpYS1hY3RpdmVkZXNjZW5kYW50PC9jb2RlPsKgYXR0cmlidXRlIG9mIHRoZSBsaXN0Ym94IHRvIHRoZSBJRCBvZiB0aGUgYWN0aXZlIGxpc3Rib3ggaXRlbSBhbmQgaXMgY2FsbGVkIGJ5IGRlZmF1bHQgd2l0aGluIDxjb2RlPnNldFVwTGlzdGJveDwvY29kZT4uXG4gICAqIEBwYXJhbSBsaXN0Ym94IExpc3Rib3ggdG8gbGlzdGVuIHRvIGZvciBhY3RpdmUgaW5kZXguXG4gICAqL1xuICBsaXN0ZW5Gb3JBY3RpdmVJbmRleChsaXN0Ym94OiBMaXN0Ym94RGlyZWN0aXZlKSB7XG4gICAgbGlzdGJveC5saXN0SXRlbXMuZm9yRWFjaCgoaXRlbSkgPT4ge1xuICAgICAgdGhpcy5yZW5kZXJlci5saXN0ZW4oaXRlbS5lbC5uYXRpdmVFbGVtZW50LCAnZm9jdXMnLCAoZXZlbnQpID0+IHtcbiAgICAgICAgbGlzdGJveC5hcmlhQWN0aXZlRGVzY2VuZGFudC5zZXQoaXRlbS5pZCk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgc2Nyb2xsSXRlbUludG9WaWV3IG1ldGhvZCBzY3JvbGxzIGdpdmVuIGl0ZW0gdG8gdG9wIG9mIGxpc3Rib3guIDxicj5cbiAgICogSXTigJlzIGNhbGxlZCBieSBkZWZhdWx0IGluIDxjb2RlPkxpc3RCb3hEaXJlY3RpdmU8L2NvZGU+IHVubGVzcyA8Y29kZT5jdXN0b21TY3JvbGxDb250cm9sPC9jb2RlPiBpcyBwcmVzZW50LiA8YnI+XG4gICAqIFRoaXMgbWV0aG9kIGNhbiBhbHNvIGNhbGwgdG8gc2Nyb2xsIHRvIGEgY3VzdG9tIGluZGV4LiBCeSBkZWZhdWx0IGl0IHdpbGwgc2Nyb2xsIHRvIHRoZSBmaXJzdCBhY3RpdmUgaXRlbSwgYnV0IHlvdSBjYW4gcHJvdmlkZSBhIHNwZWNpZmljIGl0ZW0gaWYgZGVzaXJlZC5cbiAgICogQHBhcmFtIGxpc3Rib3ggTGlzdGJveCB0byBzY3JvbGwuXG4gICAqIEBwYXJhbSBpbmRleFRvU2Nyb2xsVG8gT3B0aW9uYWwgc3BlY2lmaWMgaW5kZXggdG8gc2Nyb2xsIHRvIHRvcCAob3Rod2Vyd2lzZSwgc2Nyb2xscyB0byBmaXJzdCBhY3RpdmUgaXRlbSkuXG4gICAqL1xuICBzY3JvbGxJdGVtSW50b1ZpZXcobGlzdGJveDogTGlzdGJveERpcmVjdGl2ZSwgaW5kZXhUb1Njcm9sbFRvPzogbnVtYmVyKTogdm9pZCB7XG4gICAgY29uc3QgdGFyZ2V0SXRlbTogTGlzdGJveEl0ZW1Db21wb25lbnQgfCB1bmRlZmluZWQgPVxuICAgICAgaW5kZXhUb1Njcm9sbFRvIHx8IGluZGV4VG9TY3JvbGxUbyA9PT0gMFxuICAgICAgICA/IGxpc3Rib3gubGlzdEl0ZW1zLnRvQXJyYXkoKVtpbmRleFRvU2Nyb2xsVG9dXG4gICAgICAgIDogbGlzdGJveC5tdWx0aXNlbGVjdFxuICAgICAgICAgID8gbGlzdGJveC5saXN0SXRlbXNcbiAgICAgICAgICAgICAgLnRvQXJyYXkoKVxuICAgICAgICAgICAgICAucmV2ZXJzZSgpXG4gICAgICAgICAgICAgIC5maW5kKChpdGVtKSA9PiBpdGVtLmFjdGl2ZSlcbiAgICAgICAgICA6IGxpc3Rib3gubGlzdEl0ZW1zLnRvQXJyYXkoKS5maW5kKChpdGVtKSA9PiBpdGVtLmFjdGl2ZSk7XG5cbiAgICBpZiAodGFyZ2V0SXRlbSAmJiB0aGlzLmFwcFJlYWR5U2VydmljZS5pc0Jyb3dzZXJBbmREb21BdmFpbGFibGUoKSkge1xuICAgICAgaWYgKCFsaXN0Ym94Ll9saXN0Ym94U2Nyb2xsU3R5bGVzU2V0KSB7XG4gICAgICAgIC8vIGdhdGhlciB2YXJpYWJsZSBpbmZvcm1hdGlvblxuICAgICAgICBsaXN0Ym94Ll9saXN0Ym94SGVpZ2h0ID0gbGlzdGJveC5lbC5uYXRpdmVFbGVtZW50Lm9mZnNldEhlaWdodDtcbiAgICAgICAgbGlzdGJveC5fbGlzdGJveEl0ZW1IZWlnaHQgPSBsaXN0Ym94Lmxpc3RJdGVtcy50b0FycmF5KClbMF0uZWwubmF0aXZlRWxlbWVudC5vZmZzZXRIZWlnaHQ7XG4gICAgICAgIGxpc3Rib3guX2xpc3Rib3hHYXAgPSBwYXJzZUludCh3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZShsaXN0Ym94LmVsLm5hdGl2ZUVsZW1lbnQpLmdhcCwgMTApO1xuICAgICAgICBsaXN0Ym94Ll9saXN0Ym94R2FwID0gaXNOYU4obGlzdGJveC5fbGlzdGJveEdhcCkgPyA0IDogbGlzdGJveC5fbGlzdGJveEdhcDtcbiAgICAgIH1cblxuICAgICAgbGV0IGFtb3VudFNjcm9sbGVkQWxyZWFkeSA9IGxpc3Rib3guZWwubmF0aXZlRWxlbWVudC5zY3JvbGxUb3A7XG4gICAgICAvLyBkZXRlcm1pbmUgZGlzdGFuY2UgYmV0d2VlbiB0b3Agb2YgbGlzdGJveCBhbmQgYWN0aXZlIGl0ZW1cbiAgICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUobGlzdGJveC5lbC5uYXRpdmVFbGVtZW50LCAncG9zaXRpb24nLCAncmVsYXRpdmUnKTsgLy8gc2V0IHBvc2l0aW9uIHRvIHJlbGF0aXZlIHRvIGFsbG93IGZvciBjb3JyZWN0IG9mZnNldFRvcCBvZiBpdGVtXG4gICAgICBjb25zdCBmdWxsSXRlbURpc3RhbmNlRnJvbVRvcCA9IHRhcmdldEl0ZW0uZWwubmF0aXZlRWxlbWVudC5vZmZzZXRUb3AgKyBsaXN0Ym94Ll9saXN0Ym94SXRlbUhlaWdodDtcblxuICAgICAgLy8gaWYgaXRlbSBpcyBpbiB0aGUgdG9wIHZpZXcgb2YgdGhlIGxpc3Rib3guLlxuICAgICAgaWYgKGZ1bGxJdGVtRGlzdGFuY2VGcm9tVG9wIDwgbGlzdGJveC5fbGlzdGJveEhlaWdodCkge1xuICAgICAgICBpZiAoIWFtb3VudFNjcm9sbGVkQWxyZWFkeSkge1xuICAgICAgICAgIHJldHVybjsgLy8gZG8gbm90aGluZywgaXRlbSBpcyBpbiB2aWV3ICh1c2VyIGhhc24ndCBzY3JvbGxlZClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBzY3JvbGwgdG8gdG9wIG9mIGxpc3Rib3ggKHdoZXJlIGl0ZW0gaXMpXG4gICAgICAgICAgbGlzdGJveC5lbC5uYXRpdmVFbGVtZW50LnNjcm9sbFRvKDAsIDApO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKFxuICAgICAgICAvKipcbiAgICAgICAgICogRG8gbm90aGluZyBpZiB0aGUgaXRlbSBpcyBiZXR3ZWVuIHRoZSBhbW91bnQgYWxyZWFkeSBzY3JvbGxlZCBhbmQgdGhlIGJvdHRvbSBvZiB0aGUgbGlzdGJveFxuICAgICAgICAgKiBpZSBpdCBpcyBpbiB2aWV3IGFuZCB0aGUgdXNlciBoYXMgc2Nyb2xsZWRcbiAgICAgICAgICovXG4gICAgICAgIGFtb3VudFNjcm9sbGVkQWxyZWFkeSAmJlxuICAgICAgICBmdWxsSXRlbURpc3RhbmNlRnJvbVRvcCA8IGxpc3Rib3guX2xpc3Rib3hIZWlnaHQgKyBhbW91bnRTY3JvbGxlZEFscmVhZHkgJiZcbiAgICAgICAgZnVsbEl0ZW1EaXN0YW5jZUZyb21Ub3AgPiBhbW91bnRTY3JvbGxlZEFscmVhZHlcbiAgICAgICkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBlaXRoZXIgc2Nyb2xsIHRvIHRvcCBvZiBsaXN0Ym94IG9yIHNjcm9sbCBpdGVtIHRvIHRvcFxuICAgICAgICBsaXN0Ym94LmVsLm5hdGl2ZUVsZW1lbnQuc2Nyb2xsQnkoe1xuICAgICAgICAgIHRvcDogZnVsbEl0ZW1EaXN0YW5jZUZyb21Ub3AgLSBsaXN0Ym94Ll9saXN0Ym94R2FwIC0gYW1vdW50U2Nyb2xsZWRBbHJlYWR5IC0gbGlzdGJveC5fbGlzdGJveEl0ZW1IZWlnaHRcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFNlbGVjdCBhbGwgaXRlbXMgYmV0d2VlbiBjdXJyZW50bHkgZm9jdXNlZCBpdGVtIGFuZCBjbG9zZXN0IHNlbGVjdGVkIGl0ZW0uXG4gICAqIEBwYXJhbSBpdGVtcyBMaXN0IG9mIGl0ZW1zIHRoYXQgY29udGFpbnMgdGhlIGl0ZW1zIHlvdSB3YW50IHRvIHNlbGVjdC5cbiAgICogQHBhcmFtIGluZGV4IEluZGV4IG9mIHRoZSBmb2N1c2VkIGl0ZW0uXG4gICAqL1xuICBzZWxlY3RGcm9tQ2xvc2VyU2VsZWN0ZWRUb0ZvY3VzZWQoaXRlbXM6IExpc3Rib3hJdGVtQ29tcG9uZW50W10sIGluZGV4OiBudW1iZXIpIHtcbiAgICBsZXQgW2xlZnQsIHJpZ2h0XSA9IFtpbmRleCwgaW5kZXhdO1xuICAgIHdoaWxlIChsZWZ0ICE9PSAwIHx8IHJpZ2h0ICE9PSBpdGVtcy5sZW5ndGggLSAxKSB7XG4gICAgICBpZiAoaXRlbXNbbGVmdF1bJ2FjdGl2ZSddIHx8IGl0ZW1zW3JpZ2h0XVsnYWN0aXZlJ10pIGJyZWFrO1xuICAgICAgaWYgKGxlZnQgIT09IDApIGxlZnQtLTtcbiAgICAgIGlmIChyaWdodCAhPT0gaXRlbXMubGVuZ3RoIC0gMSkgcmlnaHQrKztcbiAgICB9XG4gICAgdGhpcy5ub3ZhTGliU2VydmljZS5zZWxlY3RJdGVtcyhpdGVtcywgaXRlbXNbbGVmdF1bJ2FjdGl2ZSddID8gbGVmdCA6IGluZGV4LCBpdGVtc1tsZWZ0XVsnYWN0aXZlJ10gPyBpbmRleCA6IHJpZ2h0KTtcbiAgfVxuXG4gIC8qID09PT09PT0gS0VZQk9BUkQgU0hPUlRDVVRTID09PT09PT0gKi9cblxuICAvKipcbiAgICogVGhlwqBhZGRTaG9ydGN1dHPCoG1ldGhvZCBhZGRzIGtleWJvYXJkIHNob3J0Y3V0cyB0byB0aGUgbGlzdGJveCBhbmQgaXMgY2FsbGVkIGJ5IGRlZmF1bHQgd2l0aGluIDxjb2RlPnNldFVwTGlzdGJveDwvY29kZT4uXG4gICAqIEBwYXJhbSBsaXN0Ym94IExpc3Rib3ggdG8gYWRkIHNob3J0Y3V0cyB0by5cbiAgICovXG4gIGFkZFNob3J0Y3V0cyhsaXN0Ym94OiBMaXN0Ym94RGlyZWN0aXZlKSB7XG4gICAgdGhpcy5yZW5kZXJlci5saXN0ZW4obGlzdGJveC5lbC5uYXRpdmVFbGVtZW50LCAna2V5ZG93bicsIChldmVudCkgPT4ge1xuICAgICAgdGhpcy5oYW5kbGVLZXlEb3duKGV2ZW50LCBsaXN0Ym94KTtcbiAgICB9KTtcbiAgICB0aGlzLnJlbmRlcmVyLmxpc3RlbihsaXN0Ym94LmVsLm5hdGl2ZUVsZW1lbnQsICdrZXl1cCcsIChldmVudCkgPT4ge1xuICAgICAgdGhpcy5oYW5kbGVLZXlVcChldmVudCwgbGlzdGJveCk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogVGhlwqBoYW5kbGVLZXl1cMKgbWV0aG9kIGhhbmRsZXMgaXRlbSBzZWxlY3Rpb24gb24ga2V5dXAgZXZlbnRzIGZvciB0aGUgbGlzdGJveCBhbmQgaXMgY2FsbGVkIGJ5IGRlZmF1bHTCoHdpdGhpbiA8Y29kZT5hZGRTaG9ydGN1dHM8L2NvZGU+LlxuICAgKiBAcGFyYW0gZXZlbnQgS2V5dXAgZXZlbnRcbiAgICogQHBhcmFtIGxpc3Rib3ggTGlzdGJveCB0byBoYW5kbGUga2V5dXAgZXZlbnQgZm9yLlxuICAgKi9cbiAgaGFuZGxlS2V5VXAoZXZlbnQ6IEtleWJvYXJkRXZlbnQsIGxpc3Rib3g6IExpc3Rib3hEaXJlY3RpdmUpIHtcbiAgICBpZiAoIWxpc3Rib3guX2lzUm9sZUxpc3Rib3hWYXJpYW50KSByZXR1cm47XG4gICAgaWYgKGV2ZW50LmtleSA9PT0gRU5URVJfS0VZIHx8IGV2ZW50LmtleSA9PT0gU1BBQ0VfS0VZIHx8IGV2ZW50LmtleSA9PT0gSE9NRV9LRVkgfHwgZXZlbnQua2V5ID09PSBFTkRfS0VZKSB7XG4gICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpOyAvLyBwcmV2ZW50IHNjcm9sbGluZ1xuICAgICAgaWYgKGV2ZW50LmtleSA9PT0gU1BBQ0VfS0VZKSB7XG4gICAgICAgIGxpc3Rib3guX3JlY2VudFNlbGVjdGVkSW5kZXggPSBsaXN0Ym94Ll9oaWdobGlnaHRJbmRleDtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGhlwqBoYW5kbGVLZXlkb3duIGhhbmRsZXMga2V5ZG93biBldmVudHMgZm9yIHRoZSBsaXN0Ym94IGFuZCBpcyBjYWxsZWQgYnkgZGVmYXVsdCB3aXRoaW4gPGNvZGU+YWRkU2hvcnRjdXRzPC9jb2RlPi5cbiAgICogQHBhcmFtIGV2ZW50IEtleWRvd24gZXZlbnRcbiAgICogQHBhcmFtIGxpc3Rib3ggTGlzdGJveCB0byBoYW5kbGUga2V5dXAgZXZlbnQgZm9yLlxuICAgKi9cbiAgaGFuZGxlS2V5RG93bihldmVudDogS2V5Ym9hcmRFdmVudCwgbGlzdGJveDogTGlzdGJveERpcmVjdGl2ZSkge1xuICAgIGlmICghbGlzdGJveC5faXNSb2xlTGlzdGJveFZhcmlhbnQpIHJldHVybjtcbiAgICBsaXN0Ym94Ll9pc0hvdGtleUV2ZW50ID0gdHJ1ZTtcbiAgICBpZiAoZXZlbnQua2V5ICE9PSBUQUJfS0VZKSB7XG4gICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE1ldGEgKyBTaGlmdCArIEhvbWUgb3IgRW5kXG4gICAgICogU2VsZWN0IHRoZSBmb2N1c2VkIG9wdGlvbiBhbmQgYWxsIG9wdGlvbnMgdXAvZG93biB0byB0aGUgZmlyc3Qgb3B0aW9uXG4gICAgICogJlxuICAgICAqIEhvbWUgb3IgRW5kXG4gICAgICogTW92ZSB0aGUgZm9jdXMgdG8gdGhlIGZpcnN0L2xhc3QgbGlzdGJveCBvcHRpb25cbiAgICAgKi9cbiAgICBpZiAoZXZlbnQua2V5ID09PSBIT01FX0tFWSB8fCBldmVudC5rZXkgPT09IEVORF9LRVkpIHRoaXMudXBkYXRlRm9jdXNJdGVtKGV2ZW50LCBsaXN0Ym94KTtcblxuICAgIC8qKlxuICAgICAqIEEtWiBPUiBhLXpcbiAgICAgKiBUeXBpbmcgYSBjaGFyYWN0ZXIgd2lsbCBtb3ZlIHRoZSBmb2N1cyB0byB0aGUgbmV4dCBpdGVtIHdpdGggYSBuYW1lIHRoYXQgc3RhcnRzIHdpdGggdGhhdCBjaGFyYWN0ZXJcbiAgICAgKiBUeXBpbmcgaW4gcmFwaWQgc3VjY2Vzc2lvbiBtb3ZlcyB0aGUgZm9jdXMgdG8gdGhlIG5leHQgaXRlbSB3aXRoIGEgbmFtZSB0aGF0IHJlZmxlY3RlcyB0aGUgc2V0IG9mIGNoYXJhY3RlcnMganVzdCB0eXBlZFxuICAgICAqL1xuICAgIGlmIChcbiAgICAgICFldmVudC5tZXRhS2V5ICYmXG4gICAgICBldmVudC5rZXkubGVuZ3RoID09PSAxICYmXG4gICAgICAoKGV2ZW50LmtleSA+PSAnYScgJiYgZXZlbnQua2V5IDw9ICd6JykgfHxcbiAgICAgICAgKGV2ZW50LmtleSA+PSAnQScgJiYgZXZlbnQua2V5IDw9ICdaJykgfHxcbiAgICAgICAgKGV2ZW50LmtleSA+PSAnMCcgJiYgZXZlbnQua2V5IDw9ICc5JykpXG4gICAgKVxuICAgICAgdGhpcy5zZWFyY2hLZXl3b3JkKGV2ZW50LCBsaXN0Ym94KTtcblxuICAgIC8qKlxuICAgICAqIFNoaWZ0ICsg4oaRL+KGk1xuICAgICAqIE1vdmUgdGhlIGZvY3VzIHRvIGFuZCB0b2dnbGUgdGhlIHNlbGVjdGVkIHN0YXRlIG9mIHRoZSBuZXh0L3ByZXZpb3VzIG9wdGlvblxuICAgICAqL1xuICAgIGlmIChldmVudC5rZXkgPT09IFVQX0FSUk9XX0tFWSB8fCBldmVudC5rZXkgPT09IERPV05fQVJST1dfS0VZKSB0aGlzLnRvZ2dsZVNlbGVjdGVkU3RhdGUoZXZlbnQsIGxpc3Rib3gpO1xuXG4gICAgLyoqXG4gICAgICogTWV0YSArIEFcbiAgICAgKiBTZWxlY3QgYWxsIG9yIGRlc2VsZWN0IGFsbCBpdGVtc1xuICAgICAqL1xuICAgIGlmIChldmVudC5tZXRhS2V5ICYmIChldmVudC5rZXkgPT09ICdBJyB8fCBldmVudC5rZXkgPT09ICdhJykgJiYgbGlzdGJveC5tdWx0aXNlbGVjdCkgdGhpcy5zZWxlY3RBbGwobGlzdGJveCk7XG5cbiAgICAvKipcbiAgICAgKiBTaGlmdCArIFNwYWNlXG4gICAgICogU2VsZWN0IGNvbnRpZ3VvdXMgaXRlbXMgZnJvbSB0aGUgbW9zdCByZWNlbnRseSBzZWxlY3RlZCBpdGVtIHRvIHRocyBmb2N1c2VkIGl0ZW1cbiAgICAgKi9cbiAgICBpZiAoZXZlbnQuY29kZSA9PT0gU1BBQ0VfQ09ERSAmJiBldmVudC5zaGlmdEtleSA9PT0gdHJ1ZSkgdGhpcy5zZWxlY3RDb250aWd1b3VzSXRlbXMobGlzdGJveCk7XG4gICAgbGlzdGJveC5faXNIb3RrZXlFdmVudCA9IGZhbHNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZcKgdXBkYXRlRm9jdXNJdGVtwqBtZXRob2QgIGlzIGFjdGl2YXRlZCBieSBNZXRhICsgU2hpZnQgKyBIb21lL0VuZCBhbmQgc2VsZWN0cyBhbGwgaXRlbXMgYmV0d2VlbiB0aGUgZm9jdXNlZCBpdGVtIGFuZCB0aGUgZmlyc3Qgb3IgbGFzdCBpdGVtLCB0aGVuIHNldHMgZm9jdXMgdG8gdGhlIGZpcnN0IG9yIGxhc3QgaXRlbS4gPGJyPlxuICAgKiBUaGlzIG1ldGhvZCBpcyBjYWxsZWQgYnkgZGVmYXVsdCB3aXRoaW4gPGNvZGU+aGFuZGxlS2V5RG93bjwvY29kZT4uXG4gICAqIEBwYXJhbSBldmVudCBLZXlib2FyZCBldmVudFxuICAgKiBAcGFyYW0gbGlzdGJveCBMaXN0Ym94IHRvIHVwZGF0ZSBmb2N1cyBpdGVtIGZvci5cbiAgICovXG4gIHVwZGF0ZUZvY3VzSXRlbShldmVudDogS2V5Ym9hcmRFdmVudCwgbGlzdGJveDogTGlzdGJveERpcmVjdGl2ZSkge1xuICAgIGNvbnN0IGxpc3RJdGVtc0FycmF5ID0gbGlzdGJveC5saXN0SXRlbXMudG9BcnJheSgpO1xuICAgIGlmIChldmVudC5tZXRhS2V5ICYmIGV2ZW50LnNoaWZ0S2V5ICYmIGxpc3Rib3gubXVsdGlzZWxlY3QpIHtcbiAgICAgIGxpc3RJdGVtc0FycmF5LmZvckVhY2goKGl0ZW0sIGkpID0+IHtcbiAgICAgICAgaWYgKGxpc3Rib3guX2hpZ2hsaWdodEluZGV4ICE9PSBudWxsICYmIGl0ZW0uZGlzYWJsZWQgIT09IHRydWUpIHtcbiAgICAgICAgICBpZiAoZXZlbnQua2V5ID09PSBIT01FX0tFWSkgbGlzdGJveC5faGlnaGxpZ2h0SW5kZXggPj0gaSA/IChpdGVtLmFjdGl2ZSA9IHRydWUpIDogKGl0ZW0uYWN0aXZlID0gZmFsc2UpO1xuICAgICAgICAgIGlmIChldmVudC5rZXkgPT09IEVORF9LRVkpIGxpc3Rib3guX2hpZ2hsaWdodEluZGV4IDw9IGkgPyAoaXRlbS5hY3RpdmUgPSB0cnVlKSA6IChpdGVtLmFjdGl2ZSA9IGZhbHNlKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICAgIGxpc3Rib3guX2hpZ2hsaWdodEluZGV4ID1cbiAgICAgIGV2ZW50LmtleSA9PT0gSE9NRV9LRVlcbiAgICAgICAgPyB0aGlzLm5vdmFMaWJTZXJ2aWNlLmZpcnN0RW5hYmxlZEl0ZW0obGlzdEl0ZW1zQXJyYXkpXG4gICAgICAgIDogdGhpcy5ub3ZhTGliU2VydmljZS5sYXN0RW5hYmxlZEl0ZW0obGlzdEl0ZW1zQXJyYXkpO1xuICAgIGxpc3Rib3gudXBkYXRlVmFsdWVGcm9tSXRlbXMoKTtcbiAgICBsaXN0SXRlbXNBcnJheVtsaXN0Ym94Ll9oaWdobGlnaHRJbmRleF0uZWwubmF0aXZlRWxlbWVudC5mb2N1cygpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZcKgc2VhcmNoS2V5d29yZMKgbWV0aG9kICBoYW5kbGVzIGtleXMgQS1aLCBhLXosIGFuZCAwLTksIG1vdmluZyB0aGUgZm9jdXMgdG8gdGhlIG5leHQgaXRlbSB3aXRoIGEgbmFtZSBzdGFydGluZyB3aXRoIHRoZSB0eXBlZCBjaGFyYWN0ZXIuIDxicj5cbiAgICogSW4gdGhlIGNhc2Ugb2YgcmFwaWQgc3VjY2Vzc2lvbiwgaXQgbW92ZXMgdGhlIGZvY3VzIHRvIHRoZSBuZXh0IGl0ZW0gd2hvc2UgbmFtZSBtYXRjaGVzIHRoZSBzZXF1ZW5jZSBvZiBjaGFyYWN0ZXJzIHR5cGVkLiA8YnIgLz5cbiAgICogVGhpcyBtZXRob2QgaXMgY2FsbGVkIGJ5IGRlZmF1bHQgd2l0aGluIDxjb2RlPmhhbmRsZUtleURvd248L2NvZGU+LlxuICAgKiBAcGFyYW0gZXZlbnQgS2V5Ym9hcmQgZXZlbnRcbiAgICogQHBhcmFtIGxpc3Rib3ggTGlzdGJveCB0byBzZWFyY2ggZm9yIGl0ZW1zIGluLlxuICAgKi9cbiAgc2VhcmNoS2V5d29yZChldmVudDogS2V5Ym9hcmRFdmVudCwgbGlzdGJveDogTGlzdGJveERpcmVjdGl2ZSkge1xuICAgIGNvbnN0IGxpc3RJdGVtc0FycmF5ID0gbGlzdGJveC5saXN0SXRlbXMudG9BcnJheSgpO1xuICAgIGxpc3Rib3guX2tleXdvcmQgPSBsaXN0Ym94Ll9rZXl3b3JkICsgZXZlbnQua2V5LnRvTG93ZXJDYXNlKCk7XG4gICAgY2xlYXJUaW1lb3V0KGxpc3Rib3guX3RpbWVvdXRJZCk7XG4gICAgY29uc3QgYm91bmNlID0gd2luZG93LnNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgaWYgKGxpc3Rib3guX2tleXdvcmQgIT09ICcnKSB7XG4gICAgICAgIGxpc3Rib3guX2tleXdvcmQgPSAnJztcbiAgICAgIH1cbiAgICB9LCAxMDAwKTtcbiAgICBsaXN0Ym94Ll90aW1lb3V0SWQgPSBib3VuY2U7XG4gICAgY29uc3Qgc2VsZWN0ZWRJbmRleCA9IGxpc3RJdGVtc0FycmF5LmZpbmRJbmRleCgoaXRlbSkgPT5cbiAgICAgIGl0ZW0uZWwubmF0aXZlRWxlbWVudC5pbm5lclRleHQ/LnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMobGlzdGJveC5fa2V5d29yZClcbiAgICApO1xuXG4gICAgaWYgKHNlbGVjdGVkSW5kZXggPj0gMCAmJiAhbGlzdEl0ZW1zQXJyYXlbc2VsZWN0ZWRJbmRleF0uZGlzYWJsZWQpIHtcbiAgICAgIGxpc3RJdGVtc0FycmF5W3NlbGVjdGVkSW5kZXhdLmVsLm5hdGl2ZUVsZW1lbnQuZm9jdXMoKTtcbiAgICAgIGxpc3Rib3guX2hpZ2hsaWdodEluZGV4ID0gc2VsZWN0ZWRJbmRleDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGhlwqBzZWxlY3RBbGzCoG1ldGhvZCBpcyBhY3RpdmF0ZWQgYnkgdGhlIE1ldGEgKyBBIGtleXMgYW5kIHNlbGVjdHMgb3IgZGVzZWxlY3RzIGFsbCBpdGVtcyBpbiB0aGUgbGlzdGJveC4gPGJyPlxuICAgKiBUaGlzIG1ldGhvZCBpcyBjYWxsZWQgYnkgZGVmYXVsdCB3aXRoaW4gPGNvZGU+aGFuZGxlS2V5RG93bjwvY29kZT4uXG4gICAqIEBwYXJhbSBsaXN0Ym94IExpc3Rib3ggdG8gc2VsZWN0IGFsbCBpdGVtcyBpbi5cbiAgICovXG4gIHNlbGVjdEFsbChsaXN0Ym94OiBMaXN0Ym94RGlyZWN0aXZlKSB7XG4gICAgaWYgKGxpc3Rib3gubXVsdGlzZWxlY3QpIHtcbiAgICAgIGNvbnN0IGxpc3RJdGVtc0FycmF5ID0gbGlzdGJveC5saXN0SXRlbXMudG9BcnJheSgpO1xuICAgICAgdGhpcy5ub3ZhTGliU2VydmljZS5kZXRlY3RBbGxJdGVtc1NlbGVjdGVkKGxpc3RJdGVtc0FycmF5KVxuICAgICAgICA/IHRoaXMubm92YUxpYlNlcnZpY2UuZGVzZWxlY3RJdGVtcyhsaXN0SXRlbXNBcnJheSlcbiAgICAgICAgOiB0aGlzLm5vdmFMaWJTZXJ2aWNlLnNlbGVjdEl0ZW1zKGxpc3RJdGVtc0FycmF5KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGhlwqBzZWxlY3RGcm9tQ2xvc2VyU2VsZWN0ZWRUb0ZvY3VzZWTCoG1ldGhvZCBzZWxlY3RzIGFsbCBpdGVtcyBiZXR3ZWVuIHRoZSBjdXJyZW50bHkgZm9jdXNlZCBpdGVtIGFuZCB0aGUgY2xvc2VzdCBzZWxlY3RlZCBpdGVtLiA8YnI+XG4gICAqIFRoaXMgbWV0aG9kIGlzIGNhbGxlZCBieSBkZWZhdWx0IHdpdGhpbiA8Y29kZT5oYW5kbGVLZXlEb3duPC9jb2RlPi5cbiAgICogQHBhcmFtIGluZGV4IEluZGV4IG9mIHRoZSBmb2N1c2VkIGl0ZW0uXG4gICAqL1xuICBzZWxlY3RDb250aWd1b3VzSXRlbXMobGlzdGJveDogTGlzdGJveERpcmVjdGl2ZSkge1xuICAgIGlmIChsaXN0Ym94Ll9oaWdobGlnaHRJbmRleCAhPT0gbnVsbCAmJiBsaXN0Ym94Ll9yZWNlbnRTZWxlY3RlZEluZGV4ICE9PSBudWxsKSB7XG4gICAgICBjb25zdCBsaXN0SXRlbXNBcnJheSA9IGxpc3Rib3gubGlzdEl0ZW1zLnRvQXJyYXkoKTtcbiAgICAgIGNvbnN0IGlzUmVjZW50TGFyZ2VyID0gbGlzdGJveC5fcmVjZW50U2VsZWN0ZWRJbmRleCA+IGxpc3Rib3guX2hpZ2hsaWdodEluZGV4O1xuICAgICAgdGhpcy5ub3ZhTGliU2VydmljZS5zZWxlY3RJdGVtcyhcbiAgICAgICAgbGlzdEl0ZW1zQXJyYXksXG4gICAgICAgIGlzUmVjZW50TGFyZ2VyID8gbGlzdGJveC5faGlnaGxpZ2h0SW5kZXggOiBsaXN0Ym94Ll9yZWNlbnRTZWxlY3RlZEluZGV4LFxuICAgICAgICBpc1JlY2VudExhcmdlciA/IGxpc3Rib3guX3JlY2VudFNlbGVjdGVkSW5kZXggOiBsaXN0Ym94Ll9oaWdobGlnaHRJbmRleFxuICAgICAgKTtcbiAgICAgIGxpc3Rib3gudXBkYXRlVmFsdWVGcm9tSXRlbXMoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGhlwqB0b2dnbGVTZWxlY3RlZFN0YXRlwqBtZXRob2QgaXMgYWN0aXZhdGVkIGJ5IFNoaWZ0ICsg4oaRL+KGkyBhbmQgbW92ZXMgdGhlIGZvY3VzIHRvLCBhbmQgdG9nZ2xlcyB0aGUgc2VsZWN0ZWQgc3RhdGUgb2YsIHRoZSBuZXh0IG9yIHByZXZpb3VzIG9wdGlvbi4gPGJyPlxuICAgKiBUaGlzIG1ldGhvZCBpcyBjYWxsZWQgYnkgZGVmYXVsdCB3aXRoaW4gPGNvZGU+aGFuZGxlS2V5RG93bjwvY29kZT4uXG4gICAqIEBwYXJhbSBldmVudCBLZXlib2FyZCBldmVudFxuICAgKiBAcGFyYW0gbGlzdGJveCBMaXN0Ym94IHRvIHRvZ2dsZSBzZWxlY3RlZCBzdGF0ZSBmb3IuXG4gICAqL1xuICB0b2dnbGVTZWxlY3RlZFN0YXRlKGV2ZW50OiBLZXlib2FyZEV2ZW50LCBsaXN0Ym94OiBMaXN0Ym94RGlyZWN0aXZlKSB7XG4gICAgY29uc3QgbGlzdEl0ZW1zQXJyYXkgPSBsaXN0Ym94Lmxpc3RJdGVtcy50b0FycmF5KCk7XG4gICAgaWYgKGV2ZW50LnNoaWZ0S2V5ID09PSB0cnVlICYmIGxpc3Rib3gubXVsdGlzZWxlY3QgJiYgbGlzdGJveC5faGlnaGxpZ2h0SW5kZXggIT09IG51bGwpIHtcbiAgICAgIGlmIChsaXN0SXRlbXNBcnJheVtsaXN0Ym94Ll9oaWdobGlnaHRJbmRleF0uYWN0aXZlKSB7XG4gICAgICAgIHRoaXMubm92YUxpYlNlcnZpY2UuZGVzZWxlY3RJdGVtKGxpc3RJdGVtc0FycmF5LCBsaXN0Ym94Ll9oaWdobGlnaHRJbmRleCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLm5vdmFMaWJTZXJ2aWNlLnNlbGVjdEl0ZW0obGlzdEl0ZW1zQXJyYXksIGxpc3Rib3guX2hpZ2hsaWdodEluZGV4KTtcbiAgICAgICAgbGlzdGJveC5fcmVjZW50U2VsZWN0ZWRJbmRleCA9IGxpc3Rib3guX2hpZ2hsaWdodEluZGV4O1xuICAgICAgfVxuICAgICAgbGlzdGJveC51cGRhdGVWYWx1ZUZyb21JdGVtcygpO1xuICAgIH1cbiAgICBsaXN0Ym94Ll9oaWdobGlnaHRJbmRleCA9XG4gICAgICBldmVudC5rZXkgPT09IFVQX0FSUk9XX0tFWVxuICAgICAgICA/IHRoaXMubm92YUxpYlNlcnZpY2UucHJldmlvdXNFbmFibGVkSXRlbShsaXN0SXRlbXNBcnJheSwgbGlzdGJveC5faGlnaGxpZ2h0SW5kZXghKVxuICAgICAgICA6IHRoaXMubm92YUxpYlNlcnZpY2UubmV4dEVuYWJsZWRJdGVtKGxpc3RJdGVtc0FycmF5LCBsaXN0Ym94Ll9oaWdobGlnaHRJbmRleCEpO1xuICB9XG59XG4iXX0=