/* eslint-disable class-methods-use-this */ /* eslint-disable no-param-reassign */ /* eslint-disable max-len */ /* eslint-disable no-plusplus */ export class URLSearchParams { private dict: { [key: string]: string[] } = {} constructor(search: string | string[] | any | URLSearchParams = '') { if (search instanceof URLSearchParams) { this.dict = this.parseToDict(search.toString()) } else { this.dict = this.parseToDict(search) } } /** * Appends a specified key/value pair as a new search parameter */ public append(name: string, value: string): void { this.appendTo(this.dict, name, value) } /** * Deletes this give search parameter, and its associated value, from the list of all search parameters */ public delete(name: string): void { delete this.dict[name] } /** * Returns the first value associated to the given search parameter */ get(name: string): string | null { return name in this.dict ? this.dict[name][0] : null } /** * Returns all the values association with a given parameter */ getAll(name: string): string[] { return name in this.dict ? this.dict[name].slice(0) : [] } /** * Test if the search parameter exists */ has(name: string): boolean { return name in this.dict } /** * Sets the value associated to a given search parameter to * the given value. If there were several values, delete the others. */ set(name: string, value: string): void { this.dict[name] = [value] } /** * Returns a string containing a query string suitable for use in a URL */ toString(): string { return Object.entries(this.dict) .map(([key, value]) => `${key}=${this.encode(value)}`) .join('&') } /** * */ parseToDict(search: string | string[] | any): any { const dict = {} if (typeof search === 'object') { // if 'search' is an array, treat it as a sequence if (Array.isArray(search)) { for (let i = 0; i < search.length; i++) { const item = search[i] if (Array.isArray(item) && item.length === 2) { this.appendTo(dict, item[0], item[1]) } else { throw new TypeError( "Failed to construct 'URLSearchParams': Sequence initalizer must only contain pair elements" ) } } } else { Object.entries(search).forEach(([key, value]) => this.appendTo(dict, key, value) ) } } else { // remove 1st ? if (search.indexOf('?') === 0) { search = search.slice(1) } const pairs = search.split('&') for (let j = 0; j < pairs.length; j++) { const value = pairs[j] const index = value.indexOf('=') if (index > -1) { this.appendTo( dict, this.decode(value.slice(0, index)), this.decode(value.slice(index + 1)) ) } else if (value) { this.appendTo(dict, this.decode(value), '') } } } return dict } appendTo(dict: any, name: string, value: string | (() => void) | any): void { // eslint-disable-next-line no-nested-ternary const val = typeof value === 'string' ? value : value !== null && value !== undefined && typeof value.toString === 'function' ? value.toString() : JSON.stringify(value) if (name in dict) { dict[name].push(value) } else { dict[name] = [val] } } decode(str: string): string { return str .replace(/[ +]/g, '%20') .replace(/(%[a-f0-9]{2})+/gi, (match) => decodeURIComponent(match)) } encode(str: string[]): string { const replace: { [key: string]: string } = { '!': '%21', "'": '%27', '(': '%28', ')': '%29', '~': '%7E', '%20': '+', '%00': '\x00', } return encodeURIComponent(str.join(',')).replace( // eslint-disable-next-line no-useless-escape /[!'\(\)~]|%20|%00/g, (match) => replace[match] || '' ) } }