Merge branch 'rewrite' of https://github.com/lhorie/mithril.js into call-async

This commit is contained in:
impinball 2016-08-03 20:32:38 -04:00
commit 2c3a0ebb3a
15 changed files with 802 additions and 717 deletions

View file

@ -7,16 +7,16 @@ var autoredraw = require("../api/autoredraw")
module.exports = function($window, renderer, pubsub) {
var router = coreRouter($window)
var route = function(root, defaultRoute, routes) {
var current = {route: null, component: null}
var current = {path: null, component: null}
var replay = router.defineRoutes(routes, function(payload, args, path, route) {
if (typeof payload.view !== "function") {
if (typeof payload.render !== "function") payload.render = function(vnode) {return vnode}
var render = function(component) {
current.route = route, current.component = component
current.path = path, current.component = component
renderer.render(root, payload.render(Vnode(component, null, args, undefined, undefined, undefined)))
}
if (typeof payload.resolve !== "function") payload.resolve = function() {render(current.component)}
if (route !== current.route) payload.resolve(render, args, path, route)
if (path !== current.path) payload.resolve(render, args, path, route)
else render(current.component)
}
else {

View file

@ -68,6 +68,71 @@ o.spec("mount", function() {
}, FRAME_BUDGET)
})
o("redraws several mount points on events", function(done) {
var onupdate0 = o.spy()
var oninit0 = o.spy()
var onclick0 = o.spy()
var onupdate1 = o.spy()
var oninit1 = o.spy()
var onclick1 = o.spy()
var e = $window.document.createEvent("MouseEvents")
e.initEvent("click", true, true)
render(root, [
m("#child0"),
m("#child1")
])
mount(root.childNodes[0], {
view : function() {
return m("div", {
oninit : oninit0,
onupdate : onupdate0,
onclick : onclick0,
})
}
})
o(oninit0.callCount).equals(1)
o(onupdate0.callCount).equals(0)
mount(root.childNodes[1], {
view : function() {
return m("div", {
oninit : oninit1,
onupdate : onupdate1,
onclick : onclick1,
})
}
})
o(oninit1.callCount).equals(1)
o(onupdate1.callCount).equals(0)
root.childNodes[0].firstChild.dispatchEvent(e)
o(onclick0.callCount).equals(1)
o(onclick0.this).equals(root.childNodes[0].firstChild)
setTimeout(function() {
o(onupdate0.callCount).equals(1)
o(onupdate1.callCount).equals(1)
root.childNodes[1].firstChild.dispatchEvent(e)
o(onclick1.callCount).equals(1)
o(onclick1.this).equals(root.childNodes[1].firstChild)
setTimeout(function() {
o(onupdate0.callCount).equals(2)
o(onupdate1.callCount).equals(2)
done()
}, FRAME_BUDGET)
}, FRAME_BUDGET)
})
o("event handlers can skip redraw", function(done) {
var onupdate = o.spy()
var oninit = o.spy()

View file

@ -11,325 +11,327 @@ var apiPubSub = require("../../api/pubsub")
var apiRouter = require("../../api/router")
o.spec("route", function() {
void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) {
o.spec("using prefix `" + prefix + "`", function() {
var FRAME_BUDGET = Math.floor(1000 / 60)
var $window, root, redraw, route
void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) {
void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) {
o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() {
var FRAME_BUDGET = Math.floor(1000 / 60)
var $window, root, redraw, route
o.beforeEach(function() {
$window = {}
o.beforeEach(function() {
$window = {}
var dom = domMock()
for (var key in dom) $window[key] = dom[key]
var dom = domMock()
for (var key in dom) $window[key] = dom[key]
var loc = pushStateMock()
for (var key in loc) $window[key] = loc[key]
var loc = pushStateMock(env)
for (var key in loc) $window[key] = loc[key]
root = $window.document.body
root = $window.document.body
redraw = apiPubSub()
route = apiRouter($window, coreRenderer($window), redraw)
route.prefix(prefix)
})
redraw = apiPubSub()
route = apiRouter($window, coreRenderer($window), redraw)
route.prefix(prefix)
})
o("renders into `root`", function(done) {
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
view: function() {
return m("div")
o("renders into `root`", function(done) {
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
view: function() {
return m("div")
}
}
}
})
callAsync(function() {
o(root.firstChild.nodeName).equals("DIV")
done()
})
})
o("default route doesn't break back button", function(done) {
$window.location.href = "http://google.com"
route(root, "/a", {
"/a" : {
view: function() {
return m("div")
}
}
})
setTimeout(function() {
o(root.firstChild.nodeName).equals("DIV")
$window.history.back()
o($window.location.pathname).equals("/")
done()
}, FRAME_BUDGET)
})
o("default route does not inherit params", function(done) {
$window.location.href = "/invalid?foo=bar"
route(root, "/a", {
"/a" : {
oninit: init,
view: function() {
return m("div")
}
}
})
function init(vnode) {
o(vnode.attrs).deepEquals({})
done()
}
})
o("redraws when render function is executed", function(done) {
var onupdate = o.spy()
var oninit = o.spy()
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
view: function() {
return m("div", {
oninit: oninit,
onupdate: onupdate
})
}
}
})
callAsync(function() {
o(oninit.callCount).equals(1)
redraw.publish()
// Wrapped to give time for the rate-limited redraw to fire
setTimeout(function() {
o(onupdate.callCount).equals(1)
})
callAsync(function() {
o(root.firstChild.nodeName).equals("DIV")
done()
}, FRAME_BUDGET)
})
})
})
o("redraws on events", function(done) {
var onupdate = o.spy()
var oninit = o.spy()
var onclick = o.spy()
var e = $window.document.createEvent("MouseEvents")
e.initEvent("click", true, true)
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
view: function() {
return m("div", {
oninit: oninit,
onupdate: onupdate,
onclick: onclick,
})
o("default route doesn't break back button", function(done) {
$window.location.href = "http://google.com"
route(root, "/a", {
"/a" : {
view: function() {
return m("div")
}
}
}
})
callAsync(function() {
root.firstChild.dispatchEvent(e)
o(oninit.callCount).equals(1)
o(onclick.callCount).equals(1)
o(onclick.this).equals(root.firstChild)
o(onclick.args[0].type).equals("click")
o(onclick.args[0].target).equals(root.firstChild)
// Wrapped to give time for the rate-limited redraw to fire
setTimeout(function() {
o(onupdate.callCount).equals(1)
done()
}, FRAME_BUDGET)
})
})
o("event handlers can skip redraw", function(done) {
var onupdate = o.spy()
var oninit = o.spy()
var onclick = o.spy()
var e = $window.document.createEvent("MouseEvents")
e.initEvent("click", true, true)
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
view: function() {
return m("div", {
oninit: oninit,
onupdate: onupdate,
onclick: function(e) {
e.redraw = false
},
})
}
}
})
callAsync(function() {
root.firstChild.dispatchEvent(e)
o(oninit.callCount).equals(1)
// Wrapped to ensure no redraw fired
setTimeout(function() {
o(onupdate.callCount).equals(0)
done()
}, FRAME_BUDGET)
})
})
o("changes location on route.link", function(done) {
var e = $window.document.createEvent("MouseEvents")
e.initEvent("click", true, true)
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
view: function() {
return m("a", {
href: "/test",
oncreate: route.link
})
}
},
"/test" : {
view : function() {
return m("div")
}
}
})
callAsync(function() {
var slash = prefix[0] === "/" ? "" : "/"
o($window.location.href).equals("http://localhost" + slash + (prefix ? prefix + "/" : ""))
root.firstChild.dispatchEvent(e)
o($window.location.href).equals("http://localhost" + slash + (prefix ? prefix + "/" : "") + "test")
done()
})
})
o("accepts object as payload", function(done) {
var Component = {
view: function() {
return m("div")
}
}
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
resolve: function(resolve) {resolve(Component)},
render: function(vnode) {return vnode},
},
})
callAsync(function() {
o(root.firstChild.nodeName).equals("DIV")
done()
})
})
o("accepts object without `render` method as payload", function(done) {
var Component = {
view: function() {
return m("div")
}
}
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
resolve: function(resolve) {resolve(Component)},
},
})
callAsync(function() {
o(root.firstChild.nodeName).equals("DIV")
done()
})
})
o("accepts object without `resolve` method as payload", function(done) {
var Component = {
view: function() {
return m("div")
}
}
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
render: function() {return m(Component)},
},
})
callAsync(function() {
o(root.firstChild.nodeName).equals("DIV")
done()
})
})
o("calls resolve and render correct number of times", function(done) {
var resolveCount = 0
var renderCount = 0
var Component = {
view: function() {
return m("div")
}
}
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
resolve: function(resolve) {
resolveCount++
resolve(Component)
},
render: function(vnode) {
renderCount++
return vnode
},
},
})
callAsync(function() {
o(resolveCount).equals(1)
o(renderCount).equals(1)
redraw.publish()
})
setTimeout(function() {
o(resolveCount).equals(1)
o(renderCount).equals(2)
o(root.firstChild.nodeName).equals("DIV")
$window.history.back()
o($window.location.pathname).equals("/")
done()
}, FRAME_BUDGET)
})
o("default route does not inherit params", function(done) {
$window.location.href = "/invalid?foo=bar"
route(root, "/a", {
"/a" : {
oninit: init,
view: function() {
return m("div")
}
}
})
function init(vnode) {
o(vnode.attrs).deepEquals({})
done()
}
})
o("redraws when render function is executed", function(done) {
var onupdate = o.spy()
var oninit = o.spy()
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
view: function() {
return m("div", {
oninit: oninit,
onupdate: onupdate
})
}
}
})
callAsync(function() {
o(oninit.callCount).equals(1)
redraw.publish()
// Wrapped to give time for the rate-limited redraw to fire
setTimeout(function() {
o(onupdate.callCount).equals(1)
done()
}, FRAME_BUDGET)
})
})
o("redraws on events", function(done) {
var onupdate = o.spy()
var oninit = o.spy()
var onclick = o.spy()
var e = $window.document.createEvent("MouseEvents")
e.initEvent("click", true, true)
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
view: function() {
return m("div", {
oninit: oninit,
onupdate: onupdate,
onclick: onclick,
})
}
}
})
callAsync(function() {
root.firstChild.dispatchEvent(e)
o(oninit.callCount).equals(1)
o(onclick.callCount).equals(1)
o(onclick.this).equals(root.firstChild)
o(onclick.args[0].type).equals("click")
o(onclick.args[0].target).equals(root.firstChild)
// Wrapped to give time for the rate-limited redraw to fire
setTimeout(function() {
o(onupdate.callCount).equals(1)
done()
}, FRAME_BUDGET)
})
})
o("event handlers can skip redraw", function(done) {
var onupdate = o.spy()
var oninit = o.spy()
var onclick = o.spy()
var e = $window.document.createEvent("MouseEvents")
e.initEvent("click", true, true)
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
view: function() {
return m("div", {
oninit: oninit,
onupdate: onupdate,
onclick: function(e) {
e.redraw = false
},
})
}
}
})
callAsync(function() {
root.firstChild.dispatchEvent(e)
o(oninit.callCount).equals(1)
// Wrapped to ensure no redraw fired
setTimeout(function() {
o(onupdate.callCount).equals(0)
done()
}, FRAME_BUDGET)
})
})
o("changes location on route.link", function(done) {
var e = $window.document.createEvent("MouseEvents")
e.initEvent("click", true, true)
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
view: function() {
return m("a", {
href: "/test",
oncreate: route.link
})
}
},
"/test" : {
view : function() {
return m("div")
}
}
})
callAsync(function() {
var slash = prefix[0] === "/" ? "" : "/"
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : ""))
root.firstChild.dispatchEvent(e)
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : "") + "test")
done()
})
})
o("accepts object as payload", function(done) {
var Component = {
view: function() {
return m("div")
}
}
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
resolve: function(resolve) {resolve(Component)},
render: function(vnode) {return vnode},
},
})
callAsync(function() {
o(root.firstChild.nodeName).equals("DIV")
done()
})
})
o("accepts object without `render` method as payload", function(done) {
var Component = {
view: function() {
return m("div")
}
}
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
resolve: function(resolve) {resolve(Component)},
},
})
callAsync(function() {
o(root.firstChild.nodeName).equals("DIV")
done()
})
})
o("accepts object without `resolve` method as payload", function(done) {
var Component = {
view: function() {
return m("div")
}
}
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
render: function() {return m(Component)},
},
})
callAsync(function() {
o(root.firstChild.nodeName).equals("DIV")
done()
})
})
o("calls resolve and render correct number of times", function(done) {
var resolveCount = 0
var renderCount = 0
var Component = {
view: function() {
return m("div")
}
}
$window.location.href = prefix + "/"
route(root, "/", {
"/" : {
resolve: function(resolve) {
resolveCount++
resolve(Component)
},
render: function(vnode) {
renderCount++
return vnode
},
},
})
callAsync(function() {
o(resolveCount).equals(1)
o(renderCount).equals(1)
redraw.publish()
setTimeout(function() {
o(resolveCount).equals(1)
o(renderCount).equals(2)
done()
}, FRAME_BUDGET)
})
})
})
})
})

