Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
return false;
}
// 1. Recursively clear format of all its child nodes
let allChildrenAreBlock = ([].slice.call(node.childNodes) as Node[])
.map(n => clearNodeFormat(n, tagsToUnwrap, tagsToStopUnwrap, attributesToPreserve))
.reduce((previousValue, value) => previousValue && value, true);
if (!canCollapse(tagsToStopUnwrap, node)) {
return false;
}
let returnBlockElement = isBlockElement(node);
// 2. If we should unwrap this tag, put it into an array and unwrap it later
if (tagsToUnwrap.indexOf(getTagOfNode(node)) >= 0 || allChildrenAreBlock) {
if (returnBlockElement && !allChildrenAreBlock) {
wrap(node);
}
unwrap(node);
} else {
// 3. Otherwise, remove all attributes
clearAttribute(node as HTMLElement, attributesToPreserve);
}
return returnBlockElement;
}
editor.addUndoSnapshot(() => {
element.parentNode.insertBefore(div, element);
// Select the new line when we are in table. This is the same behavior with Word
if (getTagOfNode(element) == 'TABLE') {
editor.select(new Position(div, PositionType.Begin).normalize());
}
});
event.rawEvent.preventDefault();
return cacheGetEventData(event, 'FIRST_STRUCTURE', () => {
// Provide a chance to keep browser default behavior by pressing SHIFT
let element = event.rawEvent.shiftKey ? null : editor.getElementAtCursor(CHILD_SELECTOR);
if (element) {
let range = editor.getSelectionRange();
if (
range &&
range.collapsed &&
isPositionAtBeginningOf(Position.getStart(range), element) &&
!editor.getBodyTraverser(element).getPreviousBlockElement()
) {
return editor.getElementAtCursor(CHILD_PARENT_TAG_MAP[getTagOfNode(element)]);
}
}
return null;
});
}
function selectEditorPoint(core: EditorCore, container: Node, offset: number): boolean {
if (!container || !contains(core.contentDiv, container)) {
return false;
}
let range = core.document.createRange();
if (container.nodeType == NodeType.Text && offset <= container.nodeValue.length) {
range.setStart(container, offset);
} else if (offset == NodeBoundary.Begin) {
range.setStartBefore(container);
} else {
range.setStartAfter(container);
}
range.collapse(true /* toStart */);
return updateSelection(core, range);
}
export const selectRange: SelectRange = (
core: EditorCore,
range: Range,
skipSameRange?: boolean
) => {
let selection: Selection;
let needAddRange = true;
if (
!contains(core.contentDiv, range) ||
!(selection = core.document.defaultView.getSelection())
) {
return false;
}
if (selection.rangeCount > 0) {
// Workaround IE exception 800a025e
try {
let currentRange: Range;
// Do not remove/add range if current selection is the same with target range
// Without this check, execCommand() may fail in Edge since we changed the selection
if (
(skipSameRange || Browser.isEdge) &&
(currentRange = selection.rangeCount == 1 ? selection.getRangeAt(0) : null) &&
currentRange.startContainer == range.startContainer &&
currentRange.startOffset == range.startOffset &&
selectionRange.collapsed &&
(focusNode = selectionRange.startContainer) &&
(focusNode == core.contentDiv ||
(focusNode.nodeType == NodeType.Text && focusNode.parentNode == core.contentDiv))
) {
let editorSelection = new EditorSelection(
core.contentDiv,
selectionRange,
core.inlineElementFactory
);
let blockElement = editorSelection.startBlockElement;
if (!blockElement) {
// Only reason we don't get the selection block is that we have an empty content div
// which can happen when users removes everything (i.e. select all and DEL, or backspace from very end to begin)
// The fix is to add a DIV wrapping, apply default format and move cursor over
let nodes = fromHtml('<div><br></div>', core.document);
let element = core.contentDiv.appendChild(nodes[0]) as HTMLElement;
applyFormat(element, core.defaultFormat);
// element points to a wrapping node we added "<div><br></div>". We should move the selection left to <br>
selectEditorPoint(core, element.firstChild, NodeBoundary.Begin);
} else if (blockElement.getStartNode().parentNode == blockElement.getEndNode().parentNode) {
// Only fix the balanced start-end block where start and end node is under same parent
// The focus node could be pointing to the content div, normalize it to have it point to a child first
let focusOffset = selectionRange.startOffset;
let editorPoint = normalizeEditorPoint(focusNode, focusOffset);
let element = wrapAll(blockElement.getContentNodes()) as HTMLElement;
if (getTagOfNode(blockElement.getStartNode()) == 'BR') {
// if the block is just BR, apply default format
// Otherwise, leave it as it is as we don't want to change the style for existing data
applyFormat(element, core.defaultFormat);
}
// Last restore the selection using the normalized editor point
function workaroundForChrome(editor: Editor) {
let traverser = editor.getSelectionTraverser();
let block = traverser && traverser.currentBlockElement;
while (block) {
let container = block.getStartNode();
if (container) {
// Add a temp <img> tag before all other nodes in the block to avoid Chrome remove existing format when toggle list
const tempNode = fromHtml(TEMP_NODE_HTML, editor.getDocument())[0];
if (isVoidHtmlElement(container) || !isBlockElement(container)) {
container.parentNode.insertBefore(tempNode, container);
} else {
container.insertBefore(tempNode, container.firstChild);
}
}
block = traverser.getNextBlockElement();
}
}
if (block) {
formatNode = block.collapseToSingleElement();
// if the block is empty, apply default format
// Otherwise, leave it as it is as we don't want to change the style for existing data
// unless the block was just created by the keyboard event (e.g. ctrl+a & start typing)
const shouldSetNodeStyles =
isNodeEmpty(formatNode) ||
(event && this.wasNodeJustCreatedByKeyboardEvent(event, formatNode));
formatNode = formatNode && shouldSetNodeStyles ? formatNode : null;
} else {
// Only reason we don't get the selection block is that we have an empty content div
// which can happen when users removes everything (i.e. select all and DEL, or backspace from very end to begin)
// The fix is to add a DIV wrapping, apply default format and move cursor over
formatNode = fromHtml(
Browser.isEdge ? '<div><span><br></span></div>' : '<div><br></div>',
this.editor.getDocument()
)[0] as HTMLElement;
this.editor.insertNode(formatNode, {
position: ContentPosition.End,
updateCursor: false,
replaceSelection: false,
insertOnNewLine: false,
});
// element points to a wrapping node we added "<div><br></div>". We should move the selection left to <br>
result = new Position(formatNode.firstChild, PositionType.Begin);
}
if (formatNode) {
applyFormat(formatNode, this.editor.getDefaultFormat(), this.editor.isDarkMode());
public insertContent(content: string, option?: InsertOption) {
if (content) {
let allNodes = fromHtml(content, this.core.document);
// If it is to insert on new line, and there are more than one node in the collection, wrap all nodes with
// a parent DIV before calling insertNode on each top level sub node. Otherwise, every sub node may get wrapped
// separately to show up on its own line
if (option && option.insertOnNewLine && allNodes.length > 0) {
allNodes = [wrapAll(allNodes)];
}
for (let i = 0; i < allNodes.length; i++) {
this.insertNode(allNodes[i], option);
}
}
}
let startBlock = editor.getBlockElementAtNode(startNode);
let endNode = Position.getEnd(range).normalize().node;
let endBlock = editor.getBlockElementAtNode(endNode);
let nodes =
startBlock && endBlock
? editor.collapseNodes(
startBlock.getStartNode(),
endBlock.getEndNode(),
true /*canSplitParent*/
)
: [];
if (nodes.length == 0) {
// Selection is collapsed and blockElement is null, we need to create an empty div.
// In case of IE and Edge, we insert ZWS to put cursor in the div, otherwise insert BR node.
nodes = fromHtml(
`<div>${Browser.isIEOrEdge ? ZERO_WIDTH_SPACE : '<br>'}</div>`,
editor.getDocument()
);
editor.insertNode(nodes[0]);
editor.select(nodes[0], PositionType.Begin);
} else if (nodes.length == 1) {
let tag = getTagOfNode(nodes[0]);
if (tag == 'BR') {
nodes = [wrap(nodes[0])];
} else if (tag == 'LI' || tag == 'TD') {
nodes = [].slice.call(nodes[0].childNodes) as Node[];
}
} else {
while (
nodes[0] &&
editor.contains(nodes[0].parentNode) &&