Merge remote-tracking branch 'origin/rewrite' into rewrite

Conflicts:
	docs/route.md
This commit is contained in:
Leo Horie 2016-09-12 09:48:30 -04:00
commit 55f0dfa4dd
16 changed files with 546 additions and 392 deletions

View file

@ -2,38 +2,56 @@
var Vnode = require("../render/vnode") var Vnode = require("../render/vnode")
var coreRouter = require("../router/router") var coreRouter = require("../router/router")
var autoredraw = require("../api/autoredraw")
module.exports = function($window, renderer, pubsub) { module.exports = function($window, mount) {
var router = coreRouter($window) var router = coreRouter($window)
var currentResolve, currentComponent, currentRender, currentArgs, currentPath
var RouteComponent = {view: function() {
return currentRender(Vnode(currentComponent, null, currentArgs, undefined, undefined, undefined))
}}
function defaultRender(vnode) {
return vnode
}
var route = function(root, defaultRoute, routes) { var route = function(root, defaultRoute, routes) {
var current = {path: null, component: "div", resolver: null}, currentResolutionIdentifier = null currentComponent = "div"
var replay = router.defineRoutes(routes, function(payload, args, path, route) { currentRender = defaultRender
var resolutionIdentifier = currentResolutionIdentifier = {} currentArgs = null
function resolve(component) {
if (resolutionIdentifier !== currentResolutionIdentifier) return mount(root, RouteComponent)
resolutionIdentifier = null
current.path = path, current.component = component router.defineRoutes(routes, function(payload, args, path) {
renderer.render(root, payload.render(Vnode(component, null, args, undefined, undefined, undefined))) var isResolver = typeof payload.view !== "function"
var render = defaultRender
var resolve = currentResolve = function (component) {
if (resolve !== currentResolve) return
currentResolve = null
currentComponent = component != null ? component : isResolver ? "div" : payload
currentRender = render
currentArgs = args
currentPath = path
root.redraw(true)
} }
if (typeof payload.view !== "function") { var onmatch = function() {
if (typeof payload.render !== "function") payload.render = function(vnode) {return vnode} resolve()
if (typeof payload.onmatch !== "function") payload.onmatch = function() {resolve(current.component)}
if (path !== current.path) payload.onmatch(Vnode(payload, null, args, undefined, undefined, undefined), resolve)
else resolve(current.component)
} }
else { if (isResolver) {
renderer.render(root, Vnode(payload, null, args, undefined, undefined, undefined)) if (typeof payload.render === "function") render = payload.render.bind(payload)
if (typeof payload.onmatch === "function") onmatch = payload.onmatch
} }
onmatch.call(payload, resolve, args, path)
}, function() { }, function() {
router.setPath(defaultRoute, null, {replace: true}) router.setPath(defaultRoute, null, {replace: true})
}) })
autoredraw(root, renderer, pubsub, replay)
} }
route.link = router.link route.link = router.link
route.prefix = router.setPrefix route.prefix = router.setPrefix
route.set = router.setPath route.set = router.setPath
route.get = router.getPath route.get = function() {return currentPath}
return route return route
} }

View file

