ngx-editor
Version:
Rich Text Editor for angular using ProseMirror
112 lines • 19.1 kB
JavaScript
import { Component, HostBinding, HostListener } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { NodeSelection } from 'prosemirror-state';
import Icon from '../../../icons';
import { Image as ImageCommand } 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 ImageComponent {
constructor(el, ngxeService, menuService) {
this.el = el;
this.ngxeService = ngxeService;
this.menuService = menuService;
this.showPopup = false;
this.isActive = false;
this.form = new FormGroup({
src: new FormControl('', [
Validators.required,
Validators.pattern('(https?://)?([\\da-z.-]+)\\.([a-z.]{2,6})[/\\w .-]*/??([^#\n\r]*)?#?([^\n\r]*)')
]),
alt: new FormControl(''),
title: new FormControl('')
});
this.update = (view) => {
const { state } = view;
this.isActive = ImageCommand.isActive(state);
};
}
get valid() {
return this.isActive || this.showPopup;
}
get icon() {
return Icon.get('image');
}
get src() {
return this.form.get('src');
}
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({
src: '',
alt: '',
title: ''
});
}
onMouseDown(e) {
if (e.button !== 0) {
return;
}
this.showPopup = !this.showPopup;
if (this.showPopup) {
this.fillForm();
}
}
fillForm() {
const { state } = this.editorView;
const { selection } = state;
if (selection instanceof NodeSelection && this.isActive) {
const { src, alt = '', title = '' } = selection.node.attrs;
this.form.setValue({
src,
alt,
title
});
}
}
insertLink(e) {
e.preventDefault();
const { src, alt, title } = this.form.getRawValue();
const { dispatch, state } = this.editorView;
const attrs = {
alt,
title
};
ImageCommand.insert(src, attrs)(state, dispatch);
this.editorView.focus();
this.hideForm();
}
ngOnInit() {
this.editorView = this.menuService.editor.view;
this.updateSubscription = this.menuService.editor.update.subscribe((view) => {
this.update(view);
});
}
ngOnDestroy() {
this.updateSubscription.unsubscribe();
}
}
ImageComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.0", ngImport: i0, type: ImageComponent, deps: [{ token: i0.ElementRef }, { token: i1.NgxEditorService }, { token: i2.MenuService }], target: i0.ɵɵFactoryTarget.Component });
ImageComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.0", type: ImageComponent, selector: "ngx-image", host: { listeners: { "document:mousedown": "onDocumentClick($event)" }, properties: { "class.NgxEditor__MenuItem--Active": "this.valid" } }, ngImport: i0, template: "<div class=\"NgxEditor__MenuItem--IconContainer\" [innerHTML]=\"icon | sanitizeHtml\" (mousedown)=\"onMouseDown($event)\"\n [title]=\"getLabel('insertImage')\">\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')}}</label>\n <input type=\"href\" id=\"href\" formControlName=\"src\" autofocus autocomplete=\"off\" />\n <div *ngIf=\"src.touched && src.invalid\" class=\"NgxEditor__HelpText NgxEditor__HelpText--Error\">\n {{ src.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('altText')}}</label>\n <input type=\"text\" formControlName=\"alt\" autocomplete=\"off\" />\n </div>\n </div>\n\n <div class=\"NgxEditor__Popup--FormGroup\">\n <div class=\"NgxEditor__Popup--Col\">\n <label class=\"NgxEditor__Popup--Label\">{{getLabel('title')}}</label>\n <input type=\"text\" formControlName=\"title\" autocomplete=\"off\" />\n </div>\n </div>\n\n <button type=\"submit\" [disabled]=\"!form.valid || !form.dirty\">{{getLabel('insert')}}</button>\n\n </form>\n</div>\n", styles: [""], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { 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]" }, { type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i4.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }], pipes: { "sanitizeHtml": i5.SanitizeHtmlPipe } });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.0", ngImport: i0, type: ImageComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-image', template: "<div class=\"NgxEditor__MenuItem--IconContainer\" [innerHTML]=\"icon | sanitizeHtml\" (mousedown)=\"onMouseDown($event)\"\n [title]=\"getLabel('insertImage')\">\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')}}</label>\n <input type=\"href\" id=\"href\" formControlName=\"src\" autofocus autocomplete=\"off\" />\n <div *ngIf=\"src.touched && src.invalid\" class=\"NgxEditor__HelpText NgxEditor__HelpText--Error\">\n {{ src.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('altText')}}</label>\n <input type=\"text\" formControlName=\"alt\" autocomplete=\"off\" />\n </div>\n </div>\n\n <div class=\"NgxEditor__Popup--FormGroup\">\n <div class=\"NgxEditor__Popup--Col\">\n <label class=\"NgxEditor__Popup--Label\">{{getLabel('title')}}</label>\n <input type=\"text\" formControlName=\"title\" autocomplete=\"off\" />\n </div>\n </div>\n\n <button type=\"submit\" [disabled]=\"!form.valid || !form.dirty\">{{getLabel('insert')}}</button>\n\n </form>\n</div>\n", styles: [""] }]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.NgxEditorService }, { type: i2.MenuService }]; }, propDecorators: { valid: [{
type: HostBinding,
args: ['class.NgxEditor__MenuItem--Active']
}], onDocumentClick: [{
type: HostListener,
args: ['document:mousedown', ['$event']]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1hZ2UuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LWVkaXRvci9zcmMvbGliL21vZHVsZXMvbWVudS9pbWFnZS9pbWFnZS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtZWRpdG9yL3NyYy9saWIvbW9kdWxlcy9tZW51L2ltYWdlL2ltYWdlLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDTCxTQUFTLEVBQWMsV0FBVyxFQUNsQyxZQUFZLEVBQ2IsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFtQixXQUFXLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3JGLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQU1sRCxPQUFPLElBQUksTUFBTSxnQkFBZ0IsQ0FBQztBQUNsQyxPQUFPLEVBQUUsS0FBSyxJQUFJLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDOzs7Ozs7O0FBT3hELE1BQU0sT0FBTyxjQUFjO0lBZ0J6QixZQUNVLEVBQWMsRUFDZCxXQUE2QixFQUM3QixXQUF3QjtRQUZ4QixPQUFFLEdBQUYsRUFBRSxDQUFZO1FBQ2QsZ0JBQVcsR0FBWCxXQUFXLENBQWtCO1FBQzdCLGdCQUFXLEdBQVgsV0FBVyxDQUFhO1FBbEJsQyxjQUFTLEdBQUcsS0FBSyxDQUFDO1FBQ2xCLGFBQVEsR0FBRyxLQUFLLENBQUM7UUFHakIsU0FBSSxHQUFHLElBQUksU0FBUyxDQUFDO1lBQ25CLEdBQUcsRUFBRSxJQUFJLFdBQVcsQ0FBQyxFQUFFLEVBQUU7Z0JBQ3ZCLFVBQVUsQ0FBQyxRQUFRO2dCQUNuQixVQUFVLENBQUMsT0FBTyxDQUFDLGdGQUFnRixDQUFDO2FBQ3JHLENBQUM7WUFDRixHQUFHLEVBQUUsSUFBSSxXQUFXLENBQUMs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QUMsS0FBSyxFQUFFLENBQUM7UUFDeEIsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ2xCLENBQUM7SUFFRCxRQUFRO1FBQ04sSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUM7UUFFL0MsSUFBSSxDQUFDLGtCQUFrQixHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQyxJQUFnQixFQUFFLEVBQUU7WUFDdEYsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNwQixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxXQUFXO1FBQ1QsSUFBSSxDQUFDLGtCQUFrQixDQUFDLFdBQVcsRUFBRSxDQUFDO0lBQ3hDLENBQUM7OzJHQTdHVSxjQUFjOytGQUFkLGNBQWMsOExDbkIzQix3L0NBb0NBOzJGRGpCYSxjQUFjO2tCQUwxQixTQUFTOytCQUNFLFdBQVc7MEpBMEJpQyxLQUFLO3NCQUExRCxXQUFXO3VCQUFDLG1DQUFtQztnQkFZQSxlQUFlO3NCQUE5RCxZQUFZO3VCQUFDLG9CQUFvQixFQUFFLENBQUMsUUFBUSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ29tcG9uZW50LCBFbGVtZW50UmVmLCBIb3N0QmluZGluZyxcbiAgSG9zdExpc3RlbmVyLCBPbkRlc3Ryb3ksIE9uSW5pdFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEFic3RyYWN0Q29udHJvbCwgRm9ybUNvbnRyb2wsIEZvcm1Hcm91cCwgVmFsaWRhdG9ycyB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcbmltcG9ydCB7IE5vZGVTZWxlY3Rpb24gfSBmcm9tICdwcm9zZW1pcnJvci1zdGF0ZSc7XG5pbXBvcnQgeyBFZGl0b3JWaWV3IH0gZnJvbSAncHJvc2VtaXJyb3Itdmlldyc7XG5pbXBvcnQgeyBTdWJzY3JpcHRpb24gfSBmcm9tICdyeGpzJztcblxuaW1wb3J0IHsgTmd4RWRpdG9yU2VydmljZSB9IGZyb20gJy4uLy4uLy4uL2VkaXRvci5zZXJ2aWNlJztcbmltcG9ydCB7IE1lbnVTZXJ2aWNlIH0gZnJvbSAnLi4vbWVudS5zZXJ2aWNlJztcbmltcG9ydCBJY29uIGZyb20gJy4uLy4uLy4uL2ljb25zJztcbmltcG9ydCB7IEltYWdlIGFzIEltYWdlQ29tbWFuZCB9IGZyb20gJy4uL01lbnVDb21tYW5kcyc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ25neC1pbWFnZScsXG4gIHRlbXBsYXRlVXJsOiAnLi9pbWFnZS5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL2ltYWdlLmNvbXBvbmVudC5zY3NzJ11cbn0pXG5leHBvcnQgY2xhc3MgSW1hZ2VDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XG4gIHNob3dQb3B1cCA9IGZhbHNlO1xuICBpc0FjdGl2ZSA9IGZhbHNlO1xuICBwcml2YXRlIHVwZGF0ZVN1YnNjcmlwdGlvbjogU3Vic2NyaXB0aW9uO1xuXG4gIGZvcm0gPSBuZXcgRm9ybUdyb3VwKHtcbiAgICBzcmM6IG5ldyBGb3JtQ29udHJvbCgnJywgW1xuICAgICAgVmFsaWRhdG9ycy5yZXF1aXJlZCxcbiAgICAgIFZhbGlkYXRvcnMucGF0dGVybignKGh0dHBzPzovLyk/KFtcXFxcZGEtei4tXSspXFxcXC4oW2Etei5dezIsNn0pWy9cXFxcdyAuLV0qLz8/KFteI1xcblxccl0qKT8jPyhbXlxcblxccl0qKScpXG4gICAgXSksXG4gICAgYWx0OiBuZXcgRm9ybUNvbnRyb2woJycpLFxuICAgIHRpdGxlOiBuZXcgRm9ybUNvbnRyb2woJycpXG4gIH0pO1xuXG4gIHByaXZhdGUgZWRpdG9yVmlldzogRWRpdG9yVmlldztcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIGVsOiBFbGVtZW50UmVmLFxuICAgIHByaXZhdGUgbmd4ZVNlcnZpY2U6IE5neEVkaXRvclNlcnZpY2UsXG4gICAgcHJpdmF0ZSBtZW51U2VydmljZTogTWVudVNlcnZpY2VcbiAgKSB7IH1cblxuICBASG9zdEJpbmRpbmcoJ2NsYXNzLk5neEVkaXRvcl9fTWVudUl0ZW0tLUFjdGl2ZScpIGdldCB2YWxpZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5pc0FjdGl2ZSB8fCB0aGlzLnNob3dQb3B1cDtcbiAgfVxuXG4gIGdldCBpY29uKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIEljb24uZ2V0KCdpbWFnZScpO1xuICB9XG5cbiAgZ2V0IHNyYygpOiBBYnN0cmFjdENvbnRyb2wge1xuICAgIHJldHVybiB0aGlzLmZvcm0uZ2V0KCdzcmMnKTtcbiAgfVxuXG4gIEBIb3N0TGlzdGVuZXIoJ2RvY3VtZW50Om1vdXNlZG93bicsIFsnJGV2ZW50J10pIG9uRG9jdW1lbnRDbGljayhlOiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmVsLm5hdGl2ZUVsZW1lbnQuY29udGFpbnMoZS50YXJnZXQpICYmIHRoaXMuc2hvd1BvcHVwKSB7XG4gICAgICB0aGlzLmhpZGVGb3JtKCk7XG4gICAgfVxuICB9XG5cbiAgZ2V0TGFiZWwoa2V5OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLm5neGVTZXJ2aWNlLmxvY2Fscy5nZXQoa2V5KTtcbiAgfVxuXG4gIHByaXZhdGUgaGlkZUZvcm0oKTogdm9pZCB7XG4gICAgdGhpcy5zaG93UG9wdXAgPSBmYWxzZTtcbiAgICB0aGlzLmZvcm0ucmVzZXQoe1xuICAgICAgc3JjOiAnJyxcbiAgICAgIGFsdDogJycsXG4gICAgICB0aXRsZTogJydcbiAgICB9KTtcbiAgfVxuXG4gIG9uTW91c2VEb3duKGU6IE1vdXNlRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoZS5idXR0b24gIT09IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLnNob3dQb3B1cCA9ICF0aGlzLnNob3dQb3B1cDtcblxuICAgIGlmICh0aGlzLnNob3dQb3B1cCkge1xuICAgICAgdGhpcy5maWxsRm9ybSgpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZmlsbEZvcm0oKTogdm9pZCB7XG4gICAgY29uc3QgeyBzdGF0ZSB9ID0gdGhpcy5lZGl0b3JWaWV3O1xuICAgIGNvbnN0IHsgc2VsZWN0aW9uIH0gPSBzdGF0ZTtcbiAgICBpZiAoc2VsZWN0aW9uIGluc3RhbmNlb2YgTm9kZVNlbGVjdGlvbiAmJiB0aGlzLmlzQWN0aXZlKSB7XG4gICAgICBjb25zdCB7IHNyYywgYWx0ID0gJycsIHRpdGxlID0gJycgfSA9IHNlbGVjdGlvbi5ub2RlLmF0dHJzO1xuXG4gICAgICB0aGlzLmZvcm0uc2V0VmFsdWUoe1xuICAgICAgICBzcmMsXG4gICAgICAgIGFsdCxcbiAgICAgICAgdGl0bGVcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgdXBkYXRlID0gKHZpZXc6IEVkaXRvclZpZXcpID0+IHtcbiAgICBjb25zdCB7IHN0YXRlIH0gPSB2aWV3O1xuICAgIHRoaXMuaXNBY3RpdmUgPSBJbWFnZUNvbW1hbmQuaXNBY3RpdmUoc3RhdGUpO1xuICB9XG5cbiAgaW5zZXJ0TGluayhlOiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIGNvbnN0IHsgc3JjLCBhbHQsIHRpdGxlIH0gPSB0aGlzLmZvcm0uZ2V0UmF3VmFsdWUoKTtcbiAgICBjb25zdCB7IGRpc3BhdGNoLCBzdGF0ZSB9ID0gdGhpcy5lZGl0b3JWaWV3O1xuXG4gICAgY29uc3QgYXR0cnMgPSB7XG4gICAgICBhbHQsXG4gICAgICB0aXRsZVxuICAgIH07XG5cbiAgICBJbWFnZUNvbW1hbmQuaW5zZXJ0KHNyYywgYXR0cnMpKHN0YXRlLCBkaXNwYXRjaCk7XG4gICAgdGhpcy5lZGl0b3JWaWV3LmZvY3VzKCk7XG4gICAgdGhpcy5oaWRlRm9ybSgpO1xuICB9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgdGhpcy5lZGl0b3JWaWV3ID0gdGhpcy5tZW51U2VydmljZS5lZGl0b3IudmlldztcblxuICAgIHRoaXMudXBkYXRlU3Vic2NyaXB0aW9uID0gdGhpcy5tZW51U2VydmljZS5lZGl0b3IudXBkYXRlLnN1YnNjcmliZSgodmlldzogRWRpdG9yVmlldykgPT4ge1xuICAgICAgdGhpcy51cGRhdGUodmlldyk7XG4gICAgfSk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLnVwZGF0ZVN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwiTmd4RWRpdG9yX19NZW51SXRlbS0tSWNvbkNvbnRhaW5lclwiIFtpbm5lckhUTUxdPVwiaWNvbiB8IHNhbml0aXplSHRtbFwiIChtb3VzZWRvd24pPVwib25Nb3VzZURvd24oJGV2ZW50KVwiXG4gIFt0aXRsZV09XCJnZXRMYWJlbCgnaW5zZXJ0SW1hZ2UnKVwiPlxuPC9kaXY+XG5cbjwhLS0gcG9wdXAgLS0+XG48ZGl2ICpuZ0lmPVwic2hvd1BvcHVwXCIgY2xhc3M9XCJOZ3hFZGl0b3JfX1BvcHVwXCI+XG4gIDxmb3JtIGNsYXNzPVwiTmd4RWRpdG9yX19Qb3B1cC0tRm9ybVwiIFtmb3JtR3JvdXBdPVwiZm9ybVwiIChuZ1N1Ym1pdCk9XCJpbnNlcnRMaW5rKCRldmVudClcIj5cblxuICAgIDxkaXYgY2xhc3M9XCJOZ3hFZGl0b3JfX1BvcHVwLS1Gb3JtR3JvdXBcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJOZ3hFZGl0b3JfX1BvcHVwLS1Db2xcIj5cbiAgICAgICAgPGxhYmVsIGNsYXNzPVwiTmd4RWRpdG9yX19Qb3B1cC0tTGFiZWxcIj57e2dldExhYmVsKCd1cmwnKX19PC9sYWJlbD5cbiAgICAgICAgPGlucHV0IHR5cGU9XCJocmVmXCIgaWQ9XCJocmVmXCIgZm9ybUNvbnRyb2xOYW1lPVwic3JjXCIgYXV0b2ZvY3VzIGF1dG9jb21wbGV0ZT1cIm9mZlwiIC8+XG4gICAgICAgIDxkaXYgKm5nSWY9XCJzcmMudG91Y2hlZCAmJiBzcmMuaW52YWxpZFwiIGNsYXNzPVwiTmd4RWRpdG9yX19IZWxwVGV4dCBOZ3hFZGl0b3JfX0hlbHBUZXh0LS1FcnJvclwiPlxuICAgICAgICAgIHt7IHNyYy5lcnJvcnM/LlsncGF0dGVybiddICYmICdQbGVhc2UgZW50ZXIgdmFsaWQgdXJsLicgfX1cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDxkaXYgY2xhc3M9XCJOZ3hFZGl0b3JfX1BvcHVwLS1Gb3JtR3JvdXBcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJOZ3hFZGl0b3JfX1BvcHVwLS1Db2xcIj5cbiAgICAgICAgPGxhYmVsIGNsYXNzPVwiTmd4RWRpdG9yX19Qb3B1cC0tTGFiZWxcIj57e2dldExhYmVsKCdhbHRUZXh0Jyl9fTwvbGFiZWw+XG4gICAgICAgIDxpbnB1dCB0eXBlPVwidGV4dFwiIGZvcm1Db250cm9sTmFtZT1cImFsdFwiIGF1dG9jb21wbGV0ZT1cIm9mZlwiIC8+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDxkaXYgY2xhc3M9XCJOZ3hFZGl0b3JfX1BvcHVwLS1Gb3JtR3JvdXBcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJOZ3hFZGl0b3JfX1BvcHVwLS1Db2xcIj5cbiAgICAgICAgPGxhYmVsIGNsYXNzPVwiTmd4RWRpdG9yX19Qb3B1cC0tTGFiZWxcIj57e2dldExhYmVsKCd0aXRsZScpfX08L2xhYmVsPlxuICAgICAgICA8aW5wdXQgdHlwZT1cInRleHRcIiBmb3JtQ29udHJvbE5hbWU9XCJ0aXRsZVwiIGF1dG9jb21wbGV0ZT1cIm9mZlwiIC8+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDxidXR0b24gdHlwZT1cInN1Ym1pdFwiIFtkaXNhYmxlZF09XCIhZm9ybS52YWxpZCB8fCAhZm9ybS5kaXJ0eVwiPnt7Z2V0TGFiZWwoJ2luc2VydCcpfX08L2J1dHRvbj5cblxuICA8L2Zvcm0+XG48L2Rpdj5cbiJdfQ==