_URL module
/*/
 * _URL module
 * 
 * Copyright (c) 2020 7happy7
 * Released under the MIT license
 * http://topia.wikidot.com/local--files/url-mod/LICENSE.txt
/*/
 
/*
var url = new Url('http://uname:pswrd@topia.wikidot.com/url-mod/code/1?a[]=1&a[]=2#hash');
console.log(url)
// hash: "hash"
// host: "topia.wikidot.com"
// password: "pswrd"
// path: "/url-mod/code/1"
// port: undefined
// protocol: "http:"
// search: {a: ["1", "2"]}
// username: "uname"
// _parseStyle: {array: {arrayType: "with_bracket", indexed: false}}
// FormData: (...getter)
// origin: (...getter/setter)
 
url.search['a'].push(3);
console.log(url + '');// toString
// http://topia.wikidot.com/url-mod/code/1?a[]=1&a[]=2&a[]=3#hash
 
url.resolve('../x.php?b=txt#title');// getRelativePath
console.log(url + '');
// http://topia.wikidot.com/url-mod/x.php?b=txt#title
 
url.origin = 'http://www.scpwiki.com:80';
console.log(url)
// hash: "hash"
// host: "www.scpwiki.com"
// password: "pswrd"
// path: "/url-mod/code/1"
// port: "80"
// protocol: "http:"
// search: {a: ["1", "2"]}
// username: "uname"
// _parseStyle: {array: {arrayType: "with_bracket", indexed: false}}
// FormData: (...getter)
// origin: (...getter/setter)
 
// xhr
var xhr = new XMLHttpRequest;
xhr.addEventListener('load', e=>console.log(e.responseText));
xhr.open('POST', url);
xhr.send(url.FormData);
 
// fetch
fetch(url, {method: 'POST', body: url.FormData})
    .then(v=>v.text())
    .then(v=>console.log(v))
*/
 
