@ckeditor/ckeditor5-paste-from-office
Version:
Paste from Office feature for CKEditor 5.
66 lines (65 loc) • 3.1 kB
JavaScript
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module paste-from-office/filters/br
*/
import { DomConverter, ViewDocument } from 'ckeditor5/src/engine.js';
/**
* Transforms `<br>` elements that are siblings to some block element into a paragraphs.
*
* @param documentFragment The view structure to be transformed.
*/
export default function transformBlockBrsToParagraphs(documentFragment, writer) {
const viewDocument = new ViewDocument(writer.document.stylesProcessor);
const domConverter = new DomConverter(viewDocument, { renderingMode: 'data' });
const blockElements = domConverter.blockElements;
const inlineObjectElements = domConverter.inlineObjectElements;
const elementsToReplace = [];
for (const value of writer.createRangeIn(documentFragment)) {
const element = value.item;
if (element.is('element', 'br')) {
const nextSibling = findSibling(element, 'forward', writer, { blockElements, inlineObjectElements });
const previousSibling = findSibling(element, 'backward', writer, { blockElements, inlineObjectElements });
const nextSiblingIsBlock = isBlockViewElement(nextSibling, blockElements);
const previousSiblingIsBlock = isBlockViewElement(previousSibling, blockElements);
// If the <br> is surrounded by blocks then convert it to a paragraph:
// * <p>foo</p>[<br>]<p>bar</p> -> <p>foo</p>[<p></p>]<p>bar</p>
// * <p>foo</p>[<br>] -> <p>foo</p>[<p></p>]
// * [<br>]<p>foo</p> -> [<p></p>]<p>foo</p>
if (previousSiblingIsBlock || nextSiblingIsBlock) {
elementsToReplace.push(element);
}
}
}
for (const element of elementsToReplace) {
if (element.hasClass('Apple-interchange-newline')) {
writer.remove(element);
}
else {
writer.replace(element, writer.createElement('p'));
}
}
}
/**
* Returns sibling node, threats inline elements as transparent (but should stop on an inline objects).
*/
function findSibling(viewElement, direction, writer, { blockElements, inlineObjectElements }) {
let position = writer.createPositionAt(viewElement, direction == 'forward' ? 'after' : 'before');
// Find first position that is just before a first:
// * text node,
// * block element,
// * inline object element.
// It's ignoring any inline (non-object) elements like span, strong, etc.
position = position.getLastMatchingPosition(({ item }) => (item.is('element') &&
!blockElements.includes(item.name) &&
!inlineObjectElements.includes(item.name)), { direction });
return direction == 'forward' ? position.nodeAfter : position.nodeBefore;
}
/**
* Returns true for view elements that are listed as block view elements.
*/
function isBlockViewElement(node, blockElements) {
return !!node && node.is('element') && blockElements.includes(node.name);
}