space-router
Version:
All the routing essentials.
94 lines (80 loc) • 2.06 kB
JavaScript
export function createHistory(options) {
const sync = options.sync
let mode = options.mode
let raf
let onPop
const memory = []
let off
let destroyed = false
if (typeof window === 'undefined') {
mode = 'memory'
raf = sync ? (cb) => cb() : global.setImmediate
} else {
raf = sync ? (cb) => cb() : requestAnimationFrame
if (mode === 'history' && !history.pushState) {
mode = 'hash'
}
}
function listen(onChange) {
onPop = () => onChange(getUrl())
if (mode !== 'memory') {
off = on(window, mode === 'history' ? 'popstate' : 'hashchange', onPop)
raf(onPop)
}
return () => {
destroyed = true
off && off()
}
}
return {
listen,
getUrl,
push(url) {
go(url)
},
replace(url) {
go(url, true)
},
}
function go(url, replace) {
if (destroyed) return
url = url.replace(/^\/?#?\/?/, '/').replace(/\/$/, '') || '/'
if (mode === 'history') {
history[replace ? 'replaceState' : 'pushState']({}, '', url)
raf(onPop)
} else if (mode === 'hash') {
location[replace ? 'replace' : 'assign']('#' + url)
} else if (mode === 'memory') {
replace ? (memory[memory.length - 1] = url) : memory.push(url)
raf(onPop)
}
}
function getUrl() {
if (mode === 'memory') {
return memory[memory.length - 1]
}
const hash = getHash()
if (mode === 'hash') {
return hash === '' ? '/' : hash
}
if (mode === 'history') {
let url = location.pathname + location.search
if (hash !== '') {
url += '#' + hash
}
return url
}
}
// Gets the true hash value. Cannot use location.hash directly due to bug
// in Firefox where location.hash will always be decoded.
function getHash() {
const match = location.href.match(/#(.*)$/)
return match ? match[1].replace('#', '') : ''
}
}
function on(el, type, fn) {
el.addEventListener(type, fn, false)
return function off() {
el.removeEventListener(type, fn, false)
}
}