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
Merged
Merged
Web API: URLSearchParams #1049
Changes from 7 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
9b86de0
feat: Added URLSearchParams and tests
kyranet 00986a6
lint: Ran format.py
kyranet 0e1528e
fix: Fix URLSearchParams tests
kyranet 50081bd
build: Fix compile error from ts-simple-ast
kyranet bea034e
legal: Add copyright notice in the top of urlsearchparams.ts
kyranet 24e30b2
docs: Removed useless line
kyranet 90907fa
build: Add urlsearchparams to BUILD.gn, formatted code
kyranet a7a2a58
rc: urlsearchparams -> url_search_params
kyranet 277df6d
tests: Prefix everything by `urlSearchParams`
kyranet 4e94d28
rc: public keyword is unnecessary in class members
kyranet File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
// Copyright 2018 the Deno authors. All rights reserved. MIT license. | ||
export class URLSearchParams { | ||
private params: Array<[string, string]> = []; | ||
|
||
public constructor(init: 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)); | ||
} | ||
} else if (Array.isArray(init)) { | ||
// Overload: sequence<sequence<USVString>> | ||
for (const tuple of init) { | ||
this.append(tuple[0], tuple[1]); | ||
} | ||
} else if (Object(init) === init) { | ||
// 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 { | ||
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 { | ||
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[] { | ||
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 { | ||
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 { | ||
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 { | ||
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 { | ||
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, key, parent) => { | ||
* console.log(value, key, parent); | ||
* }); | ||
* | ||
*/ | ||
public forEach( | ||
callbackfn: (value: string, key: string, parent: URLSearchParams) => void, | ||
// tslint:disable-next-line:no-any | ||
thisArg?: any | ||
) { | ||
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> { | ||
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> { | ||
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]> { | ||
yield* this.params; | ||
} | ||
|
||
/** Returns an iterator allowing to go through all key/value | ||
* pairs contained in this object. | ||
* | ||
* for (const [key, value] of searchParams[Symbol.iterator]()) { | ||
* console.log(key, value); | ||
* } | ||
*/ | ||
public *[Symbol.iterator](): Iterable<[string, string]> { | ||
yield* this.params; | ||
} | ||
|
||
/** Returns a query string suitable for use in a URL. | ||
* | ||
* searchParams.toString(); | ||
*/ | ||
public toString(): string { | ||
return this.params | ||
.map( | ||
tuple => | ||
`${encodeURIComponent(tuple[0])}=${encodeURIComponent(tuple[1])}` | ||
) | ||
.join("&"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
// 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=54"); | ||
}); | ||
|
||
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"); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
url_search_params.ts