UNPKG

@syncfusion/ej2-richtexteditor

Version:
1,020 lines (1,013 loc) 180 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { Component, EventHandler, Complex, Browser, addClass, detach, updateCSSText, isNullOrUndefined } from '@syncfusion/ej2-base'; import { Property, NotifyPropertyChanges, formatUnit, L10n, closest } from '@syncfusion/ej2-base'; import { setStyleAttribute, Event, removeClass, print as printWindow, attributes } from '@syncfusion/ej2-base'; import { isNullOrUndefined as isNOU, compile, append, extend, debounce } from '@syncfusion/ej2-base'; import { Touch as EJ2Touch } from '@syncfusion/ej2-base'; import { getScrollableParent } from '@syncfusion/ej2-popups'; import * as events from '../base/constant'; import * as EVENTS from './../../common/constant'; import * as classes from '../base/classes'; import { Render } from '../renderer/render'; import { ViewSource } from '../renderer/view-source'; import { executeGroup } from './interface'; import { ServiceLocator } from '../services/service-locator'; import { RendererFactory } from '../services/renderer-factory'; import { RenderType } from './enum'; import { ExecCommandCallBack } from '../actions/execute-command-callback'; import { KeyboardEvents } from '../actions/keyboard'; import { ToolbarSettings, ImageSettings, AudioSettings, VideoSettings, QuickToolbarSettings, FontFamily, FontSize, Format, NumberFormatList, BulletFormatList, FormatPainterSettings, ImportWord, ExportWord, ExportPdf, CodeBlockSettings, LineHeight } from '../../models/toolbar-settings'; import { EmojiSettings } from '../../models/emoji-settings'; import { FileManagerSettings } from '../models/fileManager-settings'; import { TableSettings, PasteCleanupSettings } from '../../models/toolbar-settings'; import { FontColor, BackgroundColor } from '../../models/toolbar-settings'; import { IFrameSettings } from '../../models/iframe-settings'; import { InlineMode } from '../../models/inline-mode'; import { defaultLocale } from '../models/default-locale'; import { setAttributes } from '../actions/html-attributes'; import { FullScreen } from '../actions/full-screen'; import { EnterKeyAction } from '../actions/enter-key'; import * as CONSTANT from '../../common/constant'; import { dispatchEvent, getEditValue, decode, isEditableValueEmpty, getDefaultValue } from '../base/util'; import { cleanHTMLString, scrollToCursor, getStructuredHtml, isIDevice, alignmentHtml, openPrintWindow } from '../../common/util'; import { DialogRenderer } from '../renderer/dialog-renderer'; import { SlashMenuSettings } from '../../models/slash-menu-settings'; import { mentionRestrictKeys } from '../../common/config'; import { CustomUserAgentData } from '../../common/user-agent'; import { cleanupInternalElements, removeSelectionClassStates, resetContentEditableElements } from '../../common/util'; import { NodeSelection } from '../../selection/index'; import { DialogType } from '../../common/enum'; import { AIAssistantSettings } from '../models/ai-assistant-settings'; import { DEFAULT_AI_COMMANDS } from '../models/items'; import { PopupUploader } from '../renderer/popup-uploader-renderer'; /** * Represents the Rich Text Editor component. * ```html * <textarea id="rte"></textarea> * <script> * var rteObj = new RichTextEditor(); * rteObj.appendTo("#rte"); * </script> * ``` */ var RichTextEditor = /** @class */ (function (_super) { __extends(RichTextEditor, _super); function RichTextEditor(options, element) { var _this = _super.call(this, options, element) || this; _this.hasContentChanged = false; _this.isPlainPaste = false; _this.needsID = true; _this.isCopyAll = false; _this.isSelecting = false; _this.isSelectionStartInRTE = false; _this.isRTEFocused = false; return _this; } /** * To provide the array of modules needed for component rendering * * @returns {ModuleDeclaration[]} - specifies the declaration. * @hidden */ RichTextEditor.prototype.requiredModules = function () { var modules = []; if (this.toolbarSettings.enable) { modules.push({ member: 'toolbar', args: [this, this.serviceLocator] }); if (this.quickToolbarSettings.enable) { modules.push({ member: 'quickToolbar', args: [this, this.serviceLocator] }); } } if (this.editorMode === 'HTML' && this.slashMenuSettings.enable) { modules.push({ member: 'slashMenu', args: [this, this.serviceLocator] }); } if (this.showCharCount) { modules.push({ member: 'count', args: [this, this.serviceLocator] }); } if (this.editorMode === 'Markdown') { modules.push({ member: 'markdownEditor', args: [this, this.serviceLocator] }); } modules.push({ member: 'link', args: [this, this.serviceLocator] }); modules.push({ member: 'table', args: [this, this.serviceLocator] }); modules.push({ member: 'image', args: [this, this.serviceLocator] }); if (this.editorMode === 'HTML') { modules.push({ member: 'audio', args: [this, this.serviceLocator] }); modules.push({ member: 'video', args: [this, this.serviceLocator] }); modules.push({ member: 'htmlEditor', args: [this, this.serviceLocator] }); modules.push({ member: 'pasteCleanup', args: [this, this.serviceLocator] }); modules.push({ member: 'importExport', args: [this, this.serviceLocator] }); modules.push({ member: 'formatPainter', args: [this] }); modules.push({ member: 'emojiPicker', args: [this, this.serviceLocator] }); modules.push({ member: 'codeBlock', args: [this, this.serviceLocator] }); if (this.enableClipboardCleanup) { modules.push({ member: 'clipBoardCleanup', args: [this] }); } if (this.enableMarkdownAutoFormat) { modules.push({ member: 'autoFormat', args: [this] }); } modules.push({ member: 'aiAssistant', args: [this, this.serviceLocator] }); } if (this.fileManagerSettings.enable) { modules.push({ member: 'fileManager', args: [this, this.serviceLocator] }); } if (this.enableResize) { modules.push({ member: 'resize', args: [this] }); } return modules; }; RichTextEditor.prototype.updateEnable = function () { if (this.enabled) { removeClass([this.element], classes.CLS_DISABLED); this.element.setAttribute('aria-disabled', 'false'); if (!isNOU(this.htmlAttributes.tabindex)) { this.inputElement.setAttribute('tabindex', this.htmlAttributes.tabindex); } else { this.inputElement.setAttribute('tabindex', '0'); } } else { if (this.getToolbar()) { removeClass(this.getToolbar().querySelectorAll('.' + classes.CLS_ACTIVE), classes.CLS_ACTIVE); removeClass([this.getToolbar().parentElement], [classes.CLS_TB_FLOAT]); } addClass([this.element], classes.CLS_DISABLED); this.element.tabIndex = -1; this.element.setAttribute('aria-disabled', 'true'); this.inputElement.setAttribute('tabindex', '-1'); } }; /** * setEnable method * * @returns {void} * @hidden */ RichTextEditor.prototype.setEnable = function () { this.updateEnable(); // eslint-disable-next-line (this.enabled) ? this.eventInitializer() : this.unWireEvents(); }; RichTextEditor.prototype.initializeValue = function () { this.isFocusOut = false; this.isRTE = false; this.isToolbarClipboardAction = false; this.isBlur = true; this.defaultResetValue = null; this.isResizeInitialized = false; }; /** * For internal use only - Initialize the event handler; * * @returns {void} * @hidden * @private */ RichTextEditor.prototype.preRender = function () { this.initializeValue(); this.clickPoints = { clientX: 0, clientY: 0 }; this.initialValue = this.value; this.serviceLocator = new ServiceLocator; this.initializeServices(); this.setContainer(); this.persistData(); setStyleAttribute(this.element, { 'width': formatUnit(this.width) }); attributes(this.element, { role: 'application', 'aria-label': 'Rich Text Editor' }); this.beforeRenderClassValue = this.element.getAttribute('class'); }; RichTextEditor.prototype.persistData = function () { if (this.enablePersistence && this.originalElement.tagName === 'TEXTAREA') { this.element.id = this.originalElement.id + '_wrapper'; var data = window.localStorage.getItem(this.getModuleName() + this.element.id); if (!(isNOU(data) || (data === ''))) { this.setProperties(JSON.parse(data), true); } } }; RichTextEditor.prototype.setContainer = function () { this.originalElement = this.element.cloneNode(true); if (this.value === null || this.valueTemplate !== null) { this.setValue(); } if (this.element.hasAttribute('tabindex')) { this.htmlAttributes = { 'tabindex': this.element.getAttribute('tabindex') }; this.element.removeAttribute('tabindex'); } this.element.innerHTML = ''; var invalidAttr = ['class', 'style', 'id', 'ejs-for']; var htmlAttr = {}; for (var a = 0; a < this.element.attributes.length; a++) { if (invalidAttr.indexOf(this.element.attributes[a].name) === -1 && !(/^data-val/.test(this.element.attributes[a].name))) { // data-val for asp.net core data annotation validation. htmlAttr[this.element.attributes[a].name] = this.element.getAttribute(this.element.attributes[a].name); } } extend(htmlAttr, this.htmlAttributes, htmlAttr); this.setProperties({ htmlAttributes: htmlAttr }, true); if (!isNOU(this.htmlAttributes.id)) { this.element.id = this.htmlAttributes.id; } this.internalID = this.element.id; if (this.element.tagName === 'TEXTAREA') { var rteOuterWrapper = this.createElement('div', { className: this.element.getAttribute('class') }); this.element.innerHTML = ''; this.element.parentElement.insertBefore(rteOuterWrapper, this.element); this.valueContainer = this.element; removeClass([this.valueContainer], this.element.getAttribute('class').split(' ')); this.element = rteOuterWrapper; } else { this.valueContainer = this.createElement('textarea', { id: this.getID() + '-value', attrs: { 'aria-labelledby': this.getID() } }); } this.valueContainer.name = this.getID(); addClass([this.valueContainer], classes.CLS_RTE_HIDDEN); if (!isNOU(this.cssClass)) { var currentClassList = this.cssClass.split(' '); for (var i = 0; i < currentClassList.length; i++) { addClass([this.valueContainer], currentClassList[i]); } } this.rootContainer = this.createElement('div', { className: classes.CLS_RTE_CONTAINER, attrs: { 'role': 'presentation' } }); this.element.appendChild(this.rootContainer); this.rootContainer.appendChild(this.valueContainer); }; /** * getPersistData method * * @returns {void} * @hidden */ RichTextEditor.prototype.getPersistData = function () { return this.addOnPersist(['value']); }; /** * Focuses the Rich Text Editor component. * * @returns {void} * @public */ RichTextEditor.prototype.focusIn = function () { if (this.enabled) { this.inputElement.focus(); this.focusHandler({}); this.notify(events.toolbarRefresh, {}); } }; /** * Blurs the Rich Text Editor component, removing focus. * * @returns {void} * @public */ RichTextEditor.prototype.focusOut = function () { if (this.enabled) { this.inputElement.blur(); this.blurHandler({}); } }; /** * Selects all content within the RichTextEditor. * * @returns {void} * @public */ RichTextEditor.prototype.selectAll = function () { this.notify(events.selectAll, {}); }; /** * Selects a specific content range or element. * * @param {Range} range - Specify the range you want to select within the content. * This method is used to select a particular sentence, word, or the entire document. * * @returns {void} * @public */ RichTextEditor.prototype.selectRange = function (range) { this.notify(events.selectRange, { range: range }); }; /** * Retrieves the HTML markup from the currently selected content in RichTextEditor. * * @returns {string} - Returns the HTML string of selected content. * @public */ RichTextEditor.prototype.getSelection = function () { var str = ''; this.notify(events.getSelectedHtml, { callBack: function (txt) { str = txt; } }); return str; }; /** * Displays the emoji picker. If coordinates are provided, it positions the picker at those locations. * * @param {number} x - The x-axis position for the emoji picker. * @param {number} y - The y-axis position for the emoji picker. * @returns {void} * @public */ RichTextEditor.prototype.showEmojiPicker = function (x, y) { if (this.readonly) { return; } this.notify(events.emojiPicker, { x: x, y: y }); }; /** * Executes a specified command within the rich text editor, optionally utilizing additional parameters to tailor execution. * * @returns {void} * @param {CommandName} commandName - The name of the command to be executed, such as 'importWord', 'insertHTML', and others. * @param {string | HTMLElement | ILinkCommandsArgs | IImageCommandsArgs | ITableCommandsArgs | FormatPainterSettingsModel | IAudioCommandsArgs | IVideoCommandsArgs} value * - An optional parameter that supplies the necessary value relevant to the command. This could be a string, an HTMLElement, or specific argument types like ILinkCommandsArgs, etc., contingent on the command requirements. * @param {ExecuteCommandOption} option - Specifies additional options for executing the command, such as enabling features like undo functionality. * @public */ RichTextEditor.prototype.executeCommand = function (commandName, value, option) { if (commandName === 'importWord') { var importContainer = this.createElement('div'); importContainer.innerHTML = value; var tableElement = importContainer.querySelectorAll('table:not(.e-rte-table):not(.e-rte-paste-table)'); for (var i = 0; i < tableElement.length; i++) { tableElement[i].classList.add('e-rte-paste-table'); } value = importContainer.innerHTML; importContainer.remove(); commandName = 'insertHTML'; } value = this.htmlPurifier(commandName, value); var internalValue; if (this.editorMode === 'HTML') { var range = this.getRange(); if (this.iframeSettings.enable) { this.formatter.editorManager.nodeSelection.Clear(this.element.ownerDocument); } var toFocus = (this.iframeSettings.enable && range.startContainer === this.inputElement) ? true : !this.inputElement.contains(range.startContainer); if (this.iframeSettings.enable) { toFocus = true; } if (toFocus) { this.focusIn(); } } var tool = executeGroup["" + commandName]; if (option && option.undo) { if (option.undo && this.formatter.getUndoRedoStack().length === 0) { this.formatter.saveData(); } } if (this.maxLength !== -1 && !isNOU(tool.command)) { var currentInsertContentLength = 0; if (tool.command === 'Links') { currentInsertContentLength = value.text.length === 0 ? value.url.length : value.text.length; } if (tool.command === 'Images' || tool.command === 'Table' || tool.command === 'Files') { currentInsertContentLength = 1; } if (tool.command === 'InsertHTML') { if (!isNOU(value)) { var tempElem = this.createElement('div'); tempElem.innerHTML = value; currentInsertContentLength = tempElem.textContent.length; } else if (!isNOU(tool.value) && (tool.value === '<hr/>' || tool.value === '<br/>')) { currentInsertContentLength = 1; } } if (tool.command === 'InsertText') { currentInsertContentLength = value.length; } var currentLength = this.getText().trim().replace(/(\r\n|\n|\r|\t)/gm, '').replace(/\u200B/g, '').length; var selectionLength = this.getSelection().length; var totalLength = (currentLength - selectionLength) + currentInsertContentLength; if (!(this.maxLength === -1 || totalLength <= this.maxLength)) { return; } } internalValue = value; if (tool.command === 'FormatPainter') { if (!isNOU(value)) { this.formatPainterSettings = value; } internalValue = { formatPainterAction: tool.value }; } if (tool.command === 'lineHeight') { internalValue = { selectedValue: (value ? value : tool.value) }; } if (tool.command === 'CodeBlock' && !isNOU(value)) { value.action = 'createCodeBlock'; } if ((tool.subCommand === 'NumberFormatList' || tool.subCommand === 'BulletFormatList')) { internalValue = { listStyle: value, type: tool.subCommand }; } this.formatter.editorManager.execCommand(tool.command, tool.subCommand ? tool.subCommand : (internalValue ? internalValue : tool.value), null, null, (internalValue ? internalValue : tool.value), (internalValue ? internalValue : (tool.value === 'UL' || tool.value === 'OL') ? null : tool.value), null, this.enterKey); scrollToCursor(this.contentModule.getDocument(), this.inputElement); if (option && option.undo) { this.formatter.saveData(); this.formatter.enableUndo(this); } this.setPlaceHolder(); this.notify(events.contentChanged, {}); }; RichTextEditor.prototype.htmlPurifier = function (command, value) { if (this.editorMode === 'HTML') { switch (command) { case 'insertHTML': if (this.enableHtmlSanitizer) { if (typeof value === 'string') { value = value.replace(/&(times|divide|ne)/g, '&amp;amp;$1'); value = this.htmlEditorModule.sanitizeHelper(value); } else { value = this.htmlEditorModule.sanitizeHelper(value.outerHTML); } } break; case 'insertTable': if (isNOU(value.width)) { value.width = { minWidth: this.tableSettings.minWidth, maxWidth: this.tableSettings.maxWidth, width: this.tableSettings.width }; } break; case 'insertImage': { var temp = this.createElement('img', { attrs: { src: value.url } }); var imageValue = temp.outerHTML; if (this.enableHtmlSanitizer) { imageValue = this.htmlEditorModule.sanitizeHelper(temp.outerHTML); } var url = (imageValue !== '' && (this.createElement('div', { innerHTML: imageValue }).firstElementChild).getAttribute('src')) || null; url = !isNOU(url) ? url : ''; value.url = url; if (isNOU(value.width)) { value.width = { minWidth: this.insertImageSettings.minWidth, maxWidth: this.insertImageSettings.maxWidth, width: this.insertImageSettings.width }; } if (isNOU(value.height)) { value.height = { minHeight: this.insertImageSettings.minHeight, maxHeight: this.insertImageSettings.maxHeight, height: this.insertImageSettings.height }; } break; } case 'insertAudio': { var wrapTemp = this.createElement('audio', { attrs: { controls: '' } }); var temp = this.createElement('source', { attrs: { src: value.url, type: value.url && value.url.split('.').length > 0 ? 'audio/' + value.url.split('.')[value.url.split('.').length - 1] : '' } }); wrapTemp.appendChild(temp); var audioValue = wrapTemp.outerHTML; if (this.enableHtmlSanitizer) { audioValue = this.htmlEditorModule.sanitizeHelper(wrapTemp.outerHTML); } var url = (audioValue !== '' && (this.createElement('div', { innerHTML: audioValue }).firstElementChild.firstElementChild).getAttribute('src')) || null; url = !isNOU(url) ? url : ''; value.url = url; break; } case 'insertVideo': { var wrapTemp = this.createElement('video', { attrs: { controls: '' } }); var temp = this.createElement('source', { attrs: { src: value.url, type: value.url && value.url.split('.').length > 0 ? 'video/' + value.url.split('.')[value.url.split('.').length - 1] : '' } }); wrapTemp.appendChild(temp); var audioValue = wrapTemp.outerHTML; if (this.enableHtmlSanitizer) { audioValue = this.htmlEditorModule.sanitizeHelper(temp.outerHTML); } var url = (audioValue !== '' && (this.createElement('div', { innerHTML: audioValue }).firstElementChild).getAttribute('src')) || null; url = !isNOU(url) ? url : ''; value.url = url; if (isNOU(value.width)) { value.width = { minWidth: this.insertVideoSettings.minWidth, maxWidth: this.insertVideoSettings.maxWidth, width: this.insertVideoSettings.width }; } if (isNOU(value.height)) { value.height = { minHeight: this.insertVideoSettings.minHeight, maxHeight: this.insertVideoSettings.maxHeight, height: this.insertVideoSettings.height }; } break; } case 'createLink': { var tempNode = this.createElement('a', { attrs: { href: value.url } }); var linkValue = tempNode.outerHTML; if (this.enableHtmlSanitizer) { linkValue = this.htmlEditorModule.sanitizeHelper(tempNode.outerHTML); } var href = (linkValue !== '' && (this.createElement('div', { innerHTML: linkValue }).firstElementChild).getAttribute('href')) || null; href = !isNOU(href) ? href : ''; value.url = href; break; } } } return value; }; RichTextEditor.prototype.encode = function (value) { var divNode = this.createElement('div'); divNode.innerText = value.trim(); // eslint-disable-next-line return divNode.innerHTML.replace(/<br\s*[\/]?>/gi, '\n'); }; /** * For internal use only - To Initialize the component rendering. * * @returns {void} * @private */ RichTextEditor.prototype.render = function () { this.setProperties({ value: this.replaceEntities(this.value) }, true); if (this.value && !this.valueTemplate) { this.setProperties({ value: this.serializeValue(this.value) }, true); } this.value = (!(this.editorMode === 'Markdown') && !isNOU(this.value)) ? this.addAnchorAriaLabel(this.value) : this.value; this.renderModule = new Render(this, this.serviceLocator); this.sourceCodeModule = new ViewSource(this, this.serviceLocator); this.notify(events.initialLoad, {}); this.trigger(events.load); this.RTERender(); // eslint-disable-next-line var execCommandCallBack = new ExecCommandCallBack(this); if (this.element.dataset.rteUnitTesting === 'true') { this.userAgentData = new CustomUserAgentData(Browser.userAgent, true); } else { this.userAgentData = new CustomUserAgentData(Browser.userAgent, false); } this.notify(events.initialEnd, {}); if (this.enableXhtml) { this.value = getStructuredHtml(cleanHTMLString(this.value, this.element), this.enterKey, this.enableHtmlEncode); this.setProperties({ value: this.getXhtml() }); } if (this.toolbarSettings.enable && (this.toolbarSettings.type === 'Expand' || this.toolbarSettings.type === 'MultiRow' || this.toolbarSettings.type === 'Scrollable') && !isNOU(this.getToolbar()) && (this.toolbarSettings.items.indexOf('Undo') > -1 && this.toolbarSettings.items.indexOf('Redo') > -1)) { this.disableToolbarItem(['Undo', 'Redo']); } if (this.value !== null) { this.valueContainer.defaultValue = this.value; } // eslint-disable-next-line (this.enabled && !this.readonly) ? this.eventInitializer() : this.unWireEvents(); this.notify(events.bindCssClass, { cssClass: this.getCssClass() }); this.addAudioVideoWrapper(); this.notify(events.tableclass, {}); this.autoResize(); this.renderComplete(); }; /** * addAudioVideoWrapper method * * @returns {void} * @hidden */ RichTextEditor.prototype.addAudioVideoWrapper = function () { var _this = this; var insertElem; var audioElm = this.element.querySelectorAll('audio'); for (var i = 0; i < audioElm.length; i++) { if (!audioElm[i].classList.contains('e-rte-audio')) { audioElm[i].classList.add('e-rte-audio'); audioElm[i].classList.add(classes.CLS_AUDIOINLINE); } // eslint-disable-next-line max-len if (!audioElm[i].parentElement.classList.contains(classes.CLS_CLICKELEM) && !audioElm[i].parentElement.classList.contains(classes.CLS_AUDIOWRAP)) { var audioWrapElem = this.createElement('span', { className: classes.CLS_AUDIOWRAP }); var csstext = 'width:300px; margin:0 auto;'; updateCSSText(audioWrapElem, csstext); audioWrapElem.contentEditable = 'false'; var audioInnerWrapElem = this.createElement('span', { className: classes.CLS_CLICKELEM }); audioWrapElem.appendChild(audioInnerWrapElem); audioElm[i].parentNode.insertBefore(audioWrapElem, audioElm[i].nextSibling); audioInnerWrapElem.appendChild(audioElm[i]); if (audioWrapElem.nextElementSibling === null) { insertElem = this.createElement('br'); audioWrapElem.parentNode.insertBefore(insertElem, audioWrapElem.nextSibling); } } } var videoElm = this.element.querySelectorAll('video'); for (var i = 0; i < videoElm.length; i++) { if (!videoElm[i].classList.contains('e-rte-video')) { videoElm[i].classList.add('e-rte-video'); videoElm[i].classList.add(classes.CLS_VIDEOINLINE); } // eslint-disable-next-line max-len if (!videoElm[i].parentElement.classList.contains(classes.CLS_CLICKELEM) && !videoElm[i].parentElement.classList.contains(classes.CLS_VIDEOWRAP)) { var videoWrapElem = this.createElement('span', { className: classes.CLS_VIDEOWRAP }); videoWrapElem.contentEditable = 'false'; videoElm[i].parentNode.insertBefore(videoWrapElem, videoElm[i].nextSibling); videoWrapElem.appendChild(videoElm[i]); if (videoWrapElem.nextElementSibling === null) { insertElem = this.createElement('br'); videoWrapElem.parentNode.insertBefore(insertElem, videoWrapElem.nextSibling); } } if (Browser.userAgent.indexOf('Firefox') !== -1) { // eslint-disable-next-line videoElm[i].addEventListener('play', function (args) { _this.notify(events.mouseDown, { args: args }); _this.notify('editAreaClick', { args: args }); }); // eslint-disable-next-line videoElm[i].addEventListener('pause', function (args) { _this.notify(events.mouseDown, { args: args }); _this.notify('editAreaClick', { args: args }); }); } } }; /** * For internal use only - Initialize the event handler * * @returns {void} * @private * @hidden */ RichTextEditor.prototype.eventInitializer = function () { this.wireEvents(); }; // eslint-disable-next-line @typescript-eslint/no-unused-vars RichTextEditor.prototype.cleanList = function (e) { var range = this.getRange(); var currentStartContainer = range.startContainer; var currentEndContainer = range.endContainer; var currentStartOffset = range.startOffset; var isSameContainer = currentStartContainer === currentEndContainer ? true : false; // eslint-disable-next-line var currentEndOffset = currentEndContainer.textContent.length; var endNode = range.endContainer.nodeName === '#text' ? range.endContainer.parentElement : range.endContainer; var closestLI = closest(endNode, 'LI'); var isDetached = false; var currentRangeEndOffset = range.endOffset; if (currentEndContainer.nodeType === Node.TEXT_NODE) { if (currentEndContainer.textContent.charAt(currentRangeEndOffset - 1) === '\uFEFF') { currentRangeEndOffset--; } } if (!isNOU(closestLI) && endNode.textContent.trim().length === currentRangeEndOffset && !range.collapsed && isNOU(endNode.nextElementSibling) && !endNode.classList.contains(classes.CLS_IMG_INNER)) { for (var i = 0; i < closestLI.childNodes.length; i++) { if (closestLI.childNodes[i].nodeName === '#text' && closestLI.childNodes[i].textContent.trim().length === 0) { detach(closestLI.childNodes[i]); isDetached = true; i--; } } var currentLastElem = closestLI; while (currentLastElem.lastChild !== null && currentLastElem.nodeName !== '#text') { currentLastElem = currentLastElem.lastChild; } if (isDetached) { var currentLast = currentLastElem.nodeName === 'BR' && !isNOU(currentLastElem.previousSibling) ? currentLastElem.previousSibling : currentLastElem; this.formatter.editorManager.nodeSelection.setSelectionText(this.contentModule.getDocument(), isSameContainer ? currentLast : currentStartContainer, currentLast, currentStartOffset, (currentLast.nodeName === 'BR' ? 0 : currentLast.textContent.length)); } } }; /** * For internal use only - keydown the event handler; * * @param {KeyboardEvent} e - specifies the event. * @returns {void} * @private * @hidden */ RichTextEditor.prototype.keyDown = function (e) { this.isSelectionStartInRTE = true; var isMacDev = this.userAgentData.getPlatform() === 'macOS'; if (((e.ctrlKey || (e.metaKey && isMacDev)) && e.shiftKey && e.keyCode === 86) || (e.metaKey && isMacDev && e.altKey && e.shiftKey && e.keyCode === 86)) { this.isPlainPaste = true; } if (this.inputElement.classList.contains('e-mention')) { var mentionPopup = this.element.ownerDocument.getElementById(this.inputElement.id + '_popup'); var slashMenuPopup = this.element.ownerDocument.getElementById(this.inputElement.id + '_slash_menu_popup'); var mentionKeys = mentionRestrictKeys; var isMentionKeys = mentionKeys.indexOf(e.key) !== -1; var isMentionPopupOpen = mentionPopup && mentionPopup.classList.contains('e-popup-open'); var isSlashMenuPopupOpen = slashMenuPopup && slashMenuPopup.classList.contains('e-popup-open'); if (isMentionKeys && (isMentionPopupOpen || isSlashMenuPopupOpen)) { return; } } if (this.editorMode === 'HTML' && !isNOU(this.codeBlockModule)) { var rangeForCodeBlock = this.getRange(); if (this.formatter.editorManager.codeBlockObj.isActionDisallowedInCodeBlock(e, rangeForCodeBlock)) { e.preventDefault(); return; } } if (this.enableTabKey) { if (this.quickToolbarModule && !e.altKey && e.key !== 'F10' && e.action !== 'toolbar-focus') { this.quickToolbarModule.hideQuickToolbars(); } var isImageResize = this.imageModule && this.imageModule.imgResizeDiv ? true : false; var isVideoResize = this.videoModule && this.videoModule.vidResizeDiv ? true : false; if (isImageResize) { this.imageModule.cancelResizeAction(); } if (isVideoResize) { this.videoModule.cancelResizeAction(); } } var isCodeBlockEnter = false; if (!isNOU(this.codeBlockModule)) { var range = this.getRange(); isCodeBlockEnter = this.formatter.editorManager.codeBlockObj.isCodeBlockEnterAction(range, e); } this.notify(events.keyDown, { member: 'keydown', args: e }); this.restrict(e); if (this.editorMode === 'HTML') { this.cleanList(e); } if (this.editorMode === 'HTML' && ((e.which === 8 && e.code === 'Backspace') || (e.which === 46 && e.code === 'Delete'))) { var range = this.getRange(); var startNode = range.startContainer.nodeName === '#text' ? range.startContainer.parentElement : range.startContainer; if (closest(startNode, 'pre') && (e.which === 8 && range.startContainer.textContent.charCodeAt(range.startOffset - 1) === 8203) || (e.which === 46 && range.startContainer.textContent.charCodeAt(range.startOffset) === 8203)) { var regEx = new RegExp('\u200B', 'g'); var pointer = e.which === 8 ? range.startOffset - 1 : range.startOffset; range.startContainer.textContent = range.startContainer.textContent.replace(regEx, ''); this.formatter.editorManager.nodeSelection.setCursorPoint(this.contentModule.getDocument(), range.startContainer, pointer); } else if ((e.code === 'Backspace' && e.which === 8) && range.startContainer.textContent.charCodeAt(0) === 8203 && range.collapsed) { var parentEle = range.startContainer.parentElement; var index = void 0; var i = void 0; for (i = 0; i < parentEle.childNodes.length; i++) { if (parentEle.childNodes[i] === range.startContainer) { index = i; } } var bool = true; var removeNodeArray = []; for (i = index; i >= 0; i--) { // eslint-disable-next-line max-len if (parentEle.childNodes[i].nodeType === 3 && parentEle.childNodes[i].textContent.charCodeAt(0) === 8203 && bool) { removeNodeArray.push(i); } else { bool = false; } } if (removeNodeArray.length > 0) { for (i = removeNodeArray.length - 1; i > 0; i--) { parentEle.childNodes[removeNodeArray[i]].textContent = ''; } } this.formatter.editorManager.nodeSelection.setCursorPoint(this.contentModule.getDocument(), range.startContainer, range.startOffset); } } var notFormatPainterCopy = isNOU(e.action) ? true : (e.action !== 'format-copy' ? true : false); if (this.formatter.getUndoRedoStack().length === 0 && notFormatPainterCopy && !(e.altKey || (e.shiftKey && e.which === 16) || (e.altKey && e.shiftKey && e.which === 67))) { this.formatter.saveData(); } var preventingMention = false; var allowInsideCodeBlock = true; if (this.editorMode === 'HTML') { var range = this.getRange(); preventingMention = !isNOU(range.startContainer) && range.startContainer === range.endContainer && range.endContainer.childNodes.length > 1 && !isNOU(range.startContainer.childNodes[range.startOffset - 1]) && range.startContainer.childNodes[range.startOffset - 1].nodeName === '#text' && !isNOU(range.startContainer.childNodes[range.startOffset - 1].previousSibling) && range.startContainer.childNodes[range.startOffset - 1].textContent.charCodeAt(0) === 32 && range.startContainer.childNodes[range.startOffset - 1].previousSibling.nodeName !== '#text' && range.startContainer.childNodes[range.startOffset - 1].previousSibling.classList.contains('e-mention-chip'); if (!isNOU(this.codeBlockModule)) { var startInCodeBlock = this.formatter.editorManager.codeBlockObj.isValidCodeBlockStructure(range.startContainer); var endInCodeBlock = this.formatter.editorManager.codeBlockObj.isValidCodeBlockStructure(range.endContainer); var codeBlockElement = !isNOU(startInCodeBlock) || !isNOU(endInCodeBlock); if (codeBlockElement) { var currentAction = e.action; var allowActions = ['undo', 'redo', 'indents', 'outdents', 'ordered-list', 'unordered-list']; allowInsideCodeBlock = allowActions.indexOf(currentAction) !== -1; } } } var keyboardEventAction = ['insert-link', 'format-copy', 'format-paste', 'insert-image', 'insert-table', 'insert-audio', 'insert-video']; if (keyboardEventAction.indexOf(e.action) === -1 && (!e.target || !(e.target.classList.contains('e-mention') && !isNOU(document.querySelector('#' + e.target.id + '_popup.e-popup-open')) && e.code === 'Tab')) && (e.action && e.action !== 'paste' && e.action !== 'space' || e.which === 9 || (e.code === 'Backspace' && e.which === 8)) && !preventingMention) { var FormatPainterEscapeAction = false; if (!isNOU(this.formatPainterModule)) { FormatPainterEscapeAction = this.formatPainterModule.previousAction === 'escape'; } var isUndoRedoAction = e.action === 'undo' || e.action === 'redo'; if ((!FormatPainterEscapeAction || isUndoRedoAction) && allowInsideCodeBlock && !isCodeBlockEnter) { if (this.editorMode === 'HTML' && (e.action === 'increase-fontsize' || e.action === 'decrease-fontsize')) { this.notify(events.onHandleFontsizeChange, { member: 'onHandleFontsizeChange', args: e }); } else { this.formatter.process(this, null, e); } } switch (e.action) { case 'toolbar-focus': if (this.toolbarSettings.enable && this.getToolbarElement()) { if (this.userAgentData.isSafari() && e.type === 'keydown' && this.formatter.editorManager.nodeSelection && this.formatter.editorManager.nodeSelection.get(this.contentModule.getDocument()).rangeCount > 0 && this.inputElement.contains(this.getRange().startContainer)) { this.notify(events.selectionSave, {}); } var toolbarFocusType = 'toolbar'; var firstActiveItem = this.getToolbarElement().querySelector('.e-toolbar-item:not(.e-overlay)[title]'); var quickToolbarElem = this.getRenderedQuickToolbarElem(); if (quickToolbarElem) { firstActiveItem = quickToolbarElem.querySelector('.e-toolbar-item:not(.e-overlay)[title]'); toolbarFocusType = 'quickToolbar'; } if (firstActiveItem) { var firstChild = firstActiveItem.firstElementChild; firstChild.removeAttribute('tabindex'); firstChild.focus(); if (this.userAgentData.isSafari() && (toolbarFocusType === 'toolbar' || toolbarFocusType === 'quickToolbar')) { this.inputElement.ownerDocument.getSelection().removeAllRanges(); } } } break; case 'escape': this.contentModule.getEditPanel().focus(); break; } } this.notify(events.afterKeyDown, { member: 'afterKeyDown', args: e }); this.autoResize(); if (!isNOU(this.placeholder)) { this.setPlaceHolder(); } if (this.editorMode === 'HTML' && !isNOU(e) && !isNOU(e.code) && (e.code === 'Backspace' || e.code === 'Delete')) { if (this.isEntireRTEContentSelected()) { this.isCopyAll = true; } } // Cmd + Backspace triggers only the keydown event; the keyup event is not triggered. if (e.metaKey && e.key === 'Backspace' && this.autoSaveOnIdle) { this.keyUp(e); } if (this.editorMode === 'HTML') { var selection = this.contentModule.getDocument().getSelection(); var range = selection && selection.getRangeAt(0); this.previousRange = range && range.cloneRange(); } }; // Clear selection timeout for keyup event triggering RichTextEditor.prototype.clearSelectionTimeout = function () { if (this.selectionTimeout) { clearTimeout(this.selectionTimeout); this.selectionTimeout = null; } }; // Triggers the selectionChanged event RichTextEditor.prototype.triggerSelectionChanged = function () { var selection = this.contentModule.getDocument().getSelection(); var currentRange = selection && selection.rangeCount > 0 && selection.getRangeAt(0); if (!this.isSelectionCollapsed()) { var isSamerange = this.previousRange && (this.previousRange.startContainer === currentRange.startContainer && this.previousRange.endContainer === currentRange.endContainer && this.previousRange.startOffset === currentRange.startOffset && this.previousRange.endOffset === currentRange.endOffset); if (!isSamerange) { var selectionArgs = { selectedContent: this.getSelectedHtml(), selection: selection, editorMode: this.editorMode }; this.trigger(events.selectionChanged, selectionArgs); this.previousRange = currentRange.cloneRange(); } } }; RichTextEditor.prototype.keyUp = function (e) { var _this = this; if (this.inputElement.classList.contains('e-mention')) { var mentionPopup = this.element.ownerDocument.getElementById(this.inputElement.id + '_popup'); var slashMenuPopup = this.element.ownerDocument.getElementById(this.inputElement.id + '_slash_menu_popup'); var isMentionPopupOpen = mentionPopup && mentionPopup.classList.contains('e-popup-open'); var isSlashMenuPopupOpen = slashMenuPopup && slashMenuPopup.classList.contains('e-popup-open'); if ((isMentionPopupOpen || isSlashMenuPopupOpen)) { return; } } if (this.editorMode === 'HTML') { var range = this.getRange(); if (!isNOU(e) && !isNOU(e.code) && (e.code === 'Backspace' || e.code === 'Delete' || e.code === 'KeyX')) { // To prevent the reformatting the content removed browser behavior. var currentRange = this.getRange(); var selection = this.iframeSettings.enable ? this.contentModule.getPanel().ownerDocument.getSelection() : this.contentModule.getDocument().getSelection(); if (this.isCopyAll) { var brElement = this.createElement('br'); var newElement = this.enterKey === 'BR' ? brElement : this.createElement(this.enterKey).appendChild(brElement).parentElement; this.inputElement.innerHTML = ''; this.inputElement.append