dop-router
Version:
232 lines (206 loc) • 7.47 kB
JavaScript
import { register, set, del, collect, intercept, isRegistered } from 'dop'
const enc = encodeURIComponent
export function createLocation(url, object, prop = 'location') {
let shallWeEmit = false
let location
let urlparsed = parse(url)
if (object !== null && typeof object == 'object') {
if (isRegistered(object)) set(object, prop, urlparsed)
else {
object[prop] = urlparsed
object = register(object)
}
location = object[prop]
} else location = register(urlparsed)
location.toString = function() {
return location.href
}
intercept(location, (mutation, object) => {
if (!shallWeEmit) {
if (mutation.prop === 'href') {
object.href = mutation.oldValue
pushState(mutation.value)
setHref(getWindowLocation())
} else if (mutation.prop === 'pathname') {
let href = mutation.value
.split('/')
.map(enc)
.join('/')
if (mutation.value[0] !== '/') href = '/' + href
href = href + location.search + location.hash
object.pathname = mutation.oldValue
pushState(href)
setHref(getWindowLocation())
} else if (mutation.prop === 'search') {
let href =
mutation.value[0] === '?'
? mutation.value.substr(1)
: mutation.value
href = href
.split('&')
.map(param => {
let splited = param.split('=')
param = enc(splited[0] || '')
if (splited.hasOwnProperty(1))
param += '=' + enc(splited[1])
return param
})
.join('&')
href = location.pathname + '?' + href + location.hash
object.search = mutation.oldValue
pushState(href)
setHref(getWindowLocation())
} else if (mutation.prop === 'hash') {
let href =
mutation.value[0] === '#'
? mutation.value
: '#' + mutation.value
href = location.pathname + location.search + href
object.hash = mutation.oldValue
pushState(href)
setHref(getWindowLocation())
} else if (mutation.prop === 'path') {
let href =
'/' +
mutation.value.map(enc).join('/') +
location.search +
location.hash
pushState(href)
setHref(getWindowLocation(), mutation)
} else if (mutation.prop === 'query') {
let href
let prop
let query = mutation.value
let search = []
for (prop in query) {
search.push(enc(prop) + '=' + enc(query[prop]))
}
href =
location.pathname + '?' + search.join('&') + location.hash
pushState(href)
setHref(getWindowLocation())
} else
// origin, protocol, domain, url
object[mutation.prop] = mutation.oldValue
}
return shallWeEmit
})
intercept(location.path, (mutation, object) => {
if (!shallWeEmit) {
let path = location.path
object[mutation.prop] = enc(path[mutation.prop])
let href =
'/' +
path.filter(p => p !== undefined).join('/') +
location.search +
location.hash
if (href !== location.pathname) {
pushState(href)
setHref(getWindowLocation(), mutation)
}
}
return shallWeEmit
})
intercept(location.query, (mutation, object) => {
if (!shallWeEmit) {
let href
let query = location.query
let search = []
let prop = mutation.prop
// Is true if is not a delete
if (mutation.hasOwnProperty('value')) {
let propenc = enc(mutation.prop)
let valueenc = enc(mutation.value)
delete object[mutation.prop]
object[propenc] = valueenc
}
for (prop in query) {
search.push(prop + '=' + query[prop])
}
href = location.pathname + '?' + search.join('&') + location.hash
pushState(href)
setHref(getWindowLocation(), mutation)
}
return shallWeEmit
})
function setHref(href, mutation) {
let newlocation = parse(href)
newlocation.href = getHref(newlocation)
let collector = collect()
if (mutation !== undefined) collector.mutations.push(mutation)
shallWeEmit = true
set(location, 'href', newlocation.href)
set(location, 'pathname', newlocation.pathname)
set(location, 'search', newlocation.search)
set(location, 'hash', newlocation.hash)
// path
newlocation.path.forEach((path, index) =>
set(location.path, index, path)
)
set(location.path, 'length', newlocation.path.length)
// query
let prop
let newquery = newlocation.query
let query = location.query
for (prop in newquery) {
set(query, prop, newquery[prop])
}
for (prop in query) {
if (!newquery.hasOwnProperty(prop)) del(query, prop)
}
// emit
shallWeEmit = false
collector.emit()
}
// when user click back/forward on browser or change the hash
if (typeof window !== 'undefined')
window.addEventListener('popstate', function() {
setHref(getWindowLocation())
})
return location
}
function pushState(url, state, title) {
// if nodejs ... todo
window.history.pushState(state, title, url)
}
function getWindowLocation() {
// if nodejs ... todo
return window.location.href
}
function getHref(location) {
return location.pathname + location.search + location.hash
}
function parse(url) {
const match = /((.*):\/\/([^/#?]+))?([^?#]*)([^#]*)(.*)?/.exec(
decodeURIComponent(url)
)
const query = {}
const location = {
url: url,
origin: match[1],
protocol: match[2],
host: match[3],
pathname: match[4] === '' ? '/' : match[4],
path: match[4].split('/').filter(item => item.length > 0),
search: match[5],
query: query,
hash: match[6] || ''
}
location.href = getHref(location)
if (location.search.length > 1) {
location.search
.substr(1)
.split('&')
.forEach(item => {
if (item.length > 0) {
let equal = item.indexOf('=')
equal > -1
? (location.query[item.substr(0, equal)] = item.substr(
equal + 1
))
: (location.query[item] = '')
}
})
}
return location
}