View file

@ -1,3 +1,3 @@
;(function() {
window.m = require("./index")
})()
var m = require("./index")
if (typeof module !== "undefined") module["exports"] = m
else window.m = m

View file

@ -23,7 +23,7 @@
Argument | Type | Required | Description
------------ | ------------------------------------------ | -------- | ---
`selector` | `String|Object` | Yes | A CSS selector or a component
`selector` | `String|Object` | Yes | A CSS selector or a [component](https://github.com/lhorie/mithril.js/blob/rewrite/docs/components.md)
`attributes` | `Object` | No | HTML attributes or element properties
`children` | `Array<Vnode>|String|Number|Boolean` | No | Child [vnodes](vnodes.md#structure). Can be written as [splat arguments](signatures.md#splats)
**returns** | `Vnode` | | A [vnode](vnodes.md#structure)

View file

@ -1,5 +1,4 @@
new function() {
"use strict"
var guid = 0, noop = function() {}, HALT = {}
function createStream() {
function stream() {
@ -944,7 +943,7 @@ var parseQueryString = function(string) {
return data
}
var coreRouter = function($window) {
var supportsPushState = typeof $window.history.pushState === "function" && $window.location.protocol !== "file:"
var supportsPushState = typeof $window.history.pushState === "function"
var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout
var prefix = "#!"
function setPrefix(value) {prefix = value}
@ -1006,6 +1005,7 @@ var coreRouter = function($window) {
var path = getPath()
var params = {}
var pathname = parsePath(path, params, params)
callAsync(function() {
for (var route in routes) {
var matcher = new RegExp("^" + route.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$")
@ -1072,16 +1072,16 @@ var autoredraw = function(root, renderer, pubsub, callback) {
m.route = function($window, renderer, pubsub) {
var router = coreRouter($window)
var route = function(root, defaultRoute, routes) {
var current = {route: null, component: null}
var current = {path: null, component: null}
var replay = router.defineRoutes(routes, function(payload, args, path, route) {
if (typeof payload.view !== "function") {
if (typeof payload.render !== "function") payload.render = function(vnode) {return vnode}
var render = function(component) {
current.route = route, current.component = component
current.path = path, current.component = component
renderer.render(root, payload.render(Vnode(component, null, args, undefined, undefined, undefined)))
}
if (typeof payload.resolve !== "function") payload.resolve = function() {render(current.component)}
if (route !== current.route) payload.resolve(render, args, path, route)
if (path !== current.path) payload.resolve(render, args, path, route)
else render(current.component)
}
else {
@ -1125,6 +1125,7 @@ m.redraw = redrawService.publish
m.request = requestService.xhr
m.jsonp = requestService.jsonp
m.version = "1.0.0"
module.exports = m
if (typeof module !== "undefined") module["exports"] = m
else window.m = m
}

38
mithril.min.js vendored
View file

@ -15,26 +15,26 @@ f)if(null==c)b(a,f,0,f.length,d,g,void 0);else if(null==f)v(a,c,0,c.length,f);el
c[q]?n(a,h(f[q],d,B),e(c,q+1,g)):null==f[q]?v(a,c,q,q+1,f):k(a,c[q],f[q],d,e(c,q+1,g),p,B),p&&c[q].tag===f[q].tag&&n(a,m(c[q]),e(c,q+1,g))}else{for(var A=q=0,r=c.length-1,z=f.length-1,u;r>=q&&z>=A;){var t=c[q],w=f[A];if(t===w)q++,A++;else if(null!=t&&null!=w&&t.key===w.key)q++,A++,k(a,t,w,d,e(c,q,g),p,B),p&&t.tag===w.tag&&n(a,m(t),g);else if(t=c[r],t===w)r--,A++;else if(null!=t&&null!=w&&t.key===w.key)k(a,t,w,d,e(c,r+1,g),p,B),n(a,m(t),e(c,q,g)),r--,A++;else break}for(;r>=q&&z>=A;){t=c[r];w=f[z];
if(t===w)r--;else if(null!=t&&null!=w&&t.key===w.key)k(a,t,w,d,e(c,r+1,g),p,B),p&&t.tag===w.tag&&n(a,m(t),g),null!=t.dom&&(g=t.dom),r--;else{if(!u){u=c;var t=r,l={},x;for(x=0;x<t;x++){var y=u[x];null!=y&&(y=y.key,null!=y&&(l[y]=x))}u=l}null!=w&&(t=u[w.key],null!=t?(l=c[t],k(a,l,w,d,e(c,r+1,g),p,B),n(a,m(l),g),c[t].skip=!0,null!=l.dom&&(g=l.dom)):(w=h(w,d,void 0),n(a,w,g),g=w))}z--;if(z<A)break}b(a,f,A,z+1,d,g,B);v(a,c,q,r+1,f)}}}function k(a,c,f,b,e,p,v){var q=c.tag;if(q===f.tag){f.state=c.state;
f.events=c.events;var r;var z;null!=f.attrs&&"function"===typeof f.attrs.onbeforeupdate&&(r=f.attrs.onbeforeupdate.call(f.state,f,c));"string"!==typeof f.tag&&"function"===typeof f.tag.onbeforeupdate&&(z=f.tag.onbeforeupdate.call(f.state,f,c));void 0===r&&void 0===z||r||z?r=!1:(f.dom=c.dom,f.domSize=c.domSize,f.instance=c.instance,r=!0);if(!r)if(null!=f.attrs&&y(f.attrs,f,b,p),"string"===typeof q)switch(q){case "#":c.children.toString()!==f.children.toString()&&(c.dom.nodeValue=f.children);f.dom=
c.dom;break;case "<":c.children!==f.children?(m(c),n(a,d(f),e)):f.dom=c.dom;break;case "[":g(a,c.children,f.children,b,e,v);c=0;b=f.children;f.dom=null;if(null!=b){for(var l=0;l<b.length;l++)a=b[l],null!=a&&null!=a.dom&&(null==f.dom&&(f.dom=a.dom),c+=a.domSize||1);1!==c&&(f.domSize=c)}break;default:a=v;e=f.dom=c.dom;switch(f.tag){case "svg":a="http://www.w3.org/2000/svg";break;case "math":a="http://www.w3.org/1998/Math/MathML"}"textarea"===f.tag&&(null==f.attrs&&(f.attrs={}),null!=f.text&&(f.attrs.value=
f.text));p=c.attrs;v=f.attrs;q=a;if(null!=v)for(l in v)A(f,l,p&&p[l],v[l],q);if(null!=p)for(l in p)null!=v&&l in v||"key"!==l&&f.dom.removeAttribute(l);null!=c.text&&null!=f.text&&""!==f.text?c.text.toString()!==f.text.toString()&&(c.dom.firstChild.nodeValue=f.text):(null!=c.text&&(c.children=[u("#",void 0,void 0,c.text,void 0,c.dom.firstChild)]),null!=f.text&&(f.children=[u("#",void 0,void 0,f.text,void 0,void 0)]),g(e,c.children,f.children,b,null,a))}else f.instance=u.normalize(f.tag.view.call(f.state,
f)),y(f.tag,f,b,p),null!=f.instance?(null==c.instance?n(a,h(f.instance,b,v),e):k(a,c.instance,f.instance,b,e,p,v),f.dom=f.instance.dom,f.domSize=f.instance.domSize):null!=c.instance?(B(a,c.instance,null,!1),f.dom=void 0,f.domSize=0):(f.dom=c.dom,f.domSize=c.domSize)}else B(a,c,null,!1),n(a,h(f,b,void 0),e)}function m(a){var c=a.domSize;if(null!=c||null==a.dom){var f=x.createDocumentFragment();if(0<c){for(a=a.dom;--c;)f.appendChild(a.nextSibling);f.insertBefore(a,f.firstChild)}return f}return a.dom}
function e(a,c,f){for(;c<a.length;c++)if(null!=a[c]&&null!=a[c].dom)return a[c].dom;return f}function n(a,c,f){f&&f.parentNode?a.insertBefore(c,f):a.appendChild(c)}function v(a,c,f,b,e){for(;f<b;f++){var g=c[f];null!=g&&(g.skip?g.skip=void 0:B(a,g,e,!1))}}function B(a,c,f,b){if(!1===b){var e=0,g=0;b=function(){++g===e&&B(a,c,f,!0)};c.attrs&&c.attrs.onbeforeremove&&(e++,c.attrs.onbeforeremove.call(c.state,c,b));"string"!==typeof c.tag&&c.tag.onbeforeremove&&(e++,c.tag.onbeforeremove.call(c.state,c,
b));if(0<e)return}p(c);if(c.dom){b=c.domSize||1;if(1<b)for(var d=c.dom;--b;)a.removeChild(d.nextSibling);null!=c.dom.parentNode&&a.removeChild(c.dom);null==f||null!=c.domSize||z(c.attrs)||"string"!==typeof c.tag||(f.pool?f.pool.push(c):f.pool=[c])}}function p(a){a.attrs&&a.attrs.onremove&&a.attrs.onremove.call(a.state,a);"string"!==typeof a.tag&&a.tag.onremove&&a.tag.onremove.call(a.state,a);if(null!=a.instance)p(a.instance);else if(a=a.children,a instanceof Array)for(var c=0;c<a.length;c++){var b=
a[c];null!=b&&p(b)}}function A(a,c,b,e,g){var d=a.dom;if("key"!==c&&(b!==e||"value"===c||"checked"===c||"selectedIndex"===c||"selected"===c&&a.dom===x.activeElement||"object"===typeof e)&&"undefined"!==typeof e&&"oninit"!==c&&"oncreate"!==c&&"onupdate"!==c&&"onremove"!==c&&"onbeforeremove"!==c&&"onbeforeupdate"!==c){var n=c.indexOf(":");if(-1<n&&"xlink"===c.substr(0,n))d.setAttributeNS("http://www.w3.org/1999/xlink",c.slice(n+1),e);else if("o"===c[0]&&"n"===c[1]&&"function"===typeof e)r(a,c,e);else if("style"===
c)if(a=b,a===e&&(d.style="",a=null),null==e)d.style="";else if("string"===typeof e)d.style=e;else{"string"===typeof a&&(d.style="");for(var h in e)d.style[h]=e[h];if(null!=a&&"string"!==typeof a)for(h in a)h in e||(d.style[h]="")}else if(c in d&&"href"!==c&&"list"!==c&&"form"!==c&&void 0===g){if("input"!==a.tag||"value"!==c||a.dom.value!==e||a.dom!==x.activeElement)d[c]=e}else"boolean"===typeof e?e?d.setAttribute(c,""):d.removeAttribute(c):d.setAttribute("className"===c?"class":c,e)}}function z(a){return null!=
a&&(a.oncreate||a.onupdate||a.onbeforeremove||a.onremove)}function r(a,c,b){var e=a.dom,d=function(a){var c=b.call(e,a);"function"===typeof D&&D.call(e,a);return c};if(c in e)e[c]=d;else{var g=c.slice(2);void 0===a.events&&(a.events={});null!=a.events[c]&&e.removeEventListener(g,a.events[c],!1);a.events[c]=d;e.addEventListener(g,a.events[c],!1)}}function l(a,c,b){"function"===typeof a.oninit&&a.oninit.call(c.state,c);"function"===typeof a.oncreate&&b.push(a.oncreate.bind(c.state,c))}function y(a,
c,b,e){e?l(a,c,b):"function"===typeof a.onupdate&&b.push(a.onupdate.bind(c.state,c))}function C(a){if(a instanceof Array){for(var c=[],b=0;b<a.length;b++)c[b]=a[b];return c}if("object"===typeof a){c={};for(b in a)c[b]=a[b];return c}return a}var x=a.document,ja=x.createDocumentFragment(),D;return{render:function(a,c){var b=[],e=x.activeElement;null==a.vnodes&&(a.vnodes=[]);c instanceof Array||(c=[c]);g(a,a.vnodes,u.normalizeChildren(c),b,null,void 0);a.vnodes=c;for(var d=0;d<b.length;d++)b[d]();x.activeElement!==
e&&e.focus()},setEventCallback:function(a){return D=a}}}(window),L=function(a){function b(a,d){if(d instanceof Array)for(var m=0;m<d.length;m++)b(a+"["+m+"]",d[m]);else if("[object Object]"===Object.prototype.toString.call(d))for(m in d)b(a+"["+m+"]",d[m]);else h.push(encodeURIComponent(a)+(null!=d&&""!==d?"="+encodeURIComponent(d):""))}if("[object Object]"!==Object.prototype.toString.call(a))return"";var h=[],d;for(d in a)b(d,a[d]);return h.join("&")},M=function(a){function b(a,b){if(null==b)return a;
for(var d=a.match(/:[^\/]+/gi)||[],g=0;g<d.length;g++){var h=d[g].slice(1);null!=b[h]&&(a=a.replace(d[g],b[h]),delete b[h])}return a}function h(a,b){var d=L(b);if(""!==d){var g=0>a.indexOf("?")?"?":"&";a+=g+d}return a}function d(a){try{return""!==a?JSON.parse(a):null}catch(b){throw Error(a);}}function g(a){return a.responseText}var k=0,m;return{xhr:function(e){var n=C.stream();void 0!==e.initialValue&&n(e.initialValue);var v="boolean"===typeof e.useBody?e.useBody:"GET"!==e.method&&"TRACE"!==e.method;
"function"!==typeof e.serialize&&(e.serialize="undefined"!==typeof FormData&&e.data instanceof FormData?function(a){return a}:JSON.stringify);"function"!==typeof e.deserialize&&(e.deserialize=d);"function"!==typeof e.extract&&(e.extract=g);e.url=b(e.url,e.data);v?e.data=e.serialize(e.data):e.url=h(e.url,e.data);var k=new a.XMLHttpRequest;k.open(e.method,e.url,"boolean"===typeof e.async?e.async:!0,"string"===typeof e.user?e.user:void 0,"string"===typeof e.password?e.password:void 0);e.serialize===
JSON.stringify&&v&&k.setRequestHeader("Content-Type","application/json; charset=utf-8");e.deserialize===d&&k.setRequestHeader("Accept","application/json, text/*");"function"===typeof e.config&&(k=e.config(k,e)||k);k.onreadystatechange=function(){if(4===k.readyState){try{var a=e.deserialize(e.extract(k,e));if(200<=k.status&&300>k.status){if("function"===typeof e.type)if(a instanceof Array)for(var b=0;b<a.length;b++)a[b]=new e.type(a[b]);else a=new e.type(a);n(a)}else{var b=Error(k.responseText),d;
for(d in a)b[d]=a[d];n.error(b)}}catch(g){n.error(g)}"function"===typeof m&&m()}};v?k.send(e.data):k.send();return n},jsonp:function(e){var d=C.stream(),g=e.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+k++,l=a.document.createElement("script");a[g]=function(b){l.parentNode.removeChild(l);d(b);"function"===typeof m&&m();delete a[g]};l.onerror=function(){l.parentNode.removeChild(l);d.error(Error("JSONP request failed"));"function"===typeof m&&m();delete a[g]};null==e.data&&(e.data={});
e.url=b(e.url,e.data);e.data[e.callbackKey||"callback"]=g;l.src=h(e.url,e.data);a.document.documentElement.appendChild(l);return d},setCompletionCallback:function(a){m=a}}}(window),H=function(){var a=[];return{subscribe:a.push.bind(a),unsubscribe:function(b){b=a.indexOf(b);-1<b&&a.splice(b,1)},publish:function(){for(var b=0;b<a.length;b++)a[b].apply(this,arguments)}}}();M.setCompletionCallback(H.publish);var Y=function(a){if(""===a||null==a)return{};"?"===a.charAt(0)&&(a=a.slice(1));a=a.split("&");
c.dom;break;case "<":c.children!==f.children?(m(c),n(a,d(f),e)):(f.dom=c.dom,f.domSize=c.domSize);break;case "[":g(a,c.children,f.children,b,e,v);c=0;b=f.children;f.dom=null;if(null!=b){for(var l=0;l<b.length;l++)a=b[l],null!=a&&null!=a.dom&&(null==f.dom&&(f.dom=a.dom),c+=a.domSize||1);1!==c&&(f.domSize=c)}break;default:a=v;e=f.dom=c.dom;switch(f.tag){case "svg":a="http://www.w3.org/2000/svg";break;case "math":a="http://www.w3.org/1998/Math/MathML"}"textarea"===f.tag&&(null==f.attrs&&(f.attrs={}),
null!=f.text&&(f.attrs.value=f.text));p=c.attrs;v=f.attrs;q=a;if(null!=v)for(l in v)A(f,l,p&&p[l],v[l],q);if(null!=p)for(l in p)null!=v&&l in v||"key"!==l&&f.dom.removeAttribute(l);null!=c.text&&null!=f.text&&""!==f.text?c.text.toString()!==f.text.toString()&&(c.dom.firstChild.nodeValue=f.text):(null!=c.text&&(c.children=[u("#",void 0,void 0,c.text,void 0,c.dom.firstChild)]),null!=f.text&&(f.children=[u("#",void 0,void 0,f.text,void 0,void 0)]),g(e,c.children,f.children,b,null,a))}else f.instance=
u.normalize(f.tag.view.call(f.state,f)),y(f.tag,f,b,p),null!=f.instance?(null==c.instance?n(a,h(f.instance,b,v),e):k(a,c.instance,f.instance,b,e,p,v),f.dom=f.instance.dom,f.domSize=f.instance.domSize):null!=c.instance?(B(a,c.instance,null,!1),f.dom=void 0,f.domSize=0):(f.dom=c.dom,f.domSize=c.domSize)}else B(a,c,null,!1),n(a,h(f,b,void 0),e)}function m(a){var c=a.domSize;if(null!=c||null==a.dom){var f=x.createDocumentFragment();if(0<c){for(a=a.dom;--c;)f.appendChild(a.nextSibling);f.insertBefore(a,
f.firstChild)}return f}return a.dom}function e(a,c,f){for(;c<a.length;c++)if(null!=a[c]&&null!=a[c].dom)return a[c].dom;return f}function n(a,c,f){f&&f.parentNode?a.insertBefore(c,f):a.appendChild(c)}function v(a,c,f,b,e){for(;f<b;f++){var g=c[f];null!=g&&(g.skip?g.skip=void 0:B(a,g,e,!1))}}function B(a,c,f,b){if(!1===b){var e=0,g=0;b=function(){++g===e&&B(a,c,f,!0)};c.attrs&&c.attrs.onbeforeremove&&(e++,c.attrs.onbeforeremove.call(c.state,c,b));"string"!==typeof c.tag&&c.tag.onbeforeremove&&(e++,
c.tag.onbeforeremove.call(c.state,c,b));if(0<e)return}p(c);if(c.dom){b=c.domSize||1;if(1<b)for(var d=c.dom;--b;)a.removeChild(d.nextSibling);null!=c.dom.parentNode&&a.removeChild(c.dom);null==f||null!=c.domSize||z(c.attrs)||"string"!==typeof c.tag||(f.pool?f.pool.push(c):f.pool=[c])}}function p(a){a.attrs&&a.attrs.onremove&&a.attrs.onremove.call(a.state,a);"string"!==typeof a.tag&&a.tag.onremove&&a.tag.onremove.call(a.state,a);if(null!=a.instance)p(a.instance);else if(a=a.children,a instanceof Array)for(var c=
0;c<a.length;c++){var b=a[c];null!=b&&p(b)}}function A(a,c,b,e,g){var d=a.dom;if("key"!==c&&(b!==e||"value"===c||"checked"===c||"selectedIndex"===c||"selected"===c&&a.dom===x.activeElement||"object"===typeof e)&&"undefined"!==typeof e&&"oninit"!==c&&"oncreate"!==c&&"onupdate"!==c&&"onremove"!==c&&"onbeforeremove"!==c&&"onbeforeupdate"!==c){var n=c.indexOf(":");if(-1<n&&"xlink"===c.substr(0,n))d.setAttributeNS("http://www.w3.org/1999/xlink",c.slice(n+1),e);else if("o"===c[0]&&"n"===c[1]&&"function"===
typeof e)r(a,c,e);else if("style"===c)if(a=b,a===e&&(d.style="",a=null),null==e)d.style="";else if("string"===typeof e)d.style=e;else{"string"===typeof a&&(d.style="");for(var h in e)d.style[h]=e[h];if(null!=a&&"string"!==typeof a)for(h in a)h in e||(d.style[h]="")}else if(c in d&&"href"!==c&&"list"!==c&&"form"!==c&&void 0===g){if("input"!==a.tag||"value"!==c||a.dom.value!==e||a.dom!==x.activeElement)d[c]=e}else"boolean"===typeof e?e?d.setAttribute(c,""):d.removeAttribute(c):d.setAttribute("className"===
c?"class":c,e)}}function z(a){return null!=a&&(a.oncreate||a.onupdate||a.onbeforeremove||a.onremove)}function r(a,c,b){var e=a.dom,d=function(a){var c=b.call(e,a);"function"===typeof D&&D.call(e,a);return c};if(c in e)e[c]=d;else{var g=c.slice(2);void 0===a.events&&(a.events={});null!=a.events[c]&&e.removeEventListener(g,a.events[c],!1);a.events[c]=d;e.addEventListener(g,a.events[c],!1)}}function l(a,c,b){"function"===typeof a.oninit&&a.oninit.call(c.state,c);"function"===typeof a.oncreate&&b.push(a.oncreate.bind(c.state,
c))}function y(a,c,b,e){e?l(a,c,b):"function"===typeof a.onupdate&&b.push(a.onupdate.bind(c.state,c))}function C(a){if(a instanceof Array){for(var c=[],b=0;b<a.length;b++)c[b]=a[b];return c}if("object"===typeof a){c={};for(b in a)c[b]=a[b];return c}return a}var x=a.document,ja=x.createDocumentFragment(),D;return{render:function(a,c){var b=[],e=x.activeElement;null==a.vnodes&&(a.vnodes=[]);c instanceof Array||(c=[c]);g(a,a.vnodes,u.normalizeChildren(c),b,null,void 0);a.vnodes=c;for(var d=0;d<b.length;d++)b[d]();
x.activeElement!==e&&e.focus()},setEventCallback:function(a){return D=a}}}(window),L=function(a){function b(a,d){if(d instanceof Array)for(var m=0;m<d.length;m++)b(a+"["+m+"]",d[m]);else if("[object Object]"===Object.prototype.toString.call(d))for(m in d)b(a+"["+m+"]",d[m]);else h.push(encodeURIComponent(a)+(null!=d&&""!==d?"="+encodeURIComponent(d):""))}if("[object Object]"!==Object.prototype.toString.call(a))return"";var h=[],d;for(d in a)b(d,a[d]);return h.join("&")},M=function(a){function b(a,
b){if(null==b)return a;for(var d=a.match(/:[^\/]+/gi)||[],g=0;g<d.length;g++){var h=d[g].slice(1);null!=b[h]&&(a=a.replace(d[g],b[h]),delete b[h])}return a}function h(a,b){var d=L(b);if(""!==d){var g=0>a.indexOf("?")?"?":"&";a+=g+d}return a}function d(a){try{return""!==a?JSON.parse(a):null}catch(b){throw Error(a);}}function g(a){return a.responseText}var k=0,m;return{xhr:function(e){var n=C.stream();void 0!==e.initialValue&&n(e.initialValue);var v="boolean"===typeof e.useBody?e.useBody:"GET"!==e.method&&
"TRACE"!==e.method;"function"!==typeof e.serialize&&(e.serialize="undefined"!==typeof FormData&&e.data instanceof FormData?function(a){return a}:JSON.stringify);"function"!==typeof e.deserialize&&(e.deserialize=d);"function"!==typeof e.extract&&(e.extract=g);e.url=b(e.url,e.data);v?e.data=e.serialize(e.data):e.url=h(e.url,e.data);var k=new a.XMLHttpRequest;k.open(e.method,e.url,"boolean"===typeof e.async?e.async:!0,"string"===typeof e.user?e.user:void 0,"string"===typeof e.password?e.password:void 0);
e.serialize===JSON.stringify&&v&&k.setRequestHeader("Content-Type","application/json; charset=utf-8");e.deserialize===d&&k.setRequestHeader("Accept","application/json, text/*");"function"===typeof e.config&&(k=e.config(k,e)||k);k.onreadystatechange=function(){if(4===k.readyState){try{var a=e.deserialize(e.extract(k,e));if(200<=k.status&&300>k.status){if("function"===typeof e.type)if(a instanceof Array)for(var b=0;b<a.length;b++)a[b]=new e.type(a[b]);else a=new e.type(a);n(a)}else{var b=Error(k.responseText),
d;for(d in a)b[d]=a[d];n.error(b)}}catch(g){n.error(g)}"function"===typeof m&&m()}};v?k.send(e.data):k.send();return n},jsonp:function(e){var d=C.stream(),g=e.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+k++,l=a.document.createElement("script");a[g]=function(b){l.parentNode.removeChild(l);d(b);"function"===typeof m&&m();delete a[g]};l.onerror=function(){l.parentNode.removeChild(l);d.error(Error("JSONP request failed"));"function"===typeof m&&m();delete a[g]};null==e.data&&(e.data=
{});e.url=b(e.url,e.data);e.data[e.callbackKey||"callback"]=g;l.src=h(e.url,e.data);a.document.documentElement.appendChild(l);return d},setCompletionCallback:function(a){m=a}}}(window),H=function(){var a=[];return{subscribe:a.push.bind(a),unsubscribe:function(b){b=a.indexOf(b);-1<b&&a.splice(b,1)},publish:function(){for(var b=0;b<a.length;b++)a[b].apply(this,arguments)}}}();M.setCompletionCallback(H.publish);var Y=function(a){if(""===a||null==a)return{};"?"===a.charAt(0)&&(a=a.slice(1));a=a.split("&");
for(var b={},h={},d=0;d<a.length;d++){var g=a[d].split("="),k=decodeURIComponent(g[0]),g=2===g.length?decodeURIComponent(g[1]):"",m=Number(g);""!==g&&!isNaN(m)||"NaN"===g?g=m:"true"===g?g=!0:"false"===g?g=!1:(m=new Date(g),isNaN(m.getTime())||(g=m));var m=k.split(/\]\[?|\[/),e=b;-1<k.indexOf("[")&&m.pop();for(var n=0;n<m.length;n++){var k=m[n],v=m[n+1],v=""==v||!isNaN(parseInt(v,10)),l=n===m.length-1;""===k&&(k=m.slice(0,n).join(),null==h[k]&&(h[k]=0),k=h[k]++);null==e[k]&&(e[k]=l?g:v?[]:{});e=e[k]}}return b},
ka=function(a){function b(b){var e=a.location[b].replace(/(?:%[a-f89][a-f0-9])+/gim,decodeURIComponent);"pathname"===b&&"/"!==e[0]&&(e="/"+e);return e}function h(a,b,e){var d=a.indexOf("?"),g=a.indexOf("#"),h=-1<d?d:-1<g?g:a.length;if(-1<d){var d=Y(a.slice(d+1,-1<g?g:a.length)),k;for(k in d)b[k]=d[k]}if(-1<g)for(k in b=Y(a.slice(g+1)),b)e[k]=b[k];return a.slice(0,h)}function d(){switch(e.charAt(0)){case "#":return b("hash").slice(e.length);case "?":return b("search").slice(e.length)+b("hash");default:return b("pathname").slice(e.length)+
b("search")+b("hash")}}function g(b,d,g){var m={},l={};b=h(b,m,l);if(null!=d){for(var z in d)m[z]=d[z];b=b.replace(/:([^\/]+)/g,function(a,b){delete m[b];return d[b]})}(z=L(m))&&(b+="?"+z);(l=L(l))&&(b+="#"+l);k?(g&&g.replace?a.history.replaceState(null,null,e+b):a.history.pushState(null,null,e+b),a.onpopstate()):a.location.href=e+b}var k="function"===typeof a.history.pushState&&"file:"!==a.location.protocol,m="function"===typeof setImmediate?setImmediate:setTimeout,e="#!";return{setPrefix:function(a){e=
a},getPath:d,setPath:g,defineRoutes:function(b,g,l){function p(){var a=d(),e={},k=h(a,e,e);m(function(){for(var d in b){var h=new RegExp("^"+d.replace(/:[^\/]+?\.{3}/g,"(.*?)").replace(/:[^\/]+/g,"([^\\/]+)")+"/?$");if(h.test(k)){k.replace(h,function(){for(var h=d.match(/:[^\/]+/g)||[],k=[].slice.call(arguments,1,-2),l=0;l<h.length;l++)e[h[l].replace(/:|\./g,"")]=decodeURIComponent(k[l]);g(b[d],e,a,d)});return}}l(a,e)})}k?a.onpopstate=p:"#"===e.charAt(0)&&(a.onhashchange=p);p();return p},link:function(a){a.dom.setAttribute("href",
e+a.attrs.href);a.dom.onclick=function(b){b.preventDefault();b.redraw=!1;g(a.attrs.href,void 0,void 0)}}}},la=function(a){var b=0,h=null,d="function"===typeof requestAnimationFrame?requestAnimationFrame:setTimeout;return function(g){var k=(new Date).getTime();!0===g||0===b||16<=k-b?(b=k,a()):null===h&&(h=d(function(){h=null;a();b=(new Date).getTime()},16-(k-b)))}},Z=function(a,b,h,d){d=la(d);null!=b&&b.setEventCallback(function(a){!1!==a.redraw&&h.publish()});null!=h&&(a.redraw&&h.unsubscribe(a.redraw),
h.subscribe(d));return a.redraw=d};l.route=function(a,b,h){var d=ka(a);a=function(a,k,l){var e=null,n=null;l=d.defineRoutes(l,function(d,h,k,l){if("function"!==typeof d.view){"function"!==typeof d.render&&(d.render=function(a){return a});var m=function(k){e=l;n=k;b.render(a,d.render(u(k,null,h,void 0,void 0,void 0)))};"function"!==typeof d.resolve&&(d.resolve=function(){m(n)});l!==e?d.resolve(m,h,k,l):m(n)}else b.render(a,u(d,null,h,void 0,void 0,void 0))},function(){d.setPath(k,null,{replace:!0})});
Z(a,b,h,l)};a.link=d.link;a.prefix=d.setPrefix;a.set=d.setPath;a.get=d.getPath;return a}(window,K,H);l.mount=function(a,b){return function(h,d){Z(h,a,b,function(){a.render(h,{tag:d})})()}}(K,H);l.trust=function(a){return u("<",void 0,void 0,a,void 0,void 0)};l.withAttr=function(a,b,h){return function(d){return b.call(h||this,a in d.currentTarget?d.currentTarget[a]:d.currentTarget.getAttribute(a))}};l.prop=C.stream;l.prop.combine=C.combine;l.prop.reject=C.reject;l.prop.merge=C.merge;l.prop.HALT=C.HALT;
l.render=K.render;l.redraw=H.publish;l.request=M.xhr;l.jsonp=M.jsonp;l.version="1.0.0";window.m=l})()};
e+a.attrs.href);a.dom.onclick=function(b){b.preventDefault();b.redraw=!1;g(a.attrs.href,void 0,void 0)}}}},la=function(a){var b=0,h=null,d="function"===typeof requestAnimationFrame?requestAnimationFrame:setTimeout;return function(g){var k=Date.now();!0===g||0===b||16<=k-b?(b=k,a()):null===h&&(h=d(function(){h=null;a();b=Date.now()},16-(k-b)))}},Z=function(a,b,h,d){d=la(d);null!=b&&b.setEventCallback(function(a){!1!==a.redraw&&h.publish()});null!=h&&(a.redraw&&h.unsubscribe(a.redraw),h.subscribe(d));
return a.redraw=d};l.route=function(a,b,h){var d=ka(a);a=function(a,k,l){var e=null,n=null;l=d.defineRoutes(l,function(d,h,k,l){if("function"!==typeof d.view){"function"!==typeof d.render&&(d.render=function(a){return a});var m=function(l){e=k;n=l;b.render(a,d.render(u(l,null,h,void 0,void 0,void 0)))};"function"!==typeof d.resolve&&(d.resolve=function(){m(n)});k!==e?d.resolve(m,h,k,l):m(n)}else b.render(a,u(d,null,h,void 0,void 0,void 0))},function(){d.setPath(k,null,{replace:!0})});Z(a,b,h,l)};
a.link=d.link;a.prefix=d.setPrefix;a.set=d.setPath;a.get=d.getPath;return a}(window,K,H);l.mount=function(a,b){return function(h,d){Z(h,a,b,function(){a.render(h,{tag:d})})()}}(K,H);l.trust=function(a){return u("<",void 0,void 0,a,void 0,void 0)};l.withAttr=function(a,b,h){return function(d){return b.call(h||this,a in d.currentTarget?d.currentTarget[a]:d.currentTarget.getAttribute(a))}};l.prop=C.stream;l.prop.combine=C.combine;l.prop.reject=C.reject;l.prop.merge=C.merge;l.prop.HALT=C.HALT;l.render=
K.render;l.redraw=H.publish;l.request=M.xhr;l.jsonp=M.jsonp;l.version="1.0.0";window.m=l})()};

