source/http/UrlParser.js
import QueryString from "./QueryString";
/**
* URL parser.
*
* @example
* const urlparser = new UrlParser('http://example.com/api/people?name=Jane');
* urlparser.queryString.set('search', 'doe');
* // urlparser.buildUrl() === 'http://example.com/api/people?name=Jane&search=doe'
*/
export class UrlParser {
/**
* Join URL paths.
*
* @example
* UrlParser.pathJoin('/test', 'user/') // == '/test/user/'
* UrlParser.pathJoin('/test/', 'user/') // == '/test/user/'
* UrlParser.pathJoin('/test/', '/user/') // == '/test/user/'
* UrlParser.pathJoin('/test') // == '/test'
* UrlParser.pathJoin('http://example.com/test/', '/user/', 10) // == http://example.com/test/user/10
*
* @param firstPath The first path. Can be an URL.
* @param paths Paths to join with the first path.
* @returns {string} The resulting path/url after joining.
*/
static pathJoin(firstPath, ...paths) {
let outputPath = firstPath;
for(let path of paths) {
path = `${path}`;
if(outputPath.endsWith('/')) {
outputPath = outputPath.substring(0, outputPath.length - 1);
}
if(path.startsWith('/')) {
path = path.substring(1);
}
outputPath = `${outputPath}/${path}`;
}
return outputPath;
}
constructor(url) {
if(typeof url !== 'string') {
throw new TypeError('url must be a string.');
}
const urlSplit = url.split('?');
this._baseUrl = urlSplit[0];
this._parsedBaseUrl = this._parseBaseUrl();
/**
* The query-string of the the URL.
* @type {QueryString}
*/
this.queryString = null;
if(urlSplit.length > 1) {
this.setQueryString(new QueryString(urlSplit[1]));
} else {
this.setQueryString(new QueryString());
}
}
/**
* Create a deep copy of this UrlParser object.
*
* @return The copy.
*/
deepCopy () {
let copy = Object.assign(Object.create(this), this)
if(this.queryString !== null) {
copy.queryString = this.queryString.deepCopy()
}
return copy
}
_splitDomainAndPath(domainAndPath) {
let split = domainAndPath.split('/');
let domain = split.shift();
let path = '';
if(split.length > 0) {
path = `/${split.join('/')}`;
}
return {
domain: domain,
path: path
}
}
_parseBaseUrl() {
let parsedBaseUrl = {
scheme: null,
path: '',
domain: null
};
if(this._baseUrl.match(/^[a-zA-Z0-9]+:\/\//)) {
// We have a full URL (<scheme>://<domain><path>)
let split = this._baseUrl.split('://');
parsedBaseUrl.scheme = split.shift();
let remaining = split.join('://');
let domainAndPath = this._splitDomainAndPath(remaining);
parsedBaseUrl.domain = domainAndPath.domain;
parsedBaseUrl.path = domainAndPath.path;
} else if(this._baseUrl.length > 0 && this._baseUrl.substring(0, 1) == '/') {
// We have path only
parsedBaseUrl.path = `${this._baseUrl}`;
} else {
// We have domain and path, but no scheme (<domain><path>)
let domainAndPath = this._splitDomainAndPath(this._baseUrl);
parsedBaseUrl.domain = domainAndPath.domain;
parsedBaseUrl.path = domainAndPath.path;
}
return parsedBaseUrl;
}
get scheme() {
return this._parsedBaseUrl.scheme;
}
get path() {
return this._parsedBaseUrl.path;
}
get domain() {
return this._parsedBaseUrl.domain;
}
/**
* Build the URL.
* @returns {String} The built URL.
*/
buildUrl() {
let url = this._baseUrl;
if(!this.queryString.isEmpty()) {
url = `${url}?${this.queryString.urlencode()}`;
}
return url;
}
/**
* Set/replace the query-string.
*
* @param {QueryString} queryStringObject The QueryString object
* to replace the current query-string with.
*
* @example
* const urlparser = UrlParser('http://example.com/api/people');
* const querystring = new QueryString();
* querystring.set('search', 'doe');
* urlparser.setQueryString(querystring);
* // urlparser.buildUrl() === 'http://example.com/api/people?search=doe'
*/
setQueryString(queryStringObject) {
this.queryString = queryStringObject;
}
}