ease-url
Version:
Wow, such a ease url parser
193 lines (149 loc) • 4.92 kB
text/typescript
export type Url = {
path: string,
host: string,
port: number,
hash: string,
scheme?: string,
query?: Array<{key: string, value: string}>
}
enum HashPos {
BEFORE,
AFTER
}
export class EaseUrl {
static parse(url?: string | Url) {
return new EaseUrl(url)
}
public path: string = '/'
public host: string = ''
public hash: string = ''
public scheme: string= 'https'
public query: Query
private _port: number = undefined
private _hashPos: HashPos = HashPos.AFTER
public set port(v: number) {
this._port = v
}
public get port() {
return this._port ?? this.defaultPorts[this.scheme]
}
public get origin() {
let port = this.defaultPorts[this.scheme] == this.port ? undefined : this.port
return `${this.scheme ? this.scheme + '://' : '' }${this.host}${port ? ':' + port : ''}`
}
public get url() {
let befHash = this._hashPos == HashPos.BEFORE ? this.hash : '',
aftHash = this._hashPos == HashPos.AFTER ? this.hash : ''
return `${this.origin}${this.path}${befHash}${this.query.params.length ? '?' + this.query.search : ''}${aftHash}`
}
private get defaultPorts() {
return {
http: 80,
https: 443
}
}
constructor(url?: string | Url ) {
this.query = new Query()
url && this.parse(url)
}
parse(url: string | Url) {
if (typeof url == 'string') {
let parsed = (url as String).split('?'),
find = parsed[0].split('#')
if (find.length > 1) {
this._hashPos = HashPos.BEFORE
this.hash = '#' + find[1]
}
find = find[0].match(/(([a-z]+):\/\/)?([0-9a-z\.\-]+)(:([0-9]{2,7}))?(\/.*)?(#.*?)?/i)
if (find) {
this.scheme = find[2] ?? 'https'
this.host = find[3]
this.path = find[6] ?? '/'
this._port = Number(find[5]) || undefined
}
if (parsed.length > 1) {
find = parsed[1].split('#')
if (find.length > 1) {
this._hashPos = HashPos.AFTER
this.hash = '#' + find[1]
}
this.query.parse(find[0])
}
}
else if (typeof url == 'object') {
this.host = url.host
this.path = url.path || '/'
this.hash = url.hash || ''
this.scheme = url.scheme || 'https'
this._port = url.port
this.query.parse(url.query)
}
}
}
export type QueryParam = {
key: string,
value: string | number
}
export class Query {
params: Array<QueryParam> = []
constructor(query: string | Array<QueryParam> | Object = []) {
this.parse(query)
}
public parse(query: string | Array<QueryParam> | Object = []) {
const queryType = query.constructor
this.params.length = 0
if (queryType == Object) {
for(let key in query as object) {
this.params.push({key: key, value: query[key]})
}
}
else if (queryType == Array) {
(query as Array<QueryParam>).forEach((item) => {
this.params.push({key: item.key, value: item.value})
});
}
else if (queryType == String) {
(query as string).split('&').forEach((item) => {
const param = item.match(/([0-9a-z\.\-\_\[\]]+)(=(.*))?/i)
if (param) {
this.params.push({key: param[1], value: param[3] || ''})
}
})
}
}
public get dict() : {[key: string]: string | number} {
const dict = {}
this.params.forEach((item) => {
if (dict[item.key] != undefined) {
if (typeof(dict[item.key]) == 'object') {
dict[item.key].push(item.value)
}
else {
dict[item.key] = [dict[item.key], item.value]
}
}
else {
dict[item.key] = item.value
}
});
return dict
}
public get search() {
const query = [];
this.params.forEach((item) => {
query.push(`${item.key}=${item.value}`)
})
return query.length ? query.join('&') : ''
}
public add(key: string, value: string | number) {
this.params.push({key: key, value: value})
}
public remove(key: string) {
for (let i in this.params) {
if (this.params[i].key == key) {
this.params.splice(Number(i), 1)
break
}
}
}
}