View file

@ -6,10 +6,10 @@
"license": "MIT",
"main": "index.js",
"scripts": {
"dev": "node bundler/cli index.js -o mithril.js -w",
"build": "npm run build-commonjs & npm run build-browser",
"build-commonjs": "node bundler/cli index.js -o mithril.js",
"build-browser": "node bundler/cli browser.js -o mithril.min.js -m",
"dev": "node bundler/cli browser.js -o mithril.js -w",
"build": "npm run build-browser & npm run build-min",
"build-browser": "node bundler/cli browser.js -o mithril.js",
"build-min": "node bundler/cli browser.js -o mithril.min.js -m",
"lintdocs": "node bundler/lintdocs",
"lint": "eslint .",
"test": "node ospec/bin/ospec",

View file

@ -356,7 +356,7 @@ module.exports = function($window) {
}
}
if (vnode.dom.parentNode != null) parent.removeChild(vnode.dom)
if (context != null && vnode.domSize == null && !hasIntegrationMethods(vnode.attrs) && typeof vnode.tag === "string" && vnode.tag !== "<") { //TODO test custom elements
if (context != null && vnode.domSize == null && !hasIntegrationMethods(vnode.attrs) && typeof vnode.tag === "string") { //TODO test custom elements
if (!context.pool) context.pool = [vnode]
else context.pool.push(vnode)
}

View file

@ -4,7 +4,7 @@ var buildQueryString = require("../querystring/build")
var parseQueryString = require("../querystring/parse")
module.exports = function($window) {
var supportsPushState = typeof $window.history.pushState === "function" && $window.location.protocol !== "file:"
var supportsPushState = typeof $window.history.pushState === "function"
var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout
var prefix = "#!"
@ -75,7 +75,7 @@ module.exports = function($window) {
var path = getPath()
var params = {}
var pathname = parsePath(path, params, params)
callAsync(function() {
for (var route in routes) {
var matcher = new RegExp("^" + route.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$")

View file

@ -6,295 +6,297 @@ var pushStateMock = require("../../test-utils/pushStateMock")
var Router = require("../../router/router")
o.spec("Router.defineRoutes", function() {
void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) {
o.spec("using prefix `" + prefix + "`", function() {
var $window, router, onRouteChange, onFail
void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) {
void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) {
o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() {
var $window, router, onRouteChange, onFail
o.beforeEach(function() {
$window = pushStateMock()
router = new Router($window)
router.setPrefix(prefix)
onRouteChange = o.spy()
onFail = o.spy()
})
o.beforeEach(function() {
$window = pushStateMock(env)
router = new Router($window)
router.setPrefix(prefix)
onRouteChange = o.spy()
onFail = o.spy()
})
o("calls onRouteChange on init", function(done) {
$window.location.href = prefix + "/a"
router.defineRoutes({"/a": {data: 1}}, onRouteChange, onFail)
o("calls onRouteChange on init", function(done) {
$window.location.href = prefix + "/a"
router.defineRoutes({"/a": {data: 1}}, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
done()
})
})
callAsync(function() {
o(onRouteChange.callCount).equals(1)
done()
o("resolves to route", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"])
o(onFail.callCount).equals(0)
done()
})
})
})
o("resolves to route", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"])
o(onFail.callCount).equals(0)
done()
o("resolves to route w/ escaped unicode", function(done) {
$window.location.href = prefix + "/%C3%B6?%C3%B6=%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(onFail.callCount).equals(0)
done()
})
})
})
o("resolves to route w/ escaped unicode", function(done) {
$window.location.href = prefix + "/%C3%B6?%C3%B6=%C3%B6#%C3%B6=%C3%B6"
router.defineRoutes({"/ö": {data: 2}}, onRouteChange, onFail)
o("resolves to route w/ unicode", function(done) {
$window.location.href = prefix + "/ö?ö=ö#ö=ö"
router.defineRoutes({"/ö": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 2}, {"ö": "ö"}, "/ö?ö=ö#ö=ö", "/ö"])
o(onFail.callCount).equals(0)
done()
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 2}, {"ö": "ö"}, "/ö?ö=ö#ö=ö", "/ö"])
o(onFail.callCount).equals(0)
done()
})
})
})
o("resolves to route w/ unicode", function(done) {
$window.location.href = prefix + "/ö?ö=ö#ö=ö"
router.defineRoutes({"/ö": {data: 2}}, onRouteChange, onFail)
o("resolves to route on fallback mode", function(done) {
$window.location.href = "file://" + prefix + "/test"
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 2}, {"ö": "ö"}, "/ö?ö=ö#ö=ö", "/ö"])
o(onFail.callCount).equals(0)
done()
router = new Router($window)
router.setPrefix(prefix)
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"])
o(onFail.callCount).equals(0)
done()
})
})
})
o("resolves to route on fallback mode", function(done) {
$window.location.href = "file://" + prefix + "/test"
o("handles parameterized route", function(done) {
$window.location.href = prefix + "/test/x"
router.defineRoutes({"/test/:a": {data: 1}}, onRouteChange, onFail)
router = new Router($window)
router.setPrefix(prefix)
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"])
o(onFail.callCount).equals(0)
done()
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {a: "x"}, "/test/x", "/test/:a"])
o(onFail.callCount).equals(0)
done()
})
})
})
o("handles parameterized route", function(done) {
$window.location.href = prefix + "/test/x"
router.defineRoutes({"/test/:a": {data: 1}}, onRouteChange, onFail)
o("handles multi-parameterized route", function(done) {
$window.location.href = prefix + "/test/x/y"
router.defineRoutes({"/test/:a/:b": {data: 1}}, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {a: "x"}, "/test/x", "/test/:a"])
o(onFail.callCount).equals(0)
done()
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {a: "x", b: "y"}, "/test/x/y", "/test/:a/:b"])
o(onFail.callCount).equals(0)
done()
})
})
})
o("handles multi-parameterized route", function(done) {
$window.location.href = prefix + "/test/x/y"
router.defineRoutes({"/test/:a/:b": {data: 1}}, onRouteChange, onFail)
o("handles rest parameterized route", function(done) {
$window.location.href = prefix + "/test/x/y"
router.defineRoutes({"/test/:a...": {data: 1}}, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {a: "x", b: "y"}, "/test/x/y", "/test/:a/:b"])
o(onFail.callCount).equals(0)
done()
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {a: "x/y"}, "/test/x/y", "/test/:a..."])
o(onFail.callCount).equals(0)
done()
})
})
})
o("handles rest parameterized route", function(done) {
$window.location.href = prefix + "/test/x/y"
router.defineRoutes({"/test/:a...": {data: 1}}, onRouteChange, onFail)
o("handles route with search", 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: "x/y"}, "/test/x/y", "/test/:a..."])
o(onFail.callCount).equals(0)
done()
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", function(done) {
$window.location.href = prefix + "/test?a=b&c=d"
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
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()
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 hash", function(done) {
$window.location.href = prefix + "/test#a=b&c=d"
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
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()
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)
o("calls reject", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/other": {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()
callAsync(function() {
o(onFail.callCount).equals(1)
o(onFail.args).deepEquals(["/test", {}])
done()
})
})
})
o("calls reject", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/other": {data: 1}}, onRouteChange, onFail)
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", {}])
done()
callAsync(function() {
o(onFail.callCount).equals(1)
o(onFail.args).deepEquals(["/test?a=b#c=d", {a: "b", c: "d"}])
done()
})
})
})
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)
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)
callAsync(function() {
o(onFail.callCount).equals(1)
o(onFail.args).deepEquals(["/test?a=b#c=d", {a: "b", c: "d"}])
done()
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/z/y/x", "/z/y/x"])
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)
o("handles reverse out of order routes", function(done) {
$window.location.href = prefix + "/z/y/x"
router.defineRoutes({"/:a...": {data: 2}, "/z/y/x": {data: 1}}, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/z/y/x", "/z/y/x"])
done()
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 2}, {a: "z/y/x"}, "/z/y/x", "/:a..."])
done()
})
})
})
o("handles reverse out of order routes", function(done) {
$window.location.href = prefix + "/z/y/x"
router.defineRoutes({"/:a...": {data: 2}, "/z/y/x": {data: 1}}, onRouteChange, onFail)
o("handles dynamically added out of order routes", function(done) {
var routes = {}
routes["/z/y/x"] = {data: 1}
routes["/:a..."] = {data: 2}
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 2}, {a: "z/y/x"}, "/z/y/x", "/:a..."])
done()
$window.location.href = prefix + "/z/y/x"
router.defineRoutes(routes, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/z/y/x", "/z/y/x"])
done()
})
})
})
o("handles dynamically added out of order routes", function(done) {
var routes = {}
routes["/z/y/x"] = {data: 1}
routes["/:a..."] = {data: 2}
o("handles reversed dynamically added out of order routes", function(done) {
var routes = {}
routes["/:a..."] = {data: 2}
routes["/z/y/x"] = {data: 1}
$window.location.href = prefix + "/z/y/x"
router.defineRoutes(routes, onRouteChange, onFail)
$window.location.href = prefix + "/z/y/x"
router.defineRoutes(routes, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/z/y/x", "/z/y/x"])
done()
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 2}, {a: "z/y/x"}, "/z/y/x", "/:a..."])
done()
})
})
})
o("handles reversed dynamically added out of order routes", function(done) {
var routes = {}
routes["/:a..."] = {data: 2}
routes["/z/y/x"] = {data: 1}
o("handles mixed out of order routes", function(done) {
var routes = {"/z/y/x": {data: 1}}
routes["/:a..."] = {data: 2}
$window.location.href = prefix + "/z/y/x"
router.defineRoutes(routes, onRouteChange, onFail)
$window.location.href = prefix + "/z/y/x"
router.defineRoutes(routes, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 2}, {a: "z/y/x"}, "/z/y/x", "/:a..."])
done()
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/z/y/x", "/z/y/x"])
done()
})
})
})
o("handles mixed out of order routes", function(done) {
var routes = {"/z/y/x": {data: 1}}
routes["/:a..."] = {data: 2}
o("handles reverse mixed out of order routes", function(done) {
var routes = {"/:a...": {data: 2}}
routes["/z/y/x"] = {data: 12}
$window.location.href = prefix + "/z/y/x"
router.defineRoutes(routes, onRouteChange, onFail)
$window.location.href = prefix + "/z/y/x"
router.defineRoutes(routes, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/z/y/x", "/z/y/x"])
done()
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 2}, {a: "z/y/x"}, "/z/y/x", "/:a..."])
done()
})
})
})
o("handles reverse mixed out of order routes", function(done) {
var routes = {"/:a...": {data: 2}}
routes["/z/y/x"] = {data: 12}
o("handles non-ascii routes", function(done) {
$window.location.href = prefix + "/ö"
router.defineRoutes({"/ö": "aaa"}, onRouteChange, onFail)
$window.location.href = prefix + "/z/y/x"
router.defineRoutes(routes, onRouteChange, onFail)
callAsync(function() {
o(onRouteChange.callCount).equals(1)
o(onRouteChange.args).deepEquals([{data: 2}, {a: "z/y/x"}, "/z/y/x", "/:a..."])
done()
callAsync(function() {
o(onRouteChange.callCount).equals(1)
done()
})
})
})
o("handles non-ascii routes", function(done) {
$window.location.href = prefix + "/ö"
router.defineRoutes({"/ö": "aaa"}, onRouteChange, onFail)
o("replays", function(done) {
$window.location.href = prefix + "/test"
var replay = router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
replay()
callAsync(function() {
o(onRouteChange.callCount).equals(1)
done()
})
})
o("replays", function(done) {
$window.location.href = prefix + "/test"
var replay = router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
replay()
callAsync(function() {
o(onRouteChange.callCount).equals(2)
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"])
o(onFail.callCount).equals(0)
done()
callAsync(function() {
o(onRouteChange.callCount).equals(2)
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"])
o(onFail.callCount).equals(0)
done()
})
})
})
})

View file

@ -5,41 +5,43 @@ var pushStateMock = require("../../test-utils/pushStateMock")
var Router = require("../../router/router")
o.spec("Router.getPath", function() {
void ["#", "?", "", "#!", "?!", '/foo'].forEach(function(prefix) {
o.spec("using prefix `" + prefix + "`", function() {
var $window, router, onRouteChange, onFail
void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) {
void ["#", "?", "", "#!", "?!", '/foo'].forEach(function(prefix) {
o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() {
var $window, router, onRouteChange, onFail
o.beforeEach(function() {
$window = pushStateMock()
router = new Router($window)
router.setPrefix(prefix)
onRouteChange = o.spy()
onFail = o.spy()
})
o.beforeEach(function() {
$window = pushStateMock(env)
router = new Router($window)
router.setPrefix(prefix)
onRouteChange = o.spy()
onFail = o.spy()
})
o("gets route", function() {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
o("gets route", function() {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
o(router.getPath()).equals("/test")
})
o("gets route w/ params", function() {
$window.location.href = prefix + "/other/x/y/z?c=d#e=f"
router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail)
o(router.getPath()).equals("/test")
})
o("gets route w/ params", function() {
$window.location.href = prefix + "/other/x/y/z?c=d#e=f"
router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail)
o(router.getPath()).equals("/other/x/y/z?c=d#e=f")
})
o("gets route w/ escaped unicode", function() {
$window.location.href = prefix + "/%C3%B6?%C3%B6=%C3%B6#%C3%B6=%C3%B6"
router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail)
o(router.getPath()).equals("/other/x/y/z?c=d#e=f")
})
o("gets route w/ escaped unicode", function() {
$window.location.href = prefix + "/%C3%B6?%C3%B6=%C3%B6#%C3%B6=%C3%B6"
router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail)
o(router.getPath()).equals("/ö?ö=ö#ö=ö")
})
o("gets route w/ unicode", function() {
$window.location.href = prefix + "/ö?ö=ö#ö=ö"
router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail)
o(router.getPath()).equals("/ö?ö=ö#ö=ö")
})
o("gets route w/ unicode", function() {
$window.location.href = prefix + "/ö?ö=ö#ö=ö"
router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail)
o(router.getPath()).equals("/ö?ö=ö#ö=ö")
o(router.getPath()).equals("/ö?ö=ö#ö=ö")
})
})
})
})

View file

@ -6,150 +6,152 @@ var pushStateMock = require("../../test-utils/pushStateMock")
var Router = require("../../router/router")
o.spec("Router.setPath", function() {
void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) {
o.spec("using prefix `" + prefix + "`", function() {
var $window, router, onRouteChange, onFail
void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) {
void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) {
o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() {
var $window, router, onRouteChange, onFail
o.beforeEach(function() {
$window = pushStateMock()
router = new Router($window)
router.setPrefix(prefix)
onRouteChange = o.spy()
onFail = o.spy()
})
o.beforeEach(function() {
$window = pushStateMock(env)
router = new Router($window)
router.setPrefix(prefix)
onRouteChange = o.spy()
onFail = o.spy()
})
o("setPath calls onRouteChange asynchronously", function(done) {
$window.location.href = prefix + "/a"
router.defineRoutes({"/a": {data: 1}, "/b": {data: 2}}, onRouteChange, onFail)
o("setPath calls onRouteChange asynchronously", function(done) {
$window.location.href = prefix + "/a"
router.defineRoutes({"/a": {data: 1}, "/b": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
router.setPath("/b")
o(onRouteChange.callCount).equals(1)
callAsync(function() {
o(onRouteChange.callCount).equals(2)
router.setPath("/b")
o(onRouteChange.callCount).equals(1)
callAsync(function() {
o(onRouteChange.callCount).equals(2)
done()
})
})
})
o("setPath calls onFail asynchronously", function(done) {
$window.location.href = prefix + "/a"
router.defineRoutes({"/a": {data: 1}, "/b": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
router.setPath("/c")
o(onFail.callCount).equals(0)
callAsync(function() {
o(onFail.callCount).equals(1)
done()
})
})
})
o("sets route via API", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
router.setPath("/other/x/y/z?c=d#e=f")
o(router.getPath()).equals("/other/x/y/z?c=d#e=f")
done()
})
})
})
o("setPath calls onFail asynchronously", function(done) {
$window.location.href = prefix + "/a"
router.defineRoutes({"/a": {data: 1}, "/b": {data: 2}}, onRouteChange, onFail)
o("sets route w/ escaped unicode", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
router.setPath("/c")
o(onFail.callCount).equals(0)
callAsync(function() {
o(onFail.callCount).equals(1)
router.setPath("/%C3%B6?%C3%B6=%C3%B6#%C3%B6=%C3%B6")
o(router.getPath()).equals("/ö?ö=ö#ö=ö")
done()
})
})
})
o("sets route via API", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail)
o("sets route w/ unicode", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
router.setPath("/other/x/y/z?c=d#e=f")
callAsync(function() {
router.setPath("/ö?ö=ö#ö=ö")
o(router.getPath()).equals("/other/x/y/z?c=d#e=f")
done()
o(router.getPath()).equals("/ö?ö=ö#ö=ö")
done()
})
})
})
o("sets route w/ escaped unicode", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
router.setPath("/%C3%B6?%C3%B6=%C3%B6#%C3%B6=%C3%B6")
o("sets route on fallback mode", function(done) {
$window.location.href = "file://" + prefix + "/test"
o(router.getPath()).equals("/ö?ö=ö#ö=ö")
done()
router = new Router($window)
router.setPrefix(prefix)
router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
router.setPath("/other/x/y/z?c=d#e=f")
o(router.getPath()).equals("/other/x/y/z?c=d#e=f")
done()
})
})
})
o("sets route w/ unicode", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail)
o("sets route via pushState/onpopstate", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
router.setPath("/ö?ö=ö#ö=ö")
callAsync(function() {
$window.history.pushState(null, null, prefix + "/other/x/y/z?c=d#e=f")
$window.onpopstate()
o(router.getPath()).equals("/ö?ö=ö#ö=ö")
done()
o(router.getPath()).equals("/other/x/y/z?c=d#e=f")
done()
})
})
})
o("sets parameterized route", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail)
o("sets route on fallback mode", function(done) {
$window.location.href = "file://" + prefix + "/test"
callAsync(function() {
router.setPath("/other/:a/:b", {a: "x", b: "y/z", c: "d", e: "f"})
router = new Router($window)
router.setPrefix(prefix)
router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
router.setPath("/other/x/y/z?c=d#e=f")
o(router.getPath()).equals("/other/x/y/z?c=d#e=f")
done()
o(router.getPath()).equals("/other/x/y/z?c=d&e=f")
done()
})
})
})
o("sets route via pushState/onpopstate", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail)
o("replace:true works", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/other": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
$window.history.pushState(null, null, prefix + "/other/x/y/z?c=d#e=f")
$window.onpopstate()
callAsync(function() {
router.setPath("/other", null, {replace: true})
$window.history.back()
o(router.getPath()).equals("/other/x/y/z?c=d#e=f")
done()
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + "/")
done()
})
})
})
o("sets parameterized route", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail)
o("replace:false works", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/other": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
router.setPath("/other/:a/:b", {a: "x", b: "y/z", c: "d", e: "f"})
callAsync(function() {
router.setPath("/other", null, {replace: false})
$window.history.back()
o(router.getPath()).equals("/other/x/y/z?c=d&e=f")
done()
})
})
o("replace:true works", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/other": {data: 2}}, onRouteChange, onFail)
var slash = prefix[0] === "/" ? "" : "/"
callAsync(function() {
router.setPath("/other", null, {replace: true})
$window.history.back()
o($window.location.href).equals("http://localhost/")
done()
})
})
o("replace:false works", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/other": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
router.setPath("/other", null, {replace: false})
$window.history.back()
var slash = prefix[0] === "/" ? "" : "/"
o($window.location.href).equals("http://localhost" + slash + (prefix ? prefix + "/" : "") + "test")
done()
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : "") + "test")
done()
})
})
})
})