@ -22,6 +22,16 @@ o.spec("mount", function() {
render = coreRenderer($window).render render = coreRenderer($window).render
}) })
o("throws on invalid `root` DOM node", function() {
var threw = false
try {
mount(null, {view: function() {}})
} catch (e) {
threw = true
}
o(threw).equals(true)
})
o("renders into `root`", function() { o("renders into `root`", function() {
mount(root, { mount(root, {
view : function() { view : function() {

View file

@ -8,13 +8,14 @@ var m = require("../../render/hyperscript")
var coreRenderer = require("../../render/render") var coreRenderer = require("../../render/render")
var apiPubSub = require("../../api/pubsub") var apiPubSub = require("../../api/pubsub")
var apiRouter = require("../../api/router") var apiRouter = require("../../api/router")
var apiMounter = require("../../api/mount")
o.spec("route", function() { o.spec("route", function() {
void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) { 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() { o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() {
var FRAME_BUDGET = Math.floor(1000 / 60) var FRAME_BUDGET = Math.floor(1000 / 60)
var $window, root, redraw, route var $window, root, redraw, mount, route
o.beforeEach(function() { o.beforeEach(function() {
$window = browserMock(env) $window = browserMock(env)
@ -22,11 +23,22 @@ o.spec("route", function() {
root = $window.document.body root = $window.document.body
redraw = apiPubSub() redraw = apiPubSub()
route = apiRouter($window, coreRenderer($window), redraw) mount = apiMounter(coreRenderer($window), redraw)
route = apiRouter($window, mount)
route.prefix(prefix) route.prefix(prefix)
}) })
o("renders into `root`", function(done) { o("throws on invalid `root` DOM node", function() {
var threw = false
try {
route(null, '/', {'/':{view: function() {}}})
} catch (e) {
threw = true
}
o(threw).equals(true)
})
o("renders into `root`", function() {
$window.location.href = prefix + "/" $window.location.href = prefix + "/"
route(root, "/", { route(root, "/", {
"/" : { "/" : {
@ -36,11 +48,21 @@ o.spec("route", function() {
} }
}) })
callAsync(function() { o(root.firstChild.nodeName).equals("DIV")
o(root.firstChild.nodeName).equals("DIV") })
done() o("routed mount points can redraw synchronoulsy (#1275)", function() {
}) var view = o.spy()
$window.location.href = prefix + "/"
route(root, "/", {"/":{view:view}})
o(view.callCount).equals(1)
redraw.publish(true)
o(view.callCount).equals(2)
}) })
o("default route doesn't break back button", function(done) { o("default route doesn't break back button", function(done) {
@ -55,11 +77,11 @@ o.spec("route", function() {
setTimeout(function() { setTimeout(function() {
o(root.firstChild.nodeName).equals("DIV") o(root.firstChild.nodeName).equals("DIV")
$window.history.back() $window.history.back()
o($window.location.pathname).equals("/") o($window.location.pathname).equals("/")
done() done()
}, FRAME_BUDGET) }, FRAME_BUDGET)
}) })
@ -77,12 +99,12 @@ o.spec("route", function() {
function init(vnode) { function init(vnode) {
o(vnode.attrs.foo).equals(undefined) o(vnode.attrs.foo).equals(undefined)
done() done()
} }
}) })
o("redraws when render function is executed", function(done) { o("redraws when render function is executed", function() {
var onupdate = o.spy() var onupdate = o.spy()
var oninit = o.spy() var oninit = o.spy()
@ -98,18 +120,11 @@ o.spec("route", function() {
} }
}) })
callAsync(function() { o(oninit.callCount).equals(1)
o(oninit.callCount).equals(1)
redraw.publish() redraw.publish(true)
// Wrapped to give time for the rate-limited redraw to fire o(onupdate.callCount).equals(1)
setTimeout(function() {
o(onupdate.callCount).equals(1)
done()
}, FRAME_BUDGET)
})
}) })
o("redraws on events", function(done) { o("redraws on events", function(done) {
@ -133,23 +148,21 @@ o.spec("route", function() {
} }
}) })
callAsync(function() { root.firstChild.dispatchEvent(e)
root.firstChild.dispatchEvent(e)
o(oninit.callCount).equals(1) o(oninit.callCount).equals(1)
o(onclick.callCount).equals(1) o(onclick.callCount).equals(1)
o(onclick.this).equals(root.firstChild) o(onclick.this).equals(root.firstChild)
o(onclick.args[0].type).equals("click") o(onclick.args[0].type).equals("click")
o(onclick.args[0].target).equals(root.firstChild) o(onclick.args[0].target).equals(root.firstChild)
// Wrapped to give time for the rate-limited redraw to fire // Wrapped to give time for the rate-limited redraw to fire
setTimeout(function() { setTimeout(function() {
o(onupdate.callCount).equals(1) o(onupdate.callCount).equals(1)
done() done()
}, FRAME_BUDGET) }, FRAME_BUDGET * 2)
})
}) })
o("event handlers can skip redraw", function(done) { o("event handlers can skip redraw", function(done) {
@ -175,21 +188,19 @@ o.spec("route", function() {
} }
}) })
callAsync(function() { root.firstChild.dispatchEvent(e)
root.firstChild.dispatchEvent(e)
o(oninit.callCount).equals(1) o(oninit.callCount).equals(1)
// Wrapped to ensure no redraw fired // Wrapped to ensure no redraw fired
setTimeout(function() { setTimeout(function() {
o(onupdate.callCount).equals(0) o(onupdate.callCount).equals(0)
done() done()
}, FRAME_BUDGET) }, FRAME_BUDGET)
})
}) })
o("changes location on route.link", function(done) { o("changes location on route.link", function() {
var e = $window.document.createEvent("MouseEvents") var e = $window.document.createEvent("MouseEvents")
e.initEvent("click", true, true) e.initEvent("click", true, true)
@ -211,20 +222,16 @@ o.spec("route", function() {
} }
}) })
callAsync(function() { var slash = prefix[0] === "/" ? "" : "/"
var slash = prefix[0] === "/" ? "" : "/"
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : "")) o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : ""))
root.firstChild.dispatchEvent(e) root.firstChild.dispatchEvent(e)
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : "") + "test") o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : "") + "test")
done()
})
}) })
o("accepts RouteResolver", function(done) { o("accepts RouteResolver", function() {
var matchCount = 0 var matchCount = 0
var renderCount = 0 var renderCount = 0
var Component = { var Component = {
@ -232,107 +239,93 @@ o.spec("route", function() {
return m("div") return m("div")
} }
} }
$window.location.href = prefix + "/" $window.location.href = prefix + "/abc"
route(root, "/abc", { route(root, "/abc", {
"/:id" : { "/:id" : {
onmatch: function(vnode, resolve) { onmatch: function(resolve, args, requestedPath) {
matchCount++ matchCount++
o(vnode.attrs.id).equals("abc") o(args.id).equals("abc")
o(route.get()).equals("/abc") o(requestedPath).equals("/abc")
resolve(Component) resolve(Component)
}, },
render: function(vnode) { render: function(vnode) {
renderCount++ renderCount++
o(vnode.attrs.id).equals("abc") o(vnode.attrs.id).equals("abc")
return vnode return vnode
}, },
}, },
}) })
setTimeout(function() { o(matchCount).equals(1)
o(matchCount).equals(1) o(renderCount).equals(1)
o(renderCount).equals(1) o(root.firstChild.nodeName).equals("DIV")
o(root.firstChild.nodeName).equals("DIV")
done()
}, FRAME_BUDGET)
}) })
o("accepts RouteResolver without `render` method as payload", function(done) { o("accepts RouteResolver without `render` method as payload", function() {
var matchCount = 0 var matchCount = 0
var Component = { var Component = {
view: function() { view: function() {
return m("div") return m("div")
} }
} }
$window.location.href = prefix + "/" $window.location.href = prefix + "/abc"
route(root, "/abc", { route(root, "/abc", {
"/:id" : { "/:id" : {
onmatch: function(vnode, resolve) { onmatch: function(resolve, args, requestedPath) {
matchCount++ matchCount++
o(vnode.attrs.id).equals("abc") o(args.id).equals("abc")
o(route.get()).equals("/abc") o(requestedPath).equals("/abc")
resolve(Component) resolve(Component)
}, },
}, },
}) })
setTimeout(function() { o(matchCount).equals(1)
o(matchCount).equals(1)
o(root.firstChild.nodeName).equals("DIV")
o(root.firstChild.nodeName).equals("DIV")
done()
}, FRAME_BUDGET)
}) })
o("accepts RouteResolver without `onmatch` method as payload", function(done) { o("accepts RouteResolver without `onmatch` method as payload", function() {
var renderCount = 0 var renderCount = 0
var Component = { var Component = {
view: function() { view: function() {
return m("div") return m("div")
} }
} }
$window.location.href = prefix + "/" $window.location.href = prefix + "/abc"
route(root, "/abc", { route(root, "/abc", {
"/:id" : { "/:id" : {
render: function(vnode) { render: function(vnode) {
renderCount++ renderCount++
o(vnode.attrs.id).equals("abc") o(vnode.attrs.id).equals("abc")
return m(Component) return m(Component)
}, },
}, },
}) })
setTimeout(function() { o(root.firstChild.nodeName).equals("DIV")
o(root.firstChild.nodeName).equals("DIV")
done()
}, FRAME_BUDGET)
}) })
o("RouteResolver `render` does not have component semantics", function(done, timeout) { o("RouteResolver `render` does not have component semantics", function(done) {
timeout(60)
var renderCount = 0 var renderCount = 0
var A = { var A = {
view: function() { view: function() {
return m("div") return m("div")
} }
} }
$window.location.href = prefix + "/" $window.location.href = prefix + "/a"
route(root, "/a", { route(root, "/a", {
"/a" : { "/a" : {
render: function(vnode) { render: function(vnode) {
@ -345,22 +338,20 @@ o.spec("route", function() {
}, },
}, },
}) })
var dom = root.firstChild
o(root.firstChild.nodeName).equals("DIV")
route.set("/b")
setTimeout(function() { setTimeout(function() {
var dom = root.firstChild o(root.firstChild).equals(dom)
o(root.firstChild.nodeName).equals("DIV")
done()
route.set("/b")
setTimeout(function() {
o(root.firstChild).equals(dom)
done()
}, FRAME_BUDGET)
}, FRAME_BUDGET) }, FRAME_BUDGET)
}) })
o("calls onmatch and view correct number of times", function(done) { o("calls onmatch and view correct number of times", function() {
var matchCount = 0 var matchCount = 0
var renderCount = 0 var renderCount = 0
var Component = { var Component = {
@ -368,11 +359,11 @@ o.spec("route", function() {
return m("div") return m("div")
} }
} }
$window.location.href = prefix + "/" $window.location.href = prefix + "/"
route(root, "/", { route(root, "/", {
"/" : { "/" : {
onmatch: function(vnode, resolve) { onmatch: function(resolve) {
matchCount++ matchCount++
resolve(Component) resolve(Component)
}, },
@ -383,69 +374,63 @@ o.spec("route", function() {
}, },
}) })
callAsync(function() { o(matchCount).equals(1)
o(matchCount).equals(1) o(renderCount).equals(1)
o(renderCount).equals(1)
redraw.publish()
setTimeout(function() { redraw.publish(true)
o(matchCount).equals(1)
o(renderCount).equals(2) o(matchCount).equals(1)
o(renderCount).equals(2)
done()
}, FRAME_BUDGET)
})
}) })
o("onmatch can redirect to another route", function(done) { o("onmatch can redirect to another route", function(done) {
var redirected = false var redirected = false
$window.location.href = prefix + "/" $window.location.href = prefix + "/a"
route(root, "/a", { route(root, "/a", {
"/a" : { "/a" : {
onmatch: function() { onmatch: function() {
route.set("/b") route.set("/b")
} }
}, },
"/b" : { "/b" : {
view: function(vnode){ view: function(vnode){
redirected = true redirected = true
} }
} }
}) })
setTimeout(function() { setTimeout(function() {
o(redirected).equals(true) o(redirected).equals(true)
done()
}, FRAME_BUDGET)
})
done()
}, FRAME_BUDGET)
})
o("onmatch can redirect to another route that has RouteResolver", function(done) { o("onmatch can redirect to another route that has RouteResolver", function(done) {
var redirected = false var redirected = false
$window.location.href = prefix + "/" $window.location.href = prefix + "/a"
route(root, "/a", { route(root, "/a", {
"/a" : { "/a" : {
onmatch: function() { onmatch: function() {
route.set("/b") route.set("/b")
} }
}, },
"/b" : { "/b" : {
render: function(vnode){ render: function(vnode){
redirected = true redirected = true
} }
} }
}) })
setTimeout(function() { setTimeout(function() {
o(redirected).equals(true) o(redirected).equals(true)
done()
}, FRAME_BUDGET)
})
done()
}, FRAME_BUDGET)
})
o("onmatch resolution callback resolves at most once", function(done) { o("onmatch resolution callback resolves at most once", function(done) {
var resolveCount = 0 var resolveCount = 0
var resolvedComponent var resolvedComponent
@ -456,7 +441,7 @@ o.spec("route", function() {
$window.location.href = prefix + "/" $window.location.href = prefix + "/"
route(root, "/", { route(root, "/", {
"/": { "/": {
onmatch: function(vnode, resolve) { onmatch: function(resolve) {
resolve(A) resolve(A)
resolve(B) resolve(B)
callAsync(function() {resolve(C)}) callAsync(function() {resolve(C)})
@ -474,15 +459,114 @@ o.spec("route", function() {
done() done()
}, FRAME_BUDGET) }, FRAME_BUDGET)
}) })
o("calling route.set invalidates pending onmatch resolution", function(done, timeout) { o("the previous view redraws while onmatch resolution is pending (#1268)", function(done) {
timeout(100) var view = o.spy()
var onmatch = o.spy()
var resolved
$window.location.href = prefix + "/a"
route(root, "/", {
"/a": {view: view},
"/b": {onmatch: onmatch}
})
o(view.callCount).equals(1)
o(onmatch.callCount).equals(0)
route.set("/b")
setTimeout(function(){
o(view.callCount).equals(1)
o(onmatch.callCount).equals(1)
redraw.publish(true)
o(view.callCount).equals(2)
o(onmatch.callCount).equals(1)
done()
}, FRAME_BUDGET)
})
o("m.route.set(m.route.get()) re-runs the resolution logic (#1180)", function(done){
var onmatch = o.spy(function(resolve){resolve()})
$window.location.href = prefix + "/" $window.location.href = prefix + "/"
route(root, '/', {
"/":{
onmatch: onmatch,
render: function(){return m("div")}
}
})
o(onmatch.callCount).equals(1)
route.set(route.get())
setTimeout(function() {
o(onmatch.callCount).equals(2)
done()
}, FRAME_BUDGET)
})
o("m.route.get() returns the last fully resolved route (#1276)", function(done){
$window.location.href = prefix + "/"
route(root, "/", {
"/": {view: function(){}},
"/2": {onmatch: function(){}}
})
o(route.get()).equals("/")
route.set("/2")
setTimeout(function(){
o(route.get()).equals("/")
done()
}, FRAME_BUDGET)
})
o("routing with RouteResolver works more than once (#1286)", function(done, timeout){
timeout(FRAME_BUDGET * 3)
$window.location.href = prefix + "/a"
route(root, '/a', {
'/a': {
render: function() {
return m("a", "a")
}
},
'/b': {
render: function() {
return m("b", "b")
}
}
})
route.set('/b')
setTimeout(function(){
route.set('/a')
setTimeout(function(){
o(root.firstChild.nodeName).equals("A")
done()
}, FRAME_BUDGET)
}, FRAME_BUDGET)
})
o("calling route.set invalidates pending onmatch resolution", function(done, timeout) {
timeout(50)
var resolved
$window.location.href = prefix + "/a"
route(root, "/a", { route(root, "/a", {
"/a": { "/a": {
onmatch: function(vnode, resolve) { onmatch: function(resolve) {
setTimeout(resolve, 20) setTimeout(resolve, 20)
}, },
render: function(vnode) {resolved = "a"} render: function(vnode) {resolved = "a"}
@ -491,15 +575,14 @@ o.spec("route", function() {
view: function() {resolved = "b"} view: function() {resolved = "b"}
} }
}) })
setTimeout(function() {
route.set("/b")
setTimeout(function() {
o(resolved).equals("b")
done() route.set("/b")
}, 30)
}, FRAME_BUDGET) setTimeout(function() {
o(resolved).equals("b")
done()
}, 30)
}) })
}) })
}) })

View file

@ -2,13 +2,13 @@
- [API](#api) - [API](#api)
- [Static members](#static-members) - [Static members](#static-members)
- [route.set](#route-set) - [route.set](#routeset)
- [route.get](#route-get) - [route.get](#routeget)
- [route.prefix](#route-prefix) - [route.prefix](#routeprefix)
- [route.link](#route-link) - [route.link](#routelink)
- [RouteResolver](#routeresolver) - [RouteResolver](#routeresolver)
- [routeResolver.onmatch](#routeresolver-onmatch) - [routeResolver.onmatch](#routeresolveronmatch)
- [routeResolver.render](#routeresolver-render) - [routeResolver.render](#routeresolverrender)
- [How it works](#how-it-works) - [How it works](#how-it-works)
- [Typical usage](#typical-usage) - [Typical usage](#typical-usage)
- [Navigating to different routes](#navigating-to-different-routes) - [Navigating to different routes](#navigating-to-different-routes)
@ -52,13 +52,13 @@ Argument | Type | Required | Description
##### route.get ##### route.get
Returns the current routing path, without the prefix. Returns the last fully resolved routing path, without the prefix. It may differ from the path displayed in the location bar while an asynchronous route is [pending resolution](#code-splitting).
`path = m.route.get()` `path = m.route.get()`
Argument | Type | Required | Description Argument | Type | Required | Description
----------------- | --------- | -------- | --- ----------------- | --------- | -------- | ---
**returns** | String | | Returns the current path **returns** | String | | Returns the last fully resolved path
##### route.prefix ##### route.prefix
@ -94,14 +94,12 @@ This method also allows you to asynchronously define what component will be rend
`routeResolver.onmatch(vnode, resolve)` `routeResolver.onmatch(vnode, resolve)`
Argument | Type | Description Argument | Type | Description
------------------- | --------------------- | --- --------------- | --------------------- | ---
`vnode` | `Vnode` | A [vnode](vnodes.md) whose attributes object contains routing parameters. If the routeResolver does not have a `resolve` method, the vnode's `tag` field defaults to a `div` `resolve` | `Function(Component)` | Call this function with a component as the first argument to use it as the route's component
`vnode.attrs` | `Object` | The [routing parameters](#routing-parameters) `args` | `Object` | The [routing parameters](#routing-parameters)
`vnode.attrs.path` | `String` | The current router path, including interpolated routing parameter values, but without the prefix. Same value as `m.route.get()` `requestedPath` | `String` | The router path requested by the last routing action, including interpolated routing parameter values, but without the prefix. When `onmatch` is called, the resolution for this path is not complete and `m.route.get()` still returns the previous path.
`vnode.attrs.route` | `String` | The matched route **returns** | | Returns `undefined`
`resolve` | `Function(Component)` | Call this function with a component as the first argument to use it as the route's component
**returns** | | Returns `undefined`
##### routeResolver.render ##### routeResolver.render
@ -113,8 +111,6 @@ Argument | Type | Description
------------------- | --------------- | ----------- ------------------- | --------------- | -----------
`vnode` | `Object` | A [vnode](vnodes.md) whose attributes object contains routing parameters. If the routeResolver does not have a `resolve` method, the vnode's `tag` field defaults to a `div` `vnode` | `Object` | A [vnode](vnodes.md) whose attributes object contains routing parameters. If the routeResolver does not have a `resolve` method, the vnode's `tag` field defaults to a `div`
`vnode.attrs` | `Object` | A [vnode](vnodes.md) whose attributes object contains routing parameters. If the routeResolver does not have a `resolve` method, the vnode defaults to a `div` `vnode.attrs` | `Object` | A [vnode](vnodes.md) whose attributes object contains routing parameters. If the routeResolver does not have a `resolve` method, the vnode defaults to a `div`
`vnode.attrs.path` | `String` | The current router path, including interpolated routing parameter values, but without the prefix. Same value as `m.route.get()`
`vnode.attrs.route` | `String` | The matched route
**returns** | `Vnode` | Returns a vnode **returns** | `Vnode` | Returns a vnode
--- ---
@ -271,7 +267,7 @@ Instead of mapping a component to a route, you can specify a RouteResolver objec
```javascript ```javascript
m.route(document.body, "/", { m.route(document.body, "/", {
"/": { "/": {
onmatch: function(vnode, resolve) { onmatch: function(resolve, args, requestedPath) {
resolve(Home) resolve(Home)
}, },
render: function(vnode) { render: function(vnode) {
@ -351,7 +347,7 @@ var Login = {
m.route(document.body, "/secret", { m.route(document.body, "/secret", {
"/secret": { "/secret": {
onmatch: function(vnode, resolve) { onmatch: function(resolve) {
if (isLoggedIn) resolve(Home) if (isLoggedIn) resolve(Home)
else m.route.set("/login") else m.route.set("/login")
}, },
@ -399,7 +395,7 @@ function load(file, done) {
m.route(document.body, "/", { m.route(document.body, "/", {
"/": { "/": {
onmatch: function(vnode, resolve) { onmatch: function(resolve) {
load("Home.js", resolve) load("Home.js", resolve)
}, },
}, },
@ -413,7 +409,7 @@ Fortunately, there are a number of tools that facilitate the task of bundling mo
```javascript ```javascript
m.route(document.body, "/", { m.route(document.body, "/", {
"/": { "/": {
onmatch: function(vnode, resolve) { onmatch: function(resolve) {
// using Webpack async code splitting // using Webpack async code splitting
require(['./Home.js'], resolve) require(['./Home.js'], resolve)
}, },

View file

@ -209,7 +209,7 @@ m.route(element, '/', {
## `m.route` mode ## `m.route` mode
`m.route.mode` was replaced by `m.route.prefix(prefix)` where `prefix` can be `#`, `?`, `` (for "pathname" mode). The new API also supports hashbang (`#!`), which is the default, and it supports non-root pathnames and arbitrary mode variations such as querybang (`?!`) `m.route.mode` was replaced by `m.route.prefix(prefix)` where `prefix` can be `#`, `?`, or an empty string (for "pathname" mode). The new API also supports hashbang (`#!`), which is the default, and it supports non-root pathnames and arbitrary mode variations such as querybang (`?!`)
## `m.route()` and anchor tags ## `m.route()` and anchor tags

View file

@ -10,8 +10,8 @@ var Stream = require("./stream")
requestService.setCompletionCallback(redrawService.publish) requestService.setCompletionCallback(redrawService.publish)
m.route = require("./route")
m.mount = require("./mount") m.mount = require("./mount")
m.route = require("./route")
m.withAttr = require("./util/withAttr") m.withAttr = require("./util/withAttr")
m.prop = Stream m.prop = Stream
m.render = renderService.render m.render = renderService.render

View file

@ -573,6 +573,7 @@ var renderService = function($window) {
Object.keys(source).forEach(function(k){target[k] = source[k]}) Object.keys(source).forEach(function(k){target[k] = source[k]})
} }
function render(dom, vnodes) { function render(dom, vnodes) {
if (!dom) throw new Error("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.")
var hooks = [] var hooks = []
var active = $doc.activeElement var active = $doc.activeElement
// First time rendering into a node clears it out // First time rendering into a node clears it out
@ -967,6 +968,56 @@ var parseQueryString = function(string) {
return data return data
} }
requestService.setCompletionCallback(redrawService.publish) requestService.setCompletionCallback(redrawService.publish)
var throttle = function(callback) {
//60fps translates to 16.6ms, round it down since setTimeout requires int
var time = 16
var last = 0, pending = null
var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
return function(synchronous) {
var now = Date.now()
if (synchronous === true || last === 0 || now - last >= time) {
last = now
callback()
}
else if (pending === null) {
pending = timeout(function() {
pending = null
callback()
last = Date.now()
}, time - (now - last))
}
}
}
var autoredraw = function(root, renderer, pubsub, callback) {
var run = throttle(callback)
if (renderer != null) {
renderer.setEventCallback(function(e) {
if (e.redraw !== false) pubsub.publish()
})
}
if (pubsub != null) {
if (root.redraw) pubsub.unsubscribe(root.redraw)
pubsub.subscribe(run)
}
return root.redraw = run
}
m.mount = function(renderer, pubsub) {
return function(root, component) {
if (component === null) {
renderer.render(root, [])
pubsub.unsubscribe(root.redraw)
delete root.redraw
return
}
var run = autoredraw(root, renderer, pubsub, function() {
renderer.render(
root,
Vnode(component, undefined, undefined, undefined, undefined, undefined)
)
})
run()
}
}(renderService, redrawService)
var coreRouter = function($window) { var coreRouter = function($window) {
var supportsPushState = typeof $window.history.pushState === "function" var supportsPushState = typeof $window.history.pushState === "function"
var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout
@ -977,6 +1028,16 @@ var coreRouter = function($window) {
if (fragment === "pathname" && data[0] !== "/") data = "/" + data if (fragment === "pathname" && data[0] !== "/") data = "/" + data
return data return data
} }
var asyncId
function debounceAsync(f) {
return function() {
if (asyncId != null) return
asyncId = callAsync(function() {
asyncId = null
f()
})
}
}
function parsePath(path, queryData, hashData) { function parsePath(path, queryData, hashData) {
var queryIndex = path.indexOf("?") var queryIndex = path.indexOf("?")
var hashIndex = path.indexOf("#") var hashIndex = path.indexOf("#")
@ -1022,7 +1083,7 @@ var coreRouter = function($window) {
else $window.location.href = prefix + path else $window.location.href = prefix + path
} }
function defineRoutes(routes, resolve, reject) { function defineRoutes(routes, resolve, reject) {
if (supportsPushState) $window.onpopstate = resolveRoute if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute)
else if (prefix.charAt(0) === "#") $window.onhashchange = resolveRoute else if (prefix.charAt(0) === "#") $window.onhashchange = resolveRoute
resolveRoute() resolveRoute()
@ -1031,23 +1092,21 @@ var coreRouter = function($window) {
var params = {} var params = {}
var pathname = parsePath(path, params, params) var pathname = parsePath(path, params, params)
callAsync(function() { for (var route in routes) {
for (var route in routes) { var matcher = new RegExp("^" + route.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$")
var matcher = new RegExp("^" + route.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$") if (matcher.test(pathname)) {
if (matcher.test(pathname)) { pathname.replace(matcher, function() {
pathname.replace(matcher, function() { var keys = route.match(/:[^\/]+/g) || []
var keys = route.match(/:[^\/]+/g) || [] var values = [].slice.call(arguments, 1, -2)
var values = [].slice.call(arguments, 1, -2) for (var i = 0; i < keys.length; i++) {
for (var i = 0; i < keys.length; i++) { params[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i])
params[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i]) }
} resolve(routes[route], params, path, route)
resolve(routes[route], params, path, route) })
}) return
return
}
} }
reject(path, params) }
}) reject(path, params)
} }
return resolveRoute return resolveRoute
} }
@ -1061,89 +1120,52 @@ var coreRouter = function($window) {
} }
return {setPrefix: setPrefix, getPath: getPath, setPath: setPath, defineRoutes: defineRoutes, link: link} return {setPrefix: setPrefix, getPath: getPath, setPath: setPath, defineRoutes: defineRoutes, link: link}
} }
var throttle = function(callback) { m.route = function($window, mount1) {
//60fps translates to 16.6ms, round it down since setTimeout requires int
var time = 16
var last = 0, pending = null
var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
return function(synchronous) {
var now = Date.now()
if (synchronous === true || last === 0 || now - last >= time) {
last = now
callback()
}
else if (pending === null) {
pending = timeout(function() {
pending = null
callback()
last = Date.now()
}, time - (now - last))
}
}
}
var autoredraw = function(root, renderer, pubsub, callback) {
var run = throttle(callback)
if (renderer != null) {
renderer.setEventCallback(function(e) {
if (e.redraw !== false) pubsub.publish()
})
}
if (pubsub != null) {
if (root.redraw) pubsub.unsubscribe(root.redraw)
pubsub.subscribe(run)
}
return root.redraw = run
}
m.route = function($window, renderer, pubsub) {
var router = coreRouter($window) var router = coreRouter($window)
var globalId, currentComponent, currentRender, currentArgs, currentPath
var RouteComponent = {view: function() {
return currentRender(Vnode(currentComponent, null, currentArgs, undefined, undefined, undefined))
}}
function defaultRender(vnode) {
return vnode
}
var route = function(root, defaultRoute, routes) { var route = function(root, defaultRoute, routes) {
var current = {path: null, component: "div", resolver: null}, currentResolutionIdentifier = null currentComponent = "div"
var replay = router.defineRoutes(routes, function(payload, args, path, route) { currentRender = defaultRender
var resolutionIdentifier = currentResolutionIdentifier = {} currentArgs = null
function resolve(component) { mount1(root, RouteComponent)
if (resolutionIdentifier !== currentResolutionIdentifier) return router.defineRoutes(routes, function(payload, args, path) {
resolutionIdentifier = null var resolutionIdentifier = globalId = {}
current.path = path, current.component = component var isResolver = typeof payload.view !== "function"
renderer.render(root, payload.render(Vnode(component, null, args, undefined, undefined, undefined))) var render = defaultRender
function resolve (component) {
if (resolutionIdentifier !== globalId) return
globalId = null
currentComponent = component != null ? component : isResolver ? "div" : payload
currentRender = render
currentArgs = args
currentPath = path
root.redraw(true)
} }
if (typeof payload.view !== "function") { var onmatch = function() {
if (typeof payload.render !== "function") payload.render = function(vnode) {return vnode} resolve()
if (typeof payload.onmatch !== "function") payload.onmatch = function() {resolve(current.component)}
if (path !== current.path) payload.onmatch(Vnode(payload, null, args, undefined, undefined, undefined), resolve)
else resolve(current.component)
} }
else { if (isResolver) {
renderer.render(root, Vnode(payload, null, args, undefined, undefined, undefined)) if (typeof payload.render === "function") render = payload.render.bind(payload)
if (typeof payload.onmatch === "function") onmatch = payload.onmatch
} }
onmatch.call(payload, resolve, args, path)
}, function() { }, function() {
router.setPath(defaultRoute, null, {replace: true}) router.setPath(defaultRoute, null, {replace: true})
}) })
autoredraw(root, renderer, pubsub, replay)
} }
route.link = router.link route.link = router.link
route.prefix = router.setPrefix route.prefix = router.setPrefix
route.set = router.setPath route.set = router.setPath
route.get = router.getPath route.get = function() {return currentPath}
return route return route
}(window, renderService, redrawService) }(window, m.mount)
m.mount = function(renderer, pubsub) {
return function(root, component) {
if (component === null) {
renderer.render(root, [])
pubsub.unsubscribe(root.redraw)
delete root.redraw
return
}
var run = autoredraw(root, renderer, pubsub, function() {
renderer.render(
root,
Vnode(component, undefined, undefined, undefined, undefined, undefined)
)
})
run()
}
}(renderService, redrawService)
m.withAttr = function(attrName, callback, context) { m.withAttr = function(attrName, callback, context) {
return function(e) { return function(e) {
return callback.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName)) return callback.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName))

83
mithril.min.js vendored
View file

@ -1,41 +1,42 @@
new function(){function u(d,e,p,g,k,m){return{tag:d,key:e,attrs:p,children:g,text:k,dom:m,domSize:void 0,state:{},events:void 0,instance:void 0,skip:!1}}function x(d){if(null==d||"string"!==typeof d&&null==d.view)throw Error("The selector must be either a string or a component.");if("string"===typeof d&&void 0===H[d]){for(var e,p,g=[],k={};e=P.exec(d);){var m=e[1],n=e[2];""===m&&""!==n?p=n:"#"===m?k.id=n:"."===m?g.push(n):"["===e[3][0]&&((m=e[6])&&(m=m.replace(/\\(["'])/g,"$1").replace(/\\\\/g,"\\")), new function(){function x(b,f,n,h,k,l){return{tag:b,key:f,attrs:n,children:h,text:k,dom:l,domSize:void 0,state:{},events:void 0,instance:void 0,skip:!1}}function y(b){if(null==b||"string"!==typeof b&&null==b.view)throw Error("The selector must be either a string or a component.");if("string"===typeof b&&void 0===F[b]){for(var f,n,h=[],k={};f=P.exec(b);){var l=f[1],m=f[2];""===l&&""!==m?n=m:"#"===l?k.id=m:"."===l?h.push(m):"["===f[3][0]&&((l=f[6])&&(l=l.replace(/\\(["'])/g,"$1").replace(/\\\\/g,"\\")),
k[e[4]]=m||!0)}0<g.length&&(k.className=g.join(" "));H[d]=function(d,c){var e=!1,g,h,m=d.className||d["class"],n;for(n in k)d[n]=k[n];void 0!==m&&(void 0!==d["class"]&&(d["class"]=void 0,d.className=m),void 0!==k.className&&(d.className=k.className+" "+m));for(n in d)if("key"!==n){e=!0;break}c instanceof Array&&1==c.length&&null!=c[0]&&"#"===c[0].tag?h=c[0].children:g=c;return u(p||"div",d.key,e?d:void 0,g,h,void 0)}}var l;null!=arguments[1]&&("object"!==typeof arguments[1]||void 0!==arguments[1].tag|| k[f[4]]=l||!0)}0<h.length&&(k.className=h.join(" "));F[b]=function(b,d){var f=!1,l,h,m=b.className||b["class"],r;for(r in k)b[r]=k[r];void 0!==m&&(void 0!==b["class"]&&(b["class"]=void 0,b.className=m),void 0!==k.className&&(b.className=k.className+" "+m));for(r in b)if("key"!==r){f=!0;break}d instanceof Array&&1==d.length&&null!=d[0]&&"#"===d[0].tag?h=d[0].children:l=d;return x(n||"div",b.key,f?b:void 0,l,h,void 0)}}var w;null!=arguments[1]&&("object"!==typeof arguments[1]||void 0!==arguments[1].tag||
arguments[1]instanceof Array)?g=1:(l=arguments[1],g=2);if(arguments.length===g+1)e=arguments[g]instanceof Array?arguments[g]:[arguments[g]];else for(e=[];g<arguments.length;g++)e.push(arguments[g]);return"string"===typeof d?H[d](l||{},u.normalizeChildren(e)):u(d,l&&l.key,l||{},u.normalizeChildren(e),void 0,void 0)}u.normalize=function(d){return d instanceof Array?u("[",void 0,void 0,u.normalizeChildren(d),void 0,void 0):null!=d&&"object"!==typeof d?u("#",void 0,void 0,d,void 0,void 0):d};u.normalizeChildren= arguments[1]instanceof Array)?h=1:(w=arguments[1],h=2);if(arguments.length===h+1)f=arguments[h]instanceof Array?arguments[h]:[arguments[h]];else for(f=[];h<arguments.length;h++)f.push(arguments[h]);return"string"===typeof b?F[b](w||{},x.normalizeChildren(f)):x(b,w&&w.key,w||{},x.normalizeChildren(f),void 0,void 0)}x.normalize=function(b){return b instanceof Array?x("[",void 0,void 0,x.normalizeChildren(b),void 0,void 0):null!=b&&"object"!==typeof b?x("#",void 0,void 0,b,void 0,void 0):b};x.normalizeChildren=
function(d){for(var e=0;e<d.length;e++)d[e]=u.normalize(d[e]);return d};var P=/(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g,H={};x.trust=function(d){return u("<",void 0,void 0,d,void 0,void 0)};x.fragment=function(d,e){return u("[",d.key,d,u.normalizeChildren(e),void 0,void 0)};var F=function(d){function e(b,a,f,c,d,e,q){for(;f<c;f++){var r=a[f];null!=r&&t(b,p(r,d,q),e)}}function p(b,a,f){var c=b.tag;null!=b.attrs&&J(b.attrs,b,a);if("string"===typeof c)switch(c){case "#":return b.dom= function(b){for(var f=0;f<b.length;f++)b[f]=x.normalize(b[f]);return b};var P=/(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g,F={};y.trust=function(b){return x("<",void 0,void 0,b,void 0,void 0)};y.fragment=function(b,f){return x("[",b.key,b,x.normalizeChildren(f),void 0,void 0)};var N=function(b){function f(c,a,e,d,b,f,p){for(;e<d;e++){var v=a[e];null!=v&&q(c,n(v,b,p),f)}}function n(c,a,e){var d=c.tag;null!=c.attrs&&D(c.attrs,c,a);if("string"===typeof d)switch(d){case "#":return c.dom=
y.createTextNode(b.children);case "<":return g(b);case "[":var d=y.createDocumentFragment();null!=b.children&&(c=b.children,e(d,c,0,c.length,a,null,f));b.dom=d.firstChild;b.domSize=d.childNodes.length;return d;default:var t=b.tag;switch(b.tag){case "svg":f="http://www.w3.org/2000/svg";break;case "math":f="http://www.w3.org/1998/Math/MathML"}var q=(c=b.attrs)&&c.is,t=f?q?y.createElementNS(f,t,{is:q}):y.createElementNS(f,t):q?y.createElement(t,{is:q}):y.createElement(t);b.dom=t;if(null!=c)for(d in q= z.createTextNode(c.children);case "<":return h(c);case "[":var b=z.createDocumentFragment();null!=c.children&&(d=c.children,f(b,d,0,d.length,a,null,e));c.dom=b.firstChild;c.domSize=b.childNodes.length;return b;default:var g=c.tag;switch(c.tag){case "svg":e="http://www.w3.org/2000/svg";break;case "math":e="http://www.w3.org/1998/Math/MathML"}var p=(d=c.attrs)&&d.is,g=e?p?z.createElementNS(e,g,{is:p}):z.createElementNS(e,g):p?z.createElement(g,{is:p}):z.createElement(g);c.dom=g;if(null!=d)for(b in p=
f,c)v(b,d,null,c[d],q);null!=b.text&&(""!==b.text?t.textContent=b.text:b.children=[u("#",void 0,void 0,b.text,void 0,void 0)]);null!=b.children&&(d=b.children,e(t,d,0,d.length,a,null,f),a=b.attrs,"select"===b.tag&&null!=a&&("value"in a&&v(b,"value",null,a.value,void 0),"selectedIndex"in a&&v(b,"selectedIndex",null,a.selectedIndex,void 0)));return t}else{b.state||(b.state={});K(b.state,b.tag);J(b.tag,b,a);b.instance=u.normalize(b.tag.view.call(b.state,b));if(null!=b.instance){if(b.instance===b)throw Error("A view cannot return the vnode it received as arguments"); e,d)u(c,b,null,d[b],p);null!=c.text&&(""!==c.text?g.textContent=c.text:c.children=[x("#",void 0,void 0,c.text,void 0,void 0)]);null!=c.children&&(b=c.children,f(g,b,0,b.length,a,null,e),a=c.attrs,"select"===c.tag&&null!=a&&("value"in a&&u(c,"value",null,a.value,void 0),"selectedIndex"in a&&u(c,"selectedIndex",null,a.selectedIndex,void 0)));return g}else{c.state||(c.state={});Q(c.state,c.tag);D(c.tag,c,a);c.instance=x.normalize(c.tag.view.call(c.state,c));if(null!=c.instance){if(c.instance===c)throw Error("A view cannot return the vnode it received as arguments");
a=p(b.instance,a,f);b.dom=b.instance.dom;b.domSize=null!=b.dom?b.instance.domSize:0;b=a}else b.domSize=0,b=D;return b}}function g(b){var a={caption:"table",thead:"table",tbody:"table",tfoot:"table",tr:"tbody",th:"tr",td:"tr",colgroup:"table",col:"colgroup"}[(b.children.match(/^\s*?<(\w+)/im)||[])[1]]||"div",a=y.createElement(a);a.innerHTML=b.children;b.dom=a.firstChild;b.domSize=a.childNodes.length;b=y.createDocumentFragment();for(var f;f=a.firstChild;)b.appendChild(f);return b}function k(b,a,f,d, a=n(c.instance,a,e);c.dom=c.instance.dom;c.domSize=null!=c.dom?c.instance.domSize:0;c=a}else c.domSize=0,c=J;return c}}function h(c){var a={caption:"table",thead:"table",tbody:"table",tfoot:"table",tr:"tbody",th:"tr",td:"tr",colgroup:"table",col:"colgroup"}[(c.children.match(/^\s*?<(\w+)/im)||[])[1]]||"div",a=z.createElement(a);a.innerHTML=c.children;c.dom=a.firstChild;c.domSize=a.childNodes.length;c=z.createDocumentFragment();for(var e;e=a.firstChild;)c.appendChild(e);return c}function k(c,a,e,b,
h,g){if(a!==f&&(null!=a||null!=f))if(null==a)e(b,f,0,f.length,d,h,void 0);else if(null==f)c(b,a,0,a.length,f);else{var q;a:{if(null!=a.pool&&Math.abs(a.pool.length-f.length)<=Math.abs(a.length-f.length)&&(q=f[0]&&f[0].children&&f[0].children.length||0,Math.abs((a.pool[0]&&a.pool[0].children&&a.pool[0].children.length||0)-q)<=Math.abs((a[0]&&a[0].children&&a[0].children.length||0)-q))){q=!0;break a}q=!1}q&&(a=a.concat(a.pool));if(a.length===f.length&&null!=f[0]&&null==f[0].key)for(var r=0;r<a.length;r++)a[r]=== g,h){if(a!==e&&(null!=a||null!=e))if(null==a)f(c,e,0,e.length,b,g,void 0);else if(null==e)d(c,a,0,a.length,e);else{var p;a:{if(null!=a.pool&&Math.abs(a.pool.length-e.length)<=Math.abs(a.length-e.length)&&(p=e[0]&&e[0].children&&e[0].children.length||0,Math.abs((a.pool[0]&&a.pool[0].children&&a.pool[0].children.length||0)-p)<=Math.abs((a[0]&&a[0].children&&a[0].children.length||0)-p))){p=!0;break a}p=!1}p&&(a=a.concat(a.pool));if(a.length===e.length&&null!=e[0]&&null==e[0].key)for(var v=0;v<a.length;v++)a[v]===
f[r]||null==a[r]&&null==f[r]||(null==a[r]?t(b,p(f[r],d,g),l(a,r+1,h)):null==f[r]?c(b,a,r,r+1,f):m(b,a[r],f[r],d,l(a,r+1,h),q,g),q&&a[r].tag===f[r].tag&&t(b,n(a[r]),l(a,r+1,h)));else{for(var B=r=0,k=a.length-1,z=f.length-1,A;k>=r&&z>=B;){var v=a[r],w=f[B];if(v===w)r++,B++;else if(null!=v&&null!=w&&v.key===w.key)r++,B++,m(b,v,w,d,l(a,r,h),q,g),q&&v.tag===w.tag&&t(b,n(v),h);else if(v=a[k],v===w)k--,B++;else if(null!=v&&null!=w&&v.key===w.key)m(b,v,w,d,l(a,k+1,h),q,g),B<z&&t(b,n(v),l(a,r,h)),k--,B++; e[v]||null==a[v]&&null==e[v]||(null==a[v]?q(c,n(e[v],b,h),w(a,v+1,g)):null==e[v]?d(c,a,v,v+1,e):l(c,a[v],e[v],b,w(a,v+1,g),p,h),p&&a[v].tag===e[v].tag&&q(c,m(a[v]),w(a,v+1,g)));else{for(var E=v=0,k=a.length-1,A=e.length-1,t;k>=v&&A>=E;){var r=a[v],u=e[E];if(r===u)v++,E++;else if(null!=r&&null!=u&&r.key===u.key)v++,E++,l(c,r,u,b,w(a,v,g),p,h),p&&r.tag===u.tag&&q(c,m(r),g);else if(r=a[k],r===u)k--,E++;else if(null!=r&&null!=u&&r.key===u.key)l(c,r,u,b,w(a,k+1,g),p,h),E<A&&q(c,m(r),w(a,v,g)),k--,E++;
else break}for(;k>=r&&z>=B;){v=a[k];w=f[z];if(v===w)k--;else if(null!=v&&null!=w&&v.key===w.key)m(b,v,w,d,l(a,k+1,h),q,g),q&&v.tag===w.tag&&t(b,n(v),h),null!=v.dom&&(h=v.dom),k--;else{if(!A){A=a;var v=k,u={},y;for(y=0;y<v;y++){var C=A[y];null!=C&&(C=C.key,null!=C&&(u[C]=y))}A=u}null!=w&&(v=A[w.key],null!=v?(u=a[v],m(b,u,w,d,l(a,k+1,h),q,g),t(b,n(u),h),a[v].skip=!0,null!=u.dom&&(h=u.dom)):(w=p(w,d,void 0),t(b,w,h),h=w))}z--;if(z<B)break}e(b,f,B,z+1,d,h,g);c(b,a,r,k+1,f)}}}function m(b,a,f,c,d,e,q){var r= else break}for(;k>=v&&A>=E;){r=a[k];u=e[A];if(r===u)k--;else if(null!=r&&null!=u&&r.key===u.key)l(c,r,u,b,w(a,k+1,g),p,h),p&&r.tag===u.tag&&q(c,m(r),g),null!=r.dom&&(g=r.dom),k--;else{if(!t){t=a;var r=k,C={},z;for(z=0;z<r;z++){var D=t[z];null!=D&&(D=D.key,null!=D&&(C[D]=z))}t=C}null!=u&&(r=t[u.key],null!=r?(C=a[r],l(c,C,u,b,w(a,k+1,g),p,h),q(c,m(C),g),a[r].skip=!0,null!=C.dom&&(g=C.dom)):(u=n(u,b,void 0),q(c,u,g),g=u))}A--;if(A<E)break}f(c,e,E,A+1,b,g,h);d(c,a,v,k+1,e)}}}function l(c,a,e,b,d,g,p){var v=
a.tag;if(r===f.tag){f.state=a.state;f.events=a.events;var h;var z;null!=f.attrs&&"function"===typeof f.attrs.onbeforeupdate&&(h=f.attrs.onbeforeupdate.call(f.state,f,a));"string"!==typeof f.tag&&"function"===typeof f.tag.onbeforeupdate&&(z=f.tag.onbeforeupdate.call(f.state,f,a));void 0===h&&void 0===z||h||z?h=!1:(f.dom=a.dom,f.domSize=a.domSize,f.instance=a.instance,h=!0);if(!h)if(null!=f.attrs&&x(f.attrs,f,c,e),"string"===typeof r)switch(r){case "#":a.children.toString()!==f.children.toString()&& a.tag;if(v===e.tag){e.state=a.state;e.events=a.events;var f;var t;null!=e.attrs&&"function"===typeof e.attrs.onbeforeupdate&&(f=e.attrs.onbeforeupdate.call(e.state,e,a));"string"!==typeof e.tag&&"function"===typeof e.tag.onbeforeupdate&&(t=e.tag.onbeforeupdate.call(e.state,e,a));void 0===f&&void 0===t||f||t?f=!1:(e.dom=a.dom,e.domSize=a.domSize,e.instance=a.instance,f=!0);if(!f)if(null!=e.attrs&&y(e.attrs,e,b,g),"string"===typeof v)switch(v){case "#":a.children.toString()!==e.children.toString()&&
(a.dom.nodeValue=f.children);f.dom=a.dom;break;case "<":a.children!==f.children?(n(a),t(b,g(f),d)):(f.dom=a.dom,f.domSize=a.domSize);break;case "[":k(b,a.children,f.children,c,d,q);a=0;c=f.children;f.dom=null;if(null!=c){for(var l=0;l<c.length;l++)b=c[l],null!=b&&null!=b.dom&&(null==f.dom&&(f.dom=b.dom),a+=b.domSize||1);1!==a&&(f.domSize=a)}break;default:b=q;d=f.dom=a.dom;switch(f.tag){case "svg":b="http://www.w3.org/2000/svg";break;case "math":b="http://www.w3.org/1998/Math/MathML"}"textarea"=== (a.dom.nodeValue=e.children);e.dom=a.dom;break;case "<":a.children!==e.children?(m(a),q(c,h(e),d)):(e.dom=a.dom,e.domSize=a.domSize);break;case "[":k(c,a.children,e.children,b,d,p);a=0;b=e.children;e.dom=null;if(null!=b){for(var B=0;B<b.length;B++)c=b[B],null!=c&&null!=c.dom&&(null==e.dom&&(e.dom=c.dom),a+=c.domSize||1);1!==a&&(e.domSize=a)}break;default:c=p;d=e.dom=a.dom;switch(e.tag){case "svg":c="http://www.w3.org/2000/svg";break;case "math":c="http://www.w3.org/1998/Math/MathML"}"textarea"===
f.tag&&(null==f.attrs&&(f.attrs={}),null!=f.text&&(f.attrs.value=f.text));e=a.attrs;q=f.attrs;r=b;if(null!=q)for(l in q)v(f,l,e&&e[l],q[l],r);if(null!=e)for(l in e)null!=q&&l in q||("o"!==l[0]||"n"!==l[1]||w(l)?"key"!==l&&f.dom.removeAttribute(l):C(f,l,void 0));null!=a.text&&null!=f.text&&""!==f.text?a.text.toString()!==f.text.toString()&&(a.dom.firstChild.nodeValue=f.text):(null!=a.text&&(a.children=[u("#",void 0,void 0,a.text,void 0,a.dom.firstChild)]),null!=f.text&&(f.children=[u("#",void 0,void 0, e.tag&&(null==e.attrs&&(e.attrs={}),null!=e.text&&(e.attrs.value=e.text));g=a.attrs;p=e.attrs;v=c;if(null!=p)for(B in p)u(e,B,g&&g[B],p[B],v);if(null!=g)for(B in g)null!=p&&B in p||("o"!==B[0]||"n"!==B[1]||r(B)?"key"!==B&&e.dom.removeAttribute(B):C(e,B,void 0));null!=a.text&&null!=e.text&&""!==e.text?a.text.toString()!==e.text.toString()&&(a.dom.firstChild.nodeValue=e.text):(null!=a.text&&(a.children=[x("#",void 0,void 0,a.text,void 0,a.dom.firstChild)]),null!=e.text&&(e.children=[x("#",void 0,void 0,
f.text,void 0,void 0)]),k(d,a.children,f.children,c,null,b))}else f.instance=u.normalize(f.tag.view.call(f.state,f)),x(f.tag,f,c,e),null!=f.instance?(null==a.instance?t(b,p(f.instance,c,q),d):m(b,a.instance,f.instance,c,d,e,q),f.dom=f.instance.dom,f.domSize=f.instance.domSize):null!=a.instance?(A(b,a.instance,null),f.dom=void 0,f.domSize=0):(f.dom=a.dom,f.domSize=a.domSize)}else A(b,a,null),t(b,p(f,c,void 0),d)}function n(b){var a=b.domSize;if(null!=a||null==b.dom){var f=y.createDocumentFragment(); e.text,void 0,void 0)]),k(d,a.children,e.children,b,null,c))}else e.instance=x.normalize(e.tag.view.call(e.state,e)),y(e.tag,e,b,g),null!=e.instance?(null==a.instance?q(c,n(e.instance,b,p),d):l(c,a.instance,e.instance,b,d,g,p),e.dom=e.instance.dom,e.domSize=e.instance.domSize):null!=a.instance?(A(c,a.instance,null),e.dom=void 0,e.domSize=0):(e.dom=a.dom,e.domSize=a.domSize)}else A(c,a,null),q(c,n(e,b,void 0),d)}function m(c){var a=c.domSize;if(null!=a||null==c.dom){var e=z.createDocumentFragment();
if(0<a){for(b=b.dom;--a;)f.appendChild(b.nextSibling);f.insertBefore(b,f.firstChild)}return f}return b.dom}function l(b,a,f){for(;a<b.length;a++)if(null!=b[a]&&null!=b[a].dom)return b[a].dom;return f}function t(b,a,f){f&&f.parentNode?b.insertBefore(a,f):b.appendChild(a)}function c(b,a,f,c,d){for(;f<c;f++){var e=a[f];null!=e&&(e.skip?e.skip=!1:A(b,e,d))}}function z(b){var a=!1;return function(){a||(a=!0,b())}}function A(b,a,f){function c(){if(++e===d&&(h(a),a.dom)){var q=a.domSize||1;if(1<q)for(var r= if(0<a){for(c=c.dom;--a;)e.appendChild(c.nextSibling);e.insertBefore(c,e.firstChild)}return e}return c.dom}function w(c,a,e){for(;a<c.length;a++)if(null!=c[a]&&null!=c[a].dom)return c[a].dom;return e}function q(c,a,e){e&&e.parentNode?c.insertBefore(a,e):c.appendChild(a)}function d(c,a,e,b,d){for(;e<b;e++){var g=a[e];null!=g&&(g.skip?g.skip=!1:A(c,g,d))}}function g(c){var a=!1;return function(){a||(a=!0,c())}}function A(c,a,e){function b(){if(++f===d&&(t(a),a.dom)){var p=a.domSize||1;if(1<p)for(var g=
a.dom;--q;)b.removeChild(r.nextSibling);null!=a.dom.parentNode&&b.removeChild(a.dom);if(q=null!=f&&null==a.domSize)q=a.attrs,q=!(null!=q&&(q.oncreate||q.onupdate||q.onbeforeremove||q.onremove));q&&"string"===typeof a.tag&&(f.pool?f.pool.push(a):f.pool=[a])}}var d=1,e=0;a.attrs&&a.attrs.onbeforeremove&&(d++,a.attrs.onbeforeremove.call(a.state,a,z(c)));"string"!==typeof a.tag&&a.tag.onbeforeremove&&(d++,a.tag.onbeforeremove.call(a.state,a,z(c)));c()}function h(b){b.attrs&&b.attrs.onremove&&b.attrs.onremove.call(b.state, a.dom;--p;)c.removeChild(g.nextSibling);null!=a.dom.parentNode&&c.removeChild(a.dom);if(p=null!=e&&null==a.domSize)p=a.attrs,p=!(null!=p&&(p.oncreate||p.onupdate||p.onbeforeremove||p.onremove));p&&"string"===typeof a.tag&&(e.pool?e.pool.push(a):e.pool=[a])}}var d=1,f=0;a.attrs&&a.attrs.onbeforeremove&&(d++,a.attrs.onbeforeremove.call(a.state,a,g(b)));"string"!==typeof a.tag&&a.tag.onbeforeremove&&(d++,a.tag.onbeforeremove.call(a.state,a,g(b)));b()}function t(c){c.attrs&&c.attrs.onremove&&c.attrs.onremove.call(c.state,
b);"string"!==typeof b.tag&&b.tag.onremove&&b.tag.onremove.call(b.state,b);if(null!=b.instance)h(b.instance);else if(b=b.children,b instanceof Array)for(var a=0;a<b.length;a++){var f=b[a];null!=f&&h(f)}}function v(b,a,f,c,d){var e=b.dom;if("key"!==a&&(f!==c||"value"===a||"checked"===a||"selectedIndex"===a||"selected"===a&&b.dom===y.activeElement||"object"===typeof c)&&"undefined"!==typeof c&&!w(a)){var q=a.indexOf(":");if(-1<q&&"xlink"===a.substr(0,q))e.setAttributeNS("http://www.w3.org/1999/xlink", c);"string"!==typeof c.tag&&c.tag.onremove&&c.tag.onremove.call(c.state,c);if(null!=c.instance)t(c.instance);else if(c=c.children,c instanceof Array)for(var a=0;a<c.length;a++){var e=c[a];null!=e&&t(e)}}function u(c,a,e,b,d){var g=c.dom;if("key"!==a&&(e!==b||"value"===a||"checked"===a||"selectedIndex"===a||"selected"===a&&c.dom===z.activeElement||"object"===typeof b)&&"undefined"!==typeof b&&!r(a)){var p=a.indexOf(":");if(-1<p&&"xlink"===a.substr(0,p))g.setAttributeNS("http://www.w3.org/1999/xlink",
a.slice(q+1),c);else if("o"===a[0]&&"n"===a[1]&&"function"===typeof c)C(b,a,c);else if("style"===a)if(b=f,b===c&&(e.style.cssText="",b=null),null==c)e.style.cssText="";else if("string"===typeof c)e.style.cssText=c;else{"string"===typeof b&&(e.style.cssText="");for(var r in c)e.style[r]=c[r];if(null!=b&&"string"!==typeof b)for(r in b)r in c||(e.style[r]="")}else if(a in e&&"href"!==a&&"list"!==a&&"form"!==a&&void 0===d){if("input"!==b.tag||"value"!==a||b.dom.value!==c||b.dom!==y.activeElement)e[a]= a.slice(p+1),b);else if("o"===a[0]&&"n"===a[1]&&"function"===typeof b)C(c,a,b);else if("style"===a)if(c=e,c===b&&(g.style.cssText="",c=null),null==b)g.style.cssText="";else if("string"===typeof b)g.style.cssText=b;else{"string"===typeof c&&(g.style.cssText="");for(var v in b)g.style[v]=b[v];if(null!=c&&"string"!==typeof c)for(v in c)v in b||(g.style[v]="")}else if(a in g&&"href"!==a&&"list"!==a&&"form"!==a&&void 0===d){if("input"!==c.tag||"value"!==a||c.dom.value!==b||c.dom!==z.activeElement)g[a]=
c}else"boolean"===typeof c?c?e.setAttribute(a,""):e.removeAttribute(a):e.setAttribute("className"===a?"class":a,c)}}function w(b){return"oninit"===b||"oncreate"===b||"onupdate"===b||"onremove"===b||"onbeforeremove"===b||"onbeforeupdate"===b}function C(b,a,c){var d=b.dom,e=function(a){var b=c.call(d,a);"function"===typeof I&&I.call(d,a);return b};if(a in d)d[a]=e;else{var h=a.slice(2);void 0===b.events&&(b.events={});null!=b.events[a]&&d.removeEventListener(h,b.events[a],!1);"function"===typeof c&& b}else"boolean"===typeof b?b?g.setAttribute(a,""):g.removeAttribute(a):g.setAttribute("className"===a?"class":a,b)}}function r(c){return"oninit"===c||"oncreate"===c||"onupdate"===c||"onremove"===c||"onbeforeremove"===c||"onbeforeupdate"===c}function C(c,a,b){var d=c.dom,g=function(a){var c=b.call(d,a);"function"===typeof I&&I.call(d,a);return c};if(a in d)d[a]=g;else{var f=a.slice(2);void 0===c.events&&(c.events={});null!=c.events[a]&&d.removeEventListener(f,c.events[a],!1);"function"===typeof b&&
(b.events[a]=e,d.addEventListener(h,b.events[a],!1))}}function J(b,a,c){"function"===typeof b.oninit&&b.oninit.call(a.state,a);"function"===typeof b.oncreate&&c.push(b.oncreate.bind(a.state,a))}function x(b,a,c,d){d?J(b,a,c):"function"===typeof b.onupdate&&c.push(b.onupdate.bind(a.state,a))}function K(b,a){Object.keys(a).forEach(function(c){b[c]=a[c]})}var y=d.document,D=y.createDocumentFragment(),I;return{render:function(b,a){var c=[],d=y.activeElement;null==b.vnodes&&(b.textContent="");a instanceof (c.events[a]=g,d.addEventListener(f,c.events[a],!1))}}function D(c,a,b){"function"===typeof c.oninit&&c.oninit.call(a.state,a);"function"===typeof c.oncreate&&b.push(c.oncreate.bind(a.state,a))}function y(c,a,b,d){d?D(c,a,b):"function"===typeof c.onupdate&&b.push(c.onupdate.bind(a.state,a))}function Q(c,a){Object.keys(a).forEach(function(b){c[b]=a[b]})}var z=b.document,J=z.createDocumentFragment(),I;return{render:function(c,a){if(!c)throw Error("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.");
Array||(a=[a]);k(b,b.vnodes,u.normalizeChildren(a),c,null,void 0);b.vnodes=a;for(var e=0;e<c.length;e++)c[e]();y.activeElement!==d&&d.focus()},setEventCallback:function(b){return I=b}}}(window),N=function(d){function e(){function a(){0<arguments.length&&arguments[0]!==E&&g(a,arguments[0],void 0);return a._state.value}p(a,arguments);0<arguments.length&&arguments[0]!==E&&g(a,arguments[0],void 0);return a}function p(a,b){a.constructor=e;a._state={id:H++,value:void 0,error:void 0,state:0,derive:void 0, var b=[],d=z.activeElement;null==c.vnodes&&(c.textContent="");a instanceof Array||(a=[a]);k(c,c.vnodes,x.normalizeChildren(a),b,null,void 0);c.vnodes=a;for(var g=0;g<b.length;g++)b[g]();z.activeElement!==d&&d.focus()},setEventCallback:function(c){return I=c}}}(window),O=function(b){function f(){function a(){0<arguments.length&&arguments[0]!==G&&h(a,arguments[0],void 0);return a._state.value}n(a,arguments);0<arguments.length&&arguments[0]!==G&&h(a,arguments[0],void 0);return a}function n(a,c){a.constructor=
recover:void 0,deps:{},parents:[],errorStream:void 0,endStream:void 0};a.map=D;a.ap=K;a.of=e;a.valueOf=y;a.toJSON=G;a.toString=y;a.run=z;a["catch"]=A;Object.defineProperties(a,{error:{get:function(){if(!a._state.errorStream){var b=function(){0<arguments.length&&arguments[0]!==E&&g(a,void 0,arguments[0]);return a._state.error};p(b,[]);w(b,[a],F,F);a._state.errorStream=b}return a._state.errorStream}},end:{get:function(){if(!a._state.endStream){var b=e();b.map(function(c){!0===c&&(x(a),x(b));return c}); f;a._state={id:H++,value:void 0,error:void 0,state:0,derive:void 0,recover:void 0,deps:{},parents:[],errorStream:void 0,endStream:void 0};a.map=x;a.ap=y;a.of=f;a.valueOf=z;a.toJSON=J;a.toString=z;a.run=g;a["catch"]=A;Object.defineProperties(a,{error:{get:function(){if(!a._state.errorStream){var c=function(){0<arguments.length&&arguments[0]!==G&&h(a,void 0,arguments[0]);return a._state.error};n(c,[]);r(c,[a],F,F);a._state.errorStream=c}return a._state.errorStream}},end:{get:function(){if(!a._state.endStream){var c=
a._state.endStream=b}return a._state.endStream}}})}function g(a,b,c){k(a,b,c);for(var d in a._state.deps)n(a._state.deps[d],!1);a._state.changed=!1;for(var f in a._state.deps)a._state.deps[f]._state.changed=!1}function k(a,b,c){c=t(b,c);if(void 0!==c&&"function"===typeof a._state.recover){if(!l(a,m,!0))return}else m(a,b,c);a._state.changed=!0;2!==a._state.state&&(a._state.state=1)}function m(a,b,c){a._state.value=b;a._state.error=c}function n(a,c){var d=a._state.parents;0<d.length&&d.filter(I).length=== f();c.map(function(b){!0===b&&(D(a),D(c));return b});a._state.endStream=c}return a._state.endStream}}})}function h(a,c,b){k(a,c,b);for(var d in a._state.deps)m(a._state.deps[d],!1);a._state.changed=!1;for(var e in a._state.deps)a._state.deps[e]._state.changed=!1}function k(a,c,b){b=q(c,b);if(void 0!==b&&"function"===typeof a._state.recover){if(!w(a,l,!0))return}else l(a,c,b);a._state.changed=!0;2!==a._state.state&&(a._state.state=1)}function l(a,c,b){a._state.value=c;a._state.error=b}function m(a,
d.length&&(c||0<d.filter(b).length)&&(d=d.filter(f),0<d.length?k(a,void 0,d[0]._state.error):l(a,k,!1))}function l(a,b,d){try{var f=d?a._state.recover():a._state.derive();if(f===E)return!1;b(a,f,void 0)}catch(e){b(a,void 0,null!=e.__error?e.__error:e),null==e.__error&&c(a,e)}return!0}function t(a,b){null!=a&&a.constructor===e&&(b=void 0!==a._state.error?a._state.error:t(a._state.value,a._state.error));return b}function c(a,b){0===Object.keys(a._state.deps).length&&setTimeout(function(){0===Object.keys(a._state.deps).length&& b){var d=a._state.parents;0<d.length&&d.filter(I).length===d.length&&(b||0<d.filter(c).length)&&(d=d.filter(e),0<d.length?k(a,void 0,d[0]._state.error):w(a,k,!1))}function w(a,c,b){try{var e=b?a._state.recover():a._state.derive();if(e===G)return!1;c(a,e,void 0)}catch(g){c(a,void 0,null!=g.__error?g.__error:g),null==g.__error&&d(a,g)}return!0}function q(a,c){null!=a&&a.constructor===f&&(c=void 0!==a._state.error?a._state.error:q(a._state.value,a._state.error));return c}function d(a,c){0===Object.keys(a._state.deps).length&&
d(b)},0)}function z(a){var b=e(),c=this;return w(b,[c],function(){return v(b,a(c()))},void 0)}function A(a){var b=e(),c=this;return w(b,[c],function(){return c._state.value},function(){return v(b,a(c._state.error))})}function h(a,c){return w(e(),c,function(){var d=c.filter(f);if(0<d.length)throw{__error:d[0]._state.error};return a.apply(this,c.concat([c.filter(b)]))},void 0)}function v(a,b){if(null!=b&&b.constructor===e){var c=b,d=function(){k(a,c._state.value,c._state.error);for(var b in a._state.deps)n(a._state.deps[b], setTimeout(function(){0===Object.keys(a._state.deps).length&&b(c)},0)}function g(a){var c=f(),b=this;return r(c,[b],function(){return u(c,a(b()))},void 0)}function A(a){var c=f(),b=this;return r(c,[b],function(){return b._state.value},function(){return u(c,a(b._state.error))})}function t(a,b){return r(f(),b,function(){var d=b.filter(e);if(0<d.length)throw{__error:d[0]._state.error};return a.apply(this,b.concat([b.filter(c)]))},void 0)}function u(a,c){if(null!=c&&c.constructor===f){var b=c,d=function(){k(a,
!1)};c.map(d)["catch"](function(a){d();throw{__error:a};});if(0===c._state.state)return E;if(c._state.error)throw{__error:c._state.error};b=c._state.value}return b}function w(b,c,d,f){var e=b._state;e.derive=d;e.recover=f;e.parents=c.filter(a);u(b,e.parents);n(b,!0);return b}function u(a,b){for(var c=0;c<b.length;c++)b[c]._state.deps[a._state.id]=a,u(a,b[c]._state.parents)}function x(a){for(var b=0;b<a._state.parents.length;b++)delete a._state.parents[b]._state.deps[a._state.id];for(var c in a._state.deps){var b= b._state.value,b._state.error);for(var c in a._state.deps)m(a._state.deps[c],!1)};b.map(d)["catch"](function(a){d();throw{__error:a};});if(0===b._state.state)return G;if(b._state.error)throw{__error:b._state.error};c=b._state.value}return c}function r(c,b,d,e){var g=c._state;g.derive=d;g.recover=e;g.parents=b.filter(a);C(c,g.parents);m(c,!0);return c}function C(a,c){for(var b=0;b<c.length;b++)c[b]._state.deps[a._state.id]=a,C(a,c[b]._state.parents)}function D(a){for(var c=0;c<a._state.parents.length;c++)delete a._state.parents[c]._state.deps[a._state.id];
a._state.deps[c],d=b._state.parents.indexOf(a);-1<d&&b._state.parents.splice(d,1)}a._state.state=2;a._state.deps={}}function D(a){return h(function(b){return a(b())},[this])}function K(a){return h(function(a,b){return a()(b())},[this,a])}function y(){return this._state.value}function G(){return null!=this._state.value&&"function"===typeof this._state.value.toJSON?this._state.value.toJSON():this._state.value}function I(a){return 1===a._state.state}function b(a){return a._state.changed}function a(a){return 2!== for(var b in a._state.deps){var c=a._state.deps[b],d=c._state.parents.indexOf(a);-1<d&&c._state.parents.splice(d,1)}a._state.state=2;a._state.deps={}}function x(a){return t(function(c){return a(c())},[this])}function y(a){return t(function(a,c){return a()(c())},[this,a])}function z(){return this._state.value}function J(){return null!=this._state.value&&"function"===typeof this._state.value.toJSON?this._state.value.toJSON():this._state.value}function I(a){return 1===a._state.state}function c(a){return a._state.changed}
a._state.state}function f(a){return a._state.error}var H=0,F=function(){},E={};e.merge=function(a){return h(function(){return a.map(function(a){return a()})},a)};e.combine=h;e.reject=function(a){var b=e();b.error(a);return b};e.HALT=E;return e}(console.log.bind(console)),D=function(d){function e(d,g){if(g instanceof Array)for(var n=0;n<g.length;n++)e(d+"["+n+"]",g[n]);else if("[object Object]"===Object.prototype.toString.call(g))for(n in g)e(d+"["+n+"]",g[n]);else p.push(encodeURIComponent(d)+(null!= function a(a){return 2!==a._state.state}function e(a){return a._state.error}var H=0,F=function(){},G={};f.merge=function(a){return t(function(){return a.map(function(a){return a()})},a)};f.combine=t;f.reject=function(a){var c=f();c.error(a);return c};f.HALT=G;return f}(console.log.bind(console)),H=function(b){function f(b,h){if(h instanceof Array)for(var m=0;m<h.length;m++)f(b+"["+m+"]",h[m]);else if("[object Object]"===Object.prototype.toString.call(h))for(m in h)f(b+"["+m+"]",h[m]);else n.push(encodeURIComponent(b)+
g&&""!==g?"="+encodeURIComponent(g):""))}if("[object Object]"!==Object.prototype.toString.call(d))return"";var p=[],g;for(g in d)e(g,d[g]);return p.join("&")},L=function(d,e){function p(c,d){if(null==d)return c;for(var e=c.match(/:[^\/]+/gi)||[],h=0;h<e.length;h++){var g=e[h].slice(1);null!=d[g]&&(c=c.replace(e[h],d[g]),delete d[g])}return c}function g(c,d){var e=D(d);if(""!==e){var h=0>c.indexOf("?")?"?":"&";c+=h+e}return c}function k(c){try{return""!==c?JSON.parse(c):null}catch(d){throw Error(c); (null!=h&&""!==h?"="+encodeURIComponent(h):""))}if("[object Object]"!==Object.prototype.toString.call(b))return"";var n=[],h;for(h in b)f(h,b[h]);return n.join("&")},K=function(b,f){function n(b,g){if(null==g)return b;for(var f=b.match(/:[^\/]+/gi)||[],h=0;h<f.length;h++){var k=f[h].slice(1);null!=g[k]&&(b=b.replace(f[h],g[k]),delete g[k])}return b}function h(b,g){var f=H(g);if(""!==f){var h=0>b.indexOf("?")?"?":"&";b+=h+f}return b}function k(b){try{return""!==b?JSON.parse(b):null}catch(g){throw Error(b);
}}function m(c){return c.responseText}function n(c,d){if("function"===typeof c)if(d instanceof Array)for(var e=0;e<d.length;e++)d[e]=new c(d[e]);else return new c(d);return d}var l=0,t;return{request:function(c){var l=e();void 0!==c.initialValue&&l(c.initialValue);var A="boolean"===typeof c.useBody?c.useBody:"GET"!==c.method&&"TRACE"!==c.method;"function"!==typeof c.serialize&&(c.serialize="undefined"!==typeof FormData&&c.data instanceof FormData?function(c){return c}:JSON.stringify);"function"!== }}function l(b){return b.responseText}function m(b,g){if("function"===typeof b)if(g instanceof Array)for(var f=0;f<g.length;f++)g[f]=new b(g[f]);else return new b(g);return g}var w=0,q;return{request:function(d){var g=f();void 0!==d.initialValue&&g(d.initialValue);var A="boolean"===typeof d.useBody?d.useBody:"GET"!==d.method&&"TRACE"!==d.method;"function"!==typeof d.serialize&&(d.serialize="undefined"!==typeof FormData&&d.data instanceof FormData?function(b){return b}:JSON.stringify);"function"!==
typeof c.deserialize&&(c.deserialize=k);"function"!==typeof c.extract&&(c.extract=m);c.url=p(c.url,c.data);A?c.data=c.serialize(c.data):c.url=g(c.url,c.data);var h=new d.XMLHttpRequest;h.open(c.method,c.url,"boolean"===typeof c.async?c.async:!0,"string"===typeof c.user?c.user:void 0,"string"===typeof c.password?c.password:void 0);c.serialize===JSON.stringify&&A&&h.setRequestHeader("Content-Type","application/json; charset=utf-8");c.deserialize===k&&h.setRequestHeader("Accept","application/json, text/*"); typeof d.deserialize&&(d.deserialize=k);"function"!==typeof d.extract&&(d.extract=l);d.url=n(d.url,d.data);A?d.data=d.serialize(d.data):d.url=h(d.url,d.data);var t=new b.XMLHttpRequest;t.open(d.method,d.url,"boolean"===typeof d.async?d.async:!0,"string"===typeof d.user?d.user:void 0,"string"===typeof d.password?d.password:void 0);d.serialize===JSON.stringify&&A&&t.setRequestHeader("Content-Type","application/json; charset=utf-8");d.deserialize===k&&t.setRequestHeader("Accept","application/json, text/*");
"function"===typeof c.config&&(h=c.config(h,c)||h);h.onreadystatechange=function(){if(4===h.readyState){try{var d=c.extract!==m?c.extract(h,c):c.deserialize(c.extract(h,c));if(200<=h.status&&300>h.status)l(n(c.type,d));else{var e=Error(h.responseText),g;for(g in d)e[g]=d[g];l.error(e)}}catch(k){l.error(k)}"function"===typeof t&&t()}};A?h.send(c.data):h.send();return l},jsonp:function(c){var k=e();void 0!==c.initialValue&&k(c.initialValue);var m=c.callbackName||"_mithril_"+Math.round(1E16*Math.random())+ "function"===typeof d.config&&(t=d.config(t,d)||t);t.onreadystatechange=function(){if(4===t.readyState){try{var b=d.extract!==l?d.extract(t,d):d.deserialize(d.extract(t,d));if(200<=t.status&&300>t.status)g(m(d.type,b));else{var f=Error(t.responseText),h;for(h in b)f[h]=b[h];g.error(f)}}catch(k){g.error(k)}"function"===typeof q&&q()}};A?t.send(d.data):t.send();return g},jsonp:function(d){var g=f();void 0!==d.initialValue&&g(d.initialValue);var k=d.callbackName||"_mithril_"+Math.round(1E16*Math.random())+
"_"+l++,h=d.document.createElement("script");d[m]=function(e){h.parentNode.removeChild(h);k(n(c.type,e));"function"===typeof t&&t();delete d[m]};h.onerror=function(){h.parentNode.removeChild(h);k.error(Error("JSONP request failed"));"function"===typeof t&&t();delete d[m]};null==c.data&&(c.data={});c.url=p(c.url,c.data);c.data[c.callbackKey||"callback"]=m;h.src=g(c.url,c.data);d.document.documentElement.appendChild(h);return k},setCompletionCallback:function(c){t=c}}}(window,N),G=function(){var d= "_"+w++,t=b.document.createElement("script");b[k]=function(f){t.parentNode.removeChild(t);g(m(d.type,f));"function"===typeof q&&q();delete b[k]};t.onerror=function(){t.parentNode.removeChild(t);g.error(Error("JSONP request failed"));"function"===typeof q&&q();delete b[k]};null==d.data&&(d.data={});d.url=n(d.url,d.data);d.data[d.callbackKey||"callback"]=k;t.src=h(d.url,d.data);b.document.documentElement.appendChild(t);return g},setCompletionCallback:function(b){q=b}}}(window,O),L=function(){var b=
[];return{subscribe:d.push.bind(d),unsubscribe:function(e){e=d.indexOf(e);-1<e&&d.splice(e,1)},publish:function(){for(var e=0;e<d.length;e++)d[e].apply(this,arguments)}}}(),M=function(d){if(""===d||null==d)return{};"?"===d.charAt(0)&&(d=d.slice(1));d=d.split("&");for(var e={},p={},g=0;g<d.length;g++){var k=d[g].split("="),m=decodeURIComponent(k[0]),k=2===k.length?decodeURIComponent(k[1]):"",n=Number(k);""!==k&&!isNaN(n)||"NaN"===k?k=n:"true"===k?k=!0:"false"===k?k=!1:(n=new Date(k),isNaN(n.getTime())|| [];return{subscribe:b.push.bind(b),unsubscribe:function(f){f=b.indexOf(f);-1<f&&b.splice(f,1)},publish:function(){for(var f=0;f<b.length;f++)b[f].apply(this,arguments)}}}(),M=function(b){if(""===b||null==b)return{};"?"===b.charAt(0)&&(b=b.slice(1));b=b.split("&");for(var f={},n={},h=0;h<b.length;h++){var k=b[h].split("="),l=decodeURIComponent(k[0]),k=2===k.length?decodeURIComponent(k[1]):"",m=Number(k);""!==k&&!isNaN(m)||"NaN"===k?k=m:"true"===k?k=!0:"false"===k?k=!1:(m=new Date(k),isNaN(m.getTime())||
(k=n));var n=m.split(/\]\[?|\[/),l=e;-1<m.indexOf("[")&&n.pop();for(var t=0;t<n.length;t++){var m=n[t],c=n[t+1],c=""==c||!isNaN(parseInt(c,10)),z=t===n.length-1;""===m&&(m=n.slice(0,t).join(),null==p[m]&&(p[m]=0),m=p[m]++);null==l[m]&&(l[m]=z?k:c?[]:{});l=l[m]}}return e};L.setCompletionCallback(G.publish);var Q=function(d){function e(e){var c=d.location[e].replace(/(?:%[a-f89][a-f0-9])+/gim,decodeURIComponent);"pathname"===e&&"/"!==c[0]&&(c="/"+c);return c}function p(d,c,e){var g=d.indexOf("?"),h= (k=m));var m=l.split(/\]\[?|\[/),w=f;-1<l.indexOf("[")&&m.pop();for(var q=0;q<m.length;q++){var l=m[q],d=m[q+1],d=""==d||!isNaN(parseInt(d,10)),g=q===m.length-1;""===l&&(l=m.slice(0,q).join(),null==n[l]&&(n[l]=0),l=n[l]++);null==w[l]&&(w[l]=g?k:d?[]:{});w=w[l]}}return f};K.setCompletionCallback(L.publish);var R=function(b){var f=0,n=null,h="function"===typeof requestAnimationFrame?requestAnimationFrame:setTimeout;return function(k){var l=Date.now();!0===k||0===f||16<=l-f?(f=l,b()):null===n&&(n=h(function(){n=
d.indexOf("#"),k=-1<g?g:-1<h?h:d.length;if(-1<g){var g=M(d.slice(g+1,-1<h?h:d.length)),l;for(l in g)c[l]=g[l]}if(-1<h)for(l in c=M(d.slice(h+1)),c)e[l]=c[l];return d.slice(0,k)}function g(){switch(l.charAt(0)){case "#":return e("hash").slice(l.length);case "?":return e("search").slice(l.length)+e("hash");default:return e("pathname").slice(l.length)+e("search")+e("hash")}}function k(e,c,g){var k={},h={};e=p(e,k,h);if(null!=c){for(var n in c)k[n]=c[n];e=e.replace(/:([^\/]+)/g,function(d,e){delete k[e]; null;b();f=Date.now()},16-(l-f)))}},S=function(b,f,n,h){h=R(h);null!=f&&f.setEventCallback(function(b){!1!==b.redraw&&n.publish()});null!=n&&(b.redraw&&n.unsubscribe(b.redraw),n.subscribe(h));return b.redraw=h};y.mount=function(b,f){return function(n,h){null===h?(b.render(n,[]),f.unsubscribe(n.redraw),delete n.redraw):S(n,b,f,function(){b.render(n,x(h,void 0,void 0,void 0,void 0,void 0))})()}}(N,L);var T=function(b){function f(g){var d=b.location[g].replace(/(?:%[a-f89][a-f0-9])+/gim,decodeURIComponent);
return c[e]})}(n=D(k))&&(e+="?"+n);(h=D(h))&&(e+="#"+h);m?(g&&g.replace?d.history.replaceState(null,null,l+e):d.history.pushState(null,null,l+e),d.onpopstate()):d.location.href=l+e}var m="function"===typeof d.history.pushState,n="function"===typeof setImmediate?setImmediate:setTimeout,l="#!";return{setPrefix:function(d){l=d},getPath:g,setPath:k,defineRoutes:function(e,c,k){function u(){var d=g(),l={},m=p(d,l,l);n(function(){for(var g in e){var n=new RegExp("^"+g.replace(/:[^\/]+?\.{3}/g,"(.*?)").replace(/:[^\/]+/g, "pathname"===g&&"/"!==d[0]&&(d="/"+d);return d}function n(b){return function(){null==d&&(d=w(function(){d=null;b()}))}}function h(b,d,f){var h=b.indexOf("?"),k=b.indexOf("#"),m=-1<h?h:-1<k?k:b.length;if(-1<h){var h=M(b.slice(h+1,-1<k?k:b.length)),l;for(l in h)d[l]=h[l]}if(-1<k)for(l in d=M(b.slice(k+1)),d)f[l]=d[l];return b.slice(0,m)}function k(){switch(q.charAt(0)){case "#":return f("hash").slice(q.length);case "?":return f("search").slice(q.length)+f("hash");default:return f("pathname").slice(q.length)+
"([^\\/]+)")+"/?$");if(n.test(m)){m.replace(n,function(){for(var k=g.match(/:[^\/]+/g)||[],n=[].slice.call(arguments,1,-2),m=0;m<k.length;m++)l[k[m].replace(/:|\./g,"")]=decodeURIComponent(n[m]);c(e[g],l,d,g)});return}}k(d,l)})}m?d.onpopstate=u:"#"===l.charAt(0)&&(d.onhashchange=u);u();return u},link:function(d){d.dom.setAttribute("href",l+d.attrs.href);d.dom.onclick=function(c){c.preventDefault();c.redraw=!1;k(d.attrs.href,void 0,void 0)}}}},R=function(d){var e=0,p=null,g="function"===typeof requestAnimationFrame? f("search")+f("hash")}}function l(d,f,k){var l={},r={};d=h(d,l,r);if(null!=f){for(var n in f)l[n]=f[n];d=d.replace(/:([^\/]+)/g,function(b,d){delete l[d];return f[d]})}(n=H(l))&&(d+="?"+n);(r=H(r))&&(d+="#"+r);m?(k&&k.replace?b.history.replaceState(null,null,q+d):b.history.pushState(null,null,q+d),b.onpopstate()):b.location.href=q+d}var m="function"===typeof b.history.pushState,w="function"===typeof setImmediate?setImmediate:setTimeout,q="#!",d;return{setPrefix:function(b){q=b},getPath:k,setPath:l,
requestAnimationFrame:setTimeout;return function(k){var m=Date.now();!0===k||0===e||16<=m-e?(e=m,d()):null===p&&(p=g(function(){p=null;d();e=Date.now()},16-(m-e)))}},O=function(d,e,p,g){g=R(g);null!=e&&e.setEventCallback(function(d){!1!==d.redraw&&p.publish()});null!=p&&(d.redraw&&p.unsubscribe(d.redraw),p.subscribe(g));return d.redraw=g};x.route=function(d,e,p){var g=Q(d);d=function(d,m,n){var l=null,t="div",c=null;n=g.defineRoutes(n,function(g,m,h,n){function p(n){x===c&&(x=null,l=h,t=n,e.render(d, defineRoutes:function(d,f,l){function u(){var b=k(),m={},n=h(b,m,m),q;for(q in d){var u=new RegExp("^"+q.replace(/:[^\/]+?\.{3}/g,"(.*?)").replace(/:[^\/]+/g,"([^\\/]+)")+"/?$");if(u.test(n)){n.replace(u,function(){for(var h=q.match(/:[^\/]+/g)||[],k=[].slice.call(arguments,1,-2),l=0;l<h.length;l++)m[h[l].replace(/:|\./g,"")]=decodeURIComponent(k[l]);f(d[q],m,b,q)});return}}l(b,m)}m?b.onpopstate=n(u):"#"===q.charAt(0)&&(b.onhashchange=u);u();return u},link:function(b){b.dom.setAttribute("href",q+
g.render(u(n,null,m,void 0,void 0,void 0))))}var x=c={};if("function"!==typeof g.view)if("function"!==typeof g.render&&(g.render=function(c){return c}),"function"!==typeof g.onmatch&&(g.onmatch=function(){p(t)}),h!==l)g.onmatch(u(g,null,m,void 0,void 0,void 0),p);else p(t);else e.render(d,u(g,null,m,void 0,void 0,void 0))},function(){g.setPath(m,null,{replace:!0})});O(d,e,p,n)};d.link=g.link;d.prefix=g.setPrefix;d.set=g.setPath;d.get=g.getPath;return d}(window,F,G);x.mount=function(d,e){return function(p, b.attrs.href);b.dom.onclick=function(d){d.preventDefault();d.redraw=!1;l(b.attrs.href,void 0,void 0)}}}};y.route=function(b,f){function n(b){return b}var h=T(b),k,l,m,w,q,d={view:function(){return m(x(l,null,w,void 0,void 0,void 0))}},g=function(b,g,u){l="div";m=n;w=null;f(b,d);h.defineRoutes(u,function(d,f,g){function h(c){t===k&&(k=null,l=null!=c?c:u?"div":d,m=x,w=f,q=g,b.redraw(!0))}var t=k={},u="function"!==typeof d.view,x=n,y=function(){h()};u&&("function"===typeof d.render&&(x=d.render.bind(d)),
g){null===g?(d.render(p,[]),e.unsubscribe(p.redraw),delete p.redraw):O(p,d,e,function(){d.render(p,u(g,void 0,void 0,void 0,void 0,void 0))})()}}(F,G);x.withAttr=function(d,e,p){return function(g){return e.call(p||this,d in g.currentTarget?g.currentTarget[d]:g.currentTarget.getAttribute(d))}};x.prop=N;x.render=F.render;x.redraw=G.publish;x.request=L.request;x.jsonp=L.jsonp;x.parseQueryString=M;x.buildQueryString=D;x.version="1.0.0";"undefined"!==typeof module?module.exports=x:window.m=x}; "function"===typeof d.onmatch&&(y=d.onmatch));y.call(d,h,f,g)},function(){h.setPath(g,null,{replace:!0})})};g.link=h.link;g.prefix=h.setPrefix;g.set=h.setPath;g.get=function(){return q};return g}(window,y.mount);y.withAttr=function(b,f,n){return function(h){return f.call(n||this,b in h.currentTarget?h.currentTarget[b]:h.currentTarget.getAttribute(b))}};y.prop=O;y.render=N.render;y.redraw=L.publish;y.request=K.request;y.jsonp=K.jsonp;y.parseQueryString=M;y.buildQueryString=H;y.version="1.0.0";"undefined"!==
typeof module?module.exports=y:window.m=y};

View file

@ -27,6 +27,11 @@ module.exports = new function init() {
if (fn) return fn.apply(this, arguments) if (fn) return fn.apply(this, arguments)
} }
if (fn)
Object.defineProperties(spy, {
length: {value: fn.length},
name: {value: fn.name}
})
spy.args = [] spy.args = []
spy.callCount = 0 spy.callCount = 0
return spy return spy

View file

@ -60,6 +60,8 @@ o.spec("ospec", function() {
var output = spy.call(state, {children: children}) var output = spy.call(state, {children: children})
o(spy.length).equals(1)
o(spy.name).equals("view")
o(spy.callCount).equals(1) o(spy.callCount).equals(1)
o(spy.args.length).equals(1) o(spy.args.length).equals(1)
o(spy.args[0]).deepEquals({children: children}) o(spy.args[0]).deepEquals({children: children})

View file

@ -516,6 +516,7 @@ module.exports = function($window) {
} }
function render(dom, vnodes) { function render(dom, vnodes) {
if (!dom) throw new Error("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.")
var hooks = [] var hooks = []
var active = $doc.activeElement var active = $doc.activeElement

View file

@ -21,4 +21,13 @@ o.spec("render", function() {
o(root.childNodes.length).equals(0) o(root.childNodes.length).equals(0)
}) })
o("throws on invalid root node", function(){
var threw = false
try {
render(null, [])
} catch (e) {
threw = true
}
o(threw).equals(true)
})
}) })

View file

@ -1,4 +1,3 @@
var renderService = require("./render") var mount = require("./mount")
var redrawService = require("./redraw")
module.exports = require("./api/router")(window, renderService, redrawService) module.exports = require("./api/router")(window, mount)

View file

@ -16,6 +16,18 @@ module.exports = function($window) {
return data return data
} }
var asyncId
function debounceAsync(f) {
return function() {
if (asyncId != null) return
asyncId = callAsync(function() {
asyncId = null
f()
})
}
}
function parsePath(path, queryData, hashData) { function parsePath(path, queryData, hashData) {
var queryIndex = path.indexOf("?") var queryIndex = path.indexOf("?")
var hashIndex = path.indexOf("#") var hashIndex = path.indexOf("#")
@ -67,7 +79,7 @@ module.exports = function($window) {
} }
function defineRoutes(routes, resolve, reject) { function defineRoutes(routes, resolve, reject) {
if (supportsPushState) $window.onpopstate = resolveRoute if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute)
else if (prefix.charAt(0) === "#") $window.onhashchange = resolveRoute else if (prefix.charAt(0) === "#") $window.onhashchange = resolveRoute
resolveRoute() resolveRoute()
@ -76,25 +88,23 @@ module.exports = function($window) {
var params = {} var params = {}
var pathname = parsePath(path, params, params) var pathname = parsePath(path, params, params)
callAsync(function() { for (var route in routes) {
for (var route in routes) { var matcher = new RegExp("^" + route.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$")
var matcher = new RegExp("^" + route.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$")
if (matcher.test(pathname)) { if (matcher.test(pathname)) {
pathname.replace(matcher, function() { pathname.replace(matcher, function() {
var keys = route.match(/:[^\/]+/g) || [] var keys = route.match(/:[^\/]+/g) || []
var values = [].slice.call(arguments, 1, -2) var values = [].slice.call(arguments, 1, -2)
for (var i = 0; i < keys.length; i++) { for (var i = 0; i < keys.length; i++) {
params[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i]) params[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i])
} }
resolve(routes[route], params, path, route) resolve(routes[route], params, path, route)
}) })
return return
}
} }
}
reject(path, params) reject(path, params)
})
} }
return resolveRoute return resolveRoute
} }

View file

@ -285,18 +285,14 @@ o.spec("Router.defineRoutes", function() {
}) })
}) })
o("replays", function(done) { o("replays", function() {
$window.location.href = prefix + "/test" $window.location.href = prefix + "/test"
var replay = router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) var replay = router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
replay() replay()
callAsync(function() { o(onRouteChange.callCount).equals(2)
o(onRouteChange.callCount).equals(2) o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"])
o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"]) o(onFail.callCount).equals(0)
o(onFail.callCount).equals(0)
done()
})
}) })
}) })
}) })

View file

@ -164,9 +164,11 @@ o.spec("api", function() {
setTimeout(function() { setTimeout(function() {
m.route.set("/b") m.route.set("/b")
o(m.route.get()).equals("/b") setTimeout(function() {
o(m.route.get()).equals("/b")
done() done()
}, FRAME_BUDGET)
}, FRAME_BUDGET) }, FRAME_BUDGET)
}) })
}) })