UNPKG

@syncfusion/ej2-richtexteditor

Version:
979 lines (978 loc) 45.4 kB
import * as events from '../base/constant'; import { Popup } from '@syncfusion/ej2-popups'; import { RadioButton } from '@syncfusion/ej2-buttons'; import { isNullOrUndefined as isNOU, isNullOrUndefined, detach, extend, addClass, removeClass } from '@syncfusion/ej2-base'; import { getUniqueID, Browser, closest } from '@syncfusion/ej2-base'; import { CLS_RTE_PASTE_KEEP_FORMAT, CLS_RTE_PASTE_REMOVE_FORMAT, CLS_RTE_PASTE_PLAIN_FORMAT } from '../base/classes'; import { CLS_RTE_PASTE_OK, CLS_RTE_PASTE_CANCEL, CLS_RTE_DIALOG_MIN_HEIGHT } from '../base/classes'; import { NodeSelection } from '../../selection/selection'; import * as EVENTS from './../../common/constant'; import { RenderType } from '../base/enum'; import { ImageInputSource } from '../../common/enum'; import { Uploader } from '@syncfusion/ej2-inputs'; import * as classes from '../base/classes'; import { sanitizeHelper } from '../base/util'; import { cleanHTMLString, scrollToCursor } from '../../common/util'; import { PasteCleanupAction } from '../../editor-manager/plugin/paste-clean-up-action'; /** * PasteCleanup module called when pasting content in RichTextEditor */ var PasteCleanup = /** @class */ (function () { function PasteCleanup(parent, serviceLocator) { this.isNotFromHtml = false; this.containsHtml = false; this.cropImageData = []; this.plainTextContent = ''; this.parent = parent; this.locator = serviceLocator; this.renderFactory = this.locator.getService('rendererFactory'); this.i10n = serviceLocator.getService('rteLocale'); this.dialogRenderObj = serviceLocator.getService('dialogRenderObject'); this.addEventListener(); this.isDestroyed = false; } PasteCleanup.prototype.addEventListener = function () { this.nodeSelectionObj = new NodeSelection(this.parent.inputElement); if (this.parent.isDestroyed) { return; } this.parent.on(events.pasteClean, this.pasteClean, this); this.parent.on(events.bindOnEnd, this.bindOnEnd, this); this.parent.on(events.bindCssClass, this.setCssClass, this); this.parent.on(events.destroy, this.destroy, this); this.parent.on(events.docClick, this.docClick, this); this.parent.on(events.updateProperty, this.updatePasteCleanupProperty, this); }; PasteCleanup.prototype.destroy = function () { if (this.isDestroyed) { return; } if (this.fireFoxUploadTime) { clearTimeout(this.fireFoxUploadTime); this.fireFoxUploadTime = null; } if (this.refreshPopupTime) { clearTimeout(this.refreshPopupTime); this.refreshPopupTime = null; } if (this.popupCloseTime) { clearTimeout(this.popupCloseTime); this.popupCloseTime = null; } if (this.failureTime) { clearTimeout(this.failureTime); this.failureTime = null; } this.removeEventListener(); if (this.popupObj && !this.popupObj.isDestroyed) { this.popupObj.destroy(); this.popupObj = null; } if (this.uploadObj && !this.uploadObj.isDestroyed) { this.uploadObj.destroy(); this.uploadObj = null; } if (this.keepRadioButton && !this.keepRadioButton.isDestroyed) { this.keepRadioButton.destroy(); this.keepRadioButton = null; } if (this.cleanRadioButton && !this.cleanRadioButton.isDestroyed) { this.cleanRadioButton.destroy(); this.cleanRadioButton = null; } if (this.plainTextRadioButton && !this.plainTextRadioButton.isDestroyed) { this.plainTextRadioButton.destroy(); this.plainTextRadioButton = null; } this.isDestroyed = true; this.plainTextContent = ''; }; PasteCleanup.prototype.removeEventListener = function () { this.parent.off(events.pasteClean, this.pasteClean); this.parent.off(events.bindCssClass, this.setCssClass); this.parent.off(events.destroy, this.destroy); this.parent.off(events.docClick, this.docClick); this.parent.off(events.bindOnEnd, this.bindOnEnd); this.parent.off(events.updateProperty, this.updatePasteCleanupProperty); }; /* * Initializes the PasteCleanupAction object in the editor manager after editor initialization is complete. * This method binds the paste cleanup module to the editor's formatter for handling paste cleanup related operations. */ PasteCleanup.prototype.bindOnEnd = function () { if (this.parent.editorMode === 'HTML' && this.parent.formatter && this.parent.formatter.editorManager && this.parent.contentModule) { var pasteModel = this.getPasteCleanupModel(); this.parent.formatter.editorManager.pasteObj = this.pasteObj = new PasteCleanupAction(this.parent.formatter.editorManager, pasteModel); } }; /* Creates and returns a paste cleanup model with editor configuration and callback methods */ PasteCleanup.prototype.getPasteCleanupModel = function () { var _this = this; // Create TableCommand with table model containing required methods var pasteModel = { rteElement: this.parent.element, enterKey: this.parent.enterKey, rootContainer: this.parent.rootContainer, enableXhtml: this.parent.enableXhtml, iframeSettings: this.parent.iframeSettings, pasteCleanupSettings: this.parent.pasteCleanupSettings, insertImageSettings: this.parent.insertImageSettings, // Retrieves the maximum allowed width for an image within the editor. getInsertImgMaxWidth: function () { return _this.parent.getInsertImgMaxWidth(); }, // Method for retrieving the document object of the content module getDocument: function () { if (!_this.contentModule) { return _this.parent.contentModule.getDocument(); } return _this.contentModule.getDocument(); }, // Method for retrieving the editable element object of the content module getEditPanel: function () { if (!_this.contentModule) { return _this.parent.contentModule.getEditPanel(); } return _this.contentModule.getEditPanel(); }, updateValue: function () { _this.parent.updateValue(); }, imageUpload: function () { _this.imgUploading(_this.parent.inputElement); }, getCropImageData: function () { return _this.getCropImageData(); } }; return pasteModel; }; /* Updates the paste cleanup object with the latest editor configuration settings */ PasteCleanup.prototype.updatePasteCleanupProperty = function () { var pasteModel = this.getPasteCleanupModel(); if (!isNullOrUndefined(this.pasteObj)) { this.pasteObj.updatePasteCleanupModel(pasteModel); } }; /** * Handles the paste cleanup operation when content is pasted into the editor * * @param {NotifyArgs} e - The notification arguments containing event data * @returns {void} */ PasteCleanup.prototype.pasteClean = function (e) { var args = { requestType: 'Paste', editorMode: this.parent.editorMode, event: e }; var value = null; var imageproperties; var allowedTypes = this.parent.insertImageSettings.allowedTypes; // Extract clipboard data if available if (e.args && !isNOU(e.args.clipboardData)) { value = e.args.clipboardData.getData('text/html'); // Store plain text content for later use if (e.args.clipboardData.getData('text/plain')) { this.plainTextContent = e.args.clipboardData.getData('text/plain'); } } // Process only if we have args, value, and in HTML mode if (e.args && value !== null && this.parent.editorMode === 'HTML') { var shouldContinue = this.processHtmlPaste(e, value, args, allowedTypes, imageproperties); if (!shouldContinue) { return; // Stop processing if indicated } } }; /* Processes HTML content from paste operation */ PasteCleanup.prototype.processHtmlPaste = function (e, value, args, allowedTypes, imageproperties) { var processedValue = value; var shouldContinue = true; // Handle empty HTML content (plain text or image) if (value.length === 0) { var result = this.handleEmptyHtmlValue(e, value, args, allowedTypes, imageproperties); processedValue = result.value; shouldContinue = result.shouldContinue; if (!shouldContinue) { return false; // Stop processing } } else if (value.length > 0) { // Handle non-empty HTML content processedValue = this.handleNonEmptyHtmlValue(e, value, args); } // Remove base tags from content if (processedValue !== null && processedValue !== '') { processedValue = processedValue.replace(/<base[^>]*>/g, ''); } this.prepareAndInsertContent(e, processedValue, args); return true; }; /* Handles case when HTML value is empty but may contain plain text or images */ PasteCleanup.prototype.handleEmptyHtmlValue = function (e, value, args, allowedTypes, imageproperties) { var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/i); var processedValue = e.args.clipboardData.getData('text/plain'); this.parent.trigger(events.beforePasteCleanup, { value: processedValue }); this.isNotFromHtml = processedValue !== '' ? true : false; processedValue = processedValue.replace(/</g, '&lt;'); processedValue = processedValue.replace(/>/g, '&gt;'); this.containsHtml = htmlRegex.test(processedValue); this.plainTextContent = processedValue; // Check for image file in clipboard var file = this.pasteObj.extractFileFromClipboard(e); // Validate image type if file exists if (file) { var fileNameParts = file.name; var imgType = fileNameParts.substring(fileNameParts.lastIndexOf('.')); if (this.isInvalidImageType(imgType, allowedTypes)) { e.args.preventDefault(); return { value: processedValue, shouldContinue: false }; // Signal to stop processing } } // Process paste with potential image var result = this.processPasteWithImage(e, file, processedValue, args, imageproperties, htmlRegex); return result; }; /* Checks if image type is not in allowed types */ PasteCleanup.prototype.isInvalidImageType = function (imgType, allowedTypes) { return allowedTypes.every(function (type) { return type.toLowerCase() !== imgType; }); }; /* Processes paste operation that may contain an image */ PasteCleanup.prototype.processPasteWithImage = function (e, file, value, args, imageproperties, htmlRegex) { var _this = this; var processedValue = value; //let shouldContinue: boolean = true; this.parent.notify(events.paste, { file: file, args: e.args, text: processedValue, callBack: function (b) { imageproperties = b; if (typeof (imageproperties) === 'object') { _this.parent.formatter.editorManager.execCommand('Images', 'Image', e.args, _this.imageFormatting.bind(_this, args), 'pasteCleanup', imageproperties, 'pasteCleanupModule'); } else { processedValue = imageproperties; } } }); // Format plain text with line breaks if not HTML if (!htmlRegex.test(processedValue)) { var divElement = this.parent.createElement('div'); divElement.innerHTML = this.pasteObj.splitBreakLine(processedValue); processedValue = divElement.innerHTML; } return { value: processedValue, shouldContinue: true }; }; /* Handles non-empty HTML content */ PasteCleanup.prototype.handleNonEmptyHtmlValue = function (e, value, args) { var _this = this; var processedValue = value; this.parent.trigger(events.beforePasteCleanup, { value: processedValue }); this.parent.formatter.editorManager.observer.notify(EVENTS.MS_WORD_CLEANUP, { args: e.args, text: e.text, allowedStylePropertiesArray: this.parent.pasteCleanupSettings.allowedStyleProps, callBack: function (a, cropImageData, pasteTableSource) { args.pasteTableSource = pasteTableSource; processedValue = a.trim(); _this.cropImageData = cropImageData; } }); return processedValue; }; /* Prepares and inserts content into the editor */ PasteCleanup.prototype.prepareAndInsertContent = function (e, value, args) { this.contentModule = this.renderFactory.getRenderer(RenderType.Content); var currentDocument = this.contentModule.getDocument(); var range = this.nodeSelectionObj.getRange(currentDocument); this.saveSelection = this.nodeSelectionObj.save(range, currentDocument); // Process content through temp div var tempDivElem = this.parent.createElement('div'); tempDivElem.innerHTML = value; // Handle unsupported images this.processUnsupportedImages(tempDivElem); var processedValue = tempDivElem.innerHTML; // Check if content is not empty var isValueNotEmpty = tempDivElem.textContent !== '' || !isNOU(tempDivElem.querySelector('img')) || !isNOU(tempDivElem.querySelector('table')); // Clean up resize elements var finalValue = processedValue; this.parent.notify(events.cleanupResizeElements, { value: processedValue, callBack: function (currentValue) { finalValue = currentValue; } }); // Removes the \n from the value finalValue = cleanHTMLString(finalValue, this.parent.element); // Handle paste based on settings this.handlePasteBasedOnSettings(e, finalValue, args, isValueNotEmpty); }; /* Processes unsupported images in pasted content */ PasteCleanup.prototype.processUnsupportedImages = function (tempDivElem) { var unsupportedImg = tempDivElem.querySelectorAll('.e-rte-image-unsupported'); for (var index = 0; index < unsupportedImg.length; index++) { unsupportedImg[index].setAttribute('alt', this.i10n.getConstant('unsupportedImage')); unsupportedImg[index].classList.remove('e-rte-image-unsupported'); } }; /* Handles paste based on editor settings */ PasteCleanup.prototype.handlePasteBasedOnSettings = function (e, value, args, isValueNotEmpty) { if (this.parent.pasteCleanupSettings.prompt && !e.isPlainPaste) { if (isValueNotEmpty) { e.args.preventDefault(); this.pasteDialog(value, args); } } else if (this.parent.pasteCleanupSettings.plainText) { e.args.preventDefault(); this.plainFormatting(value, args); } else if (this.parent.pasteCleanupSettings.keepFormat || e.isPlainPaste) { e.args.preventDefault(); this.formatting(value, false, args); } else { e.args.preventDefault(); this.formatting(value, true, args); } }; /* Processes and uploads images from pasted content */ PasteCleanup.prototype.imgUploading = function (elm) { var allImgElm = elm.querySelectorAll('.pasteContent_Img'); // Handle image upload if save URL is configured and images exist if (this.parent.insertImageSettings.saveUrl && allImgElm.length > 0) { this.processImagesWithSaveUrl(allImgElm); } else if (this.parent.insertImageSettings.saveFormat === 'Blob') { // Convert to blob format if specified this.pasteObj.getBlob(allImgElm); } // Clean up temporary CSS classes from images this.cleanupImageClasses(elm, allImgElm); }; /* Processes images when save URL is configured */ PasteCleanup.prototype.processImagesWithSaveUrl = function (allImgElm) { var base64Src = []; var imgName = []; var uploadImg = []; // Collect base64 images for (var i = 0; i < allImgElm.length; i++) { var imgSrc = allImgElm[i].getAttribute('src'); if (!isNOU(imgSrc) && imgSrc.split(',')[0].indexOf('base64') >= 0) { base64Src.push(imgSrc); imgName.push(getUniqueID('rte_image')); uploadImg.push(allImgElm[i]); } } // Convert base64 to files var fileList = []; for (var i = 0; i < base64Src.length; i++) { fileList.push(this.pasteObj.base64ToFile(base64Src[i], imgName[i])); } // Upload each file for (var i = 0; i < fileList.length; i++) { this.uploadMethod(fileList[i], uploadImg[i]); } // Convert to blob if needed if (isNOU(this.parent.insertImageSettings.path) && this.parent.insertImageSettings.saveFormat === 'Blob') { this.pasteObj.getBlob(allImgElm); } }; /* Removes temporary CSS classes from processed images */ PasteCleanup.prototype.cleanupImageClasses = function (elm, allImgElm) { var allImgElmId = elm.querySelectorAll('.pasteContent_Img'); for (var i = 0; i < allImgElmId.length; i++) { allImgElmId[i].classList.remove('pasteContent_Img'); // Remove class attribute if empty if (allImgElmId[i].getAttribute('class').trim() === '') { allImgElm[i].removeAttribute('class'); } } }; /* Enables or disables the toolbar based on state */ PasteCleanup.prototype.toolbarEnableDisable = function (state) { if (!this.parent.inlineMode.enable) { this.parent.toolbarModule.baseToolbar.toolbarObj.disable(state); } }; /* Handles the upload process for an image file */ PasteCleanup.prototype.uploadMethod = function (file, imgElem) { this.pasteObj.setImageOpacity(imgElem); var popupEle = this.pasteObj.createPopupElement(); this.createPopupObject(popupEle, imgElem, file); this.createUploader(imgElem); this.initializeUpload(file); this.hideFileSelectWrapper(); }; /* Creates and configures the popup object */ PasteCleanup.prototype.createPopupObject = function (popupEle, imgElem, file) { var _this = this; var contentEle = this.parent.createElement('input', { id: this.parent.getID() + '_upload', attrs: { type: 'File', name: 'UploadFiles' } }); this.popupObj = new Popup(popupEle, { relateTo: imgElem, height: '85px', width: '300px', content: contentEle, viewPortElement: this.parent.inputElement, enableRtl: this.parent.enableRtl, zIndex: 10001, close: function (event) { _this.parent.isBlur = false; _this.popupObj.destroy(); detach(_this.popupObj.element); } }); this.configurePopupStyles(); this.schedulePopupRefresh(imgElem, file); }; /* Configures popup styles and CSS classes */ PasteCleanup.prototype.configurePopupStyles = function () { this.popupObj.element.style.display = 'none'; addClass([this.popupObj.element], [classes.CLS_POPUP_OPEN, classes.CLS_RTE_UPLOAD_POPUP]); if (!isNOU(this.parent.cssClass)) { addClass([this.popupObj.element], this.parent.cssClass.replace(/\s+/g, ' ').trim().split(' ')); } }; /* Schedules popup refresh based on file size */ PasteCleanup.prototype.schedulePopupRefresh = function (imgElem, file) { var _this = this; var timeOut = file.size > 1000000 ? 300 : 100; this.refreshPopupTime = setTimeout(function () { _this.refreshPopup(imgElem, _this.popupObj); }, timeOut); }; /* Creates and configures the uploader component */ PasteCleanup.prototype.createUploader = function (imgElem) { var _this = this; this.uploadObj = new Uploader({ asyncSettings: { saveUrl: this.parent.insertImageSettings.saveUrl, removeUrl: this.parent.insertImageSettings.removeUrl }, cssClass: classes.CLS_RTE_DIALOG_UPLOAD, dropArea: this.parent.inputElement, allowedExtensions: this.parent.insertImageSettings.allowedTypes.toString(), success: function (e) { _this.popupClose(_this.popupObj, _this.uploadObj, imgElem, e); }, uploading: function (e) { return _this.handleUploading(e, imgElem); }, beforeUpload: function (args) { return _this.handleBeforeUpload(args); }, failure: function (e) { return _this.handleFailure(e, imgElem); }, canceling: function () { return _this.handleCanceling(imgElem); }, selected: function (e) { e.cancel = true; }, removing: function () { return _this.handleRemoving(imgElem); } }); this.uploadObj.appendTo(this.popupObj.element.childNodes[0]); }; /* Handles the uploading event */ PasteCleanup.prototype.handleUploading = function (e, imgElem) { var _this = this; if (!this.parent.isServerRendered) { this.parent.trigger(events.imageUploading, e, function (imageUploadingArgs) { if (imageUploadingArgs.cancel) { if (!isNullOrUndefined(imgElem)) { detach(imgElem); } if (!isNullOrUndefined(_this.popupObj.element)) { detach(_this.popupObj.element); } } else { _this.parent.inputElement.contentEditable = 'false'; } }); } }; /* Handles the beforeUpload event */ PasteCleanup.prototype.handleBeforeUpload = function (args) { this.parent.trigger(events.beforeImageUpload, args); this.toolbarEnableDisable(true); }; /* Handles upload failure */ PasteCleanup.prototype.handleFailure = function (e, imgElem) { var _this = this; this.failureTime = setTimeout(function () { _this.uploadFailure(imgElem, _this.uploadObj, _this.popupObj, e); }, 900); }; /* Handles upload cancellation */ PasteCleanup.prototype.handleCanceling = function (imgElem) { this.parent.inputElement.contentEditable = 'true'; if (imgElem.nextSibling && imgElem.nextSibling.textContent === ' ') { detach(imgElem.nextSibling); } detach(imgElem); this.popupObj.close(); }; /* Handles file removal */ PasteCleanup.prototype.handleRemoving = function (imgElem) { this.parent.inputElement.contentEditable = 'true'; if (imgElem.nextSibling && imgElem.nextSibling.textContent === ' ') { detach(imgElem.nextSibling); } detach(imgElem); this.popupObj.close(); }; /* Initializes the upload process */ PasteCleanup.prototype.initializeUpload = function (file) { var fileInfo = [{ name: file.name, rawFile: file, size: file.size, type: file.type, status: 'Ready to Upload', validationMessages: { minSize: '', maxSize: '' }, statusCode: '1' }]; this.uploadObj.createFileList(fileInfo); this.uploadObj.upload(fileInfo); }; /* Hides the file select wrapper */ PasteCleanup.prototype.hideFileSelectWrapper = function () { var fileSelectWrap = this.popupObj.element.getElementsByClassName('e-file-select-wrap')[0]; if (fileSelectWrap) { fileSelectWrap.style.display = 'none'; } var dialogUploadWrap = this.popupObj.element.querySelector('.e-rte-dialog-upload .e-file-select-wrap'); if (dialogUploadWrap) { detach(dialogUploadWrap); } }; /* Handles image upload failure */ PasteCleanup.prototype.uploadFailure = function (imgElem, uploadObj, popupObj, e) { if (this.parent && this.parent.isDestroyed) { return; } // Re-enable content editing this.parent.inputElement.contentEditable = 'true'; detach(imgElem); // Destroy popup if it exists this.cleanupPopup(popupObj); this.parent.trigger(events.imageUploadFailed, e); // Destroy uploader if it exists this.cleanupUploader(uploadObj); }; /* Cleans up popup object */ PasteCleanup.prototype.cleanupPopup = function (popupObj) { if (popupObj) { this.parent.isBlur = false; popupObj.destroy(); if (!isNullOrUndefined(popupObj.element)) { detach(popupObj.element); } } }; /* Cleans up uploader object */ PasteCleanup.prototype.cleanupUploader = function (uploadObj) { if (uploadObj && document.body.contains(uploadObj.element)) { uploadObj.destroy(); } }; /* Handles popup close after successful upload */ PasteCleanup.prototype.popupClose = function (popupObj, uploadObj, imgElem, e) { this.parent.inputElement.contentEditable = 'true'; this.prepareEventArgs(e, imgElem); this.handleUploadStatus(e, imgElem); this.scheduleCleanup(popupObj, uploadObj, imgElem); }; /* Prepares event arguments for upload events */ PasteCleanup.prototype.prepareEventArgs = function (e, imgElem) { e.element = imgElem; e.detectImageSource = ImageInputSource.Pasted; }; /* Handles different upload status codes */ PasteCleanup.prototype.handleUploadStatus = function (e, imgElem) { var element = e.file; if (element.statusCode === '2') { this.handleSuccessfulUpload(e, imgElem); } else if (element.statusCode === '5') { this.handleImageRemoval(e); } }; /* Handles successful image upload */ PasteCleanup.prototype.handleSuccessfulUpload = function (e, imgElem) { var _this = this; this.parent.trigger(events.imageUploadSuccess, e, function (e) { if (!isNullOrUndefined(_this.parent.insertImageSettings.path)) { var url = _this.parent.insertImageSettings.path + e.file.name; _this.updateImageSource(imgElem, url, e.file.name); } }); }; /* Updates image source after successful upload */ PasteCleanup.prototype.updateImageSource = function (imgElem, url, altText) { // Handle detached images with ID if (!this.parent.inputElement.contains(imgElem) && imgElem.id) { var imgHtmlElems = this.parent.inputElement.querySelectorAll('#' + imgElem.id); this.updateDetachedImages(imgHtmlElems, url, altText); } else { imgElem.src = url; imgElem.setAttribute('alt', altText); } }; /* Updates detached images by ID */ PasteCleanup.prototype.updateDetachedImages = function (imgHtmlElems, url, altText) { for (var i = 0; i < imgHtmlElems.length; i++) { var imgHtmlElem = imgHtmlElems[i]; if (imgHtmlElem && imgHtmlElem.style && imgHtmlElem.style.opacity === '0.5') { imgHtmlElem.src = url; imgHtmlElem.setAttribute('alt', altText); } } }; /* Handles image removal */ PasteCleanup.prototype.handleImageRemoval = function (e) { this.parent.trigger(events.imageRemoving, e, function (e) { if (!isNullOrUndefined(e.element.src)) { e.element.src = ''; } }); }; /* Schedules cleanup after upload completion */ PasteCleanup.prototype.scheduleCleanup = function (popupObj, uploadObj, imgElem) { var _this = this; this.popupCloseTime = setTimeout(function () { _this.cleanupPopup(popupObj); _this.resetImageOpacity(imgElem); _this.toolbarEnableDisable(false); _this.cleanupUploader(uploadObj); }, 1500); }; /* Resets image opacity after upload */ PasteCleanup.prototype.resetImageOpacity = function (imgElem) { if (!this.parent.inputElement.contains(imgElem) && (imgElem.id || imgElem.alt)) { this.resetDetachedImageOpacity(imgElem); } else { imgElem.style.opacity = '1'; } }; /* Resets opacity for detached images */ PasteCleanup.prototype.resetDetachedImageOpacity = function (imgElem) { var selector = imgElem.id ? "#" + imgElem.id : "[alt=\"" + imgElem.alt + "\"]"; if (selector) { var imgHtmlElems = this.parent.inputElement.querySelectorAll(selector); for (var i = 0; i < imgHtmlElems.length; i++) { var imgHtmlElem = imgHtmlElems[i]; if (imgHtmlElem && imgHtmlElem.style && imgHtmlElem.style.opacity === '0.5') { imgHtmlElem.style.opacity = '1'; } } } }; /* Refreshes popup position relative to image */ PasteCleanup.prototype.refreshPopup = function (imageElement, popupObj) { var imgPosition = this.calculateImagePosition(imageElement); var rtePosition = this.parent.element.offsetTop + this.parent.element.offsetHeight; if (imgPosition > rtePosition) { this.positionPopupAtTop(popupObj); } else { this.positionPopupAtImage(popupObj, imageElement); } }; /* Calculates image position considering iframe */ PasteCleanup.prototype.calculateImagePosition = function (imageElement) { return this.parent.iframeSettings.enable ? this.parent.element.offsetTop + imageElement.offsetTop : imageElement.offsetTop; }; /* Positions popup at the top of editor */ PasteCleanup.prototype.positionPopupAtTop = function (popupObj) { popupObj.offsetY = this.parent.iframeSettings.enable ? -30 : -65; popupObj.element.style.display = 'block'; }; /* Positions popup relative to image */ PasteCleanup.prototype.positionPopupAtImage = function (popupObj, imageElement) { if (popupObj) { popupObj.refreshPosition(imageElement); popupObj.element.style.display = 'block'; } }; /* Method for image formatting when pasting */ PasteCleanup.prototype.imageFormatting = function (pasteArgs, imgElement) { var imgElem = imgElement.elements[0]; if (!isNOU(imgElem.getAttribute('src'))) { imgElem.classList.add('pasteContent_Img'); } var imageElement = this.parent.createElement('span'); imageElement.appendChild(imgElem); var imageValue = imageElement.innerHTML; this.contentModule = this.renderFactory.getRenderer(RenderType.Content); var currentDocument = this.contentModule.getDocument(); var range = this.nodeSelectionObj.getRange(currentDocument); this.saveSelection = this.nodeSelectionObj.save(range, currentDocument); var settings = this.parent.pasteCleanupSettings; if (settings.prompt) { this.pasteDialog(imageValue, pasteArgs); } else if (settings.plainText) { this.plainFormatting(imageValue, pasteArgs); } else if (settings.keepFormat) { this.formatting(imageValue, false, pasteArgs); } else { this.formatting(imageValue, true, pasteArgs); } }; /* Renders radio buttons for paste formatting options. */ PasteCleanup.prototype.radioRender = function () { this.keepRadioButton = new RadioButton({ label: this.i10n.getConstant('keepFormat'), name: 'pasteOption', checked: true }); this.keepRadioButton.isStringTemplate = true; var keepFormatElement = this.parent.element.querySelector('#keepFormating'); this.keepRadioButton.appendTo(keepFormatElement); this.cleanRadioButton = new RadioButton({ label: this.i10n.getConstant('cleanFormat'), name: 'pasteOption' }); this.cleanRadioButton.isStringTemplate = true; var cleanFormatElement = this.parent.element.querySelector('#cleanFormat'); this.cleanRadioButton.appendTo(cleanFormatElement); this.plainTextRadioButton = new RadioButton({ label: this.i10n.getConstant('plainText'), name: 'pasteOption' }); this.plainTextRadioButton.isStringTemplate = true; var plainTextElement = this.parent.element.querySelector('#plainTextFormat'); this.plainTextRadioButton.appendTo(plainTextElement); }; PasteCleanup.prototype.selectFormatting = function (value, args, keepChecked, cleanChecked) { if (keepChecked) { this.formatting(value, false, args); } else if (cleanChecked) { this.formatting(value, true, args); } else { this.plainFormatting(value, args); } }; /* Shows the dialog for paste formatting options. */ PasteCleanup.prototype.pasteDialog = function (value, args) { this.dialogObj = this.dialogRenderObj.render(this.getDialogModel(value, args)); var rteDialogWrapper = this.parent.element.querySelector('#' + this.parent.getID() + '_pasteCleanupDialog'); if (rteDialogWrapper && rteDialogWrapper.innerHTML !== '') { this.destroyDialog(rteDialogWrapper); } if (!rteDialogWrapper) { rteDialogWrapper = this.parent.createElement('div', { id: this.parent.getID() + '_pasteCleanupDialog' }); this.parent.rootContainer.appendChild(rteDialogWrapper); } this.dialogObj.appendTo(rteDialogWrapper); this.radioRender(); this.dialogObj.show(); this.setCssClass({ cssClass: this.parent.getCssClass() }); }; /* Returns the dialog model configuration. */ PasteCleanup.prototype.getDialogModel = function (value, args) { return { buttons: [ this.getOkButton(value, args), this.getCancelButton() ], header: this.i10n.getConstant('pasteFormat'), content: this.getDialogContent(), target: this.parent.element, width: '300px', height: '265px', cssClass: CLS_RTE_DIALOG_MIN_HEIGHT, isModal: Browser.isDevice, visible: false }; }; /* Creates the OK button configuration for the dialog. */ PasteCleanup.prototype.getOkButton = function (value, args) { var _this = this; return { click: function () { if (!_this.dialogObj.isDestroyed) { var keepChecked = !!_this.parent.element.querySelector('#keepFormating').checked; var cleanChecked = !!_this.parent.element.querySelector('#cleanFormat').checked; _this.closeDialog(); _this.selectFormatting(value, args, keepChecked, cleanChecked); } }, buttonModel: { isPrimary: true, cssClass: 'e-flat ' + CLS_RTE_PASTE_OK, content: this.i10n.getConstant('pasteDialogOk') } }; }; /* Creates the Cancel button configuration for the dialog. */ PasteCleanup.prototype.getCancelButton = function () { var _this = this; return { click: function () { if (!_this.dialogObj.isDestroyed) { _this.closeDialog(); } }, buttonModel: { cssClass: 'e-flat ' + CLS_RTE_PASTE_CANCEL, content: this.i10n.getConstant('pasteDialogCancel') } }; }; /* Closes and destroys the dialog. */ PasteCleanup.prototype.closeDialog = function () { this.dialogObj.hide(); var dialogRef = this.dialogObj; this.dialogRenderObj.close(dialogRef); this.dialogObj.destroy(); }; /* Returns the HTML content for the dialog. */ PasteCleanup.prototype.getDialogContent = function () { return this.i10n.getConstant('pasteFormatContent') + "<br/><div>\n <div class=\"e-rte-radio-keep-format\">\n <input type=\"radio\" class=\"" + CLS_RTE_PASTE_KEEP_FORMAT + "\" id=\"keepFormating\"/>\n </div>\n <div class=\"e-rte-radio-remove-format\">\n <input type=\"radio\" class=\"" + CLS_RTE_PASTE_REMOVE_FORMAT + "\" id=\"cleanFormat\"/>\n </div>\n <div class=\"e-rte-radio-plain-format\">\n <input type=\"radio\" class=\"" + CLS_RTE_PASTE_PLAIN_FORMAT + "\" id=\"plainTextFormat\"/>\n </div>\n </div>"; }; /* Updates CSS classes on a component. */ PasteCleanup.prototype.updateCss = function (currentObj, e) { if (currentObj && e.cssClass) { if (isNullOrUndefined(e.oldCssClass)) { currentObj.setProperties({ cssClass: (currentObj.cssClass + ' ' + e.cssClass).trim() }); } else { currentObj.setProperties({ cssClass: (currentObj.cssClass.replace(e.oldCssClass, '').trim() + ' ' + e.cssClass).trim() }); } } }; // eslint-disable-next-line @typescript-eslint/tslint/config PasteCleanup.prototype.setCssClass = function (e) { if (this.popupObj && e.cssClass) { if (e.oldCssClass) { removeClass([this.popupObj.element], e.oldCssClass); } addClass([this.popupObj.element], e.cssClass); } this.updateCss(this.dialogObj, e); this.updateCss(this.uploadObj, e); this.updateCss(this.plainTextRadioButton, e); this.updateCss(this.cleanRadioButton, e); this.updateCss(this.keepRadioButton, e); }; /* Removes the dialog container and its child elements. */ PasteCleanup.prototype.destroyDialog = function (rteDialogWrapper) { var rteDialogContainer = this.parent.element.querySelector('.e-rte-dialog-minheight'); if (rteDialogContainer) { detach(rteDialogContainer); } while (rteDialogWrapper.firstChild) { detach(rteDialogWrapper.firstChild); } }; /* Handles document click to hide dialog if clicked outside. */ PasteCleanup.prototype.docClick = function (e) { var target = e.args.target; if (target && target.classList && this.dialogObj && !closest(target, "[id='" + this.dialogObj.element.id + "']") && !target.classList.contains('e-toolbar-item')) { this.dialogObj.hide(); } }; /* Processes and sanitizes pasted content based on cleanup settings, image handling, sanitization, and custom callbacks. */ PasteCleanup.prototype.formatting = function (value, clean, args) { var clipBoardElem = this.parent.createElement('div', { className: 'pasteContent', styles: 'display:inline;' }); // If plain text paste contains HTML, split lines appropriately if (this.isNotFromHtml && this.containsHtml) { value = this.pasteObj.splitBreakLine(value); } clipBoardElem.innerHTML = value; clipBoardElem = this.pasteObj.cleanAppleClass(clipBoardElem); // Remove denied tags and attributes as per settings clipBoardElem = this.pasteObj.cleanupDeniedTagsAndAttributes(clipBoardElem, clean); // Restrict allowed CSS style properties if configured if (this.parent.pasteCleanupSettings.allowedStyleProps !== null) { clipBoardElem = this.pasteObj.allowedStyle(clipBoardElem); } this.saveSelection.restore(); var newText = clipBoardElem.innerHTML; if (this.parent.enableHtmlSanitizer) { // Sanitize innerHTML and ensure ampersands newText = clipBoardElem.innerHTML.split('&').join('&amp;'); } clipBoardElem.innerHTML = sanitizeHelper(newText, this.parent); this.pasteObj.setImageClassAndProps(clipBoardElem); // Handle pasted <picture> elements if present if (this.pasteObj.hasPictureElement(clipBoardElem)) { this.pasteObj.processPictureElement(clipBoardElem); } // Process paste content, image conversion & emit callbacks if present if (this.pasteObj.hasContentToPaste(clipBoardElem)) { this.handlePastedImagesAndEvents(clipBoardElem, value, args); } }; PasteCleanup.prototype.handlePastedImagesAndEvents = function (clipBoardElem, value, args) { // Based on the information in your clipboard, the function below is refactored for better readability. var tempWrapperElem = this.parent.createElement('div'); tempWrapperElem.innerHTML = value; var filesData = this.pasteObj.collectBase64ImageFiles(tempWrapperElem); this.parent.trigger(events.afterPasteCleanup, { value: clipBoardElem.innerHTML, filesData: filesData }, function (updatedArgs) { value = updatedArgs.value; }); clipBoardElem.innerHTML = this.parent.addAnchorAriaLabel(value); clipBoardElem = this.pasteObj.addTableClass(clipBoardElem, args.pasteTableSource); this.execPasteCommand(clipBoardElem, args); this.parent.notify(events.autoResize, {}); scrollToCursor(this.parent.contentModule.getDocument(), this.parent.inputElement); this.pasteObj.removeTempClass(); this.parent.notify(events.toolbarRefresh, {}); this.pasteObj.cropImageHandler(this.parent.inputElement); }; PasteCleanup.prototype.execPasteCommand = function (clipBoardElem, args) { var _this = this; this.parent.formatter.editorManager.execCommand('inserthtml', 'pasteCleanup', args, function (returnArgs) { extend(args, { elements: returnArgs.elements, imageElements: returnArgs.imgElem }, true); _this.parent.formatter.onSuccess(_this.parent, args); }, clipBoardElem, null, null, this.parent.enterKey); }; PasteCleanup.prototype.getCropImageData = function () { return this.cropImageData; }; /* Handles the pasting of plain text content into the rich text editor. */ PasteCleanup.prototype.plainFormatting = function (value, args) { var _this = this; var clipBoardElem = this.parent.createElement('div', { className: 'pasteContent' }); this.plainTextContent = this.plainTextContent.replace(/</g, '&lt;'); this.plainTextContent = this.plainTextContent.replace(/>/g, '&gt;'); var sanitizedContent = sanitizeHelper(this.plainTextContent, this.parent); var splitContent = this.pasteObj.splitBreakLine(sanitizedContent); clipBoardElem.innerHTML = splitContent; if (clipBoardElem.textContent.trim() !== '') { this.saveSelection.restore(); this.parent.trigger(events.afterPasteCleanup, { value: clipBoardElem.innerHTML, filesData: null }, function (updatedArgs) { value = updatedArgs.value; }); clipBoardElem.innerHTML = value; this.parent.formatter.editorManager.execCommand('inserthtml', 'pasteCleanup', args, function (returnArgs) { extend(args, { elements: returnArgs.elements, imageElements: returnArgs.imgElem }, true); _this.parent.formatter.onSuccess(_this.parent, args); }, clipBoardElem, null, null, this.parent.enterKey); this.pasteObj.removeTempClass(); } else { this.saveSelection.restore(); extend(args, { elements: [] }, true); this.parent.formatter.onSuccess(this.parent, args); } }; /** * For internal use only - Get the module name. * * @returns {void} * @hidden */ PasteCleanup.prototype.getModuleName = function () { return 'pasteCleanup'; }; return PasteCleanup; }()); export { PasteCleanup };