UNPKG

@iget/editor

Version:

WYSIWYG Editor for Angular Material applications

817 lines (804 loc) 60.9 kB
import * as i0 from '@angular/core'; import { Injectable, Component, Inject, EventEmitter, Input, Output, HostListener, forwardRef, ViewChild, NgModule } from '@angular/core'; import * as i7 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i1 from '@angular/forms'; import { Validators, NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/forms'; import * as i4 from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import * as i2$1 from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'; import * as i3 from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button'; import * as i3$1 from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu'; import * as i1$1 from '@angular/platform-browser'; import { Subject } from 'rxjs'; import * as i2 from '@angular/material/form-field'; import * as i5 from '@angular/material/input'; import { MatInputModule } from '@angular/material/input'; const DefaultIgetEditorConfig = { editable: true, spellcheck: true, height: 'auto', minHeight: '0', width: 'auto', minWidth: '0', translate: 'yes', enableToolbar: true, showToolbar: true, placeholder: 'Enter text here...', toolbarPosition: 'after', resizable: true, toolbar: [ ['bold', 'italic', 'underline', 'strikethrough'], ['font', 'fontSize', 'textColor', 'fillColor', 'removeFormat'], ['align'], ['indent', 'quote', 'unorderedList', 'orderedList'], ['undo', 'redo'], ['anchor'] ] }; /** * enable or disable toolbar based on configuration * * @param value toolbar item * @param toolbar toolbar configuration object */ function canEnableToolbarOptions(value, toolbar) { if (value) { if (toolbar['length'] === 0) { return true; } else { const found = toolbar.filter(array => { return array.indexOf(value) !== -1; }); return found.length ? true : false; } } else { return false; } } /** * return vertical if the element is the resizer property is set to basic * * @param resizer type of resizer, either basic or stack */ function canResize(resizer) { if (resizer === 'basic') { return 'vertical'; } return false; } /** * save selection when the editor is focussed out */ function saveSelection() { if (window.getSelection) { const sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { return sel.getRangeAt(0); } } else if (document.getSelection && document.createRange) { return document.createRange(); } return null; } /** * restore selection when the editor is focussed in * * @param range saved selection when the editor is focussed out */ function restoreSelection(range) { if (range) { if (window.getSelection) { const sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); return true; } else if (document.getSelection && range.select) { range.select(); return true; } } else { return false; } } var Utils = /*#__PURE__*/Object.freeze({ __proto__: null, canEnableToolbarOptions: canEnableToolbarOptions, canResize: canResize, saveSelection: saveSelection, restoreSelection: restoreSelection }); /** time in which the message has to be cleared */ const DURATION = 7000; class MessageService { constructor() { /** variable to hold the user message */ this.message = new Subject(); } /** returns the message sent by the editor */ getMessage() { return this.message.asObservable(); } /** * sends message to the editor * * @param message message to be sent */ sendMessage(message) { this.message.next(message); this.clearMessageIn(DURATION); } /** * a short interval to clear message * * @param milliseconds time in seconds in which the message has to be cleared */ clearMessageIn(milliseconds) { setTimeout(() => { this.message.next(undefined); }, milliseconds); } } MessageService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: MessageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); MessageService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: MessageService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: MessageService, decorators: [{ type: Injectable }], ctorParameters: function () { return []; } }); class CommandExecutorService { constructor() { /** saves the selection from the editor when focussed out */ this.savedSelection = undefined; } /** * executes command from the toolbar * * @param command command to be executed */ execute(command) { if (!this.savedSelection && command !== 'enableObjectResizing') { throw new Error('Range out of Editor'); } if (command === 'enableObjectResizing') { document.execCommand('enableObjectResizing', true); } if (command === 'blockquote') { document.execCommand('formatBlock', false, 'blockquote'); } if (command === 'removeBlockquote') { document.execCommand('formatBlock', false, 'div'); } document.execCommand(command, false, null); } /** * inserts image in the editor * * @param imageURI url of the image to be inserted */ insertImage(imageURI) { if (this.savedSelection) { if (imageURI) { const restored = restoreSelection(this.savedSelection); if (restored) { const inserted = document.execCommand('insertImage', false, imageURI); if (!inserted) { throw new Error('Invalid URL'); } } } } else { throw new Error('Range out of the editor'); } } /** * inserts image in the editor * * @param videParams url of the image to be inserted */ insertVideo(videParams) { if (this.savedSelection) { if (videParams) { const restored = restoreSelection(this.savedSelection); if (restored) { if (this.isYoutubeLink(videParams.videoUrl)) { const youtubeURL = '<iframe width="' + videParams.width + '" height="' + videParams.height + '"' + 'src="' + videParams.videoUrl + '"></iframe>'; this.insertHtml(youtubeURL); } else if (this.checkTagSupportInBrowser('video')) { if (this.isValidURL(videParams.videoUrl)) { const videoSrc = '<video width="' + videParams.width + '" height="' + videParams.height + '"' + ' controls="true"><source src="' + videParams.videoUrl + '"></video>'; this.insertHtml(videoSrc); } else { throw new Error('Invalid video URL'); } } else { throw new Error('Unable to insert video'); } } } } else { throw new Error('Range out of the editor'); } } /** * checks the input url is a valid youtube URL or not * * @param url Youtue URL */ isYoutubeLink(url) { const ytRegExp = /^(http(s)?:\/\/)?((w){3}.)?youtu(be|.be)?(\.com)?\/.+/; return ytRegExp.test(url); } /** * check whether the string is a valid url or not * @param url url */ isValidURL(url) { const urlRegExp = /(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/; return urlRegExp.test(url); } /** * uploads image to the server * * @param file file that has to be uploaded * @param endPoint enpoint to which the image has to be uploaded */ uploadImage(file, endPoint) { // if (!endPoint) { // throw new Error('Image Endpoint isn`t provided or invalid'); // } // // const formData: FormData = new FormData(); // // if (file) { // // formData.append('file', file); // // const req = new HttpRequest('POST', endPoint, formData, { // reportProgress: true // }); // // return this._http.request(req); // // } else { // throw new Error('Invalid Image'); // } } /** * inserts link in the editor * * @param params parameters that holds the information for the link */ createLink(params) { if (this.savedSelection) { /** * check whether the saved selection contains a range or plain selection */ if (params.urlNewTab) { const newUrl = '<a href="' + params.urlLink + '" target="_blank">' + params.urlText + '</a>'; if (document.getSelection().type !== 'Range') { const restored = restoreSelection(this.savedSelection); if (restored) { this.insertHtml(newUrl); } } else { throw new Error('Only new links can be inserted. You cannot edit URL`s'); } } else { const restored = restoreSelection(this.savedSelection); if (restored) { document.execCommand('createLink', false, params.urlLink); } } } else { throw new Error('Range out of the editor'); } } /** * insert color either font or background * * @param color color to be inserted * @param where where the color has to be inserted either text/background */ insertColor(color, where) { if (this.savedSelection) { const restored = restoreSelection(this.savedSelection); if (restored && this.checkSelection()) { if (where === 'textColor') { document.execCommand('foreColor', false, color); } else { document.execCommand('hiliteColor', false, color); } } } else { throw new Error('Range out of the editor'); } } /** * set font size for text * * @param fontSize font-size to be set */ setFontSize(fontSize) { if (this.savedSelection && this.checkSelection()) { const deletedValue = this.deleteAndGetElement(); if (deletedValue) { const restored = restoreSelection(this.savedSelection); if (restored) { if (this.isNumeric(fontSize)) { const fontPx = '<span style="font-size: ' + fontSize + 'px;">' + deletedValue + '</span>'; this.insertHtml(fontPx); } else { const fontPx = '<span style="font-size: ' + fontSize + ';">' + deletedValue + '</span>'; this.insertHtml(fontPx); } } } } else { throw new Error('Range out of the editor'); } } /** * set font name/family for text * * @param fontName font-family to be set */ setFontName(fontName) { if (this.savedSelection && this.checkSelection()) { const deletedValue = this.deleteAndGetElement(); if (deletedValue) { const restored = restoreSelection(this.savedSelection); if (restored) { if (this.isNumeric(fontName)) { const fontFamily = '<span style="font-family: ' + fontName + 'px;">' + deletedValue + '</span>'; this.insertHtml(fontFamily); } else { const fontFamily = '<span style="font-family: ' + fontName + ';">' + deletedValue + '</span>'; this.insertHtml(fontFamily); } } } } else { throw new Error('Range out of the editor'); } } /** insert HTML */ insertHtml(html) { const isHTMLInserted = document.execCommand('insertHTML', false, html); if (!isHTMLInserted) { throw new Error('Unable to perform the operation'); } } /** * check whether the value is a number or string * if number return true * else return false */ isNumeric(value) { return /^-{0,1}\d+$/.test(value); } /** delete the text at selected range and return the value */ deleteAndGetElement() { let slectedText; if (this.savedSelection) { slectedText = this.savedSelection.toString(); this.savedSelection.deleteContents(); return slectedText; } return false; } /** check any slection is made or not */ checkSelection() { const slectedText = this.savedSelection.toString(); if (slectedText.length === 0) { throw new Error('No Selection Made'); } return true; } /** * check tag is supported by browser or not * * @param tag HTML tag */ checkTagSupportInBrowser(tag) { return !(document.createElement(tag) instanceof HTMLUnknownElement); } } CommandExecutorService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: CommandExecutorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); CommandExecutorService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: CommandExecutorService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: CommandExecutorService, decorators: [{ type: Injectable }] }); class LinkDialogComponent { constructor(formBuilder, data) { this.formBuilder = formBuilder; this.data = data; } ngOnInit() { this.form = this.buildForm(); if (this.data.selection) { this.form.patchValue({ label: this.data.selection.toString() }); } } buildForm() { return this.formBuilder.group({ 'label': [null, [Validators.required]], 'url': [null, [Validators.required]], }); } } LinkDialogComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: LinkDialogComponent, deps: [{ token: i1.FormBuilder }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); LinkDialogComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.1", type: LinkDialogComponent, selector: "iget-editor-link-dialog", providers: [], ngImport: i0, template: "<h1 mat-dialog-title>Edit Link</h1>\n<div mat-dialog-content [formGroup]=\"form\">\n <mat-form-field floatLabel=\"always\">\n <input matInput placeholder=\"Label\" formControlName=\"label\">\n </mat-form-field>\n <mat-form-field floatLabel=\"always\">\n <input matInput placeholder=\"Web address or email\" formControlName=\"url\" cdkFocusInitial>\n </mat-form-field>\n</div>\n<div mat-dialog-actions>\n <button mat-button [mat-dialog-close]>Cancel</button>\n <button mat-button [disabled]=\"form.invalid\" [mat-dialog-close]=\"form.value\">OK</button>\n</div>\n", styles: ["[mat-dialog-content] mat-form-field{width:100%}\n"], components: [{ type: i2.MatFormField, selector: "mat-form-field", inputs: ["color", "floatLabel", "appearance", "hideRequiredMarker", "hintLabel"], exportAs: ["matFormField"] }, { type: i3.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }], directives: [{ type: i4.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { type: i4.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["id", "disabled", "required", "type", "value", "readonly", "placeholder", "errorStateMatcher", "aria-describedby"], exportAs: ["matInput"] }, { type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i4.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]" }, { type: i4.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["type", "mat-dialog-close", "aria-label", "matDialogClose"], exportAs: ["matDialogClose"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: LinkDialogComponent, decorators: [{ type: Component, args: [{ selector: 'iget-editor-link-dialog', providers: [], template: "<h1 mat-dialog-title>Edit Link</h1>\n<div mat-dialog-content [formGroup]=\"form\">\n <mat-form-field floatLabel=\"always\">\n <input matInput placeholder=\"Label\" formControlName=\"label\">\n </mat-form-field>\n <mat-form-field floatLabel=\"always\">\n <input matInput placeholder=\"Web address or email\" formControlName=\"url\" cdkFocusInitial>\n </mat-form-field>\n</div>\n<div mat-dialog-actions>\n <button mat-button [mat-dialog-close]>Cancel</button>\n <button mat-button [disabled]=\"form.invalid\" [mat-dialog-close]=\"form.value\">OK</button>\n</div>\n", styles: ["[mat-dialog-content] mat-form-field{width:100%}\n"] }] }], ctorParameters: function () { return [{ type: i1.FormBuilder }, { type: undefined, decorators: [{ type: Inject, args: [MAT_DIALOG_DATA] }] }]; } }); const COLOR_PALETTE = [ '#000000', '#444444', '#666666', '#999999', '#cccccc', '#eeeeee', '#f3f3f3', '#ffffff', null, '#ff0000', '#ff9900', '#ffff00', '#00ff00', '#00ffff', '#0000ff', '#9900ff', '#ff00ff', null, '#f4cccc', '#fce5cd', '#fff2cc', '#d9ead3', '#d0e0e3', '#cfe2f3', '#d9d2e9', '#ead1dc', '#ea9999', '#f9cb9c', '#ffe599', '#b6d7a8', '#a2c4c9', '#9fc5e8', '#b4a7d6', '#d5a6bd', '#e06666', '#f6b26b', '#ffd966', '#93c47d', '#76a5af', '#6fa8dc', '#8e7cc3', '#c27ba0', '#cc0000', '#e69138', '#f1c232', '#6aa84f', '#45818e', '#3d85c6', '#674ea7', '#a64d79', '#990000', '#b45f06', '#bf9000', '#38761d', '#134f5c', '#0b5394', '#351c75', '#741b47', '#660000', '#783f04', '#7f6000', '#274e13', '#0c343d', '#073763', '#20124d', '#4c1130' ]; class IgetEditorToolbarGroupComponent { } IgetEditorToolbarGroupComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: IgetEditorToolbarGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); IgetEditorToolbarGroupComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.1", type: IgetEditorToolbarGroupComponent, selector: "iget-editor-toolbar-group", providers: [], ngImport: i0, template: "<ng-content select=\"ng-container, iget-editor-toolbar-button, mat-menu\"></ng-content>\n", styles: [":host{padding-left:4px;margin-bottom:2px}:host:not(:last-child){padding-right:4px;border-right:1px solid #e0e0e0}\n"] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: IgetEditorToolbarGroupComponent, decorators: [{ type: Component, args: [{ selector: 'iget-editor-toolbar-group', providers: [], template: "<ng-content select=\"ng-container, iget-editor-toolbar-button, mat-menu\"></ng-content>\n", styles: [":host{padding-left:4px;margin-bottom:2px}:host:not(:last-child){padding-right:4px;border-right:1px solid #e0e0e0}\n"] }] }] }); class IgetEditorToolbarButtonComponent { constructor() { this.menu = false; this.clicked = new EventEmitter(); } handleClick($event) { this.clicked.emit($event); $event.stopPropagation(); } } IgetEditorToolbarButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: IgetEditorToolbarButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); IgetEditorToolbarButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.1", type: IgetEditorToolbarButtonComponent, selector: "iget-editor-toolbar-button", inputs: { icon: "icon", title: "title", menu: "menu" }, outputs: { clicked: "clicked" }, providers: [], ngImport: i0, template: "<button mat-icon-button [title]=\"title\" (click)=\"handleClick($event)\" *ngIf=\"!menu\">\n <mat-icon>{{ icon }}</mat-icon>\n</button>\n<ng-container *ngIf=\"menu\">\n <button mat-icon-button [title]=\"title\" (click)=\"handleClick($event)\" *ngIf=\"menu\" [matMenuTriggerFor]=\"dropdown\">\n <mat-icon>{{ icon }}</mat-icon>\n </button>\n <mat-menu #dropdown=\"matMenu\">\n <ng-content select=\"[mat-menu-item],[matMenuContent]\"></ng-content>\n </mat-menu>\n</ng-container>\n", styles: ["button:focus{outline:none}button.mat-button{color:#616161}button.mat-button:hover{color:#212121}\n"], components: [{ type: i3.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: i3$1.MatMenu, selector: "mat-menu", exportAs: ["matMenu"] }], directives: [{ type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", exportAs: ["matMenuTrigger"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: IgetEditorToolbarButtonComponent, decorators: [{ type: Component, args: [{ selector: 'iget-editor-toolbar-button', providers: [], template: "<button mat-icon-button [title]=\"title\" (click)=\"handleClick($event)\" *ngIf=\"!menu\">\n <mat-icon>{{ icon }}</mat-icon>\n</button>\n<ng-container *ngIf=\"menu\">\n <button mat-icon-button [title]=\"title\" (click)=\"handleClick($event)\" *ngIf=\"menu\" [matMenuTriggerFor]=\"dropdown\">\n <mat-icon>{{ icon }}</mat-icon>\n </button>\n <mat-menu #dropdown=\"matMenu\">\n <ng-content select=\"[mat-menu-item],[matMenuContent]\"></ng-content>\n </mat-menu>\n</ng-container>\n", styles: ["button:focus{outline:none}button.mat-button{color:#616161}button.mat-button:hover{color:#212121}\n"] }] }], propDecorators: { icon: [{ type: Input }], title: [{ type: Input }], menu: [{ type: Input }], clicked: [{ type: Output }] } }); class IgetEditorToolbarComponent { constructor(_messageService, _commandExecutorService, dialog) { this._messageService = _messageService; this._commandExecutorService = _commandExecutorService; this.dialog = dialog; /** font family name */ this.fontName = ''; /** font size */ this.fontSize = ''; /** * Emits an event when a toolbar button is clicked */ this.execute = new EventEmitter(); this.colors = COLOR_PALETTE; } /** * enable or diable toolbar based on configuration * * @param value name of the toolbar buttons */ canEnableToolbarOptions(value) { return canEnableToolbarOptions(value, this.config['toolbar']); } /** * triggers command from the toolbar to be executed and emits an event * * @param command name of the command to be executed */ triggerCommand(command) { this.execute.emit(command); } /** * inserts link in the editor */ insertLink() { const linkDialogRef = this.dialog.open(LinkDialogComponent, { data: { selection: this._commandExecutorService.savedSelection } }); linkDialogRef.afterClosed().subscribe((data) => { if (data) { let urlLink = data.url; const emailPattern = new RegExp([ '^(([^<>()\\[\\]\\.,;:\\s@\\"]+(\\.[^<>()\\[\\]\\.,;:\\s@\\"]+)*)', '|(\\".+\\"))@(([^<>()[\\]\\.,;:\\s@\\"]+\\.)+[^<>()[\\]\\.,;:\\s', '@\\"]{2,})$' ].join('')); if (emailPattern.exec(urlLink)) { urlLink = `mailto:${urlLink}`; } this._commandExecutorService.createLink({ urlLink, urlText: data.label, }); } }); } /** insert text/background color */ setTextColor(color) { this.insertColor(color, 'textColor'); } /** insert text/background color */ setBackgroundColor(color) { this.insertColor(color, 'backgroundColor'); } /** insert text/background color */ insertColor(color, where) { try { this._commandExecutorService.insertColor(color, where); } catch (error) { this._messageService.sendMessage(error.message); } } /** set font size */ setFontSize(fontSize) { try { this._commandExecutorService.setFontSize(fontSize); } catch (error) { this._messageService.sendMessage(error.message); } } /** set font Name/family */ setFontName(fontName) { try { this._commandExecutorService.setFontName(fontName); } catch (error) { this._messageService.sendMessage(error.message); } } } IgetEditorToolbarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: IgetEditorToolbarComponent, deps: [{ token: MessageService }, { token: CommandExecutorService }, { token: i4.MatDialog }], target: i0.ɵɵFactoryTarget.Component }); IgetEditorToolbarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.1", type: IgetEditorToolbarComponent, selector: "iget-editor-toolbar", inputs: { config: "config" }, outputs: { execute: "execute" }, providers: [], ngImport: i0, template: "<iget-editor-toolbar-group *ngFor=\"let group of config\">\n <ng-container *ngFor=\"let button of group\">\n <iget-editor-toolbar-button *ngIf=\"button === 'bold'\" icon=\"format_bold\" title=\"Bold\" (clicked)=\"triggerCommand('bold')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'italic'\" icon=\"format_italic\" title=\"Italic\" (clicked)=\"triggerCommand('italic')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'underline'\" icon=\"format_underline\" title=\"Underline\" (clicked)=\"triggerCommand('underline')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'strikethrough'\" icon=\"format_strikethrough\" title=\"Strikethrough\" (clicked)=\"triggerCommand('strikethrough')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'font'\" icon=\"font_download\" title=\"Font Family\" [menu]=\"true\">\n <button mat-menu-item (clicked)=\"setFontName('sans-serif')\" [style.fontFamily]=\"'sans-serif'\">Sans-serif</button>\n <button mat-menu-item (clicked)=\"setFontName('serif')\" [style.fontFamily]=\"'serif'\">Serif</button>\n <button mat-menu-item (clicked)=\"setFontName('monospace')\" [style.fontFamily]=\"'monospace'\">Monospace</button>\n </iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'fontSize'\" icon=\"format_size\" title=\"Font size\" [menu]=\"true\">\n <button mat-menu-item (clicked)=\"setFontSize('10')\" [style.fontSize]=\"'10px'\">Small</button>\n <button mat-menu-item (clicked)=\"setFontSize('13')\" [style.fontSize]=\"'13px'\">Regular</button>\n <button mat-menu-item (clicked)=\"setFontSize('18')\" [style.fontSize]=\"'18px'\">Large</button>\n <button mat-menu-item (clicked)=\"setFontSize('32')\" [style.fontSize]=\"'32px'\">Huge</button>\n </iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'textColor'\" icon=\"format_color_text\" title=\"Text color\" [menu]=\"true\">\n <div class=\"colors\" matMenuContent>\n <span *ngFor=\"let color of colors\" [style.backgroundColor]=\"color\" [class.separator]=\"!color\" (clicked)=\"setTextColor(color)\"></span>\n </div>\n </iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'fillColor'\" icon=\"format_color_fill\" title=\"Fill color\" [menu]=\"true\">\n <div class=\"colors\" matMenuContent>\n <span *ngFor=\"let color of colors\" [style.backgroundColor]=\"color\" [class.separator]=\"!color\" (clicked)=\"setBackgroundColor(color)\"></span>\n </div>\n </iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'removeFormat'\" icon=\"format_clear\" title=\"Clear formatting\" (clicked)=\"triggerCommand('removeFormat')\"></iget-editor-toolbar-button>\n <ng-container *ngIf=\"button === 'align'\">\n <iget-editor-toolbar-button icon=\"format_align_left\" title=\"Align left\" (clicked)=\"triggerCommand('justifyLeft')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button icon=\"format_align_center\" title=\"Align center\" (clicked)=\"triggerCommand('justifyCenter')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button icon=\"format_align_right\" title=\"Align right\" (clicked)=\"triggerCommand('justifyRight')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button icon=\"format_align_justify\" title=\"Justify\" (clicked)=\"triggerCommand('justifyFull')\"></iget-editor-toolbar-button>\n </ng-container>\n <ng-container *ngIf=\"button === 'indent'\">\n <iget-editor-toolbar-button icon=\"format_indent_decrease\" title=\"Outdent\" (clicked)=\"triggerCommand('outdent')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button icon=\"format_indent_increase\" title=\"Indent\" (clicked)=\"triggerCommand('indent')\"></iget-editor-toolbar-button>\n </ng-container>\n <iget-editor-toolbar-button *ngIf=\"button === 'quote'\" icon=\"format_quote\" title=\"Blockquote\" (clicked)=\"triggerCommand('blockquote')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'unorderedList'\" icon=\"format_list_bulleted\" title=\"Unordered list\" (clicked)=\"triggerCommand('insertUnorderedList')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'orderedList'\" icon=\"format_list_numbered\" title=\"Ordered list\" (clicked)=\"triggerCommand('insertOrderedList')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'undo'\" icon=\"undo\" title=\"Undo\" (clicked)=\"triggerCommand('undo')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'redo'\" icon=\"redo\" title=\"Redo\" (clicked)=\"triggerCommand('redo')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'anchor'\" icon=\"insert_link\" title=\"Insert link\" (clicked)=\"insertLink()\"></iget-editor-toolbar-button>\n </ng-container>\n", styles: [":host{display:flex;flex-wrap:wrap;box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f}.mat-button{padding:0;min-width:36px}.colors{display:flex;flex-wrap:wrap;width:180px;margin:0 8px}.colors>span:not(.separator){width:calc(12.5% - 2px);display:block;height:auto;padding-top:calc(12.5% - 2px);cursor:pointer;border:1px solid white}.colors>span:not(.separator):hover{border-color:#000}.colors>span.separator{width:100%;height:4px}\n"], components: [{ type: IgetEditorToolbarGroupComponent, selector: "iget-editor-toolbar-group" }, { type: IgetEditorToolbarButtonComponent, selector: "iget-editor-toolbar-button", inputs: ["icon", "title", "menu"], outputs: ["clicked"] }, { type: i3$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["disabled", "disableRipple", "role"], exportAs: ["matMenuItem"] }], directives: [{ type: i7.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: IgetEditorToolbarComponent, decorators: [{ type: Component, args: [{ selector: 'iget-editor-toolbar', providers: [], template: "<iget-editor-toolbar-group *ngFor=\"let group of config\">\n <ng-container *ngFor=\"let button of group\">\n <iget-editor-toolbar-button *ngIf=\"button === 'bold'\" icon=\"format_bold\" title=\"Bold\" (clicked)=\"triggerCommand('bold')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'italic'\" icon=\"format_italic\" title=\"Italic\" (clicked)=\"triggerCommand('italic')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'underline'\" icon=\"format_underline\" title=\"Underline\" (clicked)=\"triggerCommand('underline')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'strikethrough'\" icon=\"format_strikethrough\" title=\"Strikethrough\" (clicked)=\"triggerCommand('strikethrough')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'font'\" icon=\"font_download\" title=\"Font Family\" [menu]=\"true\">\n <button mat-menu-item (clicked)=\"setFontName('sans-serif')\" [style.fontFamily]=\"'sans-serif'\">Sans-serif</button>\n <button mat-menu-item (clicked)=\"setFontName('serif')\" [style.fontFamily]=\"'serif'\">Serif</button>\n <button mat-menu-item (clicked)=\"setFontName('monospace')\" [style.fontFamily]=\"'monospace'\">Monospace</button>\n </iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'fontSize'\" icon=\"format_size\" title=\"Font size\" [menu]=\"true\">\n <button mat-menu-item (clicked)=\"setFontSize('10')\" [style.fontSize]=\"'10px'\">Small</button>\n <button mat-menu-item (clicked)=\"setFontSize('13')\" [style.fontSize]=\"'13px'\">Regular</button>\n <button mat-menu-item (clicked)=\"setFontSize('18')\" [style.fontSize]=\"'18px'\">Large</button>\n <button mat-menu-item (clicked)=\"setFontSize('32')\" [style.fontSize]=\"'32px'\">Huge</button>\n </iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'textColor'\" icon=\"format_color_text\" title=\"Text color\" [menu]=\"true\">\n <div class=\"colors\" matMenuContent>\n <span *ngFor=\"let color of colors\" [style.backgroundColor]=\"color\" [class.separator]=\"!color\" (clicked)=\"setTextColor(color)\"></span>\n </div>\n </iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'fillColor'\" icon=\"format_color_fill\" title=\"Fill color\" [menu]=\"true\">\n <div class=\"colors\" matMenuContent>\n <span *ngFor=\"let color of colors\" [style.backgroundColor]=\"color\" [class.separator]=\"!color\" (clicked)=\"setBackgroundColor(color)\"></span>\n </div>\n </iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'removeFormat'\" icon=\"format_clear\" title=\"Clear formatting\" (clicked)=\"triggerCommand('removeFormat')\"></iget-editor-toolbar-button>\n <ng-container *ngIf=\"button === 'align'\">\n <iget-editor-toolbar-button icon=\"format_align_left\" title=\"Align left\" (clicked)=\"triggerCommand('justifyLeft')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button icon=\"format_align_center\" title=\"Align center\" (clicked)=\"triggerCommand('justifyCenter')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button icon=\"format_align_right\" title=\"Align right\" (clicked)=\"triggerCommand('justifyRight')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button icon=\"format_align_justify\" title=\"Justify\" (clicked)=\"triggerCommand('justifyFull')\"></iget-editor-toolbar-button>\n </ng-container>\n <ng-container *ngIf=\"button === 'indent'\">\n <iget-editor-toolbar-button icon=\"format_indent_decrease\" title=\"Outdent\" (clicked)=\"triggerCommand('outdent')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button icon=\"format_indent_increase\" title=\"Indent\" (clicked)=\"triggerCommand('indent')\"></iget-editor-toolbar-button>\n </ng-container>\n <iget-editor-toolbar-button *ngIf=\"button === 'quote'\" icon=\"format_quote\" title=\"Blockquote\" (clicked)=\"triggerCommand('blockquote')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'unorderedList'\" icon=\"format_list_bulleted\" title=\"Unordered list\" (clicked)=\"triggerCommand('insertUnorderedList')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'orderedList'\" icon=\"format_list_numbered\" title=\"Ordered list\" (clicked)=\"triggerCommand('insertOrderedList')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'undo'\" icon=\"undo\" title=\"Undo\" (clicked)=\"triggerCommand('undo')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'redo'\" icon=\"redo\" title=\"Redo\" (clicked)=\"triggerCommand('redo')\"></iget-editor-toolbar-button>\n <iget-editor-toolbar-button *ngIf=\"button === 'anchor'\" icon=\"insert_link\" title=\"Insert link\" (clicked)=\"insertLink()\"></iget-editor-toolbar-button>\n </ng-container>\n", styles: [":host{display:flex;flex-wrap:wrap;box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f}.mat-button{padding:0;min-width:36px}.colors{display:flex;flex-wrap:wrap;width:180px;margin:0 8px}.colors>span:not(.separator){width:calc(12.5% - 2px);display:block;height:auto;padding-top:calc(12.5% - 2px);cursor:pointer;border:1px solid white}.colors>span:not(.separator):hover{border-color:#000}.colors>span.separator{width:100%;height:4px}\n"] }] }], ctorParameters: function () { return [{ type: MessageService }, { type: CommandExecutorService }, { type: i4.MatDialog }]; }, propDecorators: { config: [{ type: Input }], execute: [{ type: Output }] } }); class IgetEditorMessageComponent { /** * @param _messageService service to send message to the editor */ constructor(_messageService) { this._messageService = _messageService; /** property that holds the message to be displayed on the editor */ this.ngxMessage = undefined; this._messageService.getMessage().subscribe((message) => this.ngxMessage = message); } /** * clears editor message */ clearMessage() { this.ngxMessage = undefined; } } IgetEditorMessageComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: IgetEditorMessageComponent, deps: [{ token: MessageService }], target: i0.ɵɵFactoryTarget.Component }); IgetEditorMessageComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.1", type: IgetEditorMessageComponent, selector: "iget-editor-message", ngImport: i0, template: "<div class=\"iget-editor-message\" *ngIf=\"ngxMessage\" (dblclick)=\"clearMessage()\">\n {{ ngxMessage }}\n</div>\n", styles: [".iget-editor-message{font-size:80%;background-color:#f1f1f1;border:1px solid #ddd;border-top:transparent;padding:0 .5rem .1rem;transition:.5s ease-in}\n"], directives: [{ type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: IgetEditorMessageComponent, decorators: [{ type: Component, args: [{ selector: 'iget-editor-message', template: "<div class=\"iget-editor-message\" *ngIf=\"ngxMessage\" (dblclick)=\"clearMessage()\">\n {{ ngxMessage }}\n</div>\n", styles: [".iget-editor-message{font-size:80%;background-color:#f1f1f1;border:1px solid #ddd;border-top:transparent;padding:0 .5rem .1rem;transition:.5s ease-in}\n"] }] }], ctorParameters: function () { return [{ type: MessageService }]; } }); class IgetEditorGrippieComponent { /** * Constructor */ constructor() { /** previous value befor resizing the editor */ this.oldY = 0; /** set to true on mousedown event */ this.grabber = false; this.resized = new EventEmitter(); } /** * * @param event Mouseevent * * Update the height of the editor when the grabber is dragged */ onMouseMove(event) { if (!this.grabber) { return; } this.resized.emit(event.clientY - this.oldY); this.oldY = event.clientY; } /** * * @param event Mouseevent * * set the grabber to false on mouse up action */ onMouseUp(event) { this.grabber = false; } onResize(event, resizer) { this.grabber = true; this.oldY = event.clientY; event.preventDefault(); } } IgetEditorGrippieComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: IgetEditorGrippieComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); IgetEditorGrippieComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.1", type: IgetEditorGrippieComponent, selector: "iget-editor-grippie", outputs: { resized: "resized" }, host: { listeners: { "document:mousemove": "onMouseMove($event)", "document:mouseup": "onMouseUp($event)", "mousedown": "onResize($event)" } }, ngImport: i0, template: "<div class=\"iget-editor-grippie\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" style=\"isolation:isolate\" viewBox=\"651.6 235 26 5\"\n width=\"26\" height=\"5\">\n <g id=\"sprites\">\n <path d=\" M 651.6 235 L 653.6 235 L 653.6 237 L 651.6 237 M 654.6 238 L 656.6 238 L 656.6 240 L 654.6 240 M 660.6 238 L 662.6 238 L 662.6 240 L 660.6 240 M 666.6 238 L 668.6 238 L 668.6 240 L 666.6 240 M 672.6 238 L 674.6 238 L 674.6 240 L 672.6 240 M 657.6 235 L 659.6 235 L 659.6 237 L 657.6 237 M 663.6 235 L 665.6 235 L 665.6 237 L 663.6 237 M 669.6 235 L 671.6 235 L 671.6 237 L 669.6 237 M 675.6 235 L 677.6 235 L 677.6 237 L 675.6 237\"\n fill=\"rgb(147,153,159)\" />\n </g>\n </svg>\n</div>\n", styles: [".iget-editor-grippie{height:9px;position:relative;text-align:center;cursor:s-resize}.iget-editor-grippie svg{position:absolute;top:1.5px;width:50%;right:25%}\n"] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: IgetEditorGrippieComponent, decorators: [{ type: Component, args: [{ selector: 'iget-editor-grippie', template: "<div class=\"iget-editor-grippie\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" style=\"isolation:isolate\" viewBox=\"651.6 235 26 5\"\n width=\"26\" height=\"5\">\n <g id=\"sprites\">\n <path d=\" M 651.6 235 L 653.6 235 L 653.6 237 L 651.6 237 M 654.6 238 L 656.6 238 L 656.6 240 L 654.6 240 M 660.6 238 L 662.6 238 L 662.6 240 L 660.6 240 M 666.6 238 L 668.6 238 L 668.6 240 L 666.6 240 M 672.6 238 L 674.6 238 L 674.6 240 L 672.6 240 M 657.6 235 L 659.6 235 L 659.6 237 L 657.6 237 M 663.6 235 L 665.6 235 L 665.6 237 L 663.6 237 M 669.6 235 L 671.6 235 L 671.6 237 L 669.6 237 M 675.6 235 L 677.6 235 L 677.6 237 L 675.6 237\"\n fill=\"rgb(147,153,159)\" />\n </g>\n </svg>\n</div>\n", styles: [".iget-editor-grippie{height:9px;position:relative;text-align:center;cursor:s-resize}.iget-editor-grippie svg{position:absolute;top:1.5px;width:50%;right:25%}\n"] }] }], ctorParameters: function () { return []; }, propDecorators: { resized: [{ type: Output }], onMouseMove: [{ type: HostListener, args: ['document:mousemove', ['$event']] }], onMouseUp: [{ type: HostListener, args: ['document:mouseup', ['$event']] }], onResize: [{ type: HostListener, args: ['mousedown', ['$event']] }] } }); class IgetEditorComponent { /** * @param sanitizer Dom Sanitizer * @param _messageService service to send message to the editor message component * @param _commandExecutor executes command from the toolbar * @param _renderer access and manipulate the dom element */ constructor(sanitizer, _messageService, _commandExecutor, _renderer) { this.sanitizer = sanitizer; this._messageService = _messageService; this._commandExecutor = _commandExecutor; this._renderer = _renderer; /** emits `blured` event when focused out from the textarea */ this.blured = new EventEmitter(); /** emits `focused` event when focused in to the textarea */ this.focused = new EventEmitter(); this.Utils = Utils; this.showPlaceholder = true; } /** * events */ onTextAreaFocus() { this.focused.emit('focus'); } /** focus the text area when the editor is focussed */ onEditorFocus() { this.textArea.nativeElement.focus(); } /** * Executed from the contenteditable section while the input property changes */ onContentChange($event) { const innerHtml = $event.target.innerHTML; if (typeof this.onChange === 'function') { this.onChange(innerHtml); this.togglePlaceholder(innerHtml); } } onTextAreaBlur() { /** save selection if focussed out */ this._commandExecutor.savedSelection = saveSelection(); if (typeof this.onTouched === 'function') { this.onTouched(); } this.blured.emit('blur'); } /** * resizing text area * * @param offsetY vertical height of the editable portion of the editor */ resizeTextArea(offsetY) { const oldHeight = parseInt(getComputedStyle(this.textArea.nativeElement).height, 10); this.textArea.nativeElement.style.height = (oldHeight + offsetY) + 'px'; } /** * editor actions, i.e., executes command from toolbar * * @param commandName name of the command to be executed */ executeCommand(commandName) { try { this._commandExecutor.execute(commandName); } catch (error) { this._messageService.sendMessage(error.message); } } /** * Write a new value to the element. * * @param value value to be executed when there is a change in contenteditable */ writeValue(value) { this.togglePlaceholder(value); if (value === null || value === undefined || value === '' || value === '<br>') { value = null; } this.value = this.sanitizer.bypassSecurityTrustHtml(value); } /** * Set the function to be called * when the control receives a change event. * * @param fn a function */ registerOnChange(fn) { this.onChange = fn; } /** * Set the function to be called * when the control receives a touch event. * * @param fn a function */ registerOnTouched(fn) { this.onTouched = fn; } /** * toggles placeholder based on input string * * @param value A HTML string from the editor */ togglePlaceholder(value) { if (!value || value === '<br>' || value === '') { this.showPlaceholder = true; } else { this.showPlaceholder = false; } } ngOnInit() { // this.height = this.height || this.textArea.nativeElement.offsetHeight; this.executeComman