UNPKG

@syncfusion/ej2-richtexteditor

Version:
1,074 lines 65.6 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, getComponent, createElement } 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, getStructuredHtml, scrollToCursor, isBlockNode } from '../../common/util'; import { PasteCleanupAction } from '../../editor-manager/plugin/paste-clean-up-action'; import { PASTE_SOURCE } from './../../editor-manager/base'; /** * 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.pendingPasteQueue = []; this.validFiles = []; 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.failureTime) { clearTimeout(this.failureTime); this.failureTime = null; } this.removeEventListener(); var mediaPopups = this.parent.element.querySelectorAll('.e-rte-upload-popup'); for (var i = 0; i < mediaPopups.length; i++) { var uploader = this.getUploaderInstance(mediaPopups[i]); if (uploader && !uploader.isDestroyed) { uploader.destroy(); } var popup = getComponent(mediaPopups[i], 'popup'); if (popup && !popup.isDestroyed) { popup.destroy(); } } 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 = ''; this.pendingPasteQueue = []; this.validFiles = []; }; 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); }, mediaUpload: function () { _this.mediaUploading(_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; var files = e.args.clipboardData.files; var elm = createElement('p'); elm.innerHTML = value; var source = this.findSource(elm); // Handle empty HTML content (plain text or image) if (source === 'html' && !isNOU(files) && files.length > 0) { var result = this.handleFileDataPaste(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.handleHtmlValuePaste(e, value, args); } else if (value.length === 0) { // handle for the plain text case var result = this.handlePlainTextPaste(e, value, args); processedValue = result.value; shouldContinue = result.shouldContinue; if (!shouldContinue) { return false; } } processedValue = this.handleGoogleDocs(processedValue); // Remove base tags from content if (processedValue !== null && processedValue !== '') { processedValue = processedValue.replace(/<base[^>]*>/g, ''); } this.prepareAndInsertContent(e, processedValue, args); return true; }; // when google docs has b tags wrapped in default need to remove it PasteCleanup.prototype.handleGoogleDocs = function (processedValue) { // check if the content is pasted from google docs if (processedValue && processedValue.indexOf('id="docs-internal-guid') >= 0) { // remove the docs-internal-guid id attribute entirely processedValue = processedValue.replace(/\s*id="docs-internal-guid[^"]*"/gi, ''); // check inside the b tags and do manipulation processedValue = processedValue.replace(/<b\b([^>]*)>([\s\S]*?)<\/b>/gi, function (match, attrs, inner) { // Look inside the inner HTML for a span with font-weight:700 var hasBoldSpan = /<span[^>]*style=["'][^"']*font-weight\s*:\s*700[^"']*["'][^>]*>/i.test(inner); // Look inside the inner HTML for a span with font-style:style var hasItalicSpan = /<span[^>]*style=["'][^"']*font-style\s*:\s*italic[^"']*["'][^>]*>/i.test(inner); if (hasBoldSpan && hasItalicSpan) { // Wrap with both <b> and <em> return "<b><em>" + inner + "</em></b>"; } if (hasBoldSpan && !hasItalicSpan) { // keep <b> if the nested span is bold but not italic return "<b>" + inner + "</b>"; } if (hasItalicSpan) { // convert to <em> if the nested span is italic return "<em>" + inner + "</em>"; } // otherwise unwrap <b> return inner; }); } return processedValue; }; PasteCleanup.prototype.findSource = function (element) { var metaNodes = element.querySelectorAll('meta'); for (var i = 0; i < metaNodes.length; i++) { var metaNode = metaNodes[i]; var content = metaNode.getAttribute('content'); var name_1 = metaNode.getAttribute('name'); if (name_1 && name_1.toLowerCase().indexOf('generator') >= 0 && content && content.toLowerCase().indexOf('microsoft') >= 0) { for (var j = 0; j < PASTE_SOURCE.length; j++) { if (content.toLowerCase().indexOf(PASTE_SOURCE[j]) >= 0) { return PASTE_SOURCE[j]; } } } } // Fallback: Check for Excel-specific patterns in HTML structure if (this.isExcelContent(element) && (this.parent.userAgentData.getBrowser() === 'Safari')) { return 'excel'; } return 'html'; }; /* Detects Excel content by checking for Excel-specific HTML patterns */ PasteCleanup.prototype.isExcelContent = function (element) { // Check for Excel-specific table attributes and styles var tables = element.querySelectorAll('table'); if (tables.length > 0) { var table = tables[0]; // Check for Excel-specific style patterns var style = (table.getAttribute('style') || '').toLowerCase(); if (style.indexOf('border-collapse') >= 0 || style.indexOf('mso-') >= 0) { return true; } } return false; }; /* Handles plain text paste with event notification and callback */ PasteCleanup.prototype.handlePlainTextPaste = function (e, value, args) { var processedValue = value || e.args.clipboardData.getData('text/plain'); var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/i); // Trigger beforePasteCleanup event this.parent.trigger(events.beforePasteCleanup, { value: processedValue }, function (updatedArgs) { processedValue = updatedArgs.value; }); // Escape HTML characters processedValue = processedValue.replace(/</g, '&lt;'); processedValue = processedValue.replace(/>/g, '&gt;'); // Mark as plain text content this.plainTextContent = processedValue; // Notify paste event with callback for custom handling this.parent.notify(events.paste, { file: null, args: e.args, text: processedValue, callBack: function (b) { if (typeof (b) === 'string') { processedValue = b; } } }); // Format plain text with line breaks 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 case when HTML value is empty but may contain plain text or images */ PasteCleanup.prototype.handleFileDataPaste = 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 }, function (updatedArgs) { processedValue = updatedArgs.value; }); this.isNotFromHtml = processedValue !== '' ? true : false; processedValue = processedValue.replace(/</g, '&lt;'); processedValue = processedValue.replace(/>/g, '&gt;'); this.containsHtml = htmlRegex.test(processedValue); this.plainTextContent = processedValue; // Collect clipboard files var clipboardData = e.args.clipboardData; var clipboardFiles = clipboardData && clipboardData.files && clipboardData.files.length ? Array.from(clipboardData.files) : []; var validImages = []; var validVideos = []; var validAudios = []; var audioAllowedTypes = this.parent.insertAudioSettings && this.parent.insertAudioSettings.allowedTypes ? this.parent.insertAudioSettings.allowedTypes : []; var videoAllowedTypes = this.parent.insertVideoSettings && this.parent.insertVideoSettings.allowedTypes ? this.parent.insertVideoSettings.allowedTypes : []; for (var i = 0; i < clipboardFiles.length; i++) { var file = clipboardFiles[i]; if (!file) { continue; } var mime = (file.type || '').toLowerCase(); // Image handling: validate against image allowed types if (mime.indexOf('image/') === 0) { var imgType = mime.split('/')[1] ? '.' + mime.split('/')[1] : ''; if (this.isInvalidImageType(imgType, allowedTypes)) { continue; } validImages.push(file); continue; } // Video handling: validate against video allowed types if (mime.indexOf('video/') === 0) { var isValidVideo = false; for (var j = 0; j < videoAllowedTypes.length; j++) { if (mime.endsWith(videoAllowedTypes[j].replace('.', ''))) { isValidVideo = true; break; } } if (!isValidVideo) { continue; } validVideos.push(file); continue; } // Audio handling: validate against audio allowed types if (mime.indexOf('audio/') === 0) { var isValidAudio = false; for (var j = 0; j < audioAllowedTypes.length; j++) { if (mime.endsWith(audioAllowedTypes[j].replace('.', ''))) { isValidAudio = true; break; } } if (!isValidAudio) { continue; } validAudios.push(file); } } this.validFiles = validImages; // Process video files if (validVideos.length > 0) { var videoResult = this.processPasteWithMedia('video', validVideos, e, value); if (!videoResult.shouldContinue) { return videoResult; } } // Process audio files if (validAudios.length > 0) { var audioResult = this.processPasteWithMedia('audio', validAudios, e, value); if (!audioResult.shouldContinue) { return audioResult; } } return this.processPasteWithMedia('image', validImages, e, processedValue, args, imageproperties, htmlRegex); }; // Unified handler for pasting image, audio, or video files. PasteCleanup.prototype.processPasteWithMedia = function (mediaType, file, e, value, args, imageproperties, htmlRegex) { var _this = this; // Media-specific configuration var mediaConfig = { image: { event: events.paste, command: 'Images', subCommand: 'Image' }, audio: { event: events.audioPaste, command: 'Audios', subCommand: 'Audio' }, video: { event: events.videoPaste, command: 'Videos', subCommand: 'Video' } }; var config = mediaConfig[mediaType]; // For audio/video: ensure there are files and track them as valid files if (mediaType !== 'image') { var files = file; if (files.length === 0) { return { value: value, shouldContinue: true }; } this.validFiles = files; } var processedValue = value; var mediaproperties = imageproperties; // Choose the correct formatter callback based on media type var formatterCallback = mediaType === 'image' ? this.imageFormatting.bind(this, args) : this.mediaFormatting.bind(this, e.args); // Notify the appropriate paste event to let the respective module insert markup this.parent.notify(config.event, { file: file, args: e.args, text: processedValue, isWordPaste: mediaType !== 'image' ? false : undefined, callBack: function (b) { mediaproperties = b; if (typeof (mediaproperties) === 'object') { _this.parent.formatter.editorManager.execCommand(config.command, config.subCommand, e.args, formatterCallback, 'pasteCleanup', mediaproperties, 'pasteCleanupModule'); } else { processedValue = mediaproperties; } } }); // Image flow: continue processing, format plain text with line breaks if not HTML if (mediaType === 'image') { if (htmlRegex && !htmlRegex.test(processedValue)) { var divElement = this.parent.createElement('div'); divElement.innerHTML = this.pasteObj.splitBreakLine(processedValue); processedValue = divElement.innerHTML; } return { value: processedValue, shouldContinue: true }; } // Audio/Video flow: stop further processing return { value: '', shouldContinue: false }; }; /* Processes and uploads audios from pasted content */ /* Processes and uploads audios from pasted content with base64 sources */ PasteCleanup.prototype.processMediassWithSaveUrl = function (allMediaElm) { var base64Src = []; var mediaName = []; var uploadMedia = []; // Collect base64 audios for (var i = 0; i < allMediaElm.length; i++) { var audioSrc = allMediaElm[i].querySelector('source').src; if (!isNOU(audioSrc) && audioSrc.split(',')[0].indexOf('base64') >= 0) { base64Src.push(audioSrc); if (allMediaElm[i].classList.contains('pasteContent_Audio')) { mediaName.push(getUniqueID('rte_audio')); } if (allMediaElm[i].classList.contains('pasteContent_Video')) { mediaName.push(getUniqueID('rte_video')); } uploadMedia.push(allMediaElm[i]); } } // Convert base64 to files and upload var fileList = []; for (var i = 0; i < base64Src.length; i++) { fileList.push(this.pasteObj.base64ToFile(base64Src[i], mediaName[i])); } // Upload each file with opacity effect for (var i = 0; i < fileList.length; i++) { var audioSrc = allMediaElm[i].querySelector('source'); this.uploadMethod(fileList[i], audioSrc); } }; PasteCleanup.prototype.mediaFormatting = function (pasteArgs, audioElement) { var mediaELm = audioElement.elements[0]; var mediaValueValue; if (mediaELm.tagName === 'AUDIO') { var audioWrapper = createElement('span', { className: classes.CLS_AUDIOWRAP, attrs: { contentEditable: 'false' } }); var audElement = createElement('span', { className: classes.CLS_CLICKELEM }); audioWrapper.appendChild(audElement); audElement.appendChild(mediaELm); if (!isNOU(mediaELm.querySelector('source'))) { audioWrapper.classList.add('pasteContent_Audio'); } mediaValueValue = audioWrapper.outerHTML; } if (mediaELm.tagName === 'VIDEO') { var videoWrapper = createElement('span', { className: classes.CLS_VIDEOWRAP, attrs: { contentEditable: 'false' } }); var vidElement = createElement('span', { className: classes.CLS_CLICKELEM }); videoWrapper.appendChild(vidElement); vidElement.appendChild(mediaELm); if (!isNOU(mediaELm.querySelector('source'))) { videoWrapper.classList.add('pasteContent_Video'); } mediaValueValue = videoWrapper.outerHTML; } 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; // Queue audio similar to image flow so batch insertion and imgUploading() detection works this.pendingPasteQueue.push(mediaValueValue); var isAudioQueueCompleted = (this.validFiles.length === 0) || (this.pendingPasteQueue.length === this.validFiles.length); if (!isAudioQueueCompleted) { return; } var combinedHtml = this.pendingPasteQueue.join(''); if (settings.prompt) { this.pasteDialog(combinedHtml, pasteArgs); return; } else if (settings.plainText) { this.plainFormatting(combinedHtml, pasteArgs); this.pendingPasteQueue = []; } else if (settings.keepFormat) { this.formatting(combinedHtml, false, pasteArgs); this.pendingPasteQueue = []; } else { this.formatting(combinedHtml, true, pasteArgs); this.pendingPasteQueue = []; } }; /* Checks if image type is not in allowed types */ PasteCleanup.prototype.isInvalidImageType = function (imgType, allowedTypes) { return allowedTypes.every(function (type) { return type.toLowerCase() !== imgType; }); }; /* Handles non-empty HTML content */ PasteCleanup.prototype.handleHtmlValuePaste = function (e, value, args) { var _this = this; var processedValue = value; this.parent.trigger(events.beforePasteCleanup, { value: processedValue }, function (updatedArgs) { processedValue = updatedArgs.value; }); this.parent.formatter.editorManager.observer.notify(EVENTS.MS_WORD_CLEANUP, { args: e.args, text: processedValue, 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); finalValue = getStructuredHtml(finalValue, this.parent.enterKey, this.parent.enableHtmlEncode, true); finalValue = this.handleBlockEleInsideInlineEle(finalValue); // Handle paste based on settings this.handlePasteBasedOnSettings(e, finalValue, args, isValueNotEmpty); }; /* When copying and pasting content which has block element inside inline elements */ PasteCleanup.prototype.handleBlockEleInsideInlineEle = function (innerValue) { // Create a safe wrapper element for HTML manipulation var tempDiv = document.createElement('div'); tempDiv.innerHTML = innerValue; var nodes = Array.from(tempDiv.childNodes); var isBlockInsideInlineEle = false; for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) { var child = nodes_1[_i]; if (child.nodeType === Node.ELEMENT_NODE && !isBlockNode(child)) { var childElement = Array.from(child.childNodes); for (var _a = 0, childElement_1 = childElement; _a < childElement_1.length; _a++) { var childrens = childElement_1[_a]; if (isBlockNode(childrens)) { isBlockInsideInlineEle = true; } else { isBlockInsideInlineEle = false; } } } } if (isBlockInsideInlineEle) { for (var _b = 0, nodes_2 = nodes; _b < nodes_2.length; _b++) { var child = nodes_2[_b]; if (child.nodeType === Node.ELEMENT_NODE) { var childElement = child; while (childElement.firstChild) { childElement.parentNode.insertBefore(childElement.firstChild, childElement); } childElement.remove(); return tempDiv.innerHTML; } } } return tempDiv.innerHTML; }; /* 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 && this.parent.insertImageSettings.saveUrl && allImgElm.length > 0) { this.processImagesWithSaveUrl(allImgElm); } else if (this.parent.insertImageSettings && this.parent.insertImageSettings.saveFormat === 'Blob') { // Convert to blob format if specified this.pasteObj.getBlob(allImgElm); } // Clean up temporary CSS classes from images and audios this.cleanupMediaClasses(elm, allImgElm); }; /* Processes and uploads audio and video from pasted content */ PasteCleanup.prototype.mediaUploading = function (elm) { var allAudioElm = elm.querySelectorAll('.pasteContent_Audio'); var allVideoElm = elm.querySelectorAll('.pasteContent_Video'); // Handle audio upload if save URL is configured and audios exist if (this.parent.insertAudioSettings && this.parent.insertAudioSettings.saveUrl && allAudioElm.length > 0) { this.processMediassWithSaveUrl(allAudioElm); } else if (this.parent.insertAudioSettings && this.parent.insertAudioSettings.saveFormat === 'Blob') { // Convert to blob format if specified this.pasteObj.getBlob(allAudioElm); } // Handle audio upload if save URL is configured and audios exist if (this.parent.insertVideoSettings && this.parent.insertVideoSettings.saveUrl && allVideoElm.length > 0) { this.processMediassWithSaveUrl(allVideoElm); } else if (this.parent.insertVideoSettings && this.parent.insertVideoSettings.saveFormat === 'Blob') { // Convert to blob format if specified this.pasteObj.getBlob(allVideoElm); } // Clean up temporary CSS classes from videos and audios this.cleanupMediaClasses(elm, allAudioElm); this.cleanupMediaClasses(elm, allVideoElm); }; /* 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.cleanupMediaClasses = function (elm, allImgElm) { var allImgElmId = elm.querySelectorAll('.pasteContent_Img'); var allAudioElmId = elm.querySelectorAll('.pasteContent_Audio'); var allVideoElmId = elm.querySelectorAll('.pasteContent_Video'); 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'); } } for (var i = 0; i < allAudioElmId.length; i++) { allAudioElmId[i].classList.remove('pasteContent_Audio'); if (allAudioElmId[i].getAttribute('class').trim() === '') { allAudioElmId[i].removeAttribute('class'); } } for (var i = 0; i < allVideoElmId.length; i++) { allVideoElmId[i].classList.remove('pasteContent_Video'); if (allVideoElmId[i].getAttribute('class').trim() === '') { allVideoElmId[i].removeAttribute('class'); } } }; /* Enables or disables the toolbar based on state */ PasteCleanup.prototype.toolbarEnableDisable = function (state) { if (!this.parent.inlineMode.enable && this.parent.toolbarModule && this.parent.toolbarModule.baseToolbar) { if (state) { // To disable toolbar items this.parent.toolbarModule.baseToolbar.toolbarObj.disable(state); } else { var mediaPopups = this.parent.element.querySelectorAll('.e-rte-upload-popup'); if (mediaPopups.length === 0) { //To enable Toolbar items this.parent.toolbarModule.baseToolbar.toolbarObj.disable(state); } } } }; /* Handles the upload process for an image file */ PasteCleanup.prototype.uploadMethod = function (file, mediaElem) { this.pasteObj.setImageOpacity(mediaElem); var popupObj; if (mediaElem.tagName === 'SOURCE') { popupObj = this.createPopupObject(mediaElem.parentElement, file); } else { popupObj = this.createPopupObject(mediaElem, file); } this.createUploader(mediaElem, popupObj, file); this.hideFileSelectWrapper(popupObj); }; /* Creates and configures the popup object */ PasteCleanup.prototype.createPopupObject = function (imgElem, file) { var popupEle = this.pasteObj.createPopupElement(); popupEle.setAttribute('id', getUniqueID(this.parent.getID() + '_upload_popup')); var boundObj = { popupRoot: popupEle, self: this }; var contentEle = this.parent.createElement('input', { id: getUniqueID(this.parent.getID() + '_upload'), attrs: { type: 'File', name: 'UploadFiles' } }); var popup = new Popup(popupEle, { relateTo: imgElem, height: '85px', width: '300px', content: contentEle, viewPortElement: this.parent.inputElement, enableRtl: this.parent.enableRtl, zIndex: 10001, close: this.onPopupClose.bind(boundObj) }); this.configurePopupStyles(popup); this.schedulePopupRefresh(imgElem, file, popup); return popup; }; /* Handle uploader popup close behavior */ PasteCleanup.prototype.onPopupClose = function () { var currentPopupElem = this.popupRoot; var currentPasteCleanupObj = this.self; if (!isNOU(currentPopupElem) && !isNOU(currentPasteCleanupObj)) { var popupObj = getComponent(currentPopupElem, 'popup'); currentPasteCleanupObj.parent.isBlur = false; if (isNOU(popupObj)) { return; } var uploaderObj = currentPasteCleanupObj.getUploaderInstance(currentPopupElem); if (isNOU(uploaderObj)) { return; } uploaderObj.destroy(); popupObj.destroy(); detach(currentPopupElem); } }; /* To get the uploader object from popup element */ PasteCleanup.prototype.getUploaderInstance = function (element) { var currentUploader = element.querySelector('.e-uploader'); return getComponent(currentUploader, 'uploader'); }; /* Configures popup styles and CSS classes */ PasteCleanup.prototype.configurePopupStyles = function (popup) { if (isNOU(popup) || isNOU(popup.element)) { return; } popup.element.style.display = 'none'; addClass([popup.element], [classes.CLS_POPUP_OPEN, classes.CLS_RTE_UPLOAD_POPUP]); if (!isNOU(this.parent.cssClass)) { addClass([popup.element], this.parent.cssClass.replace(/\s+/g, ' ').trim().split(' ')); } }; /* Schedules popup refresh based on file size */ PasteCleanup.prototype.schedulePopupRefresh = function (imgElem, file, popupObj) { var _this = this; var timeOut = file.size > 1000000 ? 300 : 100; this.refreshPopupTime = setTimeout(function () { _this.refreshPopup(imgElem, popupObj); }, timeOut); }; /* Creates and configures the uploader component */ PasteCleanup.prototype.createUploader = function (imgElem, popup, file) { var _this = this; var uploadObj = new Uploader({ asyncSettings: { saveUrl: this.parent.insertImageSettings.saveUrl, removeUrl: this.parent.insertImageSettings.removeUrl }, cssClass: classes.CLS_RTE_DIALOG_UPLOAD, allowedExtensions: this.parent.insertImageSettings.allowedTypes.toString(), success: function (e) { _this.popupClose(popup, imgElem, e); }, uploading: function (e) { return _this.handleUploading(e, imgElem, popup); }, beforeUpload: function (args) { return _this.handleBeforeUpload(args); }, failure: function (e) { return _this.handleFailure(e, imgElem, popup); }, canceling: function () { return _this.handleCanceling(imgElem, popup); }, selected: function (e) { e.cancel = true; }, removing: function () { return _this.handleRemoving(imgElem, popup); } }); uploadObj.appendTo(popup.element.childNodes[0]); this.initializeUpload(file, uploadObj); return uploadObj; }; /* Handles the uploading event */ PasteCleanup.prototype.handleUploading = function (e, imgElem, popupObj) { if (!this.parent.isServerRendered) { this.parent.trigger(events.imageUploading, e, function (imageUploadingArgs) { if (imageUploadingArgs.cancel) { if (!isNullOrUndefined(imgElem)) { detach(imgElem); } if (!isNullOrUndefined(popupObj.element)) { detach(popupObj.element); } } }); } }; /* 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, popupObj) { var _this = this; this.failureTime = setTimeout(function () { _this.uploadFailure(imgElem, popupObj, e); }, 900); }; /* Handles upload cancellation */ PasteCleanup.prototype.handleCanceling = function (imgElem, popupObj) { if (imgElem.nextSibling && imgElem.nextSibling.textContent === ' ') { detach(imgElem.nextSibling); } detach(imgElem); popupObj.close(); }; /* Handles file removal */ PasteCleanup.prototype.handleRemoving = function (imgElem, popupObj) { if (imgElem.nextSibling && imgElem.nextSibling.textContent === ' ') { detach(imgElem.nextSibling); } detach(imgElem); popupObj.close(); }; /* Initializes the upload process */ PasteCleanup.prototype.initializeUpload = function (file, uploadObj) { var fileInfo = [{ name: file.name, rawFile: file, size: file.size, type: file.type, status: 'Ready to Upload', validationMessages: { minSize: '', maxSize: '' }, statusCode: '1' }]; uploadObj.createFileList(fileInfo); uploadObj.upload(fileInfo); }; /* Hides the file select wrapper */ PasteCleanup.prototype.hideFileSelectWrapper = function (popupObj) { if (isNOU(popupObj) || isNOU(popupObj.element)) { return; } var fileSelectWrap = popupObj.element.getElementsByClassName('e-file-select-wrap')[0]; if (fileSelectWrap) { fileSelectWrap.style.display = 'none'; } }; /* Handles image upload failure */ PasteCleanup.prototype.uploadFailure = function (imgElem, popupObj, e) { if ((this.parent && this.parent.isDestroyed) || isNOU(popupObj)) { return; } detach(imgElem); popupObj.close(); this.parent.trigger(events.imageUploadFailed, e); }; /* Handles popup close after successful upload */ PasteCleanup.prototype.popupClose = function (popupObj, imgElem, e) { this.prepareEventArgs(e, imgElem); this.handleUploadStatus(e, imgElem); this.scheduleCleanup(popupObj, 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 (isNOU(element)) { return; } 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, imgElem) { popupObj.close(); this.resetImageOpacity(imgElem); this.toolbarEnableDisable(false); }; /* 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