urlsearchparams
Version:
implementaion of URLSearchParams at WHATWG Living Standard https://url.spec.whatwg.org/
355 lines • 12.5 kB
JavaScript
/// <reference path="types/webidl.d.ts" />
/// <reference path="types/utf8-encoding.d.ts" />
var TextEncoder, TextDecoder;
if (typeof window === "undefined") {
var TextEncoding = require("utf8-encoding");
TextEncoder = TextEncoding.TextEncoder;
TextDecoder = TextEncoding.TextDecoder;
}
// save platform implementation if exists
var nativeURLSearchParams;
if (typeof URLSearchParams !== "undefined") {
nativeURLSearchParams = URLSearchParams;
}
var URLSearchParams;
(function (_URLSearchParams) {
var encoder = new TextEncoder("utf-8");
var decoder = new TextDecoder();
function encode(s) {
"use strict";
return encoder.encode(s);
}
function decode(bytes) {
"use strict";
return decoder.decode(bytes);
}
function copy(obj) {
"use strict";
return JSON.parse(JSON.stringify(obj));
}
/**
* TODO: separate percentEncoder to module
*/
// https://url.spec.whatwg.org/#percent-encode
function percentEncode(byt) {
"use strict";
return "%" + byt.toString(16).toUpperCase();
}
// utility not in spec
function percentEncoder(str) {
"use strict";
var encoded = encode(str);
var result = "";
for (var i = 0; i < encoded.length; i++) {
result += percentEncode(encoded[i]);
}
return result;
}
_URLSearchParams.percentEncoder = percentEncoder;
// TODO: better using []number ?
// https://url.spec.whatwg.org/#percent-decode
function percentDecode(input) {
"use strict";
// step 1
var output = [];
for (var i = 0; i < input.length; i++) {
var byt = input[i];
// step 2-1
if (byt !== 37) {
output.push(byt);
continue;
}
// step 2-2
if (byt === 37) {
// has more 2 byte
if (i + 2 < input.length) {
var b1 = input[i + 1];
var b2 = input[i + 2];
var range1 = (0x30 <= b1 && b1 <= 0x39) || (0x41 <= b1 && b1 <= 0x46) || (0x61 <= b1 && b1 <= 0x66);
var range2 = (0x30 <= b2 && b2 <= 0x39) || (0x41 <= b2 && b2 <= 0x46) || (0x61 <= b2 && b2 <= 0x66);
if (!range1 && !range2) {
output.push(byt);
continue;
}
}
}
// step 2-3
// step 2-3-1
var u1 = input[i + 1];
var u2 = input[i + 2];
var hex = decode(new Uint8Array([u1, u2]));
var bytePoint = parseInt(hex, 16);
// step 2-3-2
output.push(bytePoint);
// step 2-3-3
i = i + 2;
continue;
}
// step 3
return new Uint8Array(output);
}
// utility not in spec
function percentDecoder(str) {
"use strict";
var encoded = encode(str);
return decode(percentDecode(encoded));
}
_URLSearchParams.percentDecoder = percentDecoder;
// https://url.spec.whatwg.org/#concept-urlencoded-parser
function URLEncodedParse(input, encodingOverride, useCharset, isIndex) {
"use strict";
// step 1
if (encodingOverride === undefined) {
encodingOverride = "utf-8";
}
// step 2
if (encodingOverride !== "utf-8") {
throw new Error("unsupported encoding");
}
// step 3
var array = Array.prototype.slice.call(input);
var sequences = [];
while (true) {
var i = array.indexOf(38); // &
if (i < 0) {
sequences.push(array);
break;
}
sequences.push(array.splice(0, i));
array.shift();
}
// step 4
if (isIndex === true) {
if (sequences[0].indexOf(61) === -1) {
sequences[0].unshift(61);
}
}
// step 5, 6
var pairs = sequences.map(function (bytes) {
// step 6-1
if (bytes.length === 0)
return;
// step 6-2
var name, value;
var i = bytes.indexOf(61);
if (i > 0) {
name = bytes.splice(0, i);
bytes.shift();
value = bytes;
}
else {
name = bytes;
value = [];
}
// step 4
name.map(function (e) {
if (e === 43) {
e = 0x20;
}
return e;
});
// step 5
if (useCharset && name === "_charset_") {
throw new Error("unsupported flug '_charset_'");
}
// step 8 parsent decode
name = decode(percentDecode(new Uint8Array(name)));
value = decode(percentDecode(new Uint8Array(value)));
return { name: name, value: value };
});
return pairs;
}
// https://url.spec.whatwg.org/#concept-urlencoded-serializer
function URLEncodedSerialize(pairs, encodingOverride) {
"use strict";
// step 1
if (encodingOverride === undefined) {
encodingOverride = "utf-8";
}
// this imeplementation support only utf-8
if (encodingOverride !== "utf-8") {
throw new Error("unsupported encoding");
}
// step 2
var output = "";
// step 3
pairs.forEach(function (pair, index) {
// step 3-1
var outputPair = copy(pair);
// step 3-2
var encodedName = encode(outputPair.name);
var encodedValue = encode(outputPair.value);
// step 3-3
outputPair.name = URLEncodedByteSerialize(encodedName);
outputPair.value = URLEncodedByteSerialize(encodedValue);
// step 3-4
if (index !== 0) {
output += "&";
}
// step 3-5
output += "" + outputPair.name + "=" + outputPair.value;
});
// step 4
return output;
}
// https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer
function URLEncodedByteSerialize(input) {
"use strict";
// step 1
var output = "";
for (var i = 0; i < input.length; i++) {
var byt = input[i];
if (byt === 0x20) {
output += "+"; // 0x2B
continue;
}
if ([0x2A, 0x2D, 0x2E].indexOf(byt) !== -1) {
output += String.fromCodePoint(byt);
continue;
}
if (0x30 <= byt && byt <= 0x39) {
output += String.fromCodePoint(byt);
continue;
}
if (0x41 <= byt && byt <= 0x5A) {
output += String.fromCodePoint(byt);
continue;
}
if (byt === 0x5F) {
output += String.fromCodePoint(byt);
continue;
}
if (0x61 <= byt && byt <= 0x7A) {
output += String.fromCodePoint(byt);
continue;
}
// otherwise
output += percentEncode(byt);
}
// step 3
return output;
}
;
var URLSearchParams = (function () {
function URLSearchParams(init) {
// https://url.spec.whatwg.org/#concept-urlsearchparams-list
this.list = [];
// https://url.spec.whatwg.org/#concept-urlsearchparams-url-object
this.urlObject = [];
// step 1
var query = this;
// step 2
if (typeof init === "string") {
query.list = this.parse(init);
}
// step 3
if (URLSearchParams.prototype.isPrototypeOf(init)) {
query.list = copy(init.list);
}
// step 4
return query;
}
// https://url.spec.whatwg.org/#dom-urlsearchparams-append
URLSearchParams.prototype.append = function (name, value) {
if (name === undefined || value === undefined) {
throw new TypeError("Not enough arguments to URLSearchParams.append.");
}
// step 1
this.list.push({ name: name, value: value });
// step 2
this.update();
};
// https://url.spec.whatwg.org/#dom-urlsearchparams-delete
URLSearchParams.prototype.delete = function (name) {
if (name === undefined) {
throw new TypeError("Not enough arguments to URLSearchParams.delete.");
}
// step 1
this.list = this.list.filter(function (pair) { return pair.name !== name; });
// step 2
this.update();
};
// https://url.spec.whatwg.org/#dom-urlsearchparams-get
URLSearchParams.prototype.get = function (name) {
if (name === undefined) {
throw new TypeError("Not enough arguments to URLSearchParams.get.");
}
return this.getAll(name).shift() || null;
};
// https://url.spec.whatwg.org/#dom-urlsearchparams-getall
URLSearchParams.prototype.getAll = function (name) {
if (name === undefined) {
throw new TypeError("Not enough arguments to URLSearchParams.getAll.");
}
return this.list.reduce(function (acc, pair) {
if (pair.name === name) {
acc.push(pair.value);
}
return acc;
}, []);
};
// https://url.spec.whatwg.org/#dom-urlsearchparams-set
URLSearchParams.prototype.set = function (name, value) {
if (name === undefined || value === undefined) {
throw new TypeError("Not enough arguments to URLSearchParams.set.");
}
// if exists, this appended will remove in filter.
this.list.push({ name: name, value: value });
// update all pair
this.list = this.list.map(function (pair) {
if (pair.name === name) {
pair.value = value;
}
return pair;
}).filter(function (pair) {
if (pair.name === name) {
if (this.emitted) {
// current pair is duplicate
return false;
}
else {
// first pair of key
this.emitted = true;
return true;
}
}
// other pair
return true;
}, { emitted: false });
// step 3
this.update();
};
// https://url.spec.whatwg.org/#dom-urlsearchparams-has
URLSearchParams.prototype.has = function (name) {
if (name === undefined) {
throw new TypeError("Not enough arguments to URLSearchParams.has.");
}
return this.list.some(function (pair) { return pair.name === name; });
};
// https://url.spec.whatwg.org/#concept-urlencoded-string-parser
URLSearchParams.prototype.parse = function (input) {
return URLEncodedParse(encode(input));
};
// https://url.spec.whatwg.org/#concept-urlsearchparams-update
URLSearchParams.prototype.update = function () {
// step 1
this.urlObject.forEach(function (url) {
// TODO: add query to url
// url.query = this.serialize(this.list);
// step 2
// this.urlObject.preupdate();
});
};
URLSearchParams.prototype.toString = function () {
return URLEncodedSerialize(this.list);
};
return URLSearchParams;
})();
_URLSearchParams.URLSearchParams = URLSearchParams;
})(URLSearchParams || (URLSearchParams = {}));
// export to
// - window in browser
// - module.exports in node.js
// if platform has implements, use that.
this.URLSearchParams = nativeURLSearchParams || URLSearchParams.URLSearchParams;
//# sourceMappingURL=urlsearchparams.js.map