You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
3.9 KiB
122 lines
3.9 KiB
6 years ago
|
var REVERSE_SOLIDUS = 0x5c; // \
|
||
|
var QUOTATION_MARK = 0x22; // "
|
||
|
var APOSTROPHE = 0x27; // '
|
||
|
var TAB = 0x09; // tab
|
||
|
var WHITESPACE = 0x20; // space
|
||
|
var AMPERSAND = 0x26;
|
||
|
var LESSTHANSIGN = 0x3C;
|
||
|
var GREATERTHANSIGN = 0x3E;
|
||
|
|
||
|
function isHex(code) {
|
||
|
return (code >= 48 && code <= 57) || // 0 .. 9
|
||
|
(code >= 65 && code <= 70) || // A .. F
|
||
|
(code >= 97 && code <= 102); // a .. f
|
||
|
}
|
||
|
|
||
|
function decodeString(str) {
|
||
|
var decoded = '';
|
||
|
var len = str.length;
|
||
|
var firstChar = str.charCodeAt(0);
|
||
|
var start = firstChar === QUOTATION_MARK || firstChar === APOSTROPHE ? 1 : 0;
|
||
|
var end = start === 1 && len > 1 && str.charCodeAt(len - 1) === firstChar ? len - 2 : len - 1;
|
||
|
|
||
|
for (var i = start; i <= end; i++) {
|
||
|
var code = str.charCodeAt(i);
|
||
|
|
||
|
if (code === REVERSE_SOLIDUS) {
|
||
|
// special case at the ending
|
||
|
if (i === end) {
|
||
|
// if the next input code point is EOF, do nothing
|
||
|
// otherwise include last quote as escaped
|
||
|
if (i !== len - 1) {
|
||
|
decoded = str.substr(i + 1);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
code = str.charCodeAt(++i);
|
||
|
|
||
|
// ignore escaped newline
|
||
|
if (code !== 0x0A && code !== 0x0C && code !== 0x0D) { // TODO: should treat a "CR/LF" pair (U+000D/U+000A) as a single white space character
|
||
|
// https://drafts.csswg.org/css-syntax/#consume-escaped-code-point
|
||
|
for (var j = 0; j < 6 && i + j <= end;) {
|
||
|
code = str.charCodeAt(i + j);
|
||
|
|
||
|
if (isHex(code)) {
|
||
|
j++;
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (j > 0) {
|
||
|
code = str.charCodeAt(i + j);
|
||
|
|
||
|
// include space into sequence
|
||
|
// TODO: add newline support
|
||
|
if (code === WHITESPACE || code === TAB) {
|
||
|
j++;
|
||
|
}
|
||
|
|
||
|
code = parseInt(str.substr(i, j), 16);
|
||
|
|
||
|
if (
|
||
|
(code === 0) || // If this number is zero,
|
||
|
(code >= 0xD800 && code <= 0xDFFF) || // or is for a surrogate,
|
||
|
(code > 0x10FFFF) // or is greater than the maximum allowed code point
|
||
|
) {
|
||
|
// ... return U+FFFD REPLACEMENT CHARACTER
|
||
|
code = 0xFFFD;
|
||
|
}
|
||
|
|
||
|
// FIXME: code above 0xFFFF will be converted incorrectly,
|
||
|
// better to use String.fromCharPoint() but it lack of support by engines
|
||
|
decoded += String.fromCharCode(code);
|
||
|
i += j - 1;
|
||
|
} else {
|
||
|
decoded += str.charAt(i);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
decoded += str.charAt(i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return decoded;
|
||
|
}
|
||
|
|
||
|
function encodeString(str, apostrophe) {
|
||
|
var quote = apostrophe ? '\'' : '"';
|
||
|
var quoteCode = apostrophe ? APOSTROPHE : QUOTATION_MARK;
|
||
|
var encoded = quote;
|
||
|
var wsBeforeHexIsNeeded = false;
|
||
|
|
||
|
for (var i = 0; i < str.length; i++) {
|
||
|
var code = str.charCodeAt(i);
|
||
|
|
||
|
if (code <= 0x1F || code === AMPERSAND || code === LESSTHANSIGN || code === GREATERTHANSIGN) {
|
||
|
encoded += '\\' + code.toString(16);
|
||
|
wsBeforeHexIsNeeded = true;
|
||
|
} else if (code === REVERSE_SOLIDUS || code === quoteCode) {
|
||
|
encoded += '\\' + str.charAt(i);
|
||
|
wsBeforeHexIsNeeded = false;
|
||
|
} else {
|
||
|
if (wsBeforeHexIsNeeded && isHex(code)) {
|
||
|
encoded += ' ';
|
||
|
}
|
||
|
|
||
|
encoded += str.charAt(i);
|
||
|
wsBeforeHexIsNeeded = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
encoded += quote;
|
||
|
|
||
|
return encoded;
|
||
|
}
|
||
|
|
||
|
module.exports = {
|
||
|
decode: decodeString,
|
||
|
encode: encodeString
|
||
|
};
|