New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Web API: URLSearchParams #1049
Web API: URLSearchParams #1049
Changes from 1 commit
9b86de0
00986a6
0e1528e
50081bd
bea034e
24e30b2
90907fa
a7a2a58
277df6d
4e94d28
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
export class URLSearchParams { | ||
|
||
private params: [string, string][] = []; | ||
|
||
public constructor(init: string | Iterable<[string, string]> | Record<string, string> = '') { | ||
if (typeof init === "string") { | ||
// 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)); | ||
} | ||
// Is object | ||
} else if (Object(init) === init) { | ||
if (Symbol.iterator in init) { | ||
// Overload: sequence<sequence<USVString>> | ||
for (const tuple of init[Symbol.iterator]()) { | ||
this.append(tuple[0], tuple[1]); | ||
} | ||
} else { | ||
// Overload: record<USVString, USVString> | ||
for (const key of Object.keys(init)) { | ||
this.append(key, init[key]); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** Appends a specified key/value pair as a new search parameter. | ||
* | ||
* searchParams.append('name', 'first'); | ||
* searchParams.append('name', 'second'); | ||
*/ | ||
public append(name: string, value: string): void { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed this and all other occurrences in the next commit, thanks you 👍 |
||
this.params.push([name, value]); | ||
} | ||
|
||
/** Deletes the given search parameter and its associated value, | ||
* from the list of all search parameters. | ||
* | ||
* searchParams.delete('name'); | ||
*/ | ||
public delete(name: string): void { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
let i = 0; | ||
while (i < this.params.length) { | ||
if (this.params[i][0] === name) this.params.splice(i, 1); | ||
else i++; | ||
} | ||
} | ||
|
||
/** Returns all the values associated with a given search parameter | ||
* as an array. | ||
* | ||
* searchParams.getAll('name'); | ||
*/ | ||
public getAll(name: string): string[] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
const values = []; | ||
for (const entry of this.params) { | ||
if (entry[0] === name) values.push(entry[1]); | ||
} | ||
|
||
return values; | ||
} | ||
|
||
/** Returns the first value associated to the given search parameter. | ||
* | ||
* searchParams.get('name'); | ||
*/ | ||
public get(name: string): string | null { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
for (const entry of this.params) { | ||
if (entry[0] === name) return entry[1]; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** Returns a Boolean that indicates whether a parameter with the | ||
* specified name exists. | ||
* | ||
* searchParams.has('name'); | ||
*/ | ||
public has(name: string): boolean { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
return this.params.some((entry) => entry[0] === name); | ||
} | ||
|
||
/** Sets the value associated with a given search parameter to the | ||
* given value. If there were several matching values, this method | ||
* deletes the others. If the search parameter doesn't exist, this | ||
* method creates it. | ||
* | ||
* searchParams.set('name', 'value'); | ||
*/ | ||
public set(name: string, value: string): void { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
this.delete(name); | ||
this.append(name, value); | ||
} | ||
|
||
/** Sort all key/value pairs contained in this object in place and | ||
* return undefined. The sort order is according to Unicode code | ||
* points of the keys. | ||
* | ||
* searchParams.sort(); | ||
*/ | ||
public sort(): void { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
this.params = this.params.sort((a, b) => a[0] === b[0] ? 0 : a[0] > b[0] ? 1 : -1); | ||
} | ||
|
||
/** Calls a function for each element contained in this object in | ||
* place and return undefined. Optionally accepts an object to use | ||
* as this when executing callback as second argument. | ||
* | ||
* searchParams.forEach((value: string, key: string, parent: URLSearchParams) => { | ||
* console.log(value, key, parent); | ||
* }); | ||
* | ||
*/ | ||
public forEach(callbackfn: (value: string, key: string, parent: URLSearchParams) => void, thisArg?: any) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DItto |
||
if (typeof thisArg !== "undefined") callbackfn = callbackfn.bind(thisArg); | ||
for (const [key, value] of this.entries()) { | ||
callbackfn(value, key, this); | ||
} | ||
} | ||
|
||
/** Returns an iterator allowing to go through all keys contained | ||
* in this object. | ||
* | ||
* for (const key of searchParams.keys()) console.log(key); | ||
*/ | ||
public *keys(): Iterable<string> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
for (const entry of this.params) yield entry[0]; | ||
} | ||
|
||
/** Returns an iterator allowing to go through all values contained | ||
* in this object. | ||
* | ||
* for (const value of searchParams.values()) console.log(value); | ||
*/ | ||
public *values(): Iterable<string> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
for (const entry of this.params) yield entry[1]; | ||
} | ||
|
||
/** Returns an iterator allowing to go through all key/value | ||
* pairs contained in this object. | ||
* | ||
* for (const [key, value] of searchParams.entries()) console.log(key, value); | ||
*/ | ||
public *entries(): Iterable<[string, string]> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
yield* this.params; | ||
} | ||
|
||
/** Returns an iterator allowing to go through all key/value | ||
* pairs contained in this object. | ||
* | ||
* for (const [key, value] of searchParams) console.log(key, value); | ||
*/ | ||
public *[Symbol.iterator](): Iterable<[string, string]> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
yield* this.params; | ||
} | ||
|
||
/** Returns a query string suitable for use in a URL. | ||
* | ||
* searchParams.toString(); | ||
*/ | ||
public toString(): string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DItto |
||
return this.params.map((tuple) => `${encodeURIComponent(tuple[0])}=${encodeURIComponent(tuple[1])}`).join("&"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
// Copyright 2018 the Deno authors. All rights reserved. MIT license. | ||
import { test, assert, assertEqual } from "./test_util.ts"; | ||
|
||
test(function initString() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you prefix call of the test function names with “url” or “urlSearchParams” There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, fixed in next commit 👍 |
||
const init = "c=4&a=2&b=3&%C3%A1=1"; | ||
const searchParams = new URLSearchParams(init); | ||
assert(init === searchParams.toString(), "The init query string does not match"); | ||
}); | ||
|
||
test(function initIterable() { | ||
const init = [["a", "54"], ["b", "true"]]; | ||
const searchParams = new URLSearchParams(init); | ||
assertEqual(searchParams.toString(), "a=54&b=true"); | ||
}); | ||
|
||
test(function initRecord() { | ||
const init = { a: '54', b: 'true' }; | ||
const searchParams = new URLSearchParams(init); | ||
assertEqual(searchParams.toString(), "a=54&b=true"); | ||
}); | ||
|
||
test(function appendSuccess() { | ||
const searchParams = new URLSearchParams(); | ||
searchParams.append("a", "true"); | ||
assertEqual(searchParams.toString(), "a=true"); | ||
}); | ||
|
||
test(function deleteSuccess() { | ||
const init = "a=54&b=true"; | ||
const searchParams = new URLSearchParams(init); | ||
searchParams.delete("b"); | ||
assertEqual(searchParams.toString(), "a=true"); | ||
}); | ||
|
||
test(function getAllSuccess() { | ||
const init = "a=54&b=true&a=true"; | ||
const searchParams = new URLSearchParams(init); | ||
assertEqual(searchParams.getAll("a"), ["54", "true"]); | ||
assertEqual(searchParams.getAll("b"), ["true"]); | ||
assertEqual(searchParams.getAll("c"), []); | ||
}); | ||
|
||
test(function getSuccess() { | ||
const init = "a=54&b=true&a=true"; | ||
const searchParams = new URLSearchParams(init); | ||
assertEqual(searchParams.get("a"), "54"); | ||
assertEqual(searchParams.get("b"), "true"); | ||
assertEqual(searchParams.get("c"), null); | ||
}); | ||
|
||
test(function hasSuccess() { | ||
const init = "a=54&b=true&a=true"; | ||
const searchParams = new URLSearchParams(init); | ||
assert(searchParams.has("a")); | ||
assert(searchParams.has("b")); | ||
assert(!searchParams.has("c")); | ||
}); | ||
|
||
test(function setSuccess() { | ||
const init = "a=54&b=true&a=true"; | ||
const searchParams = new URLSearchParams(init); | ||
searchParams.set("a", "false"); | ||
assertEqual(searchParams.toString(), "b=true&a=false"); | ||
}); | ||
|
||
test(function sortSuccess() { | ||
const init = "c=4&a=2&b=3&a=1"; | ||
const searchParams = new URLSearchParams(init); | ||
searchParams.sort(); | ||
assertEqual(searchParams.toString(), "a=2&a=1&b=3&c=4"); | ||
}); | ||
|
||
test(function forEachSuccess() { | ||
const init = [["a", "54"], ["b", "true"]]; | ||
const searchParams = new URLSearchParams(init); | ||
let callNum = 0; | ||
searchParams.forEach((value, key, parent) => { | ||
assertEqual(searchParams, parent); | ||
assertEqual(value, init[callNum][1]); | ||
assertEqual(key, init[callNum][0]); | ||
callNum++; | ||
}); | ||
assertEqual(callNum, init.length); | ||
}); | ||
|
||
test(function missingName() { | ||
const init = "=4"; | ||
const searchParams = new URLSearchParams(init); | ||
assertEqual(searchParams.get(""), "4"); | ||
assertEqual(searchParams.toString(), "=4"); | ||
}); | ||
|
||
test(function missingValue() { | ||
const init = "4="; | ||
const searchParams = new URLSearchParams(init); | ||
assertEqual(searchParams.get("4"), ""); | ||
assertEqual(searchParams.toString(), "4="); | ||
}); | ||
|
||
test(function missingEqualSign() { | ||
const init = "4"; | ||
const searchParams = new URLSearchParams(init); | ||
assertEqual(searchParams.get("4"), ""); | ||
assertEqual(searchParams.toString(), "4="); | ||
}); | ||
|
||
test(function missingPair() { | ||
const init = "c=4&&a=54&"; | ||
const searchParams = new URLSearchParams(init); | ||
assertEqual(searchParams.toString(), "c=4&a=54"); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
init
may beundefined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
=
at the end states it's optional. It defaults to''
to comply with WHATWG.