All files / roosterjs-content-model-core/lib/command/paste retrieveHtmlInfo.ts

100% Statements 34/34
90.91% Branches 20/22
100% Functions 5/5
100% Lines 32/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 971x 1x                 1x 1x                               1x       43x         43x 42x             42x     43x       42x   42x 285x 147x   147x 53x   138x 107x       42x                 42x 42x 42x 42x 42x   42x 17x 17x 17x   25x     42x       42x   47x    
import { retrieveCssRules } from '../createModelFromHtml/convertInlineCss';
import {
    isBlockElement,
    isNodeOfType,
    retrieveDocumentMetadata,
    toArray,
} from 'roosterjs-content-model-dom';
import type { ClipboardData } from 'roosterjs-content-model-types';
import type { CssRule } from '../createModelFromHtml/convertInlineCss';
 
const START_FRAGMENT = '<!--StartFragment-->';
const END_FRAGMENT = '<!--EndFragment-->';
 
/**
 * @internal
 */
export interface HtmlFromClipboard {
    metadata: Record<string, string>;
    globalCssRules: CssRule[];
    htmlBefore?: string;
    htmlAfter?: string;
    containsBlockElements?: boolean;
}
 
/**
 * @internal
 */
export function retrieveHtmlInfo(
    doc: Document | null,
    clipboardData: Partial<ClipboardData>
): HtmlFromClipboard {
    let result: HtmlFromClipboard = {
        metadata: {},
        globalCssRules: [],
    };
 
    if (doc) {
        result = {
            ...retrieveHtmlStrings(clipboardData),
            globalCssRules: retrieveCssRules(doc),
            metadata: retrieveDocumentMetadata(doc),
            containsBlockElements: checkBlockElements(doc),
        };
 
        clipboardData.htmlFirstLevelChildTags = retrieveTopLevelTags(doc);
    }
 
    return result;
}
 
function retrieveTopLevelTags(doc: Document): string[] {
    const topLevelTags: string[] = [];
 
    for (let child = doc.body.firstChild; child; child = child.nextSibling) {
        if (isNodeOfType(child, 'TEXT_NODE')) {
            const trimmedString = child.nodeValue?.replace(/(\r\n|\r|\n)/gm, '').trim();
 
            if (trimmedString) {
                topLevelTags.push(''); // Push an empty string as tag for text node
            }
        } else if (isNodeOfType(child, 'ELEMENT_NODE')) {
            topLevelTags.push(child.tagName);
        }
    }
 
    return topLevelTags;
}
 
function retrieveHtmlStrings(
    clipboardData: Partial<ClipboardData>
): {
    htmlBefore: string;
    htmlAfter: string;
} {
    const rawHtml = clipboardData.rawHtml ?? '';
    const startIndex = rawHtml.indexOf(START_FRAGMENT);
    const endIndex = rawHtml.lastIndexOf(END_FRAGMENT);
    let htmlBefore = '';
    let htmlAfter = '';
 
    if (startIndex >= 0 && endIndex >= startIndex + START_FRAGMENT.length) {
        htmlBefore = rawHtml.substring(0, startIndex);
        htmlAfter = rawHtml.substring(endIndex + END_FRAGMENT.length);
        clipboardData.html = rawHtml.substring(startIndex + START_FRAGMENT.length, endIndex);
    } else {
        clipboardData.html = rawHtml;
    }
 
    return { htmlBefore, htmlAfter };
}
 
function checkBlockElements(doc: Document): boolean {
    const elements = toArray(doc.body.querySelectorAll('*'));
 
    return elements.some(el => isNodeOfType(el, 'ELEMENT_NODE') && isBlockElement(el));
}