@clr/angular
Version:
Angular components for Clarity
348 lines • 58.5 kB
JavaScript
/*
* Copyright (c) 2016-2025 Broadcom. All Rights Reserved.
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
import { animate, state, style, transition, trigger } from '@angular/animations';
import { isPlatformBrowser } from '@angular/common';
import { Component, ContentChildren, ElementRef, EventEmitter, Inject, Input, Optional, Output, PLATFORM_ID, SkipSelf, ViewChild, } from '@angular/core';
import { Subject } from 'rxjs';
import { debounceTime, filter } from 'rxjs/operators';
import { IfExpandService } from '../../utils/conditional/if-expanded.service';
import { Keys } from '../../utils/enums/keys.enum';
import { isKeyEitherLetterOrNumber, normalizeKey, preventArrowKeyScroll } from '../../utils/focus/key-focus/util';
import { uniqueIdFactory } from '../../utils/id-generator/id-generator.service';
import { LoadingListener } from '../../utils/loading/loading-listener';
import { DeclarativeTreeNodeModel } from './models/declarative-tree-node.model';
import { ClrSelectedState } from './models/selected-state.enum';
import { TREE_FEATURES_PROVIDER } from './tree-features.service';
import { ClrTreeNodeLink } from './tree-node-link';
import * as i0 from "@angular/core";
import * as i1 from "./tree-features.service";
import * as i2 from "../../utils/conditional/if-expanded.service";
import * as i3 from "../../utils/i18n/common-strings.service";
import * as i4 from "./tree-focus-manager.service";
import * as i5 from "@angular/common";
import * as i6 from "../../icon/icon";
import * as i7 from "./recursive-children";
const LVIEW_CONTEXT_INDEX = 8;
// If the user types multiple keys without allowing 200ms to pass between them,
// then those keys are sent together in one request.
const TREE_TYPE_AHEAD_TIMEOUT = 200;
export class ClrTreeNode {
constructor(platformId, parent, featuresService, expandService, commonStrings, focusManager, elementRef, injector) {
this.platformId = platformId;
this.featuresService = featuresService;
this.expandService = expandService;
this.commonStrings = commonStrings;
this.focusManager = focusManager;
this.elementRef = elementRef;
this.selectedChange = new EventEmitter(false);
this.expandedChange = new EventEmitter();
this.STATES = ClrSelectedState;
this.isModelLoading = false;
this.nodeId = uniqueIdFactory();
this.contentContainerTabindex = -1;
this.skipEmitChange = false;
this.typeAheadKeyBuffer = '';
this.typeAheadKeyEvent = new Subject();
this.subscriptions = [];
if (featuresService.recursion) {
// I'm completely stuck, we have to hack into private properties until either
// https://github.com/angular/angular/issues/14935 or https://github.com/angular/angular/issues/15998
// are fixed
// This is for non-ivy implementations
if (injector.view) {
this._model = injector.view.context.clrModel;
}
else {
// Ivy puts this on a specific index of a _lView property
this._model = injector._lView[LVIEW_CONTEXT_INDEX].clrModel;
}
}
else {
// Force cast for now, not sure how to tie the correct type here to featuresService.recursion
this._model = new DeclarativeTreeNodeModel(parent ? parent._model : null);
}
this._model.nodeId = this.nodeId;
}
get disabled() {
return this._model.disabled;
}
set disabled(value) {
this._model.disabled = value;
}
get selected() {
return this._model.selected.value;
}
set selected(value) {
this.featuresService.selectable = true;
// Gracefully handle falsy states like null or undefined because it's just easier than answering questions.
// This shouldn't happen with strict typing on the app's side, but it's not up to us.
if (value === null || typeof value === 'undefined') {
value = ClrSelectedState.UNSELECTED;
}
// We match booleans to the corresponding ClrSelectedState
if (typeof value === 'boolean') {
value = value ? ClrSelectedState.SELECTED : ClrSelectedState.UNSELECTED;
}
// We propagate only if the tree is in smart mode, and skip emitting the output when we set the input
// See https://github.com/vmware/clarity/issues/3073
this.skipEmitChange = true;
this._model.setSelected(value, this.featuresService.eager, this.featuresService.eager);
this.skipEmitChange = false;
}
// I'm caving on this, for tree nodes I think we can tolerate having a two-way binding on the component
// rather than enforce the clrIfExpanded structural directive for dynamic cases. Mostly because for the smart
// case, you can't use a structural directive, it would need to go on an ng-container.
get expanded() {
return this.expandService.expanded;
}
set expanded(value) {
this.expandService.expanded = value;
}
set clrForTypeAhead(value) {
this._model.textContent = trimAndLowerCase(value || this.elementRef.nativeElement.textContent);
}
get ariaSelected() {
if (this.isSelectable()) {
return this._model.selected.value === ClrSelectedState.SELECTED;
}
else if (this.treeNodeLink?.active) {
return true;
}
else {
return null;
}
}
get treeNodeLink() {
return this.treeNodeLinkList && this.treeNodeLinkList.first;
}
get isParent() {
return this._model.children && this._model.children.length > 0;
}
ngOnInit() {
this._model.expanded = this.expanded;
this._model.disabled = this.disabled;
this.subscriptions.push(this._model.selected.pipe(filter(() => !this.skipEmitChange)).subscribe(value => {
this.selectedChange.emit(value);
}));
this.subscriptions.push(this.expandService.expandChange.subscribe(value => {
this.expandedChange.emit(value);
this._model.expanded = value;
}));
this.subscriptions.push(this.focusManager.focusRequest.subscribe(nodeId => {
if (this.nodeId === nodeId) {
this.focusTreeNode();
}
}), this.focusManager.focusChange.subscribe(nodeId => {
this.checkTabIndex(nodeId);
}));
this.subscriptions.push(this._model.loading$.pipe(debounceTime(0)).subscribe(isLoading => (this.isModelLoading = isLoading)));
}
ngAfterContentInit() {
this.subscriptions.push(this.typeAheadKeyEvent.pipe(debounceTime(TREE_TYPE_AHEAD_TIMEOUT)).subscribe((bufferedKeys) => {
this.focusManager.focusNodeStartsWith(bufferedKeys, this._model);
// reset once bufferedKeys are used
this.typeAheadKeyBuffer = '';
}));
}
ngAfterViewInit() {
if (!this._model.textContent) {
this._model.textContent = trimAndLowerCase(this.elementRef.nativeElement.textContent);
}
}
ngOnDestroy() {
this._model.destroy();
this.subscriptions.forEach(sub => sub.unsubscribe());
}
isExpandable() {
if (typeof this.expandable !== 'undefined') {
return this.expandable;
}
return !!this.expandService.expandable || this.isParent;
}
isSelectable() {
return this.featuresService.selectable;
}
focusTreeNode() {
const containerEl = this.contentContainer.nativeElement;
if (isPlatformBrowser(this.platformId) && document.activeElement !== containerEl) {
this.setTabIndex(0);
containerEl.focus();
containerEl.scrollIntoView({ block: 'nearest', inline: 'nearest' });
}
}
broadcastFocusOnContainer() {
this.focusManager.broadcastFocusedNode(this.nodeId);
}
onKeyDown(event) {
// Two reasons to prevent default behavior:
// 1. to prevent scrolling on arrow keys
// 2. Assistive Technology focus differs from Keyboard focus behavior.
// By default, pressing arrow key makes AT focus go into the nested content of the item.
preventArrowKeyScroll(event);
// https://www.w3.org/TR/wai-aria-practices-1.1/#keyboard-interaction-22
switch (normalizeKey(event.key)) {
case Keys.ArrowUp:
this.focusManager.focusNodeAbove(this._model);
break;
case Keys.ArrowDown:
this.focusManager.focusNodeBelow(this._model);
break;
case Keys.ArrowRight:
this.expandOrFocusFirstChild();
break;
case Keys.ArrowLeft:
this.collapseOrFocusParent();
break;
case Keys.Home:
event.preventDefault();
this.focusManager.focusFirstVisibleNode();
break;
case Keys.End:
event.preventDefault();
this.focusManager.focusLastVisibleNode();
break;
case Keys.Enter:
this.toggleExpandOrTriggerDefault();
break;
case Keys.Space:
case Keys.Spacebar:
// to prevent scrolling on space key in this specific case
event.preventDefault();
this.toggleExpandOrTriggerDefault();
break;
default:
if (this._model.textContent && isKeyEitherLetterOrNumber(event)) {
this.typeAheadKeyBuffer += event.key;
this.typeAheadKeyEvent.next(this.typeAheadKeyBuffer);
return;
}
break;
}
// if non-letter keys are pressed, do reset.
this.typeAheadKeyBuffer = '';
}
setTabIndex(value) {
this.contentContainerTabindex = value;
this.contentContainer.nativeElement.setAttribute('tabindex', value.toString());
}
checkTabIndex(nodeId) {
if (isPlatformBrowser(this.platformId) && this.nodeId !== nodeId && this.contentContainerTabindex !== -1) {
this.setTabIndex(-1);
}
}
toggleExpandOrTriggerDefault() {
if (this.disabled) {
return;
}
if (this.isExpandable() && !this.isSelectable()) {
this.expandService.expanded = !this.expanded;
}
else {
this.triggerDefaultAction();
}
}
expandOrFocusFirstChild() {
if (this.disabled) {
return;
}
if (this.expanded) {
// if the node is already expanded and has children, focus its very first child
if (this.isParent) {
this.focusManager.focusNodeBelow(this._model);
}
}
else {
// we must check if the node is expandable, in order to set .expanded to true from false
// because we shouldn't set .expanded to true if it's not expandable node
if (this.isExpandable()) {
this.expandService.expanded = true;
}
}
}
collapseOrFocusParent() {
if (this.disabled) {
return;
}
if (this.expanded) {
this.expandService.expanded = false;
}
else {
this.focusManager.focusParent(this._model);
}
}
triggerDefaultAction() {
if (this.treeNodeLink) {
this.treeNodeLink.activate();
}
else {
if (this.isSelectable()) {
this._model.toggleSelection(this.featuresService.eager);
}
}
}
}
ClrTreeNode.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTreeNode, deps: [{ token: PLATFORM_ID }, { token: ClrTreeNode, optional: true, skipSelf: true }, { token: i1.TreeFeaturesService }, { token: i2.IfExpandService }, { token: i3.ClrCommonStringsService }, { token: i4.TreeFocusManagerService }, { token: i0.ElementRef }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component });
ClrTreeNode.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrTreeNode, selector: "clr-tree-node", inputs: { expandable: ["clrExpandable", "expandable"], disabled: ["clrDisabled", "disabled"], selected: ["clrSelected", "selected"], expanded: ["clrExpanded", "expanded"], clrForTypeAhead: "clrForTypeAhead" }, outputs: { selectedChange: "clrSelectedChange", expandedChange: "clrExpandedChange" }, host: { properties: { "class.clr-tree-node": "true", "class.disabled": "this._model.disabled" } }, providers: [TREE_FEATURES_PROVIDER, IfExpandService, { provide: LoadingListener, useExisting: IfExpandService }], queries: [{ propertyName: "treeNodeLinkList", predicate: ClrTreeNodeLink }], viewQueries: [{ propertyName: "contentContainer", first: true, predicate: ["contentContainer"], descendants: true, read: ElementRef, static: true }], ngImport: i0, template: "<!--\n ~ Copyright (c) 2016-2025 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<div\n #contentContainer\n role=\"treeitem\"\n class=\"clr-tree-node-content-container\"\n tabindex=\"-1\"\n [class.clr-form-control-disabled]=\"disabled\"\n [attr.aria-disabled]=\"disabled\"\n [attr.aria-expanded]=\"isExpandable() ? expanded : null\"\n [attr.aria-selected]=\"ariaSelected\"\n (keydown)=\"onKeyDown($event)\"\n (focus)=\"broadcastFocusOnContainer()\"\n>\n <button\n *ngIf=\"isExpandable() && !isModelLoading && !expandService.loading\"\n aria-hidden=\"true\"\n type=\"button\"\n tabindex=\"-1\"\n class=\"clr-treenode-caret\"\n (click)=\"expandService.toggle();\"\n (focus)=\"focusTreeNode()\"\n [disabled]=\"disabled\"\n >\n <cds-icon\n class=\"clr-treenode-caret-icon\"\n shape=\"angle\"\n [attr.direction]=\"expandService.expanded ? 'down' : 'right'\"\n ></cds-icon>\n </button>\n <div class=\"clr-treenode-spinner-container\" *ngIf=\"expandService.loading || isModelLoading\">\n <span class=\"clr-treenode-spinner spinner\"></span>\n </div>\n <div class=\"clr-checkbox-wrapper clr-treenode-checkbox\" *ngIf=\"featuresService.selectable\">\n <input\n aria-hidden=\"true\"\n type=\"checkbox\"\n [id]=\"nodeId + '-check'\"\n class=\"clr-checkbox\"\n [disabled]=\"disabled\"\n [checked]=\"_model.selected.value === STATES.SELECTED\"\n [indeterminate]=\"_model.selected.value === STATES.INDETERMINATE\"\n (change)=\"_model.toggleSelection(featuresService.eager)\"\n (focus)=\"focusTreeNode()\"\n tabindex=\"-1\"\n />\n <label [for]=\"nodeId + '-check'\" class=\"clr-control-label\">\n <ng-container [ngTemplateOutlet]=\"treenodeContent\"></ng-container>\n </label>\n </div>\n <div class=\"clr-treenode-content\" (mouseup)=\"focusTreeNode()\" *ngIf=\"!featuresService.selectable\">\n <ng-container [ngTemplateOutlet]=\"treenodeContent\"></ng-container>\n </div>\n\n <ng-template #treenodeContent>\n <ng-content></ng-content>\n <div class=\"clr-sr-only\" *ngIf=\"featuresService.selectable || ariaSelected\">\n <span> {{ariaSelected ? commonStrings.keys.selectedTreeNode : commonStrings.keys.unselectedTreeNode}}</span>\n </div>\n </ng-template>\n</div>\n<div\n class=\"clr-treenode-children\"\n [@toggleChildrenAnim]=\"expandService.expanded ? 'expanded' : 'collapsed'\"\n [attr.role]=\"isExpandable() && !featuresService.recursion ? 'group' : null\"\n>\n <ng-content select=\"clr-tree-node\"></ng-content>\n <ng-content select=\"[clrIfExpanded]\"></ng-content>\n <clr-recursive-children [parent]=\"_model\"></clr-recursive-children>\n</div>\n", dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i6.CdsIconCustomTag, selector: "cds-icon" }, { kind: "component", type: i7.RecursiveChildren, selector: "clr-recursive-children", inputs: ["parent", "children"] }], animations: [
trigger('toggleChildrenAnim', [
transition('collapsed => expanded', [style({ height: 0 }), animate(200, style({ height: '*' }))]),
transition('expanded => collapsed', [style({ height: '*' }), animate(200, style({ height: 0 }))]),
state('expanded', style({ height: '*', 'overflow-y': 'visible' })),
state('collapsed', style({ height: 0 })),
]),
] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTreeNode, decorators: [{
type: Component,
args: [{ selector: 'clr-tree-node', providers: [TREE_FEATURES_PROVIDER, IfExpandService, { provide: LoadingListener, useExisting: IfExpandService }], animations: [
trigger('toggleChildrenAnim', [
transition('collapsed => expanded', [style({ height: 0 }), animate(200, style({ height: '*' }))]),
transition('expanded => collapsed', [style({ height: '*' }), animate(200, style({ height: 0 }))]),
state('expanded', style({ height: '*', 'overflow-y': 'visible' })),
state('collapsed', style({ height: 0 })),
]),
], host: {
'[class.clr-tree-node]': 'true',
'[class.disabled]': 'this._model.disabled',
}, template: "<!--\n ~ Copyright (c) 2016-2025 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<div\n #contentContainer\n role=\"treeitem\"\n class=\"clr-tree-node-content-container\"\n tabindex=\"-1\"\n [class.clr-form-control-disabled]=\"disabled\"\n [attr.aria-disabled]=\"disabled\"\n [attr.aria-expanded]=\"isExpandable() ? expanded : null\"\n [attr.aria-selected]=\"ariaSelected\"\n (keydown)=\"onKeyDown($event)\"\n (focus)=\"broadcastFocusOnContainer()\"\n>\n <button\n *ngIf=\"isExpandable() && !isModelLoading && !expandService.loading\"\n aria-hidden=\"true\"\n type=\"button\"\n tabindex=\"-1\"\n class=\"clr-treenode-caret\"\n (click)=\"expandService.toggle();\"\n (focus)=\"focusTreeNode()\"\n [disabled]=\"disabled\"\n >\n <cds-icon\n class=\"clr-treenode-caret-icon\"\n shape=\"angle\"\n [attr.direction]=\"expandService.expanded ? 'down' : 'right'\"\n ></cds-icon>\n </button>\n <div class=\"clr-treenode-spinner-container\" *ngIf=\"expandService.loading || isModelLoading\">\n <span class=\"clr-treenode-spinner spinner\"></span>\n </div>\n <div class=\"clr-checkbox-wrapper clr-treenode-checkbox\" *ngIf=\"featuresService.selectable\">\n <input\n aria-hidden=\"true\"\n type=\"checkbox\"\n [id]=\"nodeId + '-check'\"\n class=\"clr-checkbox\"\n [disabled]=\"disabled\"\n [checked]=\"_model.selected.value === STATES.SELECTED\"\n [indeterminate]=\"_model.selected.value === STATES.INDETERMINATE\"\n (change)=\"_model.toggleSelection(featuresService.eager)\"\n (focus)=\"focusTreeNode()\"\n tabindex=\"-1\"\n />\n <label [for]=\"nodeId + '-check'\" class=\"clr-control-label\">\n <ng-container [ngTemplateOutlet]=\"treenodeContent\"></ng-container>\n </label>\n </div>\n <div class=\"clr-treenode-content\" (mouseup)=\"focusTreeNode()\" *ngIf=\"!featuresService.selectable\">\n <ng-container [ngTemplateOutlet]=\"treenodeContent\"></ng-container>\n </div>\n\n <ng-template #treenodeContent>\n <ng-content></ng-content>\n <div class=\"clr-sr-only\" *ngIf=\"featuresService.selectable || ariaSelected\">\n <span> {{ariaSelected ? commonStrings.keys.selectedTreeNode : commonStrings.keys.unselectedTreeNode}}</span>\n </div>\n </ng-template>\n</div>\n<div\n class=\"clr-treenode-children\"\n [@toggleChildrenAnim]=\"expandService.expanded ? 'expanded' : 'collapsed'\"\n [attr.role]=\"isExpandable() && !featuresService.recursion ? 'group' : null\"\n>\n <ng-content select=\"clr-tree-node\"></ng-content>\n <ng-content select=\"[clrIfExpanded]\"></ng-content>\n <clr-recursive-children [parent]=\"_model\"></clr-recursive-children>\n</div>\n" }]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }, { type: ClrTreeNode, decorators: [{
type: Optional
}, {
type: SkipSelf
}] }, { type: i1.TreeFeaturesService }, { type: i2.IfExpandService }, { type: i3.ClrCommonStringsService }, { type: i4.TreeFocusManagerService }, { type: i0.ElementRef }, { type: i0.Injector }]; }, propDecorators: { expandable: [{
type: Input,
args: ['clrExpandable']
}], selectedChange: [{
type: Output,
args: ['clrSelectedChange']
}], expandedChange: [{
type: Output,
args: ['clrExpandedChange']
}], contentContainer: [{
type: ViewChild,
args: ['contentContainer', { read: ElementRef, static: true }]
}], treeNodeLinkList: [{
type: ContentChildren,
args: [ClrTreeNodeLink, { descendants: false }]
}], disabled: [{
type: Input,
args: ['clrDisabled']
}], selected: [{
type: Input,
args: ['clrSelected']
}], expanded: [{
type: Input,
args: ['clrExpanded']
}], clrForTypeAhead: [{
type: Input,
args: ['clrForTypeAhead']
}] } });
function trimAndLowerCase(value) {
return value.toLocaleLowerCase().trim();
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJlZS1ub2RlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvYW5ndWxhci9zcmMvZGF0YS90cmVlLXZpZXcvdHJlZS1ub2RlLnRzIiwiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvYW5ndWxhci9zcmMvZGF0YS90cmVlLXZpZXcvdHJlZS1ub2RlLmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0dBS0c7QUFFSCxPQUFPLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLE9BQU8sRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ2pGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3BELE9BQU8sRUFHTCxTQUFTLEVBQ1QsZUFBZSxFQUNmLFVBQVUsRUFDVixZQUFZLEVBQ1osTUFBTSxFQUVOLEtBQUssRUFHTCxRQUFRLEVBQ1IsTUFBTSxFQUNOLFdBQVcsRUFFWCxRQUFRLEVBQ1IsU0FBUyxHQUNWLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxPQUFPLEVBQWdCLE1BQU0sTUFBTSxDQUFDO0FBQzdDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFdEQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDZDQUE2QyxDQUFDO0FBQzlFLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUNuRCxPQUFPLEVBQUUseUJBQXlCLEVBQUUsWUFBWSxFQUFFLHFCQUFxQixFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFFbEgsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLCtDQUErQyxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUN2RSxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUNoRixPQUFPLEVBQUUsZ0JB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R0FBRyxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDOzRCQUNsRSxLQUFLLENBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDO3lCQUN6QyxDQUFDO3FCQUNILFFBQ0s7d0JBQ0osdUJBQXVCLEVBQUUsTUFBTTt3QkFDL0Isa0JBQWtCLEVBQUUsc0JBQXNCO3FCQUMzQzs7MEJBNEJFLE1BQU07MkJBQUMsV0FBVzs7MEJBQ2xCLFFBQVE7OzBCQUNSLFFBQVE7d09BekJhLFVBQVU7c0JBQWpDLEtBQUs7dUJBQUMsZUFBZTtnQkFFTyxjQUFjO3NCQUExQyxNQUFNO3VCQUFDLG1CQUFtQjtnQkFDRSxjQUFjO3NCQUExQyxNQUFNO3VCQUFDLG1CQUFtQjtnQkFhZ0QsZ0JBQWdCO3NCQUExRixTQUFTO3VCQUFDLGtCQUFrQixFQUFFLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFO2dCQUlDLGdCQUFnQjtzQkFBakYsZUFBZTt1QkFBQyxlQUFlLEVBQUUsRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFO2dCQWlDcEQsUUFBUTtzQkFEWCxLQUFLO3VCQUFDLGFBQWE7Z0JBU2hCLFFBQVE7c0JBRFgsS0FBSzt1QkFBQyxhQUFhO2dCQTBCaEIsUUFBUTtzQkFEWCxLQUFLO3VCQUFDLGFBQWE7Z0JBU2hCLGVBQWU7c0JBRGxCLEtBQUs7dUJBQUMsaUJBQWlCOztBQXNOMUIsU0FBUyxnQkFBZ0IsQ0FBQyxLQUFhO0lBQ3JDLE9BQU8sS0FBSyxDQUFDLGlCQUFpQixFQUFFLENBQUMsSUFBSSxFQUFFLENBQUM7QUFDMUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKGMpIDIwMTYtMjAyNSBCcm9hZGNvbS4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIFRoZSB0ZXJtIFwiQnJvYWRjb21cIiByZWZlcnMgdG8gQnJvYWRjb20gSW5jLiBhbmQvb3IgaXRzIHN1YnNpZGlhcmllcy5cbiAqIFRoaXMgc29mdHdhcmUgaXMgcmVsZWFzZWQgdW5kZXIgTUlUIGxpY2Vuc2UuXG4gKiBUaGUgZnVsbCBsaWNlbnNlIGluZm9ybWF0aW9uIGNhbiBiZSBmb3VuZCBpbiBMSUNFTlNFIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGlzIHByb2plY3QuXG4gKi9cblxuaW1wb3J0IHsgYW5pbWF0ZSwgc3RhdGUsIHN0eWxlLCB0cmFuc2l0aW9uLCB0cmlnZ2VyIH0gZnJvbSAnQGFuZ3VsYXIvYW5pbWF0aW9ucyc7XG5pbXBvcnQgeyBpc1BsYXRmb3JtQnJvd3NlciB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQge1xuICBBZnRlckNvbnRlbnRJbml0LFxuICBBZnRlclZpZXdJbml0LFxuICBDb21wb25lbnQsXG4gIENvbnRlbnRDaGlsZHJlbixcbiAgRWxlbWVudFJlZixcbiAgRXZlbnRFbWl0dGVyLFxuICBJbmplY3QsXG4gIEluamVjdG9yLFxuICBJbnB1dCxcbiAgT25EZXN0cm95LFxuICBPbkluaXQsXG4gIE9wdGlvbmFsLFxuICBPdXRwdXQsXG4gIFBMQVRGT1JNX0lELFxuICBRdWVyeUxpc3QsXG4gIFNraXBTZWxmLFxuICBWaWV3Q2hpbGQsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgU3ViamVjdCwgU3Vic2NyaXB0aW9uIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBkZWJvdW5jZVRpbWUsIGZpbHRlciB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuaW1wb3J0IHsgSWZFeHBhbmRTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vdXRpbHMvY29uZGl0aW9uYWwvaWYtZXhwYW5kZWQuc2VydmljZSc7XG5pbXBvcnQgeyBLZXlzIH0gZnJvbSAnLi4vLi4vdXRpbHMvZW51bXMva2V5cy5lbnVtJztcbmltcG9ydCB7IGlzS2V5RWl0aGVyTGV0dGVyT3JOdW1iZXIsIG5vcm1hbGl6ZUtleSwgcHJldmVudEFycm93S2V5U2Nyb2xsIH0gZnJvbSAnLi4vLi4vdXRpbHMvZm9jdXMva2V5LWZvY3VzL3V0aWwnO1xuaW1wb3J0IHsgQ2xyQ29tbW9uU3RyaW5nc1NlcnZpY2UgfSBmcm9tICcuLi8uLi91dGlscy9pMThuL2NvbW1vbi1zdHJpbmdzLnNlcnZpY2UnO1xuaW1wb3J0IHsgdW5pcXVlSWRGYWN0b3J5IH0gZnJvbSAnLi4vLi4vdXRpbHMvaWQtZ2VuZXJhdG9yL2lkLWdlbmVyYXRvci5zZXJ2aWNlJztcbmltcG9ydCB7IExvYWRpbmdMaXN0ZW5lciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvYWRpbmcvbG9hZGluZy1saXN0ZW5lcic7XG5pbXBvcnQgeyBEZWNsYXJhdGl2ZVRyZWVOb2RlTW9kZWwgfSBmcm9tICcuL21vZGVscy9kZWNsYXJhdGl2ZS10cmVlLW5vZGUubW9kZWwnO1xuaW1wb3J0IHsgQ2xyU2VsZWN0ZWRTdGF0ZSB9IGZyb20gJy4vbW9kZWxzL3NlbGVjdGVkLXN0YXRlLmVudW0nO1xuaW1wb3J0IHsgVHJlZU5vZGVNb2RlbCB9IGZyb20gJy4vbW9kZWxzL3RyZWUtbm9kZS5tb2RlbCc7XG5pbXBvcnQgeyBUUkVFX0ZFQVRVUkVTX1BST1ZJREVSLCBUcmVlRmVhdHVyZXNTZXJ2aWNlIH0gZnJvbSAnLi90cmVlLWZlYXR1cmVzLnNlcnZpY2UnO1xuaW1wb3J0IHsgVHJlZUZvY3VzTWFuYWdlclNlcnZpY2UgfSBmcm9tICcuL3RyZWUtZm9jdXMtbWFuYWdlci5zZXJ2aWNlJztcbmltcG9ydCB7IENsclRyZWVOb2RlTGluayB9IGZyb20gJy4vdHJlZS1ub2RlLWxpbmsnO1xuXG5jb25zdCBMVklFV19DT05URVhUX0lOREVYID0gODtcblxuLy8gSWYgdGhlIHVzZXIgdHlwZXMgbXVsdGlwbGUga2V5cyB3aXRob3V0IGFsbG93aW5nIDIwMG1zIHRvIHBhc3MgYmV0d2VlbiB0aGVtLFxuLy8gdGhlbiB0aG9zZSBrZXlzIGFyZSBzZW50IHRvZ2V0aGVyIGluIG9uZSByZXF1ZXN0LlxuY29uc3QgVFJFRV9UWVBFX0FIRUFEX1RJTUVPVVQgPSAyMDA7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2Nsci10cmVlLW5vZGUnLFxuICB0ZW1wbGF0ZVVybDogJy4vdHJlZS1ub2RlLmh0bWwnLFxuICBwcm92aWRlcnM6IFtUUkVFX0ZFQVRVUkVTX1BST1ZJREVSLCBJZkV4cGFuZFNlcnZpY2UsIHsgcHJvdmlkZTogTG9hZGluZ0xpc3RlbmVyLCB1c2VFeGlzdGluZzogSWZFeHBhbmRTZXJ2aWNlIH1dLFxuICBhbmltYXRpb25zOiBbXG4gICAgdHJpZ2dlcigndG9nZ2xlQ2hpbGRyZW5BbmltJywgW1xuICAgICAgdHJhbnNpdGlvbignY29sbGFwc2VkID0+IGV4cGFuZGVkJywgW3N0eWxlKHsgaGVpZ2h0OiAwIH0pLCBhbmltYXRlKDIwMCwgc3R5bGUoeyBoZWlnaHQ6ICcqJyB9KSldKSxcbiAgICAgIHRyYW5zaXRpb24oJ2V4cGFuZGVkID0+IGNvbGxhcHNlZCcsIFtzdHlsZSh7IGhlaWdodDogJyonIH0pLCBhbmltYXRlKDIwMCwgc3R5bGUoeyBoZWlnaHQ6IDAgfSkpXSksXG4gICAgICBzdGF0ZSgnZXhwYW5kZWQnLCBzdHlsZSh7IGhlaWdodDogJyonLCAnb3ZlcmZsb3cteSc6ICd2aXNpYmxlJyB9KSksXG4gICAgICBzdGF0ZSgnY29sbGFwc2VkJywgc3R5bGUoeyBoZWlnaHQ6IDAgfSkpLFxuICAgIF0pLFxuICBdLFxuICBob3N0OiB7XG4gICAgJ1tjbGFzcy5jbHItdHJlZS1ub2RlXSc6ICd0cnVlJyxcbiAgICAnW2NsYXNzLmRpc2FibGVkXSc6ICd0aGlzLl9tb2RlbC5kaXNhYmxlZCcsXG4gIH0sXG59KVxuZXhwb3J0IGNsYXNzIENsclRyZWVOb2RlPFQ+IGltcGxlbWVudHMgT25Jbml0LCBBZnRlckNvbnRlbnRJbml0LCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3kge1xuICAvLyBBbGxvd3MgdGhlIGNvbnN1bWVyIHRvIG92ZXJyaWRlIG91ciBsb2dpYyBkZWNpZGluZyBpZiBhIG5vZGUgaXMgZXhwYW5kYWJsZS5cbiAgLy8gVXNlZnVsIGZvciByZWN1cnNpdmUgdHJlZXMgdGhhdCBkb24ndCB3YW50IHRvIHByZS1sb2FkIG9uZSBsZXZlbCBhaGVhZCBqdXN0IHRvIGtub3cgd2hpY2ggbm9kZXMgYXJlIGV4cGFuZGFibGUuXG4gIEBJbnB1dCgnY2xyRXhwYW5kYWJsZScpIGV4cGFuZGFibGU6IGJvb2xlYW4gfCB1bmRlZmluZWQ7XG5cbiAgQE91dHB1dCgnY2xyU2VsZWN0ZWRDaGFuZ2UnKSBzZWxlY3RlZENoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8Q2xyU2VsZWN0ZWRTdGF0ZT4oZmFsc2UpO1xuICBAT3V0cHV0KCdjbHJFeHBhbmRlZENoYW5nZScpIGV4cGFuZGVkQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxib29sZWFuPigpO1xuXG4gIFNUQVRFUyA9IENsclNlbGVjdGVkU3RhdGU7XG4gIGlzTW9kZWxMb2FkaW5nID0gZmFsc2U7XG4gIG5vZGVJZCA9IHVuaXF1ZUlkRmFjdG9yeSgpO1xuICBjb250ZW50Q29udGFpbmVyVGFiaW5kZXggPSAtMTtcbiAgX21vZGVsOiBUcmVlTm9kZU1vZGVsPFQ+O1xuXG4gIHByaXZhdGUgc2tpcEVtaXRDaGFuZ2UgPSBmYWxzZTtcbiAgcHJpdmF0ZSB0eXBlQWhlYWRLZXlCdWZmZXIgPSAnJztcbiAgcHJpdmF0ZSB0eXBlQWhlYWRLZXlFdmVudCA9IG5ldyBTdWJqZWN0PHN0cmluZz4oKTtcbiAgcHJpdmF0ZSBzdWJzY3JpcHRpb25zOiBTdWJzY3JpcHRpb25bXSA9IFtdO1xuXG4gIEBWaWV3Q2hpbGQoJ2NvbnRlbnRDb250YWluZXInLCB7IHJlYWQ6IEVsZW1lbnRSZWYsIHN0YXRpYzogdHJ1ZSB9KSBwcml2YXRlIGNvbnRlbnRDb250YWluZXI6IEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+O1xuXG4gIC8vIEBDb250ZW50Q2hpbGQgd291bGQgaGF2ZSBiZWVuIG1vcmUgc3VjY2luY3RcbiAgLy8gYnV0IGl0IGRvZXNuJ3Qgb2ZmZXIgYSB3YXkgdG8gcXVlcnkgb25seSBhbiBpbW1lZGlhdGUgY2hpbGRcbiAgQENvbnRlbnRDaGlsZHJlbihDbHJUcmVlTm9kZUxpbmssIHsgZGVzY2VuZGFudHM6IGZhbHNlIH0pIHByaXZhdGUgdHJlZU5vZGVMaW5rTGlzdDogUXVlcnlMaXN0PENsclRyZWVOb2RlTGluaz47XG5cbiAgY29uc3RydWN0b3IoXG4gICAgQEluamVjdChQTEFURk9STV9JRCkgcHJpdmF0ZSBwbGF0Zm9ybUlkOiBhbnksXG4gICAgQE9wdGlvbmFsKClcbiAgICBAU2tpcFNlbGYoKVxuICAgIHBhcmVudDogQ2xyVHJlZU5vZGU8VD4sXG4gICAgcHVibGljIGZlYXR1cmVzU2VydmljZTogVHJlZUZlYXR1cmVzU2VydmljZTxUPixcbiAgICBwdWJsaWMgZXhwYW5kU2VydmljZTogSWZFeHBhbmRTZXJ2aWNlLFxuICAgIHB1YmxpYyBjb21tb25TdHJpbmdzOiBDbHJDb21tb25TdHJpbmdzU2VydmljZSxcbiAgICBwcml2YXRlIGZvY3VzTWFuYWdlcjogVHJlZUZvY3VzTWFuYWdlclNlcnZpY2U8VD4sXG4gICAgcHJpdmF0ZSBlbGVtZW50UmVmOiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PixcbiAgICBpbmplY3RvcjogSW5qZWN0b3JcbiAgKSB7XG4gICAgaWYgKGZlYXR1cmVzU2VydmljZS5yZWN1cnNpb24pIHtcbiAgICAgIC8vIEknbSBjb21wbGV0ZWx5IHN0dWNrLCB3ZSBoYXZlIHRvIGhhY2sgaW50byBwcml2YXRlIHByb3BlcnRpZXMgdW50aWwgZWl0aGVyXG4gICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vYW5ndWxhci9hbmd1bGFyL2lzc3Vlcy8xNDkzNSBvciBodHRwczovL2dpdGh1Yi5jb20vYW5ndWxhci9hbmd1bGFyL2lzc3Vlcy8xNTk5OFxuICAgICAgLy8gYXJlIGZpeGVkXG4gICAgICAvLyBUaGlzIGlzIGZvciBub24taXZ5IGltcGxlbWVudGF0aW9uc1xuICAgICAgaWYgKChpbmplY3RvciBhcyBhbnkpLnZpZXcpIHtcbiAgICAgICAgdGhpcy5fbW9kZWwgPSAoaW5qZWN0b3IgYXMgYW55KS52aWV3LmNvbnRleHQuY2xyTW9kZWw7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBJdnkgcHV0cyB0aGlzIG9uIGEgc3BlY2lmaWMgaW5kZXggb2YgYSBfbFZpZXcgcHJvcGVydHlcbiAgICAgICAgdGhpcy5fbW9kZWwgPSAoaW5qZWN0b3IgYXMgYW55KS5fbFZpZXdbTFZJRVdfQ09OVEVYVF9JTkRFWF0uY2xyTW9kZWw7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEZvcmNlIGNhc3QgZm9yIG5vdywgbm90IHN1cmUgaG93IHRvIHRpZSB0aGUgY29ycmVjdCB0eXBlIGhlcmUgdG8gZmVhdHVyZXNTZXJ2aWNlLnJlY3Vyc2lvblxuICAgICAgdGhpcy5fbW9kZWwgPSBuZXcgRGVjbGFyYXRpdmVUcmVlTm9kZU1vZGVsKHBhcmVudCA/IChwYXJlbnQuX21vZGVsIGFzIERlY2xhcmF0aXZlVHJlZU5vZGVNb2RlbDxUPikgOiBudWxsKTtcbiAgICB9XG4gICAgdGhpcy5fbW9kZWwubm9kZUlkID0gdGhpcy5ub2RlSWQ7XG4gIH1cblxuICBASW5wdXQoJ2NsckRpc2FibGVkJylcbiAgZ2V0IGRpc2FibGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLl9tb2RlbC5kaXNhYmxlZDtcbiAgfVxuICBzZXQgZGlzYWJsZWQodmFsdWU6IGJvb2xlYW4pIHtcbiAgICB0aGlzLl9tb2RlbC5kaXNhYmxlZCA9IHZhbHVlO1xuICB9XG5cbiAgQElucHV0KCdjbHJTZWxlY3RlZCcpXG4gIGdldCBzZWxlY3RlZCgpOiBDbHJTZWxlY3RlZFN0YXRlIHwgYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuX21vZGVsLnNlbGVjdGVkLnZhbHVlO1xuICB9XG4gIHNldCBzZWxlY3RlZCh2YWx1ZTogQ2xyU2VsZWN0ZWRTdGF0ZSB8IGJvb2xlYW4pIHtcbiAgICB0aGlzLmZlYXR1cmVzU2VydmljZS5zZWxlY3RhYmxlID0gdHJ1ZTtcbiAgICAvLyBHcmFjZWZ1bGx5IGhhbmRsZSBmYWxzeSBzdGF0ZXMgbGlrZSBudWxsIG9yIHVuZGVmaW5lZCBiZWNhdXNlIGl0J3MganVzdCBlYXNpZXIgdGhhbiBhbnN3ZXJpbmcgcXVlc3Rpb25zLlxuICAgIC8vIFRoaXMgc2hvdWxkbid0IGhhcHBlbiB3aXRoIHN0cmljdCB0eXBpbmcgb24gdGhlIGFwcCdzIHNpZGUsIGJ1dCBpdCdzIG5vdCB1cCB0byB1cy5cbiAgICBpZiAodmFsdWUgPT09IG51bGwgfHwgdHlwZW9mIHZhbHVlID09PSAndW5kZWZpbmVkJykge1xuICAgICAgdmFsdWUgPSBDbHJTZWxlY3RlZFN0YXRlLlVOU0VMRUNURUQ7XG4gICAgfVxuICAgIC8vIFdlIG1hdGNoIGJvb2xlYW5zIHRvIHRoZSBjb3JyZXNwb25kaW5nIENsclNlbGVjdGVkU3RhdGVcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnYm9vbGVhbicpIHtcbiAgICAgIHZhbHVlID0gdmFsdWUgPyBDbHJTZWxlY3RlZFN0YXRlLlNFTEVDVEVEIDogQ2xyU2VsZWN0ZWRTdGF0ZS5VTlNFTEVDVEVEO1xuICAgIH1cbiAgICAvLyBXZSBwcm9wYWdhdGUgb25seSBpZiB0aGUgdHJlZSBpcyBpbiBzbWFydCBtb2RlLCBhbmQgc2tpcCBlbWl0dGluZyB0aGUgb3V0cHV0IHdoZW4gd2Ugc2V0IHRoZSBpbnB1dFxuICAgIC8vIFNlZSBodHRwczovL2dpdGh1Yi5jb20vdm13YXJlL2NsYXJpdHkvaXNzdWVzLzMwNzNcbiAgICB0aGlzLnNraXBFbWl0Q2hhbmdlID0gdHJ1ZTtcbiAgICB0aGlzLl9tb2RlbC5zZXRTZWxlY3RlZCh2YWx1ZSwgdGhpcy5mZWF0dXJlc1NlcnZpY2UuZWFnZXIsIHRoaXMuZmVhdHVyZXNTZXJ2aWNlLmVhZ2VyKTtcbiAgICB0aGlzLnNraXBFbWl0Q2hhbmdlID0gZmFsc2U7XG4gIH1cblxuICAvLyBJJ20gY2F2aW5nIG9uIHRoaXMsIGZvciB0cmVlIG5vZGVzIEkgdGhpbmsgd2UgY2FuIHRvbGVyYXRlIGhhdmluZyBhIHR3by13YXkgYmluZGluZyBvbiB0aGUgY29tcG9uZW50XG4gIC8vIHJhdGhlciB0aGFuIGVuZm9yY2UgdGhlIGNscklmRXhwYW5kZWQgc3RydWN0dXJhbCBkaXJlY3RpdmUgZm9yIGR5bmFtaWMgY2FzZXMuIE1vc3RseSBiZWNhdXNlIGZvciB0aGUgc21hcnRcbiAgLy8gY2FzZSwgeW91IGNhbid0IHVzZSBhIHN0cnVjdHVyYWwgZGlyZWN0aXZlLCBpdCB3b3VsZCBuZWVkIHRvIGdvIG9uIGFuIG5nLWNvbnRhaW5lci5cbiAgQElucHV0KCdjbHJFeHBhbmRlZCcpXG4gIGdldCBleHBhbmRlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5leHBhbmRTZXJ2aWNlLmV4cGFuZGVkO1xuICB9XG4gIHNldCBleHBhbmRlZCh2YWx1ZTogYm9vbGVhbikge1xuICAgIHRoaXMuZXhwYW5kU2VydmljZS5leHBhbmRlZCA9IHZhbHVlO1xuICB9XG5cbiAgQElucHV0KCdjbHJGb3JUeXBlQWhlYWQnKVxuICBzZXQgY2xyRm9yVHlwZUFoZWFkKHZhbHVlOiBzdHJpbmcpIHtcbiAgICB0aGlzLl9tb2RlbC50ZXh0Q29udGVudCA9IHRyaW1BbmRMb3dlckNhc2UodmFsdWUgfHwgdGhpcy5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQudGV4dENvbnRlbnQpO1xuICB9XG5cbiAgZ2V0IGFyaWFTZWxlY3RlZCgpOiBib29sZWFuIHtcbiAgICBpZiAodGhpcy5pc1NlbGVjdGFibGUoKSkge1xuICAgICAgcmV0dXJuIHRoaXMuX21vZGVsLnNlbGVjdGVkLnZhbHVlID09PSBDbHJTZWxlY3RlZFN0YXRlLlNFTEVDVEVEO1xuICAgIH0gZWxzZSBpZiAodGhpcy50cmVlTm9kZUxpbms/LmFjdGl2ZSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIGdldCB0cmVlTm9kZUxpbmsoKSB7XG4gICAgcmV0dXJuIHRoaXMudHJlZU5vZGVMaW5rTGlzdCAmJiB0aGlzLnRyZWVOb2RlTGlua0xpc3QuZmlyc3Q7XG4gIH1cblxuICBwcml2YXRlIGdldCBpc1BhcmVudCgpIHtcbiAgICByZXR1cm4gdGhpcy5fbW9kZWwuY2hpbGRyZW4gJiYgdGhpcy5fbW9kZWwuY2hpbGRyZW4ubGVuZ3RoID4gMDtcbiAgfVxuXG4gIG5nT25Jbml0KCkge1xuICAgIHRoaXMuX21vZGVsLmV4cGFuZGVkID0gdGhpcy5leHBhbmRlZDtcbiAgICB0aGlzLl9tb2RlbC5kaXNhYmxlZCA9IHRoaXMuZGlzYWJsZWQ7XG4gICAgdGhpcy5zdWJzY3JpcHRpb25zLnB1c2goXG4gICAgICB0aGlzLl9tb2RlbC5zZWxlY3RlZC5waXBlKGZpbHRlcigoKSA9PiAhdGhpcy5za2lwRW1pdENoYW5nZSkpLnN1YnNjcmliZSh2YWx1ZSA9PiB7XG4gICAgICAgIHRoaXMuc2VsZWN0ZWRDaGFuZ2UuZW1pdCh2YWx1ZSk7XG4gICAgICB9KVxuICAgICk7XG4gICAgdGhpcy5zdWJzY3JpcHRpb25zLnB1c2goXG4gICAgICB0aGlzLmV4cGFuZFNlcnZpY2UuZXhwYW5kQ2hhbmdlLnN1YnNjcmliZSh2YWx1ZSA9PiB7XG4gICAgICAgIHRoaXMuZXhwYW5kZWRDaGFuZ2UuZW1pdCh2YWx1ZSk7XG4gICAgICAgIHRoaXMuX21vZGVsLmV4cGFuZGVkID0gdmFsdWU7XG4gICAgICB9KVxuICAgICk7XG4gICAgdGhpcy5zdWJzY3JpcHRpb25zLnB1c2goXG4gICAgICB0aGlzLmZvY3VzTWFuYWdlci5mb2N1c1JlcXVlc3Quc3Vic2NyaWJlKG5vZGVJZCA9PiB7XG4gICAgICAgIGlmICh0aGlzLm5vZGVJZCA9PT0gbm9kZUlkKSB7XG4gICAgICAgICAgdGhpcy5mb2N1c1RyZWVOb2RlKCk7XG4gICAgICAgIH1cbiAgICAgIH0pLFxuICAgICAgdGhpcy5mb2N1c01hbmFnZXIuZm9jdXNDaGFuZ2Uuc3Vic2NyaWJlKG5vZGVJZCA9PiB7XG4gICAgICAgIHRoaXMuY2hlY2tUYWJJbmRleChub2RlSWQpO1xuICAgICAgfSlcbiAgICApO1xuXG4gICAgdGhpcy5zdWJzY3JpcHRpb25zLnB1c2goXG4gICAgICB0aGlzLl9tb2RlbC5sb2FkaW5nJC5waXBlKGRlYm91bmNlVGltZSgwKSkuc3Vic2NyaWJlKGlzTG9hZGluZyA9PiAodGhpcy5pc01vZGVsTG9hZGluZyA9IGlzTG9hZGluZykpXG4gICAgKTtcbiAgfVxuXG4gIG5nQWZ0ZXJDb250ZW50SW5pdCgpIHtcbiAgICB0aGlzLnN1YnNjcmlwdGlvbnMucHVzaChcbiAgICAgIHRoaXMudHlwZUFoZWFkS2V5RXZlbnQucGlwZShkZWJvdW5jZVRpbWUoVFJFRV9UWVBFX0FIRUFEX1RJTUVPVVQpKS5zdWJzY3JpYmUoKGJ1ZmZlcmVkS2V5czogc3RyaW5nKSA9PiB7XG4gICAgICAgIHRoaXMuZm9jdXNNYW5hZ2VyLmZvY3VzTm9kZVN0YXJ0c1dpdGgoYnVmZmVyZWRLZXlzLCB0aGlzLl9tb2RlbCk7XG4gICAgICAgIC8vIHJlc2V0IG9uY2UgYnVmZmVyZWRLZXlzIGFyZSB1c2VkXG4gICAgICAgIHRoaXMudHlwZUFoZWFkS2V5QnVmZmVyID0gJyc7XG4gICAgICB9KVxuICAgICk7XG4gIH1cblxuICBuZ0FmdGVyVmlld0luaXQoKSB7XG4gICAgaWYgKCF0aGlzLl9tb2RlbC50ZXh0Q29udGVudCkge1xuICAgICAgdGhpcy5fbW9kZWwudGV4dENvbnRlbnQgPSB0cmltQW5kTG93ZXJDYXNlKHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50LnRleHRDb250ZW50KTtcbiAgICB9XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICB0aGlzLl9tb2RlbC5kZXN0cm95KCk7XG4gICAgdGhpcy5zdWJzY3JpcHRpb25zLmZvckVhY2goc3ViID0+IHN1Yi51bnN1YnNjcmliZSgpKTtcbiAgfVxuXG4gIGlzRXhwYW5kYWJsZSgpIHtcbiAgICBpZiAodHlwZW9mIHRoaXMuZXhwYW5kYWJsZSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHJldHVybiB0aGlzLmV4cGFuZGFibGU7XG4gICAgfVxuICAgIHJldHVybiAhIXRoaXMuZXhwYW5kU2VydmljZS5leHBhbmRhYmxlIHx8IHRoaXMuaXNQYXJlbnQ7XG4gIH1cblxuICBpc1NlbGVjdGFibGUoKSB7XG4gICAgcmV0dXJuIHRoaXMuZmVhdHVyZXNTZXJ2aWNlLnNlbGVjdGFibGU7XG4gIH1cblxuICBmb2N1c1RyZWVOb2RlKCk6IHZvaWQge1xuICAgIGNvbnN0IGNvbnRhaW5lckVsID0gdGhpcy5jb250ZW50Q29udGFpbmVyLm5hdGl2ZUVsZW1lbnQ7XG4gICAgaWYgKGlzUGxhdGZvcm1Ccm93c2VyKHRoaXMucGxhdGZvcm1JZCkgJiYgZG9jdW1lbnQuYWN0aXZlRWxlbWVudCAhPT0gY29udGFpbmVyRWwpIHtcbiAgICAgIHRoaXMuc2V0VGFiSW5kZXgoMCk7XG4gICAgICBjb250YWluZXJFbC5mb2N1cygpO1xuICAgICAgY29udGFpbmVyRWwuc2Nyb2xsSW50b1ZpZXcoeyBibG9jazogJ25lYXJlc3QnLCBpbmxpbmU6ICduZWFyZXN0JyB9KTtcbiAgICB9XG4gIH1cblxuICBicm9hZGNhc3RGb2N1c09uQ29udGFpbmVyKCkge1xuICAgIHRoaXMuZm9jdXNNYW5hZ2VyLmJyb2FkY2FzdEZvY3VzZWROb2RlKHRoaXMubm9kZUlkKTtcbiAgfVxuXG4gIG9uS2V5RG93bihldmVudDogS2V5Ym9hcmRFdmVudCkge1xuICAgIC8vIFR3byByZWFzb25zIHRvIHByZXZlbnQgZGVmYXVsdCBiZWhhdmlvcjpcbiAgICAvLyAxLiB0byBwcmV2ZW50IHNjcm9s