-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathencoder.js
141 lines (115 loc) · 3.96 KB
/
encoder.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
/* HTML character entity encoder and decoder */
var named = require('./named.json'), // named entities
special = require('./special.json'); // special numerical entities
// encode characters in the set &<>"' as character entities
exports.encode = function(str, ascii) {
// <bool> ascii: also encode non-ASCII and non-printable characters
var p = 0, s, res = '', pos, len = str.length;
for (pos = 0; pos < len; pos++) {
c = str.charAt(pos);
if (c == '&') {
s = '&';
}
else if (c == '<') {
s = '<';
}
else if (c == '>') {
s = '>';
}
else if (c == '"') {
s = '"'
}
else if (c == '\'') {
s = ''';
}
else if (ascii === true && (c > "~" || c < " ")) {
s = '&#' + c.charCodeAt(0) + ';';
}
else {
continue;
}
res += str.slice(p, pos) + s, p = pos + 1;
}
return res + str.substr(p);
};
// decode numerical and named character entities
exports.decode = function(str, attr) {
// <bool> attr: str is an html attribute value (see below)
var pos = 0, res = '',
p, c, s, base;
loop: while (pos < str.length) {
p = str.indexOf('&', pos);
if (p == -1) {
res += str.substr(pos);
break;
}
res += str.slice(pos, p);
c = str.charAt(++p);
if (c == '#') {
// numerical entity
c = str.charAt(++p);
if (s = c, c == 'x' || c == 'X') {
// hexadecimal
pos = ++p, base = 16; // consume 'x'
while(c = str.charAt(p),
c >= '0' && c <= '9' || c >= 'a' && c <= 'f'
|| c >= 'A' && c <= 'F') {
++p;
}
}
else {
// decimal
pos = p, base = 10;
while (c >= '0' && c <= '9') {
c = str.charAt(++p);
}
}
if (pos == p) {
// no characters consumed, abort
res += base == 10 ? '&#' : '&#' + s;
continue;
}
i = parseInt(str.slice(pos, p), base);
if (special[i] !== void 0) {
res += special[i];
}
else if (i >= 0xD800 && i <= 0xDFFF || i > 0x10FFFF) {
// illegal, issue replacement character
res += '\uFFFD';
}
else {
res += String.fromCharCode(i);
}
// consume ';' and set position
pos = str.charAt(++p) == ';' ? p + 1 : p;
}
else {
// named entity
pos = p;
// maximum length of an entity that does not end in a semicolon: 6
p = str.indexOf(';', pos) + 1 || pos + 6;
do {
// the minimum length of an entity is 2
if (p < pos + 2) {
// no match
res += '&';
continue loop;
}
s = str.slice(pos, p--);
} while (named[s] === void 0);
/* "If the character reference is being consumed as part of an
attribute, and the last character matched is not a SEMICOLON
character, and the next character is either a EQUALS SIGN
character or an alphanumeric ASCII character, then, for
historical reasons, all the characters that were matched after
the AMPERSAND character must be unconsumed."
*/
res += attr === true && str.charAt(p) != ';'
&& (c = str.charAt(p + 1), c >= 'a' && c <= 'z'
|| c >= 'A' && c <= 'Z' || c == '=' || c >= '0' && c <= '9')
? '&' + s : named[s];
pos = p + 1;
}
}
return res;
};