whatwg-url
Advanced tools
Comparing version 0.2.1 to 0.3.0
681
lib/url.js
@@ -9,3 +9,3 @@ "use strict"; | ||
const relativeSchemas = { | ||
const specialSchemas = { | ||
"ftp": "21", | ||
@@ -39,16 +39,17 @@ "file": null, | ||
RELATIVE: 4, | ||
RELATIVE_OR_AUTHORITY: 5, | ||
AUTHORITY_FIRST_SLASH: 6, | ||
SCHEME_DATA: 7, | ||
SPECIAL_RELATIVE_OR_AUTHORITY: 5, | ||
SPECIAL_AUTHORITY_SLASHES: 6, | ||
NON_RELATIVE_PATH: 7, | ||
QUERY: 8, | ||
FRAGMENT: 9, | ||
AUTHORITY_IGNORE_SLASHES: 10, | ||
SPECIAL_AUTHORITY_IGNORE_SLASHES: 10, | ||
RELATIVE_SLASH: 11, | ||
RELATIVE_PATH: 12, | ||
PATH: 12, | ||
FILE_HOST: 13, | ||
AUTHORITY: 14, | ||
HOST: 15, | ||
RELATIVE_PATH_START: 16, | ||
PATH_START: 16, | ||
HOST_NAME: 17, | ||
PORT: 18 | ||
PORT: 18, | ||
PATH_OR_AUTHORITY: 19 | ||
}; | ||
@@ -77,2 +78,11 @@ | ||
function percentEncode(c) { | ||
let hex = c.toString(16).toUpperCase(); | ||
if (hex.length === 1) { | ||
hex = "0" + hex; | ||
} | ||
return "%" + hex; | ||
} | ||
const invalidCodePoint = String.fromCodePoint(65533); | ||
@@ -88,8 +98,3 @@ function utf8PercentEncode(c) { | ||
for (let i = 0; i < buf.length; ++i) { | ||
let hex = buf[i].toString(16).toUpperCase(); | ||
if (hex.length === 1) { | ||
hex = "0" + hex; | ||
} | ||
str += "%" + hex; | ||
str += percentEncode(buf[i]); | ||
} | ||
@@ -113,3 +118,4 @@ | ||
if (c <= 0x20 || c >= 0x7E || c_str === "\"" || c_str === "#" || | ||
c_str === "<" || c_str === ">" || c_str === "?" || c_str === "`") { | ||
c_str === "<" || c_str === ">" || c_str === "?" || c_str === "`" || | ||
c_str === "{" || c_str === "}") { | ||
return utf8PercentEncode(c_str); | ||
@@ -125,2 +131,3 @@ } else { | ||
c_str === "<" || c_str === ">" || c_str === "?" || c_str === "`" || | ||
c_str === "{" || c_str === "}" || | ||
c_str === "/" || c_str === "@" || c_str === "\\") { | ||
@@ -137,2 +144,3 @@ return utf8PercentEncode(c_str); | ||
c_str === "<" || c_str === ">" || c_str === "?" || c_str === "`" || | ||
c_str === "{" || c_str === "}" || | ||
c_str === "/" || c_str === "@" || c_str === "\\" || c_str === ":") { | ||
@@ -145,2 +153,76 @@ return utf8PercentEncode(c_str); | ||
function parseIPv4Number(input) { | ||
let R = 10; | ||
if (input.length >= 2 && input.charAt(0) === "0" && input.charAt(1).toLowerCase() === "x") { | ||
input = input.substring(2); | ||
R = 16; | ||
} else if (input.length >= 1 && input.charAt(0) === "0") { | ||
input = input.substring(1); | ||
R = 8; | ||
} | ||
const regex = R === 10 ? /[^0-9]/ : (R === 16 ? /[^0-9A-Fa-f]/ : /[^0-7]/); | ||
if (regex.test(input)) { | ||
return null; | ||
} | ||
return parseInt(input, R); | ||
} | ||
function parseIPv4(input) { | ||
let parts = input.split("."); | ||
if (parts[parts.length - 1] === "") { | ||
parts.pop(); | ||
} | ||
if (parts.length > 4) { | ||
return input; | ||
} | ||
let numbers = []; | ||
for (const part of parts) { | ||
const n = parseIPv4Number(part); | ||
if (n === null) { | ||
return input; | ||
} | ||
numbers.push(n); | ||
} | ||
for (let i = 0; i < numbers.length - 1; ++i) { | ||
if (numbers[i] > 255) { | ||
throw new TypeError("Invalid Host"); | ||
} | ||
} | ||
if (numbers[numbers.length - 1] >= Math.pow(256, 5 - numbers.length)) { | ||
throw new TypeError("Invalid Host"); | ||
} | ||
let ipv4 = numbers.pop(); | ||
let counter = 0; | ||
for (const n of numbers) { | ||
ipv4 += n * Math.pow(256, 3 - counter); | ||
++counter; | ||
} | ||
return ipv4; | ||
} | ||
function serializeIPv4(address) { | ||
let output = ""; | ||
let n = address; | ||
for (let i = 0; i < 4; ++i) { | ||
output = String(n % 256) + output; | ||
if (i !== 3) { | ||
output = "." + output; | ||
} | ||
n = Math.floor(n / 256); | ||
} | ||
return output; | ||
} | ||
function parseIPv6(input) { | ||
@@ -247,3 +329,7 @@ const ip = [0, 0, 0, 0, 0, 0, 0, 0]; | ||
} | ||
++pointer; | ||
if (input[pointer] !== undefined) { | ||
++pointer; | ||
} | ||
if (dotsSeen === 3 && input[pointer] !== undefined) { | ||
@@ -273,7 +359,29 @@ throw new TypeError("Invalid Host"); | ||
function parseHost(input, isUnicode) { | ||
if (input === "") { | ||
throw new TypeError("Invalid Host"); | ||
function serializeIPv6(address) { | ||
let output = ""; | ||
const seqResult = findLongestZeroSequence(address); | ||
const compressPtr = seqResult.idx; | ||
for (var i = 0; i < address.length; ++i) { | ||
if (compressPtr === i) { | ||
if (i === 0) { | ||
output += "::"; | ||
} else { | ||
output += ":"; | ||
} | ||
i += seqResult.len - 1; | ||
continue; | ||
} | ||
output += address[i].toString(16); | ||
if (i !== address.length - 1) { | ||
output += ":"; | ||
} | ||
} | ||
return output; | ||
} | ||
function parseHost(input, isUnicode) { | ||
if (input[0] === "[") { | ||
@@ -284,3 +392,3 @@ if (input[input.length - 1] !== "]") { | ||
return serializeHost(parseIPv6(input.substr(1, input.length - 2))); | ||
return parseIPv6(input.substring(1, input.length - 1)); | ||
} | ||
@@ -304,2 +412,7 @@ | ||
let ipv4Host = parseIPv4(asciiDomain); | ||
if (typeof ipv4Host === "number") { | ||
return ipv4Host; | ||
} | ||
return isUnicode ? tr46.toUnicode(asciiDomain, false).domain : asciiDomain; | ||
@@ -338,4 +451,4 @@ } | ||
function serializeHost(host) { | ||
if (host === null) { | ||
return ""; | ||
if (typeof host === "number") { | ||
return serializeIPv4(host); | ||
} | ||
@@ -345,25 +458,3 @@ | ||
if (host instanceof Array) { | ||
let output = ""; | ||
const seqResult = findLongestZeroSequence(host); | ||
const compressPtr = seqResult.idx; | ||
for (var i = 0; i < host.length; ++i) { | ||
if (compressPtr === i) { | ||
if (i === 0) { | ||
output += "::"; | ||
} else { | ||
output += ":"; | ||
} | ||
i += seqResult.len - 1; | ||
continue; | ||
} | ||
output += host[i].toString(16); | ||
if (i !== host.length - 1) { | ||
output += ":"; | ||
} | ||
} | ||
return "[" + output + "]"; | ||
return "[" + serializeIPv6(host) + "]"; | ||
} | ||
@@ -374,2 +465,6 @@ | ||
function trimControlChars(url) { | ||
return url.replace(/^[\u0000-\u001F\u0020]+|[\u0000-\u001F\u0020]+$/g, ""); | ||
} | ||
function URLStateMachine(input, base, encoding_override, url, state_override) { | ||
@@ -386,3 +481,2 @@ this.pointer = 0; | ||
scheme: "", | ||
scheme_data: "", | ||
username: "", | ||
@@ -396,6 +490,6 @@ password: null, | ||
isRelative: false | ||
nonRelative: false | ||
}; | ||
this.input = this.input.trim(); | ||
this.input = trimControlChars(this.input); | ||
} | ||
@@ -440,2 +534,10 @@ | ||
} else if (c_str === ":") { | ||
if (this.state_override) { | ||
// TODO: XOR | ||
if (specialSchemas[this.url.scheme] !== undefined && !specialSchemas[this.buffer]) { | ||
return false; | ||
} else if (specialSchemas[this.url.scheme] === undefined && specialSchemas[this.buffer]) { | ||
return false; | ||
} | ||
} | ||
this.url.scheme = this.buffer; | ||
@@ -446,13 +548,16 @@ this.buffer = ""; | ||
} | ||
if (relativeSchemas[this.url.scheme] !== undefined) { | ||
this.url.isRelative = true; | ||
} | ||
if (this.url.scheme === "file") { | ||
this.state = STATES.RELATIVE; | ||
} else if (this.url.isRelative && this.base !== null && this.base.scheme === this.url.scheme) { | ||
this.state = STATES.RELATIVE_OR_AUTHORITY; | ||
} else if (this.url.isRelative) { | ||
this.state = STATES.AUTHORITY_FIRST_SLASH; | ||
} else if (specialSchemas[this.url.scheme] !== undefined && this.base !== null && | ||
this.base.scheme === this.url.scheme) { | ||
this.state = STATES.SPECIAL_RELATIVE_OR_AUTHORITY; | ||
} else if (specialSchemas[this.url.scheme] !== undefined) { | ||
this.state = STATES.SPECIAL_AUTHORITY_SLASHES; | ||
} else if (at(this.input, this.pointer + 1) === "/") { | ||
this.state = STATES.PATH_OR_AUTHORITY; | ||
++this.pointer; | ||
} else { | ||
this.state = STATES.SCHEME_DATA; | ||
this.url.nonRelative = true; | ||
this.url.path.push(""); | ||
this.state = STATES.NON_RELATIVE_PATH; | ||
} | ||
@@ -463,4 +568,2 @@ } else if (!this.state_override) { | ||
this.pointer = -1; | ||
} else if (isNaN(c)) { | ||
return false; | ||
} else { | ||
@@ -474,4 +577,11 @@ this.parse_error = true; | ||
//jshint unused:false | ||
if (this.base === null || relativeSchemas[this.base.scheme] === undefined) { | ||
if (this.base === null || (this.base.nonRelative && c_str !== "#")) { | ||
throw new TypeError("Invalid URL"); | ||
} else if (this.base.nonRelative && c_str === "#") { | ||
this.url.scheme = this.base.scheme; | ||
this.url.path = this.base.path.slice(); | ||
this.url.query = this.base.query; | ||
this.url.fragment = ""; | ||
this.url.nonRelative = true; | ||
this.state = STATES.FRAGMENT; | ||
} else { | ||
@@ -483,4 +593,23 @@ this.state = STATES.RELATIVE; | ||
URLStateMachine.prototype["parse" + STATES.SPECIAL_RELATIVE_OR_AUTHORITY] = function parseScheme(c, c_str) { | ||
if (c_str === "/" && at(this.input, this.pointer + 1) === "/") { | ||
this.state = STATES.SPECIAL_AUTHORITY_IGNORE_SLASHES; | ||
++this.pointer; | ||
} else { | ||
this.parse_error = true; | ||
this.state = STATES.RELATIVE; | ||
--this.pointer; | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.PATH_OR_AUTHORITY] = function parseScheme(c, c_str) { | ||
if (c_str === "/") { | ||
this.state = STATES.AUTHORITY; | ||
} else { | ||
this.state = STATES.PATH; | ||
--this.pointer; | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.RELATIVE] = function parseScheme(c, c_str) { | ||
this.url.isRelative = true; | ||
if (this.url.scheme !== "file") { | ||
@@ -490,2 +619,4 @@ this.url.scheme = this.base.scheme; | ||
if (isNaN(c)) { | ||
this.url.username = this.base.username; | ||
this.url.password = this.base.password; | ||
this.url.host = this.base.host; | ||
@@ -495,8 +626,7 @@ this.url.port = this.base.port; | ||
this.url.query = this.base.query; | ||
} else if (c_str === "\\" || c_str === "/") { | ||
if (c_str === "\\") { | ||
this.parse_error = true; | ||
} | ||
} else if (c_str === "/") { | ||
this.state = STATES.RELATIVE_SLASH; | ||
} else if (c_str === "?") { | ||
this.url.username = this.base.username; | ||
this.url.password = this.base.password; | ||
this.url.host = this.base.host; | ||
@@ -508,2 +638,4 @@ this.url.port = this.base.port; | ||
} else if (c_str === "#") { | ||
this.url.username = this.base.username; | ||
this.url.password = this.base.password; | ||
this.url.host = this.base.host; | ||
@@ -515,2 +647,5 @@ this.url.port = this.base.port; | ||
this.state = STATES.FRAGMENT; | ||
} else if (specialSchemas[this.url.scheme] !== undefined && c_str === "\\") { | ||
this.parse_error = true; | ||
this.state = STATES.RELATIVE_SLASH; | ||
} else { | ||
@@ -522,2 +657,4 @@ let nextChar = at(this.input, this.pointer + 1); | ||
nextNextChar === "?" || nextNextChar === "#")) { | ||
this.url.username = this.base.username; | ||
this.url.password = this.base.password; | ||
this.url.host = this.base.host; | ||
@@ -528,3 +665,3 @@ this.url.port = this.base.port; | ||
this.state = STATES.RELATIVE_PATH; | ||
this.state = STATES.PATH; | ||
--this.pointer; | ||
@@ -534,96 +671,4 @@ } | ||
URLStateMachine.prototype["parse" + STATES.RELATIVE_OR_AUTHORITY] = function parseScheme(c, c_str) { | ||
if (c_str === "/" && at(this.input, this.pointer + 1) === "/") { | ||
this.state = STATES.AUTHORITY_IGNORE_SLASHES; | ||
++this.pointer; | ||
} else { | ||
this.parse_error = true; | ||
this.state = STATES.RELATIVE; | ||
--this.pointer; | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.AUTHORITY_FIRST_SLASH] = function parseScheme(c, c_str) { | ||
if (c_str === "/" && at(this.input, this.pointer + 1) === "/") { // patching AUTHORITY_SECOND_SLASH out here | ||
++this.pointer; | ||
this.state = STATES.AUTHORITY_IGNORE_SLASHES; | ||
} else { | ||
this.parse_error = true; | ||
this.state = STATES.AUTHORITY_IGNORE_SLASHES; | ||
--this.pointer; | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.SCHEME_DATA] = function parseScheme(c, c_str) { | ||
if (c_str === "?") { | ||
this.url.query = ""; | ||
this.state = STATES.QUERY; | ||
} else if (c_str === "#") { | ||
this.url.fragment = ""; | ||
this.state = STATES.FRAGMENT; | ||
} else { | ||
//TODO: If c is not the EOF code point, not a URL code point, and not "%", parse error. | ||
if (c_str === "%" && | ||
(!isASCIIHex(at(this.input, this.pointer + 1)) || | ||
!isASCIIHex(at(this.input, this.pointer + 2)))) { | ||
this.parse_error = true; | ||
} else if (c !== undefined && c !== 0x9 && c !== 0xA && c !== 0xD) { | ||
this.url.scheme_data += simpleEncode(c); | ||
} | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.QUERY] = function parseScheme(c, c_str) { | ||
if (isNaN(c) || (!this.state_override && c_str === "#")) { | ||
if (!this.url.isRelative || this.url.scheme === "ws" || this.url.scheme === "wss") { | ||
this.encoding_override = "utf-8"; | ||
} | ||
this.buffer = this.buffer; //TODO: Use encoding override instead | ||
this.url.query += encodeURI(this.buffer); | ||
this.buffer = ""; | ||
if (c_str === "#") { | ||
this.url.fragment = ""; | ||
this.state = STATES.FRAGMENT; | ||
} | ||
} else if (c === 0x9 || c === 0xA || c === 0xD) { | ||
this.parse_error = true; | ||
} else { | ||
//TODO: If c is not a URL code point and not "%", parse error. | ||
if (c_str === "%" && | ||
(!isASCIIHex(at(this.input, this.pointer + 1)) || | ||
!isASCIIHex(at(this.input, this.pointer + 2)))) { | ||
this.parse_error = true; | ||
} else { | ||
this.buffer += c_str; | ||
} | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.FRAGMENT] = function parseScheme(c, c_str) { | ||
if (isNaN(c)) { // do nothing | ||
} else if (c === 0x0 || c === 0x9 || c === 0xA || c === 0xD) { | ||
this.parse_error = true; | ||
} else { | ||
//TODO: If c is not a URL code point and not "%", parse error. | ||
if (c_str === "%" && | ||
(!isASCIIHex(at(this.input, this.pointer + 1)) || | ||
!isASCIIHex(at(this.input, this.pointer + 2)))) { | ||
this.parse_error = true; | ||
} else { | ||
this.url.fragment += c_str; | ||
} | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.AUTHORITY_IGNORE_SLASHES] = function parseScheme(c, c_str) { | ||
if (c_str !== "/" && c_str !== "\\") { | ||
this.state = STATES.AUTHORITY; | ||
--this.pointer; | ||
} else { | ||
this.parse_error = true; | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.RELATIVE_SLASH] = function parseScheme(c, c_str) { | ||
if (c_str === "/" || c_str === "\\") { | ||
if (c_str === "/" || (specialSchemas[this.url.scheme] !== undefined && c_str === "\\")) { | ||
if (c_str === "\\") { | ||
@@ -635,10 +680,12 @@ this.parse_error = true; | ||
} else { | ||
this.state = STATES.AUTHORITY_IGNORE_SLASHES; | ||
this.state = STATES.SPECIAL_AUTHORITY_IGNORE_SLASHES; | ||
} | ||
} else { | ||
if (this.url.scheme !== "file") { | ||
this.url.username = this.base.username; | ||
this.url.password = this.base.password; | ||
this.url.host = this.base.host; | ||
this.url.port = this.base.port; | ||
} | ||
this.state = STATES.RELATIVE_PATH; | ||
this.state = STATES.PATH; | ||
--this.pointer; | ||
@@ -648,65 +695,19 @@ } | ||
URLStateMachine.prototype["parse" + STATES.RELATIVE_PATH] = function parseScheme(c, c_str) { | ||
if (isNaN(c) || c_str === "/" || c_str === "\\" || (!this.state_override && (c_str === "?" || c_str === "#"))) { | ||
if (c_str === "\\") { | ||
this.parse_error = true; | ||
} | ||
this.buffer = bufferReplacement[this.buffer.toLowerCase()] || this.buffer; | ||
if (this.buffer === "..") { | ||
this.url.path.pop(); | ||
if (c_str !== "/" && c_str !== "\\") { | ||
this.url.path.push(""); | ||
} | ||
} else if (this.buffer === "." && c_str !== "/" && c_str !== "\\") { | ||
this.url.path.push(""); | ||
} else if (this.buffer !== ".") { | ||
if (this.url.scheme === "file" && this.url.path.length === 0 && | ||
this.buffer.length === 2 && isASCIIAlpha(this.buffer.codePointAt(0)) && this.buffer[1] === "|") { | ||
this.buffer = this.buffer[0] + ":"; | ||
} | ||
this.url.path.push(this.buffer); | ||
} | ||
this.buffer = ""; | ||
if (c_str === "?") { | ||
this.url.query = ""; | ||
this.state = STATES.QUERY; | ||
} | ||
if (c_str === "#") { | ||
this.url.fragment = ""; | ||
this.state = STATES.FRAGMENT; | ||
} | ||
} else if (c === 0x9 || c === 0xA || c === 0xD) { | ||
URLStateMachine.prototype["parse" + STATES.SPECIAL_AUTHORITY_SLASHES] = function parseScheme(c, c_str) { | ||
if (c_str === "/" && at(this.input, this.pointer + 1) === "/") { | ||
this.state = STATES.SPECIAL_AUTHORITY_IGNORE_SLASHES; | ||
++this.pointer; | ||
} else { | ||
this.parse_error = true; | ||
} else { | ||
//TODO:If c is not a URL code point and not "%", parse error. | ||
if (c_str === "%" && | ||
(!isASCIIHex(at(this.input, this.pointer + 1)) || | ||
!isASCIIHex(at(this.input, this.pointer + 2)))) { | ||
this.parse_error = true; | ||
} | ||
this.buffer += defaultEncode(c); | ||
this.state = STATES.SPECIAL_AUTHORITY_IGNORE_SLASHES; | ||
--this.pointer; | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.FILE_HOST] = function parseScheme(c, c_str) { | ||
if (isNaN(c) || c_str === "/" || c_str === "\\" || c_str === "?" || c_str === "#") { | ||
URLStateMachine.prototype["parse" + STATES.SPECIAL_AUTHORITY_IGNORE_SLASHES] = function parseScheme(c, c_str) { | ||
if (c_str !== "/" && c_str !== "\\") { | ||
this.state = STATES.AUTHORITY; | ||
--this.pointer; | ||
// don't need to count symbols here since we check ASCII values | ||
if (this.buffer.length === 2 && | ||
isASCIIAlpha(this.buffer.codePointAt(0)) && (this.buffer[1] === ":" || this.buffer[1] === "|")) { | ||
this.state = STATES.RELATIVE_PATH; | ||
} else if (this.buffer === "") { | ||
this.state = STATES.RELATIVE_PATH_START; | ||
} else { | ||
let host = parseHost(this.buffer); | ||
this.url.host = host; | ||
this.buffer = ""; | ||
this.state = STATES.RELATIVE_PATH_START; | ||
} | ||
} else if (c === 0x9 || c === 0xA || c === 0xD) { | ||
} else { | ||
this.parse_error = true; | ||
} else { | ||
this.buffer += c_str; | ||
} | ||
@@ -717,4 +718,4 @@ }; | ||
if (c_str === "@") { | ||
this.parse_error = true; | ||
if (this.at_flag) { | ||
this.parse_error = true; | ||
this.buffer = "%40" + this.buffer; | ||
@@ -733,11 +734,5 @@ } | ||
if (c === 0x9 || c === 0xA || c === 0xD) { | ||
this.parse_error = true; | ||
continue; | ||
} | ||
//TODO: If code point is not a URL code point and not "%", parse error. | ||
if (c_str === "%" && | ||
(!isASCIIHex(this.buffer.codePointAt(pointer + 1)) || | ||
!isASCIIHex(this.buffer.codePointAt(pointer + 2)))) { | ||
this.parse_error = true; | ||
} | ||
if (c_str === ":" && this.url.password === null) { | ||
@@ -748,9 +743,10 @@ this.url.password = ""; | ||
if (this.url.password !== null) { | ||
this.url.password += simpleEncode(c); | ||
this.url.password += passwordEncode(c); | ||
} else { | ||
this.url.username += simpleEncode(c); | ||
this.url.username += usernameEncode(c); | ||
} | ||
} | ||
this.buffer = ""; | ||
} else if (isNaN(c) || c_str === "/" || c_str === "\\" || c_str === "?" || c_str === "#") { | ||
} else if (isNaN(c) || c_str === "/" || c_str === "?" || c_str === "#" || | ||
(specialSchemas[this.url.scheme] !== undefined && c_str === "\\")) { | ||
this.pointer -= countSymbols(this.buffer) + 1; | ||
@@ -767,2 +763,6 @@ this.buffer = ""; | ||
if (c_str === ":" && !this.arr_flag) { | ||
if (specialSchemas[this.url.scheme] !== undefined && this.buffer === "") { | ||
throw new TypeError("Invalid URL"); | ||
} | ||
let host = parseHost(this.buffer); | ||
@@ -775,8 +775,13 @@ this.url.host = host; | ||
} | ||
} else if (isNaN(c) || c_str === "/" || c_str === "\\" || c_str === "?" || c_str === "#") { | ||
} else if (isNaN(c) || c_str === "/" || c_str === "?" || c_str === "#" || | ||
(specialSchemas[this.url.scheme] !== undefined && c_str === "\\")) { | ||
--this.pointer; | ||
if (specialSchemas[this.url.scheme] !== undefined && this.buffer === "") { | ||
throw new TypeError("Invalid URL"); | ||
} | ||
let host = parseHost(this.buffer); | ||
this.url.host = host; | ||
this.buffer = ""; | ||
this.state = STATES.RELATIVE_PATH_START; | ||
this.state = STATES.PATH_START; | ||
if (this.state_override) { | ||
@@ -797,10 +802,22 @@ return false; | ||
URLStateMachine.prototype["parse" + STATES.RELATIVE_PATH_START] = function parseScheme(c, c_str) { | ||
if (c_str === "\\") { | ||
URLStateMachine.prototype["parse" + STATES.FILE_HOST] = function parseScheme(c, c_str) { | ||
if (isNaN(c) || c_str === "/" || c_str === "\\" || c_str === "?" || c_str === "#") { | ||
--this.pointer; | ||
// don't need to count symbols here since we check ASCII values | ||
if (this.buffer.length === 2 && | ||
isASCIIAlpha(this.buffer.codePointAt(0)) && (this.buffer[1] === ":" || this.buffer[1] === "|")) { | ||
this.state = STATES.PATH; | ||
} else if (this.buffer === "") { | ||
this.state = STATES.PATH_START; | ||
} else { | ||
let host = parseHost(this.buffer); | ||
this.url.host = host; | ||
this.buffer = ""; | ||
this.state = STATES.PATH_START; | ||
} | ||
} else if (c === 0x9 || c === 0xA || c === 0xD) { | ||
this.parse_error = true; | ||
} else { | ||
this.buffer += c_str; | ||
} | ||
this.state = STATES.RELATIVE_PATH; | ||
if (c_str !== "\\" && c_str !== "/") { | ||
--this.pointer; | ||
} | ||
}; | ||
@@ -811,7 +828,8 @@ | ||
this.buffer += c_str; | ||
} else if (isNaN(c) || c_str === "/" || c_str === "\\" || c_str === "?" || c_str === "#") { | ||
} else if (isNaN(c) || c_str === "/" || c_str === "?" || c_str === "#" || | ||
(specialSchemas[this.url.scheme] !== undefined && c_str === "\\")) { | ||
while (this.buffer[0] === "0" && this.buffer.length > 1) { | ||
this.buffer = this.buffer.substr(1); | ||
} | ||
if (this.buffer === relativeSchemas[this.url.scheme]) { | ||
if (this.buffer === specialSchemas[this.url.scheme]) { | ||
this.buffer = ""; | ||
@@ -824,3 +842,3 @@ } | ||
this.buffer = ""; | ||
this.state = STATES.RELATIVE_PATH_START; | ||
this.state = STATES.PATH_START; | ||
--this.pointer; | ||
@@ -835,5 +853,137 @@ } else if (c === 0x9 || c === 0xA || c === 0xD) { | ||
URLStateMachine.prototype["parse" + STATES.PATH_START] = function parseScheme(c, c_str) { | ||
if (specialSchemas[this.url.scheme] !== undefined && c_str === "\\") { | ||
this.parse_error = true; | ||
} | ||
this.state = STATES.PATH; | ||
if (c_str !== "/" && !(specialSchemas[this.url.scheme] !== undefined && c_str === "\\")) { | ||
--this.pointer; | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.PATH] = function parseScheme(c, c_str) { | ||
if (isNaN(c) || c_str === "/" || (specialSchemas[this.url.scheme] !== undefined && c_str === "\\") || | ||
(!this.state_override && (c_str === "?" || c_str === "#"))) { | ||
if (specialSchemas[this.url.scheme] !== undefined && c_str === "\\") { | ||
this.parse_error = true; | ||
} | ||
this.buffer = bufferReplacement[this.buffer.toLowerCase()] || this.buffer; | ||
if (this.buffer === "..") { | ||
this.url.path.pop(); | ||
if (c_str !== "/" && !(specialSchemas[this.url.scheme] !== undefined && c_str === "\\")) { | ||
this.url.path.push(""); | ||
} | ||
} else if (this.buffer === "." && c_str !== "/" && | ||
!(specialSchemas[this.url.scheme] !== undefined && c_str === "\\")) { | ||
this.url.path.push(""); | ||
} else if (this.buffer !== ".") { | ||
if (this.url.scheme === "file" && this.url.path.length === 0 && | ||
this.buffer.length === 2 && isASCIIAlpha(this.buffer.codePointAt(0)) && this.buffer[1] === "|") { | ||
this.buffer = this.buffer[0] + ":"; | ||
} | ||
this.url.path.push(this.buffer); | ||
} | ||
this.buffer = ""; | ||
if (c_str === "?") { | ||
this.url.query = ""; | ||
this.state = STATES.QUERY; | ||
} | ||
if (c_str === "#") { | ||
this.url.fragment = ""; | ||
this.state = STATES.FRAGMENT; | ||
} | ||
} else if (c === 0x9 || c === 0xA || c === 0xD) { | ||
this.parse_error = true; | ||
} else { | ||
//TODO:If c is not a URL code point and not "%", parse error. | ||
if (c_str === "%" && | ||
(!isASCIIHex(at(this.input, this.pointer + 1)) || | ||
!isASCIIHex(at(this.input, this.pointer + 2)))) { | ||
this.parse_error = true; | ||
} | ||
this.buffer += defaultEncode(c); | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.NON_RELATIVE_PATH] = function parseScheme(c, c_str) { | ||
if (c_str === "?") { | ||
this.url.query = ""; | ||
this.state = STATES.QUERY; | ||
} else if (c_str === "#") { | ||
this.url.fragment = ""; | ||
this.state = STATES.FRAGMENT; | ||
} else { | ||
// TODO: Add: not a URL code point | ||
if (!isNaN(c) && c_str !== "%") { | ||
this.parse_error = true; | ||
} | ||
if (c_str === "%" && | ||
(!isASCIIHex(at(this.input, this.pointer + 1)) || | ||
!isASCIIHex(at(this.input, this.pointer + 2)))) { | ||
this.parse_error = true; | ||
} | ||
if (!isNaN(c) && c !== 0x9 && c !== 0xA && c !== 0xD) { | ||
this.url.path[0] = this.url.path[0] + simpleEncode(c); | ||
} | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.QUERY] = function parseScheme(c, c_str) { | ||
if (isNaN(c) || (!this.state_override && c_str === "#")) { | ||
if (specialSchemas[this.url.scheme] === undefined || this.url.scheme === "ws" || this.url.scheme === "wss") { | ||
this.encoding_override = "utf-8"; | ||
} | ||
const buffer = new Buffer(this.buffer); //TODO: Use encoding override instead | ||
for (let i = 0; i < buffer.length; ++i) { | ||
if (buffer[i] < 0x21 || buffer[i] > 0x7E || buffer[i] === 0x22 || buffer[i] === 0x23 || | ||
buffer[i] === 0x3C || buffer[i] === 0x3E) { | ||
this.url.query += percentEncode(buffer[i]); | ||
} else { | ||
this.url.query += String.fromCodePoint(buffer[i]); | ||
} | ||
} | ||
this.buffer = ""; | ||
if (c_str === "#") { | ||
this.url.fragment = ""; | ||
this.state = STATES.FRAGMENT; | ||
} | ||
} else if (c === 0x9 || c === 0xA || c === 0xD) { | ||
this.parse_error = true; | ||
} else { | ||
//TODO: If c is not a URL code point and not "%", parse error. | ||
if (c_str === "%" && | ||
(!isASCIIHex(at(this.input, this.pointer + 1)) || | ||
!isASCIIHex(at(this.input, this.pointer + 2)))) { | ||
this.parse_error = true; | ||
} else { | ||
this.buffer += c_str; | ||
} | ||
} | ||
}; | ||
URLStateMachine.prototype["parse" + STATES.FRAGMENT] = function parseScheme(c, c_str) { | ||
if (isNaN(c)) { // do nothing | ||
} else if (c === 0x0 || c === 0x9 || c === 0xA || c === 0xD) { | ||
this.parse_error = true; | ||
} else { | ||
//TODO: If c is not a URL code point and not "%", parse error. | ||
if (c_str === "%" && | ||
(!isASCIIHex(at(this.input, this.pointer + 1)) || | ||
!isASCIIHex(at(this.input, this.pointer + 2)))) { | ||
this.parse_error = true; | ||
} else { | ||
this.url.fragment += c_str; | ||
} | ||
} | ||
}; | ||
function serializeURL(url, excludeFragment) { | ||
let output = url.scheme + ":"; | ||
if (url.isRelative) { | ||
if (url.host !== null) { | ||
output += "//" + url.username; | ||
@@ -850,5 +1000,8 @@ if (url.password !== null) { | ||
} | ||
} | ||
if (url.nonRelative) { | ||
output += url.path[0]; | ||
} else { | ||
output += "/" + url.path.join("/"); | ||
} else { | ||
output += url.scheme_data; | ||
} | ||
@@ -871,10 +1024,10 @@ | ||
} | ||
let result = tuple.scheme + "://"; | ||
result += tr46.toUnicode(tuple.host, false).domain; | ||
if (relativeSchemas[tuple.scheme] && tuple.port !== relativeSchemas[tuple.scheme]) { | ||
if (specialSchemas[tuple.scheme] && tuple.port !== specialSchemas[tuple.scheme]) { | ||
result += ":" + tuple.port; | ||
} | ||
return result; | ||
@@ -951,3 +1104,3 @@ } | ||
} | ||
const url = this[urlSymbol].url; | ||
@@ -959,4 +1112,4 @@ switch (url.scheme) { | ||
} catch (e) { | ||
// Chrome behaviour | ||
return "://"; | ||
// serializing an opaque identifier returns "null" | ||
return "null"; | ||
} | ||
@@ -973,3 +1126,3 @@ break; | ||
host: url.host, | ||
port: url.port === "" ? relativeSchemas[url.scheme] : url.port | ||
port: url.port === "" ? specialSchemas[url.scheme] : url.port | ||
}); | ||
@@ -980,4 +1133,4 @@ case "file": | ||
default: | ||
// Chrome behaviour, Firefox returns "null" | ||
return this[urlSymbol].url.scheme + "://"; | ||
// serializing an opaque identifier returns "null" | ||
return "null"; | ||
} | ||
@@ -1003,3 +1156,3 @@ }, | ||
set username(val) { | ||
if (this[urlSymbol] === null || !this[urlSymbol].url.isRelative) { | ||
if (this[urlSymbol] === null || this[urlSymbol].url.host === null || this[urlSymbol].url.nonRelative) { | ||
return; | ||
@@ -1019,3 +1172,3 @@ } | ||
set password(val) { | ||
if (this[urlSymbol] === null || !this[urlSymbol].url.isRelative) { | ||
if (this[urlSymbol] === null || this[urlSymbol].url.host === null || this[urlSymbol].url.nonRelative) { | ||
return; | ||
@@ -1032,3 +1185,3 @@ } | ||
get host() { | ||
if (this[urlSymbol] === null) { | ||
if (this[urlSymbol] === null || this[urlSymbol].url.host === null) { | ||
return ""; | ||
@@ -1040,3 +1193,3 @@ } | ||
set host(val) { | ||
if (this[urlSymbol] === null || !this[urlSymbol].url.isRelative) { | ||
if (this[urlSymbol] === null || this[urlSymbol].url.nonRelative) { | ||
return; | ||
@@ -1048,3 +1201,3 @@ } | ||
get hostname() { | ||
if (this[urlSymbol] === null) { | ||
if (this[urlSymbol] === null || this[urlSymbol].url.host === null) { | ||
return ""; | ||
@@ -1055,3 +1208,3 @@ } | ||
set hostname(val) { | ||
if (this[urlSymbol] === null || !this[urlSymbol].url.isRelative) { | ||
if (this[urlSymbol] === null || this[urlSymbol].url.nonRelative) { | ||
return; | ||
@@ -1069,3 +1222,3 @@ } | ||
set port(val) { | ||
if (this[urlSymbol] === null || !this[urlSymbol].url.isRelative || this[urlSymbol].url.scheme === "file") { | ||
if (this[urlSymbol] === null || this[urlSymbol].url.nonRelative || this[urlSymbol].url.scheme === "file") { | ||
return; | ||
@@ -1080,4 +1233,4 @@ } | ||
} | ||
if (!this[urlSymbol].url.isRelative) { | ||
return this[urlSymbol].url.scheme_data; | ||
if (this[urlSymbol].url.nonRelative) { | ||
return this[urlSymbol].url.path[0]; | ||
} | ||
@@ -1088,7 +1241,7 @@ | ||
set pathname(val) { | ||
if (this[urlSymbol] === null || !this[urlSymbol].url.isRelative) { | ||
if (this[urlSymbol] === null || this[urlSymbol].url.nonRelative) { | ||
return; | ||
} | ||
this[urlSymbol].url.path = []; | ||
this[urlSymbol] = new URLStateMachine(val, null, null, this[urlSymbol].url, STATES.RELATIVE_PATH_START); | ||
this[urlSymbol] = new URLStateMachine(val, null, null, this[urlSymbol].url, STATES.PATH_START); | ||
}, | ||
@@ -1199,4 +1352,4 @@ | ||
setTheInput(obj, null, null); | ||
mixin(URLUtils, obj); | ||
}; |
{ | ||
"name": "whatwg-url", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "An implementation of the WHATWG URL algorithm", | ||
"main": "lib/url.js", | ||
"author": { | ||
"name": "Sebastian Mayr", | ||
"email": "Sebastian Mayr <github@smayr.name>" | ||
}, | ||
"author": "Sebastian Mayr <github@smayr.name>", | ||
"license": "MIT", | ||
"repository": "jsdom/whatwg-url", | ||
"dependencies": { | ||
@@ -11,0 +10,0 @@ "tr46": "~0.0.1" |
# WHATWG-URL | ||
WHATWG-URL is a full implementation of the [WHATWG URL](https://url.spec.whatwg.org/) specification. |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
0
36627
5
1135