mithril
Version:
A framework for building brilliant applications
200 lines (186 loc) • 5.07 kB
JavaScript
var parseURL = require("../test-utils/parseURL")
var callAsync = require("../test-utils/callAsync")
function debouncedAsync(f) {
var ref
return function() {
if (ref != null) return
ref = callAsync(function(){
ref = null
f()
})
}
}
module.exports = function(options) {
if (options == null) options = {}
var $window = options.window || {}
var protocol = options.protocol || "http:"
var hostname = options.hostname || "localhost"
var port = ""
var pathname = "/"
var search = ""
var hash = ""
var past = [{url: getURL(), isNew: true, state: null, title: null}], future = []
function getURL() {
if (protocol === "file:") return protocol + "//" + pathname + search + hash
return protocol + "//" + hostname + prefix(":", port) + pathname + search + hash
}
function setURL(value) {
var data = parseURL(value, {protocol: protocol, hostname: hostname, port: port, pathname: pathname})
var isNew = false
if (data.protocol != null && data.protocol !== protocol) protocol = data.protocol, isNew = true
if (data.hostname != null && data.hostname !== hostname) hostname = data.hostname, isNew = true
if (data.port != null && data.port !== port) port = data.port, isNew = true
if (data.pathname != null && data.pathname !== pathname) pathname = data.pathname, isNew = true
if (data.search != null && data.search !== search) search = data.search, isNew = true
if (data.hash != null && data.hash !== hash) {
hash = data.hash
if (!isNew) {
hashchange()
}
}
return isNew
}
function prefix(prefix, value) {
if (value === "") return ""
return (value.charAt(0) !== prefix ? prefix : "") + value
}
function _hashchange() {
if (typeof $window.onhashchange === "function") $window.onhashchange({type: "hashchange"})
}
var hashchange = debouncedAsync(_hashchange)
function popstate() {
if (typeof $window.onpopstate === "function") $window.onpopstate({type: "popstate", state: $window.history.state})
}
function unload() {
if (typeof $window.onunload === "function") $window.onunload({type: "unload"})
}
$window.location = {
get protocol() {
return protocol
},
get hostname() {
return hostname
},
get port() {
return port
},
get pathname() {
return pathname
},
get search() {
return search
},
get hash() {
return hash
},
get origin() {
if (protocol === "file:") return "null"
return protocol + "//" + hostname + prefix(":", port)
},
get host() {
if (protocol === "file:") return ""
return hostname + prefix(":", port)
},
get href() {
return getURL()
},
set protocol(value) {
throw new Error("Protocol is read-only")
},
set hostname(value) {
unload()
past.push({url: getURL(), isNew: true})
future = []
hostname = value
},
set port(value) {
if (protocol === "file:") throw new Error("Port is read-only under `file://` protocol")
unload()
past.push({url: getURL(), isNew: true})
future = []
port = value
},
set pathname(value) {
if (protocol === "file:") throw new Error("Pathname is read-only under `file://` protocol")
unload()
past.push({url: getURL(), isNew: true})
future = []
pathname = prefix("/", value)
},
set search(value) {
unload()
past.push({url: getURL(), isNew: true})
future = []
search = prefix("?", value)
},
set hash(value) {
var oldHash = hash
past.push({url: getURL(), isNew: false})
future = []
hash = prefix("#", value)
if (oldHash != hash) hashchange()
},
set origin(value) {
//origin is writable but ignored
},
set host(value) {
//host is writable but ignored in Chrome
},
set href(value) {
var url = getURL()
var isNew = setURL(value)
if (isNew) {
setURL(url)
unload()
setURL(value)
}
past.push({url: url, isNew: isNew})
future = []
},
}
$window.history = {
pushState: function(state, title, url) {
past.push({url: getURL(), isNew: false, state: state, title: title})
future = []
setURL(url)
},
replaceState: function(state, title, url) {
var entry = past[past.length - 1]
entry.state = state
entry.title = title
setURL(url)
},
back: function() {
if (past.length > 1) {
var entry = past.pop()
if (entry.isNew) unload()
future.push({url: getURL(), isNew: false, state: entry.state, title: entry.title})
setURL(entry.url)
if (!entry.isNew) popstate()
}
},
forward: function() {
var entry = future.pop()
if (entry != null) {
if (entry.isNew) unload()
past.push({url: getURL(), isNew: false, state: entry.state, title: entry.title})
setURL(entry.url)
if (!entry.isNew) popstate()
}
},
get state() {
return past.length === 0 ? null : past[past.length - 1].state
},
}
$window.onpopstate = null,
$window.onhashchange = null,
$window.onunload = null
$window.addEventListener = function (name, handler) {
$window["on" + name] = handler
}
$window.removeEventListener = function (name, handler) {
$window["on" + name] = handler
}
return $window
}