UNPKG

@kolkov/angular-editor

Version:

A simple native WYSIWYG editor for Angular 6+, 7+, 8+. Rich Text editor component for Angular.

956 lines (953 loc) 153 kB
import { Injectable, Inject, ɵɵdefineInjectable, ɵɵinject, EventEmitter, Component, Renderer2, Input, Output, ViewChild, SecurityContext, forwardRef, ChangeDetectorRef, Attribute, HostBinding, HostListener, ViewEncapsulation, ElementRef, NgModule } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { DOCUMENT, CommonModule } from '@angular/common'; import { NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { DomSanitizer } from '@angular/platform-browser'; /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @record */ function UploadResponse() { } if (false) { /** @type {?} */ UploadResponse.prototype.imageUrl; } class AngularEditorService { /** * @param {?} http * @param {?} doc */ constructor(http, doc) { this.http = http; this.doc = doc; /** * save selection when the editor is focussed out */ this.saveSelection = (/** * @return {?} */ () => { if (this.doc.getSelection) { /** @type {?} */ const sel = this.doc.getSelection(); if (sel.getRangeAt && sel.rangeCount) { this.savedSelection = sel.getRangeAt(0); this.selectedText = sel.toString(); } } else if (this.doc.getSelection && this.doc.createRange) { this.savedSelection = document.createRange(); } else { this.savedSelection = null; } }); } /** * Executed command from editor header buttons exclude toggleEditorMode * @param {?} command string from triggerCommand * @return {?} */ executeCommand(command) { /** @type {?} */ const commands = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'pre']; if (commands.includes(command)) { this.doc.execCommand('formatBlock', false, command); return; } this.doc.execCommand(command, false, null); } /** * Create URL link * @param {?} url string from UI prompt * @return {?} */ createLink(url) { if (!url.includes('http')) { this.doc.execCommand('createlink', false, url); } else { /** @type {?} */ const newUrl = '<a href="' + url + '" target="_blank">' + this.selectedText + '</a>'; this.insertHtml(newUrl); } } /** * insert color either font or background * * @param {?} color color to be inserted * @param {?} where where the color has to be inserted either text/background * @return {?} */ insertColor(color, where) { /** @type {?} */ const restored = this.restoreSelection(); if (restored) { if (where === 'textColor') { this.doc.execCommand('foreColor', false, color); } else { this.doc.execCommand('hiliteColor', false, color); } } } /** * Set font name * @param {?} fontName string * @return {?} */ setFontName(fontName) { this.doc.execCommand('fontName', false, fontName); } /** * Set font size * @param {?} fontSize string * @return {?} */ setFontSize(fontSize) { this.doc.execCommand('fontSize', false, fontSize); } /** * Create raw HTML * @param {?} html HTML string * @return {?} */ insertHtml(html) { /** @type {?} */ const isHTMLInserted = this.doc.execCommand('insertHTML', false, html); if (!isHTMLInserted) { throw new Error('Unable to perform the operation'); } } /** * restore selection when the editor is focused in * * saved selection when the editor is focused out * @return {?} */ restoreSelection() { if (this.savedSelection) { if (this.doc.getSelection) { /** @type {?} */ const sel = this.doc.getSelection(); sel.removeAllRanges(); sel.addRange(this.savedSelection); return true; } else if (this.doc.getSelection /*&& this.savedSelection.select*/) { // this.savedSelection.select(); return true; } } else { return false; } } /** * setTimeout used for execute 'saveSelection' method in next event loop iteration * @param {?} callbackFn * @param {?=} timeout * @return {?} */ executeInNextQueueIteration(callbackFn, timeout = 1e2) { setTimeout(callbackFn, timeout); } /** * check any selection is made or not * @private * @return {?} */ checkSelection() { /** @type {?} */ const selectedText = this.savedSelection.toString(); if (selectedText.length === 0) { throw new Error('No Selection Made'); } return true; } /** * Upload file to uploadUrl * @param {?} file The file * @return {?} */ uploadImage(file) { /** @type {?} */ const uploadData = new FormData(); uploadData.append('file', file, file.name); return this.http.post(this.uploadUrl, uploadData, { reportProgress: true, observe: 'events', withCredentials: this.uploadWithCredentials, }); } /** * Insert image with Url * @param {?} imageUrl The imageUrl. * @return {?} */ insertImage(imageUrl) { this.doc.execCommand('insertImage', false, imageUrl); } /** * @param {?} separator * @return {?} */ setDefaultParagraphSeparator(separator) { this.doc.execCommand('defaultParagraphSeparator', false, separator); } /** * @param {?} customClass * @return {?} */ createCustomClass(customClass) { /** @type {?} */ let newTag = this.selectedText; if (customClass) { /** @type {?} */ const tagName = customClass.tag ? customClass.tag : 'span'; newTag = '<' + tagName + ' class="' + customClass.class + '">' + this.selectedText + '</' + tagName + '>'; } this.insertHtml(newTag); } /** * @param {?} videoUrl * @return {?} */ insertVideo(videoUrl) { if (videoUrl.match('www.youtube.com')) { this.insertYouTubeVideoTag(videoUrl); } if (videoUrl.match('vimeo.com')) { this.insertVimeoVideoTag(videoUrl); } } /** * @private * @param {?} videoUrl * @return {?} */ insertYouTubeVideoTag(videoUrl) { /** @type {?} */ const id = videoUrl.split('v=')[1]; /** @type {?} */ const imageUrl = `https://img.youtube.com/vi/${id}/0.jpg`; /** @type {?} */ const thumbnail = ` <div style='position: relative'> <img style='position: absolute; left:200px; top:140px' src="https://img.icons8.com/color/96/000000/youtube-play.png"/> <a href='${videoUrl}' target='_blank'> <img src="${imageUrl}" alt="click to watch"/> </a> </div>`; this.insertHtml(thumbnail); } /** * @private * @param {?} videoUrl * @return {?} */ insertVimeoVideoTag(videoUrl) { /** @type {?} */ const sub = this.http.get(`https://vimeo.com/api/oembed.json?url=${videoUrl}`).subscribe((/** * @param {?} data * @return {?} */ data => { /** @type {?} */ const imageUrl = data.thumbnail_url_with_play_button; /** @type {?} */ const thumbnail = `<div> <a href='${videoUrl}' target='_blank'> <img src="${imageUrl}" alt="${data.title}"/> </a> </div>`; this.insertHtml(thumbnail); sub.unsubscribe(); })); } /** * @param {?} node * @return {?} */ nextNode(node) { if (node.hasChildNodes()) { return node.firstChild; } else { while (node && !node.nextSibling) { node = node.parentNode; } if (!node) { return null; } return node.nextSibling; } } /** * @param {?} range * @param {?} includePartiallySelectedContainers * @return {?} */ getRangeSelectedNodes(range, includePartiallySelectedContainers) { /** @type {?} */ let node = range.startContainer; /** @type {?} */ const endNode = range.endContainer; /** @type {?} */ let rangeNodes = []; // Special case for a range that is contained within a single node if (node === endNode) { rangeNodes = [node]; } else { // Iterate nodes until we hit the end container while (node && node !== endNode) { rangeNodes.push(node = this.nextNode(node)); } // Add partially selected nodes at the start of the range node = range.startContainer; while (node && node !== range.commonAncestorContainer) { rangeNodes.unshift(node); node = node.parentNode; } } // Add ancestors of the range container, if required if (includePartiallySelectedContainers) { node = range.commonAncestorContainer; while (node) { rangeNodes.push(node); node = node.parentNode; } } return rangeNodes; } /** * @return {?} */ getSelectedNodes() { /** @type {?} */ const nodes = []; if (this.doc.getSelection) { /** @type {?} */ const sel = this.doc.getSelection(); for (let i = 0, len = sel.rangeCount; i < len; ++i) { nodes.push.apply(nodes, this.getRangeSelectedNodes(sel.getRangeAt(i), true)); } } return nodes; } /** * @param {?} el * @return {?} */ replaceWithOwnChildren(el) { /** @type {?} */ const parent = el.parentNode; while (el.hasChildNodes()) { parent.insertBefore(el.firstChild, el); } parent.removeChild(el); } /** * @param {?} tagNames * @return {?} */ removeSelectedElements(tagNames) { /** @type {?} */ const tagNamesArray = tagNames.toLowerCase().split(','); this.getSelectedNodes().forEach((/** * @param {?} node * @return {?} */ (node) => { if (node.nodeType === 1 && tagNamesArray.indexOf(node.tagName.toLowerCase()) > -1) { // Remove the node and replace it with its children this.replaceWithOwnChildren(node); } })); } } AngularEditorService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; /** @nocollapse */ AngularEditorService.ctorParameters = () => [ { type: HttpClient }, { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] } ]; /** @nocollapse */ AngularEditorService.ngInjectableDef = ɵɵdefineInjectable({ factory: function AngularEditorService_Factory() { return new AngularEditorService(ɵɵinject(HttpClient), ɵɵinject(DOCUMENT)); }, token: AngularEditorService, providedIn: "root" }); if (false) { /** @type {?} */ AngularEditorService.prototype.savedSelection; /** @type {?} */ AngularEditorService.prototype.selectedText; /** @type {?} */ AngularEditorService.prototype.uploadUrl; /** @type {?} */ AngularEditorService.prototype.uploadWithCredentials; /** * save selection when the editor is focussed out * @type {?} */ AngularEditorService.prototype.saveSelection; /** * @type {?} * @private */ AngularEditorService.prototype.http; /** * @type {?} * @private */ AngularEditorService.prototype.doc; } /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @record */ function CustomClass() { } if (false) { /** @type {?} */ CustomClass.prototype.name; /** @type {?} */ CustomClass.prototype.class; /** @type {?|undefined} */ CustomClass.prototype.tag; } /** * @record */ function Font() { } if (false) { /** @type {?} */ Font.prototype.name; /** @type {?} */ Font.prototype.class; } /** * @record */ function AngularEditorConfig() { } if (false) { /** @type {?|undefined} */ AngularEditorConfig.prototype.editable; /** @type {?|undefined} */ AngularEditorConfig.prototype.spellcheck; /** @type {?|undefined} */ AngularEditorConfig.prototype.height; /** @type {?|undefined} */ AngularEditorConfig.prototype.minHeight; /** @type {?|undefined} */ AngularEditorConfig.prototype.maxHeight; /** @type {?|undefined} */ AngularEditorConfig.prototype.width; /** @type {?|undefined} */ AngularEditorConfig.prototype.minWidth; /** @type {?|undefined} */ AngularEditorConfig.prototype.translate; /** @type {?|undefined} */ AngularEditorConfig.prototype.enableToolbar; /** @type {?|undefined} */ AngularEditorConfig.prototype.showToolbar; /** @type {?|undefined} */ AngularEditorConfig.prototype.placeholder; /** @type {?|undefined} */ AngularEditorConfig.prototype.defaultParagraphSeparator; /** @type {?|undefined} */ AngularEditorConfig.prototype.defaultFontName; /** @type {?|undefined} */ AngularEditorConfig.prototype.defaultFontSize; /** @type {?|undefined} */ AngularEditorConfig.prototype.uploadUrl; /** @type {?|undefined} */ AngularEditorConfig.prototype.uploadWithCredentials; /** @type {?|undefined} */ AngularEditorConfig.prototype.fonts; /** @type {?|undefined} */ AngularEditorConfig.prototype.customClasses; /** @type {?|undefined} */ AngularEditorConfig.prototype.sanitize; /** @type {?|undefined} */ AngularEditorConfig.prototype.toolbarPosition; /** @type {?|undefined} */ AngularEditorConfig.prototype.outline; /** @type {?|undefined} */ AngularEditorConfig.prototype.toolbarHiddenButtons; } /** @type {?} */ const angularEditorConfig = { editable: true, spellcheck: true, height: 'auto', minHeight: '0', maxHeight: 'auto', width: 'auto', minWidth: '0', translate: 'yes', enableToolbar: true, showToolbar: true, placeholder: 'Enter text here...', defaultParagraphSeparator: '', defaultFontName: '', defaultFontSize: '', fonts: [ { class: 'arial', name: 'Arial' }, { class: 'times-new-roman', name: 'Times New Roman' }, { class: 'calibri', name: 'Calibri' }, { class: 'comic-sans-ms', name: 'Comic Sans MS' } ], uploadUrl: 'v1/image', uploadWithCredentials: false, sanitize: true, toolbarPosition: 'top', outline: true, }; /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class AngularEditorToolbarComponent { /** * @param {?} r * @param {?} editorService * @param {?} doc */ constructor(r, editorService, doc) { this.r = r; this.editorService = editorService; this.doc = doc; this.htmlMode = false; this.linkSelected = false; this.block = 'default'; this.fontName = 'Times New Roman'; this.fontSize = '3'; this.headings = [ { label: 'Heading 1', value: 'h1', }, { label: 'Heading 2', value: 'h2', }, { label: 'Heading 3', value: 'h3', }, { label: 'Heading 4', value: 'h4', }, { label: 'Heading 5', value: 'h5', }, { label: 'Heading 6', value: 'h6', }, { label: 'Heading 7', value: 'h7', }, { label: 'Paragraph', value: 'p', }, { label: 'Predefined', value: 'pre' }, { label: 'Standard', value: 'div' }, { label: 'default', value: 'default' } ]; this.fontSizes = [ { label: '1', value: '1', }, { label: '2', value: '2', }, { label: '3', value: '3', }, { label: '4', value: '4', }, { label: '5', value: '5', }, { label: '6', value: '6', }, { label: '7', value: '7', } ]; this.customClassId = '-1'; this.customClassList = [{ label: '', value: '' }]; // uploadUrl: string; this.tagMap = { BLOCKQUOTE: 'indent', A: 'link' }; this.select = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P', 'PRE', 'DIV']; this.buttons = ['bold', 'italic', 'underline', 'strikeThrough', 'subscript', 'superscript', 'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull', 'indent', 'outdent', 'insertUnorderedList', 'insertOrderedList', 'link']; this.fonts = [{ label: '', value: '' }]; this.execute = new EventEmitter(); } /** * @param {?} classes * @return {?} */ set customClasses(classes) { if (classes) { this._customClasses = classes; this.customClassList = this._customClasses.map((/** * @param {?} x * @param {?} i * @return {?} */ (x, i) => ({ label: x.name, value: i.toString() }))); this.customClassList.unshift({ label: 'Clear Class', value: '-1' }); } } /** * @param {?} value * @return {?} */ set defaultFontName(value) { if (value) { this.fontName = value; } } /** * @param {?} value * @return {?} */ set defaultFontSize(value) { if (value) { this.fontSize = value; } } /** * @return {?} */ get isLinkButtonDisabled() { return this.htmlMode || !Boolean(this.editorService.selectedText); } /** * Trigger command from editor header buttons * @param {?} command string from toolbar buttons * @return {?} */ triggerCommand(command) { this.execute.emit(command); } /** * highlight editor buttons when cursor moved or positioning * @return {?} */ triggerButtons() { if (!this.showToolbar) { return; } this.buttons.forEach((/** * @param {?} e * @return {?} */ e => { /** @type {?} */ const result = this.doc.queryCommandState(e); /** @type {?} */ const elementById = this.doc.getElementById(e + '-' + this.id); if (result) { this.r.addClass(elementById, 'active'); } else { this.r.removeClass(elementById, 'active'); } })); } /** * trigger highlight editor buttons when cursor moved or positioning in block * @param {?} nodes * @return {?} */ triggerBlocks(nodes) { if (!this.showToolbar) { return; } this.linkSelected = nodes.findIndex((/** * @param {?} x * @return {?} */ x => x.nodeName === 'A')) > -1; /** @type {?} */ let found = false; this.select.forEach((/** * @param {?} y * @return {?} */ y => { /** @type {?} */ const node = nodes.find((/** * @param {?} x * @return {?} */ x => x.nodeName === y)); if (node !== undefined && y === node.nodeName) { if (found === false) { this.block = node.nodeName.toLowerCase(); found = true; } } else if (found === false) { this.block = 'default'; } })); found = false; if (this._customClasses) { this._customClasses.forEach((/** * @param {?} y * @param {?} index * @return {?} */ (y, index) => { /** @type {?} */ const node = nodes.find((/** * @param {?} x * @return {?} */ x => { if (x instanceof Element) { return x.className === y.class; } })); if (node !== undefined) { if (found === false) { this.customClassId = index.toString(); found = true; } } else if (found === false) { this.customClassId = '-1'; } })); } Object.keys(this.tagMap).map((/** * @param {?} e * @return {?} */ e => { /** @type {?} */ const elementById = this.doc.getElementById(this.tagMap[e] + '-' + this.id); /** @type {?} */ const node = nodes.find((/** * @param {?} x * @return {?} */ x => x.nodeName === e)); if (node !== undefined && e === node.nodeName) { this.r.addClass(elementById, 'active'); } else { this.r.removeClass(elementById, 'active'); } })); this.foreColour = this.doc.queryCommandValue('ForeColor'); this.fontSize = this.doc.queryCommandValue('FontSize'); this.fontName = this.doc.queryCommandValue('FontName').replace(/"/g, ''); this.backColor = this.doc.queryCommandValue('backColor'); } /** * insert URL link * @return {?} */ insertUrl() { /** @type {?} */ let url = 'https:\/\/'; /** @type {?} */ const selection = this.editorService.savedSelection; if (selection && selection.commonAncestorContainer.parentElement.nodeName === 'A') { /** @type {?} */ const parent = (/** @type {?} */ (selection.commonAncestorContainer.parentElement)); if (parent.href !== '') { url = parent.href; } } url = prompt('Insert URL link', url); if (url && url !== '' && url !== 'https://') { this.editorService.createLink(url); } } /** * insert Video link * @return {?} */ insertVideo() { this.execute.emit(''); /** @type {?} */ const url = prompt('Insert Video link', `https://`); if (url && url !== '' && url !== `https://`) { this.editorService.insertVideo(url); } } /** * insert color * @param {?} color * @param {?} where * @return {?} */ insertColor(color, where) { this.editorService.insertColor(color, where); this.execute.emit(''); } /** * set font Name/family * @param {?} foreColor string * @return {?} */ setFontName(foreColor) { this.editorService.setFontName(foreColor); this.execute.emit(''); } /** * set font Size * @param {?} fontSize string * @return {?} */ setFontSize(fontSize) { this.editorService.setFontSize(fontSize); this.execute.emit(''); } /** * toggle editor mode (WYSIWYG or SOURCE) * @param {?} m boolean * @return {?} */ setEditorMode(m) { /** @type {?} */ const toggleEditorModeButton = this.doc.getElementById('toggleEditorMode' + '-' + this.id); if (m) { this.r.addClass(toggleEditorModeButton, 'active'); } else { this.r.removeClass(toggleEditorModeButton, 'active'); } this.htmlMode = m; } /** * Upload image when file is selected * @param {?} event * @return {?} */ onFileChanged(event) { /** @type {?} */ const file = event.target.files[0]; if (file.type.includes('image/')) { if (this.uploadUrl) { this.editorService.uploadImage(file).subscribe((/** * @param {?} e * @return {?} */ e => { if (e instanceof HttpResponse) { this.editorService.insertImage(e.body.imageUrl); this.fileReset(); } })); } else { /** @type {?} */ const reader = new FileReader(); reader.onload = (/** * @param {?} e * @return {?} */ (e) => { /** @type {?} */ const fr = (/** @type {?} */ (e.currentTarget)); this.editorService.insertImage(fr.result.toString()); }); reader.readAsDataURL(file); } } } /** * Reset Input * @return {?} */ fileReset() { this.myInputFile.nativeElement.value = ''; } /** * Set custom class * @param {?} classId * @return {?} */ setCustomClass(classId) { if (classId === '-1') { this.execute.emit('clear'); } else { this.editorService.createCustomClass(this._customClasses[+classId]); } } /** * @param {?} name * @return {?} */ isButtonHidden(name) { if (!name) { return false; } if (!(this.hiddenButtons instanceof Array)) { return false; } /** @type {?} */ let result; for (const arr of this.hiddenButtons) { if (arr instanceof Array) { result = arr.find((/** * @param {?} item * @return {?} */ item => item === name)); } if (result) { break; } } return result !== undefined; } } AngularEditorToolbarComponent.decorators = [ { type: Component, args: [{ selector: 'angular-editor-toolbar', template: "<div class=\"angular-editor-toolbar\" *ngIf=\"showToolbar\">\n <div class=\"angular-editor-toolbar-set\">\n <button type=\"button\" title=\"Undo\" class=\"angular-editor-button\" (click)=\"triggerCommand('undo')\"\n [hidden]=\"isButtonHidden('undo')\" tabindex=\"-1\"><i\n class='fa fa-undo'></i></button>\n <button type=\"button\" title=\"Redo\" class=\"angular-editor-button\" (click)=\"triggerCommand('redo')\"\n [hidden]=\"isButtonHidden('redo')\" tabindex=\"-1\"><i\n class='fa fa-repeat'></i></button>\n </div>\n <div class=\"angular-editor-toolbar-set\">\n <button [id]=\"'bold-'+id\" type=\"button\" title=\"Bold\" class=\"angular-editor-button\" (click)=\"triggerCommand('bold')\"\n [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('bold')\" tabindex=\"-1\"><i class='fa fa-bold'></i></button>\n <button [id]=\"'italic-'+id\" type=\"button\" title=\"Italic\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('italic')\"\n [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('italic')\" tabindex=\"-1\"><i class='fa fa-italic'></i>\n </button>\n <button [id]=\"'underline-'+id\" type=\"button\" title=\"Underline\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('underline')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('underline')\"\n tabindex=\"-1\"><i class='fa fa-underline'></i></button>\n <button [id]=\"'strikeThrough-'+id\" type=\"button\" title=\"Strikethrough\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('strikeThrough')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('strikeThrough')\"\n tabindex=\"-1\"><i class='fa fa-strikethrough'></i></button>\n <button [id]=\"'subscript-'+id\" type=\"button\" title=\"Subscript\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('subscript')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('subscript')\"\n tabindex=\"-1\"><i class='fa fa-subscript'></i></button>\n <button [id]=\"'superscript-'+id\" type=\"button\" title=\"Superscript\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('superscript')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('superscript')\"\n tabindex=\"-1\"><i class='fa fa-superscript'></i></button>\n </div>\n <div class=\"angular-editor-toolbar-set\">\n <button [id]=\"'justifyLeft-'+id\" type=\"button\" title=\"Justify Left\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('justifyLeft')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('justifyLeft')\"\n tabindex=\"-1\"><i\n class='fa fa-align-left'></i></button>\n <button [id]=\"'justifyCenter-'+id\" type=\"button\" title=\"Justify Center\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('justifyCenter')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('justifyCenter')\"\n tabindex=\"-1\"><i\n class='fa fa-align-center'></i></button>\n <button [id]=\"'justifyRight-'+id\" type=\"button\" title=\"Justify Right\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('justifyRight')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('justifyRight')\"\n tabindex=\"-1\">\n <i class='fa fa-align-right'></i></button>\n <button [id]=\"'justifyFull-'+id\" type=\"button\" title=\"Justify Full\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('justifyFull')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('justifyFull')\"\n tabindex=\"-1\"><i\n class='fa fa-align-justify'></i></button>\n </div>\n <div class=\"angular-editor-toolbar-set\">\n <button [id]=\"'indent-'+id\" type=\"button\" title=\"Indent\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('indent')\"\n [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('indent')\" tabindex=\"-1\"><i\n class='fa fa-indent'></i></button>\n <button [id]=\"'outdent-'+id\" type=\"button\" title=\"Outdent\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('outdent')\"\n [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('outdent')\" tabindex=\"-1\"><i\n class='fa fa-outdent'></i></button>\n </div>\n <div class=\"angular-editor-toolbar-set\">\n <button [id]=\"'insertUnorderedList-'+id\" type=\"button\" title=\"Unordered List\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('insertUnorderedList')\" [disabled]=\"htmlMode\"\n [hidden]=\"isButtonHidden('insertUnorderedList')\" tabindex=\"-1\"><i\n class='fa fa-list-ul'></i></button>\n <button [id]=\"'insertOrderedList-'+id\" type=\"button\" title=\"Ordered List\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('insertOrderedList')\" [disabled]=\"htmlMode\"\n [hidden]=\"isButtonHidden('insertOrderedList')\" tabindex=\"-1\"><i\n class='fa fa-list-ol'></i></button>\n </div>\n <div class=\"angular-editor-toolbar-set\">\n\n <ae-select class=\"select-heading\" [options]=\"headings\"\n [(ngModel)]=\"block\"\n (change)=\"triggerCommand(block)\"\n [disabled]=\"htmlMode\"\n [hidden]=\"isButtonHidden('heading')\"\n tabindex=\"-1\"></ae-select>\n </div>\n <div class=\"angular-editor-toolbar-set\">\n\n <ae-select class=\"select-font\" [options]=\"fonts\"\n [(ngModel)]=\"fontName\"\n (change)=\"setFontName(fontName)\"\n [disabled]=\"htmlMode\"\n [hidden]=\"isButtonHidden('fontName')\"\n tabindex=\"-1\"></ae-select>\n </div>\n <div class=\"angular-editor-toolbar-set\">\n\n <ae-select class=\"select-font-size\" [options]=\"fontSizes\"\n [(ngModel)]=\"fontSize\"\n (change)=\"setFontSize(fontSize)\"\n [disabled]=\"htmlMode\"\n [hidden]=\"isButtonHidden('fontSize')\"\n tabindex=\"-1\">\n </ae-select>\n </div>\n <div class=\"angular-editor-toolbar-set\">\n <input\n style=\"display: none\"\n type=\"color\" (change)=\"insertColor(fgInput.value, 'textColor')\"\n #fgInput>\n <button [id]=\"'foregroundColorPicker-'+id\" type=\"button\" class=\"angular-editor-button\" (click)=\"fgInput.click()\"\n title=\"Text Color\"\n [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('textColor')\" tabindex=\"-1\"><span\n class=\"color-label foreground\"><i class=\"fa fa-font\"></i></span>\n </button>\n <input\n style=\"display: none\"\n type=\"color\" (change)=\"insertColor(bgInput.value, 'backgroundColor')\"\n #bgInput>\n <button [id]=\"'backgroundColorPicker-'+id\" type=\"button\" class=\"angular-editor-button\" (click)=\"bgInput.click()\"\n title=\"Background Color\"\n [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('backgroundColor')\" tabindex=\"-1\"><span\n class=\"color-label background\"><i class=\"fa fa-font\"></i></span>\n </button>\n </div>\n <div *ngIf=\"_customClasses\" class=\"angular-editor-toolbar-set\">\n <ae-select class=\"select-custom-style\" [options]=\"customClassList\"\n [(ngModel)]=\"customClassId\"\n (change)=\"setCustomClass(customClassId)\"\n [disabled]=\"htmlMode\"\n [hidden]=\"isButtonHidden('customClasses')\"\n tabindex=\"-1\"></ae-select>\n </div>\n <div class=\"angular-editor-toolbar-set\">\n <button [id]=\"'link-'+id\" type=\"button\" class=\"angular-editor-button\" (click)=\"insertUrl()\"\n title=\"Insert Link\" [disabled]=\"isLinkButtonDisabled\" [hidden]=\"isButtonHidden('link')\" tabindex=\"-1\">\n <i class=\"fa fa-link\"></i>\n </button>\n <button [id]=\"'unlink-'+id\" type=\"button\" class=\"angular-editor-button\" (click)=\"triggerCommand('unlink')\"\n title=\"Unlink\" [disabled]=\"htmlMode || !linkSelected\" [hidden]=\"isButtonHidden('unlink')\" tabindex=\"-1\">\n <i class=\"fa fa-chain-broken\"></i>\n </button>\n <input\n style=\"display: none\"\n accept=\"image/*\"\n type=\"file\" (change)=\"onFileChanged($event)\"\n #fileInput>\n <button [id]=\"'insertImage-'+id\" type=\"button\" class=\"angular-editor-button\" (click)=\"fileInput.click()\"\n title=\"Insert Image\"\n [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('insertImage')\" tabindex=\"-1\"><i class=\"fa fa-image\"></i>\n </button>\n <button [id]=\"'insertVideo-'+id\" type=\"button\" class=\"angular-editor-button\"\n (click)=\"insertVideo()\" title=\"Insert Video\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('insertVideo')\"\n tabindex=\"-1\"><i\n class=\"fa fa-video-camera\"></i></button>\n <button [id]=\"'insertHorizontalRule-'+id\" type=\"button\" title=\"Horizontal Line\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('insertHorizontalRule')\" [disabled]=\"htmlMode\"\n [hidden]=\"isButtonHidden('insertHorizontalRule')\" tabindex=\"-1\"><i\n class=\"fa fa-minus\"></i></button>\n </div>\n <div class=\"angular-editor-toolbar-set\">\n <button [id]=\"'clearFormatting-'+id\" type=\"button\" title=\"Clear Formatting\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('removeFormat')\" [disabled]=\"htmlMode\" [hidden]=\"isButtonHidden('removeFormat')\"\n tabindex=\"-1\"><i class='fa fa-remove'></i>\n </button>\n </div>\n <div class=\"angular-editor-toolbar-set\">\n <button [id]=\"'toggleEditorMode-'+id\" type=\"button\" title=\"HTML Code\" class=\"angular-editor-button\"\n (click)=\"triggerCommand('toggleEditorMode')\" [hidden]=\"isButtonHidden('toggleEditorMode')\" tabindex=\"-1\"><i\n class='fa fa-code'></i></button>\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";/*!\n * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome\n * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)\n */@font-face{font-family:FontAwesome;src:url(https://netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts/fontawesome-webfont.eot?v=4.7.0);src:url(https://netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts/fontawesome-webfont.eot?#iefix&v=4.7.0) format(\"embedded-opentype\"),url(https://netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2?v=4.7.0) format(\"woff2\"),url(https://netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts/fontawesome-webfont.woff?v=4.7.0) format(\"woff\"),url(https://netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts/fontawesome-webfont.ttf?v=4.7.0) format(\"truetype\"),url(https://netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular) format(\"svg\");font-weight:400;font-style:normal}.fa{display:inline-block;font:14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857em;text-align:center}.fa-ul{padding-left:0;margin-left:2.1428571429em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.1428571429em;width:2.1428571429em;top:.1428571429em;text-align:center}.fa-li.fa-lg{left:-1.8571428571em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:2s linear infinite fa-spin;animation:2s linear infinite fa-spin}.fa-pulse{-webkit-animation:1s steps(8) infinite fa-spin;animation:1s steps(8) infinite fa-spin}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-webkit-transform:scale(1,-1);transform:scale(1,-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{-webkit-filter:none;filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:\"\uF000\"}.fa-music:before{content:\"\uF001\"}.fa-search:before{content:\"\uF002\"}.fa-envelope-o:before{content:\"\uF003\"}.fa-heart:before{content:\"\uF004\"}.fa-star:before{content:\"\uF005\"}.fa-star-o:before{content:\"\uF006\"}.fa-user:before{content:\"\uF007\"}.fa-film:before{content:\"\uF008\"}.fa-th-large:before{content:\"\uF009\"}.fa-th:before{content:\"\uF00A\"}.fa-th-list:before{content:\"\uF00B\"}.fa-check:before{content:\"\uF00C\"}.fa-close:before,.fa-remove:before,.fa-times:before{content:\"\uF00D\"}.fa-search-plus:before{content:\"\uF00E\"}.fa-search-minus:before{content:\"\uF010\"}.fa-power-off:before{content:\"\uF011\"}.fa-signal:before{content:\"\uF012\"}.fa-cog:before,.fa-gear:before{content:\"\uF013\"}.fa-trash-o:before{content:\"\uF014\"}.fa-home:before{content:\"\uF015\"}.fa-file-o:before{content:\"\uF016\"}.fa-clock-o:before{content:\"\uF017\"}.fa-road:before{content:\"\uF018\"}.fa-download:before{content:\"\uF019\"}.fa-arrow-circle-o-down:before{content:\"\uF01A\"}.fa-arrow-circle-o-up:before{content:\"\uF01B\"}.fa-inbox:before{content:\"\uF01C\"}.fa-play-circle-o:before{content:\"\uF01D\"}.fa-repeat:before,.fa-rotate-right:before{content:\"\uF01E\"}.fa-refresh:before{content:\"\uF021\"}.fa-list-alt:before{content:\"\uF022\"}.fa-lock:before{content:\"\uF023\"}.fa-flag:before{content:\"\uF024\"}.fa-headphones:before{content:\"\uF025\"}.fa-volume-off:before{content:\"\uF026\"}.fa-volume-down:before{content:\"\uF027\"}.fa-volume-up:before{content:\"\uF028\"}.fa-qrcode:before{content:\"\uF029\"}.fa-barcode:before{content:\"\uF02A\"}.fa-tag:before{content:\"\uF02B\"}.fa-tags:before{content:\"\uF02C\"}.fa-book:before{content:\"\uF02D\"}.fa-bookmark:before{content:\"\uF02E\"}.fa-print:before{content:\"\uF02F\"}.fa-camera:before{content:\"\uF030\"}.fa-font:before{content:\"\uF031\"}.fa-bold:before{content:\"\uF032\"}.fa-italic:before{content:\"\uF033\"}.fa-text-height:before{content:\"\uF034\"}.fa-text-width:before{content:\"\uF035\"}.fa-align-left:before{content:\"\uF036\"}.fa-align-center:before{content:\"\uF037\"}.fa-align-right:before{content:\"\uF038\"}.fa-align-justify:before{content:\"\uF039\"}.fa-list:before{content:\"\uF03A\"}.fa-dedent:before,.fa-outdent:before{content:\"\uF03B\"}.fa-indent:before{content:\"\uF03C\"}.fa-video-camera:before{content:\"\uF03D\"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:\"\uF03E\"}.fa-pencil:before{content:\"\uF040\"}.fa-map-marker:before{content:\"\uF041\"}.fa-adjust:before{content:\"\uF042\"}.fa-tint:before{content:\"\uF043\"}.fa-edit:before,.fa-pencil-square-o:before{content:\"\uF044\"}.fa-share-square-o:before{content:\"\uF045\"}.fa-check-square-o:before{content:\"\uF046\"}.fa-arrows:before{content:\"\uF047\"}.fa-step-backward:before{content:\"\uF048\"}.fa-fast-backward:before{content:\"\uF049\"}.fa-backward:before{content:\"\uF04A\"}.fa-play:before{content:\"\uF04B\"}.fa-pause:before{content:\"\uF04C\"}.fa-stop:before{content:\"\uF04D\"}.fa-forward:before{content:\"\uF04E\"}.fa-fast-forward:before{content:\"\uF050\"}.fa-step-forward:before{content:\"\uF051\"}.fa-eject:before{content:\"\uF052\"}.fa-chevron-left:before{content:\"\uF053\"}.fa-chevron-right:before{content:\"\uF054\"}.fa-plus-circle:before{content:\"\uF055\"}.fa-minus-circle:before{content:\"\uF056\"}.fa-times-circle:before{content:\"\uF057\"}.fa-check-circle:before{content:\"\uF058\"}.fa-question-circle:before{content:\"\uF059\"}.fa-info-circle:before{content:\"\uF05A\"}.fa-crosshairs:before{content:\"\uF05B\"}.fa-times-circle-o:before{content:\"\uF05C\"}.fa-check-circle-o:before{content:\"\uF05D\"}.fa-ban:before{content:\"\uF05E\"}.fa-arrow-left:before{content:\"\uF060\"}.fa-arrow-right:before{content:\"\uF061\"}.fa-arrow-up:before{content:\"\uF062\"}.fa-arrow-down:before{content:\"\uF063\"}.fa-mail-forward:before,.fa-share:before{content:\"\uF064\"}.fa-expand:before{content:\"\uF065\"}.fa-compress:before{content:\"\uF066\"}.fa-plus:before{content:\"\uF067\"}.fa-minus:before{content:\"\uF068\"}.fa-asterisk:before{content:\"\uF069\"}.fa-exclamation-circle:before{content:\"\uF06A\"}.fa-gift:before{content:\"\uF06B\"}.fa-leaf:before{content:\"\uF06C\"}.fa-fire:before{content:\"\uF06D\"}.fa-eye:before{content:\"\uF06E\"}.fa-eye-slash:before{content:\"\uF070\"}.fa-exclamation-triangle:before,.fa-warning:before{content:\"\uF071\"}.fa-plane:before{content:\"\uF072\"}.fa-calendar:before{content:\"\uF073\"}.fa-random:before{content:\"\uF074\"}.fa-comment:before{content:\"\uF075\"}.fa-magnet:before{content:\"\uF076\"}.fa-chevron-up:before{content:\"\uF077\"}.fa-chevron-down:before{content:\"\uF078\"}.fa-retweet:before{content:\"\uF079\"}.fa-shopping-cart:before{content:\"\uF07A\"}.fa-folder:before{content:\"\uF07B\"}.fa-folder-open:before{content:\"\uF07C\"}.fa-arrows-v:before{content:\"\uF07D\"}.fa-arrows-h:before{content:\"\uF07E\"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:\"\uF080\"}.fa-twitter-square:before{content:\"\uF081\"}.fa-facebook-square:before{content:\"\uF082\"}.fa-camera-retro:before{content:\"\uF083\"}.fa-key:before{content:\"\uF084\"}.fa-cogs:before,.fa-gears:before{content:\"\uF085\"}.fa-comments:before{content:\"\uF086\"}.fa-thumbs-o-up:before{content:\"\uF087\"}.fa-thumbs-o-down:before{content:\"\uF088\"}.fa-star-half:before{content:\"\uF089\"}.fa-heart-o:before{content:\"\uF08A\"}.fa-sign-out:before{content:\"\uF08B\"}.fa-linkedin-square:before{content:\"\uF08C\"}.fa-thumb-tack:before{content:\"\uF08D\"}.fa-external-link:before{content:\"\uF08E\"}.fa-sign-in:before{content:\"\uF090\"}.fa-trophy:before{content:\"\uF091\"}.fa-github-square:before{content:\"\uF092\"}.fa-upload:before{content:\"\uF093\"}.fa-lemon-o:before{content:\"\uF094\"}.fa-phone:before{content:\"\uF095\"}.fa-square-o:before{content:\"\uF096\"}.fa-bookmark-o:before{content:\"\uF097\"}.fa-phone-square:before{content:\"\uF098\"}.fa-twitter:before{content:\"\uF099\"}.fa-facebook-f:before,.fa-facebook:before{content:\"\uF09A\"}.fa-github:before{content:\"\uF09B\"}.fa-unlock:before{content:\"\uF09C\"}.fa-credit-card:before{content:\"\uF09D\"}.fa-feed:before,.fa-rss:before{content:\"\uF09E\"}.fa-hdd-o:before{content:\"\uF0A0\"}.fa-bullhorn:before{content:\"\uF0A1\"}.fa-bell:before{content:\"\uF0F3\"}.fa-certificate:before{content:\"\uF0A3\"}.fa-hand-o-right:before{content:\"\uF0A4\"}.fa-hand-o-left:before{content:\"\uF0A5\"}.fa-hand-o-up:before{content:\"\uF0A6\"}.fa-hand-o-down:before{content:\"\uF0A7\"}.fa-arrow-circle-left:before{content:\"\uF0A8\"}.fa-arrow-circle-right:before{content:\"\uF0A9\"}.fa-arrow-circle-up:before{content:\"\uF0AA\"}.fa-arrow-circle-down:before{content:\"\uF0AB\"}.fa-globe:before{content:\"\uF0AC\"}.fa-wrench:before{content:\"\uF0AD\"}.fa-tasks:before{content:\"\uF0AE\"}.fa-filter:before{content:\"\uF0B0\"}.fa-briefcase:before{content:\"\uF0B1\"}.fa-arrows-alt:before{content:\"\uF0B2\"}.fa-group:before,.fa-users:before{content:\"\uF0C0\"}.fa-chain:before,.fa-link:before{content:\"\uF0C1\"}.fa-cloud:before{content:\"\uF0C2\"}.fa-flask:before{content:\"\uF0C3\"}.fa-cut:before,.fa-scissors:before{content:\"\uF0C4\"}.fa-copy:before,.fa-files-o:before{content:\"\uF0C5\"}.fa-paperclip:before{content:\"\uF0C6\"}.fa-floppy-o:before,.fa-save:before{content:\"\uF0C7\"}.fa-square:before{content:\"\uF0C8\"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:\"\uF0C9\"}.fa-list-ul:before{content:\"\uF0CA\"}.fa-list-ol:before{content:\"\uF0CB\"}.fa-strikethrough:before{content:\"\uF0CC\"}.fa-underline:before{content:\"\uF0CD\"}.fa-table:before{content:\"\uF0CE\"}.fa-magic:before{content:\"\uF0D0\"}.fa-truck:before{content:\"\uF0D1\"}.fa-pinterest:before{content:\"\uF0D2\"}.fa-pinterest-square:before{content:\"\uF0D3\"}.fa-google-plus-square:before{content:\"\uF0D4\"}.fa-google-plus:before{content:\"\uF0D5\"}.fa-money:before{content:\"\uF0D6\"}.fa-caret-down:before{content:\"\uF0D7\"}.fa-caret-up:before{content:\"\uF0D8\"}.fa-caret-left:before{content:\"\uF0D9\"}.fa-caret-right:before{content:\"\uF0DA\"}.fa-columns:before{content:\"\uF0DB\"}.fa-sort:before,.fa-unsorted:before{content:\"\uF0DC\"}.fa-sort-desc:before,.fa-sort-down:before{content:\"\uF0DD\"}.fa-sort-asc:before,.fa-sort-up:before{content:\"\uF0DE\"}.fa-envelope:before{content:\"\uF0E0\"}.fa-linkedin:before{content:\"\uF0E1\"}.fa-rotate-left:before,.fa-undo:before{content:\"\uF0E2\"}.fa-gavel:before,.fa-legal:before{content:\"\uF0E3\"}.fa-dashboard:before,.fa-tachometer:before{content:\"\uF0E4\"}.fa-comment-o:before{content:\"\uF0E5\"}.fa-comments-o:before{content:\"\uF0E6\"}.fa-bolt:before,.fa-flash:before{content:\"\uF0E7\"}.fa-sitemap:before{content:\"\uF0E8\"}.fa-umbrella:before{content:\"\uF0E9\"}.fa-clipboard:before,.fa-paste:before{content:\"\uF0EA\"}.fa-lightbulb-o:before{content:\"\uF0EB\"}.fa-exchange:before{content:\"\uF0EC\"}.fa-cloud-download:before{content:\"\uF0ED\"}.fa-cloud-upload:before{content:\"\uF0EE\"}.fa-user-md:before{content:\"\uF0F0\"}.fa-stethoscope:before{content:\"\uF0F1\"}.fa-sui