1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-28 18:19:08 -05:00
denoland-deno/cli/js/web/url_search_params.ts

234 lines
5.7 KiB
TypeScript
Raw Normal View History

2020-01-02 15:13:47 -05:00
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { URL } from "./url.ts";
import { requiredArguments } from "./util.ts";
2020-03-05 07:05:41 -05:00
// Returns whether o is iterable.
// @internal
export function isIterable<T, P extends keyof T, K extends T[P]>(
o: T
): o is T & Iterable<[P, K]> {
// checks for null and undefined
if (o == null) {
return false;
}
return (
typeof ((o as unknown) as Iterable<[P, K]>)[Symbol.iterator] === "function"
);
}
2018-10-21 11:07:29 -04:00
export class URLSearchParams {
private params: Array<[string, string]> = [];
private url: URL | null = null;
2018-10-21 11:07:29 -04:00
constructor(init: string | string[][] | Record<string, string> = "") {
if (typeof init === "string") {
this._handleStringInitialization(init);
return;
}
2018-10-21 11:07:29 -04:00
if (Array.isArray(init) || isIterable(init)) {
this._handleArrayInitialization(init);
return;
}
if (Object(init) !== init) {
return;
}
if (init instanceof URLSearchParams) {
this.params = init.params;
return;
}
// Overload: record<USVString, USVString>
for (const key of Object.keys(init)) {
this.append(key, init[key]);
2018-10-21 11:07:29 -04:00
}
}
private updateSteps(): void {
if (this.url === null) {
return;
}
let query: string | null = this.toString();
if (query === "") {
query = null;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.url as any)._parts.query = query;
}
2018-10-21 11:07:29 -04:00
append(name: string, value: string): void {
requiredArguments("URLSearchParams.append", arguments.length, 2);
this.params.push([String(name), String(value)]);
2019-06-10 23:55:38 -04:00
this.updateSteps();
2018-10-21 11:07:29 -04:00
}
delete(name: string): void {
requiredArguments("URLSearchParams.delete", arguments.length, 1);
name = String(name);
2018-10-21 11:07:29 -04:00
let i = 0;
while (i < this.params.length) {
if (this.params[i][0] === name) {
this.params.splice(i, 1);
} else {
i++;
}
}
this.updateSteps();
2018-10-21 11:07:29 -04:00
}
getAll(name: string): string[] {
requiredArguments("URLSearchParams.getAll", arguments.length, 1);
name = String(name);
2018-10-21 11:07:29 -04:00
const values = [];
for (const entry of this.params) {
if (entry[0] === name) {
values.push(entry[1]);
}
}
return values;
}
get(name: string): string | null {
requiredArguments("URLSearchParams.get", arguments.length, 1);
name = String(name);
2018-10-21 11:07:29 -04:00
for (const entry of this.params) {
if (entry[0] === name) {
return entry[1];
}
}
return null;
}
has(name: string): boolean {
requiredArguments("URLSearchParams.has", arguments.length, 1);
name = String(name);
return this.params.some((entry): boolean => entry[0] === name);
2018-10-21 11:07:29 -04:00
}
set(name: string, value: string): void {
requiredArguments("URLSearchParams.set", arguments.length, 2);
// If there are any name-value pairs whose name is name, in list,
// set the value of the first such name-value pair to value
// and remove the others.
name = String(name);
value = String(value);
let found = false;
let i = 0;
while (i < this.params.length) {
if (this.params[i][0] === name) {
if (!found) {
this.params[i][1] = value;
found = true;
i++;
} else {
this.params.splice(i, 1);
}
} else {
i++;
}
}
// Otherwise, append a new name-value pair whose name is name
// and value is value, to list.
if (!found) {
this.append(name, value);
}
2019-06-10 23:55:38 -04:00
this.updateSteps();
2018-10-21 11:07:29 -04:00
}
sort(): void {
this.params = this.params.sort((a, b): number =>
a[0] === b[0] ? 0 : a[0] > b[0] ? 1 : -1
2018-10-21 11:07:29 -04:00
);
2019-06-10 23:55:38 -04:00
this.updateSteps();
2018-10-21 11:07:29 -04:00
}
forEach(
callbackfn: (value: string, key: string, parent: this) => void,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2018-10-21 11:07:29 -04:00
thisArg?: any
): void {
requiredArguments("URLSearchParams.forEach", arguments.length, 1);
2018-10-21 11:07:29 -04:00
if (typeof thisArg !== "undefined") {
callbackfn = callbackfn.bind(thisArg);
}
2018-10-21 11:07:29 -04:00
for (const [key, value] of this.entries()) {
callbackfn(value, key, this);
}
}
*keys(): IterableIterator<string> {
2018-10-21 11:07:29 -04:00
for (const entry of this.params) {
yield entry[0];
}
}
*values(): IterableIterator<string> {
2018-10-21 11:07:29 -04:00
for (const entry of this.params) {
yield entry[1];
}
}
*entries(): IterableIterator<[string, string]> {
2018-10-21 11:07:29 -04:00
yield* this.params;
}
*[Symbol.iterator](): IterableIterator<[string, string]> {
2018-10-21 11:07:29 -04:00
yield* this.params;
}
toString(): string {
return this.params
.map(
(tuple): string =>
2018-10-21 11:07:29 -04:00
`${encodeURIComponent(tuple[0])}=${encodeURIComponent(tuple[1])}`
)
.join("&");
}
private _handleStringInitialization(init: string): void {
// Overload: USVString
// If init is a string and starts with U+003F (?),
// remove the first code point from init.
if (init.charCodeAt(0) === 0x003f) {
init = init.slice(1);
}
for (const pair of init.split("&")) {
// Empty params are ignored
if (pair.length === 0) {
continue;
}
const position = pair.indexOf("=");
const name = pair.slice(0, position === -1 ? pair.length : position);
const value = pair.slice(name.length + 1);
this.append(decodeURIComponent(name), decodeURIComponent(value));
}
}
private _handleArrayInitialization(
init: string[][] | Iterable<[string, string]>
): void {
// Overload: sequence<sequence<USVString>>
for (const tuple of init) {
// If pair does not contain exactly two items, then throw a TypeError.
if (tuple.length !== 2) {
throw new TypeError(
"URLSearchParams.constructor tuple array argument must only contain pair elements"
);
}
this.append(tuple[0], tuple[1]);
}
}
2018-10-21 11:07:29 -04:00
}