@syncfusion/ej2-richtexteditor
Version:
Essential JS 2 RichTextEditor component
893 lines • 146 kB
JavaScript
import * as CONSTANT from './../base/constant';
import { createElement, detach, prepend, append, attributes, Browser, addClass, removeClass } from '@syncfusion/ej2-base';
import { markerClassName } from './dom-node';
import * as EVENTS from './../../common/constant';
import { setStyleAttribute } from '@syncfusion/ej2-base';
import { isIDevice, setEditFrameFocus } from '../../common/util';
import { isNullOrUndefined, isNullOrUndefined as isNOU, closest } from '@syncfusion/ej2-base';
import { InsertHtml } from './inserthtml';
import { DOMMethods } from './dom-tree';
/**
* Lists internal component
*
* @hidden
* @private
*/
var Lists = /** @class */ (function () {
/**
* Constructor for creating the Lists plugin
*
* @param {EditorManager} parent - specifies the parent element
* @hidden
* @private
*/
function Lists(parent) {
this.listTabIndentation = false;
this.parent = parent;
this.domNode = this.parent.domNode;
this.addEventListener();
}
Lists.prototype.addEventListener = function () {
this.parent.observer.on(EVENTS.LIST_TYPE, this.applyListsHandler, this);
this.parent.observer.on(EVENTS.KEY_UP_HANDLER, this.onKeyUp, this);
this.parent.observer.on(EVENTS.KEY_DOWN_HANDLER, this.keyDownHandler, this);
this.parent.observer.on(EVENTS.SPACE_ACTION, this.spaceKeyAction, this);
this.parent.observer.on(EVENTS.INTERNAL_DESTROY, this.destroy, this);
};
Lists.prototype.removeEventListener = function () {
this.parent.observer.off(EVENTS.LIST_TYPE, this.applyListsHandler);
this.parent.observer.off(EVENTS.KEY_UP_HANDLER, this.onKeyUp);
this.parent.observer.off(EVENTS.KEY_DOWN_HANDLER, this.keyDownHandler);
this.parent.observer.off(EVENTS.SPACE_ACTION, this.spaceKeyAction);
this.parent.observer.off(EVENTS.INTERNAL_DESTROY, this.destroy);
};
Lists.prototype.testList = function (elem) {
var olListRegex = [/^[\d]+[.]+$/,
/^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})[.]$/gi,
/^[a-zA-Z][.]+$/];
var elementStart = !isNullOrUndefined(elem) ? elem.innerText.trim().split('.')[0] + '.' : null;
if (!isNullOrUndefined(elementStart)) {
for (var i = 0; i < olListRegex.length; i++) {
if (olListRegex[i].test(elementStart)) {
return true;
}
}
}
return false;
};
Lists.prototype.isOrderedList = function (range) {
var olListStartRegex = [/^[1]+[.]+$/, /^[i]+[.]+$/, /^[a]+[.]+$/];
if (!isNullOrUndefined(range.startContainer.textContent.slice(0, range.startOffset))) {
var editorValue = range.startContainer.textContent.replace(/\u200B/g, '').slice(0, range.startOffset).trim();
for (var i = 0; i < olListStartRegex.length; i++) {
if (olListStartRegex[i].test(editorValue) && editorValue.length === 2) {
return true;
}
}
}
return false;
};
Lists.prototype.isUnOrderedList = function (range) {
var ulListStartRegex = [/^[*]$/, /^[-]$/];
if (!isNullOrUndefined(range.startContainer.textContent.slice(0, range.startOffset))) {
var editorValue = range.startContainer.textContent.replace(/\u200B/g, '').slice(0, range.startOffset).trim();
for (var i = 0; i < ulListStartRegex.length; i++) {
if (ulListStartRegex[i].test(editorValue)) {
return true;
}
}
}
return false;
};
Lists.prototype.isCheckList = function (range) {
// Updated regex to match checkbox patterns with at most one space: [], [x], [ ], [x ], [ x], [ x ]
var ulListStartRegex = [/^\[\s?\]$/, /^\[\s?x\s?\]$/i];
if (!isNullOrUndefined(range.startContainer.textContent.slice(0, range.startOffset))) {
var editorValue = range.startContainer.textContent.replace(/\u200B/g, '').slice(0, range.startOffset).trim();
for (var i = 0; i < ulListStartRegex.length; i++) {
if (ulListStartRegex[i].test(editorValue)) {
return true;
}
}
}
return false;
};
Lists.prototype.createAutoList = function (enterKey, shiftEnterKey) {
var autoListRules = {
BR: { BR: true, P: true, DIV: true },
P: { BR: false, P: true, DIV: true },
DIV: { BR: false, P: true, DIV: true }
};
if (autoListRules[enterKey] && autoListRules[enterKey][shiftEnterKey] !== undefined) {
return autoListRules[enterKey][shiftEnterKey];
}
return false;
};
Lists.prototype.isInsideSameListType = function (startNode, range) {
if (!startNode) {
return false;
}
// Find the closest <li> ancestor of the startNode
var listItem = startNode.closest('li');
if (!listItem) {
return false; // Not inside a list item
}
// Get the parent list element (either <ul> or <ol>)
var parentList = listItem.closest('ul, ol');
if (!parentList) {
return false; // No valid list container found
}
// Check if parentList is OL or UL and compare with startElementOLTest
if (this.isOrderedList(range) && parentList.tagName === 'OL') {
return true;
}
else if (this.isUnOrderedList(range) && parentList.tagName === 'UL' && !parentList.classList.contains('e-rte-checklist')) {
return true;
}
else if (this.isCheckList(range) && parentList.tagName === 'UL' && parentList.classList.contains('e-rte-checklist')) {
return true;
}
else {
return false;
}
};
Lists.prototype.spaceList = function (e) {
var range = this.parent.nodeSelection.getRange(this.parent.currentDocument);
this.saveSelection = this.parent.nodeSelection.save(range, this.parent.currentDocument);
var startNode = this.parent.domNode.getSelectedNode(range.startContainer, range.startOffset);
// eslint-disable-next-line
var endNode = this.parent.domNode.getSelectedNode(range.endContainer, range.endOffset);
var preElement = startNode.previousElementSibling;
var nextElement = startNode.nextElementSibling;
var preElemULStart = !isNullOrUndefined(preElement) ?
preElement.innerText.trim().substring(0, 1) : null;
var nextElemULStart = !isNullOrUndefined(nextElement) ?
nextElement.innerText.trim().substring(0, 1) : null;
var startElementOLTest = this.isOrderedList(range);
var preElementOLTest = this.testList(preElement);
var nextElementOLTest = this.testList(nextElement);
var isInsideSameListType = this.isInsideSameListType(startNode, range);
var nextElementBRTest = range.startContainer.previousElementSibling && range.startContainer.previousElementSibling.tagName === 'BR';
if (!isInsideSameListType && !preElementOLTest && !nextElementOLTest && preElemULStart !== '*' && nextElemULStart !== '*' && (this.createAutoList(e.enterKey, e.shiftEnterKey) || !nextElementBRTest)) {
var brElement = createElement('br');
if (startElementOLTest) {
range.startContainer.textContent = range.startContainer.textContent.slice(range.startOffset, range.startContainer.textContent.length);
if (range.startContainer.nodeName === '#text' && range.startContainer.textContent.length === 0) {
this.parent.domNode.insertAfter(brElement, range.startContainer);
}
this.applyListsHandler({ subCommand: 'OL', callBack: e.callBack, enterAction: e.enterKey });
e.event.preventDefault();
}
else if (range.startContainer.textContent.replace(/\u200B/g, '').slice(0, range.startOffset).trim() === '*' ||
range.startContainer.textContent.replace(/\u200B/g, '').slice(0, range.startOffset).trim() === '-') {
range.startContainer.textContent = range.startContainer.textContent.slice(range.startOffset, range.startContainer.textContent.length);
if (range.startContainer.nodeName === '#text' && range.startContainer.textContent.length === 0) {
this.parent.domNode.insertAfter(brElement, range.startContainer);
}
this.applyListsHandler({ subCommand: 'UL', callBack: e.callBack, enterAction: e.enterKey });
e.event.preventDefault();
}
else if (this.isCheckList(range)) {
var isChecked = /^\[\s*x\s*\]$/i.test(range.startContainer.textContent.replace(/\u200B/g, '').slice(0, range.startOffset).trim());
range.startContainer.textContent = range.startContainer.textContent.slice(range.startOffset, range.startContainer.textContent.length);
if (range.startContainer.nodeName === '#text' && range.startContainer.textContent.length === 0) {
this.parent.domNode.insertAfter(brElement, range.startContainer);
}
this.applyListsHandler({ subCommand: 'Checklist', callBack: e.callBack, enterAction: e.enterKey }, isChecked);
e.event.preventDefault();
}
}
};
Lists.prototype.isCtrlEnterInChecklist = function (e) {
var storeIntoStack = false;
if (e.event && (e.event.ctrlKey || e.event.metaKey) && e.event.key === 'Enter' && e.event.action === 'checklist-toggle') {
var domMethods = new DOMMethods(this.parent.editableElement);
var li = domMethods.getLiElementsInRange();
for (var i = 0; i < li.length; i++) {
if (li[i].nodeName === 'LI' && li[i].parentElement.nodeName === 'UL'
&& !li[i].classList.contains('e-rte-checklist-hidden')
&& li[i].parentElement.classList.contains('e-rte-checklist')) {
storeIntoStack = true;
if (li[i].classList.contains('e-rte-checklist-checked')) {
li[i].classList.remove('e-rte-checklist-checked');
}
else {
li[i].classList.add('e-rte-checklist-checked');
}
}
}
}
if (storeIntoStack) {
if (e.callBack) {
e.callBack({
requestType: this.currentAction,
editorMode: 'HTML',
range: this.parent.nodeSelection.getRange(this.parent.currentDocument),
elements: this.parent.domNode.blockNodes(),
event: e.event
});
}
storeIntoStack = false;
}
};
Lists.prototype.enterList = function (e) {
var range = this.parent.nodeSelection.getRange(this.parent.currentDocument);
var startNode = range.startContainer.nodeName === 'LI' ? range.startContainer :
range.startContainer.parentElement.closest('LI');
var endNode = range.endContainer.nodeName === 'LI' ? range.endContainer :
range.endContainer.parentElement.closest('LI');
// Command handler for Ctrl+Enter or Cmd+Enter checklist toggle
this.isCtrlEnterInChecklist(e);
var hasMediaElem = false;
if (!isNOU(startNode)) {
var videoElemList = startNode.querySelectorAll('.e-video-clickelem');
var embedVideoElem = videoElemList.length > 0 && videoElemList[0].childNodes[0].nodeName === 'IFRAME';
hasMediaElem = startNode.querySelectorAll('IMG').length > 0 || startNode.querySelectorAll('AUDIO').length > 0 || startNode.querySelectorAll('VIDEO').length > 0 || embedVideoElem;
}
var startNodeParent;
var parentOfCurrentOLUL;
if (startNode) {
startNodeParent = startNode.parentElement;
if (startNodeParent) {
parentOfCurrentOLUL = startNodeParent.parentElement;
}
}
var tableElement = !isNullOrUndefined(startNode) ? startNode.querySelector('TABLE') : null;
if (!isNOU(startNode) && !isNOU(endNode) && startNode === endNode && startNode.tagName === 'LI' &&
startNode.textContent.trim() === '' && !hasMediaElem && isNOU(tableElement)) {
if (startNode.innerHTML.indexOf(' ') >= 0) {
return;
}
if (startNode.textContent.charCodeAt(0) === 65279) {
startNode.textContent = '';
}
if (isNOU(parentOfCurrentOLUL.closest('UL')) && isNOU(parentOfCurrentOLUL.closest('OL'))) {
if (!isNOU(startNode.nextElementSibling)) {
var nearBlockNode = this.parent.domNode.blockParentNode(startNode);
this.parent.nodeCutter.GetSpliceNode(range, nearBlockNode);
}
var insertTag = void 0;
if (e.enterAction === 'DIV') {
insertTag = createElement('div');
insertTag.innerHTML = '<br>';
}
else if (e.enterAction === 'P') {
insertTag = createElement('p');
insertTag.innerHTML = '<br>';
}
else {
insertTag = createElement('br');
}
var immediateBlock = this.domNode.getImmediateBlockNode(range.startContainer);
var _a = this.applyFormattingFromRange(insertTag, range, immediateBlock, e.enterAction), formattedElement = _a.formattedElement, cursorTarget = _a.cursorTarget;
insertTag = formattedElement;
if (!isNOU(parentOfCurrentOLUL) && parentOfCurrentOLUL.nodeName === 'BLOCKQUOTE') {
this.parent.observer.notify('blockquote_list_handled', {});
}
this.parent.domNode.insertAfter(insertTag, startNodeParent);
e.event.preventDefault();
this.parent.nodeSelection.setCursorPoint(this.parent.currentDocument, cursorTarget, 0);
if (startNodeParent.textContent === '' && (startNodeParent.querySelectorAll('audio,video,table').length === 0)) {
detach(startNodeParent);
}
else {
detach(startNode);
}
}
// To handle the nested enter key press in the list for the first LI element
if (!isNOU(parentOfCurrentOLUL) && (!isNOU(parentOfCurrentOLUL.closest('UL')) || !isNOU(parentOfCurrentOLUL.closest('OL'))) &&
parentOfCurrentOLUL.nodeName === 'LI' && parentOfCurrentOLUL.style.listStyleType === 'none' &&
parentOfCurrentOLUL.textContent === '' && startNode.textContent === '' && startNode === startNodeParent.firstElementChild &&
isNOU(startNode.nextSibling)) {
detach(startNodeParent);
parentOfCurrentOLUL.style.removeProperty('list-style-type');
e.event.preventDefault();
}
}
var startContainer = range.startContainer.nodeName === '#text' ? range.startContainer.parentElement : range.startContainer;
var isCloseTableOrEditableElem = this.isNodeInListNotTable(startContainer);
if (!isNOU(startNode) && !isNOU(endNode) && startNode === endNode && startNode.tagName === 'LI' && startNode.textContent.length !== 0 && isCloseTableOrEditableElem && e.event && !e.event.shiftKey && !(e.event.ctrlKey || e.event.metaKey)) {
this.splitListAtCursor(range, startNode, startNodeParent);
e.event.preventDefault();
return;
}
this.handleNestedEnterKeyForLists(e, parentOfCurrentOLUL, startNode, startNodeParent);
};
/*
* Splits a list item at the cursor position, creating a new list item with content after the cursor.
* This method handles both simple and complex list structures, including nested lists.
*/
Lists.prototype.splitListAtCursor = function (range, startNode, startNodeParent) {
var newRange = this.parent.editableElement.ownerDocument.createRange();
var selfClosingElements = ['AREA', 'BASE', 'BR', 'COL', 'EMBED', 'HR', 'IMG', 'INPUT', 'LINK', 'META', 'SOURCE', 'TRACK', 'WBR'];
var startContainer = range.startContainer;
var startOffset = range.startOffset;
var clonedContent;
if (range.startContainer === range.endContainer && range.startOffset === range.endOffset) {
newRange.setStart(startContainer, startOffset);
newRange.setEndAfter(startNode);
this.parent.nodeSelection.setRange(this.parent.currentDocument, newRange);
var getNewRange = this.parent.nodeSelection.getRange(this.parent.editableElement.ownerDocument);
clonedContent = getNewRange.cloneContents();
this.cleanupListElements(clonedContent, selfClosingElements);
newRange.deleteContents();
}
else {
range.deleteContents();
newRange.setStart(startContainer, startOffset);
newRange.setEndAfter(startNode);
this.parent.nodeSelection.setRange(this.parent.currentDocument, newRange);
var getNewRange = this.parent.nodeSelection.getRange(this.parent.editableElement.ownerDocument);
clonedContent = getNewRange.cloneContents();
this.cleanupListElements(clonedContent, selfClosingElements);
newRange.deleteContents();
}
if (startNode.querySelectorAll('*:empty').length > 0) {
var emptyElem = startNode.querySelectorAll('*:empty');
for (var i = 0; i < emptyElem.length; i++) {
if (selfClosingElements.indexOf(emptyElem[i].nodeName) === -1) {
detach(emptyElem[i]);
}
}
}
if (startNode.innerHTML === '') {
startNode.innerHTML = '<br>';
}
clonedContent.normalize();
var firstPosition = this.parent.nodeSelection.findFirstContentNode(clonedContent);
if (startNode.nextElementSibling) {
startNodeParent.insertBefore(clonedContent, startNode.nextElementSibling);
}
else {
startNodeParent.appendChild(clonedContent);
}
if (firstPosition.node.nodeName === 'BR') {
var newRange_1 = this.parent.editableElement.ownerDocument.createRange();
newRange_1.setStartBefore(firstPosition.node);
newRange_1.setEndBefore(firstPosition.node);
this.parent.nodeSelection.setRange(this.parent.currentDocument, newRange_1);
}
else {
this.parent.nodeSelection.setCursorPoint(this.parent.currentDocument, firstPosition.node, 0);
}
};
/*
* Cleans up empty elements within list content and ensures proper structure.
* This method removes empty elements and adds necessary <br> elements for proper display.
*/
Lists.prototype.cleanupListElements = function (content, selfClosingElements) {
// Find and remove empty elements
var liElement = content.firstElementChild;
var emptyElement = liElement.querySelectorAll('*:empty');
for (var i = 0; i < emptyElement.length - 1; i++) {
if (selfClosingElements.indexOf(emptyElement[i].nodeName) === -1) {
detach(emptyElement[i]);
}
}
this.ensureListItemContent(content);
};
/*
* Ensures list items have proper content by adding <br> elements to empty list items or to list items that only contain nested lists.
*/
Lists.prototype.ensureListItemContent = function (content) {
var listItems = content.querySelectorAll('li');
for (var i = 0; i < listItems.length; i++) {
var li = listItems[i];
// Check if list item has no text content or only contains nested lists
var hasOnlyLists = li.childNodes.length > 0 && ((li.firstChild &&
(li.firstChild.nodeName === 'UL' || li.firstChild.nodeName === 'OL')) ||
(li.firstElementChild && (li.firstElementChild.nodeName === 'UL' || li.firstElementChild.nodeName === 'OL'))) &&
!(li.firstChild && li.firstChild.nodeName === '#text' && li.firstChild.textContent.trim().length > 0);
if (hasOnlyLists || li.innerHTML === '') {
var brElement = document.createElement('br');
if (li.firstChild) {
li.insertBefore(brElement, li.firstChild);
}
else {
li.appendChild(brElement);
}
}
else {
var emptyElement = li.querySelector('*:empty');
if (emptyElement) {
emptyElement.appendChild(document.createElement('br'));
}
}
}
};
/*
* Checks if a node is inside a table or list element.
* Returns true if the node is inside a list and not inside a table.
* Returns false if the node is inside a table or not inside a list element.
*/
Lists.prototype.isNodeInListNotTable = function (node) {
var currentNode = node;
// Traverse up the DOM tree until reaching the editable element or body
while (currentNode && currentNode !== this.parent.editableElement) {
if (currentNode.nodeName === 'TABLE') {
// If a table is found, return false regardless of whether a list is found or not
return false;
}
else if ((currentNode.nodeName === 'UL' || currentNode.nodeName === 'OL')) {
// Mark that we found a list element
return true;
}
currentNode = currentNode.parentNode;
}
// Return true only if a list was found and no table was encountered
return false;
};
Lists.prototype.applyFormattingFromRange = function (element, range, blockNode, enterAction) {
var cursorTarget = element;
var formatTags = [];
if (blockNode) {
var currentNode = range.startContainer;
var blockElements = ['div', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'ul', 'ol', 'table', 'tr', 'td', 'th'];
while (currentNode && currentNode !== blockNode) {
var nodeName = currentNode.nodeName.toLowerCase();
if (blockElements.indexOf(nodeName) === -1 && currentNode.nodeType === Node.ELEMENT_NODE) {
formatTags.push({
tag: nodeName,
element: currentNode
});
}
currentNode = currentNode.parentNode;
}
if (formatTags.length > 0) {
element = (enterAction === 'BR') ? createElement('DIV') : element;
element.innerHTML = '';
var currentElement_1 = element;
formatTags.reverse().forEach(function (format) {
var newElement = createElement(format.tag);
Array.from(format.element.attributes).forEach(function (attr) {
newElement.setAttribute(attr.name, attr.value);
});
currentElement_1.appendChild(newElement);
currentElement_1 = newElement;
});
var brElement = createElement('br');
currentElement_1.appendChild(brElement);
cursorTarget = currentElement_1;
}
}
return { formattedElement: (enterAction === 'BR' && formatTags.length > 0) ? element.firstChild : element, cursorTarget: cursorTarget };
};
Lists.prototype.handleNestedEnterKeyForLists = function (e, parentOfCurrentOLUL, startNode, startNodeParent) {
var hasIgnoredElement = false;
if (!isNOU(startNode) && startNode.querySelectorAll('audio,video,table,img,HR').length > 0) {
hasIgnoredElement = true;
}
if (!isNOU(parentOfCurrentOLUL) && (!isNOU(parentOfCurrentOLUL.closest('UL')) || !isNOU(parentOfCurrentOLUL.closest('OL')) || startNodeParent.nodeName === 'UL' || startNodeParent.nodeName === 'OL') &&
(parentOfCurrentOLUL.nodeName === 'LI' || startNode.nodeName === 'LI') && (parentOfCurrentOLUL.style.listStyleType === 'none' || parentOfCurrentOLUL.style.listStyleType === '') &&
parentOfCurrentOLUL.textContent !== '' && (!isNOU(startNode.lastElementChild) && startNode.lastElementChild.textContent !== '') && startNode.firstElementChild && (startNode.firstElementChild.textContent === '' && !hasIgnoredElement) && (startNode === startNodeParent.firstElementChild || startNode.nodeName === 'LI')) {
var range = this.parent.nodeSelection.getRange(this.parent.currentDocument);
this.saveSelection = this.parent.nodeSelection.save(range, this.parent.currentDocument);
this.domNode.setMarker(this.saveSelection);
e.event.preventDefault();
var nodes = [];
if (startNode === startNodeParent.firstElementChild) {
nodes.push(startNodeParent.firstElementChild);
}
else if (startNode.nodeName === 'LI') {
nodes.push(startNode);
}
this.revertList(nodes, e);
this.revertClean();
this.saveSelection = this.domNode.saveMarker(this.saveSelection);
this.saveSelection.restore();
}
};
Lists.prototype.backspaceList = function (e) {
var range = this.parent.nodeSelection.getRange(this.parent.currentDocument);
var startNode = this.parent.domNode.getSelectedNode(range.startContainer, range.startOffset);
var endNode = this.parent.domNode.getSelectedNode(range.endContainer, range.endOffset);
startNode = startNode.nodeName === 'BR' ? startNode.parentElement : startNode;
endNode = endNode.nodeName === 'BR' ? endNode.parentElement : endNode;
if (!isNOU(startNode) && startNode.closest('li')) {
var listCursorInfo = this.getListCursorInfo(range);
var isFirst = startNode.previousElementSibling === null;
var allowedCursorSelections = ['StartParent'];
var allowedSelections = ['SingleFull', 'MultipleFull'];
var blockNodes = this.parent.domNode.blockNodes();
var isAllListSelected = this.isAllListNodesSelected(startNode.closest('li').parentElement);
var hasIndent = listCursorInfo.position === 'StartNested' && startNode && startNode.parentElement &&
startNode.parentElement.closest('li') && startNode.parentElement.closest('li').getAttribute('style')
&& startNode.parentElement.closest('li').getAttribute('style').indexOf('list-style-type: none;') !== -1;
if (isFirst && (allowedCursorSelections.indexOf(listCursorInfo.position) > -1 || hasIndent)) {
e.event.preventDefault();
var saveSelection = this.parent.nodeSelection.save(range, this.parent.currentDocument);
this.domNode.setMarker(saveSelection);
for (var i = 0; i < blockNodes.length; i++) {
if (blockNodes.length > 0 && blockNodes[i].tagName !== 'LI'
&& blockNodes[i].parentNode.tagName === 'LI') {
blockNodes[i] = blockNodes[i].parentNode;
}
}
this.revertList([blockNodes[0]], e);
this.revertClean();
saveSelection = this.domNode.saveMarker(saveSelection);
saveSelection.restore();
return;
}
else if (allowedSelections.indexOf(listCursorInfo.selectionState) > -1 && isAllListSelected) {
e.event.preventDefault();
blockNodes[0].innerHTML = '';
range.deleteContents();
if (blockNodes.length > 1) {
for (var i = 0; i < blockNodes.length; i++) {
if (i === 0) {
continue; // First List is needed after the removal of list items.
}
var list = blockNodes[i];
detach(list);
}
}
this.parent.nodeSelection.setCursorPoint(this.parent.currentDocument, blockNodes[0], 0);
return;
}
}
if (startNode === endNode && !isNullOrUndefined(closest(startNode, 'li')) &&
((startNode.textContent.trim() === '' && startNode.textContent.charCodeAt(0) === 65279) ||
(startNode.textContent.length === 1 && startNode.textContent.charCodeAt(0) === 8203))) {
startNode.textContent = '';
}
if (startNode === endNode && startNode.tagName === 'LI' && startNode.textContent.length === 0 &&
isNOU(startNode.previousElementSibling)) {
startNode.removeAttribute('style');
}
if (startNode === endNode && startNode.textContent === '' && !this.hasMediaElement(startNode)) {
if (startNode.parentElement.tagName === 'LI' && endNode.parentElement.tagName === 'LI') {
detach(startNode);
}
else if (startNode.closest('ul') || startNode.closest('ol')) {
var parentList = !isNOU(startNode.closest('ul')) ? startNode.closest('ul') : startNode.closest('ol');
if (parentList.firstElementChild === startNode && !isNOU(parentList.children[1]) &&
(parentList.children[1].tagName === 'OL' || parentList.children[1].tagName === 'UL')) {
if (parentList.tagName === parentList.children[1].tagName) {
while (parentList.children[1].lastChild) {
this.parent.domNode.insertAfter(parentList.children[1].lastChild, parentList.children[1]);
}
detach(parentList.children[1]);
}
else {
parentList.parentElement.insertBefore(parentList.children[1], parentList);
}
}
}
}
else if (!isNOU(startNode.firstChild) && startNode.firstChild.nodeName === 'BR' &&
(!isNullOrUndefined(startNode.childNodes[1]) && (startNode.childNodes[1].nodeName === 'UL' ||
startNode.childNodes[1].nodeName === 'OL'))) {
var parentList = !isNOU(startNode.closest('ul')) ? startNode.closest('ul') : startNode.closest('ol');
if (!isNOU(parentList) && parentList.tagName === startNode.childNodes[1].nodeName) {
while (startNode.childNodes[1].lastChild) {
this.parent.domNode.insertAfter(startNode.children[1].lastChild, startNode);
}
detach(startNode.childNodes[1]);
}
else if (!isNOU(parentList)) {
parentList.parentElement.insertBefore(startNode.children[1], parentList);
}
}
if (startNode === endNode && startNode.tagName === 'LI' && this.isAtListStart(startNode, range) && !isNOU(startNode.closest('ul, ol'))) {
var currentList = startNode.closest('ul, ol');
var parentListItem = currentList.parentElement;
var prevSibling = startNode.previousElementSibling;
var nestedList = startNode.querySelector('ol, ul');
if ((!isNOU(parentListItem) && parentListItem.tagName === 'LI' && !isNOU(currentList.previousSibling)) || (!isNOU(prevSibling) && prevSibling.nodeName === 'LI')) {
if (!isNOU(nestedList) && (isNOU(prevSibling) || !isNOU(prevSibling))) {
e.event.preventDefault();
// Preventing a default content editable div behaviour and Handles rearrangement of nested lists when press the backspace while the cursor is at the nested list structure and also redistributes child nodes and maintains cursor position after rearrangement
this.handleNestedListRearrangement(startNode, currentList, parentListItem, prevSibling, nestedList);
}
}
}
this.removeList(range, e);
this.firstListBackSpace(range, e);
};
Lists.prototype.hasMediaElement = function (element) {
if (!element) {
return false;
}
var videoElemList = element.querySelectorAll('.e-video-clickelem');
var embedVideoElem = videoElemList.length > 0 && videoElemList[0].childNodes[0].nodeName === 'IFRAME';
if (element.querySelectorAll('audio,video,table,img,hr').length > 0 || ['AUDIO', 'VIDEO', 'TABLE', 'IMG', 'HR'].indexOf(element.tagName) !== -1 || embedVideoElem) {
return true;
}
return false;
};
Lists.prototype.handleNestedListRearrangement = function (startNode, currentList, parentListItem, prevSibling, nestedList) {
var cursorOffset = this.parent.nodeSelection.findLastTextPosition(!isNOU(prevSibling) ? prevSibling : currentList.previousSibling);
var childNodes = Array.from(startNode.childNodes);
for (var i = 0; i < childNodes.length; i++) {
var child = childNodes[i];
if (child === nestedList && nestedList) {
while (nestedList.firstChild) {
currentList.insertBefore(nestedList.firstChild, startNode);
var emptyOL = startNode.querySelector('OL:empty,UL:empty');
if (emptyOL) {
startNode.remove();
}
}
}
else {
if (!isNOU(prevSibling)) {
cursorOffset.node.parentElement.closest('li').appendChild(child);
}
else {
parentListItem.insertBefore(child, currentList);
}
}
}
this.parent.nodeSelection.setCursorPoint(this.parent.currentDocument, cursorOffset.node, cursorOffset.offset);
};
Lists.prototype.findPreviousElementForCursor = function (currentElement) {
var previousNode = null;
// Try to find a previous sibling first
if (currentElement.previousElementSibling) {
previousNode = currentElement.previousElementSibling;
}
// If no previous sibling, try the parent (if not the editable element itself)
else if (currentElement.parentElement && currentElement.parentElement !== this.parent.editableElement) {
previousNode = currentElement.parentElement;
}
return previousNode;
};
Lists.prototype.handleCursorPositioningAfterListRemoval = function (previousNode) {
if (!previousNode) {
return;
}
// For Safari, explicitly set the cursor position
if (this.parent.userAgentData.isSafari()) {
var cursorPosition = this.parent.nodeSelection.findLastTextPosition(previousNode);
if (cursorPosition) {
this.parent.nodeSelection.setCursorPoint(this.parent.currentDocument, cursorPosition.node, cursorPosition.offset);
}
else {
// If we can't find a text position, place at the end of the element
this.parent.nodeSelection.setCursorPoint(this.parent.currentDocument, previousNode, previousNode.childNodes.length);
}
}
};
Lists.prototype.removeList = function (range, e) {
var _this = this;
var startNode = this.parent.domNode.getSelectedNode(range.startContainer, range.startOffset);
var endNode = (!isNOU(range.endContainer.parentElement.closest('li')) && range.endContainer.parentElement.closest('li').childElementCount > 1 && range.endContainer.nodeName === '#text') ? range.endContainer : this.parent.domNode.getSelectedNode(range.endContainer, range.endOffset);
var parentList = (range.startContainer.nodeName === '#text') ? range.startContainer.parentElement.closest('li') : range.startContainer.closest('li');
var endParentList = (range.endContainer.nodeName === '#text') ? range.endContainer.parentElement.closest('li') : range.endContainer.closest('li');
var fullContent = '';
if (!isNOU(parentList) && !isNOU(parentList.firstChild)) {
parentList.childNodes.forEach(function (e) {
fullContent = fullContent + e.textContent;
});
}
startNode = startNode.nodeName === 'BR' ? startNode.parentElement : startNode;
endNode = endNode.nodeName === 'BR' ? endNode.parentElement : endNode;
startNode = startNode.nodeName !== 'LI' && !isNOU(startNode.closest('LI')) ? startNode.closest('LI') : startNode;
endNode = endNode.nodeName !== 'LI' && endNode.nodeName !== '#text' && !isNOU(endNode.closest('LI')) ? endNode.closest('LI') : endNode;
var endNodeNextElementSibling = (!isNOU(endParentList) && isNOU(endParentList.nextElementSibling));
if (((range.commonAncestorContainer.nodeName === 'OL' || range.commonAncestorContainer.nodeName === 'UL' || range.commonAncestorContainer.nodeName === 'LI') &&
isNOU(endNode.nextElementSibling) && endNode.textContent.length === range.endOffset && endNodeNextElementSibling &&
isNOU(startNode.previousElementSibling) && range.startOffset === 0) ||
(Browser.userAgent.indexOf('Firefox') !== -1 && range.startContainer === range.endContainer && range.startContainer === this.parent.editableElement &&
range.startOffset === 0 && range.endOffset === 1)) {
// Find where to place the cursor before removing elements for safari
var previousNode_1;
if (Browser.userAgent.indexOf('Firefox') !== -1) {
previousNode_1 = this.findPreviousElementForCursor(range.commonAncestorContainer.childNodes[0]);
detach(range.commonAncestorContainer.childNodes[0]);
}
else if (range.commonAncestorContainer.nodeName === 'LI') {
previousNode_1 = this.findPreviousElementForCursor(range.commonAncestorContainer.parentElement);
detach(range.commonAncestorContainer.parentElement);
}
else {
previousNode_1 = this.findPreviousElementForCursor(range.commonAncestorContainer);
detach(range.commonAncestorContainer);
}
e.event.preventDefault();
// Handle cursor positioning for safari
this.handleCursorPositioningAfterListRemoval(previousNode_1);
parentList = (range.startContainer.nodeName === '#text') ? range.startContainer.parentElement.closest('li') : range.startContainer.closest('li');
}
var previousNode;
if ((!isNOU(endParentList) && range.commonAncestorContainer === this.parent.editableElement) || (!isNOU(parentList) && (!range.collapsed || (parentList.textContent.trim() === '' && isNOU(parentList.previousElementSibling) && isNOU(parentList.nextElementSibling))) && parentList.textContent === fullContent)) {
range.deleteContents();
var listItems_1 = this.parent.editableElement.querySelectorAll('li');
var _loop_1 = function (i) {
if (!isNOU(listItems_1[i].childNodes)) {
listItems_1[i].childNodes.forEach(function (child) {
if (child.nodeName === 'A' && child.textContent === '') {
listItems_1[i].removeChild(child);
}
});
}
if ((!listItems_1[i].firstChild || listItems_1[i].textContent.trim() === '' && !this_1.hasMediaElement(listItems_1[i])) && (listItems_1[i] === startNode || listItems_1[i] === endNode || listItems_1[i] === endParentList)) {
previousNode = this_1.findPreviousElementForCursor(listItems_1[i]);
listItems_1[i].parentNode.removeChild(listItems_1[i]);
}
};
var this_1 = this;
for (var i = 0; i < listItems_1.length; i++) {
_loop_1(i);
}
this.parent.editableElement.querySelectorAll('ol').forEach(function (ol) {
if (!ol.firstChild || ol.textContent.trim() === '' && !_this.hasMediaElement(ol)) {
previousNode = _this.findPreviousElementForCursor(ol);
ol.parentNode.removeChild(ol);
}
});
this.parent.editableElement.querySelectorAll('ul').forEach(function (ul) {
if (!ul.firstChild || ul.textContent.trim() === '' && !_this.hasMediaElement(ul)) {
previousNode = _this.findPreviousElementForCursor(ul);
ul.parentNode.removeChild(ul);
}
});
e.event.preventDefault();
// Handle cursor positioning for safari
this.handleCursorPositioningAfterListRemoval(previousNode);
}
};
Lists.prototype.onKeyUp = function (e) {
if (!isNOU(this.commonLIParent) && !isNOU(this.commonLIParent.querySelector('.removeList'))) {
var currentLIElem = this.commonLIParent.querySelector('.removeList');
while (!isNOU(currentLIElem.firstChild)) {
this.parent.domNode.insertAfter(currentLIElem.firstChild, currentLIElem);
}
detach(currentLIElem);
}
if (e.event.keyCode === 13) {
var listElements = this.parent.editableElement.querySelectorAll('UL, OL');
for (var i = 0; i < listElements.length; i++) {
if (!isNullOrUndefined(listElements[i]) && !isNOU(listElements[i].parentElement) && !isNullOrUndefined(listElements[i].previousElementSibling) && (listElements[i].parentElement.nodeName === 'UL' || listElements[i].parentElement.nodeName === 'OL')) {
listElements[i].previousElementSibling.appendChild(listElements[i]);
}
}
}
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Lists.prototype.firstListBackSpace = function (range, _e) {
var startNode = this.parent.domNode.getSelectedNode(range.startContainer, range.startOffset);
var listItem = startNode.closest('LI');
if (!isNOU(listItem) && !this.isAtListStart(listItem, range)) {
return;
}
if (!isNOU(startNode.closest('OL'))) {
this.commonLIParent = startNode.closest('OL');
}
else if (!isNOU(startNode.closest('UL'))) {
this.commonLIParent = startNode.closest('UL');
}
if (!isNOU(listItem) && range.startOffset === 0 && range.endOffset === 0 &&
isNOU(startNode.previousSibling) && !isNOU(this.commonLIParent) && isNOU(this.commonLIParent.previousSibling) &&
(isNOU(this.commonLIParent.parentElement.closest('OL')) && isNOU(this.commonLIParent.parentElement.closest('UL')) &&
isNOU(this.commonLIParent.parentElement.closest('LI')))) {
var currentElem = createElement('P');
currentElem.innerHTML = '​';
startNode.classList.add('removeList');
this.commonLIParent.parentElement.insertBefore(currentElem, this.commonLIParent);
}
};
Lists.prototype.isAtListStart = function (startNode, range) {
if (startNode.nodeName !== 'LI') {
return false;
}
var listItem = startNode;
var firstTextNode = this.getFirstTextNode(listItem);
return firstTextNode === range.startContainer && range.startOffset === 0;
};
Lists.prototype.getFirstTextNode = function (element) {
if (element.nodeType === Node.TEXT_NODE) {
return element;
}
if (element.nodeName === 'BR') {
return element;
}
for (var i = 0; i < element.childNodes.length; i++) {
var firstTextNode = this.getFirstTextNode(element.childNodes[i]);
if (firstTextNode) {
return firstTextNode;
}
}
return null;
};
Lists.prototype.keyDownHandler = function (e) {
if (e.event.which === 13) {
this.enterList(e);
}
if (e.event.which === 32) {
this.spaceList(e);
}
if (e.event.which === 8) {
this.backspaceList(e);
}
if ((e.event.which === 46 && e.event.action === 'delete')) {
var range = this.parent.nodeSelection.getRange(this.parent.currentDocument);
var commonAncestor = range.commonAncestorContainer;
var startEle = range.startContainer;
var endEle = range.endContainer;
var startNode = startEle.nodeType === 3 ? this.domNode.blockParentNode(startEle) : startEle;
var endNode = endEle.nodeType === 3 ? this.domNode.blockParentNode(endEle) : endEle;
if ((commonAncestor.nodeName === 'UL' || commonAncestor.nodeName === 'OL') && startNode !== endNode
&& (!isNullOrUndefined(closest(startNode, 'ul')) || !isNullOrUndefined(closest(startNode, 'ol')))
&& (!isNullOrUndefined(closest(endNode, 'ul')) || !isNullOrUndefined(closest(endNode, 'ol')))
&& ((commonAncestor.lastElementChild === closest(endNode, 'li') && commonAncestor.lastChild !== endNode)) && !range.collapsed) {
if (this.areAllListItemsSelected(commonAncestor, range)) {
detach(commonAncestor);
}
}
this.removeList(range, e);
}
if (e.event.which === 9) {
var range = this.parent.nodeSelection.getRange(this.parent.currentDocument);
if (!(e.event.action && e.event.action === 'indent')) {
this.saveSelection = this.parent.nodeSelection.save(range, this.parent.currentDocument);
}
if (e.enableTabKey) {
this.handleListIndentation(e);
}
var blockNodes = void 0;
var startNode = this.parent.domNode.getSelectedNode(range.startContainer, range.startOffset);
var endNode = this.parent.domNode.getSelectedNode(range.endContainer, range.endOffset);
if ((startNode === endNode && (startNode.nodeName === 'BR' || startNode.nodeName === '#text') &&
CONSTANT.IGNORE_BLOCK_TAGS.indexOf(startNode.parentNode.tagName.toLocaleLowerCase()) >= 0)) {
return;
}
else {
if (!(e.event.action && (e.event.action === 'indent')) && !this.listTabIndentation) {
this.domNode.setMarker(this.saveSelection);
}
blockNodes = this.domNode.blockNodes();
}
var nodes = [];
var isNested = true;
for (var i = 0; i < blockNodes.length; i++) {
if (blockNodes[i].parentNode.tagName === 'LI') {
nodes.push(blockNodes[i].parentNode);
}
else if (!closest(blockNodes[i], 'OL') && !closest(blockNodes[i], 'UL') && closest(blockNodes[i], 'LI')) {
nodes.push(closest(blockNodes[i], 'LI'));
}
else if (blockNodes[i].tagName === 'LI' && blockNodes[i].childNodes[0].tagName !== 'P' &&
(blockNodes[i].childNodes[0].tagName !== 'OL' &&
blockNodes[i].childNodes[0].tagName !== 'UL')) {
nodes.push(blockNodes[i]);
}
}
if (nodes.length > 1 || nodes.length === 1) {
e.event.preventDefault();
e.event.stopPropagation();
this.currentAction = this.getAction(nodes[0]);
if (e.event.shiftKey && (!e.enableTabKey || (e.enableTabKey && !this.listTabIndentation))) {
this.revertList(nodes, e);
this.revertClean();
}
else if (!e.enableTabKey || (e.enableTabKey && !this.listTabIndentation)) {
if (this.indentTab(e)) {
isNested = this.nestedList(nodes);
}
}
if (isNested) {
this.cleanNode();
this.parent.editableElement.focus({ preventScroll: true });
}
if (!(e.event.action && (e.event.action === 'indent')) && !this.listTabIndentation) {
this.saveSelection = this.domNode.saveMarker(this.saveSelection);
this.saveSelection.restore();
if (e.callBack) {
e.callBack({
requestType: this.currentAction,
editorMode: 'HTML',
range: this.parent.nodeSelection.getRange(this.parent.currentDocument),
elements: this.parent.domNode.blockNodes(),
event: e.event
});
}
}
}
else {
if (!(e.event.action && (e.event.action === 'indent')) && !this.listTabIndentation) {
if (e.event && e.event.shiftKey && e.event.key === 'Tab') {
e.event.action = 'tab';
}
this.saveSelection = this