View file

@ -2,9 +2,11 @@
var parseURL = require("../test-utils/parseURL")
module.exports = function() {
var protocol = "http:"
var hostname = "localhost"
module.exports = function(options) {
if (options == null) options = {}
var protocol = options.protocol || "http:"
var hostname = options.hostname || "localhost"
var port = ""
var pathname = "/"
var search = ""

View file

@ -173,7 +173,7 @@ o.spec("stream", function() {
Stream.stream("20"),
Stream.stream({value: 30}),
])
o(all()).deepEquals([10, "20", {value: 30}])
})
o("remains pending until all streams are active", function() {
@ -184,7 +184,7 @@ o.spec("stream", function() {
Stream.stream("20"),
straggler,
])
o(all()).equals(undefined)
straggler(30)
@ -549,9 +549,18 @@ o.spec("stream", function() {
var absorbed = Stream.stream()
var mapped = stream.run(function(value) {return absorbed})
absorbed(2)
// TODO: uncomment when fixed.
// var depCallCount = 0
// mapped.map(function (value) {
// o(value).equals(200)
// depCallCount += 1
// })
// o(depCallCount).equals(0)
o(mapped()).equals(2)
absorbed(200)
// o(depCallCount).equals(1)
o(mapped()).equals(200)
})
o("works when updating pending stream to errored state", function() {
var stream = Stream.stream(undefined)