cytoscape-angular-ng17
Version:
Angular 17+ components for cytoscape charting.
319 lines • 32.1 kB
JavaScript
import { Component, EventEmitter, Input, Output, ViewChild, } from '@angular/core';
import { FormInfo } from '../fluid-form/form-info';
import { createStyleCoreFormInfo, createStyleEdgeFieldSets, createStyleNodeFieldSets, StylesheetImpl, } from '../style/style';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common";
import * as i2 from "@angular/forms";
import * as i3 from "primeng/button";
import * as i4 from "primeng/autocomplete";
import * as i5 from "../fluid-form/fluid-form.component";
export class CytoscapeStyleToolComponent {
styleForm;
selectorDropDown;
_styles;
enableAdd = false;
lastValidSelectorModelText;
get styles() {
return this._styles;
}
set styles(styles) {
this._styles = styles;
}
stylesChange = new EventEmitter();
styleSelectorChange = new EventEmitter();
nodeFormInfo;
edgeFormInfo;
coreFormInfo;
selectedStyleSheet;
selectors;
changed = false;
constructor() { }
ngOnInit() {
this.coreFormInfo = createStyleCoreFormInfo();
this.nodeFormInfo = new FormInfo('Node', createStyleNodeFieldSets());
this.edgeFormInfo = new FormInfo('Edge', createStyleEdgeFieldSets());
if (!this.styles) {
this.styles = [new StylesheetImpl()];
}
this.setSelectorsFromStyles(null);
if (this.selectors && this.selectors.length > 0) {
this.selectedStyleSheet = this.selectors[0];
console.log(`this.selectors.length:`, this.selectors.length);
}
}
ngOnChanges(changes) {
console.log('ngOnChanges style changes:', JSON.stringify(changes));
if (changes['styles']) {
this.setSelectorsFromStyles(null);
if (this.selectors && this.selectors.length > 0) {
this.selectedStyleSheet = this.selectors[0];
console.log(`styles updated this.selectors.length:`, this.selectors.length);
}
}
}
ngAfterViewInit() {
// console.debug("ngAfterViewInit")
}
ngAfterViewChecked() {
// console.debug("ngAfterViewChecked")
}
onFormModelChange() {
console.log('onFormModelChange');
this.changed = true;
}
onApplyStyle() {
this.changed = false;
this.stylesChange.emit(this.styles);
}
search(event) {
let searchString = event.query;
this.setSelectorsFromStyles(searchString);
}
setSelectorsFromStyles(searchString) {
this.selectors = this.styles?.filter((stylesheet) => {
return searchString ? stylesheet.selector.includes(searchString) : true;
});
}
onAddSelector() {
const newStylesheetStyle = new StylesheetImpl();
newStylesheetStyle.selector = this.lastValidSelectorModelText || '';
console.log('Adding new style with selector:', this.lastValidSelectorModelText);
this.styles?.unshift(newStylesheetStyle);
this.selectedStyleSheet = newStylesheetStyle;
}
/*
* The param can be a selector object when the user selects a stylesheet entry from the dropdown and changes the
* selection (which will fire a selection change to let the graph, say, focus on the selected node).
* or the param is text if the user is just typing in the field, which doens't change the selector untl the user
* clicks Add.
*/
onSelectorModelChange(param) {
console.log(`selectorModelChanged:${JSON.stringify(param)}`);
const selector = param.selector ? param.selector : param;
const stylesheet = param.selector
? param
: this.getStylesheetForSelector(selector);
if (stylesheet) {
if (this.changed) {
this.onApplyStyle();
}
this.styleSelectorChange.emit(selector);
}
else {
this.enableAdd = this.isValidSelector(selector);
if (this.enableAdd) {
this.lastValidSelectorModelText = selector;
}
else {
this.lastValidSelectorModelText = undefined;
}
}
}
getStylesheetForSelector(selectorName) {
const stylesheet = this.selectors?.find((selector) => selector.selector === selectorName);
return stylesheet;
}
isValidSelector(text) {
console.log('isValidSelector:', text);
if (text?.startsWith('node') ||
text?.startsWith('edge') ||
text?.startsWith('core')) {
const openBracket = text.indexOf('[');
if (openBracket > -1) {
if (text.indexOf(']') < openBracket) {
return false;
}
}
const openQuote = text.indexOf("'");
if (openQuote > -1) {
let closeQuote = text.indexOf("'", openQuote + 1);
if (closeQuote < openQuote) {
return false;
}
}
return true;
}
return false;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.3", ngImport: i0, type: CytoscapeStyleToolComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.3", type: CytoscapeStyleToolComponent, selector: "cytoscape-style-tool", inputs: { styles: "styles" }, outputs: { stylesChange: "stylesChange", styleSelectorChange: "styleSelectorChange" }, viewQueries: [{ propertyName: "styleForm", first: true, predicate: ["styleForm"], descendants: true }, { propertyName: "selectorDropDown", first: true, predicate: ["selectorDropDown"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
<div>
<div style="display: flex;">
<h3 class="style-header">Edit Styles</h3>
</div>
<div class="selectors-container">
<button
class="add-button"
pButton
label=" Add "
[disabled]="!enableAdd"
(click)="onAddSelector()"
></button>
<span> </span>
<label for="selectorDropDown"
><a href="https://js.cytoscape.org/#selectors">Selectors</a></label
>
<p-autoComplete
#selectorDropDown
id="selectorDropDown"
class="selector-drop-down"
placeholder="Selector"
[(ngModel)]="selectedStyleSheet"
[suggestions]="selectors || []"
field="selector"
dataKey="selector"
[completeOnFocus]="true"
[dropdown]="true"
[autofocus]="true"
[style]="{ width: '70%' }"
[inputStyle]="{ width: '70%' }"
(completeMethod)="search($event)"
(ngModelChange)="onSelectorModelChange($event)"
>
</p-autoComplete>
</div>
</div>
<hr />
<div class="apply-div">
<button
class="apply-button"
pButton
label="Apply"
[disabled]="!changed"
(click)="onApplyStyle()"
></button>
<span class="selector-span">Selector </span
><span>{{ selectedStyleSheet?.selector }}</span>
</div>
<ng-container *ngIf="!selectedStyleSheet">
<div>
Please select a
<a href="https://js.cytoscape.org/#selectors">selector</a> above or type
a selector name and click "Add" to create a new stylesheet for that
selector.
</div>
</ng-container>
<ng-container *ngIf="selectedStyleSheet?.selector?.startsWith('node')">
<cyng-fluid-form
[model]="selectedStyleSheet?.style"
[formInfo]="nodeFormInfo"
(modelChange)="onFormModelChange()"
>
</cyng-fluid-form>
</ng-container>
<ng-container *ngIf="selectedStyleSheet?.selector?.startsWith('edge')">
<cyng-fluid-form
[model]="selectedStyleSheet?.style"
[formInfo]="edgeFormInfo"
(modelChange)="onFormModelChange()"
>
</cyng-fluid-form>
</ng-container>
<ng-container *ngIf="selectedStyleSheet?.selector?.startsWith('core')">
<cyng-fluid-form
[model]="selectedStyleSheet?.style"
[formInfo]="coreFormInfo"
(modelChange)="onFormModelChange()"
>
</cyng-fluid-form>
</ng-container>
`, isInline: true, styles: [".selectors-container{display:flex;align-items:baseline;flex-wrap:nowrap;flex-grow:0}label[for=selectorDropDown]{font-size:125%;font-weight:700}.selector-drop-down{flex-grow:1;padding:10px}.selector-span{padding:10px;font-size:125%;font-weight:700}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "label", "icon", "loading"] }, { kind: "component", type: i4.AutoComplete, selector: "p-autoComplete", inputs: ["minLength", "delay", "style", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "disabled", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "maxlength", "name", "required", "size", "appendTo", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "field", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "itemSize", "optionLabel", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover"], outputs: ["completeMethod", "onSelect", "onUnselect", "onFocus", "onBlur", "onDropdownClick", "onClear", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "component", type: i5.FluidFormComponent, selector: "cyng-fluid-form", inputs: ["model", "modelProperty", "formInfo"], outputs: ["modelChange"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.3", ngImport: i0, type: CytoscapeStyleToolComponent, decorators: [{
type: Component,
args: [{ selector: 'cytoscape-style-tool', template: `
<div>
<div style="display: flex;">
<h3 class="style-header">Edit Styles</h3>
</div>
<div class="selectors-container">
<button
class="add-button"
pButton
label=" Add "
[disabled]="!enableAdd"
(click)="onAddSelector()"
></button>
<span> </span>
<label for="selectorDropDown"
><a href="https://js.cytoscape.org/#selectors">Selectors</a></label
>
<p-autoComplete
#selectorDropDown
id="selectorDropDown"
class="selector-drop-down"
placeholder="Selector"
[(ngModel)]="selectedStyleSheet"
[suggestions]="selectors || []"
field="selector"
dataKey="selector"
[completeOnFocus]="true"
[dropdown]="true"
[autofocus]="true"
[style]="{ width: '70%' }"
[inputStyle]="{ width: '70%' }"
(completeMethod)="search($event)"
(ngModelChange)="onSelectorModelChange($event)"
>
</p-autoComplete>
</div>
</div>
<hr />
<div class="apply-div">
<button
class="apply-button"
pButton
label="Apply"
[disabled]="!changed"
(click)="onApplyStyle()"
></button>
<span class="selector-span">Selector </span
><span>{{ selectedStyleSheet?.selector }}</span>
</div>
<ng-container *ngIf="!selectedStyleSheet">
<div>
Please select a
<a href="https://js.cytoscape.org/#selectors">selector</a> above or type
a selector name and click "Add" to create a new stylesheet for that
selector.
</div>
</ng-container>
<ng-container *ngIf="selectedStyleSheet?.selector?.startsWith('node')">
<cyng-fluid-form
[model]="selectedStyleSheet?.style"
[formInfo]="nodeFormInfo"
(modelChange)="onFormModelChange()"
>
</cyng-fluid-form>
</ng-container>
<ng-container *ngIf="selectedStyleSheet?.selector?.startsWith('edge')">
<cyng-fluid-form
[model]="selectedStyleSheet?.style"
[formInfo]="edgeFormInfo"
(modelChange)="onFormModelChange()"
>
</cyng-fluid-form>
</ng-container>
<ng-container *ngIf="selectedStyleSheet?.selector?.startsWith('core')">
<cyng-fluid-form
[model]="selectedStyleSheet?.style"
[formInfo]="coreFormInfo"
(modelChange)="onFormModelChange()"
>
</cyng-fluid-form>
</ng-container>
`, styles: [".selectors-container{display:flex;align-items:baseline;flex-wrap:nowrap;flex-grow:0}label[for=selectorDropDown]{font-size:125%;font-weight:700}.selector-drop-down{flex-grow:1;padding:10px}.selector-span{padding:10px;font-size:125%;font-weight:700}\n"] }]
}], ctorParameters: () => [], propDecorators: { styleForm: [{
type: ViewChild,
args: ['styleForm']
}], selectorDropDown: [{
type: ViewChild,
args: ['selectorDropDown']
}], styles: [{
type: Input
}], stylesChange: [{
type: Output
}], styleSelectorChange: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3l0b3NjYXBlLXN0eWxlLXRvb2wuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvY3l0b3NjYXBlLWFuZ3VsYXIvc3JjL2xpYi9jeXRvc2NhcGUtc3R5bGUtdG9vbC9jeXRvc2NhcGUtc3R5bGUtdG9vbC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUdMLFNBQVMsRUFDVCxZQUFZLEVBRVosS0FBSyxFQUdMLE1BQU0sRUFFTixTQUFTLEdBQ1YsTUFBTSxlQUFlLENBQUM7QUFFdkIsT0FBTyxFQUEyQixRQUFRLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM1RSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLHdCQUF3QixFQUN4Qix3QkFBd0IsRUFDeEIsY0FBYyxHQUNmLE1BQU0sZ0JBQWdCLENBQUM7Ozs7Ozs7QUFxSHhCLE1BQU0sT0FBTywyQkFBMkI7SUFHZCxTQUFTLENBQUM7SUFDSCxnQkFBZ0IsQ0FBQztJQUVoRCxPQUFPLENBQWdDO0lBQ3ZDLFNBQVMsR0FBRyxLQUFLLENBQUM7SUFDViwwQkFBMEIsQ0FBcUI7SUFFdkQsSUFDSSxNQUFNO1FBQ1IsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDO0lBQ3RCLENBQUM7SUFDRCxJQUFJLE1BQU0sQ0FBQyxNQUFxQztRQUM5QyxJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQztJQUN4QixDQUFDO0lBRUQsWUFBWSxHQUFvQyxJQUFJLFlBQVksRUFFN0QsQ0FBQztJQUdKLG1CQUFtQixHQUF5QixJQUFJLFlBQVksRUFBVSxDQUFDO0lBRXZFLFlBQVksQ0FBWTtJQUN4QixZQUFZLENBQVk7SUFDeEIsWUFBWSxDQUFZO0lBRXhCLGtCQUFrQixDQUE4QjtJQUNoRCxTQUFTLENBQWdDO0lBQ3pDLE9BQU8sR0FBRyxLQUFLLENBQUM7SUFFaEIsZ0JBQWUsQ0FBQztJQUVoQixRQUFRO1FBQ04sSUFBSSxDQUFD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Ozs7Ozs7O0dBaUZUOzsyRkFnQ1UsMkJBQTJCO2tCQW5IdkMsU0FBUzsrQkFDRSxzQkFBc0IsWUFDdEI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWlGVDt3REFtQ3VCLFNBQVM7c0JBQWhDLFNBQVM7dUJBQUMsV0FBVztnQkFDUyxnQkFBZ0I7c0JBQTlDLFNBQVM7dUJBQUMsa0JBQWtCO2dCQU96QixNQUFNO3NCQURULEtBQUs7Z0JBUU4sWUFBWTtzQkFEWCxNQUFNO2dCQU1QLG1CQUFtQjtzQkFEbEIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIEFmdGVyVmlld0NoZWNrZWQsXG4gIEFmdGVyVmlld0luaXQsXG4gIENvbXBvbmVudCxcbiAgRXZlbnRFbWl0dGVyLFxuICBIb3N0TGlzdGVuZXIsXG4gIElucHV0LFxuICBPbkNoYW5nZXMsXG4gIE9uSW5pdCxcbiAgT3V0cHV0LFxuICBTaW1wbGVDaGFuZ2VzLFxuICBWaWV3Q2hpbGQsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgU3R5bGVzaGVldCwgU3R5bGVzaGVldFN0eWxlIH0gZnJvbSAnY3l0b3NjYXBlJztcbmltcG9ydCB7IEZpZWxkSW5mbywgRmllbGRzZXRJbmZvLCBGb3JtSW5mbyB9IGZyb20gJy4uL2ZsdWlkLWZvcm0vZm9ybS1pbmZvJztcbmltcG9ydCB7XG4gIGNyZWF0ZVN0eWxlQ29yZUZvcm1JbmZvLFxuICBjcmVhdGVTdHlsZUVkZ2VGaWVsZFNldHMsXG4gIGNyZWF0ZVN0eWxlTm9kZUZpZWxkU2V0cyxcbiAgU3R5bGVzaGVldEltcGwsXG59IGZyb20gJy4uL3N0eWxlL3N0eWxlJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnY3l0b3NjYXBlLXN0eWxlLXRvb2wnLFxuICB0ZW1wbGF0ZTogYFxuICAgIDxkaXY+XG4gICAgICA8ZGl2IHN0eWxlPVwiZGlzcGxheTogZmxleDtcIj5cbiAgICAgICAgPGgzIGNsYXNzPVwic3R5bGUtaGVhZGVyXCI+RWRpdCBTdHlsZXM8L2gzPlxuICAgICAgPC9kaXY+XG4gICAgICA8ZGl2IGNsYXNzPVwic2VsZWN0b3JzLWNvbnRhaW5lclwiPlxuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgY2xhc3M9XCJhZGQtYnV0dG9uXCJcbiAgICAgICAgICBwQnV0dG9uXG4gICAgICAgICAgbGFiZWw9XCImbmJzcDsmbmJzcDtBZGQmbmJzcDsmbmJzcDtcIlxuICAgICAgICAgIFtkaXNhYmxlZF09XCIhZW5hYmxlQWRkXCJcbiAgICAgICAgICAoY2xpY2spPVwib25BZGRTZWxlY3RvcigpXCJcbiAgICAgICAgPjwvYnV0dG9uPlxuICAgICAgICA8c3Bhbj4mbmJzcDsmbmJzcDs8L3NwYW4+XG4gICAgICAgIDxsYWJlbCBmb3I9XCJzZWxlY3RvckRyb3BEb3duXCJcbiAgICAgICAgICA+PGEgaHJlZj1cImh0dHBzOi8vanMuY3l0b3NjYXBlLm9yZy8jc2VsZWN0b3JzXCI+U2VsZWN0b3JzPC9hPjwvbGFiZWxcbiAgICAgICAgPlxuICAgICAgICA8cC1hdXRvQ29tcGxldGVcbiAgICAgICAgICAjc2VsZWN0b3JEcm9wRG93blxuICAgICAgICAgIGlkPVwic2VsZWN0b3JEcm9wRG93blwiXG4gICAgICAgICAgY2xhc3M9XCJzZWxlY3Rvci1kcm9wLWRvd25cIlxuICAgICAgICAgIHBsYWNlaG9sZGVyPVwiU2VsZWN0b3JcIlxuICAgICAgICAgIFsobmdNb2RlbCldPVwic2VsZWN0ZWRTdHlsZVNoZWV0XCJcbiAgICAgICAgICBbc3VnZ2VzdGlvbnNdPVwic2VsZWN0b3JzIHx8IFtdXCJcbiAgICAgICAgICBmaWVsZD1cInNlbGVjdG9yXCJcbiAgICAgICAgICBkYXRhS2V5PVwic2VsZWN0b3JcIlxuICAgICAgICAgIFtjb21wbGV0ZU9uRm9jdXNdPVwidHJ1ZVwiXG4gICAgICAgICAgW2Ryb3Bkb3duXT1cInRydWVcIlxuICAgICAgICAgIFthdXRvZm9jdXNdPVwidHJ1ZVwiXG4gICAgICAgICAgW3N0eWxlXT1cInsgd2lkdGg6ICc3MCUnIH1cIlxuICAgICAgICAgIFtpbnB1dFN0eWxlXT1cInsgd2lkdGg6ICc3MCUnIH1cIlxuICAgICAgICAgIChjb21wbGV0ZU1ldGhvZCk9XCJzZWFyY2goJGV2ZW50KVwiXG4gICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwib25TZWxlY3Rvck1vZGVsQ2hhbmdlKCRldmVudClcIlxuICAgICAgICA+XG4gICAgICAgIDwvcC1hdXRvQ29tcGxldGU+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgICA8aHIgLz5cbiAgICA8ZGl2IGNsYXNzPVwiYXBwbHktZGl2XCI+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIGNsYXNzPVwiYXBwbHktYnV0dG9uXCJcbiAgICAgICAgcEJ1dHRvblxuICAgICAgICBsYWJlbD1cIkFwcGx5XCJcbiAgICAgICAgW2Rpc2FibGVkXT1cIiFjaGFuZ2VkXCJcbiAgICAgICAgKGNsaWNrKT1cIm9uQXBwbHlTdHlsZSgpXCJcbiAgICAgID48L2J1dHRvbj5cbiAgICAgIDxzcGFuIGNsYXNzPVwic2VsZWN0b3Itc3BhblwiPlNlbGVjdG9yIDwvc3BhblxuICAgICAgPjxzcGFuPnt7IHNlbGVjdGVkU3R5bGVTaGVldD8uc2VsZWN0b3IgfX08L3NwYW4+XG4gICAgPC9kaXY+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIiFzZWxlY3RlZFN0eWxlU2hlZXRcIj5cbiAgICAgIDxkaXY+XG4gICAgICAgIFBsZWFzZSBzZWxlY3QgYVxuICAgICAgICA8YSBocmVmPVwiaHR0cHM6Ly9qcy5jeXRvc2NhcGUub3JnLyNzZWxlY3RvcnNcIj5zZWxlY3RvcjwvYT4gYWJvdmUgb3IgdHlwZVxuICAgICAgICBhIHNlbGVjdG9yIG5hbWUgYW5kIGNsaWNrIFwiQWRkXCIgdG8gY3JlYXRlIGEgbmV3IHN0eWxlc2hlZXQgZm9yIHRoYXRcbiAgICAgICAgc2VsZWN0b3IuXG4gICAgICA8L2Rpdj5cbiAgICA8L25nLWNvbnRhaW5lcj5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwic2VsZWN0ZWRTdHlsZVNoZWV0Py5zZWxlY3Rvcj8uc3RhcnRzV2l0aCgnbm9kZScpXCI+XG4gICAgICA8Y3luZy1mbHVpZC1mb3JtXG4gICAgICAgIFttb2RlbF09XCJzZWxlY3RlZFN0eWxlU2hlZXQ/LnN0eWxlXCJcbiAgICAgICAgW2Zvcm1JbmZvXT1cIm5vZGVGb3JtSW5mb1wiXG4gICAgICAgIChtb2RlbENoYW5nZSk9XCJvbkZvcm1Nb2RlbENoYW5nZSgpXCJcbiAgICAgID5cbiAgICAgIDwvY3luZy1mbHVpZC1mb3JtPlxuICAgIDwvbmctY29udGFpbmVyPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJzZWxlY3RlZFN0eWxlU2hlZXQ/LnNlbGVjdG9yPy5zdGFydHNXaXRoKCdlZGdlJylcIj5cbiAgICAgIDxjeW5nLWZsdWlkLWZvcm1cbiAgICAgICAgW21vZGVsXT1cInNlbGVjdGVkU3R5bGVTaGVldD8uc3R5bGVcIlxuICAgICAgICBbZm9ybUluZm9dPVwiZWRnZUZvcm1JbmZvXCJcbiAgICAgICAgKG1vZGVsQ2hhbmdlKT1cIm9uRm9ybU1vZGVsQ2hhbmdlKClcIlxuICAgICAgPlxuICAgICAgPC9jeW5nLWZsdWlkLWZvcm0+XG4gICAgPC9uZy1jb250YWluZXI+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cInNlbGVjdGVkU3R5bGVTaGVldD8uc2VsZWN0b3I/LnN0YXJ0c1dpdGgoJ2NvcmUnKVwiPlxuICAgICAgPGN5bmctZmx1aWQtZm9ybVxuICAgICAgICBbbW9kZWxdPVwic2VsZWN0ZWRTdHlsZVNoZWV0Py5zdHlsZVwiXG4gICAgICAgIFtmb3JtSW5mb109XCJjb3JlRm9ybUluZm9cIlxuICAgICAgICAobW9kZWxDaGFuZ2UpPVwib25Gb3JtTW9kZWxDaGFuZ2UoKVwiXG4gICAgICA+XG4gICAgICA8L2N5bmctZmx1aWQtZm9ybT5cbiAgICA8L25nLWNvbnRhaW5lcj5cbiAgYCxcbiAgc3R5bGVzOiBbXG4gICAgYFxuICAgICAgLnNlbGVjdG9ycy1jb250YWluZXIge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBhbGlnbi1pdGVtczogYmFzZWxpbmU7XG4gICAgICAgIGZsZXgtd3JhcDogbm93cmFwO1xuICAgICAgICBmbGV4LWdyb3c6IDA7XG4gICAgICB9XG5cbiAgICAgIGxhYmVsW2Zvcj0nc2VsZWN0b3JEcm9wRG93biddIHtcbiAgICAgICAgZm9udC1zaXplOiAxMjUlO1xuICAgICAgICBmb250LXdlaWdodDogYm9sZDtcbiAgICAgIH1cblxuICAgICAgLnNlbGVjdG9yLWRyb3AtZG93biB7XG4gICAgICAgIGZsZXgtZ3JvdzogMTtcbiAgICAgICAgcGFkZGluZzogMTBweDtcbiAgICAgIH1cblxuICAgICAgLnNlbGVjdG9yLXNwYW4ge1xuICAgICAgICBwYWRkaW5nOiAxMHB4O1xuICAgICAgICBmb250LXNpemU6IDEyNSU7XG4gICAgICAgIGZvbnQtd2VpZ2h0OiBib2xkO1xuICAgICAgfVxuICAgICAgLmFkZC1idXR0b24ge1xuICAgICAgfVxuICAgICAgLmFwcGx5LWJ1dHRvbiB7XG4gICAgICB9XG4gICAgYCxcbiAgXSxcbn0pXG5leHBvcnQgY2xhc3MgQ3l0b3NjYXBlU3R5bGVUb29sQ29tcG9uZW50XG4gIGltcGxlbWVudHMgT25Jbml0LCBPbkNoYW5nZXMsIEFmdGVyVmlld0luaXQsIEFmdGVyVmlld0NoZWNrZWRcbntcbiAgQFZpZXdDaGlsZCgnc3R5bGVGb3JtJykgc3R5bGVGb3JtO1xuICBAVmlld0NoaWxkKCdzZWxlY3RvckRyb3BEb3duJykgc2VsZWN0b3JEcm9wRG93bjtcblxuICBfc3R5bGVzOiBTdHlsZXNoZWV0U3R5bGVbXSB8IHVuZGVmaW5lZDtcbiAgZW5hYmxlQWRkID0gZmFsc2U7XG4gIHByaXZhdGUgbGFzdFZhbGlkU2VsZWN0b3JNb2RlbFRleHQ6IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICBASW5wdXQoKVxuICBnZXQgc3R5bGVzKCk6IFN0eWxlc2hlZXRTdHlsZVtdIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5fc3R5bGVzO1xuICB9XG4gIHNldCBzdHlsZXMoc3R5bGVzOiBTdHlsZXNoZWV0U3R5bGVbXSB8IHVuZGVmaW5lZCkge1xuICAgIHRoaXMuX3N0eWxlcyA9IHN0eWxlcztcbiAgfVxuICBAT3V0cHV0KClcbiAgc3R5bGVzQ2hhbmdlOiBFdmVudEVtaXR0ZXI8U3R5bGVzaGVldFN0eWxlW10+ID0gbmV3IEV2ZW50RW1pdHRlcjxcbiAgICBTdHlsZXNoZWV0U3R5bGVbXVxuICA+KCk7XG5cbiAgQE91dHB1dCgpXG4gIHN0eWxlU2VsZWN0b3JDaGFuZ2U6IEV2ZW50RW1pdHRlcjxzdHJpbmc+ID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG5cbiAgbm9kZUZvcm1JbmZvITogRm9ybUluZm87XG4gIGVkZ2VGb3JtSW5mbyE6IEZvcm1JbmZvO1xuICBjb3JlRm9ybUluZm8hOiBGb3JtSW5mbztcblxuICBzZWxlY3RlZFN0eWxlU2hlZXQ6IFN0eWxlc2hlZXRTdHlsZSB8IHVuZGVmaW5lZDtcbiAgc2VsZWN0b3JzOiBTdHlsZXNoZWV0U3R5bGVbXSB8IHVuZGVmaW5lZDtcbiAgY2hhbmdlZCA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKCkge31cblxuICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICB0aGlzLmNvcmVGb3JtSW5mbyA9IGNyZWF0ZVN0eWxlQ29yZUZvcm1JbmZvKCk7XG4gICAgdGhpcy5ub2RlRm9ybUluZm8gPSBuZXcgRm9ybUluZm8oJ05vZGUnLCBjcmVhdGVTdHlsZU5vZGVGaWVsZFNldHMoKSk7XG4gICAgdGhpcy5lZGdlRm9ybUluZm8gPSBuZXcgRm9ybUluZm8oJ0VkZ2UnLCBjcmVhdGVTdHlsZUVkZ2VGaWVsZFNldHMoKSk7XG4gICAgaWYgKCF0aGlzLnN0eWxlcykge1xuICAgICAgdGhpcy5zdHlsZXMgPSBbbmV3IFN0eWxlc2hlZXRJbXBsKCldO1xuICAgIH1cbiAgICB0aGlzLnNldFNlbGVjdG9yc0Zyb21TdHlsZXMobnVsbCk7XG4gICAgaWYgKHRoaXMuc2VsZWN0b3JzICYmIHRoaXMuc2VsZWN0b3JzLmxlbmd0aCA+IDApIHtcbiAgICAgIHRoaXMuc2VsZWN0ZWRTdHlsZVNoZWV0ID0gdGhpcy5zZWxlY3RvcnNbMF07XG4gICAgICBjb25zb2xlLmxvZyhgdGhpcy5zZWxlY3RvcnMubGVuZ3RoOmAsIHRoaXMuc2VsZWN0b3JzLmxlbmd0aCk7XG4gICAgfVxuICB9XG5cbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgIGNvbnNvbGUubG9nKCduZ09uQ2hhbmdlcyBzdHlsZSBjaGFuZ2VzOicsIEpTT04uc3RyaW5naWZ5KGNoYW5nZXMpKTtcbiAgICBpZiAoY2hhbmdlc1snc3R5bGVzJ10pIHtcbiAgICAgIHRoaXMuc2V0U2VsZWN0b3JzRnJvbVN0eWxlcyhudWxsKTtcbiAgICAgIGlmICh0aGlzLnNlbGVjdG9ycyAmJiB0aGlzLnNlbGVjdG9ycy5sZW5ndGggPiAwKSB7XG4gICAgICAgIHRoaXMuc2VsZWN0ZWRTdHlsZVNoZWV0ID0gdGhpcy5zZWxlY3RvcnNbMF07XG4gICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgIGBzdHlsZXMgdXBkYXRlZCB0aGlzLnNlbGVjdG9ycy5sZW5ndGg6YCxcbiAgICAgICAgICB0aGlzLnNlbGVjdG9ycy5sZW5ndGhcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBuZ0FmdGVyVmlld0luaXQoKTogdm9pZCB7XG4gICAgLy8gY29uc29sZS5kZWJ1ZyhcIm5nQWZ0ZXJWaWV3SW5pdFwiKVxuICB9XG5cbiAgbmdBZnRlclZpZXdDaGVja2VkKCk6IHZvaWQge1xuICAgIC8vIGNvbnNvbGUuZGVidWcoXCJuZ0FmdGVyVmlld0NoZWNrZWRcIilcbiAgfVxuXG4gIG9uRm9ybU1vZGVsQ2hhbmdlKCkge1xuICAgIGNvbnNvbGUubG9nKCdvbkZvcm1Nb2RlbENoYW5nZScpO1xuICAgIHRoaXMuY2hhbmdlZCA9IHRydWU7XG4gIH1cblxuICBvbkFwcGx5U3R5bGUoKSB7XG4gICAgdGhpcy5jaGFuZ2VkID0gZmFsc2U7XG4gICAgdGhpcy5zdHlsZXNDaGFuZ2UuZW1pdCh0aGlzLnN0eWxlcyk7XG4gIH1cblxuICBzZWFyY2goZXZlbnQpIHtcbiAgICBsZXQgc2VhcmNoU3RyaW5nID0gZXZlbnQucXVlcnk7XG4gICAgdGhpcy5zZXRTZWxlY3RvcnNGcm9tU3R5bGVzKHNlYXJjaFN0cmluZyk7XG4gIH1cblxuICBwcml2YXRlIHNldFNlbGVjdG9yc0Zyb21TdHlsZXMoc2VhcmNoU3RyaW5nOiBzdHJpbmcgfCBudWxsKSB7XG4gICAgdGhpcy5zZWxlY3RvcnMgPSB0aGlzLnN0eWxlcz8uZmlsdGVyKChzdHlsZXNoZWV0OiBTdHlsZXNoZWV0U3R5bGUpID0+IHtcbiAgICAgIHJldHVybiBzZWFyY2hTdHJpbmcgPyBzdHlsZXNoZWV0LnNlbGVjdG9yLmluY2x1ZGVzKHNlYXJjaFN0cmluZykgOiB0cnVlO1xuICAgIH0pO1xuICB9XG5cbiAgb25BZGRTZWxlY3RvcigpIHtcbiAgICBjb25zdCBuZXdTdHlsZXNoZWV0U3R5bGU6IFN0eWxlc2hlZXRTdHlsZSA9IG5ldyBTdHlsZXNoZWV0SW1wbCgpO1xuICAgIG5ld1N0eWxlc2hlZXRTdHlsZS5zZWxlY3RvciA9IHRoaXMubGFzdFZhbGlkU2VsZWN0b3JNb2RlbFRleHQgfHwgJyc7XG4gICAgY29uc29sZS5sb2coXG4gICAgICAnQWRkaW5nIG5ldyBzdHlsZSB3aXRoIHNlbGVjdG9yOicsXG4gICAgICB0aGlzLmxhc3RWYWxpZFNlbGVjdG9yTW9kZWxUZXh0XG4gICAgKTtcbiAgICB0aGlzLnN0eWxlcz8udW5zaGlmdChuZXdTdHlsZXNoZWV0U3R5bGUpO1xuICAgIHRoaXMuc2VsZWN0ZWRTdHlsZVNoZWV0ID0gbmV3U3R5bGVzaGVldFN0eWxlO1xuICB9XG5cbiAgLypcbiAgICogVGhlIHBhcmFtIGNhbiBiZSBhIHNlbGVjdG9yIG9iamVjdCB3aGVuIHRoZSB1c2VyIHNlbGVjdHMgYSBzdHlsZXNoZWV0IGVudHJ5IGZyb20gdGhlIGRyb3Bkb3duIGFuZCBjaGFuZ2VzIHRoZVxuICAgKiBzZWxlY3Rpb24gKHdoaWNoIHdpbGwgZmlyZSBhIHNlbGVjdGlvbiBjaGFuZ2UgdG8gbGV0IHRoZSBncmFwaCwgc2F5LCBmb2N1cyBvbiB0aGUgc2VsZWN0ZWQgbm9kZSkuXG4gICAqIG9yIHRoZSBwYXJhbSBpcyB0ZXh0IGlmIHRoZSB1c2VyIGlzIGp1c3QgdHlwaW5nIGluIHRoZSBmaWVsZCwgd2hpY2ggZG9lbnMndCBjaGFuZ2UgdGhlIHNlbGVjdG9yIHVudGwgdGhlIHVzZXJcbiAgICogY2xpY2tzIEFkZC5cbiAgICovXG4gIG9uU2VsZWN0b3JNb2RlbENoYW5nZShwYXJhbTogYW55KSB7XG4gICAgY29uc29sZS5sb2coYHNlbGVjdG9yTW9kZWxDaGFuZ2VkOiR7SlNPTi5zdHJpbmdpZnkocGFyYW0pfWApO1xuICAgIGNvbnN0IHNlbGVjdG9yID0gcGFyYW0uc2VsZWN0b3IgPyBwYXJhbS5zZWxlY3RvciA6IHBhcmFtO1xuICAgIGNvbnN0IHN0eWxlc2hlZXQgPSBwYXJhbS5zZWxlY3RvclxuICAgICAgPyBwYXJhbVxuICAgICAgOiB0aGlzLmdldFN0eWxlc2hlZXRGb3JTZWxlY3RvcihzZWxlY3Rvcik7XG4gICAgaWYgKHN0eWxlc2hlZXQpIHtcbiAgICAgIGlmICh0aGlzLmNoYW5nZWQpIHtcbiAgICAgICAgdGhpcy5vbkFwcGx5U3R5bGUoKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuc3R5bGVTZWxlY3RvckNoYW5nZS5lbWl0KHNlbGVjdG9yKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5lbmFibGVBZGQgPSB0aGlzLmlzVmFsaWRTZWxlY3RvcihzZWxlY3Rvcik7XG4gICAgICBpZiAodGhpcy5lbmFibGVBZGQpIHtcbiAgICAgICAgdGhpcy5sYXN0VmFsaWRTZWxlY3Rvck1vZGVsVGV4dCA9IHNlbGVjdG9yO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5sYXN0VmFsaWRTZWxlY3Rvck1vZGVsVGV4dCA9IHVuZGVmaW5lZDtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBnZXRTdHlsZXNoZWV0Rm9yU2VsZWN0b3Ioc2VsZWN0b3JOYW1lKTogU3R5bGVzaGVldCB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3Qgc3R5bGVzaGVldCA9IHRoaXMuc2VsZWN0b3JzPy5maW5kKFxuICAgICAgKHNlbGVjdG9yKSA9PiBzZWxlY3Rvci5zZWxlY3RvciA9PT0gc2VsZWN0b3JOYW1lXG4gICAgKTtcbiAgICByZXR1cm4gc3R5bGVzaGVldDtcbiAgfVxuXG4gIGlzVmFsaWRTZWxlY3Rvcih0ZXh0OiBzdHJpbmcpIHtcbiAgICBjb25zb2xlLmxvZygnaXNWYWxpZFNlbGVjdG9yOicsIHRleHQpO1xuICAgIGlmIChcbiAgICAgIHRleHQ/LnN0YXJ0c1dpdGgoJ25vZGUnKSB8fFxuICAgICAgdGV4dD8uc3RhcnRzV2l0aCgnZWRnZScpIHx8XG4gICAgICB0ZXh0Py5zdGFydHNXaXRoKCdjb3JlJylcbiAgICApIHtcbiAgICAgIGNvbnN0IG9wZW5CcmFja2V0ID0gdGV4dC5pbmRleE9mKCdbJyk7XG4gICAgICBpZiAob3BlbkJyYWNrZXQgPiAtMSkge1xuICAgICAgICBpZiAodGV4dC5pbmRleE9mKCddJykgPCBvcGVuQnJhY2tldCkge1xuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgY29uc3Qgb3BlblF1b3RlID0gdGV4dC5pbmRleE9mKFwiJ1wiKTtcbiAgICAgIGlmIChvcGVuUXVvdGUgPiAtMSkge1xuICAgICAgICBsZXQgY2xvc2VRdW90ZSA9IHRleHQuaW5kZXhPZihcIidcIiwgb3BlblF1b3RlICsgMSk7XG4gICAgICAgIGlmIChjbG9zZVF1b3RlIDwgb3BlblF1b3RlKSB7XG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG4iXX0=