@asoftwareworld/form-builder-pro
Version:
ASW Form Builder Pro helps you with rapid development and designed web forms which includes several controls. The key feature of Form Builder is to make your content attractive and effective. We can customize our control at run time and preview the same b
1 lines • 106 kB
Source Map (JSON)
{"version":3,"file":"asoftwareworld-form-builder-pro-editor.mjs","sources":["../../src/components/editor/editor.service.ts","../../src/components/editor/editor-dialog/editor-dialog.ts","../../src/components/editor/editor-dialog/editor-dialog.html","../../src/components/editor/editor-select/editor-select.ts","../../src/components/editor/editor-select/editor-select.html","../../src/components/editor/editor-toolbar/editor-toolbar.ts","../../src/components/editor/editor-toolbar/editor-toolbar.html","../../src/components/editor/editor.ts","../../src/components/editor/editor.html","../../src/components/editor/editor-button/editor-button.ts","../../src/components/editor/editor-button/editor-button.html","../../src/components/editor/editor-toolbar-set/editor-toolbar-set.ts","../../src/components/editor/editor-toolbar-set/editor-toolbar-set.html","../../src/components/editor/editor.module.ts","../../src/components/editor/public_api.ts","../../src/components/editor/asoftwareworld-form-builder-pro-editor.ts"],"sourcesContent":["import { DOCUMENT } from '@angular/common';\nimport { HttpClient, HttpEvent } from '@angular/common/http';\nimport { Inject, Injectable } from '@angular/core';\nimport { CustomClass, UploadResponse } from '@asoftwareworld/form-builder-pro/api';\nimport { Observable } from 'rxjs';\n\n@Injectable()\nexport class AswEditorService {\n savedSelection!: Range | null;\n selectedText!: string;\n uploadUrl: any;\n uploadWithCredentials?: boolean;\n\n constructor(\n private http: HttpClient,\n @Inject(DOCUMENT) private doc: any\n ) {}\n\n /**\n * Executed command from editor header buttons exclude toggleEditorMode\n * @param command string from triggerCommand\n */\n executeCommand(command: string, value?: string): void {\n const commands = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'pre'];\n if (commands.includes(command)) {\n this.doc.execCommand('formatBlock', false, command);\n return;\n }\n this.doc.execCommand(command, false, value);\n }\n\n /**\n * Create URL link\n * @param url string from UI prompt\n */\n createLink(url: string): void {\n if (!url.includes('http')) {\n this.doc.execCommand('createlink', false, url);\n } else {\n const newUrl = `<a href='${url}' target='_blank'> ${this.selectedText} </a>`;\n this.insertHtml(newUrl);\n }\n }\n\n /**\n * insert color either font or background\n *\n * @param color color to be inserted\n * @param where where the color has to be inserted either text/background\n */\n insertColor(color: string, where: string): void {\n const restored = this.restoreSelection();\n if (restored) {\n if (where === 'textColor') {\n this.doc.execCommand('foreColor', false, color);\n } else {\n this.doc.execCommand('hiliteColor', false, color);\n }\n }\n }\n\n /**\n * Set font name\n * @param fontName string\n */\n setFontName(fontName: string): void {\n this.doc.execCommand('fontName', false, fontName);\n }\n\n /**\n * Set font size\n * @param fontSize string\n */\n setFontSize(fontSize: string): void {\n this.doc.execCommand('fontSize', false, fontSize);\n }\n\n /**\n * Create raw HTML\n * @param html HTML string\n */\n insertHtml(html: string): void {\n const isHTMLInserted = this.doc.execCommand('insertHTML', false, html);\n\n if (!isHTMLInserted) {\n throw new Error('Unable to perform the operation');\n }\n }\n\n /**\n * save selection when the editor is focussed out\n */\n public saveSelection = (): void => {\n if (this.doc.getSelection) {\n const sel = this.doc.getSelection();\n if (sel.getRangeAt && sel.rangeCount) {\n this.savedSelection = sel.getRangeAt(0);\n this.selectedText = sel.toString();\n }\n } else if (this.doc.getSelection && this.doc.createRange) {\n this.savedSelection = document.createRange();\n } else {\n this.savedSelection = null;\n }\n };\n\n /**\n * restore selection when the editor is focused in\n *\n * saved selection when the editor is focused out\n */\n restoreSelection(): boolean {\n if (this.savedSelection) {\n if (this.doc.getSelection) {\n const sel = this.doc.getSelection();\n sel.removeAllRanges();\n sel.addRange(this.savedSelection);\n return true;\n } else if (this.doc.getSelection /*&& this.savedSelection.select*/) {\n // this.savedSelection.select();\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n }\n\n /**\n * setTimeout used for execute 'saveSelection' method in next event loop iteration\n */\n public executeInNextQueueIteration(callbackFn: (...args: any[]) => any, timeout = 1e2): void {\n setTimeout(callbackFn, timeout);\n }\n\n /** check any selection is made or not */\n private checkSelection(): any {\n const selectedText = this.savedSelection?.toString();\n\n if (selectedText?.length === 0) {\n throw new Error('No Selection Made');\n }\n return true;\n }\n\n /**\n * Upload file to uploadUrl\n * @param file The file\n */\n uploadImage(file: File): Observable<HttpEvent<UploadResponse>> | any {\n const uploadData: FormData = new FormData();\n\n uploadData.append('file', file, file.name);\n\n return this.http.post<UploadResponse>(this.uploadUrl, uploadData, {\n reportProgress: true,\n observe: 'events',\n withCredentials: this.uploadWithCredentials\n });\n }\n\n /**\n * Insert image with Url\n * @param imageUrl The imageUrl.\n */\n insertImage(imageUrl: string): void {\n this.doc.execCommand('insertImage', false, imageUrl);\n }\n\n setDefaultParagraphSeparator(separator: string): void {\n this.doc.execCommand('defaultParagraphSeparator', false, separator);\n }\n\n createCustomClass(customClass: CustomClass): void {\n let newTag = this.selectedText;\n if (customClass) {\n const tagName = customClass.tag ? customClass.tag : 'span';\n newTag = '<' + tagName + ' class=\"' + customClass.class + '\">' + this.selectedText + '</' + tagName + '>';\n }\n this.insertHtml(newTag);\n }\n\n insertVideo(videoUrl: string): void {\n if (videoUrl.match('www.youtube.com')) {\n this.insertYouTubeVideoTag(videoUrl);\n }\n if (videoUrl.match('vimeo.com')) {\n this.insertVimeoVideoTag(videoUrl);\n }\n }\n\n private insertYouTubeVideoTag(videoUrl: string): void {\n const id = videoUrl.split('v=')[1];\n const imageUrl = `https://img.youtube.com/vi/${id}/0.jpg`;\n const thumbnail = `\n <div style='position: relative'>\n <a href='${videoUrl}' target='_blank'>\n <img src=\"${imageUrl}\" alt=\"click to watch\"/>\n <img style='position: absolute; left:200px; top:140px'\n src=\"https://img.icons8.com/color/96/000000/youtube-play.png\"/>\n </a>\n </div>`;\n this.insertHtml(thumbnail);\n }\n\n private insertVimeoVideoTag(videoUrl: string): void {\n const sub = this.http.get<any>(`https://vimeo.com/api/oembed.json?url=${videoUrl}`).subscribe((data) => {\n const imageUrl = data.thumbnail_url_with_play_button;\n const thumbnail = `<div>\n <a href='${videoUrl}' target='_blank'>\n <img src=\"${imageUrl}\" alt=\"${data.title}\"/>\n </a>\n </div>`;\n this.insertHtml(thumbnail);\n sub.unsubscribe();\n });\n }\n\n nextNode(node: any): any {\n if (node.hasChildNodes()) {\n return node.firstChild;\n } else {\n while (node && !node.nextSibling) {\n node = node.parentNode;\n }\n if (!node) {\n return null;\n }\n return node.nextSibling;\n }\n }\n\n getRangeSelectedNodes(range: any, includePartiallySelectedContainers: any): any[] {\n let node = range.startContainer;\n const endNode = range.endContainer;\n let rangeNodes = [];\n\n // Special case for a range that is contained within a single node\n if (node === endNode) {\n rangeNodes = [node];\n } else {\n // Iterate nodes until we hit the end container\n while (node && node !== endNode) {\n rangeNodes.push((node = this.nextNode(node)));\n }\n\n // Add partially selected nodes at the start of the range\n node = range.startContainer;\n while (node && node !== range.commonAncestorContainer) {\n rangeNodes.unshift(node);\n node = node.parentNode;\n }\n }\n\n // Add ancestors of the range container, if required\n if (includePartiallySelectedContainers) {\n node = range.commonAncestorContainer;\n while (node) {\n rangeNodes.push(node);\n node = node.parentNode;\n }\n }\n\n return rangeNodes;\n }\n\n getSelectedNodes(): any[] {\n const nodes: any[] = [];\n if (this.doc.getSelection) {\n const sel = this.doc.getSelection();\n for (let i = 0, len = sel.rangeCount; i < len; ++i) {\n nodes.push.apply(nodes, this.getRangeSelectedNodes(sel.getRangeAt(i), true));\n }\n }\n return nodes;\n }\n\n replaceWithOwnChildren(el: any): void {\n const parent = el.parentNode;\n while (el.hasChildNodes()) {\n parent.insertBefore(el.firstChild, el);\n }\n parent.removeChild(el);\n }\n\n removeSelectedElements(tagNames: any): void {\n const tagNamesArray = tagNames.toLowerCase().split(',');\n this.getSelectedNodes().forEach((node) => {\n if (node.nodeType === 1 && tagNamesArray.indexOf(node.tagName.toLowerCase()) > -1) {\n // Remove the node and replace it with its children\n this.replaceWithOwnChildren(node);\n }\n });\n }\n}\n","/**\n * @license\n * Copyright ASW (A Software World) All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file\n */\n\nimport { Component, Inject, OnInit } from '@angular/core';\nimport { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';\nimport { Icons } from '@asoftwareworld/form-builder-pro/common';\n\n@Component({\n selector: 'asw-editor-dialog',\n templateUrl: './editor-dialog.html'\n})\nexport class AswEditorDialog implements OnInit {\n icons = Icons;\n linkTo!: string;\n constructor(\n public dialogRef: MatDialogRef<AswEditorDialog>,\n @Inject(MAT_DIALOG_DATA) public data: any\n ) {}\n\n ngOnInit(): void {\n this.linkTo = this.data.linkTo;\n }\n\n onNoClick(): void {\n this.dialogRef.close();\n }\n}\n","<div class=\"asw-dialog-header\">\r\n <div class=\"asw-edit-row-dialog\">\r\n <div class=\"asw-dialog-header clearfix\">\r\n <div class=\"asw-dialog-about\">\r\n <h1 mat-dialog-title class=\"asw-m-0\">Add Link</h1>\r\n </div>\r\n </div>\r\n </div>\r\n <button mat-icon-button (click)=\"onNoClick()\" aria-label=\"Close dialog\" class=\"asw-mt-2 asw-me-2\">\r\n <div [innerHTML]=\"icons.close | aswSafeHtml\">add</div>\r\n </button>\r\n</div>\r\n<mat-divider></mat-divider>\r\n<div mat-dialog-content class=\"mat-typography\">\r\n <mat-form-field appearance=\"outline\" class=\"asw-width-100\">\r\n <mat-label>Link to</mat-label>\r\n <input matInput \r\n type=\"text\"\r\n placeholder=\"Link to\"\r\n matTooltip=\"Link to\"\r\n #input=\"ngModel\"\r\n [(ngModel)]=\"linkTo\">\r\n </mat-form-field>\r\n</div>\r\n<mat-divider></mat-divider>\r\n<div mat-dialog-actions align=\"end\">\r\n <button id=\"no\"\r\n type=\"button\"\r\n class=\"asw-button asw-button-danger asw-mb-1 asw-me-2\"\r\n (click)=\"onNoClick()\">\r\n Cancel\r\n </button>\r\n <button type=\"button\"\r\n id=\"yes\"\r\n class=\"asw-button asw-button-primary asw-mb-1\"\r\n [mat-dialog-close]=\"linkTo\"\r\n cdkFocusInitial>\r\n Ok\r\n </button>\r\n</div>","import { Component, ElementRef, EventEmitter, forwardRef, HostBinding, HostListener, Input, OnInit, Output, Renderer2, ViewChild, ViewEncapsulation } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { isDefined } from '@asoftwareworld/form-builder-pro/utils';\n\nexport interface SelectOption {\n label: string;\n value: string;\n}\n\n@Component({\n selector: 'asw-editor-select',\n templateUrl: './editor-select.html',\n styleUrls: ['./editor-select.scss'],\n encapsulation: ViewEncapsulation.None,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => AswEditorSelect),\n multi: true\n }\n ]\n})\nexport class AswEditorSelect implements OnInit, ControlValueAccessor {\n @Input() options: SelectOption[] = [];\n\n // tslint:disable-next-line:no-input-rename\n @Input('hidden') isHidden?: boolean;\n\n selectedOption!: SelectOption;\n disabled = false;\n optionId = 0;\n\n get label(): string {\n return this.selectedOption && this.selectedOption.hasOwnProperty('label') ? this.selectedOption.label : 'Select';\n }\n\n opened = false;\n\n get value(): string {\n return this.selectedOption.value;\n }\n\n @HostBinding('style.display') hidden = 'inline-block';\n\n @Output('change') changeEvent = new EventEmitter();\n\n @ViewChild('labelButton', { static: true }) labelButton!: ElementRef;\n\n constructor(\n private elRef: ElementRef,\n private r: Renderer2\n ) {}\n\n ngOnInit(): void {\n this.selectedOption = this.options[0];\n if (isDefined(this.isHidden) && this.isHidden) {\n this.hide();\n }\n }\n\n hide(): void {\n this.hidden = 'none';\n }\n\n optionSelect(option: SelectOption, event: MouseEvent): void {\n event.stopPropagation();\n this.setValue(option.value);\n this.onChange(this.selectedOption.value);\n this.changeEvent.emit(this.selectedOption.value);\n this.onTouched();\n this.opened = false;\n }\n\n toggleOpen(event: MouseEvent): void {\n // event.stopPropagation();\n if (this.disabled) {\n return;\n }\n this.opened = !this.opened;\n }\n\n @HostListener('document:click', ['$event'])\n onClick($event: MouseEvent): void {\n if (!this.elRef.nativeElement.contains($event.target)) {\n this.close();\n }\n }\n\n close(): void {\n this.opened = false;\n }\n\n get isOpen(): boolean {\n return this.opened;\n }\n\n writeValue(value: string): void {\n if (!value || typeof value !== 'string') {\n return;\n }\n this.setValue(value);\n }\n\n setValue(value: string): void {\n let index = 0;\n const selectedEl = this.options.find((el, i) => {\n index = i;\n return el.value === value;\n });\n if (selectedEl) {\n this.selectedOption = selectedEl;\n this.optionId = index;\n }\n }\n\n onChange: any = () => {};\n onTouched: any = () => {};\n\n registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: any): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.labelButton.nativeElement.disabled = isDisabled;\n const div = this.labelButton.nativeElement;\n const action = isDisabled ? 'addClass' : 'removeClass';\n this.r[action](div, 'disabled');\n this.disabled = isDisabled;\n }\n\n @HostListener('keydown', ['$event'])\n handleKeyDown($event: KeyboardEvent): void {\n if (!this.opened) {\n return;\n }\n // console.log($event.key);\n // if (KeyCode[$event.key]) {\n switch ($event.key) {\n case 'ArrowDown':\n this._handleArrowDown();\n break;\n case 'ArrowUp':\n this._handleArrowUp();\n break;\n case 'Space':\n this._handleSpace();\n break;\n case 'Enter':\n this._handleEnter($event);\n break;\n case 'Tab':\n this._handleTab();\n break;\n case 'Escape':\n this.close();\n $event.preventDefault();\n break;\n case 'Backspace':\n this._handleBackspace();\n break;\n }\n // } else if ($event.key && $event.key.length === 1) {\n // this._keyPress$.next($event.key.toLocaleLowerCase());\n // }\n }\n\n _handleArrowDown(): void {\n if (this.optionId < this.options.length - 1) {\n this.optionId++;\n }\n }\n\n _handleArrowUp(): void {\n if (this.optionId >= 1) {\n this.optionId--;\n }\n }\n\n _handleSpace(): void {}\n\n _handleEnter($event: any): void {\n this.optionSelect(this.options[this.optionId], $event);\n }\n\n _handleTab(): void {}\n\n _handleBackspace(): void {}\n}\n","<span class=\"asw-font asw-picker\" [ngClass]=\"{'asw-expanded':isOpen}\">\r\n <button [tabIndex]=\"-1\" #labelButton tabindex=\"0\" type=\"button\" role=\"button\" class=\"asw-picker-label\" matTooltip=\"{{label}}\"\r\n (click)=\"toggleOpen($event);\">{{label}}\r\n <svg viewBox=\"0 0 18 18\">\r\n <polygon class=\"asw-stroke\" points=\"7 11 9 13 11 11 7 11\"></polygon>\r\n <polygon class=\"asw-stroke\" points=\"7 7 9 5 11 7 7 7\"></polygon>\r\n </svg>\r\n </button>\r\n <span class=\"asw-picker-options\">\r\n <button tabindex=\"-1\" type=\"button\" role=\"button\" class=\"asw-picker-item\"\r\n *ngFor=\"let item of options; let i = index\"\r\n [ngClass]=\"{'selected': item.value === value, 'focused': i === optionId}\"\r\n (click)=\"optionSelect(item, $event)\">\r\n {{item.label}}\r\n </button>\r\n <span class=\"dropdown-item\" *ngIf=\"!options.length\">No items for select</span>\r\n </span>\r\n</span>","import { DOCUMENT } from '@angular/common';\nimport { HttpEvent, HttpResponse } from '@angular/common/http';\nimport { Component, ElementRef, EventEmitter, Inject, Input, OnInit, Output, Renderer2, ViewChild } from '@angular/core';\nimport { MatDialog } from '@angular/material/dialog';\nimport { CustomClass, ElementDOM, UploadResponse } from '@asoftwareworld/form-builder-pro/api';\nimport { Constants, Icons } from '@asoftwareworld/form-builder-pro/common';\nimport { Observable } from 'rxjs';\nimport { AswEditorDialog } from '../editor-dialog/editor-dialog';\nimport { SelectOption } from './../editor-select/editor-select';\nimport { AswEditorService } from './../editor.service';\n\n@Component({\n selector: 'asw-editor-toolbar',\n templateUrl: './editor-toolbar.html',\n styleUrls: ['./editor-toolbar.scss']\n})\nexport class AswEditorToolbar implements OnInit, ElementDOM {\n htmlMode = false;\n linkSelected = false;\n block = 'default';\n fontSize = '3';\n foreColour: any;\n backColor: any;\n icons: any = Icons;\n colorsName: string[] = [];\n constant = Constants;\n textColor = '#000105';\n backgroundColor = '#ffffff';\n selectedColor!: string;\n show = false;\n\n headings: SelectOption[] = [\n {\n label: 'Heading 1',\n value: 'h1'\n },\n {\n label: 'Heading 2',\n value: 'h2'\n },\n {\n label: 'Heading 3',\n value: 'h3'\n },\n {\n label: 'Heading 4',\n value: 'h4'\n },\n {\n label: 'Heading 5',\n value: 'h5'\n },\n {\n label: 'Heading 6',\n value: 'h6'\n },\n {\n label: 'Heading 7',\n value: 'h7'\n },\n {\n label: 'Paragraph',\n value: 'p'\n },\n {\n label: 'Predefined',\n value: 'pre'\n },\n {\n label: 'Standard',\n value: 'div'\n },\n {\n label: 'default',\n value: 'default'\n }\n ];\n\n fontSizes: SelectOption[] = [\n {\n label: '1',\n value: '1'\n },\n {\n label: '2',\n value: '2'\n },\n {\n label: '3',\n value: '3'\n },\n {\n label: '4',\n value: '4'\n },\n {\n label: '5',\n value: '5'\n },\n {\n label: '6',\n value: '6'\n },\n {\n label: '7',\n value: '7'\n }\n ];\n\n customClassId = '-1';\n\n customClasses$: CustomClass[] = [];\n customClassList: SelectOption[] = [{ label: '', value: '' }];\n // uploadUrl: string;\n\n tagMap: any = {\n BLOCKQUOTE: 'indent',\n A: 'link'\n };\n\n select = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P', 'PRE', 'DIV'];\n\n buttons = ['bold', 'italic', 'underline', 'strikeThrough', 'subscript', 'superscript', 'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull', 'indent', 'outdent', 'insertUnorderedList', 'insertOrderedList', 'link'];\n\n @Input() id!: string;\n @Input() uploadUrl!: string;\n @Input() upload: ((file: File) => Observable<HttpEvent<UploadResponse>>) | any;\n @Input() showToolbar!: boolean;\n @Input() fonts: SelectOption[] = [{ label: '', value: '' }];\n\n @Input()\n set customClasses(classes: CustomClass[]) {\n if (classes) {\n this.customClasses$ = classes;\n this.customClassList = this.customClasses$.map((x, i) => ({ label: x.name, value: i.toString() }));\n this.customClassList.unshift({ label: 'Clear Class', value: '-1' });\n }\n }\n\n @Input()\n set defaultFontSize(value: string) {\n if (value) {\n this.fontSize = value;\n }\n }\n\n @Input() hiddenButtons?: string[][];\n\n @Output() execute: EventEmitter<string> = new EventEmitter<string>();\n\n @ViewChild('fileInput', { static: true }) myInputFile?: ElementRef;\n\n public get isLinkButtonDisabled(): boolean {\n return this.htmlMode || !Boolean(this.aswEditorService.selectedText);\n }\n\n constructor(\n private renderer: Renderer2,\n private aswEditorService: AswEditorService,\n @Inject(DOCUMENT) private doc: any,\n public dialog: MatDialog,\n public elementDOM: ElementRef<HTMLElement>\n ) {}\n\n ngOnInit(): void {\n this.colorsName = Object.keys(this.constant.colors);\n }\n\n /**\n * Trigger command from editor header buttons\n * @param command string from toolbar buttons\n */\n triggerCommand(command: string): void {\n this.execute.emit(command);\n }\n\n /**\n * highlight editor buttons when cursor moved or positioning\n */\n triggerButtons(): void {\n if (!this.showToolbar) {\n return;\n }\n this.buttons.forEach((e) => {\n const result = this.doc.queryCommandState(e);\n const elementById = this.getChildById(e + '-' + this.id);\n if (result) {\n this.renderer.addClass(elementById, 'active');\n } else {\n this.renderer.removeClass(elementById, 'active');\n }\n });\n }\n\n getChildById(id: string): any {\n return this.elementDOM.nativeElement.querySelector(`#${id}`);\n }\n\n /**\n * trigger highlight editor buttons when cursor moved or positioning in block\n */\n triggerBlocks(nodes: Node[]): void {\n if (!this.showToolbar) {\n return;\n }\n this.linkSelected = nodes.findIndex((x) => x.nodeName === 'A') > -1;\n let found = false;\n this.select.forEach((y) => {\n const node = nodes.find((x) => x.nodeName === y);\n if (node !== undefined && y === node.nodeName) {\n if (found === false) {\n this.block = node.nodeName.toLowerCase();\n found = true;\n }\n } else if (found === false) {\n this.block = 'default';\n }\n });\n\n found = false;\n if (this.customClasses$) {\n this.customClasses$.forEach((y, index) => {\n const node = nodes.find((x): any => {\n if (x instanceof Element) {\n return x.className === y.class;\n }\n });\n if (node !== undefined) {\n if (found === false) {\n this.customClassId = index.toString();\n found = true;\n }\n } else if (found === false) {\n this.customClassId = '-1';\n }\n });\n }\n\n Object.keys(this.tagMap).map((e: any) => {\n const elementById = this.getChildById(this.tagMap[e] + '-' + this.id);\n const node = nodes.find((x) => x.nodeName === e);\n if (node !== undefined && e === node.nodeName) {\n this.renderer.addClass(elementById, 'active');\n } else {\n this.renderer.removeClass(elementById, 'active');\n }\n });\n\n this.foreColour = this.doc.queryCommandValue('ForeColor');\n this.fontSize = this.doc.queryCommandValue('FontSize');\n // this.fontName = this.doc.queryCommandValue('FontName').replace(/\"/g, '');\n this.backColor = this.doc.queryCommandValue('backColor');\n }\n\n /**\n * insert URL link\n */\n insertUrl(): void {\n let url: any = 'https://';\n const selection = this.aswEditorService.savedSelection;\n if (selection && selection.commonAncestorContainer.parentElement?.nodeName === 'A') {\n const parent = selection.commonAncestorContainer.parentElement as HTMLAnchorElement;\n if (parent.href !== '') {\n url = parent.href;\n }\n }\n // const dialogRef = this.dialog.open(AswEditorDialog, {\n // width: '450px',\n // data: { linkTo: url }\n // });\n // dialogRef.afterClosed().subscribe((result) => {\n // if (result !== undefined) {\n // url = result;\n // this.aswEditorService.createLink(url);\n // }\n // });\n url = prompt('Insert URL link', url);\n if (url && url !== '' && url !== 'https://') {\n this.aswEditorService.createLink(url);\n }\n }\n\n /**\n * insert Video link\n */\n insertVideo(): void {\n this.execute.emit('');\n const dialogRef = this.dialog.open(AswEditorDialog, {\n width: '450px',\n data: { linkTo: `https://` }\n });\n dialogRef.afterClosed().subscribe((result) => {\n if (result !== undefined) {\n const url = result;\n if (url && url !== '' && url !== `https://`) {\n this.aswEditorService.insertVideo(url);\n }\n }\n });\n }\n\n /** insert color */\n insertColor(color: string, where: string): void {\n if (where === 'textColor') {\n this.textColor = color;\n } else {\n this.backgroundColor = color;\n }\n this.aswEditorService.insertColor(color, where);\n this.execute.emit('');\n this.show = false;\n }\n\n /**\n * Change status of visibility to color picker\n */\n public toggleColors(where: string): void {\n this.show = !this.show;\n this.selectedColor = where;\n }\n\n /**\n * Change color from input\n */\n public setColor(data: any): void {\n this.insertColor(data.colorCode, data.where);\n }\n\n /**\n * set font Name/family\n * @param foreColor string\n */\n setFontName(foreColor: string): void {\n this.aswEditorService.setFontName(foreColor);\n this.execute.emit('');\n }\n\n /**\n * set font Size\n * @param fontSize string\n */\n setFontSize(fontSize: string): void {\n this.aswEditorService.setFontSize(fontSize);\n this.execute.emit('');\n }\n\n /**\n * toggle editor mode (WYSIWYG or SOURCE)\n * @param m boolean\n */\n setEditorMode(m: boolean): void {\n const toggleEditorModeButton = this.getChildById('toggleEditorMode' + '-' + this.id);\n if (m) {\n this.renderer.addClass(toggleEditorModeButton, 'active');\n } else {\n this.renderer.removeClass(toggleEditorModeButton, 'active');\n }\n this.htmlMode = m;\n }\n\n /**\n * Upload image when file is selected.\n */\n onFileChanged(event: any): void {\n const file = event.target.files[0];\n if (file.type.includes('image/')) {\n if (this.upload) {\n this.upload(file).subscribe((response: HttpResponse<UploadResponse>) => this.watchUploadImage(response, event));\n } else if (this.uploadUrl) {\n this.aswEditorService.uploadImage(file).subscribe((response: HttpResponse<UploadResponse>) => this.watchUploadImage(response, event));\n } else {\n const reader = new FileReader();\n reader.onload = (e: ProgressEvent) => {\n const fr = e.currentTarget as FileReader;\n if (fr.result) {\n this.aswEditorService.insertImage(fr.result.toString());\n }\n };\n reader.readAsDataURL(file);\n }\n }\n }\n\n watchUploadImage(response: HttpResponse<{ imageUrl: string }> | any, event: any): void {\n const { imageUrl } = response.body;\n this.aswEditorService.insertImage(imageUrl);\n event.srcElement.value = null;\n }\n\n /**\n * Set custom class\n */\n setCustomClass(classId: string): void {\n if (classId === '-1') {\n this.execute.emit('clear');\n } else {\n this.aswEditorService.createCustomClass(this.customClasses$[+classId]);\n }\n }\n\n isButtonHidden(name: string): boolean {\n if (!name) {\n return false;\n }\n if (!(this.hiddenButtons instanceof Array)) {\n return false;\n }\n let result: any;\n for (const arr of this.hiddenButtons) {\n if (arr instanceof Array) {\n result = arr.find((item) => item === name);\n }\n if (result) {\n break;\n }\n }\n return result !== undefined;\n }\n\n focus(): void {\n this.execute.emit('focus');\n }\n}\n","<div class=\"asw-editor-toolbar\" *ngIf=\"showToolbar\">\r\n <div class=\"asw-editor-toolbar-set\">\r\n <button type=\"button\" matTooltip=\"Undo (Ctrl+Z)\" class=\"asw-editor-button\" (click)=\"triggerCommand('undo')\"\r\n [hidden]=\"isButtonHidden('undo')\" tabindex=\"-1\">\r\n <div [innerHTML]=\"icons.undo | aswSafeHtml\"></div>\r\n </button>\r\n <button type=\"button\" matTooltip=\"Redo (Ctrl+Y)\" class=\"asw-editor-button\" (click)=\"triggerCommand('redo')\"\r\n [hidden]=\"isButtonHidden('redo')\" tabindex=\"-1\">\r\n <div [innerHTML]=\"icons.redo | aswSafeHtml\"></div>\r\n </button>\r\n </div>\r\n <div class=\"asw-editor-toolbar-set\">\r\n <button [id]=\"'bold-'+id\" type=\"button\" matTooltip=\"Bold (Ctrl+B)\" class=\"asw-editor-button\" (click)=\"triggerCommand('bold')\"\r\n [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('bold')\" tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.bold | aswSafeHtml\"></div>\r\n </button>\r\n <button [id]=\"'italic-'+id\" type=\"button\" matTooltip=\"Italic (Ctrl+I)\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('italic')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('italic')\"\r\n tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.italic | aswSafeHtml\"></div>\r\n </button>\r\n <button [id]=\"'underline-'+id\" type=\"button\" matTooltip=\"Underline (Ctrl+U)\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('underline')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('underline')\"\r\n tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.underline | aswSafeHtml\"></div>\r\n </button>\r\n <button [id]=\"'strikeThrough-'+id\" type=\"button\" matTooltip=\"Strikethrough\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('strikeThrough')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('strikeThrough')\"\r\n tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.strikeThrough | aswSafeHtml\"></div>\r\n </button>\r\n <!-- <button [id]=\"'subscript-'+id\" type=\"button\" matTooltip=\"Subscript\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('subscript')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('subscript')\"\r\n tabindex=\"-1\">\r\n <mat-icon [ngClass]=\"{'asw-icon-disabled': htmlMode }\">subscript</mat-icon>\r\n </button>\r\n <button [id]=\"'superscript-'+id\" type=\"button\" matTooltip=\"Superscript\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('superscript')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('superscript')\"\r\n tabindex=\"-1\">\r\n <mat-icon [ngClass]=\"{'asw-icon-disabled': htmlMode }\">superscript</mat-icon>\r\n </button> -->\r\n </div>\r\n <div class=\"asw-editor-toolbar-set\">\r\n <button [id]=\"'foregroundColorPicker-'+id\" type=\"button\" class=\"asw-editor-button\"\r\n (click)=\"toggleColors('textColor')\" matTooltip=\"Text color\" [disabled]=\"htmlMode\"\r\n [hidden]=\"isButtonHidden('textColor')\" tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.formatColorText | aswSafeHtml\"></div>\r\n </button>\r\n <ng-container *ngIf=\"show\">\r\n <asw-color-picker [textColor]=\"textColor\"\r\n [isEditor]=\"true\"\r\n [backgroundColor]=\"backgroundColor\"\r\n (colorChange)=\"setColor($event)\"></asw-color-picker>\r\n </ng-container>\r\n </div>\r\n <div class=\"asw-editor-toolbar-set\">\r\n <button [id]=\"'alignLeft-'+id\" type=\"button\" matTooltip=\"Align left\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('justifyLeft')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('justifyLeft')\"\r\n tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.justifyLeft | aswSafeHtml\"></div>\r\n </button>\r\n <button [id]=\"'alignCenter-'+id\" type=\"button\" matTooltip=\"Align center\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('justifyCenter')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('justifyCenter')\"\r\n tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.justifyCenter | aswSafeHtml\"></div>\r\n </button>\r\n <button [id]=\"'alignRight-'+id\" type=\"button\" matTooltip=\"Align right\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('justifyRight')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('justifyRight')\"\r\n tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.justifyRight | aswSafeHtml\"></div>\r\n </button>\r\n <button [id]=\"'justifyFull-'+id\" type=\"button\" matTooltip=\"Justify Full\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('justifyFull')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('justifyFull')\"\r\n tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.justifyFull | aswSafeHtml\"></div>\r\n </button>\r\n </div>\r\n <div class=\"asw-editor-toolbar-set\">\r\n <button [id]=\"'indent-'+id\" type=\"button\" matTooltip=\"Indent\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('indent')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('indent')\"\r\n tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.indent | aswSafeHtml\"></div>\r\n </button>\r\n <button [id]=\"'outdent-'+id\" type=\"button\" matTooltip=\"Outdent\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('outdent')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('outdent')\"\r\n tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.outdent | aswSafeHtml\"></div>\r\n </button>\r\n </div>\r\n <div class=\"asw-editor-toolbar-set\">\r\n <button [id]=\"'insertUnorderedList-'+id\" type=\"button\" matTooltip=\"Unordered List\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('insertUnorderedList')\" [disabled]=\"htmlMode\"\r\n [hidden]=\"isButtonHidden('insertUnorderedList')\" tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.formatListBulleted | aswSafeHtml\"></div>\r\n </button>\r\n <button [id]=\"'insertOrderedList-'+id\" type=\"button\" matTooltip=\"Ordered List\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('insertOrderedList')\" [disabled]=\"htmlMode\"\r\n [hidden]=\"isButtonHidden('insertOrderedList')\" tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.formatListNumbered | aswSafeHtml\"></div>\r\n </button>\r\n </div>\r\n <div class=\"asw-editor-toolbar-set\">\r\n <asw-editor-select class=\"select-heading\" [options]=\"headings\" [(ngModel)]=\"block\"\r\n (change)=\"triggerCommand(block)\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('heading')\" tabindex=\"-1\">\r\n </asw-editor-select>\r\n </div>\r\n <!-- <div class=\"asw-editor-toolbar-set\">\r\n <asw-editor-select class=\"select-font\" [options]=\"fonts\" [(ngModel)]=\"fontName\" (change)=\"setFontName(fontName)\"\r\n [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('fontName')\" tabindex=\"-1\"></asw-editor-select>\r\n </div> -->\r\n <div class=\"asw-editor-toolbar-set\">\r\n <asw-editor-select class=\"select-font-size\" [options]=\"fontSizes\" [(ngModel)]=\"fontSize\"\r\n (change)=\"setFontSize(fontSize)\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('fontSize')\" tabindex=\"-1\">\r\n </asw-editor-select>\r\n </div>\r\n \r\n <div *ngIf=\"customClasses$\" class=\"asw-editor-toolbar-set\">\r\n <asw-editor-select class=\"select-custom-style\" [options]=\"customClassList\" [(ngModel)]=\"customClassId\"\r\n (change)=\"setCustomClass(customClassId)\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('customClasses')\"\r\n tabindex=\"-1\"></asw-editor-select>\r\n </div>\r\n <div class=\"asw-editor-toolbar-set\">\r\n <button [id]=\"'link-'+id\" type=\"button\" class=\"asw-editor-button\" (click)=\"insertUrl()\" matTooltip=\"Insert Link\"\r\n [disabled]=\"isLinkButtonDisabled\" [hidden]=\"isButtonHidden('link')\" tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.link | aswSafeHtml\"></div>\r\n </button>\r\n <button [id]=\"'unlink-'+id\" type=\"button\" class=\"asw-editor-button\" (click)=\"triggerCommand('unlink')\"\r\n matTooltip=\"Unlink\" [disabled]=\"htmlMode || !linkSelected\" [hidden]=\"isButtonHidden('unlink')\" tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.unlink | aswSafeHtml\"></div>\r\n </button>\r\n <!-- ToDO: need to improve for image and video upload -->\r\n <!-- <input style=\"display: none\" accept=\"image/*\" type=\"file\" (change)=\"onFileChanged($event)\" #fileInput>\r\n <button [id]=\"'insertImage-'+id\" type=\"button\" class=\"asw-editor-button\" (click)=\"focus(); fileInput.click()\"\r\n matTooltip=\"Insert Image\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('insertImage')\" tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.image | aswSafeHtml\"></div>\r\n </button>\r\n <button [id]=\"'insertVideo-'+id\" type=\"button\" class=\"asw-editor-button\" (click)=\"insertVideo()\"\r\n matTooltip=\"Insert Video\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('insertVideo')\" tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.videoCam | aswSafeHtml\"></div>\r\n </button> -->\r\n <button [id]=\"'insertHorizontalRule-'+id\" type=\"button\" matTooltip=\"Horizontal Line\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('insertHorizontalRule')\" [disabled]=\"htmlMode\"\r\n [hidden]=\"isButtonHidden('insertHorizontalRule')\" tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.remove | aswSafeHtml\"></div>\r\n </button>\r\n </div>\r\n <div class=\"asw-editor-toolbar-set\">\r\n <button [id]=\"'clearFormatting-'+id\" type=\"button\" matTooltip=\"Remove formatting\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('removeFormat')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('removeFormat')\"\r\n tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.formatClear | aswSafeHtml\"></div>\r\n </button>\r\n </div>\r\n <div class=\"asw-editor-toolbar-set\">\r\n <button [id]=\"'toggleEditorMode-'+id\" type=\"button\" matTooltip=\"HTML Code\" class=\"asw-editor-button\"\r\n (click)=\"triggerCommand('toggleEditorMode')\" [hidden]=\"isButtonHidden('toggleEditorMode')\" tabindex=\"-1\">\r\n <div [ngClass]=\"{'asw-icon-disabled': htmlMode }\" [innerHTML]=\"icons.code | aswSafeHtml\"></div>\r\n </button>\r\n </div>\r\n <ng-content></ng-content>\r\n</div>\r\n","import { DOCUMENT } from '@angular/common';\nimport {\n AfterViewInit,\n Attribute,\n ChangeDetectorRef,\n Component,\n ContentChild,\n ElementRef,\n EventEmitter,\n forwardRef,\n HostBinding,\n HostListener,\n Inject,\n Input,\n OnDestroy,\n OnInit,\n Output,\n Renderer2,\n SecurityContext,\n TemplateRef,\n ViewChild\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { AswEditorConfig, aswEditorConfig, ElementDOM } from '@asoftwareworld/form-builder-pro/api';\nimport { isDefined } from '@asoftwareworld/form-builder-pro/utils';\nimport { AswEditorToolbar } from './editor-toolbar/editor-toolbar';\nimport { AswEditorService } from './editor.service';\n\n@Component({\n selector: 'asw-editor',\n templateUrl: './editor.html',\n styleUrls: ['./editor.scss'],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => AswEditor),\n multi: true\n }\n ]\n})\nexport class AswEditor implements OnInit, ControlValueAccessor, AfterViewInit, OnDestroy, ElementDOM {\n private onChange: ((value: string) => void) | any;\n private onTouched!: () => void;\n\n modeVisual = true;\n showPlaceholder = false;\n disabled = false;\n focused = false;\n touched = false;\n changed = false;\n\n focusInstance: any;\n blurInstance: any;\n\n @Input() id = '';\n @Input() config: AswEditorConfig | any = aswEditorConfig;\n @Input() placeholder = '';\n @Input() tabIndex: number | null;\n\n @Output() html: any;\n\n @ViewChild('editor', { static: true }) textArea!: ElementRef;\n @ViewChild('editorWrapper', { static: true }) editorWrapper!: ElementRef;\n @ViewChild('editorToolbar') editorToolbar?: AswEditorToolbar;\n @ContentChild('customButtons') customButtonsTemplateRef: TemplateRef<any> | any;\n executeCommandFn = this.executeCommand.bind(this);\n\n @Output() viewMode = new EventEmitter<boolean>();\n\n /** emits `blur` event when focused out from the textarea */\n @Output('blur') blurEvent: EventEmitter<FocusEvent> = new EventEmitter<FocusEvent>();\n\n /** emits `focus` event when focused in to the textarea */\n // eslint-disable-next-line @angular-eslint/no-output-rename, @angular-eslint/no-output-native\n @Output('focus') focusEvent: EventEmitter<FocusEvent> = new EventEmitter<FocusEvent>();\n\n @HostBinding('attr.tabindex') tabindex = -1;\n\n @HostListener('focus')\n onFocus(): void {\n this.focus();\n }\n\n constructor(\n private renderer: Renderer2,\n private aswEditorService: AswEditorService,\n @Inject(DOCUMENT) private doc: any,\n private sanitizer: DomSanitizer,\n private cdRef: ChangeDetectorRef,\n @Attribute('tabindex') defaultTabIndex: string,\n @Attribute('autofocus') private autoFocus: any,\n public elementDOM: ElementRef<HTMLElement>\n ) {\n const parsedTabIndex = Number(defaultTabIndex);\n this.tabIndex = parsedTabIndex || parsedTabIndex === 0 ? parsedTabIndex : null;\n }\n\n ngOnInit(): void {\n this.config.toolbarPosition = this.config.toolbarPosition ? this.config.toolbarPosition : aswEditorConfig.toolbarPosition;\n }\n\n ngAfterViewInit(): void {\n if (isDefined(this.autoFocus)) {\n this.focus();\n }\n }\n\n onPaste(event: ClipboardEvent): any {\n if (this.config.rawPaste) {\n event.preventDefault();\n const text = event.clipboardData?.getData('text/plain');\n document.execCommand('insertHTML', false, text);\n return text;\n }\n }\n\n /**\n * Executed command from editor header buttons\n * @param command string from triggerCommand\n */\n executeCommand(command: string, value?: string): void {\n this.focus();\n if (command === 'focus') {\n return;\n }\n if (command === 'toggleEditorMode') {\n this.toggleEditorMode(this.modeVisual);\n } else if (command !== '') {\n if (command === 'clear') {\n this.aswEditorService.removeSelectedElements(this.getCustomTags());\n this.onContentChange(this.textArea.nativeElement);\n } else if (command === 'default') {\n this.aswEditorService.removeSelectedElements('h1,h2,h3,h4,h5,h6,p,pre');\n this.onContentChange(this.textArea.nativeElement);\n } else {\n this.aswEditorService.executeCommand(command, value);\n }\n this.exec();\n }\n }\n\n /**\n * focus event\n */\n onTextAreaFocus(event: FocusEvent): void {\n if (this.focused) {\n event.stopPropagation();\n return;\n }\n this.focused = true;\n this.focusEvent.emit(event);\n if (!this.touched || !this.changed) {\n this.aswEditorService.executeInNextQueueIteration(() => {\n this.configure();\n this.touched = true;\n });\n }\n }\n\n /**\n * @description fires when cursor leaves textarea\n */\n public onTextAreaMouseOut(event: MouseEvent): void {\n this.aswEditorService.saveSelection();\n }\n\n /**\n * blur event\n */\n onTextAreaBlur(event: FocusEvent): void {\n /**\n * save selection if focussed out\n */\n this