@syncfusion/ej2-richtexteditor
Version:
Essential JS 2 RichTextEditor component
979 lines (978 loc) • 45.4 kB
JavaScript
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, '<');
processedValue = processedValue.replace(/>/g, '>');
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('&');
}
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, '<');
this.plainTextContent = this.plainTextContent.replace(/>/g, '>');
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 };