|
'use strict'; |
|
|
|
const charCodeDefinitions = require('./char-code-definitions.cjs'); |
|
|
|
function getCharCode(source, offset) { |
|
return offset < source.length ? source.charCodeAt(offset) : 0; |
|
} |
|
|
|
function getNewlineLength(source, offset, code) { |
|
if (code === 13 && getCharCode(source, offset + 1) === 10 ) { |
|
return 2; |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
function cmpChar(testStr, offset, referenceCode) { |
|
let code = testStr.charCodeAt(offset); |
|
|
|
|
|
if (charCodeDefinitions.isUppercaseLetter(code)) { |
|
code = code | 32; |
|
} |
|
|
|
return code === referenceCode; |
|
} |
|
|
|
function cmpStr(testStr, start, end, referenceStr) { |
|
if (end - start !== referenceStr.length) { |
|
return false; |
|
} |
|
|
|
if (start < 0 || end > testStr.length) { |
|
return false; |
|
} |
|
|
|
for (let i = start; i < end; i++) { |
|
const referenceCode = referenceStr.charCodeAt(i - start); |
|
let testCode = testStr.charCodeAt(i); |
|
|
|
|
|
if (charCodeDefinitions.isUppercaseLetter(testCode)) { |
|
testCode = testCode | 32; |
|
} |
|
|
|
if (testCode !== referenceCode) { |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
function findWhiteSpaceStart(source, offset) { |
|
for (; offset >= 0; offset--) { |
|
if (!charCodeDefinitions.isWhiteSpace(source.charCodeAt(offset))) { |
|
break; |
|
} |
|
} |
|
|
|
return offset + 1; |
|
} |
|
|
|
function findWhiteSpaceEnd(source, offset) { |
|
for (; offset < source.length; offset++) { |
|
if (!charCodeDefinitions.isWhiteSpace(source.charCodeAt(offset))) { |
|
break; |
|
} |
|
} |
|
|
|
return offset; |
|
} |
|
|
|
function findDecimalNumberEnd(source, offset) { |
|
for (; offset < source.length; offset++) { |
|
if (!charCodeDefinitions.isDigit(source.charCodeAt(offset))) { |
|
break; |
|
} |
|
} |
|
|
|
return offset; |
|
} |
|
|
|
|
|
function consumeEscaped(source, offset) { |
|
|
|
|
|
offset += 2; |
|
|
|
|
|
if (charCodeDefinitions.isHexDigit(getCharCode(source, offset - 1))) { |
|
|
|
|
|
for (const maxOffset = Math.min(source.length, offset + 5); offset < maxOffset; offset++) { |
|
if (!charCodeDefinitions.isHexDigit(getCharCode(source, offset))) { |
|
break; |
|
} |
|
} |
|
|
|
|
|
const code = getCharCode(source, offset); |
|
if (charCodeDefinitions.isWhiteSpace(code)) { |
|
offset += getNewlineLength(source, offset, code); |
|
} |
|
} |
|
|
|
return offset; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function consumeName(source, offset) { |
|
|
|
|
|
for (; offset < source.length; offset++) { |
|
const code = source.charCodeAt(offset); |
|
|
|
|
|
if (charCodeDefinitions.isName(code)) { |
|
|
|
continue; |
|
} |
|
|
|
|
|
if (charCodeDefinitions.isValidEscape(code, getCharCode(source, offset + 1))) { |
|
|
|
offset = consumeEscaped(source, offset) - 1; |
|
continue; |
|
} |
|
|
|
|
|
|
|
break; |
|
} |
|
|
|
return offset; |
|
} |
|
|
|
|
|
function consumeNumber(source, offset) { |
|
let code = source.charCodeAt(offset); |
|
|
|
|
|
|
|
if (code === 0x002B || code === 0x002D) { |
|
code = source.charCodeAt(offset += 1); |
|
} |
|
|
|
|
|
if (charCodeDefinitions.isDigit(code)) { |
|
offset = findDecimalNumberEnd(source, offset + 1); |
|
code = source.charCodeAt(offset); |
|
} |
|
|
|
|
|
if (code === 0x002E && charCodeDefinitions.isDigit(source.charCodeAt(offset + 1))) { |
|
|
|
|
|
offset += 2; |
|
|
|
|
|
|
|
|
|
|
|
|
|
offset = findDecimalNumberEnd(source, offset); |
|
} |
|
|
|
|
|
|
|
if (cmpChar(source, offset, 101 )) { |
|
let sign = 0; |
|
code = source.charCodeAt(offset + 1); |
|
|
|
|
|
if (code === 0x002D || code === 0x002B) { |
|
sign = 1; |
|
code = source.charCodeAt(offset + 2); |
|
} |
|
|
|
|
|
if (charCodeDefinitions.isDigit(code)) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
offset = findDecimalNumberEnd(source, offset + 1 + sign + 1); |
|
} |
|
} |
|
|
|
return offset; |
|
} |
|
|
|
|
|
|
|
|
|
function consumeBadUrlRemnants(source, offset) { |
|
|
|
for (; offset < source.length; offset++) { |
|
const code = source.charCodeAt(offset); |
|
|
|
|
|
|
|
if (code === 0x0029) { |
|
|
|
offset++; |
|
break; |
|
} |
|
|
|
if (charCodeDefinitions.isValidEscape(code, getCharCode(source, offset + 1))) { |
|
|
|
|
|
|
|
|
|
offset = consumeEscaped(source, offset); |
|
} |
|
} |
|
|
|
return offset; |
|
} |
|
|
|
|
|
|
|
function decodeEscaped(escaped) { |
|
|
|
if (escaped.length === 1 && !charCodeDefinitions.isHexDigit(escaped.charCodeAt(0))) { |
|
return escaped[0]; |
|
} |
|
|
|
|
|
let code = parseInt(escaped, 16); |
|
|
|
if ( |
|
(code === 0) || |
|
(code >= 0xD800 && code <= 0xDFFF) || |
|
(code > 0x10FFFF) |
|
) { |
|
|
|
code = 0xFFFD; |
|
} |
|
|
|
|
|
return String.fromCodePoint(code); |
|
} |
|
|
|
exports.cmpChar = cmpChar; |
|
exports.cmpStr = cmpStr; |
|
exports.consumeBadUrlRemnants = consumeBadUrlRemnants; |
|
exports.consumeEscaped = consumeEscaped; |
|
exports.consumeName = consumeName; |
|
exports.consumeNumber = consumeNumber; |
|
exports.decodeEscaped = decodeEscaped; |
|
exports.findDecimalNumberEnd = findDecimalNumberEnd; |
|
exports.findWhiteSpaceEnd = findWhiteSpaceEnd; |
|
exports.findWhiteSpaceStart = findWhiteSpaceStart; |
|
exports.getNewlineLength = getNewlineLength; |
|
|