1 | import { TAG_NAMES as $, NS, hasUnescapedText } from '../common/html.js';
|
2 | import { escapeText, escapeAttribute } from 'entities/lib/escape.js';
|
3 | import { defaultTreeAdapter } from '../tree-adapters/default.js';
|
4 |
|
5 | const VOID_ELEMENTS = new Set([
|
6 | $.AREA,
|
7 | $.BASE,
|
8 | $.BASEFONT,
|
9 | $.BGSOUND,
|
10 | $.BR,
|
11 | $.COL,
|
12 | $.EMBED,
|
13 | $.FRAME,
|
14 | $.HR,
|
15 | $.IMG,
|
16 | $.INPUT,
|
17 | $.KEYGEN,
|
18 | $.LINK,
|
19 | $.META,
|
20 | $.PARAM,
|
21 | $.SOURCE,
|
22 | $.TRACK,
|
23 | $.WBR,
|
24 | ]);
|
25 | function isVoidElement(node, options) {
|
26 | return (options.treeAdapter.isElementNode(node) &&
|
27 | options.treeAdapter.getNamespaceURI(node) === NS.HTML &&
|
28 | VOID_ELEMENTS.has(options.treeAdapter.getTagName(node)));
|
29 | }
|
30 | const defaultOpts = { treeAdapter: defaultTreeAdapter, scriptingEnabled: true };
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 | export function serialize(node, options) {
|
54 | const opts = { ...defaultOpts, ...options };
|
55 | if (isVoidElement(node, opts)) {
|
56 | return '';
|
57 | }
|
58 | return serializeChildNodes(node, opts);
|
59 | }
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 | export function serializeOuter(node, options) {
|
80 | const opts = { ...defaultOpts, ...options };
|
81 | return serializeNode(node, opts);
|
82 | }
|
83 | function serializeChildNodes(parentNode, options) {
|
84 | let html = '';
|
85 |
|
86 | const container = options.treeAdapter.isElementNode(parentNode) &&
|
87 | options.treeAdapter.getTagName(parentNode) === $.TEMPLATE &&
|
88 | options.treeAdapter.getNamespaceURI(parentNode) === NS.HTML
|
89 | ? options.treeAdapter.getTemplateContent(parentNode)
|
90 | : parentNode;
|
91 | const childNodes = options.treeAdapter.getChildNodes(container);
|
92 | if (childNodes) {
|
93 | for (const currentNode of childNodes) {
|
94 | html += serializeNode(currentNode, options);
|
95 | }
|
96 | }
|
97 | return html;
|
98 | }
|
99 | function serializeNode(node, options) {
|
100 | if (options.treeAdapter.isElementNode(node)) {
|
101 | return serializeElement(node, options);
|
102 | }
|
103 | if (options.treeAdapter.isTextNode(node)) {
|
104 | return serializeTextNode(node, options);
|
105 | }
|
106 | if (options.treeAdapter.isCommentNode(node)) {
|
107 | return serializeCommentNode(node, options);
|
108 | }
|
109 | if (options.treeAdapter.isDocumentTypeNode(node)) {
|
110 | return serializeDocumentTypeNode(node, options);
|
111 | }
|
112 |
|
113 | return '';
|
114 | }
|
115 | function serializeElement(node, options) {
|
116 | const tn = options.treeAdapter.getTagName(node);
|
117 | return `<${tn}${serializeAttributes(node, options)}>${isVoidElement(node, options) ? '' : `${serializeChildNodes(node, options)}</${tn}>`}`;
|
118 | }
|
119 | function serializeAttributes(node, { treeAdapter }) {
|
120 | let html = '';
|
121 | for (const attr of treeAdapter.getAttrList(node)) {
|
122 | html += ' ';
|
123 | if (!attr.namespace) {
|
124 | html += attr.name;
|
125 | }
|
126 | else
|
127 | switch (attr.namespace) {
|
128 | case NS.XML: {
|
129 | html += `xml:${attr.name}`;
|
130 | break;
|
131 | }
|
132 | case NS.XMLNS: {
|
133 | if (attr.name !== 'xmlns') {
|
134 | html += 'xmlns:';
|
135 | }
|
136 | html += attr.name;
|
137 | break;
|
138 | }
|
139 | case NS.XLINK: {
|
140 | html += `xlink:${attr.name}`;
|
141 | break;
|
142 | }
|
143 | default: {
|
144 | html += `${attr.prefix}:${attr.name}`;
|
145 | }
|
146 | }
|
147 | html += `="${escapeAttribute(attr.value)}"`;
|
148 | }
|
149 | return html;
|
150 | }
|
151 | function serializeTextNode(node, options) {
|
152 | const { treeAdapter } = options;
|
153 | const content = treeAdapter.getTextNodeContent(node);
|
154 | const parent = treeAdapter.getParentNode(node);
|
155 | const parentTn = parent && treeAdapter.isElementNode(parent) && treeAdapter.getTagName(parent);
|
156 | return parentTn &&
|
157 | treeAdapter.getNamespaceURI(parent) === NS.HTML &&
|
158 | hasUnescapedText(parentTn, options.scriptingEnabled)
|
159 | ? content
|
160 | : escapeText(content);
|
161 | }
|
162 | function serializeCommentNode(node, { treeAdapter }) {
|
163 | return `<!--${treeAdapter.getCommentNodeContent(node)}-->`;
|
164 | }
|
165 | function serializeDocumentTypeNode(node, { treeAdapter }) {
|
166 | return `<!DOCTYPE ${treeAdapter.getDocumentTypeNodeName(node)}>`;
|
167 | }
|
168 |
|
\ | No newline at end of file |