import { Fun, Optional, Type } from '@ephox/katamari'; import { Attribute, Css, Html, Insert, Remove, SugarElement, SugarShadowDom } from '@ephox/sugar'; import Editor from '../api/Editor'; import AstNode from '../api/html/Node'; import * as Options from '../api/Options'; import Tools from '../api/util/Tools'; import * as ElementType from '../dom/ElementType'; import * as TrimHtml from '../dom/TrimHtml'; import * as Zwsp from '../text/Zwsp'; import { cleanupBogusElements, cleanupInputNames } from './ContentCleanup'; import { Content, GetContentArgs } from './ContentTypes'; const trimEmptyContents = (editor: Editor, html: string): string => { const blockName = Options.getForcedRootBlock(editor); const emptyRegExp = new RegExp(`^(<${blockName}[^>]*>( | |\\s|\u00a0|
|)<\\/${blockName}>[\r\n]*|
[\r\n]*)$`); return html.replace(emptyRegExp, ''); }; const getPlainTextContent = (editor: Editor, body: HTMLElement) => { const doc = editor.getDoc(); const dos = SugarShadowDom.getRootNode(SugarElement.fromDom(editor.getBody())); const offscreenDiv = SugarElement.fromTag('div', doc); Attribute.set(offscreenDiv, 'data-mce-bogus', 'all'); Css.setAll(offscreenDiv, { position: 'fixed', left: '-9999999px', top: '0' }); Html.set(offscreenDiv, body.innerHTML); cleanupBogusElements(offscreenDiv); cleanupInputNames(offscreenDiv); // Append the wrapper element so that the browser will evaluate styles when getting the `innerText` const root = SugarShadowDom.getContentContainer(dos); Insert.append(root, offscreenDiv); const content = Zwsp.trim(offscreenDiv.dom.innerText); Remove.remove(offscreenDiv); return content; }; const getContentFromBody = (editor: Editor, args: GetContentArgs, body: HTMLElement): Content => { let content: Content; if (args.format === 'raw') { content = Tools.trim(TrimHtml.trimExternal(editor.serializer, body.innerHTML)); } else if (args.format === 'text') { content = getPlainTextContent(editor, body); } else if (args.format === 'tree') { content = editor.serializer.serialize(body, args); } else { content = trimEmptyContents(editor, editor.serializer.serialize(body, args)); } // Trim if not using a whitespace preserve format/element const shouldTrim = args.format !== 'text' && !ElementType.isWsPreserveElement(SugarElement.fromDom(body)); return shouldTrim && Type.isString(content) ? Tools.trim(content) : content; }; export const getContentInternal = (editor: Editor, args: GetContentArgs): Content => Optional.from(editor.getBody()) .fold( Fun.constant(args.format === 'tree' ? new AstNode('body', 11) : ''), (body) => getContentFromBody(editor, args, body) );