summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
Diffstat (limited to 'js')
-rw-r--r--js/globals.ts3
-rw-r--r--js/unit_tests.ts1
-rw-r--r--js/url_search_params.ts207
-rw-r--r--js/url_search_params_test.ts114
4 files changed, 325 insertions, 0 deletions
diff --git a/js/globals.ts b/js/globals.ts
index a67524f96..e65fee928 100644
--- a/js/globals.ts
+++ b/js/globals.ts
@@ -6,6 +6,7 @@ import { globalEval } from "./global_eval";
import { libdeno } from "./libdeno";
import * as textEncoding from "./text_encoding";
import * as timers from "./timers";
+import * as urlsearchparams from "./url_search_params";
// During the build process, augmentations to the variable `window` in this
// file are tracked and created as part of default library that is built into
@@ -33,6 +34,8 @@ window.TextDecoder = textEncoding.TextDecoder;
window.atob = textEncoding.atob;
window.btoa = textEncoding.btoa;
+window.URLSearchParams = urlsearchparams.URLSearchParams;
+
window.fetch = fetch_.fetch;
window.Headers = fetch_.DenoHeaders;
diff --git a/js/unit_tests.ts b/js/unit_tests.ts
index 2f1a41f62..35cffc767 100644
--- a/js/unit_tests.ts
+++ b/js/unit_tests.ts
@@ -28,4 +28,5 @@ import "./truncate_test.ts";
import "./v8_source_maps_test.ts";
import "../website/app_test.js";
import "./metrics_test.ts";
+import "./url_search_params_test.ts";
import "./util_test.ts";
diff --git a/js/url_search_params.ts b/js/url_search_params.ts
new file mode 100644
index 000000000..cf005fc16
--- /dev/null
+++ b/js/url_search_params.ts
@@ -0,0 +1,207 @@
+// Copyright 2018 the Deno authors. All rights reserved. MIT license.
+export class URLSearchParams {
+ private params: Array<[string, string]> = [];
+
+ 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');
+ */
+ 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');
+ */
+ 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');
+ */
+ 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');
+ */
+ 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');
+ */
+ 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');
+ */
+ 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();
+ */
+ 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);
+ * });
+ *
+ */
+ 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);
+ * }
+ */
+ *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);
+ * }
+ */
+ *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);
+ * }
+ */
+ *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);
+ * }
+ */
+ *[Symbol.iterator](): Iterable<[string, string]> {
+ yield* this.params;
+ }
+
+ /** Returns a query string suitable for use in a URL.
+ *
+ * searchParams.toString();
+ */
+ toString(): string {
+ return this.params
+ .map(
+ tuple =>
+ `${encodeURIComponent(tuple[0])}=${encodeURIComponent(tuple[1])}`
+ )
+ .join("&");
+ }
+}
diff --git a/js/url_search_params_test.ts b/js/url_search_params_test.ts
new file mode 100644
index 000000000..46e8103d9
--- /dev/null
+++ b/js/url_search_params_test.ts
@@ -0,0 +1,114 @@
+// Copyright 2018 the Deno authors. All rights reserved. MIT license.
+import { test, assert, assertEqual } from "./test_util.ts";
+
+test(function urlSearchParamsInitString() {
+ 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 urlSearchParamsInitIterable() {
+ const init = [["a", "54"], ["b", "true"]];
+ const searchParams = new URLSearchParams(init);
+ assertEqual(searchParams.toString(), "a=54&b=true");
+});
+
+test(function urlSearchParamsInitRecord() {
+ const init = { a: "54", b: "true" };
+ const searchParams = new URLSearchParams(init);
+ assertEqual(searchParams.toString(), "a=54&b=true");
+});
+
+test(function urlSearchParamsAppendSuccess() {
+ const searchParams = new URLSearchParams();
+ searchParams.append("a", "true");
+ assertEqual(searchParams.toString(), "a=true");
+});
+
+test(function urlSearchParamsDeleteSuccess() {
+ const init = "a=54&b=true";
+ const searchParams = new URLSearchParams(init);
+ searchParams.delete("b");
+ assertEqual(searchParams.toString(), "a=54");
+});
+
+test(function urlSearchParamsGetAllSuccess() {
+ 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 urlSearchParamsGetSuccess() {
+ 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 urlSearchParamsHasSuccess() {
+ 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 urlSearchParamsSetSuccess() {
+ 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 urlSearchParamsSortSuccess() {
+ 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 urlSearchParamsForEachSuccess() {
+ 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 urlSearchParamsMissingName() {
+ const init = "=4";
+ const searchParams = new URLSearchParams(init);
+ assertEqual(searchParams.get(""), "4");
+ assertEqual(searchParams.toString(), "=4");
+});
+
+test(function urlSearchParamsMissingValue() {
+ const init = "4=";
+ const searchParams = new URLSearchParams(init);
+ assertEqual(searchParams.get("4"), "");
+ assertEqual(searchParams.toString(), "4=");
+});
+
+test(function urlSearchParamsMissingEqualSign() {
+ const init = "4";
+ const searchParams = new URLSearchParams(init);
+ assertEqual(searchParams.get("4"), "");
+ assertEqual(searchParams.toString(), "4=");
+});
+
+test(function urlSearchParamsMissingPair() {
+ const init = "c=4&&a=54&";
+ const searchParams = new URLSearchParams(init);
+ assertEqual(searchParams.toString(), "c=4&a=54");
+});