UNPKG

@ckeditor/ckeditor5-clipboard

Version:

Clipboard integration feature for CKEditor 5.

96 lines (95 loc) 3.79 kB
/** * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options */ // Elements which should not have empty-line padding. // Most `view.ContainerElement` want to be separate by new-line, but some are creating one structure // together (like `<li>`) so it is better to separate them by only one "\n". const smallPaddingElements = ['figcaption', 'li']; const listElements = ['ol', 'ul']; /** * Converts {@link module:engine/view/item~Item view item} and all of its children to plain text. * * @param converter The converter instance. * @param viewItem View item to convert. * @returns Plain text representation of `viewItem`. */ export default function viewToPlainText(converter, viewItem) { if (viewItem.is('$text') || viewItem.is('$textProxy')) { return viewItem.data; } if (viewItem.is('element', 'img') && viewItem.hasAttribute('alt')) { return viewItem.getAttribute('alt'); } if (viewItem.is('element', 'br')) { return '\n'; // Convert soft breaks to single line break (#8045). } /** * Item is a document fragment, attribute element or container element. It doesn't * have it's own text value, so we need to convert its children elements. */ let text = ''; let prev = null; for (const child of viewItem.getChildren()) { text += newLinePadding(child, prev) + viewToPlainText(converter, child); prev = child; } // If item is a raw element, the only way to get its content is to render it and read the text directly from DOM. if (viewItem.is('rawElement')) { const tempElement = document.createElement('div'); viewItem.render(tempElement, converter); text += domElementToPlainText(tempElement); } return text; } /** * Recursively converts DOM element and all of its children to plain text. */ function domElementToPlainText(element) { let text = ''; if (element.nodeType === Node.TEXT_NODE) { return element.textContent; } else if (element.tagName === 'BR') { return '\n'; } for (const child of element.childNodes) { text += domElementToPlainText(child); } return text; } /** * Returns new line padding to prefix the given elements with. */ function newLinePadding(element, previous) { if (!previous) { // Don't add padding to first elements in a level. return ''; } if (element.is('element', 'li') && !element.isEmpty && element.getChild(0).is('containerElement')) { // Separate document list items with empty lines. return '\n\n'; } if (listElements.includes(element.name) && listElements.includes(previous.name)) { /** * Because `<ul>` and `<ol>` are AttributeElements, two consecutive lists will not have any padding between * them (see the `if` statement below). To fix this, we need to make an exception for this case. */ return '\n\n'; } if (!element.is('containerElement') && !previous.is('containerElement')) { // Don't add padding between non-container elements. return ''; } if (smallPaddingElements.includes(element.name) || smallPaddingElements.includes(previous.name)) { // Add small padding between selected container elements. return '\n'; } // Do not add padding around the elements that won't be rendered. if (element.is('element') && element.getCustomProperty('dataPipeline:transparentRendering') || previous.is('element') && previous.getCustomProperty('dataPipeline:transparentRendering')) { return ''; } // Add empty lines between container elements. return '\n\n'; }