2023-10-03 11:14:36 +08:00
|
|
|
"use strict";
|
|
|
|
const usm = require("./url-state-machine");
|
|
|
|
const urlencoded = require("./urlencoded");
|
|
|
|
const URLSearchParams = require("./URLSearchParams");
|
|
|
|
|
|
|
|
exports.implementation = class URLImpl {
|
|
|
|
constructor(globalObject, constructorArgs) {
|
|
|
|
const url = constructorArgs[0];
|
|
|
|
const base = constructorArgs[1];
|
|
|
|
|
|
|
|
let parsedBase = null;
|
|
|
|
if (base !== undefined) {
|
|
|
|
parsedBase = usm.basicURLParse(base);
|
|
|
|
if (parsedBase === null) {
|
|
|
|
throw new TypeError(`Invalid base URL: ${base}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const parsedURL = usm.basicURLParse(url, { baseURL: parsedBase });
|
|
|
|
if (parsedURL === null) {
|
|
|
|
throw new TypeError(`Invalid URL: ${url}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const query = parsedURL.query !== null ? parsedURL.query : "";
|
|
|
|
|
|
|
|
this._url = parsedURL;
|
|
|
|
|
|
|
|
// We cannot invoke the "new URLSearchParams object" algorithm without going through the constructor, which strips
|
|
|
|
// question mark by default. Therefore the doNotStripQMark hack is used.
|
|
|
|
this._query = URLSearchParams.createImpl(globalObject, [query], { doNotStripQMark: true });
|
|
|
|
this._query._url = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
get href() {
|
|
|
|
return usm.serializeURL(this._url);
|
|
|
|
}
|
|
|
|
|
|
|
|
set href(v) {
|
|
|
|
const parsedURL = usm.basicURLParse(v);
|
|
|
|
if (parsedURL === null) {
|
|
|
|
throw new TypeError(`Invalid URL: ${v}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._url = parsedURL;
|
|
|
|
|
|
|
|
this._query._list.splice(0);
|
|
|
|
const { query } = parsedURL;
|
|
|
|
if (query !== null) {
|
|
|
|
this._query._list = urlencoded.parseUrlencodedString(query);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
get origin() {
|
|
|
|
return usm.serializeURLOrigin(this._url);
|
|
|
|
}
|
|
|
|
|
|
|
|
get protocol() {
|
|
|
|
return `${this._url.scheme}:`;
|
|
|
|
}
|
|
|
|
|
|
|
|
set protocol(v) {
|
|
|
|
usm.basicURLParse(`${v}:`, { url: this._url, stateOverride: "scheme start" });
|
|
|
|
}
|
|
|
|
|
|
|
|
get username() {
|
|
|
|
return this._url.username;
|
|
|
|
}
|
|
|
|
|
|
|
|
set username(v) {
|
|
|
|
if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
usm.setTheUsername(this._url, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
get password() {
|
|
|
|
return this._url.password;
|
|
|
|
}
|
|
|
|
|
|
|
|
set password(v) {
|
|
|
|
if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
usm.setThePassword(this._url, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
get host() {
|
|
|
|
const url = this._url;
|
|
|
|
|
|
|
|
if (url.host === null) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (url.port === null) {
|
|
|
|
return usm.serializeHost(url.host);
|
|
|
|
}
|
|
|
|
|
|
|
|
return `${usm.serializeHost(url.host)}:${usm.serializeInteger(url.port)}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
set host(v) {
|
|
|
|
if (usm.hasAnOpaquePath(this._url)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
usm.basicURLParse(v, { url: this._url, stateOverride: "host" });
|
|
|
|
}
|
|
|
|
|
|
|
|
get hostname() {
|
|
|
|
if (this._url.host === null) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return usm.serializeHost(this._url.host);
|
|
|
|
}
|
|
|
|
|
|
|
|
set hostname(v) {
|
|
|
|
if (usm.hasAnOpaquePath(this._url)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
usm.basicURLParse(v, { url: this._url, stateOverride: "hostname" });
|
|
|
|
}
|
|
|
|
|
|
|
|
get port() {
|
|
|
|
if (this._url.port === null) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return usm.serializeInteger(this._url.port);
|
|
|
|
}
|
|
|
|
|
|
|
|
set port(v) {
|
|
|
|
if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v === "") {
|
|
|
|
this._url.port = null;
|
|
|
|
} else {
|
|
|
|
usm.basicURLParse(v, { url: this._url, stateOverride: "port" });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
get pathname() {
|
|
|
|
return usm.serializePath(this._url);
|
|
|
|
}
|
|
|
|
|
|
|
|
set pathname(v) {
|
|
|
|
if (usm.hasAnOpaquePath(this._url)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._url.path = [];
|
|
|
|
usm.basicURLParse(v, { url: this._url, stateOverride: "path start" });
|
|
|
|
}
|
|
|
|
|
|
|
|
get search() {
|
|
|
|
if (this._url.query === null || this._url.query === "") {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return `?${this._url.query}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
set search(v) {
|
|
|
|
const url = this._url;
|
|
|
|
|
|
|
|
if (v === "") {
|
|
|
|
url.query = null;
|
|
|
|
this._query._list = [];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const input = v[0] === "?" ? v.substring(1) : v;
|
|
|
|
url.query = "";
|
|
|
|
usm.basicURLParse(input, { url, stateOverride: "query" });
|
|
|
|
this._query._list = urlencoded.parseUrlencodedString(input);
|
|
|
|
}
|
|
|
|
|
|
|
|
get searchParams() {
|
|
|
|
return this._query;
|
|
|
|
}
|
|
|
|
|
|
|
|
get hash() {
|
|
|
|
if (this._url.fragment === null || this._url.fragment === "") {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return `#${this._url.fragment}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
set hash(v) {
|
|
|
|
if (v === "") {
|
|
|
|
this._url.fragment = null;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const input = v[0] === "#" ? v.substring(1) : v;
|
|
|
|
this._url.fragment = "";
|
|
|
|
usm.basicURLParse(input, { url: this._url, stateOverride: "fragment" });
|
|
|
|
}
|
|
|
|
|
|
|
|
toJSON() {
|
|
|
|
return this.href;
|
|
|
|
}
|
|
|
|
};
|