Source: uri.js

/**
 * mindtouch-http.js - A JavaScript library to construct URLs and make HTTP requests using the fetch API
 *
 * Copyright (c) 2015 MindTouch Inc.
 * www.mindtouch.com  oss@mindtouch.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { UriParser } from './lib/uriParser.js';

/**
 * A class for parsing and manipulating URIs.
 */
export class Uri {

    /**
     * Construct a Uri from a supplied string.
     * @param {String} [url] The URI string to parse. If not supplied, an empty URI will be initialized.
     */
    constructor(url = '') {
        this.parsedUrl = new UriParser(url);
    }

    /**
     * Get the protocol portion of the Uri.
     */
    get protocol() {
        return this.parsedUrl.protocol;
    }

    /**
     * Set the protocol for the Uri.
     * @param {String} protocol The new protocol for the Uri. Must be one of `http:` or `https:`.
     */
    set protocol(protocol) {
        this.parsedUrl.protocol = protocol;
    }

    /**
     * Get the hostname portion of the Uri.
     */
    get hostname() {
        return this.parsedUrl.hostname;
    }

    /**
     * Set the hostname for the Uri.
     * @param {String} hostname The new hostname for the Uri.
     */
    set hostname(hostname) {
        this.parsedUrl.hostname = hostname;
    }

    /**
     * Get the origin portion of the Uri.
     */
    get origin() {
        return this.parsedUrl.origin;
    }

    /**
     * Get the path portion of the Uri.
     */
    get path() {
        return this.parsedUrl.pathname;
    }

    /**
     * Get the search portion of the Uri detailing the query parameters as a string.
     */
    get search() {
        return this.parsedUrl.params.count === 0 ? '' : `?${this.parsedUrl.params.toString()}`;
    }

    /**
     * Get the hash portion of the Uri.
     */
    get hash() {
        return this.parsedUrl.hash;
    }

    /**
     * Get a query parameter value.
     * @param {String} key The key of the query string value to fetch.
     * @returns {String} The value of the query parameter.
     */
    getQueryParam(key) {
        return this.parsedUrl.searchParams.get(key);
    }

    /**
     * Remove a query string parameter from the Uri.
     * @param {String} key The key of the query parameter to remove.
     */
    removeQueryParam(key) {
        this.parsedUrl.searchParams.delete(key);
    }

    /**
     * Additively sets a query string parameter. It is possible to add multiple query parameter values with the same key using this function.
     * @param {String} key The key of the query parameter to add.
     * @param {String|Number|Boolean} value The value of the added query parameter.
     */
    addQueryParam(key, value) {
        const paramVal = value === null || typeof value === 'undefined' ? '' : encodeURIComponent(value);
        this.parsedUrl.searchParams.append(key, paramVal);
    }

    /**
     * Add a batch of query parameters.
     * @param {Object} queryMap A list of key-value pairs to add as query parameters. It is possible to add multiple query parameter values with the same key using this function.
     */
    addQueryParams(queryMap) {
        Object.keys(queryMap).forEach((key) => {
            this.addQueryParam(key, queryMap[key]);
        });
    }

    /**
     * Set the value of an existing query parameter, or add it if it does not exist.
     * @param {String} key The key of the query parameter to add.
     * @param {String|Number|Boolean} value The value of the added query parameter.
     */
    setQueryParam(key, value) {
        this.removeQueryParam(key);
        this.addQueryParam(key, value);
    }

    /**
     * Set a batch of query parameters.
     * @param {Object} queryMap A list of key-value pairs to set as query parameters. If any of the keys of this input object conflict with existing query parameter keys, they will be replaced.
     */
    setQueryParams(queryMap) {
        Object.keys(queryMap).forEach((key) => {
            this.setQueryParam(key, queryMap[key]);
        });
    }

    /**
     * Add path segments to the Uri.
     * @param {...String} segments The segments to be added to the Uri.
     */
    addSegments(...segments) {
        let path = '';
        segments.forEach((segment) => {
            if(Array.isArray(segment)) {
                segment.forEach((arraySegment) => {
                    if(arraySegment[0] === '/') {
                        arraySegment = arraySegment.slice(1);
                    }
                    path = `${path}/${arraySegment}`;
                });
            } else {
                if(segment[0] === '/') {
                    segment = segment.slice(1);
                }
                path = `${path}/${segment}`;
            }
        });
        let pathName = this.parsedUrl.pathname;
        if(pathName === '/') {
            pathName = '';
        }
        this.parsedUrl.pathname = `${pathName}${path}`;
    }

    /**
     * Get a string representation of the current state of the Uri.
     * @returns {String} The Uri string.
     */
    toString() {
        return this.parsedUrl.toString();
    }
}