-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserialize.js
148 lines (131 loc) · 3.16 KB
/
serialize.js
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
var voidMap = require("./void-map");
var REG_ESCAPE_ALL = /[<>&]/g;
var REG_ESCAPE_PRESERVE_ENTITIES = /[<>]|&(?:#?[a-zA-Z0-9]+;)?/g;
var metadataContentTags = {
style: true,
script: true,
template: true
};
function tnValue(node) {
return node.data != null ? node.data : node.nodeValue;
}
function* serialize(rootNode) {
var node = rootNode;
var prev = node;
// This depth value will be incremented as the depth increases and
// decremented as the depth decreases. The depth of the initial node is 0.
var depth = 0;
var skip = false;
do {
if(!skip) {
switch(node.nodeType) {
// Element
case 1:
yield "<" + node.nodeName.toLowerCase();
for(var i = 0, len = node.attributes.length; i < len; i++) {
var attr = node.attributes[i];
if(attr.value) {
if (attr.name === 'href' || attr.name === 'src') {
yield ` ${attr.name}="${attr.value}"`;
}
// Not sure if this is right, can-simple-dom doesn't do this.
else if(attr.name === 'srcdoc') {
yield ` ${attr.name}="${escapeAttributeValue(attr.value)}"`;
} else {
yield ` ${attr.name}="${escapeAttributeValue(attr.value)}"`;
}
} else {
yield " " + attr.name;
}
}
yield ">";
if(!node.firstChild && node.nodeType === 1 && !isVoid(node)) {
yield "</" + node.nodeName.toLowerCase() + ">";
}
break;
// TextNode
case 3:
if(prev && prev.nodeType === 3) {
yield "<!--__DONEJS-SEP__-->";
}
if (isMetadataTag(node.parentNode)) {
yield tnValue(node);
} else {
yield giveSpace(escapeText(tnValue(node)));
}
break;
// Comments
case 8:
yield `<!--${node.data || node.nodeValue}-->`;
break;
// Doctype node
case 10:
yield `<!DOCTYPE ${node.name}>`;
break;
}
}
if(!skip && node.firstChild) {
depth++;
prev = node;
node = node.firstChild;
} else if(node.nextSibling) {
prev = node;
node = node.nextSibling;
skip = false;
} else {
node = node.parentNode;
depth--;
skip = true;
if(node.nodeType === 1 && !isVoid(node)) {
yield "</" + node.nodeName.toLowerCase() + ">";
}
}
} while ( depth > 0 );
}
function serializeToString(rootNode) {
var html = "";
for(var chunk of serialize(rootNode)) {
html += chunk;
}
return html;
}
function escapeAttributeValue(value) {
return value.replace(/"|&(?:#?[a-zA-Z0-9]+;)?/g, function(match) {
switch(match) {
case '&':
return '&';
case '\"':
return '"';
default:
return match;
}
});
}
function escapeText(value, escapeAll) {
return value.replace(escapeAll ? REG_ESCAPE_ALL : REG_ESCAPE_PRESERVE_ENTITIES, function(match) {
switch(match) {
case '&':
return '&';
case '<':
return '<';
case '>':
return '>';
default:
return match;
}
});
}
function isMetadataTag (elem) {
return !!elem && metadataContentTags[elem.nodeName.toLowerCase()];
}
function isVoid(element) {
return voidMap[element.nodeName] === true;
}
function giveSpace(txt) {
if(txt.length) {
return txt;
}
return " ";
}
exports.serialize = serialize;
exports.serializeToString = serializeToString;