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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | 26x 26x 10x 10x 9x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 26x 10x 10x 9x 9x 46x 10x 10x 1x 1x 10x 26x 10x 10x 46x 18x 10x | import { DocumentNode, NodeType, TagNode, NodeWithChildren, Node } from '../..';
/**
* Transform an arbitrary fragment DOM into a valid HTML page.
* Will create <html>, <head>, <body>, and <title> tags if not already present.
* Duplicates of those tags will be merged, and the final tags will all be placed in the correct order
*
* @param dom DOM to transform
*/
export function buildPage(dom: DocumentNode): void {
// extract PIs
const procIns = dom.findChildNodesByNodeType(NodeType.ProcessingInstruction);
for (const procIn of procIns) {
procIn.removeSelf();
}
// init head
const headTag = createHead(dom);
// init body
const bodyTag = createBody(dom);
// get or create html
const htmlTag = createHtml(dom);
// remove any left over nodes
dom.clear();
// rebuild page
dom.appendChildren(procIns);
dom.appendChild(htmlTag);
htmlTag.appendChild(headTag);
htmlTag.appendChild(bodyTag);
}
function createHtml(dom: DocumentNode): TagNode {
const htmlTag = new TagNode('html');
const sourceHtmlTags = dom.findChildTagsByTagName('html');
for (const sourceHtml of sourceHtmlTags) {
// copy children
htmlTag.appendChildren(sourceHtml.childNodes);
// copy attributes
sourceHtml.getAttributes().forEach((value, key) => {
if (!htmlTag.hasAttribute(key)) {
htmlTag.setRawAttribute(key, value);
}
});
// remove HTML tag
sourceHtml.removeSelf();
}
// add required "lang" attribute if missing
Eif (!htmlTag.hasAttribute('lang')) {
htmlTag.setAttribute('lang', 'en');
}
return htmlTag;
}
// Tags that are only allowed (or should only be used) in the <head> section
const headTags = [
'style',
'link',
'meta',
'base'
];
function createHead(root: NodeWithChildren): TagNode {
const headTag = new TagNode('head');
// extract children of head tags into new head
for (const existingHead of root.findChildTagsByTagName('head')) {
// move children
headTag.appendChildren(existingHead.childNodes);
// remove the old head when done
existingHead.removeSelf();
}
// move elements that can only exist in the head
const headContents: Node[] = root.findChildTags((tag: TagNode) => headTags.includes(tag.tagName));
headTag.appendChildren(headContents);
// add title if missing
if (headTag.findChildTagByTagName('title', false) === null) {
const titleTag = new TagNode('title');
headTag.appendChild(titleTag);
}
return headTag;
}
// Tags that should be "promoted" in the <body> section.
// Promoting a tag means removing it but preserving its children.
const bodyPromoteTags = [
'html',
'body'
];
function createBody(root: NodeWithChildren): TagNode {
const bodyTag = new TagNode('body');
// copy all remaining elements from dom
bodyTag.appendChildren(root.childNodes);
// "promote" children of nested <head> tags
for (const childBody of bodyTag.findChildTags((tag: TagNode) => bodyPromoteTags.includes(tag.tagName))) {
childBody.removeSelf(true);
}
return bodyTag;
} |