(function(global){
    var REG = {
        URL: {
            R: new RegExp('^(?:([^:\\s]+:)|)(?:\\/\\/(?:([^:\\/\\?#]+?):([^:@]+?)@|)([^:\\/\\?#]+)(?::([0-9]+)|)|)(\\/?[^\\?#]*)(?:\\?([^\\?#]*)|)(?:#(.*)|)$','i'),
            G: ['protocol','username','password','host','port','path','search','hash']
        },
        ORIGIN: {
            R: new RegExp('^(?:([^:\\s]+:)|)(?:\\/\\/(?:[^:\\/\\?#]+?:[^:@]+?@|)([^:\\/\\?#]+)(?::([0-9]+)|)|)\\/?$','i'),
            G: ['protocol','host','port']
        }
    };
    var PARAM = {
        BASE: function(s) {// [['p', '1'], ['t', 'text']]
            return (s || '').split('&').filter(function(v){return v}).map(function(v){return (v = v.split('='), v[0] = decodeURI(v[0]).replace(/\+/g, ' '), v)})
        },
        ARRAY_TYPE: {
            'comma': {
                PARSE: function(s) {
                    var b = PARAM.BASE(s), o = {};
                    b.forEach(function(v){
                        v[1] = v[1].split(',').map(function(p){return decodeURI(p).replace(/\+/g, ' ')});
                        o[v[0]] = v[1].length - 1 ? v[1] : v[1][0];
                    });
                    return o;
                },
                TO_STR: function(o) {
                    return Object.keys(o).map(function(k) {
                        return (k + '=' + Array.isArray(o[k])
                            ? o[k].map(function(v){
                                return encodeURIComponent(v)
                                }).join(',')
                            : encodeURI(o[k]))
                    }).join('&')
                },
                TO_FORM: function(o) {
                    var f = new FormData;
                    return (Object.keys(o).forEach(function(k) {
                        f.append(k, o[k])
                    }), f)
                }
            },
            'sepalate': {
                PARSE: function(s) {
                    var b = PARAM.BASE(s), o = {};
                    b.forEach(function(v){
                        v[1] = decodeURI(v[1]).replace(/\+/g, ' '),
                        o[v[0]] = o[v[0]]
                            ? (function(p, a){
                                return ((a = Array.isArray(p) ? p : [p]).push(v[1]), a);
                            })(o[v[0]])
                            : v[1]
                    });
                    return o;
                },
                TO_STR: function(o) {
                    return Object.keys(o).map(function(k) {
                        return (Array.isArray(o[k])
                            ? o[k].map(function(v){
                                return k + '=' + encodeURI(v)
                                })
                            : k + '=' + encodeURI(o[k]))
                    }).reduce(function(a,v){
                        return a.concat(v)
                    },[]).join('&')
                },
                TO_FORM: function(o) {
                    var f = new FormData;
                    return (Object.keys(o).forEach(function(k) {
                        Array.isArray(o[k])
                            ? o[k].forEach(function(v){
                                f.append(k, v)
                                })
                            : f.append(k, o[k])
                    }), f)
                }
            },
            'with_bracket': {
                PARSE: function(s, i) {
                    var b = PARAM.BASE(s), o = {};
                    b.forEach(function(v){
                        var m;
                        v[1] = decodeURI(v[1]).replace(/\+/g, ' '),
                        (m=v[0].match(new RegExp(i ? '^(.+?)\\[(\\d+?)\\]$' : '^(.+?)\\[\\]$')))
                            ? (o[m[1]] = (function(p, a){
                                return (a = (p==void(0)) ? [] : (Array.isArray(p) ? p : [p]), (m[2] ? (a[Number(m[2])] = v[1]) : a.push(v[1])), a);
                            })(o[m[1]]))
                            : (o[v[0]] = v[1])
                    });
                    return o;
                },
                TO_STR: function(o, i) {
                    return Object.keys(o).map(function(k) {
                        return (Array.isArray(o[k])
                            ? o[k].map(function(v,_){
                                return k + '[' + (i ? _ : '') + ']' + '=' + encodeURI(v)
                                })
                            : k + '=' + encodeURI(o[k]))
                    }).reduce(function(a,v){
                        return a.concat(v)
                    },[]).join('&')
                },
                TO_FORM: function(o, i) {
                    var f = new FormData;
                    return (Object.keys(o).forEach(function(k) {
                        Array.isArray(o[k])
                            ? o[k].forEach(function(v,_){
                                f.append(k + '[' + (i ? _ : '') + ']', v)
                                })
                            : f.append(k, o[k])
                    }), f)
                }
            },
        }
    }
    function E(s) {
        var e = new Error(s);
        e.name = 'UrlError';
        throw e;
    }
    function Url(url, option) {
        var s = this, u, arrayType = (option && option.arrayType) || 'with_bracket', indexed = (option && option.indexed) || false;
        (u = (url ? String(url) : '').match(REG.URL.R))
            ? (REG.URL.G.forEach(function(g,i){
                s[g] = u[i+1]
                }),
                s.path = s.path || '/',
                s.search = (function(t){
                    return t ? t.PARSE(s.search, indexed) : (E('Invalid arrayType: "' + arrayType + '"'), void(0))
                })(PARAM.ARRAY_TYPE[arrayType]),
                s._parseStyle = {array: {arrayType: arrayType, indexed: indexed}},
                Object.defineProperties(s, {
                    'origin': {
                        get: function() {
                            return(s.protocol||'') + (s.host?('//'+s.host):'') + (s.port?(':'+s.port):'')
                        },
                        set: function(v) {
                            var o;
                            (o = String(v).match(REG.ORIGIN.R))
                                ? REG.ORIGIN.G.forEach(function(g,i){
                                    s[g] = o[i+1]
                                    })
                                : E('Invalid origin: "' + v + '"')
                        }
                    },
                    'FormData': {
                        get: function() {
                            return(function(t){
                                return t ? t.TO_FORM(s.search, indexed) : (E('Invalid arrayType: "' + arrayType + '"'), void(0))
                            })(PARAM.ARRAY_TYPE[arrayType])
                        }
                    }
                })
            )
            : E('Invalid URL: "' + url + '"');
    }
    Url.prototype.toString = function() {
        var s = this, a = s._parseStyle.array;
        return (s.protocol && s.host
            ? s.protocol + '//' + (s.username && s.password
                ? s.username + ':' + s.password + '@'
                : '')
                + s.host + (s.port ? ':' + s.port : '')
            : ''
        )
        + s.path
        + (Object.keys(s.search).length
            ? '?' + (function(t) {
                return t ? t.TO_STR(s.search, a.indexed).replace(/%20/g,'+') : (E('Invalid arrayType: "' + a.arrayType + '"'), '') 
                })(PARAM.ARRAY_TYPE[a.arrayType])
            : ''
        )
        + (s.hash ? '#' + s.hash : '')
    }
    Url.prototype.resolve = function(rel_path) {
        var rs, bs = this.path.split('/'), rl = new Url(rel_path, this._parseStyle.array), sp = rl.path.split('/').filter(function(v) {return v !== '.'});
        return((this.host || E('Base URL is not a absolute path: "' + this + '"')), (rl.host && E('"' + rel_path + '" is not a relative path')), bs.pop(), sp[0] == ''
            ? (rs = rl.path)
            : (function(v) {
                while((v = sp.shift())) {
                    v == '..'
                        ? bs.pop() || E('Invalid relative path: "' + rel_path + '"')
                        : bs.push(v)
                }
                rs = bs.join('/')
            })(),
        this.path = rs,
        this.search = rl.search,
        this.hash = rl.hash,
        this)
    }
 
    global.Url = Url;
})(this);
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License