/* @flow */
|
|
import { warn } from './warn'
|
|
const encodeReserveRE = /[!'()*]/g
|
const encodeReserveReplacer = c => '%' + c.charCodeAt(0).toString(16)
|
const commaRE = /%2C/g
|
|
// fixed encodeURIComponent which is more conformant to RFC3986:
|
// - escapes [!'()*]
|
// - preserve commas
|
const encode = str =>
|
encodeURIComponent(str)
|
.replace(encodeReserveRE, encodeReserveReplacer)
|
.replace(commaRE, ',')
|
|
export function decode (str: string) {
|
try {
|
return decodeURIComponent(str)
|
} catch (err) {
|
if (process.env.NODE_ENV !== 'production') {
|
warn(false, `Error decoding "${str}". Leaving it intact.`)
|
}
|
}
|
return str
|
}
|
|
export function resolveQuery (
|
query: ?string,
|
extraQuery: Dictionary<string> = {},
|
_parseQuery: ?Function
|
): Dictionary<string> {
|
const parse = _parseQuery || parseQuery
|
let parsedQuery
|
try {
|
parsedQuery = parse(query || '')
|
} catch (e) {
|
process.env.NODE_ENV !== 'production' && warn(false, e.message)
|
parsedQuery = {}
|
}
|
for (const key in extraQuery) {
|
const value = extraQuery[key]
|
parsedQuery[key] = Array.isArray(value)
|
? value.map(castQueryParamValue)
|
: castQueryParamValue(value)
|
}
|
return parsedQuery
|
}
|
|
const castQueryParamValue = value => (value == null || typeof value === 'object' ? value : String(value))
|
|
function parseQuery (query: string): Dictionary<string> {
|
const res = {}
|
|
query = query.trim().replace(/^(\?|#|&)/, '')
|
|
if (!query) {
|
return res
|
}
|
|
query.split('&').forEach(param => {
|
const parts = param.replace(/\+/g, ' ').split('=')
|
const key = decode(parts.shift())
|
const val = parts.length > 0 ? decode(parts.join('=')) : null
|
|
if (res[key] === undefined) {
|
res[key] = val
|
} else if (Array.isArray(res[key])) {
|
res[key].push(val)
|
} else {
|
res[key] = [res[key], val]
|
}
|
})
|
|
return res
|
}
|
|
export function stringifyQuery (obj: Dictionary<string>): string {
|
const res = obj
|
? Object.keys(obj)
|
.map(key => {
|
const val = obj[key]
|
|
if (val === undefined) {
|
return ''
|
}
|
|
if (val === null) {
|
return encode(key)
|
}
|
|
if (Array.isArray(val)) {
|
const result = []
|
val.forEach(val2 => {
|
if (val2 === undefined) {
|
return
|
}
|
if (val2 === null) {
|
result.push(encode(key))
|
} else {
|
result.push(encode(key) + '=' + encode(val2))
|
}
|
})
|
return result.join('&')
|
}
|
|
return encode(key) + '=' + encode(val)
|
})
|
.filter(x => x.length > 0)
|
.join('&')
|
: null
|
return res ? `?${res}` : ''
|
}
|