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, ',') const decode = decodeURIComponent export function parseQuery (query) { 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, encodeStr = encode) { const res = obj ? Object.keys(obj).map(key => { const val = obj[key] if (val === undefined) { return '' } if (val === null) { return encodeStr(key) } if (Array.isArray(val)) { const result = [] val.forEach(val2 => { if (val2 === undefined) { return } if (val2 === null) { result.push(encodeStr(key)) } else { result.push(encodeStr(key) + '=' + encodeStr(val2)) } }) return result.join('&') } return encodeStr(key) + '=' + encodeStr(val) }).filter(x => x.length > 0).join('&') : null return res ? `?${res}` : '' } export function decodedQuery (query = {}) { const decodedQuery = {} Object.keys(query).forEach(name => { try { decodedQuery[name] = decode(query[name]) } catch (e) { decodedQuery[name] = query[name] } }) return decodedQuery }