Clarify pathname docs, follow spec with fragments (#2448)
* Clarify pathname docs, follow spec with fragments - Valid URLs must not contain a `#` within its fragment. https://github.com/MithrilJS/mithril.js/issues/2445 - Our docs were a little confusing and misleading - `m.pathname` isn't aware of URLs, just path names. - Removed the relevant extension to `m.parseQueryString` required to support the hash parsing extension. Now we just shave it off and ignore it. - Fix support for arbitrary prefixes, so prefixes like `?#` are handled correctly. - Add a bunch of tests to cover various areas of confusion and unusual edge cases. * Update with PR [skip ci]
This commit is contained in:
parent
9e9b89d900
commit
85bfd0f77d
15 changed files with 85 additions and 127 deletions
|
|
@ -9,18 +9,25 @@ module.exports = function($window) {
|
|||
var supportsPushState = typeof $window.history.pushState === "function"
|
||||
var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout
|
||||
|
||||
function normalize(fragment) {
|
||||
var data = $window.location[fragment].replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent)
|
||||
if (fragment === "pathname" && data[0] !== "/") data = "/" + data
|
||||
return data
|
||||
}
|
||||
|
||||
var asyncId
|
||||
var router = {prefix: "#!"}
|
||||
router.getPath = function() {
|
||||
if (router.prefix.charAt(0) === "#") return normalize("hash").slice(router.prefix.length)
|
||||
if (router.prefix.charAt(0) === "?") return normalize("search").slice(router.prefix.length) + normalize("hash")
|
||||
return normalize("pathname").slice(router.prefix.length) + normalize("search") + normalize("hash")
|
||||
// Consider the pathname holistically. The prefix might even be invalid,
|
||||
// but that's not our problem.
|
||||
var prefix = $window.location.hash
|
||||
if (router.prefix[0] !== "#") {
|
||||
prefix = $window.location.search + prefix
|
||||
if (router.prefix[0] !== "?") {
|
||||
prefix = $window.location.pathname + prefix
|
||||
if (prefix[0] !== "/") prefix = "/" + prefix
|
||||
}
|
||||
}
|
||||
// This seemingly useless `.concat()` speeds up the tests quite a bit,
|
||||
// since the representation is consistently a relatively poorly
|
||||
// optimized cons string.
|
||||
return prefix.concat()
|
||||
.replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent)
|
||||
.slice(router.prefix.length)
|
||||
}
|
||||
|
||||
router.setPath = function(path, data, options) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ var Router = require("../../router/router")
|
|||
|
||||
o.spec("Router.defineRoutes", function() {
|
||||
void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) {
|
||||
void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) {
|
||||
void ["#", "?", "", "#!", "?!", "/foo", "?#", "##"].forEach(function(prefix) {
|
||||
o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() {
|
||||
var $window, router, onRouteChange, onFail
|
||||
|
||||
|
|
@ -44,12 +44,12 @@ o.spec("Router.defineRoutes", function() {
|
|||
})
|
||||
|
||||
o("resolves to route w/ escaped unicode", function(done) {
|
||||
$window.location.href = prefix + "/%C3%B6?%C3%B6=%C3%B6#%C3%B6=%C3%B6"
|
||||
$window.location.href = prefix + "/%C3%B6?%C3%B6=%C3%B6"
|
||||
router.defineRoutes({"/ö": {data: 2}}, onRouteChange, onFail)
|
||||
|
||||
callAsync(function() {
|
||||
o(onRouteChange.callCount).equals(1)
|
||||
o(onRouteChange.args).deepEquals([{data: 2}, {"ö": "ö"}, "/ö?ö=ö#ö=ö", "/ö"])
|
||||
o(onRouteChange.args).deepEquals([{data: 2}, {"ö": "ö"}, "/ö?ö=ö", "/ö"])
|
||||
o(onFail.callCount).equals(0)
|
||||
|
||||
done()
|
||||
|
|
@ -57,12 +57,12 @@ o.spec("Router.defineRoutes", function() {
|
|||
})
|
||||
|
||||
o("resolves to route w/ unicode", function(done) {
|
||||
$window.location.href = prefix + "/ö?ö=ö#ö=ö"
|
||||
$window.location.href = prefix + "/ö?ö=ö"
|
||||
router.defineRoutes({"/ö": {data: 2}}, onRouteChange, onFail)
|
||||
|
||||
callAsync(function() {
|
||||
o(onRouteChange.callCount).equals(1)
|
||||
o(onRouteChange.args).deepEquals([{data: 2}, {"ö": "ö"}, "/ö?ö=ö#ö=ö", "/ö"])
|
||||
o(onRouteChange.args).deepEquals([{data: 2}, {"ö": "ö"}, "/ö?ö=ö", "/ö"])
|
||||
o(onFail.callCount).equals(0)
|
||||
|
||||
done()
|
||||
|
|
@ -138,45 +138,6 @@ o.spec("Router.defineRoutes", function() {
|
|||
})
|
||||
})
|
||||
|
||||
o("handles route with hash", function(done) {
|
||||
$window.location.href = prefix + "/test#a=b&c=d"
|
||||
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
|
||||
|
||||
callAsync(function() {
|
||||
o(onRouteChange.callCount).equals(1)
|
||||
o(onRouteChange.args).deepEquals([{data: 1}, {a: "b", c: "d"}, "/test#a=b&c=d", "/test"])
|
||||
o(onFail.callCount).equals(0)
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
o("handles route with search and hash", function(done) {
|
||||
$window.location.href = prefix + "/test?a=b#c=d"
|
||||
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
|
||||
|
||||
callAsync(function() {
|
||||
o(onRouteChange.callCount).equals(1)
|
||||
o(onRouteChange.args).deepEquals([{data: 1}, {a: "b", c: "d"}, "/test?a=b#c=d", "/test"])
|
||||
o(onFail.callCount).equals(0)
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
o("handles route with search and hash + duplicate params", function(done) {
|
||||
$window.location.href = prefix + "/test?a=b#a=d"
|
||||
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
|
||||
|
||||
callAsync(function() {
|
||||
o(onRouteChange.callCount).equals(1)
|
||||
o(onRouteChange.args).deepEquals([{data: 1}, {a: "d"}, "/test?a=b#a=d", "/test"])
|
||||
o(onFail.callCount).equals(0)
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
o("calls reject", function(done) {
|
||||
$window.location.href = prefix + "/test"
|
||||
router.defineRoutes({"/other": {data: 1}}, onRouteChange, onFail)
|
||||
|
|
@ -189,18 +150,6 @@ o.spec("Router.defineRoutes", function() {
|
|||
})
|
||||
})
|
||||
|
||||
o("calls reject w/ search and hash", function(done) {
|
||||
$window.location.href = prefix + "/test?a=b#c=d"
|
||||
router.defineRoutes({"/other": {data: 1}}, onRouteChange, onFail)
|
||||
|
||||
callAsync(function() {
|
||||
o(onFail.callCount).equals(1)
|
||||
o(onFail.args).deepEquals(["/test?a=b#c=d", {a: "b", c: "d"}])
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
o("handles out of order routes", function(done) {
|
||||
$window.location.href = prefix + "/z/y/x"
|
||||
router.defineRoutes({"/z/y/x": {data: 1}, "/:a...": {data: 2}}, onRouteChange, onFail)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ var Router = require("../../router/router")
|
|||
|
||||
o.spec("Router.getPath", function() {
|
||||
void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) {
|
||||
void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) {
|
||||
void ["#", "?", "", "#!", "?!", "/foo", "?#", "##"].forEach(function(prefix) {
|
||||
o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() {
|
||||
var $window, router, onRouteChange, onFail
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue