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 961x 1x               1x 1x                               1x       51x         51x 50x             50x     51x       50x   50x 391x 200x   200x 93x   191x 149x       50x                 50x 50x 50x 50x 50x   50x 23x 23x 23x   27x     50x       50x   79x    
import { retrieveCssRules } from '../createModelFromHtml/convertInlineCss';
import {
    isBlockElement,
    isNodeOfType,
    retrieveDocumentMetadata,
    toArray,
} from 'roosterjs-content-model-dom';
import type { ClipboardData, CssRule } from 'roosterjs-content-model-types';
 
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));
}