ngx-editor
Version:
The Rich Text Editor for Angular, Built on ProseMirror
122 lines • 22.6 kB
JavaScript
import { Component, HostListener, } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Link as LinkCommand } from '../MenuCommands';
import * as i0 from "@angular/core";
import * as i1 from "../../../editor.service";
import * as i2 from "../menu.service";
import * as i3 from "@angular/common";
import * as i4 from "@angular/forms";
import * as i5 from "../../../pipes/sanitize/sanitize-html.pipe";
export class LinkComponent {
constructor(el, ngxeService, menuService) {
this.el = el;
this.ngxeService = ngxeService;
this.menuService = menuService;
this.showPopup = false;
this.isActive = false;
this.canExecute = true;
this.setText = () => {
const { state: { selection, doc } } = this.editorView;
const { empty, from, to } = selection;
const selectedText = !empty ? doc.textBetween(from, to) : '';
if (selectedText) {
this.text.patchValue(selectedText);
this.text.disable();
}
};
this.update = (view) => {
const { state } = view;
this.isActive = LinkCommand.isActive(state, { strict: false });
this.canExecute = LinkCommand.canExecute(state);
};
}
get icon() {
return this.ngxeService.getIcon(this.isActive ? 'unlink' : 'link');
}
get title() {
return this.ngxeService.locals.get(this.isActive ? 'removeLink' : 'insertLink');
}
get href() {
return this.form.get('href');
}
get text() {
return this.form.get('text');
}
onDocumentClick(e) {
if (!this.el.nativeElement.contains(e.target) && this.showPopup) {
this.hideForm();
}
}
getLabel(key) {
return this.ngxeService.locals.get(key);
}
hideForm() {
this.showPopup = false;
this.form.reset({
href: '',
text: '',
openInNewTab: true,
});
this.text.enable();
}
onMouseDown(e) {
if (e.button !== 0) {
return;
}
const { state, dispatch } = this.editorView;
if (this.isActive) {
LinkCommand.remove(state, dispatch);
return;
}
this.showPopup = !this.showPopup;
if (this.showPopup) {
this.setText();
}
}
insertLink(e) {
e.preventDefault();
const { text, href, openInNewTab } = this.form.getRawValue();
const { dispatch, state } = this.editorView;
const { selection } = state;
const attrs = {
title: href,
href,
target: openInNewTab ? '_blank' : '_self',
};
if (selection.empty) {
LinkCommand.insert(text, attrs)(state, dispatch);
this.editorView.focus();
}
else {
LinkCommand.update(attrs)(state, dispatch);
}
this.hideForm();
}
ngOnInit() {
this.editorView = this.menuService.editor.view;
this.form = new FormGroup({
href: new FormControl('', [
Validators.required,
Validators.pattern(this.menuService.editor.linkValidationPattern),
]),
text: new FormControl('', [Validators.required]),
openInNewTab: new FormControl(true),
});
this.updateSubscription = this.menuService.editor.update.subscribe((view) => {
this.update(view);
});
}
ngOnDestroy() {
this.updateSubscription.unsubscribe();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.1", ngImport: i0, type: LinkComponent, deps: [{ token: i0.ElementRef }, { token: i1.NgxEditorService }, { token: i2.MenuService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.1", type: LinkComponent, selector: "ngx-link", host: { listeners: { "document:mousedown": "onDocumentClick($event)" } }, ngImport: i0, template: "<div class=\"NgxEditor__MenuItem--IconContainer\" [class.NgxEditor__MenuItem--Active]=\"isActive || showPopup\"\n [class.NgxEditor--Disabled]=\"!canExecute\" [innerHTML]=\"icon | sanitizeHtml\" (mousedown)=\"onMouseDown($event)\"\n [title]=\"title | async\">\n</div>\n\n<!-- popup -->\n<div *ngIf=\"showPopup\" class=\"NgxEditor__Popup\">\n <form class=\"NgxEditor__Popup--Form\" [formGroup]=\"form\" (ngSubmit)=\"insertLink($event)\">\n\n <div class=\"NgxEditor__Popup--FormGroup\">\n <div class=\"NgxEditor__Popup--Col\">\n <label class=\"NgxEditor__Popup--Label\">{{getLabel('url') | async}}</label>\n <input type=\"href\" id=\"href\" formControlName=\"href\" autofocus autocomplete=\"off\" />\n <div *ngIf=\"href.touched && href.invalid\" class=\"NgxEditor__HelpText NgxEditor__HelpText--Error\">\n {{ href.errors?.['pattern'] && 'Please enter valid url.' }}\n </div>\n </div>\n </div>\n\n <div class=\"NgxEditor__Popup--FormGroup\">\n <div class=\"NgxEditor__Popup--Col\">\n <label class=\"NgxEditor__Popup--Label\">{{getLabel('text') | async}}</label>\n <input type=\"text\" formControlName=\"text\" autocomplete=\"off\" />\n <div *ngIf=\"text.touched && text.invalid\" class=\"NgxEditor__HelpText NgxEditor__HelpText--Error\">\n {{ text.errors?.['required'] && 'This is required' }}\n </div>\n </div>\n </div>\n\n <div class=\"NgxEditor__Popup--FormGroup\">\n <div class=\"NgxEditor__Popup--Col\">\n <label>\n <input type=\"checkbox\" formControlName=\"openInNewTab\" />\n {{getLabel('openInNewTab') | async}}\n </label>\n </div>\n </div>\n\n <button type=\"submit\" [disabled]=\"!form.valid\">{{getLabel('insert') | async}}</button>\n\n </form>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i4.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.SanitizeHtmlPipe, name: "sanitizeHtml" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.1", ngImport: i0, type: LinkComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-link', template: "<div class=\"NgxEditor__MenuItem--IconContainer\" [class.NgxEditor__MenuItem--Active]=\"isActive || showPopup\"\n [class.NgxEditor--Disabled]=\"!canExecute\" [innerHTML]=\"icon | sanitizeHtml\" (mousedown)=\"onMouseDown($event)\"\n [title]=\"title | async\">\n</div>\n\n<!-- popup -->\n<div *ngIf=\"showPopup\" class=\"NgxEditor__Popup\">\n <form class=\"NgxEditor__Popup--Form\" [formGroup]=\"form\" (ngSubmit)=\"insertLink($event)\">\n\n <div class=\"NgxEditor__Popup--FormGroup\">\n <div class=\"NgxEditor__Popup--Col\">\n <label class=\"NgxEditor__Popup--Label\">{{getLabel('url') | async}}</label>\n <input type=\"href\" id=\"href\" formControlName=\"href\" autofocus autocomplete=\"off\" />\n <div *ngIf=\"href.touched && href.invalid\" class=\"NgxEditor__HelpText NgxEditor__HelpText--Error\">\n {{ href.errors?.['pattern'] && 'Please enter valid url.' }}\n </div>\n </div>\n </div>\n\n <div class=\"NgxEditor__Popup--FormGroup\">\n <div class=\"NgxEditor__Popup--Col\">\n <label class=\"NgxEditor__Popup--Label\">{{getLabel('text') | async}}</label>\n <input type=\"text\" formControlName=\"text\" autocomplete=\"off\" />\n <div *ngIf=\"text.touched && text.invalid\" class=\"NgxEditor__HelpText NgxEditor__HelpText--Error\">\n {{ text.errors?.['required'] && 'This is required' }}\n </div>\n </div>\n </div>\n\n <div class=\"NgxEditor__Popup--FormGroup\">\n <div class=\"NgxEditor__Popup--Col\">\n <label>\n <input type=\"checkbox\" formControlName=\"openInNewTab\" />\n {{getLabel('openInNewTab') | async}}\n </label>\n </div>\n </div>\n\n <button type=\"submit\" [disabled]=\"!form.valid\">{{getLabel('insert') | async}}</button>\n\n </form>\n</div>\n" }]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.NgxEditorService }, { type: i2.MenuService }]; }, propDecorators: { onDocumentClick: [{
type: HostListener,
args: ['document:mousedown', ['$event']]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGluay5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtZWRpdG9yL3NyYy9saWIvbW9kdWxlcy9tZW51L2xpbmsvbGluay5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtZWRpdG9yL3NyYy9saWIvbW9kdWxlcy9tZW51L2xpbmsvbGluay5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUNULFlBQVksR0FDYixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQW1CLFdBQVcsRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFNckYsT0FBTyxFQUFFLElBQUksSUFBSSxXQUFXLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQzs7Ozs7OztBQVN0RCxNQUFNLE9BQU8sYUFBYTtJQVN4QixZQUNVLEVBQWMsRUFDZCxXQUE2QixFQUM3QixXQUF3QjtRQUZ4QixPQUFFLEdBQUYsRUFBRSxDQUFZO1FBQ2QsZ0JBQVcsR0FBWCxXQUFXLENBQWtCO1FBQzdCLGdCQUFXLEdBQVgsV0FBVyxDQUFhO1FBWGxDLGNBQVMsR0FBRyxLQUFLLENBQUM7UUFDbEIsYUFBUSxHQUFHLEtBQUssQ0FBQztRQUNqQixlQUFVLEdBQUcsSUFBSSxDQUFDO1FBa0VWLFlBQU8sR0FBRyxHQUFHLEVBQUU7WUFDckIsTUFBTSxFQUFFLEtBQUssRUFBRSxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsRUFBRSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUM7WUFDdEQsTUFBTSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLEdBQUcsU0FBUyxDQUFDO1lBQ3RDLE1BQU0sWUFBWSxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLElBQUksRUFB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QztTQUNwQyxDQUFDLENBQUM7UUFFSCxJQUFJLENBQUMsa0JBQWtCLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDLElBQWdCLEVBQUUsRUFBRTtZQUN0RixJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3BCLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELFdBQVc7UUFDVCxJQUFJLENBQUMsa0JBQWtCLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDeEMsQ0FBQzs4R0E5SFUsYUFBYTtrR0FBYixhQUFhLDBIQ25CMUIsaXlEQTBDQTs7MkZEdkJhLGFBQWE7a0JBTnpCLFNBQVM7K0JBQ0UsVUFBVTswSkFvQzRCLGVBQWU7c0JBQTlELFlBQVk7dUJBQUMsb0JBQW9CLEVBQUUsQ0FBQyxRQUFRLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBDb21wb25lbnQsIEVsZW1lbnRSZWYsXG4gIEhvc3RMaXN0ZW5lciwgT25EZXN0cm95LCBPbkluaXQsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQWJzdHJhY3RDb250cm9sLCBGb3JtQ29udHJvbCwgRm9ybUdyb3VwLCBWYWxpZGF0b3JzIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgRWRpdG9yVmlldyB9IGZyb20gJ3Byb3NlbWlycm9yLXZpZXcnO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSwgU3Vic2NyaXB0aW9uIH0gZnJvbSAncnhqcyc7XG5cbmltcG9ydCB7IE5neEVkaXRvclNlcnZpY2UgfSBmcm9tICcuLi8uLi8uLi9lZGl0b3Iuc2VydmljZSc7XG5pbXBvcnQgeyBNZW51U2VydmljZSB9IGZyb20gJy4uL21lbnUuc2VydmljZSc7XG5pbXBvcnQgeyBMaW5rIGFzIExpbmtDb21tYW5kIH0gZnJvbSAnLi4vTWVudUNvbW1hbmRzJztcbmltcG9ydCB7IEhUTUwgfSBmcm9tICcuLi8uLi8uLi90cnVzdGVkVHlwZXNVdGlsJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbmd4LWxpbmsnLFxuICB0ZW1wbGF0ZVVybDogJy4vbGluay5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL2xpbmsuY29tcG9uZW50LnNjc3MnXSxcbn0pXG5cbmV4cG9ydCBjbGFzcyBMaW5rQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3kge1xuICBzaG93UG9wdXAgPSBmYWxzZTtcbiAgaXNBY3RpdmUgPSBmYWxzZTtcbiAgY2FuRXhlY3V0ZSA9IHRydWU7XG4gIGZvcm06IEZvcm1Hcm91cDtcblxuICBwcml2YXRlIGVkaXRvclZpZXc6IEVkaXRvclZpZXc7XG4gIHByaXZhdGUgdXBkYXRlU3Vic2NyaXB0aW9uOiBTdWJzY3JpcHRpb247XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBlbDogRWxlbWVudFJlZixcbiAgICBwcml2YXRlIG5neGVTZXJ2aWNlOiBOZ3hFZGl0b3JTZXJ2aWNlLFxuICAgIHByaXZhdGUgbWVudVNlcnZpY2U6IE1lbnVTZXJ2aWNlLFxuICApIHsgfVxuXG4gIGdldCBpY29uKCk6IEhUTUwge1xuICAgIHJldHVybiB0aGlzLm5neGVTZXJ2aWNlLmdldEljb24odGhpcy5pc0FjdGl2ZSA/ICd1bmxpbmsnIDogJ2xpbmsnKTtcbiAgfVxuXG4gIGdldCB0aXRsZSgpOiBPYnNlcnZhYmxlPHN0cmluZz4ge1xuICAgIHJldHVybiB0aGlzLm5neGVTZXJ2aWNlLmxvY2Fscy5nZXQodGhpcy5pc0FjdGl2ZSA/ICdyZW1vdmVMaW5rJyA6ICdpbnNlcnRMaW5rJyk7XG4gIH1cblxuICBnZXQgaHJlZigpOiBBYnN0cmFjdENvbnRyb2wge1xuICAgIHJldHVybiB0aGlzLmZvcm0uZ2V0KCdocmVmJyk7XG4gIH1cblxuICBnZXQgdGV4dCgpOiBBYnN0cmFjdENvbnRyb2wge1xuICAgIHJldHVybiB0aGlzLmZvcm0uZ2V0KCd0ZXh0Jyk7XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdkb2N1bWVudDptb3VzZWRvd24nLCBbJyRldmVudCddKSBvbkRvY3VtZW50Q2xpY2soZTogTW91c2VFdmVudCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5lbC5uYXRpdmVFbGVtZW50LmNvbnRhaW5zKGUudGFyZ2V0KSAmJiB0aGlzLnNob3dQb3B1cCkge1xuICAgICAgdGhpcy5oaWRlRm9ybSgpO1xuICAgIH1cbiAgfVxuXG4gIGdldExhYmVsKGtleTogc3RyaW5nKTogT2JzZXJ2YWJsZTxzdHJpbmc+IHtcbiAgICByZXR1cm4gdGhpcy5uZ3hlU2VydmljZS5sb2NhbHMuZ2V0KGtleSk7XG4gIH1cblxuICBwcml2YXRlIGhpZGVGb3JtKCk6IHZvaWQge1xuICAgIHRoaXMuc2hvd1BvcHVwID0gZmFsc2U7XG4gICAgdGhpcy5mb3JtLnJlc2V0KHtcbiAgICAgIGhyZWY6ICcnLFxuICAgICAgdGV4dDogJycsXG4gICAgICBvcGVuSW5OZXdUYWI6IHRydWUsXG4gICAgfSk7XG4gICAgdGhpcy50ZXh0LmVuYWJsZSgpO1xuICB9XG5cbiAgb25Nb3VzZURvd24oZTogTW91c2VFdmVudCk6IHZvaWQge1xuICAgIGlmIChlLmJ1dHRvbiAhPT0gMCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHsgc3RhdGUsIGRpc3BhdGNoIH0gPSB0aGlzLmVkaXRvclZpZXc7XG5cbiAgICBpZiAodGhpcy5pc0FjdGl2ZSkge1xuICAgICAgTGlua0NvbW1hbmQucmVtb3ZlKHN0YXRlLCBkaXNwYXRjaCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5zaG93UG9wdXAgPSAhdGhpcy5zaG93UG9wdXA7XG4gICAgaWYgKHRoaXMuc2hvd1BvcHVwKSB7XG4gICAgICB0aGlzLnNldFRleHQoKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHNldFRleHQgPSAoKSA9PiB7XG4gICAgY29uc3QgeyBzdGF0ZTogeyBzZWxlY3Rpb24sIGRvYyB9IH0gPSB0aGlzLmVkaXRvclZpZXc7XG4gICAgY29uc3QgeyBlbXB0eSwgZnJvbSwgdG8gfSA9IHNlbGVjdGlvbjtcbiAgICBjb25zdCBzZWxlY3RlZFRleHQgPSAhZW1wdHkgPyBkb2MudGV4dEJldHdlZW4oZnJvbSwgdG8pIDogJyc7XG5cbiAgICBpZiAoc2VsZWN0ZWRUZXh0KSB7XG4gICAgICB0aGlzLnRleHQucGF0Y2hWYWx1ZShzZWxlY3RlZFRleHQpO1xuICAgICAgdGhpcy50ZXh0LmRpc2FibGUoKTtcbiAgICB9XG4gIH07XG5cbiAgcHJpdmF0ZSB1cGRhdGUgPSAodmlldzogRWRpdG9yVmlldykgPT4ge1xuICAgIGNvbnN0IHsgc3RhdGUgfSA9IHZpZXc7XG4gICAgdGhpcy5pc0FjdGl2ZSA9IExpbmtDb21tYW5kLmlzQWN0aXZlKHN0YXRlLCB7IHN0cmljdDogZmFsc2UgfSk7XG4gICAgdGhpcy5jYW5FeGVjdXRlID0gTGlua0NvbW1hbmQuY2FuRXhlY3V0ZShzdGF0ZSk7XG4gIH07XG5cbiAgaW5zZXJ0TGluayhlOiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIGNvbnN0IHsgdGV4dCwgaHJlZiwgb3BlbkluTmV3VGFiIH0gPSB0aGlzLmZvcm0uZ2V0UmF3VmFsdWUoKTtcbiAgICBjb25zdCB7IGRpc3BhdGNoLCBzdGF0ZSB9ID0gdGhpcy5lZGl0b3JWaWV3O1xuICAgIGNvbnN0IHsgc2VsZWN0aW9uIH0gPSBzdGF0ZTtcblxuICAgIGNvbnN0IGF0dHJzID0ge1xuICAgICAgdGl0bGU6IGhyZWYsXG4gICAgICBocmVmLFxuICAgICAgdGFyZ2V0OiBvcGVuSW5OZXdUYWIgPyAnX2JsYW5rJyA6ICdfc2VsZicsXG4gICAgfTtcblxuICAgIGlmIChzZWxlY3Rpb24uZW1wdHkpIHtcbiAgICAgIExpbmtDb21tYW5kLmluc2VydCh0ZXh0LCBhdHRycykoc3RhdGUsIGRpc3BhdGNoKTtcbiAgICAgIHRoaXMuZWRpdG9yVmlldy5mb2N1cygpO1xuICAgIH0gZWxzZSB7XG4gICAgICBMaW5rQ29tbWFuZC51cGRhdGUoYXR0cnMpKHN0YXRlLCBkaXNwYXRjaCk7XG4gICAgfVxuICAgIHRoaXMuaGlkZUZvcm0oKTtcbiAgfVxuXG4gIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIHRoaXMuZWRpdG9yVmlldyA9IHRoaXMubWVudVNlcnZpY2UuZWRpdG9yLnZpZXc7XG5cbiAgICB0aGlzLmZvcm0gPSBuZXcgRm9ybUdyb3VwKHtcbiAgICAgIGhyZWY6IG5ldyBGb3JtQ29udHJvbCgnJywgW1xuICAgICAgICBWYWxpZGF0b3JzLnJlcXVpcmVkLFxuICAgICAgICBWYWxpZGF0b3JzLnBhdHRlcm4odGhpcy5tZW51U2VydmljZS5lZGl0b3IubGlua1ZhbGlkYXRpb25QYXR0ZXJuKSxcbiAgICAgIF0pLFxuICAgICAgdGV4dDogbmV3IEZvcm1Db250cm9sKCcnLCBbVmFsaWRhdG9ycy5yZXF1aXJlZF0pLFxuICAgICAgb3BlbkluTmV3VGFiOiBuZXcgRm9ybUNvbnRyb2wodHJ1ZSksXG4gICAgfSk7XG5cbiAgICB0aGlzLnVwZGF0ZVN1YnNjcmlwdGlvbiA9IHRoaXMubWVudVNlcnZpY2UuZWRpdG9yLnVwZGF0ZS5zdWJzY3JpYmUoKHZpZXc6IEVkaXRvclZpZXcpID0+IHtcbiAgICAgIHRoaXMudXBkYXRlKHZpZXcpO1xuICAgIH0pO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy51cGRhdGVTdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTtcbiAgfVxufVxuIiwiPGRpdiBjbGFzcz1cIk5neEVkaXRvcl9fTWVudUl0ZW0tLUljb25Db250YWluZXJcIiBbY2xhc3MuTmd4RWRpdG9yX19NZW51SXRlbS0tQWN0aXZlXT1cImlzQWN0aXZlIHx8IHNob3dQb3B1cFwiXG4gIFtjbGFzcy5OZ3hFZGl0b3ItLURpc2FibGVkXT1cIiFjYW5FeGVjdXRlXCIgW2lubmVySFRNTF09XCJpY29uIHwgc2FuaXRpemVIdG1sXCIgKG1vdXNlZG93bik9XCJvbk1vdXNlRG93bigkZXZlbnQpXCJcbiAgW3RpdGxlXT1cInRpdGxlIHwgYXN5bmNcIj5cbjwvZGl2PlxuXG48IS0tIHBvcHVwIC0tPlxuPGRpdiAqbmdJZj1cInNob3dQb3B1cFwiIGNsYXNzPVwiTmd4RWRpdG9yX19Qb3B1cFwiPlxuICA8Zm9ybSBjbGFzcz1cIk5neEVkaXRvcl9fUG9wdXAtLUZvcm1cIiBbZm9ybUdyb3VwXT1cImZvcm1cIiAobmdTdWJtaXQpPVwiaW5zZXJ0TGluaygkZXZlbnQpXCI+XG5cbiAgICA8ZGl2IGNsYXNzPVwiTmd4RWRpdG9yX19Qb3B1cC0tRm9ybUdyb3VwXCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiTmd4RWRpdG9yX19Qb3B1cC0tQ29sXCI+XG4gICAgICAgIDxsYWJlbCBjbGFzcz1cIk5neEVkaXRvcl9fUG9wdXAtLUxhYmVsXCI+e3tnZXRMYWJlbCgndXJsJykgfCBhc3luY319PC9sYWJlbD5cbiAgICAgICAgPGlucHV0IHR5cGU9XCJocmVmXCIgaWQ9XCJocmVmXCIgZm9ybUNvbnRyb2xOYW1lPVwiaHJlZlwiIGF1dG9mb2N1cyBhdXRvY29tcGxldGU9XCJvZmZcIiAvPlxuICAgICAgICA8ZGl2ICpuZ0lmPVwiaHJlZi50b3VjaGVkICYmIGhyZWYuaW52YWxpZFwiIGNsYXNzPVwiTmd4RWRpdG9yX19IZWxwVGV4dCBOZ3hFZGl0b3JfX0hlbHBUZXh0LS1FcnJvclwiPlxuICAgICAgICAgIHt7IGhyZWYuZXJyb3JzPy5bJ3BhdHRlcm4nXSAmJiAnUGxlYXNlIGVudGVyIHZhbGlkIHVybC4nIH19XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG5cbiAgICA8ZGl2IGNsYXNzPVwiTmd4RWRpdG9yX19Qb3B1cC0tRm9ybUdyb3VwXCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiTmd4RWRpdG9yX19Qb3B1cC0tQ29sXCI+XG4gICAgICAgIDxsYWJlbCBjbGFzcz1cIk5neEVkaXRvcl9fUG9wdXAtLUxhYmVsXCI+e3tnZXRMYWJlbCgndGV4dCcpIHwgYXN5bmN9fTwvbGFiZWw+XG4gICAgICAgIDxpbnB1dCB0eXBlPVwidGV4dFwiIGZvcm1Db250cm9sTmFtZT1cInRleHRcIiBhdXRvY29tcGxldGU9XCJvZmZcIiAvPlxuICAgICAgICA8ZGl2ICpuZ0lmPVwidGV4dC50b3VjaGVkICYmIHRleHQuaW52YWxpZFwiIGNsYXNzPVwiTmd4RWRpdG9yX19IZWxwVGV4dCBOZ3hFZGl0b3JfX0hlbHBUZXh0LS1FcnJvclwiPlxuICAgICAgICAgIHt7IHRleHQuZXJyb3JzPy5bJ3JlcXVpcmVkJ10gJiYgJ1RoaXMgaXMgcmVxdWlyZWQnIH19XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG5cbiAgICA8ZGl2IGNsYXNzPVwiTmd4RWRpdG9yX19Qb3B1cC0tRm9ybUdyb3VwXCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiTmd4RWRpdG9yX19Qb3B1cC0tQ29sXCI+XG4gICAgICAgIDxsYWJlbD5cbiAgICAgICAgICA8aW5wdXQgdHlwZT1cImNoZWNrYm94XCIgZm9ybUNvbnRyb2xOYW1lPVwib3BlbkluTmV3VGFiXCIgLz5cbiAgICAgICAgICB7e2dldExhYmVsKCdvcGVuSW5OZXdUYWInKSB8IGFzeW5jfX1cbiAgICAgICAgPC9sYWJlbD5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuXG4gICAgPGJ1dHRvbiB0eXBlPVwic3VibWl0XCIgW2Rpc2FibGVkXT1cIiFmb3JtLnZhbGlkXCI+e3tnZXRMYWJlbCgnaW5zZXJ0JykgfCBhc3luY319PC9idXR0b24+XG5cbiAgPC9mb3JtPlxuPC9kaXY+XG4iXX0=