Merge branch 'rewrite' into array-isArray
This commit is contained in:
commit
aa72f87408
27 changed files with 1172 additions and 529 deletions
20
.npmignore
Normal file
20
.npmignore
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Configuration files
|
||||||
|
.deploy.env
|
||||||
|
.editorconfig
|
||||||
|
.eslintrc.js
|
||||||
|
.gitattributes
|
||||||
|
.gitignore
|
||||||
|
.travis.yml
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
test-utils/
|
||||||
|
tests/
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
docs/
|
||||||
|
examples/
|
||||||
|
CONTRIBUTING.md
|
||||||
|
|
||||||
|
# Browser stub (use index.js w/ a bundler or mithril.js w/o one instead)
|
||||||
|
module/
|
||||||
|
browser.js
|
||||||
|
|
@ -34,6 +34,6 @@ There are over 4000 assertions in the test suite, and tests cover even difficult
|
||||||
|
|
||||||
## Modularity
|
## Modularity
|
||||||
|
|
||||||
Despite the huge improvements in performance and modularity, the new codebase is smaller than v0.2.x, currently clocking at <!-- size -->7.43 KB<!-- /size --> min+gzip
|
Despite the huge improvements in performance and modularity, the new codebase is smaller than v0.2.x, currently clocking at <!-- size -->7.47 KB<!-- /size --> min+gzip
|
||||||
|
|
||||||
In addition, Mithril is now completely modular: you can import only the modules that you need and easily integrate 3rd party modules if you wish to use a different library for routing, ajax, and even rendering
|
In addition, Mithril is now completely modular: you can import only the modules that you need and easily integrate 3rd party modules if you wish to use a different library for routing, ajax, and even rendering
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,59 @@
|
||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
var Vnode = require("../render/vnode")
|
var Vnode = require("../render/vnode")
|
||||||
|
var Promise = require("../promise/promise")
|
||||||
var coreRouter = require("../router/router")
|
var coreRouter = require("../router/router")
|
||||||
|
|
||||||
module.exports = function($window, redrawService) {
|
module.exports = function($window, redrawService) {
|
||||||
var routeService = coreRouter($window)
|
var routeService = coreRouter($window)
|
||||||
|
|
||||||
var identity = function(v) {return v}
|
var identity = function(v) {return v}
|
||||||
var resolver, component, attrs, currentPath, resolve
|
var render, component, attrs, currentPath, updatePending = false
|
||||||
var route = function(root, defaultRoute, routes) {
|
var route = function(root, defaultRoute, routes) {
|
||||||
if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined")
|
if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined")
|
||||||
var update = function(routeResolver, comp, params, path) {
|
var update = function(routeResolver, comp, params, path) {
|
||||||
resolver = routeResolver, component = comp, attrs = params, currentPath = path, resolve = null
|
component = comp != null && typeof comp.view === "function" ? comp : "div", attrs = params, currentPath = path, updatePending = false
|
||||||
resolver.render = routeResolver.render || identity
|
render = (routeResolver.render || identity).bind(routeResolver)
|
||||||
render()
|
run()
|
||||||
}
|
}
|
||||||
var render = function() {
|
var run = function() {
|
||||||
if (resolver != null) redrawService.render(root, resolver.render(Vnode(component, attrs.key, attrs)))
|
if (render != null) redrawService.render(root, render(Vnode(component, attrs.key, attrs)))
|
||||||
|
}
|
||||||
|
var bail = function() {
|
||||||
|
routeService.setPath(defaultRoute)
|
||||||
}
|
}
|
||||||
routeService.defineRoutes(routes, function(payload, params, path) {
|
routeService.defineRoutes(routes, function(payload, params, path) {
|
||||||
if (payload.view) update({}, payload, params, path)
|
if (payload.view) update({}, payload, params, path)
|
||||||
else {
|
else {
|
||||||
if (payload.onmatch) {
|
if (payload.onmatch) {
|
||||||
if (resolve != null) update(payload, component, params, path)
|
updatePending = true
|
||||||
else {
|
Promise.resolve(payload.onmatch(params, path)).then(function(resolved) {
|
||||||
resolve = function(resolved) {
|
if (updatePending) update(payload, resolved, params, path)
|
||||||
update(payload, resolved, params, path)
|
}, bail)
|
||||||
}
|
|
||||||
payload.onmatch(function(resolved) {
|
|
||||||
if (resolve != null) resolve(resolved)
|
|
||||||
}, params, path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else update(payload, "div", params, path)
|
else update(payload, "div", params, path)
|
||||||
}
|
}
|
||||||
}, function() {
|
}, bail)
|
||||||
routeService.setPath(defaultRoute)
|
redrawService.subscribe(root, run)
|
||||||
})
|
}
|
||||||
redrawService.subscribe(root, render)
|
route.set = function(path, data, options) {
|
||||||
|
if (updatePending) options = {replace: true}
|
||||||
|
updatePending = false
|
||||||
|
routeService.setPath(path, data, options)
|
||||||
}
|
}
|
||||||
route.set = routeService.setPath
|
|
||||||
route.get = function() {return currentPath}
|
route.get = function() {return currentPath}
|
||||||
route.prefix = routeService.setPrefix
|
route.prefix = function(prefix) {routeService.prefix = prefix}
|
||||||
route.link = routeService.link
|
route.link = function(vnode) {
|
||||||
|
vnode.dom.setAttribute("href", routeService.prefix + vnode.attrs.href)
|
||||||
|
vnode.dom.onclick = function(e) {
|
||||||
|
if (e.ctrlKey || e.metaKey || e.shiftKey || e.which === 2) return
|
||||||
|
e.preventDefault()
|
||||||
|
e.redraw = false
|
||||||
|
var href = this.getAttribute("href")
|
||||||
|
if (href.indexOf(routeService.prefix) === 0) href = href.slice(routeService.prefix.length)
|
||||||
|
route.set(href, undefined, undefined)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return route
|
return route
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@ var callAsync = require("../../test-utils/callAsync")
|
||||||
var browserMock = require("../../test-utils/browserMock")
|
var browserMock = require("../../test-utils/browserMock")
|
||||||
|
|
||||||
var m = require("../../render/hyperscript")
|
var m = require("../../render/hyperscript")
|
||||||
|
var callAsync = require("../../test-utils/callAsync")
|
||||||
var coreRenderer = require("../../render/render")
|
var coreRenderer = require("../../render/render")
|
||||||
var apiRedraw = require("../../api/redraw")
|
var apiRedraw = require("../../api/redraw")
|
||||||
var apiRouter = require("../../api/router")
|
var apiRouter = require("../../api/router")
|
||||||
|
var Promise = require("../../promise/promise")
|
||||||
|
|
||||||
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) {
|
||||||
|
|
@ -73,7 +75,7 @@ o.spec("route", function() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
setTimeout(function() {
|
callAsync(function() {
|
||||||
o(root.firstChild.nodeName).equals("DIV")
|
o(root.firstChild.nodeName).equals("DIV")
|
||||||
|
|
||||||
$window.history.back()
|
$window.history.back()
|
||||||
|
|
@ -81,7 +83,7 @@ o.spec("route", function() {
|
||||||
o($window.location.pathname).equals("/")
|
o($window.location.pathname).equals("/")
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}, FRAME_BUDGET)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("default route does not inherit params", function(done) {
|
o("default route does not inherit params", function(done) {
|
||||||
|
|
@ -156,11 +158,11 @@ o.spec("route", function() {
|
||||||
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() {
|
callAsync(function() {
|
||||||
o(onupdate.callCount).equals(1)
|
o(onupdate.callCount).equals(1)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}, FRAME_BUDGET * 2)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("event handlers can skip redraw", function(done) {
|
o("event handlers can skip redraw", function(done) {
|
||||||
|
|
@ -191,11 +193,11 @@ o.spec("route", function() {
|
||||||
o(oninit.callCount).equals(1)
|
o(oninit.callCount).equals(1)
|
||||||
|
|
||||||
// Wrapped to ensure no redraw fired
|
// Wrapped to ensure no redraw fired
|
||||||
setTimeout(function() {
|
callAsync(function() {
|
||||||
o(onupdate.callCount).equals(0)
|
o(onupdate.callCount).equals(0)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}, FRAME_BUDGET)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("changes location on route.link", function() {
|
o("changes location on route.link", function() {
|
||||||
|
|
@ -229,23 +231,23 @@ o.spec("route", function() {
|
||||||
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")
|
||||||
})
|
})
|
||||||
|
|
||||||
o("accepts RouteResolver", function() {
|
o("accepts RouteResolver with onmatch that returns Component", function(done) {
|
||||||
var matchCount = 0
|
var matchCount = 0
|
||||||
var renderCount = 0
|
var renderCount = 0
|
||||||
var Component = {
|
var Component = {
|
||||||
view: function() {
|
view: function() {
|
||||||
return m("div")
|
return m("span")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolver = {
|
var resolver = {
|
||||||
onmatch: function(resolve, args, requestedPath) {
|
onmatch: function(args, requestedPath) {
|
||||||
matchCount++
|
matchCount++
|
||||||
|
|
||||||
o(args.id).equals("abc")
|
o(args.id).equals("abc")
|
||||||
o(requestedPath).equals("/abc")
|
o(requestedPath).equals("/abc")
|
||||||
o(this).equals(resolver)
|
o(this).equals(resolver)
|
||||||
resolve(Component)
|
return Component
|
||||||
},
|
},
|
||||||
render: function(vnode) {
|
render: function(vnode) {
|
||||||
renderCount++
|
renderCount++
|
||||||
|
|
@ -262,12 +264,175 @@ o.spec("route", function() {
|
||||||
"/:id" : resolver
|
"/:id" : resolver
|
||||||
})
|
})
|
||||||
|
|
||||||
o(matchCount).equals(1)
|
callAsync(function() {
|
||||||
o(renderCount).equals(1)
|
o(matchCount).equals(1)
|
||||||
o(root.firstChild.nodeName).equals("DIV")
|
o(renderCount).equals(1)
|
||||||
|
o(root.firstChild.nodeName).equals("SPAN")
|
||||||
|
done()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("accepts RouteResolver without `render` method as payload", function() {
|
o("accepts RouteResolver with onmatch that returns Promise<Component>", function(done) {
|
||||||
|
var matchCount = 0
|
||||||
|
var renderCount = 0
|
||||||
|
var Component = {
|
||||||
|
view: function() {
|
||||||
|
return m("span")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resolver = {
|
||||||
|
onmatch: function(args, requestedPath) {
|
||||||
|
matchCount++
|
||||||
|
|
||||||
|
o(args.id).equals("abc")
|
||||||
|
o(requestedPath).equals("/abc")
|
||||||
|
o(this).equals(resolver)
|
||||||
|
return Promise.resolve(Component)
|
||||||
|
},
|
||||||
|
render: function(vnode) {
|
||||||
|
renderCount++
|
||||||
|
|
||||||
|
o(vnode.attrs.id).equals("abc")
|
||||||
|
o(this).equals(resolver)
|
||||||
|
|
||||||
|
return vnode
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/abc"
|
||||||
|
route(root, "/abc", {
|
||||||
|
"/:id" : resolver
|
||||||
|
})
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
o(matchCount).equals(1)
|
||||||
|
o(renderCount).equals(1)
|
||||||
|
o(root.firstChild.nodeName).equals("SPAN")
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
o("accepts RouteResolver with onmatch that returns Promise<undefined>", function(done) {
|
||||||
|
var matchCount = 0
|
||||||
|
var renderCount = 0
|
||||||
|
var Component = {
|
||||||
|
view: function() {
|
||||||
|
return m("span")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resolver = {
|
||||||
|
onmatch: function(args, requestedPath) {
|
||||||
|
matchCount++
|
||||||
|
|
||||||
|
o(args.id).equals("abc")
|
||||||
|
o(requestedPath).equals("/abc")
|
||||||
|
o(this).equals(resolver)
|
||||||
|
return Promise.resolve()
|
||||||
|
},
|
||||||
|
render: function(vnode) {
|
||||||
|
renderCount++
|
||||||
|
|
||||||
|
o(vnode.attrs.id).equals("abc")
|
||||||
|
o(this).equals(resolver)
|
||||||
|
|
||||||
|
return vnode
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/abc"
|
||||||
|
route(root, "/abc", {
|
||||||
|
"/:id" : resolver
|
||||||
|
})
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
o(matchCount).equals(1)
|
||||||
|
o(renderCount).equals(1)
|
||||||
|
o(root.firstChild.nodeName).equals("DIV")
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
o("accepts RouteResolver with onmatch that returns Promise<any>", function(done) {
|
||||||
|
var matchCount = 0
|
||||||
|
var renderCount = 0
|
||||||
|
var Component = {
|
||||||
|
view: function() {
|
||||||
|
return m("span")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resolver = {
|
||||||
|
onmatch: function(args, requestedPath) {
|
||||||
|
matchCount++
|
||||||
|
|
||||||
|
o(args.id).equals("abc")
|
||||||
|
o(requestedPath).equals("/abc")
|
||||||
|
o(this).equals(resolver)
|
||||||
|
return Promise.resolve([])
|
||||||
|
},
|
||||||
|
render: function(vnode) {
|
||||||
|
renderCount++
|
||||||
|
|
||||||
|
o(vnode.attrs.id).equals("abc")
|
||||||
|
o(this).equals(resolver)
|
||||||
|
|
||||||
|
return vnode
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/abc"
|
||||||
|
route(root, "/abc", {
|
||||||
|
"/:id" : resolver
|
||||||
|
})
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
o(matchCount).equals(1)
|
||||||
|
o(renderCount).equals(1)
|
||||||
|
o(root.firstChild.nodeName).equals("DIV")
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
o("accepts RouteResolver with onmatch that returns rejected Promise", function(done) {
|
||||||
|
var matchCount = 0
|
||||||
|
var renderCount = 0
|
||||||
|
var spy = o.spy()
|
||||||
|
var Component = {
|
||||||
|
view: function() {
|
||||||
|
return m("span")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resolver = {
|
||||||
|
onmatch: function(args, requestedPath) {
|
||||||
|
matchCount++
|
||||||
|
return Promise.reject(new Error("error"))
|
||||||
|
},
|
||||||
|
render: function(vnode) {
|
||||||
|
renderCount++
|
||||||
|
return vnode
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/test/1"
|
||||||
|
route(root, "/default", {
|
||||||
|
"/default" : {view: spy},
|
||||||
|
"/test/:id" : resolver
|
||||||
|
})
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
o(matchCount).equals(1)
|
||||||
|
o(renderCount).equals(0)
|
||||||
|
o(spy.callCount).equals(1)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
o("accepts RouteResolver without `render` method as payload", function(done) {
|
||||||
var matchCount = 0
|
var matchCount = 0
|
||||||
var Component = {
|
var Component = {
|
||||||
view: function() {
|
view: function() {
|
||||||
|
|
@ -278,29 +443,29 @@ o.spec("route", function() {
|
||||||
$window.location.href = prefix + "/abc"
|
$window.location.href = prefix + "/abc"
|
||||||
route(root, "/abc", {
|
route(root, "/abc", {
|
||||||
"/:id" : {
|
"/:id" : {
|
||||||
onmatch: function(resolve, args, requestedPath) {
|
onmatch: function(args, requestedPath) {
|
||||||
matchCount++
|
matchCount++
|
||||||
|
|
||||||
o(args.id).equals("abc")
|
o(args.id).equals("abc")
|
||||||
o(requestedPath).equals("/abc")
|
o(requestedPath).equals("/abc")
|
||||||
|
|
||||||
resolve(Component)
|
return Component
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
o(matchCount).equals(1)
|
callAsync(function() {
|
||||||
|
o(matchCount).equals(1)
|
||||||
o(root.firstChild.nodeName).equals("DIV")
|
o(root.firstChild.nodeName).equals("DIV")
|
||||||
|
done()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("changing `vnode.key` in `render` resets the component", function(done, timeout){
|
o("changing `vnode.key` in `render` resets the component", function(done, timeout){
|
||||||
timeout(FRAME_BUDGET * 6)
|
|
||||||
|
|
||||||
var oninit = o.spy()
|
var oninit = o.spy()
|
||||||
var Component = {
|
var Component = {
|
||||||
oninit: oninit,
|
oninit: oninit,
|
||||||
view: function(){
|
view: function() {
|
||||||
return m("div")
|
return m("div")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -310,14 +475,14 @@ o.spec("route", function() {
|
||||||
return m(Component, {key: vnode.attrs.id})
|
return m(Component, {key: vnode.attrs.id})
|
||||||
}}
|
}}
|
||||||
})
|
})
|
||||||
setTimeout(function(){
|
callAsync(function() {
|
||||||
o(oninit.callCount).equals(1)
|
o(oninit.callCount).equals(1)
|
||||||
route.set('/def')
|
route.set("/def")
|
||||||
setTimeout(function(){
|
callAsync(function() {
|
||||||
o(oninit.callCount).equals(2)
|
o(oninit.callCount).equals(2)
|
||||||
done()
|
done()
|
||||||
}, FRAME_BUDGET)
|
})
|
||||||
}, FRAME_BUDGET)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("accepts RouteResolver without `onmatch` method as payload", function() {
|
o("accepts RouteResolver without `onmatch` method as payload", function() {
|
||||||
|
|
@ -371,14 +536,14 @@ o.spec("route", function() {
|
||||||
|
|
||||||
route.set("/b")
|
route.set("/b")
|
||||||
|
|
||||||
setTimeout(function() {
|
callAsync(function() {
|
||||||
o(root.firstChild).equals(dom)
|
o(root.firstChild).equals(dom)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}, FRAME_BUDGET)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("calls onmatch and view correct number of times", function() {
|
o("calls onmatch and view correct number of times", function(done) {
|
||||||
var matchCount = 0
|
var matchCount = 0
|
||||||
var renderCount = 0
|
var renderCount = 0
|
||||||
var Component = {
|
var Component = {
|
||||||
|
|
@ -390,9 +555,9 @@ o.spec("route", function() {
|
||||||
$window.location.href = prefix + "/"
|
$window.location.href = prefix + "/"
|
||||||
route(root, "/", {
|
route(root, "/", {
|
||||||
"/" : {
|
"/" : {
|
||||||
onmatch: function(resolve) {
|
onmatch: function() {
|
||||||
matchCount++
|
matchCount++
|
||||||
resolve(Component)
|
return Component
|
||||||
},
|
},
|
||||||
render: function(vnode) {
|
render: function(vnode) {
|
||||||
renderCount++
|
renderCount++
|
||||||
|
|
@ -401,48 +566,126 @@ o.spec("route", function() {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
o(matchCount).equals(1)
|
callAsync(function() {
|
||||||
o(renderCount).equals(1)
|
o(matchCount).equals(1)
|
||||||
|
o(renderCount).equals(1)
|
||||||
|
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
|
|
||||||
o(matchCount).equals(1)
|
o(matchCount).equals(1)
|
||||||
o(renderCount).equals(2)
|
o(renderCount).equals(2)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
o("calls onmatch and view correct number of times when not onmatch returns undefined", function(done) {
|
||||||
|
var matchCount = 0
|
||||||
|
var renderCount = 0
|
||||||
|
var Component = {
|
||||||
|
view: function() {
|
||||||
|
return m("div")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/"
|
||||||
|
route(root, "/", {
|
||||||
|
"/" : {
|
||||||
|
onmatch: function() {
|
||||||
|
matchCount++
|
||||||
|
},
|
||||||
|
render: function(vnode) {
|
||||||
|
renderCount++
|
||||||
|
return {tag: Component}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
o(matchCount).equals(1)
|
||||||
|
o(renderCount).equals(1)
|
||||||
|
|
||||||
|
redrawService.redraw()
|
||||||
|
|
||||||
|
o(matchCount).equals(1)
|
||||||
|
o(renderCount).equals(2)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("onmatch can redirect to another route", function(done) {
|
o("onmatch can redirect to another route", function(done) {
|
||||||
var redirected = false
|
var redirected = false
|
||||||
|
var render = o.spy()
|
||||||
|
|
||||||
$window.location.href = prefix + "/a"
|
$window.location.href = prefix + "/a"
|
||||||
route(root, "/a", {
|
route(root, "/a", {
|
||||||
"/a" : {
|
"/a" : {
|
||||||
onmatch: function() {
|
onmatch: function() {
|
||||||
route.set("/b")
|
route.set("/b")
|
||||||
}
|
},
|
||||||
|
render: render
|
||||||
},
|
},
|
||||||
"/b" : {
|
"/b" : {
|
||||||
view: function(vnode){
|
view: function() {
|
||||||
redirected = true
|
redirected = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
setTimeout(function() {
|
callAsync(function() {
|
||||||
|
o(render.callCount).equals(0)
|
||||||
o(redirected).equals(true)
|
o(redirected).equals(true)
|
||||||
|
|
||||||
done()
|
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 w/ only onmatch", function(done) {
|
||||||
var redirected = false
|
var redirected = false
|
||||||
|
var render = o.spy()
|
||||||
|
var view = o.spy(function() {return m("div")})
|
||||||
|
|
||||||
$window.location.href = prefix + "/a"
|
$window.location.href = prefix + "/a"
|
||||||
route(root, "/a", {
|
route(root, "/a", {
|
||||||
"/a" : {
|
"/a" : {
|
||||||
onmatch: function() {
|
onmatch: function() {
|
||||||
route.set("/b")
|
route.set("/b")
|
||||||
|
},
|
||||||
|
render: render
|
||||||
|
},
|
||||||
|
"/b" : {
|
||||||
|
onmatch: function() {
|
||||||
|
redirected = true
|
||||||
|
return {view: view}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
o(render.callCount).equals(0)
|
||||||
|
o(redirected).equals(true)
|
||||||
|
o(view.callCount).equals(1)
|
||||||
|
o(root.childNodes.length).equals(1)
|
||||||
|
o(root.firstChild.nodeName).equals("DIV")
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
o("onmatch can redirect to another route that has RouteResolver w/ only render", function(done) {
|
||||||
|
var redirected = false
|
||||||
|
var render = o.spy()
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/a"
|
||||||
|
route(root, "/a", {
|
||||||
|
"/a" : {
|
||||||
|
onmatch: function() {
|
||||||
|
route.set("/b")
|
||||||
|
},
|
||||||
|
render: render
|
||||||
},
|
},
|
||||||
"/b" : {
|
"/b" : {
|
||||||
render: function(vnode){
|
render: function(vnode){
|
||||||
|
|
@ -451,45 +694,219 @@ o.spec("route", function() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
setTimeout(function() {
|
callAsync(function() {
|
||||||
|
o(render.callCount).equals(0)
|
||||||
o(redirected).equals(true)
|
o(redirected).equals(true)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}, FRAME_BUDGET)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("onmatch resolution callback resolves at most once", function(done) {
|
o("onmatch can redirect to another route that has RouteResolver whose onmatch resolves asynchronously", function(done) {
|
||||||
var resolveCount = 0
|
var redirected = false
|
||||||
var resolvedComponent
|
var render = o.spy()
|
||||||
var A = {view: function() {}}
|
var view = o.spy()
|
||||||
var B = {view: function() {}}
|
|
||||||
var C = {view: function() {}}
|
|
||||||
|
|
||||||
$window.location.href = prefix + "/"
|
$window.location.href = prefix + "/a"
|
||||||
route(root, "/", {
|
route(root, "/a", {
|
||||||
"/": {
|
"/a" : {
|
||||||
onmatch: function(resolve) {
|
onmatch: function() {
|
||||||
resolve(A)
|
route.set("/b")
|
||||||
resolve(B)
|
},
|
||||||
callAsync(function() {resolve(C)})
|
render: render
|
||||||
|
},
|
||||||
|
"/b" : {
|
||||||
|
onmatch: function() {
|
||||||
|
redirected = true
|
||||||
|
return new Promise(function(fulfill){
|
||||||
|
callAsync(function(){
|
||||||
|
fulfill({view: view})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
o(render.callCount).equals(0)
|
||||||
|
o(redirected).equals(true)
|
||||||
|
o(view.callCount).equals(1)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
o("onmatch can redirect to another route asynchronously", function(done) {
|
||||||
|
var redirected = false
|
||||||
|
var render = o.spy()
|
||||||
|
var view = o.spy()
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/a"
|
||||||
|
route(root, "/a", {
|
||||||
|
"/a" : {
|
||||||
|
onmatch: function() {
|
||||||
|
callAsync(function() {route.set("/b")})
|
||||||
|
return new Promise(function() {})
|
||||||
|
},
|
||||||
|
render: render
|
||||||
|
},
|
||||||
|
"/b" : {
|
||||||
|
onmatch: function() {
|
||||||
|
redirected = true
|
||||||
|
return {view: view}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
o(render.callCount).equals(0)
|
||||||
|
o(redirected).equals(true)
|
||||||
|
o(view.callCount).equals(1)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
o("onmatch can redirect w/ window.history.back()", function(done) {
|
||||||
|
|
||||||
|
var render = o.spy()
|
||||||
|
var component = {view: o.spy()}
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/a"
|
||||||
|
route(root, "/a", {
|
||||||
|
"/a" : {
|
||||||
|
onmatch: function() {
|
||||||
|
return component
|
||||||
},
|
},
|
||||||
render: function(vnode) {
|
render: function(vnode) {
|
||||||
resolveCount++
|
return vnode
|
||||||
resolvedComponent = vnode.tag
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/b" : {
|
||||||
|
onmatch: function() {
|
||||||
|
$window.history.back()
|
||||||
|
return new Promise(function() {})
|
||||||
|
},
|
||||||
|
render: render
|
||||||
|
}
|
||||||
})
|
})
|
||||||
setTimeout(function() {
|
|
||||||
o(resolveCount).equals(1)
|
|
||||||
o(resolvedComponent).equals(A)
|
|
||||||
|
|
||||||
done()
|
callAsync(function() {
|
||||||
}, FRAME_BUDGET)
|
route.set('/b')
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
o(render.callCount).equals(0)
|
||||||
|
o(component.view.callCount).equals(2)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
o("onmatch can redirect to a non-existent route that defaults to a RouteResolver w/ onmatch", function(done) {
|
||||||
|
var redirected = false
|
||||||
|
var render = o.spy()
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/a"
|
||||||
|
route(root, "/b", {
|
||||||
|
"/a" : {
|
||||||
|
onmatch: function() {
|
||||||
|
route.set("/c")
|
||||||
|
},
|
||||||
|
render: render
|
||||||
|
},
|
||||||
|
"/b" : {
|
||||||
|
onmatch: function(vnode){
|
||||||
|
redirected = true
|
||||||
|
return {view: function() {}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
o(render.callCount).equals(0)
|
||||||
|
o(redirected).equals(true)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
o("onmatch can redirect to a non-existent route that defaults to a RouteResolver w/ render", function(done) {
|
||||||
|
var redirected = false
|
||||||
|
var render = o.spy()
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/a"
|
||||||
|
route(root, "/b", {
|
||||||
|
"/a" : {
|
||||||
|
onmatch: function() {
|
||||||
|
route.set("/c")
|
||||||
|
},
|
||||||
|
render: render
|
||||||
|
},
|
||||||
|
"/b" : {
|
||||||
|
render: function(vnode){
|
||||||
|
redirected = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
o(render.callCount).equals(0)
|
||||||
|
o(redirected).equals(true)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
o("onmatch can redirect to a non-existent route that defaults to a component", function(done) {
|
||||||
|
var redirected = false
|
||||||
|
var render = o.spy()
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/a"
|
||||||
|
route(root, "/b", {
|
||||||
|
"/a" : {
|
||||||
|
onmatch: function() {
|
||||||
|
route.set("/c")
|
||||||
|
},
|
||||||
|
render: render
|
||||||
|
},
|
||||||
|
"/b" : {
|
||||||
|
view: function(vnode){
|
||||||
|
redirected = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
o(render.callCount).equals(0)
|
||||||
|
o(redirected).equals(true)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("the previous view redraws while onmatch resolution is pending (#1268)", function(done) {
|
o("the previous view redraws while onmatch resolution is pending (#1268)", function(done) {
|
||||||
var view = o.spy()
|
var view = o.spy()
|
||||||
var onmatch = o.spy()
|
var onmatch = o.spy(function() {
|
||||||
|
return new Promise(function() {})
|
||||||
|
})
|
||||||
|
|
||||||
$window.location.href = prefix + "/a"
|
$window.location.href = prefix + "/a"
|
||||||
route(root, "/", {
|
route(root, "/", {
|
||||||
|
|
@ -502,7 +919,7 @@ o.spec("route", function() {
|
||||||
|
|
||||||
route.set("/b")
|
route.set("/b")
|
||||||
|
|
||||||
setTimeout(function(){
|
callAsync(function() {
|
||||||
o(view.callCount).equals(1)
|
o(view.callCount).equals(1)
|
||||||
o(onmatch.callCount).equals(1)
|
o(onmatch.callCount).equals(1)
|
||||||
|
|
||||||
|
|
@ -512,12 +929,12 @@ o.spec("route", function() {
|
||||||
o(onmatch.callCount).equals(1)
|
o(onmatch.callCount).equals(1)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}, FRAME_BUDGET)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("m.route.set(m.route.get()) re-runs the resolution logic (#1180)", function(done){
|
o("m.route.set(m.route.get()) re-runs the resolution logic (#1180)", function(done){
|
||||||
var onmatch = o.spy(function(resolve) {resolve()})
|
var onmatch = o.spy()
|
||||||
var render = o.spy(function(){return m("div")})
|
var render = o.spy(function() {return m("div")})
|
||||||
|
|
||||||
$window.location.href = prefix + "/"
|
$window.location.href = prefix + "/"
|
||||||
route(root, '/', {
|
route(root, '/', {
|
||||||
|
|
@ -527,25 +944,33 @@ o.spec("route", function() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
o(onmatch.callCount).equals(1)
|
callAsync(function() {
|
||||||
o(render.callCount).equals(1)
|
o(onmatch.callCount).equals(1)
|
||||||
|
o(render.callCount).equals(1)
|
||||||
|
|
||||||
route.set(route.get())
|
route.set(route.get())
|
||||||
|
|
||||||
setTimeout(function() {
|
callAsync(function() {
|
||||||
o(onmatch.callCount).equals(2)
|
callAsync(function() {
|
||||||
o(render.callCount).equals(2)
|
o(onmatch.callCount).equals(2)
|
||||||
|
o(render.callCount).equals(2)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}, FRAME_BUDGET)
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("m.route.get() returns the last fully resolved route (#1276)", function(done){
|
o("m.route.get() returns the last fully resolved route (#1276)", function(done){
|
||||||
$window.location.href = prefix + "/"
|
$window.location.href = prefix + "/"
|
||||||
|
|
||||||
route(root, "/", {
|
route(root, "/", {
|
||||||
"/": {view: function(){}},
|
"/": {view: function() {}},
|
||||||
"/2": {onmatch: function(){}}
|
"/2": {
|
||||||
|
onmatch: function() {
|
||||||
|
return new Promise(function() {})
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -553,15 +978,13 @@ o.spec("route", function() {
|
||||||
|
|
||||||
route.set("/2")
|
route.set("/2")
|
||||||
|
|
||||||
setTimeout(function(){
|
callAsync(function() {
|
||||||
o(route.get()).equals("/")
|
o(route.get()).equals("/")
|
||||||
done()
|
done()
|
||||||
}, FRAME_BUDGET)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("routing with RouteResolver works more than once", function(done, timeout) {
|
o("routing with RouteResolver works more than once", function(done) {
|
||||||
timeout(200)
|
|
||||||
|
|
||||||
$window.location.href = prefix + "/a"
|
$window.location.href = prefix + "/a"
|
||||||
route(root, '/a', {
|
route(root, '/a', {
|
||||||
'/a': {
|
'/a': {
|
||||||
|
|
@ -578,44 +1001,96 @@ o.spec("route", function() {
|
||||||
|
|
||||||
route.set('/b')
|
route.set('/b')
|
||||||
|
|
||||||
setTimeout(function(){
|
callAsync(function() {
|
||||||
route.set('/a')
|
route.set('/a')
|
||||||
|
|
||||||
setTimeout(function(){
|
callAsync(function() {
|
||||||
o(root.firstChild.nodeName).equals("A")
|
o(root.firstChild.nodeName).equals("A")
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}, FRAME_BUDGET)
|
})
|
||||||
}, FRAME_BUDGET)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("calling route.set invalidates pending onmatch resolution", function(done, timeout) {
|
o("calling route.set invalidates pending onmatch resolution", function(done) {
|
||||||
timeout(200)
|
var rendered = false
|
||||||
|
|
||||||
var resolved
|
var resolved
|
||||||
$window.location.href = prefix + "/a"
|
$window.location.href = prefix + "/a"
|
||||||
route(root, "/a", {
|
route(root, "/a", {
|
||||||
"/a": {
|
"/a": {
|
||||||
onmatch: function(resolve) {
|
onmatch: function() {
|
||||||
setTimeout(resolve, 20)
|
return new Promise(function(resolve) {
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
resolve({view: function() {}})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
},
|
},
|
||||||
render: function(vnode) {resolved = "a"}
|
render: function(vnode) {
|
||||||
|
rendered = true
|
||||||
|
resolved = "a"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"/b": {
|
"/b": {
|
||||||
view: function() {resolved = "b"}
|
view: function() {
|
||||||
|
resolved = "b"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
route.set("/b")
|
route.set("/b")
|
||||||
|
|
||||||
setTimeout(function() {
|
callAsync(function() {
|
||||||
|
o(rendered).equals(false)
|
||||||
o(resolved).equals("b")
|
o(resolved).equals("b")
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}, 30)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("route changes activate onbeforeremove", function(done, timeout) {
|
o("calling route.set invalidates pending onmatch resolution", function(done) {
|
||||||
|
var rendered = false
|
||||||
|
var resolved
|
||||||
|
$window.location.href = prefix + "/a"
|
||||||
|
route(root, "/a", {
|
||||||
|
"/a": {
|
||||||
|
onmatch: function() {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
resolve({view: function() {rendered = true}})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
render: function(vnode) {
|
||||||
|
rendered = true
|
||||||
|
resolved = "a"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/b": {
|
||||||
|
view: function() {
|
||||||
|
resolved = "b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
route.set("/b")
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
o(rendered).equals(false)
|
||||||
|
o(resolved).equals("b")
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
o(rendered).equals(false)
|
||||||
|
o(resolved).equals("b")
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
o("route changes activate onbeforeremove", function(done) {
|
||||||
var spy = o.spy()
|
var spy = o.spy()
|
||||||
|
|
||||||
$window.location.href = prefix + "/a"
|
$window.location.href = prefix + "/a"
|
||||||
|
|
@ -631,13 +1106,46 @@ o.spec("route", function() {
|
||||||
|
|
||||||
route.set("/b")
|
route.set("/b")
|
||||||
|
|
||||||
setTimeout(function() {
|
callAsync(function() {
|
||||||
o(spy.callCount).equals(1)
|
o(spy.callCount).equals(1)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}, 30)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
o("asynchronous route.set in onmatch works", function(done) {
|
||||||
|
var rendered = false, resolved
|
||||||
|
route(root, "/a", {
|
||||||
|
"/a": {
|
||||||
|
onmatch: function() {
|
||||||
|
return Promise.resolve().then(function() {
|
||||||
|
route.set("/b")
|
||||||
|
})
|
||||||
|
},
|
||||||
|
render: function(vnode) {
|
||||||
|
rendered = true
|
||||||
|
resolved = "a"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/b": {
|
||||||
|
view: function() {
|
||||||
|
resolved = "b"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
callAsync(function() { // tick for popstate for /a
|
||||||
|
callAsync(function() { // tick for promise in onmatch
|
||||||
|
callAsync(function() { // tick for onpopstate for /b
|
||||||
|
o(rendered).equals(false)
|
||||||
|
o(resolved).equals("b")
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
o("throttles", function(done, timeout) {
|
o("throttles", function(done, timeout) {
|
||||||
timeout(200)
|
timeout(200)
|
||||||
|
|
||||||
|
|
@ -654,12 +1162,12 @@ o.spec("route", function() {
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
var after = i
|
var after = i
|
||||||
|
|
||||||
setTimeout(function(){
|
setTimeout(function() {
|
||||||
o(before).equals(1) // routes synchronously
|
o(before).equals(1) // routes synchronously
|
||||||
o(after).equals(2) // redraws synchronously
|
o(after).equals(2) // redraws synchronously
|
||||||
o(i).equals(3) // throttles rest
|
o(i).equals(3) // throttles rest
|
||||||
done()
|
done()
|
||||||
},40)
|
}, FRAME_BUDGET * 2)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ var querystring = m.buildQueryString({a: "1", b: "2"})
|
||||||
```javascript
|
```javascript
|
||||||
var state = {
|
var state = {
|
||||||
value: "",
|
value: "",
|
||||||
setValue: function(v) {value = v}
|
setValue: function(v) {state.value = v}
|
||||||
}
|
}
|
||||||
|
|
||||||
var Component = {
|
var Component = {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@ If you are migrating, consider using the [mithril-codemods](https://www.npmjs.co
|
||||||
- [`m.prop` removed](#mprop-removed)
|
- [`m.prop` removed](#mprop-removed)
|
||||||
- [`m.component` removed](#mcomponent-removed)
|
- [`m.component` removed](#mcomponent-removed)
|
||||||
- [`config` function](#config-function)
|
- [`config` function](#config-function)
|
||||||
- [Cancelling redraw from event handlers](#cancelling-redraw-from-event-handlers)
|
- [Changes in redraw behaviour](#changes-in-redraw-behaviour)
|
||||||
|
- [No more redraw locks](#no-more-redraw-locks)
|
||||||
|
- [Cancelling redraw from event handlers](#cancelling-redraw-from-event-handlers)
|
||||||
- [Component `controller` function](#component-controller-function)
|
- [Component `controller` function](#component-controller-function)
|
||||||
- [Component arguments](#component-arguments)
|
- [Component arguments](#component-arguments)
|
||||||
- [`view()` parameters](#view-parameters)
|
- [`view()` parameters](#view-parameters)
|
||||||
|
|
@ -25,6 +27,7 @@ If you are migrating, consider using the [mithril-codemods](https://www.npmjs.co
|
||||||
- [Accessing route params](#accessing-route-params)
|
- [Accessing route params](#accessing-route-params)
|
||||||
- [Preventing unmounting](#preventing-unmounting)
|
- [Preventing unmounting](#preventing-unmounting)
|
||||||
- [`m.request`](#mrequest)
|
- [`m.request`](#mrequest)
|
||||||
|
- [`m.deferred` removed](#mdeferred-removed)
|
||||||
- [`m.sync` removed](#msync-removed)
|
- [`m.sync` removed](#msync-removed)
|
||||||
- [`xlink` namespace required](#xlink-namespace-required)
|
- [`xlink` namespace required](#xlink-namespace-required)
|
||||||
- [Nested arrays in views](#nested-arrays-in-views)
|
- [Nested arrays in views](#nested-arrays-in-views)
|
||||||
|
|
@ -118,7 +121,15 @@ If available the DOM-Element of the vnode can be accessed at `vnode.dom`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Cancelling redraw from event handlers
|
## Changes in redraw behaviour
|
||||||
|
|
||||||
|
Mithril's rendering engine still operates on the basis of semi-automated global redraws, but some APIs and behaviours differ:
|
||||||
|
|
||||||
|
### No more redraw locks
|
||||||
|
|
||||||
|
In v0.2.x, Mithril allowed 'redraw locks' which temporarily prevented blocked draw logic: by default, `m.request` would lock the draw loop on execution and unlock when all pending requests had resolved - the same behaviour could be invoked manually using `m.startComputation()` and `m.endComputation()`. The latter APIs and the associated behaviour has been removed in v1.x. Redraw locking can lead to buggy UIs: the concerns of one part of the application should not be allowed to prevent other parts of the view from updating to reflect change.
|
||||||
|
|
||||||
|
### Cancelling redraw from event handlers
|
||||||
|
|
||||||
`m.mount()` and `m.route()` still automatically redraw after a DOM event handler runs. Cancelling these redraws from within your event handlers is now done by setting the `redraw` property on the passed-in event object to `false`.
|
`m.mount()` and `m.route()` still automatically redraw after a DOM event handler runs. Cancelling these redraws from within your event handlers is now done by setting the `redraw` property on the passed-in event object to `false`.
|
||||||
|
|
||||||
|
|
@ -492,9 +503,45 @@ Additionally, if the `extract` option is passed to `m.request` the return value
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## `m.deferred` removed
|
||||||
|
|
||||||
|
`v0.2.x` used its own custom asynchronous contract object, exposed as `m.deferred`, which was used as the basis for `m.request`. `v1.x` uses Promises instead, and implements a [polyfill](promises.md) in non-supporting environments. In situations where you would have used `m.deferred`, you should use Promises instead.
|
||||||
|
|
||||||
|
### `v0.2.x`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var greetAsync = function() {
|
||||||
|
var deferred = m.deferred();
|
||||||
|
setTimeout(function() {
|
||||||
|
deferred.resolve("hello");
|
||||||
|
}, 1000);
|
||||||
|
return deferred.promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
greetAsync()
|
||||||
|
.then(function(value) {return value + " world"})
|
||||||
|
.then(function(value) {console.log(value)}); //logs "hello world" after 1 second
|
||||||
|
```
|
||||||
|
|
||||||
|
### `v1.x`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var greetAsync = new Promise(function(resolve){
|
||||||
|
setTimeout(function() {
|
||||||
|
resolve("hello");
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
greetAsync()
|
||||||
|
.then(function(value) {return value + " world"})
|
||||||
|
.then(function(value) {console.log(value)}); //logs "hello world" after 1 second
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## `m.sync` removed
|
## `m.sync` removed
|
||||||
|
|
||||||
`m.sync` has been removed in favor of `Promise.all`
|
Since `v1.x` uses standards-compliant Promises, `m.sync` is redundant. Use `Promise.all` instead.
|
||||||
|
|
||||||
### `v0.2.x`
|
### `v0.2.x`
|
||||||
|
|
||||||
|
|
@ -560,7 +607,7 @@ If a vnode is strictly equal to the vnode occupying its place in the last draw,
|
||||||
|
|
||||||
## `m.startComputation`/`m.endComputation` removed
|
## `m.startComputation`/`m.endComputation` removed
|
||||||
|
|
||||||
They are considered anti-patterns and have a number of problematic edge cases, so they no longer exist in v1.x
|
They are considered anti-patterns and have a number of problematic edge cases, so they no longer exist in v1.x.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
- [Signature](#signature)
|
- [Signature](#signature)
|
||||||
- [How it works](#how-it-works)
|
- [How it works](#how-it-works)
|
||||||
- [Performance considerations](#performance-considerations)
|
- [Performance considerations](#performance-considerations)
|
||||||
- [Differences from m.render](#differences-from-m-render)
|
- [Differences from m.render](#differences-from-mrender)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -73,4 +73,4 @@ A component rendered via `m.mount` automatically auto-redraws in response to vie
|
||||||
|
|
||||||
`m.mount()` is suitable for application developers integrating Mithril widgets into existing codebases where routing is handled by another library or framework, while still enjoying Mithril's auto-redrawing facilities.
|
`m.mount()` is suitable for application developers integrating Mithril widgets into existing codebases where routing is handled by another library or framework, while still enjoying Mithril's auto-redrawing facilities.
|
||||||
|
|
||||||
`m.render()` is suitable for library authors who wish to manually control rendering (e.g. when hooking to a third party router, or using third party data-layer libraries like Redux).
|
`m.render()` is suitable for library authors who wish to manually control rendering (e.g. when hooking to a third party router, or using third party data-layer libraries like Redux).
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ m.request({
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
Calls to `m.request` return a [promise](promise.md).
|
A call to `m.request` return a [promise](promise.md) and trigger a redraw upon completion of its promise chain.
|
||||||
|
|
||||||
By default, `m.request` assumes the response is in JSON format and parses it into a Javascript object (or array).
|
By default, `m.request` assumes the response is in JSON format and parses it into a Javascript object (or array).
|
||||||
|
|
||||||
|
|
|
||||||
129
docs/route.md
129
docs/route.md
|
|
@ -16,9 +16,10 @@
|
||||||
- [Routing parameters](#routing-parameters)
|
- [Routing parameters](#routing-parameters)
|
||||||
- [Changing router prefix](#changing-router-prefix)
|
- [Changing router prefix](#changing-router-prefix)
|
||||||
- [Advanced component resolution](#advanced-component-resolution)
|
- [Advanced component resolution](#advanced-component-resolution)
|
||||||
- [Wrapping a layout component](#wrapping-a-layout-component)
|
- [Wrapping a layout component](#wrapping-a-layout-component)
|
||||||
- [Authentication](#authentication)
|
- [Authentication](#authentication)
|
||||||
- [Code splitting](#code-splitting)
|
- [Preloading data](#preloading-data)
|
||||||
|
- [Code splitting](#code-splitting)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -107,18 +108,23 @@ A RouterResolver is an object that contains an `onmatch` method and/or a `render
|
||||||
|
|
||||||
##### routeResolver.onmatch
|
##### routeResolver.onmatch
|
||||||
|
|
||||||
The `onmatch` hook is called when the router needs to find a component to render. It is called once when a router path changes, but not on subsequent redraws. It can be used to run logic before a component initializes (for example authentication logic)
|
The `onmatch` hook is called when the router needs to find a component to render. It is called once per router path changes, but not on subsequent redraws while on the same path. It can be used to run logic before a component initializes (for example authentication logic, data preloading, redirection analytics tracking, etc)
|
||||||
|
|
||||||
This method also allows you to asynchronously define what component will be rendered, making it suitable for code splitting and asynchronous module loading.
|
This method also allows you to asynchronously define what component will be rendered, making it suitable for code splitting and asynchronous module loading. To render a component asynchronously return a promise that resolves to a component.
|
||||||
|
|
||||||
`routeResolver.onmatch(resolve, args, requestedPath)`
|
For more information on `onmatch`, see the [advanced component resolution](#advanced-component-resolution) section
|
||||||
|
|
||||||
Argument | Type | Description
|
`routeResolver.onmatch(args, requestedPath)`
|
||||||
--------------- | ------------------------ | ---
|
|
||||||
`resolve` | `Component -> undefined` | Call this function with a component as the first argument to use it as the route's component
|
Argument | Type | Description
|
||||||
`args` | `Object` | The [routing parameters](#routing-parameters)
|
--------------- | ------------------------------ | ---
|
||||||
`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.
|
`args` | `Object` | The [routing parameters](#routing-parameters)
|
||||||
**returns** | | Returns `undefined`
|
`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.
|
||||||
|
**returns** | `Component|Promise<Component>` | Returns a component or a promise that resolves to a component
|
||||||
|
|
||||||
|
If `onmatch` returns a component or a promise that resolves to a component, this component is used as the `vnode.tag` for the first argument in the RouteResolver's `render` method. Otherwise, `vnode.tag` is set to `"div"`. Similarly, if the `onmatch` method is omitted, `vnode.tag` is also `"div"`.
|
||||||
|
|
||||||
|
If `onmatch` returns a promise that gets rejected, the router redirects back to `defaultRoute`. You may override this behavior by calling `.catch` on the promise chain before returning it.
|
||||||
|
|
||||||
##### routeResolver.render
|
##### routeResolver.render
|
||||||
|
|
||||||
|
|
@ -128,8 +134,8 @@ The `render` method is called on every redraw for a matching route. It is simila
|
||||||
|
|
||||||
Argument | Type | Description
|
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 onmatch does not return a component or a promise that resolves to a component, the vnode's `tag` field defaults to `"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 map of URL parameter values
|
||||||
**returns** | `Vnode` | Returns a vnode
|
**returns** | `Vnode` | Returns a vnode
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -258,7 +264,7 @@ m.route(document.body, "/edit/pictures/image.jpg", {
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Changing route prefix
|
### Changing router prefix
|
||||||
|
|
||||||
The router prefix is a fragment of the URL that dictates the underlying [strategy](routing-strategies.md) used by the router.
|
The router prefix is a fragment of the URL that dictates the underlying [strategy](routing-strategies.md) used by the router.
|
||||||
|
|
||||||
|
|
@ -286,8 +292,8 @@ 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(resolve, args, requestedPath) {
|
onmatch: function(args, requestedPath) {
|
||||||
resolve(Home)
|
return Home
|
||||||
},
|
},
|
||||||
render: function(vnode) {
|
render: function(vnode) {
|
||||||
return vnode // equivalent to m(Home)
|
return vnode // equivalent to m(Home)
|
||||||
|
|
@ -300,7 +306,7 @@ RouteResolvers are useful for implementing a variety of advanced routing use cas
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Wrapping a layout component
|
#### Wrapping a layout component
|
||||||
|
|
||||||
It's often desirable to wrap all or most of the routed components in a reusable shell (often called a "layout"). In order to do that, you first need to create a component that contains the common markup that will wrap around the various different components:
|
It's often desirable to wrap all or most of the routed components in a reusable shell (often called a "layout"). In order to do that, you first need to create a component that contains the common markup that will wrap around the various different components:
|
||||||
|
|
||||||
|
|
@ -344,7 +350,7 @@ Note that in this case, if the Layout component the `oninit` and `oncreate` life
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Authentication
|
#### Authentication
|
||||||
|
|
||||||
The RouterResolver's `onmatch` hook can be used to run logic before the top level component in a route is initializated. The example below shows how to implement a login wall that prevents users from seeing the `/secret` page unless they login.
|
The RouterResolver's `onmatch` hook can be used to run logic before the top level component in a route is initializated. The example below shows how to implement a login wall that prevents users from seeing the `/secret` page unless they login.
|
||||||
|
|
||||||
|
|
@ -366,10 +372,12 @@ var Login = {
|
||||||
|
|
||||||
m.route(document.body, "/secret", {
|
m.route(document.body, "/secret", {
|
||||||
"/secret": {
|
"/secret": {
|
||||||
onmatch: function(resolve) {
|
onmatch: function() {
|
||||||
if (isLoggedIn) resolve(Home)
|
if (!isLoggedIn) m.route.set("/login")
|
||||||
else m.route.set("/login")
|
|
||||||
},
|
},
|
||||||
|
render: function() {
|
||||||
|
return m(Home)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"/login": Login
|
"/login": Login
|
||||||
})
|
})
|
||||||
|
|
@ -381,11 +389,67 @@ For the sake of simplicity, in the example above, the user's logged in status is
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Code splitting
|
#### Preloading data
|
||||||
|
|
||||||
In a large application, it may be desirable to download the code for each route on demand, rather than upfront. Dividing the codebase this way is known as code splitting or lazy loading. In Mithril, this can be accomplished by calling the `resolve` callback of the `onmatch` hook asynchronously:
|
Typically, a component can load data upon initialization. Loading data this way renders the component twice (once upon routing, and once after the request completes).
|
||||||
|
|
||||||
At its simplest form, one could do the following:
|
```javascript
|
||||||
|
var state = {
|
||||||
|
users: [],
|
||||||
|
loadUsers: function() {
|
||||||
|
return m.request("/api/v1/users").then(function(users) {
|
||||||
|
state.users = users
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.route(document.body, "/user/list", {
|
||||||
|
"/user/list": {
|
||||||
|
oninit: state.loadUsers,
|
||||||
|
view: function() {
|
||||||
|
return state.users.length > 0 ? state.users.map(function() {
|
||||||
|
return m("div", user.id)
|
||||||
|
}) : "loading"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
In the example above, on the first render, the UI displays `"loading"` since `state.users` is an empty array before the request completes. Then, once data is available, the UI redraws and a list of user ids is shown.
|
||||||
|
|
||||||
|
RouteResolvers can be used as a mechanism to preload data before rendering a component in order to avoid UI flickering and thus bypassing the need for a loading indicator:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var state = {
|
||||||
|
users: [],
|
||||||
|
loadUsers: function() {
|
||||||
|
return m.request("/api/v1/users").then(function(users) {
|
||||||
|
state.users = users
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.route(document.body, "/user/list", {
|
||||||
|
"/user/list": {
|
||||||
|
onmatch: state.loadUsers,
|
||||||
|
render: function() {
|
||||||
|
return state.users.length > 0 ? state.users.map(function() {
|
||||||
|
return m("div", user.id)
|
||||||
|
}) : "loading"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Above, `render` only runs after the request completes, making the ternary operator redundant.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Code splitting
|
||||||
|
|
||||||
|
In a large application, it may be desirable to download the code for each route on demand, rather than upfront. Dividing the codebase this way is known as code splitting or lazy loading. In Mithril, this can be accomplished by returning a promise from the `onmatch` hook:
|
||||||
|
|
||||||
|
At its most basic form, one could do the following:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Home.js
|
// Home.js
|
||||||
|
|
@ -401,21 +465,20 @@ module.export = {
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// index.js
|
// index.js
|
||||||
function load(file, done) {
|
function load(file) {
|
||||||
m.request({
|
return m.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: file,
|
url: file,
|
||||||
extract: function(xhr) {
|
extract: function(xhr) {
|
||||||
return new Function("var module = {};" + xhr.responseText + ";return module.exports;")
|
return new Function("var module = {};" + xhr.responseText + ";return module.exports;")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.run(done)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m.route(document.body, "/", {
|
m.route(document.body, "/", {
|
||||||
"/": {
|
"/": {
|
||||||
onmatch: function(resolve) {
|
onmatch: function() {
|
||||||
load("Home.js", resolve)
|
return load("Home.js")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
@ -428,9 +491,11 @@ 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(resolve) {
|
onmatch: function() {
|
||||||
// using Webpack async code splitting
|
// using Webpack async code splitting
|
||||||
require(['./Home.js'], resolve)
|
return new Promise(function(resolve) {
|
||||||
|
require(['./Home.js'], resolve)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ Returns an event handler that runs `callback` with the value of the specified DO
|
||||||
```javascript
|
```javascript
|
||||||
var state = {
|
var state = {
|
||||||
value: "",
|
value: "",
|
||||||
setValue: function(v) {value = v}
|
setValue: function(v) {state.value = v}
|
||||||
}
|
}
|
||||||
|
|
||||||
var Component = {
|
var Component = {
|
||||||
|
|
@ -63,18 +63,18 @@ document.body.onclick = m.withAttr("title", function(value) {
|
||||||
Typically, `m.withAttr()` can be used in Mithril component views to avoid polluting the data layer with DOM event model concerns:
|
Typically, `m.withAttr()` can be used in Mithril component views to avoid polluting the data layer with DOM event model concerns:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var Data = {
|
var state = {
|
||||||
email: "",
|
email: "",
|
||||||
setEmail: function(email) {
|
setEmail: function(email) {
|
||||||
Data.email = email.toLowerCase()
|
state.email = email.toLowerCase()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var MyComponent = {
|
var MyComponent = {
|
||||||
view: function() {
|
view: function() {
|
||||||
return m("input", {
|
return m("input", {
|
||||||
oninput: m.withAttr("value", Data.setEmail),
|
oninput: m.withAttr("value", state.setEmail),
|
||||||
value: Data.email
|
value: state.email
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,15 +89,15 @@ m.mount(document.body, MyComponent)
|
||||||
The `m.withAttr()` helper reads the value of the element to which the event handler is bound, which is not necessarily the same as the element where the event originated.
|
The `m.withAttr()` helper reads the value of the element to which the event handler is bound, which is not necessarily the same as the element where the event originated.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var Data = {
|
var state = {
|
||||||
url: "",
|
url: "",
|
||||||
setURL: function(url) {Data.url = url}
|
setURL: function(url) {state.url = url}
|
||||||
}
|
}
|
||||||
|
|
||||||
var MyComponent = {
|
var MyComponent = {
|
||||||
view: function() {
|
view: function() {
|
||||||
return m("a[href='/foo']", {onclick: m.withAttr("href", Data.setURL)}, [
|
return m("a[href='/foo']", {onclick: m.withAttr("href", state.setURL)}, [
|
||||||
m("span", Data.url)
|
m("span", state.url)
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -117,21 +117,21 @@ The first argument of `m.withAttr()` can be either an attribute or a property.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// reads from `select.selectedIndex` property
|
// reads from `select.selectedIndex` property
|
||||||
var Data = {
|
var state = {
|
||||||
index: 0,
|
index: 0,
|
||||||
setIndex: function(index) {Data.index = index}
|
setIndex: function(index) {state.index = index}
|
||||||
}
|
}
|
||||||
m("select", {onclick: m.withAttr("selectedIndex", Data.setIndex)})
|
m("select", {onclick: m.withAttr("selectedIndex", state.setIndex)})
|
||||||
```
|
```
|
||||||
|
|
||||||
If a value can be both an attribute *and* a property, the property value is used.
|
If a value can be both an attribute *and* a property, the property value is used.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// value is a boolean, because the `input.checked` property is boolean
|
// value is a boolean, because the `input.checked` property is boolean
|
||||||
var Data = {
|
var state = {
|
||||||
selected: false,
|
selected: false,
|
||||||
setSelected: function(selected) {Data.selected = selected}
|
setSelected: function(selected) {state.selected = selected}
|
||||||
}
|
}
|
||||||
m("input[type=checkbox]", {onclick: m.withAttr("checked", Data.setSelected)})
|
m("input[type=checkbox]", {onclick: m.withAttr("checked", state.setSelected)})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
113
mithril.js
113
mithril.js
|
|
@ -214,7 +214,7 @@ var _8 = function($window, Promise) {
|
||||||
var next = then0.apply(promise0, arguments)
|
var next = then0.apply(promise0, arguments)
|
||||||
next.then(complete, function(e) {
|
next.then(complete, function(e) {
|
||||||
complete()
|
complete()
|
||||||
throw e
|
if (count === 0) throw e
|
||||||
})
|
})
|
||||||
return finalize(next)
|
return finalize(next)
|
||||||
}
|
}
|
||||||
|
|
@ -953,6 +953,7 @@ var _16 = function(redrawService0) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.mount = _16(redrawService)
|
m.mount = _16(redrawService)
|
||||||
|
var Promise = PromisePolyfill
|
||||||
var parseQueryString = function(string) {
|
var parseQueryString = function(string) {
|
||||||
if (string === "" || string == null) return {}
|
if (string === "" || string == null) return {}
|
||||||
if (string.charAt(0) === "?") string = string.slice(1)
|
if (string.charAt(0) === "?") string = string.slice(1)
|
||||||
|
|
@ -986,20 +987,18 @@ var parseQueryString = function(string) {
|
||||||
var coreRouter = function($window) {
|
var coreRouter = function($window) {
|
||||||
var supportsPushState = typeof $window.history.pushState === "function"
|
var supportsPushState = typeof $window.history.pushState === "function"
|
||||||
var callAsync0 = typeof setImmediate === "function" ? setImmediate : setTimeout
|
var callAsync0 = typeof setImmediate === "function" ? setImmediate : setTimeout
|
||||||
var prefix1 = "#!"
|
|
||||||
function setPrefix(value) {prefix1 = value}
|
|
||||||
function normalize1(fragment0) {
|
function normalize1(fragment0) {
|
||||||
var data = $window.location[fragment0].replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent)
|
var data = $window.location[fragment0].replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent)
|
||||||
if (fragment0 === "pathname" && data[0] !== "/") data = "/" + data
|
if (fragment0 === "pathname" && data[0] !== "/") data = "/" + data
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
var asyncId
|
var asyncId
|
||||||
function debounceAsync(f) {
|
function debounceAsync(callback0) {
|
||||||
return function() {
|
return function() {
|
||||||
if (asyncId != null) return
|
if (asyncId != null) return
|
||||||
asyncId = callAsync0(function() {
|
asyncId = callAsync0(function() {
|
||||||
asyncId = null
|
asyncId = null
|
||||||
f()
|
callback0()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1018,15 +1017,16 @@ var coreRouter = function($window) {
|
||||||
}
|
}
|
||||||
return path.slice(0, pathEnd)
|
return path.slice(0, pathEnd)
|
||||||
}
|
}
|
||||||
function getPath() {
|
var router = {prefix: "#!"}
|
||||||
var type2 = prefix1.charAt(0)
|
router.getPath = function() {
|
||||||
|
var type2 = router.prefix.charAt(0)
|
||||||
switch (type2) {
|
switch (type2) {
|
||||||
case "#": return normalize1("hash").slice(prefix1.length)
|
case "#": return normalize1("hash").slice(router.prefix.length)
|
||||||
case "?": return normalize1("search").slice(prefix1.length) + normalize1("hash")
|
case "?": return normalize1("search").slice(router.prefix.length) + normalize1("hash")
|
||||||
default: return normalize1("pathname").slice(prefix1.length) + normalize1("search") + normalize1("hash")
|
default: return normalize1("pathname").slice(router.prefix.length) + normalize1("search") + normalize1("hash")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function setPath(path, data, options) {
|
router.setPath = function(path, data, options) {
|
||||||
var queryData = {}, hashData = {}
|
var queryData = {}, hashData = {}
|
||||||
path = parsePath(path, queryData, hashData)
|
path = parsePath(path, queryData, hashData)
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
|
|
@ -1041,15 +1041,15 @@ var coreRouter = function($window) {
|
||||||
var hash = buildQueryString(hashData)
|
var hash = buildQueryString(hashData)
|
||||||
if (hash) path += "#" + hash
|
if (hash) path += "#" + hash
|
||||||
if (supportsPushState) {
|
if (supportsPushState) {
|
||||||
if (options && options.replace) $window.history.replaceState(null, null, prefix1 + path)
|
if (options && options.replace) $window.history.replaceState(null, null, router.prefix + path)
|
||||||
else $window.history.pushState(null, null, prefix1 + path)
|
else $window.history.pushState(null, null, router.prefix + path)
|
||||||
$window.onpopstate(true)
|
$window.onpopstate()
|
||||||
}
|
}
|
||||||
else $window.location.href = prefix1 + path
|
else $window.location.href = router.prefix + path
|
||||||
}
|
}
|
||||||
function defineRoutes(routes, resolve, reject) {
|
router.defineRoutes = function(routes, resolve, reject) {
|
||||||
function resolveRoute() {
|
function resolveRoute() {
|
||||||
var path = getPath()
|
var path = router.getPath()
|
||||||
var params = {}
|
var params = {}
|
||||||
var pathname = parsePath(path, params, params)
|
var pathname = parsePath(path, params, params)
|
||||||
|
|
||||||
|
|
@ -1071,72 +1071,71 @@ var coreRouter = function($window) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute)
|
if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute)
|
||||||
else if (prefix1.charAt(0) === "#") $window.onhashchange = resolveRoute
|
else if (router.prefix.charAt(0) === "#") $window.onhashchange = resolveRoute
|
||||||
resolveRoute()
|
resolveRoute()
|
||||||
}
|
}
|
||||||
function link(vnode2) {
|
|
||||||
vnode2.dom.setAttribute("href", prefix1 + vnode2.attrs.href)
|
return router
|
||||||
vnode2.dom.onclick = function(e) {
|
|
||||||
if (e.ctrlKey || e.metaKey || e.shiftKey || e.which === 2) return
|
|
||||||
e.preventDefault()
|
|
||||||
e.redraw = false
|
|
||||||
var href = this.getAttribute("href")
|
|
||||||
if (href.indexOf(prefix1) === 0) href = href.slice(prefix1.length)
|
|
||||||
setPath(href, undefined, undefined)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {setPrefix: setPrefix, getPath: getPath, setPath: setPath, defineRoutes: defineRoutes, link: link}
|
|
||||||
}
|
}
|
||||||
var _20 = function($window, redrawService0) {
|
var _20 = function($window, redrawService0) {
|
||||||
var routeService = coreRouter($window)
|
var routeService = coreRouter($window)
|
||||||
|
|
||||||
var identity = function(v) {return v}
|
var identity = function(v) {return v}
|
||||||
var resolver, component, attrs3, currentPath, resolve
|
var render1, component, attrs3, currentPath, updatePending = false
|
||||||
var route = function(root, defaultRoute, routes) {
|
var route = function(root, defaultRoute, routes) {
|
||||||
if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined")
|
if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined")
|
||||||
var update = function(routeResolver, comp, params, path) {
|
var update = function(routeResolver, comp, params, path) {
|
||||||
resolver = routeResolver, component = comp, attrs3 = params, currentPath = path, resolve = null
|
component = comp != null && typeof comp.view === "function" ? comp : "div", attrs3 = params, currentPath = path, updatePending = false
|
||||||
resolver.render = routeResolver.render || identity
|
render1 = (routeResolver.render || identity).bind(routeResolver)
|
||||||
render1()
|
run1()
|
||||||
}
|
}
|
||||||
var render1 = function() {
|
var run1 = function() {
|
||||||
if (resolver != null) redrawService0.render(root, resolver.render(Vnode(component, attrs3.key, attrs3)))
|
if (render1 != null) redrawService0.render(root, render1(Vnode(component, attrs3.key, attrs3)))
|
||||||
|
}
|
||||||
|
var bail = function() {
|
||||||
|
routeService.setPath(defaultRoute)
|
||||||
}
|
}
|
||||||
routeService.defineRoutes(routes, function(payload, params, path) {
|
routeService.defineRoutes(routes, function(payload, params, path) {
|
||||||
if (payload.view) update({}, payload, params, path)
|
if (payload.view) update({}, payload, params, path)
|
||||||
else {
|
else {
|
||||||
if (payload.onmatch) {
|
if (payload.onmatch) {
|
||||||
if (resolve != null) update(payload, component, params, path)
|
updatePending = true
|
||||||
else {
|
Promise.resolve(payload.onmatch(params, path)).then(function(resolved) {
|
||||||
resolve = function(resolved) {
|
if (updatePending) update(payload, resolved, params, path)
|
||||||
update(payload, resolved, params, path)
|
}, bail)
|
||||||
}
|
|
||||||
payload.onmatch(function(resolved) {
|
|
||||||
if (resolve != null) resolve(resolved)
|
|
||||||
}, params, path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else update(payload, "div", params, path)
|
else update(payload, "div", params, path)
|
||||||
}
|
}
|
||||||
}, function() {
|
}, bail)
|
||||||
routeService.setPath(defaultRoute)
|
redrawService0.subscribe(root, run1)
|
||||||
})
|
}
|
||||||
redrawService0.subscribe(root, render1)
|
route.set = function(path, data, options) {
|
||||||
|
if (updatePending) options = {replace: true}
|
||||||
|
updatePending = false
|
||||||
|
routeService.setPath(path, data, options)
|
||||||
}
|
}
|
||||||
route.set = routeService.setPath
|
|
||||||
route.get = function() {return currentPath}
|
route.get = function() {return currentPath}
|
||||||
route.prefix = routeService.setPrefix
|
route.prefix = function(prefix0) {routeService.prefix = prefix0}
|
||||||
route.link = routeService.link
|
route.link = function(vnode1) {
|
||||||
|
vnode1.dom.setAttribute("href", routeService.prefix + vnode1.attrs.href)
|
||||||
|
vnode1.dom.onclick = function(e) {
|
||||||
|
if (e.ctrlKey || e.metaKey || e.shiftKey || e.which === 2) return
|
||||||
|
e.preventDefault()
|
||||||
|
e.redraw = false
|
||||||
|
var href = this.getAttribute("href")
|
||||||
|
if (href.indexOf(routeService.prefix) === 0) href = href.slice(routeService.prefix.length)
|
||||||
|
route.set(href, undefined, undefined)
|
||||||
|
}
|
||||||
|
}
|
||||||
return route
|
return route
|
||||||
}
|
}
|
||||||
m.route = _20(window, redrawService)
|
m.route = _20(window, redrawService)
|
||||||
m.withAttr = function(attrName, callback0, context) {
|
m.withAttr = function(attrName, callback1, context) {
|
||||||
return function(e) {
|
return function(e) {
|
||||||
return callback0.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName))
|
return callback1.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var _27 = coreRenderer(window)
|
var _28 = coreRenderer(window)
|
||||||
m.render = _27.render
|
m.render = _28.render
|
||||||
m.redraw = redrawService.redraw
|
m.redraw = redrawService.redraw
|
||||||
m.request = requestService.request
|
m.request = requestService.request
|
||||||
m.jsonp = requestService.jsonp
|
m.jsonp = requestService.jsonp
|
||||||
|
|
|
||||||
81
mithril.min.js
vendored
81
mithril.min.js
vendored
|
|
@ -1,40 +1,41 @@
|
||||||
new function(){function r(a,c,h,d,g,l){return{tag:a,key:c,attrs:h,children:d,text:g,dom:l,domSize:void 0,state:{},events:void 0,instance:void 0,skip:!1}}function y(a){if(null==a||"string"!==typeof a&&null==a.view)throw Error("The selector must be either a string or a component.");if("string"===typeof a&&void 0===I[a]){for(var c,h,d=[],g={};c=P.exec(a);){var l=c[1],m=c[2];""===l&&""!==m?h=m:"#"===l?g.id=m:"."===l?d.push(m):"["===c[3][0]&&((l=c[6])&&(l=l.replace(/\\(["'])/g,"$1").replace(/\\\\/g,"\\")),
|
new function(){function w(a,c,h,d,g,m){return{tag:a,key:c,attrs:h,children:d,text:g,dom:m,domSize:void 0,state:{},events:void 0,instance:void 0,skip:!1}}function z(a){if(null==a||"string"!==typeof a&&null==a.view)throw Error("The selector must be either a string or a component.");if("string"===typeof a&&void 0===K[a]){for(var c,h,d=[],g={};c=P.exec(a);){var m=c[1],l=c[2];""===m&&""!==l?h=l:"#"===m?g.id=l:"."===m?d.push(l):"["===c[3][0]&&((m=c[6])&&(m=m.replace(/\\(["'])/g,"$1").replace(/\\\\/g,"\\")),
|
||||||
"class"===c[4]?d.push(l):g[c[4]]=l||!0)}0<d.length&&(g.className=d.join(" "));I[a]=function(a,c){var l=!1,b,d,v=a.className||a["class"],u;for(u in g)a[u]=g[u];void 0!==v&&(void 0!==a["class"]&&(a["class"]=void 0,a.className=v),void 0!==g.className&&(a.className=g.className+" "+v));for(u in a)if("key"!==u){l=!0;break}Array.isArray(c)&&1==c.length&&null!=c[0]&&"#"===c[0].tag?d=c[0].children:b=c;return r(h||"div",a.key,l?a:void 0,b,d,void 0)}}var A;null==arguments[1]||"object"===typeof arguments[1]&&
|
"class"===c[4]?d.push(m):g[c[4]]=m||!0)}0<d.length&&(g.className=d.join(" "));K[a]=function(a,c){var m=!1,b,p,d=a.className||a["class"],t;for(t in g)a[t]=g[t];void 0!==d&&(void 0!==a["class"]&&(a["class"]=void 0,a.className=d),void 0!==g.className&&(a.className=g.className+" "+d));for(t in a)if("key"!==t){m=!0;break}c instanceof Array&&1==c.length&&null!=c[0]&&"#"===c[0].tag?p=c[0].children:b=c;return w(h||"div",a.key,m?a:void 0,b,p,void 0)}}var n;null!=arguments[1]&&("object"!==typeof arguments[1]||
|
||||||
void 0===arguments[1].tag&&!Array.isArray(arguments[1])?(A=arguments[1],d=2):d=1;if(arguments.length===d+1)c=Array.isArray(arguments[d])?arguments[d]:[arguments[d]];else for(c=[];d<arguments.length;d++)c.push(arguments[d]);return"string"===typeof a?I[a](A||{},r.normalizeChildren(c)):r(a,A&&A.key,A||{},r.normalizeChildren(c),void 0,void 0)}function Q(a){var c=0,h=null,d="function"===typeof requestAnimationFrame?requestAnimationFrame:setTimeout;return function(){var g=Date.now();0===c||16<=g-c?(c=g,
|
void 0!==arguments[1].tag||arguments[1]instanceof Array)?d=1:(n=arguments[1],d=2);if(arguments.length===d+1)c=arguments[d]instanceof Array?arguments[d]:[arguments[d]];else for(c=[];d<arguments.length;d++)c.push(arguments[d]);return"string"===typeof a?K[a](n||{},w.normalizeChildren(c)):w(a,n&&n.key,n||{},w.normalizeChildren(c),void 0,void 0)}function Q(a){var c=0,h=null,d="function"===typeof requestAnimationFrame?requestAnimationFrame:setTimeout;return function(){var g=Date.now();0===c||16<=g-c?(c=
|
||||||
a()):null===h&&(h=d(function(){h=null;a();c=Date.now()},16-(g-c)))}}r.normalize=function(a){return Array.isArray(a)?r("[",void 0,void 0,r.normalizeChildren(a),void 0,void 0):null!=a&&"object"!==typeof a?r("#",void 0,void 0,a,void 0,void 0):a};r.normalizeChildren=function(a){for(var c=0;c<a.length;c++)a[c]=r.normalize(a[c]);return a};var P=/(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g,I={};y.trust=function(a){null==a&&(a="");return r("<",void 0,void 0,a,void 0,void 0)};
|
g,a()):null===h&&(h=d(function(){h=null;a();c=Date.now()},16-(g-c)))}}w.normalize=function(a){return a instanceof Array?w("[",void 0,void 0,w.normalizeChildren(a),void 0,void 0):null!=a&&"object"!==typeof a?w("#",void 0,void 0,a,void 0,void 0):a};w.normalizeChildren=function(a){for(var c=0;c<a.length;c++)a[c]=w.normalize(a[c]);return a};var P=/(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g,K={};z.trust=function(a){null==a&&(a="");return w("<",void 0,void 0,a,void 0,
|
||||||
y.fragment=function(a,c){return r("[",a.key,a,r.normalizeChildren(c),void 0,void 0)};var t=function(a){function c(a,b){return function v(c){var k;try{if(!b||null==c||"object"!==typeof c&&"function"!==typeof c||"function"!==typeof(k=c.then))G(function(){b||0!==a.length||console.error("Possible unhandled promise rejection:",c);for(var d=0;d<a.length;d++)a[d](c);g.length=0;l.length=0;n.state=b;n.retry=function(){v(c)}});else{if(c===d)throw new TypeError("Promise can't be resolved w/ itself");h(k.bind(c))}}catch(C){A(C)}}}
|
void 0)};z.fragment=function(a,c){return w("[",a.key,a,w.normalizeChildren(c),void 0,void 0)};var v=function(a){function c(a,b){return function C(c){var l;try{if(!b||null==c||"object"!==typeof c&&"function"!==typeof c||"function"!==typeof(l=c.then))k(function(){b||0!==a.length||console.error("Possible unhandled promise rejection:",c);for(var d=0;d<a.length;d++)a[d](c);g.length=0;m.length=0;p.state=b;p.retry=function(){C(c)}});else{if(c===d)throw new TypeError("Promise can't be resolved w/ itself");
|
||||||
function h(a){function b(b){return function(a){0<c++||b(a)}}var c=0,d=b(A);try{a(b(m),d)}catch(u){d(u)}}if(!(this instanceof t))throw Error("Promise must be called with `new`");if("function"!==typeof a)throw new TypeError("executor must be a function");var d=this,g=[],l=[],m=c(g,!0),A=c(l,!1),n=d._instance={resolvers:g,rejectors:l},G="function"===typeof setImmediate?setImmediate:setTimeout;h(a)};t.prototype.then=function(a,c){function h(a,c,h,k){c.push(function(b){if("function"!==typeof a)h(b);else try{g(a(b))}catch(M){l&&
|
h(l.bind(c))}}catch(G){n(G)}}}function h(a){function b(b){return function(a){0<c++||b(a)}}var c=0,d=b(n);try{a(b(l),d)}catch(t){d(t)}}if(!(this instanceof v))throw Error("Promise must be called with `new`");if("function"!==typeof a)throw new TypeError("executor must be a function");var d=this,g=[],m=[],l=c(g,!0),n=c(m,!1),p=d._instance={resolvers:g,rejectors:m},k="function"===typeof setImmediate?setImmediate:setTimeout;h(a)};v.prototype.then=function(a,c){function h(a,c,h,l){c.push(function(b){if("function"!==
|
||||||
l(M)}});"function"===typeof d.retry&&k===d.state&&d.retry()}var d=this._instance,g,l,m=new t(function(a,c){g=a;l=c});h(a,d.resolvers,g,!0);h(c,d.rejectors,l,!1);return m};t.prototype["catch"]=function(a){return this.then(null,a)};t.resolve=function(a){return a instanceof t?a:new t(function(c){c(a)})};t.reject=function(a){return new t(function(c,h){h(a)})};t.all=function(a){return new t(function(c,h){var d=a.length,g=0,l=[];if(0===a.length)c([]);else for(var m=0;m<a.length;m++)(function(m){function n(a){g++;
|
typeof a)h(b);else try{g(a(b))}catch(D){m&&m(D)}});"function"===typeof d.retry&&l===d.state&&d.retry()}var d=this._instance,g,m,l=new v(function(a,c){g=a;m=c});h(a,d.resolvers,g,!0);h(c,d.rejectors,m,!1);return l};v.prototype["catch"]=function(a){return this.then(null,a)};v.resolve=function(a){return a instanceof v?a:new v(function(c){c(a)})};v.reject=function(a){return new v(function(c,h){h(a)})};v.all=function(a){return new v(function(c,h){var d=a.length,g=0,m=[];if(0===a.length)c([]);else for(var l=
|
||||||
l[m]=a;g===d&&c(l)}null==a[m]||"object"!==typeof a[m]&&"function"!==typeof a[m]||"function"!==typeof a[m].then?n(a[m]):a[m].then(n,h)})(m)})};t.race=function(a){return new t(function(c,h){for(var d=0;d<a.length;d++)a[d].then(c,h)})};"undefined"===typeof Promise&&("undefined"!==typeof window?window.Promise=t:"undefined"!==typeof global&&(global.Promise=t));var E=function(a){function c(a,d){if(Array.isArray(d))for(var g=0;g<d.length;g++)c(a+"["+g+"]",d[g]);else if("[object Object]"===Object.prototype.toString.call(d))for(g in d)c(a+
|
0;l<a.length;l++)(function(l){function p(a){g++;m[l]=a;g===d&&c(m)}null==a[l]||"object"!==typeof a[l]&&"function"!==typeof a[l]||"function"!==typeof a[l].then?p(a[l]):a[l].then(p,h)})(l)})};v.race=function(a){return new v(function(c,h){for(var d=0;d<a.length;d++)a[d].then(c,h)})};"undefined"===typeof E&&("undefined"!==typeof window?window.Promise=v:"undefined"!==typeof global&&(global.Promise=v));var H="undefined"!==typeof E?E:v,I=function(a){function c(a,d){if(d instanceof Array)for(var g=0;g<d.length;g++)c(a+
|
||||||
"["+g+"]",d[g]);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)c(d,a[d]);return h.join("&")},K=function(a,c){function h(){function b(){0===--a&&"function"===typeof k&&k()}var a=0;return function u(c){var d=c.then;c.then=function(){a++;var g=d.apply(c,arguments);g.then(b,function(a){b();throw a;});return u(g)};return c}}function d(b,a){if("string"===typeof b){var c=b;b=a||{};null==
|
"["+g+"]",d[g]);else if("[object Object]"===Object.prototype.toString.call(d))for(g in d)c(a+"["+g+"]",d[g]);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)c(d,a[d]);return h.join("&")},M=function(a,c){function h(){function b(){0===--a&&"function"===typeof r&&r()}var a=0;return function t(c){var d=c.then;c.then=function(){a++;var g=d.apply(c,arguments);g.then(b,function(c){b();
|
||||||
b.url&&(b.url=c)}return b}function g(b,a){if(null==a)return b;for(var c=b.match(/:[^\/]+/gi)||[],d=0;d<c.length;d++){var g=c[d].slice(1);null!=a[g]&&(b=b.replace(c[d],a[g]),delete a[g])}return b}function l(a,c){var b=E(c);if(""!==b){var d=0>a.indexOf("?")?"?":"&";a+=d+b}return a}function m(a){try{return""!==a?JSON.parse(a):null}catch(M){throw Error(a);}}function A(a){return a.responseText}function n(a,c){if("function"===typeof a)if(Array.isArray(c))for(var b=0;b<c.length;b++)c[b]=new a(c[b]);else return new a(c);
|
if(0===a)throw c;});return t(g)};return c}}function d(b,a){if("string"===typeof b){var c=b;b=a||{};null==b.url&&(b.url=c)}return b}function g(b,a){if(null==a)return b;for(var c=b.match(/:[^\/]+/gi)||[],d=0;d<c.length;d++){var g=c[d].slice(1);null!=a[g]&&(b=b.replace(c[d],a[g]),delete a[g])}return b}function m(a,c){var b=I(c);if(""!==b){var d=0>a.indexOf("?")?"?":"&";a+=d+b}return a}function l(a){try{return""!==a?JSON.parse(a):null}catch(D){throw Error(a);}}function n(a){return a.responseText}function p(a,
|
||||||
return c}var r=0,k;return{request:function(b,k){var v=h();b=d(b,k);var u=new c(function(c,d){null==b.method&&(b.method="GET");b.method=b.method.toUpperCase();var h="boolean"===typeof b.useBody?b.useBody:"GET"!==b.method&&"TRACE"!==b.method;"function"!==typeof b.serialize&&(b.serialize="undefined"!==typeof FormData&&b.data instanceof FormData?function(a){return a}:JSON.stringify);"function"!==typeof b.deserialize&&(b.deserialize=m);"function"!==typeof b.extract&&(b.extract=A);b.url=g(b.url,b.data);
|
c){if("function"===typeof a)if(c instanceof Array)for(var b=0;b<c.length;b++)c[b]=new a(c[b]);else return new a(c);return c}var k=0,r;return{request:function(b,k){var r=h();b=d(b,k);var t=new c(function(c,d){null==b.method&&(b.method="GET");b.method=b.method.toUpperCase();var h="boolean"===typeof b.useBody?b.useBody:"GET"!==b.method&&"TRACE"!==b.method;"function"!==typeof b.serialize&&(b.serialize="undefined"!==typeof FormData&&b.data instanceof FormData?function(a){return a}:JSON.stringify);"function"!==
|
||||||
h?b.data=b.serialize(b.data):b.url=l(b.url,b.data);var k=new a.XMLHttpRequest;k.open(b.method,b.url,"boolean"===typeof b.async?b.async:!0,"string"===typeof b.user?b.user:void 0,"string"===typeof b.password?b.password:void 0);b.serialize===JSON.stringify&&h&&k.setRequestHeader("Content-Type","application/json; charset=utf-8");b.deserialize===m&&k.setRequestHeader("Accept","application/json, text/*");b.withCredentials&&(k.withCredentials=b.withCredentials);"function"===typeof b.config&&(k=b.config(k,
|
typeof b.deserialize&&(b.deserialize=l);"function"!==typeof b.extract&&(b.extract=n);b.url=g(b.url,b.data);h?b.data=b.serialize(b.data):b.url=m(b.url,b.data);var k=new a.XMLHttpRequest;k.open(b.method,b.url,"boolean"===typeof b.async?b.async:!0,"string"===typeof b.user?b.user:void 0,"string"===typeof b.password?b.password:void 0);b.serialize===JSON.stringify&&h&&k.setRequestHeader("Content-Type","application/json; charset=utf-8");b.deserialize===l&&k.setRequestHeader("Accept","application/json, text/*");
|
||||||
b)||k);k.onreadystatechange=function(){if(4===k.readyState)try{var a=b.extract!==A?b.extract(k,b):b.deserialize(b.extract(k,b));if(200<=k.status&&300>k.status||304===k.status)c(n(b.type,a));else{var g=Error(k.responseText),h;for(h in a)g[h]=a[h];d(g)}}catch(F){d(F)}};h&&null!=b.data?k.send(b.data):k.send()});return!0===b.background?u:v(u)},jsonp:function(b,k){var m=h();b=d(b,k);var u=new c(function(c,d){var k=b.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+r++,h=a.document.createElement("script");
|
b.withCredentials&&(k.withCredentials=b.withCredentials);"function"===typeof b.config&&(k=b.config(k,b)||k);k.onreadystatechange=function(){if(4===k.readyState)try{var a=b.extract!==n?b.extract(k,b):b.deserialize(b.extract(k,b));if(200<=k.status&&300>k.status||304===k.status)c(p(b.type,a));else{var g=Error(k.responseText),h;for(h in a)g[h]=a[h];d(g)}}catch(F){d(F)}};h&&null!=b.data?k.send(b.data):k.send()});return!0===b.background?t:r(t)},jsonp:function(b,l){var n=h();b=d(b,l);var t=new c(function(c,
|
||||||
a[k]=function(d){h.parentNode.removeChild(h);c(n(b.type,d));delete a[k]};h.onerror=function(){h.parentNode.removeChild(h);d(Error("JSONP request failed"));delete a[k]};null==b.data&&(b.data={});b.url=g(b.url,b.data);b.data[b.callbackKey||"callback"]=k;h.src=l(b.url,b.data);a.document.documentElement.appendChild(h)});return!0===b.background?u:m(u)},setCompletionCallback:function(a){k=a}}}(window,"undefined"!==typeof Promise?Promise:t),O=function(a){function c(e,f,a,b,c,d,g){for(;a<b;a++){var q=f[a];
|
d){var h=b.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+k++,l=a.document.createElement("script");a[h]=function(d){l.parentNode.removeChild(l);c(p(b.type,d));delete a[h]};l.onerror=function(){l.parentNode.removeChild(l);d(Error("JSONP request failed"));delete a[h]};null==b.data&&(b.data={});b.url=g(b.url,b.data);b.data[b.callbackKey||"callback"]=h;l.src=m(b.url,b.data);a.document.documentElement.appendChild(l)});return!0===b.background?t:n(t)},setCompletionCallback:function(a){r=a}}}(window,
|
||||||
null!=q&&n(e,h(q,c,g),d)}}function h(e,f,a){var q=e.tag;null!=e.attrs&&J(e.attrs,e,f);if("string"===typeof q)switch(q){case "#":return e.dom=B.createTextNode(e.children);case "<":return d(e);case "[":var b=B.createDocumentFragment();null!=e.children&&(q=e.children,c(b,q,0,q.length,f,null,a));e.dom=b.firstChild;e.domSize=b.childNodes.length;return b;default:var g=e.tag;switch(e.tag){case "svg":a="http://www.w3.org/2000/svg";break;case "math":a="http://www.w3.org/1998/Math/MathML"}var k=(q=e.attrs)&&
|
H),O=function(a){function c(e,f,a,b,c,d,g){for(;a<b;a++){var q=f[a];null!=q&&p(e,h(q,c,g),d)}}function h(e,f,a){var q=e.tag;null!=e.attrs&&L(e.attrs,e,f);if("string"===typeof q)switch(q){case "#":return e.dom=B.createTextNode(e.children);case "<":return d(e);case "[":var b=B.createDocumentFragment();null!=e.children&&(q=e.children,c(b,q,0,q.length,f,null,a));e.dom=b.firstChild;e.domSize=b.childNodes.length;return b;default:var g=e.tag;switch(e.tag){case "svg":a="http://www.w3.org/2000/svg";break;
|
||||||
q.is,g=a?k?B.createElementNS(a,g,{is:k}):B.createElementNS(a,g):k?B.createElement(g,{is:k}):B.createElement(g);e.dom=g;if(null!=q)for(b in k=a,q)u(e,b,null,q[b],k);null!=e.attrs&&null!=e.attrs.contenteditable?G(e):(null!=e.text&&(""!==e.text?g.textContent=e.text:e.children=[r("#",void 0,void 0,e.text,void 0,void 0)]),null!=e.children&&(b=e.children,c(g,b,0,b.length,f,null,a),f=e.attrs,"select"===e.tag&&null!=f&&("value"in f&&u(e,"value",null,f.value,void 0),"selectedIndex"in f&&u(e,"selectedIndex",
|
case "math":a="http://www.w3.org/1998/Math/MathML"}var p=(q=e.attrs)&&q.is,g=a?p?B.createElementNS(a,g,{is:p}):B.createElementNS(a,g):p?B.createElement(g,{is:p}):B.createElement(g);e.dom=g;if(null!=q)for(b in p=a,q)t(e,b,null,q[b],p);null!=e.attrs&&null!=e.attrs.contenteditable?k(e):(null!=e.text&&(""!==e.text?g.textContent=e.text:e.children=[w("#",void 0,void 0,e.text,void 0,void 0)]),null!=e.children&&(b=e.children,c(g,b,0,b.length,f,null,a),f=e.attrs,"select"===e.tag&&null!=f&&("value"in f&&t(e,
|
||||||
null,f.selectedIndex,void 0))));return g}else{e.state||(e.state={});E(e.state,e.tag);b=e.tag.view;if(null!=b.reentrantLock)e=N;else if(b.reentrantLock=!0,J(e.tag,e,f),e.instance=r.normalize(b.call(e.state,e)),b.reentrantLock=null,null!=e.instance){if(e.instance===e)throw Error("A view cannot return the vnode it received as arguments");f=h(e.instance,f,a);e.dom=e.instance.dom;e.domSize=null!=e.dom?e.instance.domSize:0;e=f}else e.domSize=0,e=N;return e}}function d(e){var f={caption:"table",thead:"table",
|
"value",null,f.value,void 0),"selectedIndex"in f&&t(e,"selectedIndex",null,f.selectedIndex,void 0))));return g}else{e.state||(e.state={});R(e.state,e.tag);b=e.tag.view;if(null!=b.reentrantLock)e=E;else if(b.reentrantLock=!0,L(e.tag,e,f),e.instance=w.normalize(b.call(e.state,e)),b.reentrantLock=null,null!=e.instance){if(e.instance===e)throw Error("A view cannot return the vnode it received as arguments");f=h(e.instance,f,a);e.dom=e.instance.dom;e.domSize=null!=e.dom?e.instance.domSize:0;e=f}else e.domSize=
|
||||||
tbody:"table",tfoot:"table",tr:"tbody",th:"tr",td:"tr",colgroup:"table",col:"colgroup"}[(e.children.match(/^\s*?<(\w+)/im)||[])[1]]||"div",f=B.createElement(f);f.innerHTML=e.children;e.dom=f.firstChild;e.domSize=f.childNodes.length;e=B.createDocumentFragment();for(var a;a=f.firstChild;)e.appendChild(a);return e}function g(e,f,a,b,d,g){if(f!==a&&(null!=f||null!=a))if(null==f)c(e,a,0,a.length,b,d,void 0);else if(null==a)k(f,0,f.length,a);else{for(var q=!1,w=0;w<a.length;w++)if(null!=a[w]){q=null==a[w].key;
|
0,e=E;return e}}function d(e){var f={caption:"table",thead:"table",tbody:"table",tfoot:"table",tr:"tbody",th:"tr",td:"tr",colgroup:"table",col:"colgroup"}[(e.children.match(/^\s*?<(\w+)/im)||[])[1]]||"div",f=B.createElement(f);f.innerHTML=e.children;e.dom=f.firstChild;e.domSize=f.childNodes.length;e=B.createDocumentFragment();for(var a;a=f.firstChild;)e.appendChild(a);return e}function g(e,f,a,b,d,g){if(f!==a&&(null!=f||null!=a))if(null==f)c(e,a,0,a.length,b,d,void 0);else if(null==a)r(f,0,f.length,
|
||||||
break}if(f.length===a.length&&q)for(w=0;w<f.length;w++)f[w]!==a[w]&&(null==f[w]?n(e,h(a[w],b,g),A(f,w+1,d)):null==a[w]?k(f,w,w+1,a):l(e,f[w],a[w],b,A(f,w+1,d),!1,g));else{a:{if(null!=f.pool&&Math.abs(f.pool.length-a.length)<=Math.abs(f.length-a.length)&&(q=a[0]&&a[0].children&&a[0].children.length||0,Math.abs((f.pool[0]&&f.pool[0].children&&f.pool[0].children.length||0)-q)<=Math.abs((f[0]&&f[0].children&&f[0].children.length||0)-q))){q=!0;break a}q=!1}q&&(f=f.concat(f.pool));for(var u=w=0,z=f.length-
|
a);else{for(var q=!1,k=0;k<a.length;k++)if(null!=a[k]){q=null==a[k].key;break}if(f.length===a.length&&q)for(k=0;k<f.length;k++)f[k]!==a[k]&&(null==f[k]?p(e,h(a[k],b,g),n(f,k+1,d)):null==a[k]?r(f,k,k+1,a):m(e,f[k],a[k],b,n(f,k+1,d),!1,g));else{a:{if(null!=f.pool&&Math.abs(f.pool.length-a.length)<=Math.abs(f.length-a.length)&&(q=a[0]&&a[0].children&&a[0].children.length||0,Math.abs((f.pool[0]&&f.pool[0].children&&f.pool[0].children.length||0)-q)<=Math.abs((f[0]&&f[0].children&&f[0].children.length||
|
||||||
1,v=a.length-1,D;z>=w&&v>=u;){var x=f[w],p=a[u];if(x!==p||q)if(null==x)w++;else if(null==p)u++;else if(x.key===p.key)w++,u++,l(e,x,p,b,A(f,w,d),q,g),q&&x.tag===p.tag&&n(e,m(x),d);else if(x=f[z],x!==p||q)if(null==x)z--;else if(null==p)u++;else if(x.key===p.key)l(e,x,p,b,A(f,z+1,d),q,g),(q||u<v)&&n(e,m(x),A(f,w,d)),z--,u++;else break;else z--,u++;else w++,u++}for(;z>=w&&v>=u;){x=f[z];p=a[v];if(x!==p||q)if(null==x)z--;else{if(null!=p)if(x.key===p.key)l(e,x,p,b,A(f,z+1,d),q,g),q&&x.tag===p.tag&&n(e,m(x),
|
0)-q))){q=!0;break a}q=!1}q&&(f=f.concat(f.pool));for(var A=k=0,y=f.length-1,t=a.length-1,D;y>=k&&t>=A;){var x=f[k],u=a[A];if(x!==u||q)if(null==x)k++;else if(null==u)A++;else if(x.key===u.key)k++,A++,m(e,x,u,b,n(f,k,d),q,g),q&&x.tag===u.tag&&p(e,l(x),d);else if(x=f[y],x!==u||q)if(null==x)y--;else if(null==u)A++;else if(x.key===u.key)m(e,x,u,b,n(f,y+1,d),q,g),(q||A<t)&&p(e,l(x),n(f,k,d)),y--,A++;else break;else y--,A++;else k++,A++}for(;y>=k&&t>=A;){x=f[y];u=a[t];if(x!==u||q)if(null==x)y--;else{if(null!=
|
||||||
d),null!=x.dom&&(d=x.dom),z--;else{if(!D){D=f;var x=z,r={},C;for(C=0;C<x;C++){var t=D[C];null!=t&&(t=t.key,null!=t&&(r[t]=C))}D=r}null!=p&&(x=D[p.key],null!=x?(r=f[x],l(e,r,p,b,A(f,z+1,d),q,g),n(e,m(r),d),f[x].skip=!0,null!=r.dom&&(d=r.dom)):(p=h(p,b,void 0),n(e,p,d),d=p))}v--}else z--,v--;if(v<u)break}c(e,a,u,v+1,b,d,g);k(f,w,z+1,a)}}}function l(a,f,b,c,k,z,v){var e=f.tag;if(e===b.tag){b.state=f.state;b.events=f.events;var q;var w;null!=b.attrs&&"function"===typeof b.attrs.onbeforeupdate&&(q=b.attrs.onbeforeupdate.call(b.state,
|
u)if(x.key===u.key)m(e,x,u,b,n(f,y+1,d),q,g),q&&x.tag===u.tag&&p(e,l(x),d),null!=x.dom&&(d=x.dom),y--;else{if(!D){D=f;var x=y,C={},w;for(w=0;w<x;w++){var v=D[w];null!=v&&(v=v.key,null!=v&&(C[v]=w))}D=C}null!=u&&(x=D[u.key],null!=x?(C=f[x],m(e,C,u,b,n(f,y+1,d),q,g),p(e,l(C),d),f[x].skip=!0,null!=C.dom&&(d=C.dom)):(u=h(u,b,void 0),p(e,u,d),d=u))}t--}else y--,t--;if(t<A)break}c(e,a,A,t+1,b,d,g);r(f,k,y+1,a)}}}function m(a,f,b,c,u,y,n){var e=f.tag;if(e===b.tag){b.state=f.state;b.events=f.events;var q;
|
||||||
b,f));"string"!==typeof b.tag&&"function"===typeof b.tag.onbeforeupdate&&(w=b.tag.onbeforeupdate.call(b.state,b,f));void 0===q&&void 0===w||q||w?q=!1:(b.dom=f.dom,b.domSize=f.domSize,b.instance=f.instance,q=!0);if(!q)if(null!=b.attrs&&y(b.attrs,b,c,z),"string"===typeof e)switch(e){case "#":f.children.toString()!==b.children.toString()&&(f.dom.nodeValue=b.children);b.dom=f.dom;break;case "<":f.children!==b.children?(m(f),n(a,d(b),k)):(b.dom=f.dom,b.domSize=f.domSize);break;case "[":g(a,f.children,
|
var A;null!=b.attrs&&"function"===typeof b.attrs.onbeforeupdate&&(q=b.attrs.onbeforeupdate.call(b.state,b,f));"string"!==typeof b.tag&&"function"===typeof b.tag.onbeforeupdate&&(A=b.tag.onbeforeupdate.call(b.state,b,f));void 0===q&&void 0===A||q||A?q=!1:(b.dom=f.dom,b.domSize=f.domSize,b.instance=f.instance,q=!0);if(!q)if(null!=b.attrs&&z(b.attrs,b,c,y),"string"===typeof e)switch(e){case "#":f.children.toString()!==b.children.toString()&&(f.dom.nodeValue=b.children);b.dom=f.dom;break;case "<":f.children!==
|
||||||
b.children,c,k,v);f=0;c=b.children;b.dom=null;if(null!=c){for(var p=0;p<c.length;p++)a=c[p],null!=a&&null!=a.dom&&(null==b.dom&&(b.dom=a.dom),f+=a.domSize||1);1!==f&&(b.domSize=f)}break;default:a=v;k=b.dom=f.dom;switch(b.tag){case "svg":a="http://www.w3.org/2000/svg";break;case "math":a="http://www.w3.org/1998/Math/MathML"}"textarea"===b.tag&&(null==b.attrs&&(b.attrs={}),null!=b.text&&(b.attrs.value=b.text,b.text=void 0));z=f.attrs;v=b.attrs;e=a;if(null!=v)for(p in v)u(b,p,z&&z[p],v[p],e);if(null!=
|
b.children?(l(f),p(a,d(b),u)):(b.dom=f.dom,b.domSize=f.domSize);break;case "[":g(a,f.children,b.children,c,u,n);f=0;c=b.children;b.dom=null;if(null!=c){for(var r=0;r<c.length;r++)a=c[r],null!=a&&null!=a.dom&&(null==b.dom&&(b.dom=a.dom),f+=a.domSize||1);1!==f&&(b.domSize=f)}break;default:a=n;u=b.dom=f.dom;switch(b.tag){case "svg":a="http://www.w3.org/2000/svg";break;case "math":a="http://www.w3.org/1998/Math/MathML"}"textarea"===b.tag&&(null==b.attrs&&(b.attrs={}),null!=b.text&&(b.attrs.value=b.text,
|
||||||
z)for(p in z)null!=v&&p in v||("className"===p&&(p="class"),"o"!==p[0]||"n"!==p[1]||D(p)?"key"!==p&&b.dom.removeAttribute(p):C(b,p,void 0));null!=b.attrs&&null!=b.attrs.contenteditable?G(b):null!=f.text&&null!=b.text&&""!==b.text?f.text.toString()!==b.text.toString()&&(f.dom.firstChild.nodeValue=b.text):(null!=f.text&&(f.children=[r("#",void 0,void 0,f.text,void 0,f.dom.firstChild)]),null!=b.text&&(b.children=[r("#",void 0,void 0,b.text,void 0,void 0)]),g(k,f.children,b.children,c,null,a))}else b.instance=
|
b.text=void 0));y=f.attrs;n=b.attrs;e=a;if(null!=n)for(r in n)t(b,r,y&&y[r],n[r],e);if(null!=y)for(r in y)null!=n&&r in n||("className"===r&&(r="class"),"o"!==r[0]||"n"!==r[1]||v(r)?"key"!==r&&b.dom.removeAttribute(r):G(b,r,void 0));null!=b.attrs&&null!=b.attrs.contenteditable?k(b):null!=f.text&&null!=b.text&&""!==b.text?f.text.toString()!==b.text.toString()&&(f.dom.firstChild.nodeValue=b.text):(null!=f.text&&(f.children=[w("#",void 0,void 0,f.text,void 0,f.dom.firstChild)]),null!=b.text&&(b.children=
|
||||||
r.normalize(b.tag.view.call(b.state,b)),y(b.tag,b,c,z),null!=b.instance?(null==f.instance?n(a,h(b.instance,c,v),k):l(a,f.instance,b.instance,c,k,z,v),b.dom=b.instance.dom,b.domSize=b.instance.domSize):null!=f.instance?(t(f.instance,null),b.dom=void 0,b.domSize=0):(b.dom=f.dom,b.domSize=f.domSize)}else t(f,null),n(a,h(b,c,v),k)}function m(b){var a=b.domSize;if(null!=a||null==b.dom){var e=B.createDocumentFragment();if(0<a){for(b=b.dom;--a;)e.appendChild(b.nextSibling);e.insertBefore(b,e.firstChild)}return e}return b.dom}
|
[w("#",void 0,void 0,b.text,void 0,void 0)]),g(u,f.children,b.children,c,null,a))}else b.instance=w.normalize(b.tag.view.call(b.state,b)),z(b.tag,b,c,y),null!=b.instance?(null==f.instance?p(a,h(b.instance,c,n),u):m(a,f.instance,b.instance,c,u,y,n),b.dom=b.instance.dom,b.domSize=b.instance.domSize):null!=f.instance?(D(f.instance,null),b.dom=void 0,b.domSize=0):(b.dom=f.dom,b.domSize=f.domSize)}else D(f,null),p(a,h(b,c,n),u)}function l(b){var a=b.domSize;if(null!=a||null==b.dom){var e=B.createDocumentFragment();
|
||||||
function A(b,a,c){for(;a<b.length;a++)if(null!=b[a]&&null!=b[a].dom)return b[a].dom;return c}function n(b,a,c){c&&c.parentNode?b.insertBefore(a,c):b.appendChild(a)}function G(b){var a=b.children;if(null!=a&&1===a.length&&"<"===a[0].tag)a=a[0].children,b.dom.innerHTML!==a&&(b.dom.innerHTML=a);else if(null!=b.text||null!=a&&0!==a.length)throw Error("Child node of a contenteditable must be trusted");}function k(b,a,c,d){for(;a<c;a++){var e=b[a];null!=e&&(e.skip?e.skip=!1:t(e,d))}}function b(b){var a=
|
if(0<a){for(b=b.dom;--a;)e.appendChild(b.nextSibling);e.insertBefore(b,e.firstChild)}return e}return b.dom}function n(b,a,c){for(;a<b.length;a++)if(null!=b[a]&&null!=b[a].dom)return b[a].dom;return c}function p(b,a,c){c&&c.parentNode?b.insertBefore(a,c):b.appendChild(a)}function k(b){var a=b.children;if(null!=a&&1===a.length&&"<"===a[0].tag)a=a[0].children,b.dom.innerHTML!==a&&(b.dom.innerHTML=a);else if(null!=b.text||null!=a&&0!==a.length)throw Error("Child node of a contenteditable must be trusted");
|
||||||
!1;return function(){a||(a=!0,b())}}function t(a,f){function e(){if(++d===c&&(v(a),a.dom)){var b=a.domSize||1;if(1<b)for(var e=a.dom;--b;){var g=e.nextSibling,k=g.parentNode;null!=k&&k.removeChild(g)}b=a.dom;e=b.parentNode;null!=e&&e.removeChild(b);if(b=null!=f&&null==a.domSize)b=a.attrs,b=!(null!=b&&(b.oncreate||b.onupdate||b.onbeforeremove||b.onremove));b&&"string"===typeof a.tag&&(f.pool?f.pool.push(a):f.pool=[a])}}var c=1,d=0;a.attrs&&a.attrs.onbeforeremove&&(c++,a.attrs.onbeforeremove.call(a.state,
|
}function r(b,a,c,d){for(;a<c;a++){var e=b[a];null!=e&&(e.skip?e.skip=!1:D(e,d))}}function b(b){var a=!1;return function(){a||(a=!0,b())}}function D(a,f){function e(){if(++d===c&&(C(a),a.dom)){var b=a.domSize||1;if(1<b)for(var e=a.dom;--b;){var g=e.nextSibling,k=g.parentNode;null!=k&&k.removeChild(g)}b=a.dom;e=b.parentNode;null!=e&&e.removeChild(b);if(b=null!=f&&null==a.domSize)b=a.attrs,b=!(null!=b&&(b.oncreate||b.onupdate||b.onbeforeremove||b.onremove));b&&"string"===typeof a.tag&&(f.pool?f.pool.push(a):
|
||||||
a,b(e)));"string"!==typeof a.tag&&a.tag.onbeforeremove&&(c++,a.tag.onbeforeremove.call(a.state,a,b(e)));e()}function v(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)v(a.instance);else if(a=a.children,Array.isArray(a))for(var b=0;b<a.length;b++){var e=a[b];null!=e&&v(e)}}function u(a,b,c,d,g){var e=a.dom;if("key"!==b&&(c!==d||"value"===b||"checked"===b||"selectedIndex"===b||"selected"===b&&
|
f.pool=[a])}}var c=1,d=0;a.attrs&&a.attrs.onbeforeremove&&(c++,a.attrs.onbeforeremove.call(a.state,a,b(e)));"string"!==typeof a.tag&&a.tag.onbeforeremove&&(c++,a.tag.onbeforeremove.call(a.state,a,b(e)));e()}function C(b){b.attrs&&b.attrs.onremove&&b.attrs.onremove.call(b.state,b);"string"!==typeof b.tag&&b.tag.onremove&&b.tag.onremove.call(b.state,b);if(null!=b.instance)C(b.instance);else if(b=b.children,b instanceof Array)for(var a=0;a<b.length;a++){var e=b[a];null!=e&&C(e)}}function t(b,a,c,d,g){var e=
|
||||||
a.dom===B.activeElement||"object"===typeof d)&&"undefined"!==typeof d&&!D(b)){var f=b.indexOf(":");if(-1<f&&"xlink"===b.substr(0,f))e.setAttributeNS("http://www.w3.org/1999/xlink",b.slice(f+1),d);else if("o"===b[0]&&"n"===b[1]&&"function"===typeof d)C(a,b,d);else if("style"===b)if(a=c,a===d&&(e.style.cssText="",a=null),null==d)e.style.cssText="";else if("string"===typeof d)e.style.cssText=d;else{"string"===typeof a&&(e.style.cssText="");for(var k in d)e.style[k]=d[k];if(null!=a&&"string"!==typeof a)for(k in a)k in
|
b.dom;if("key"!==a&&(c!==d||"value"===a||"checked"===a||"selectedIndex"===a||"selected"===a&&b.dom===B.activeElement||"object"===typeof d)&&"undefined"!==typeof d&&!v(a)){var f=a.indexOf(":");if(-1<f&&"xlink"===a.substr(0,f))e.setAttributeNS("http://www.w3.org/1999/xlink",a.slice(f+1),d);else if("o"===a[0]&&"n"===a[1]&&"function"===typeof d)G(b,a,d);else if("style"===a)if(b=c,b===d&&(e.style.cssText="",b=null),null==d)e.style.cssText="";else if("string"===typeof d)e.style.cssText=d;else{"string"===
|
||||||
d||(e.style[k]="")}else b in e&&"href"!==b&&"list"!==b&&"form"!==b&&"width"!==b&&"height"!==b&&void 0===g?"input"===a.tag&&"value"===b&&a.dom.value===d&&a.dom===B.activeElement||"select"===a.tag&&"value"===b&&a.dom.value===d&&a.dom===B.activeElement||"option"===a.tag&&"value"===b&&a.dom.value===d||(e[b]=d):"boolean"===typeof d?d?e.setAttribute(b,""):e.removeAttribute(b):e.setAttribute("className"===b?"class":b,d)}}function D(a){return"oninit"===a||"oncreate"===a||"onupdate"===a||"onremove"===a||"onbeforeremove"===
|
typeof b&&(e.style.cssText="");for(var k in d)e.style[k]=d[k];if(null!=b&&"string"!==typeof b)for(k in b)k in d||(e.style[k]="")}else a in e&&"href"!==a&&"list"!==a&&"form"!==a&&"width"!==a&&"height"!==a&&void 0===g?"input"===b.tag&&"value"===a&&b.dom.value===d&&b.dom===B.activeElement||"select"===b.tag&&"value"===a&&b.dom.value===d&&b.dom===B.activeElement||"option"===b.tag&&"value"===a&&b.dom.value===d||(e[a]=d):"boolean"===typeof d?d?e.setAttribute(a,""):e.removeAttribute(a):e.setAttribute("className"===
|
||||||
a||"onbeforeupdate"===a}function C(a,b,c){var d=a.dom,e="function"!==typeof F?c:function(a){var b=c.call(d,a);F.call(d,a);return b};if(b in d)d[b]="function"===typeof c?e:null;else{var f=b.slice(2);void 0===a.events&&(a.events={});a.events[b]!==e&&(null!=a.events[b]&&d.removeEventListener(f,a.events[b],!1),"function"===typeof c&&(a.events[b]=e,d.addEventListener(f,a.events[b],!1)))}}function J(a,b,c){"function"===typeof a.oninit&&a.oninit.call(b.state,b);"function"===typeof a.oncreate&&c.push(a.oncreate.bind(b.state,
|
a?"class":a,d)}}function v(b){return"oninit"===b||"oncreate"===b||"onupdate"===b||"onremove"===b||"onbeforeremove"===b||"onbeforeupdate"===b}function G(b,a,c){var d=b.dom,e="function"!==typeof F?c:function(b){var a=c.call(d,b);F.call(d,b);return a};if(a in d)d[a]="function"===typeof c?e:null;else{var f=a.slice(2);void 0===b.events&&(b.events={});b.events[a]!==e&&(null!=b.events[a]&&d.removeEventListener(f,b.events[a],!1),"function"===typeof c&&(b.events[a]=e,d.addEventListener(f,b.events[a],!1)))}}
|
||||||
b))}function y(a,b,c,d){d?J(a,b,c):"function"===typeof a.onupdate&&c.push(a.onupdate.bind(b.state,b))}function E(a,b){Object.keys(b).forEach(function(c){a[c]=b[c]})}var B=a.document,N=B.createDocumentFragment(),F;return{render:function(a,b){if(!a)throw Error("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.");var c=[],d=B.activeElement;null==a.vnodes&&(a.textContent="");Array.isArray(b)||(b=[b]);g(a,a.vnodes,r.normalizeChildren(b),c,null,void 0);a.vnodes=b;for(var e=
|
function L(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 z(b,a,c,d){d?L(b,a,c):"function"===typeof b.onupdate&&c.push(b.onupdate.bind(a.state,a))}function R(b,a){Object.keys(a).forEach(function(c){b[c]=a[c]})}var B=a.document,E=B.createDocumentFragment(),F;return{render:function(b,a){if(!b)throw Error("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.");var c=[],d=B.activeElement;
|
||||||
0;e<c.length;e++)c[e]();B.activeElement!==d&&d.focus()},setEventCallback:function(a){return F=a}}},H=function(a){function c(a){a=d.indexOf(a);-1<a&&d.splice(a,2)}function h(){for(var a=1;a<d.length;a+=2)d[a]()}a=O(a);a.setEventCallback(function(a){!1!==a.redraw&&h()});var d=[];return{subscribe:function(a,h){c(a);d.push(a,Q(h))},unsubscribe:c,redraw:h,render:a.render}}(window);K.setCompletionCallback(H.redraw);y.mount=function(a){return function(c,h){if(null===h)a.render(c,[]),a.unsubscribe(c);else{if(null==
|
null==b.vnodes&&(b.textContent="");a instanceof Array||(a=[a]);g(b,b.vnodes,w.normalizeChildren(a),c,null,void 0);b.vnodes=a;for(var e=0;e<c.length;e++)c[e]();B.activeElement!==d&&d.focus()},setEventCallback:function(b){return F=b}}},J=function(a){function c(a){a=d.indexOf(a);-1<a&&d.splice(a,2)}function h(){for(var a=1;a<d.length;a+=2)d[a]()}a=O(a);a.setEventCallback(function(a){!1!==a.redraw&&h()});var d=[];return{subscribe:function(a,h){c(a);d.push(a,Q(h))},unsubscribe:c,redraw:h,render:a.render}}(window);
|
||||||
h.view)throw Error("m.mount(element, component) expects a component, not a vnode");a.subscribe(c,function(){a.render(c,r(h))});a.redraw()}}}(H);var L=function(a){if(""===a||null==a)return{};"?"===a.charAt(0)&&(a=a.slice(1));a=a.split("&");for(var c={},h={},d=0;d<a.length;d++){var g=a[d].split("="),l=decodeURIComponent(g[0]),g=2===g.length?decodeURIComponent(g[1]):"";"true"===g?g=!0:"false"===g&&(g=!1);var m=l.split(/\]\[?|\[/),r=c;-1<l.indexOf("[")&&m.pop();for(var n=0;n<m.length;n++){var l=m[n],
|
M.setCompletionCallback(J.redraw);z.mount=function(a){return function(c,h){if(null===h)a.render(c,[]),a.unsubscribe(c);else{if(null==h.view)throw Error("m.mount(element, component) expects a component, not a vnode");a.subscribe(c,function(){a.render(c,w(h))});a.redraw()}}}(J);var E=H,N=function(a){if(""===a||null==a)return{};"?"===a.charAt(0)&&(a=a.slice(1));a=a.split("&");for(var c={},h={},d=0;d<a.length;d++){var g=a[d].split("="),m=decodeURIComponent(g[0]),g=2===g.length?decodeURIComponent(g[1]):
|
||||||
t=m[n+1],t=""==t||!isNaN(parseInt(t,10)),k=n===m.length-1;""===l&&(l=m.slice(0,n).join(),null==h[l]&&(h[l]=0),l=h[l]++);null==r[l]&&(r[l]=k?g:t?[]:{});r=r[l]}}return c},R=function(a){function c(c){var b=a.location[c].replace(/(?:%[a-f89][a-f0-9])+/gim,decodeURIComponent);"pathname"===c&&"/"!==b[0]&&(b="/"+b);return b}function h(a){return function(){null==t&&(t=r(function(){t=null;a()}))}}function d(a,b,c){var d=a.indexOf("?"),g=a.indexOf("#"),k=-1<d?d:-1<g?g:a.length;if(-1<d){var d=L(a.slice(d+1,
|
"";"true"===g?g=!0:"false"===g&&(g=!1);var l=m.split(/\]\[?|\[/),n=c;-1<m.indexOf("[")&&l.pop();for(var p=0;p<l.length;p++){var m=l[p],k=l[p+1],k=""==k||!isNaN(parseInt(k,10)),r=p===l.length-1;""===m&&(m=l.slice(0,p).join(),null==h[m]&&(h[m]=0),m=h[m]++);null==n[m]&&(n[m]=r?g:k?[]:{});n=n[m]}}return c},S=function(a){function c(c){var d=a.location[c].replace(/(?:%[a-f89][a-f0-9])+/gim,decodeURIComponent);"pathname"===c&&"/"!==d[0]&&(d="/"+d);return d}function h(a){return function(){null==l&&(l=m(function(){l=
|
||||||
-1<g?g:a.length)),h;for(h in d)b[h]=d[h]}if(-1<g)for(h in b=L(a.slice(g+1)),b)c[h]=b[h];return a.slice(0,k)}function g(){switch(n.charAt(0)){case "#":return c("hash").slice(n.length);case "?":return c("search").slice(n.length)+c("hash");default:return c("pathname").slice(n.length)+c("search")+c("hash")}}function l(c,b,g){var k={},h={};c=d(c,k,h);if(null!=b){for(var l in b)k[l]=b[l];c=c.replace(/:([^\/]+)/g,function(a,c){delete k[c];return b[c]})}(l=E(k))&&(c+="?"+l);(h=E(h))&&(c+="#"+h);m?(g&&g.replace?
|
null;a()}))}}function d(a,c,d){var b=a.indexOf("?"),g=a.indexOf("#"),k=-1<b?b:-1<g?g:a.length;if(-1<b){var b=N(a.slice(b+1,-1<g?g:a.length)),h;for(h in b)c[h]=b[h]}if(-1<g)for(h in c=N(a.slice(g+1)),c)d[h]=c[h];return a.slice(0,k)}var g="function"===typeof a.history.pushState,m="function"===typeof setImmediate?setImmediate:setTimeout,l,n={prefix:"#!",getPath:function(){switch(n.prefix.charAt(0)){case "#":return c("hash").slice(n.prefix.length);case "?":return c("search").slice(n.prefix.length)+c("hash");
|
||||||
a.history.replaceState(null,null,n+c):a.history.pushState(null,null,n+c),a.onpopstate(!0)):a.location.href=n+c}var m="function"===typeof a.history.pushState,r="function"===typeof setImmediate?setImmediate:setTimeout,n="#!",t;return{setPrefix:function(a){n=a},getPath:g,setPath:l,defineRoutes:function(c,b,l){function k(){var a=g(),h={},k=d(a,h,h),m;for(m in c){var n=new RegExp("^"+m.replace(/:[^\/]+?\.{3}/g,"(.*?)").replace(/:[^\/]+/g,"([^\\/]+)")+"/?$");if(n.test(k)){k.replace(n,function(){for(var d=
|
default:return c("pathname").slice(n.prefix.length)+c("search")+c("hash")}},setPath:function(c,k,h){var b={},l={};c=d(c,b,l);if(null!=k){for(var m in k)b[m]=k[m];c=c.replace(/:([^\/]+)/g,function(a,c){delete b[c];return k[c]})}(m=I(b))&&(c+="?"+m);(l=I(l))&&(c+="#"+l);g?(h&&h.replace?a.history.replaceState(null,null,n.prefix+c):a.history.pushState(null,null,n.prefix+c),a.onpopstate()):a.location.href=n.prefix+c},defineRoutes:function(c,k,l){function b(){var a=n.getPath(),b={},g=d(a,b,b),h;for(h in c){var m=
|
||||||
m.match(/:[^\/]+/g)||[],g=[].slice.call(arguments,1,-2),k=0;k<d.length;k++)h[d[k].replace(/:|\./g,"")]=decodeURIComponent(g[k]);b(c[m],h,a,m)});return}}l(a,h)}m?a.onpopstate=h(k):"#"===n.charAt(0)&&(a.onhashchange=k);k()},link:function(a){a.dom.setAttribute("href",n+a.attrs.href);a.dom.onclick=function(a){a.ctrlKey||a.metaKey||a.shiftKey||2===a.which||(a.preventDefault(),a.redraw=!1,a=this.getAttribute("href"),0===a.indexOf(n)&&(a=a.slice(n.length)),l(a,void 0,void 0))}}}};y.route=function(a,c){var h=
|
new RegExp("^"+h.replace(/:[^\/]+?\.{3}/g,"(.*?)").replace(/:[^\/]+/g,"([^\\/]+)")+"/?$");if(m.test(g)){g.replace(m,function(){for(var d=h.match(/:[^\/]+/g)||[],g=[].slice.call(arguments,1,-2),l=0;l<d.length;l++)b[d[l].replace(/:|\./g,"")]=decodeURIComponent(g[l]);k(c[h],b,a,h)});return}}l(a,b)}g?a.onpopstate=h(b):"#"===n.prefix.charAt(0)&&(a.onhashchange=b);b()}};return n};z.route=function(a,c){var h=S(a),d=function(a){return a},g,m,l,n,p=!1,k=function(a,b,k){if(null==a)throw Error("Ensure the DOM element that was passed to `m.route` is not undefined");
|
||||||
R(a),d=function(a){return a},g,l,m,t,n,y=function(a,b,y){if(null==a)throw Error("Ensure the DOM element that was passed to `m.route` is not undefined");var k=function(a,b,c,h){g=a;l=b;m=c;t=h;n=null;g.render=a.render||d;u()},u=function(){null!=g&&c.render(a,g.render(r(l,m.key,m)))};h.defineRoutes(y,function(a,b,c){a.view?k({},a,b,c):a.onmatch?null!=n?k(a,l,b,c):(n=function(d){k(a,d,b,c)},a.onmatch(function(a){null!=n&&n(a)},b,c)):k(a,"div",b,c)},function(){h.setPath(b)});c.subscribe(a,u)};y.set=h.setPath;
|
var r=function(a,b,c,h){m=null!=b&&"function"===typeof b.view?b:"div";l=c;n=h;p=!1;g=(a.render||d).bind(a);t()},t=function(){null!=g&&c.render(a,g(w(m,l.key,l)))},v=function(){h.setPath(b)};h.defineRoutes(k,function(a,b,c){a.view?r({},a,b,c):a.onmatch?(p=!0,E.resolve(a.onmatch(b,c)).then(function(d){p&&r(a,d,b,c)},v)):r(a,"div",b,c)},v);c.subscribe(a,t)};k.set=function(a,b,c){p&&(c={replace:!0});p=!1;h.setPath(a,b,c)};k.get=function(){return n};k.prefix=function(a){h.prefix=a};k.link=function(a){a.dom.setAttribute("href",
|
||||||
y.get=function(){return t};y.prefix=h.setPrefix;y.link=h.link;return y}(window,H);y.withAttr=function(a,c,h){return function(d){return c.call(h||this,a in d.currentTarget?d.currentTarget[a]:d.currentTarget.getAttribute(a))}};var S=O(window);y.render=S.render;y.redraw=H.redraw;y.request=K.request;y.jsonp=K.jsonp;y.parseQueryString=L;y.buildQueryString=E;y.version="1.0.0-rc.6";"undefined"!==typeof module?module.exports=y:window.m=y};
|
h.prefix+a.attrs.href);a.dom.onclick=function(a){a.ctrlKey||a.metaKey||a.shiftKey||2===a.which||(a.preventDefault(),a.redraw=!1,a=this.getAttribute("href"),0===a.indexOf(h.prefix)&&(a=a.slice(h.prefix.length)),k.set(a,void 0,void 0))}};return k}(window,J);z.withAttr=function(a,c,h){return function(d){return c.call(h||this,a in d.currentTarget?d.currentTarget[a]:d.currentTarget.getAttribute(a))}};H=O(window);z.render=H.render;z.redraw=J.redraw;z.request=M.request;z.jsonp=M.jsonp;z.parseQueryString=
|
||||||
|
N;z.buildQueryString=I;z.version="1.0.0-rc.6";"undefined"!==typeof module?module.exports=z:window.m=z};
|
||||||
|
|
@ -202,7 +202,10 @@ module.exports = new function init() {
|
||||||
status = 1
|
status = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(results.length + " assertions completed in " + Math.round(new Date - start) + "ms")
|
console.log(
|
||||||
|
results.length + " assertions completed in " + Math.round(new Date - start) + "ms, " +
|
||||||
|
"of which " + results.filter(function(result){return result.error}).length + " failed"
|
||||||
|
)
|
||||||
if (hasProcess && status === 1) process.exit(1)
|
if (hasProcess && status === 1) process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -403,6 +403,27 @@ o.spec("promise", function() {
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
o("triggers all branched rejection handlers upon rejection", function(done) {
|
||||||
|
var promise = Promise.reject()
|
||||||
|
var then = o.spy()
|
||||||
|
var catch1 = o.spy()
|
||||||
|
var catch2 = o.spy()
|
||||||
|
var catch3 = o.spy()
|
||||||
|
|
||||||
|
promise.catch(catch1)
|
||||||
|
promise.then(then, catch2)
|
||||||
|
promise.then(then).catch(catch3)
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
o(catch1.callCount).equals(1)
|
||||||
|
o(then.callCount).equals(0)
|
||||||
|
o(catch2.callCount).equals(1)
|
||||||
|
o(catch3.callCount).equals(1)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
o("does not absorb resolved promise via static rejector", function(done) {
|
o("does not absorb resolved promise via static rejector", function(done) {
|
||||||
var promise = Promise.reject(Promise.resolve(1))
|
var promise = Promise.reject(Promise.resolve(1))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ module.exports = function($window, Promise) {
|
||||||
var next = then.apply(promise, arguments)
|
var next = then.apply(promise, arguments)
|
||||||
next.then(complete, function(e) {
|
next.then(complete, function(e) {
|
||||||
complete()
|
complete()
|
||||||
throw e
|
if (count === 0) throw e
|
||||||
})
|
})
|
||||||
return finalize(next)
|
return finalize(next)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
<body>
|
<body>
|
||||||
<script src="../../module/module.js"></script>
|
<script src="../../module/module.js"></script>
|
||||||
<script src="../../ospec/ospec.js"></script>
|
<script src="../../ospec/ospec.js"></script>
|
||||||
|
<script src="../../test-utils/callAsync.js"></script>
|
||||||
<script src="../../querystring/parse.js"></script>
|
<script src="../../querystring/parse.js"></script>
|
||||||
<script src="../../test-utils/parseURL.js"></script>
|
<script src="../../test-utils/parseURL.js"></script>
|
||||||
<script src="../../test-utils/callAsync.js"></script>
|
<script src="../../test-utils/callAsync.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
var o = require("../../ospec/ospec")
|
var o = require("../../ospec/ospec")
|
||||||
|
var callAsync = require("../../test-utils/callAsync")
|
||||||
var xhrMock = require("../../test-utils/xhrMock")
|
var xhrMock = require("../../test-utils/xhrMock")
|
||||||
var Request = require("../../request/request")
|
var Request = require("../../request/request")
|
||||||
var Promise = require("../../promise/promise")
|
var Promise = require("../../promise/promise")
|
||||||
|
|
@ -336,7 +337,7 @@ o.spec("xhr", function() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
var promise = xhr("/item", {background: true}).then(function() {})
|
var promise = xhr("/item", {background: true}).then(function() {})
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
o(complete.callCount).equals(0)
|
o(complete.callCount).equals(0)
|
||||||
done()
|
done()
|
||||||
|
|
@ -377,5 +378,31 @@ o.spec("xhr", function() {
|
||||||
o(e.message).equals("error")
|
o(e.message).equals("error")
|
||||||
}).then(done)
|
}).then(done)
|
||||||
})
|
})
|
||||||
|
o("triggers all branched catches upon rejection", function(done) {
|
||||||
|
mock.$defineRoutes({
|
||||||
|
"GET /item": function(request) {
|
||||||
|
return {status: 500, responseText: "error"}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
var request = xhr({method: "GET", url: "/item"})
|
||||||
|
var then = o.spy()
|
||||||
|
var catch1 = o.spy()
|
||||||
|
var catch2 = o.spy()
|
||||||
|
var catch3 = o.spy()
|
||||||
|
|
||||||
|
request.catch(catch1)
|
||||||
|
request.then(then, catch2)
|
||||||
|
request.then(then).catch(catch3)
|
||||||
|
|
||||||
|
callAsync(function() {
|
||||||
|
callAsync(function() {
|
||||||
|
o(catch1.callCount).equals(1)
|
||||||
|
o(then.callCount).equals(0)
|
||||||
|
o(catch2.callCount).equals(1)
|
||||||
|
o(catch3.callCount).equals(1)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,6 @@ module.exports = 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
|
||||||
|
|
||||||
var prefix = "#!"
|
|
||||||
function setPrefix(value) {prefix = value}
|
|
||||||
|
|
||||||
function normalize(fragment) {
|
function normalize(fragment) {
|
||||||
var data = $window.location[fragment].replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent)
|
var data = $window.location[fragment].replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent)
|
||||||
if (fragment === "pathname" && data[0] !== "/") data = "/" + data
|
if (fragment === "pathname" && data[0] !== "/") data = "/" + data
|
||||||
|
|
@ -17,14 +14,13 @@ module.exports = function($window) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var asyncId
|
var asyncId
|
||||||
function debounceAsync(f) {
|
function debounceAsync(callback) {
|
||||||
return function() {
|
return function() {
|
||||||
if (asyncId != null) return
|
if (asyncId != null) return
|
||||||
asyncId = callAsync(function() {
|
asyncId = callAsync(function() {
|
||||||
asyncId = null
|
asyncId = null
|
||||||
f()
|
callback()
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,16 +40,16 @@ module.exports = function($window) {
|
||||||
return path.slice(0, pathEnd)
|
return path.slice(0, pathEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPath() {
|
var router = {prefix: "#!"}
|
||||||
var type = prefix.charAt(0)
|
router.getPath = function() {
|
||||||
|
var type = router.prefix.charAt(0)
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "#": return normalize("hash").slice(prefix.length)
|
case "#": return normalize("hash").slice(router.prefix.length)
|
||||||
case "?": return normalize("search").slice(prefix.length) + normalize("hash")
|
case "?": return normalize("search").slice(router.prefix.length) + normalize("hash")
|
||||||
default: return normalize("pathname").slice(prefix.length) + normalize("search") + normalize("hash")
|
default: return normalize("pathname").slice(router.prefix.length) + normalize("search") + normalize("hash")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
router.setPath = function(path, data, options) {
|
||||||
function setPath(path, data, options) {
|
|
||||||
var queryData = {}, hashData = {}
|
var queryData = {}, hashData = {}
|
||||||
path = parsePath(path, queryData, hashData)
|
path = parsePath(path, queryData, hashData)
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
|
|
@ -71,16 +67,15 @@ module.exports = function($window) {
|
||||||
if (hash) path += "#" + hash
|
if (hash) path += "#" + hash
|
||||||
|
|
||||||
if (supportsPushState) {
|
if (supportsPushState) {
|
||||||
if (options && options.replace) $window.history.replaceState(null, null, prefix + path)
|
if (options && options.replace) $window.history.replaceState(null, null, router.prefix + path)
|
||||||
else $window.history.pushState(null, null, prefix + path)
|
else $window.history.pushState(null, null, router.prefix + path)
|
||||||
$window.onpopstate(true)
|
$window.onpopstate()
|
||||||
}
|
}
|
||||||
else $window.location.href = prefix + path
|
else $window.location.href = router.prefix + path
|
||||||
}
|
}
|
||||||
|
router.defineRoutes = function(routes, resolve, reject) {
|
||||||
function defineRoutes(routes, resolve, reject) {
|
|
||||||
function resolveRoute() {
|
function resolveRoute() {
|
||||||
var path = getPath()
|
var path = router.getPath()
|
||||||
var params = {}
|
var params = {}
|
||||||
var pathname = parsePath(path, params, params)
|
var pathname = parsePath(path, params, params)
|
||||||
|
|
||||||
|
|
@ -104,21 +99,9 @@ module.exports = function($window) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute)
|
if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute)
|
||||||
else if (prefix.charAt(0) === "#") $window.onhashchange = resolveRoute
|
else if (router.prefix.charAt(0) === "#") $window.onhashchange = resolveRoute
|
||||||
resolveRoute()
|
resolveRoute()
|
||||||
}
|
}
|
||||||
|
|
||||||
function link(vnode) {
|
return router
|
||||||
vnode.dom.setAttribute("href", prefix + vnode.attrs.href)
|
|
||||||
vnode.dom.onclick = function(e) {
|
|
||||||
if (e.ctrlKey || e.metaKey || e.shiftKey || e.which === 2) return
|
|
||||||
e.preventDefault()
|
|
||||||
e.redraw = false
|
|
||||||
var href = this.getAttribute("href")
|
|
||||||
if (href.indexOf(prefix) === 0) href = href.slice(prefix.length)
|
|
||||||
setPath(href, undefined, undefined)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {setPrefix: setPrefix, getPath: getPath, setPath: setPath, defineRoutes: defineRoutes, link: link}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
<script src="../../test-utils/pushStateMock.js"></script>
|
<script src="../../test-utils/pushStateMock.js"></script>
|
||||||
<script src="../../test-utils/domMock.js"></script>
|
<script src="../../test-utils/domMock.js"></script>
|
||||||
|
|
||||||
|
<script src="../../promise/promise.js"></script>
|
||||||
<script src="../../render/vnode.js"></script>
|
<script src="../../render/vnode.js"></script>
|
||||||
<script src="../../render/render.js"></script>
|
<script src="../../render/render.js"></script>
|
||||||
<script src="../../querystring/build.js"></script>
|
<script src="../../querystring/build.js"></script>
|
||||||
|
|
@ -19,7 +20,6 @@
|
||||||
<script src="test-defineRoutes.js"></script>
|
<script src="test-defineRoutes.js"></script>
|
||||||
<script src="test-getPath.js"></script>
|
<script src="test-getPath.js"></script>
|
||||||
<script src="test-setPath.js"></script>
|
<script src="test-setPath.js"></script>
|
||||||
<script src="test-link.js"></script>
|
|
||||||
|
|
||||||
<script>require("../../ospec/ospec").run()</script>
|
<script>require("../../ospec/ospec").run()</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ o.spec("Router.defineRoutes", function() {
|
||||||
o.beforeEach(function() {
|
o.beforeEach(function() {
|
||||||
$window = pushStateMock(env)
|
$window = pushStateMock(env)
|
||||||
router = new Router($window)
|
router = new Router($window)
|
||||||
router.setPrefix(prefix)
|
router.prefix = prefix
|
||||||
onRouteChange = o.spy()
|
onRouteChange = o.spy()
|
||||||
onFail = o.spy()
|
onFail = o.spy()
|
||||||
})
|
})
|
||||||
|
|
@ -73,7 +73,7 @@ o.spec("Router.defineRoutes", function() {
|
||||||
$window.location.href = "file://" + prefix + "/test"
|
$window.location.href = "file://" + prefix + "/test"
|
||||||
|
|
||||||
router = new Router($window)
|
router = new Router($window)
|
||||||
router.setPrefix(prefix)
|
router.prefix = prefix
|
||||||
|
|
||||||
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
|
router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ o.spec("Router.getPath", function() {
|
||||||
o.beforeEach(function() {
|
o.beforeEach(function() {
|
||||||
$window = pushStateMock(env)
|
$window = pushStateMock(env)
|
||||||
router = new Router($window)
|
router = new Router($window)
|
||||||
router.setPrefix(prefix)
|
router.prefix = prefix
|
||||||
onRouteChange = o.spy()
|
onRouteChange = o.spy()
|
||||||
onFail = o.spy()
|
onFail = o.spy()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
"use strict"
|
|
||||||
|
|
||||||
var o = require("../../ospec/ospec")
|
|
||||||
var renderService = require("../../render/render")
|
|
||||||
var callAsync = require("../../test-utils/callAsync")
|
|
||||||
var pushStateMock = require("../../test-utils/pushStateMock")
|
|
||||||
var domMock = require("../../test-utils/domMock")
|
|
||||||
var Router = require("../../router/router")
|
|
||||||
|
|
||||||
o.spec("Router.link", function() {
|
|
||||||
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, dom, root, router, onRouteChange, onFail, render
|
|
||||||
|
|
||||||
o.beforeEach(function() {
|
|
||||||
$window = pushStateMock(env)
|
|
||||||
dom = domMock()
|
|
||||||
root = dom.document.body
|
|
||||||
router = new Router($window)
|
|
||||||
router.setPrefix(prefix)
|
|
||||||
onRouteChange = o.spy()
|
|
||||||
onFail = o.spy()
|
|
||||||
render = renderService(dom).render
|
|
||||||
})
|
|
||||||
|
|
||||||
o("works", function(done) {
|
|
||||||
var A = {
|
|
||||||
view: function() {
|
|
||||||
return {tag: "a", attrs: {href: "/b", oncreate: router.link}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var B = {
|
|
||||||
view: function() {
|
|
||||||
return {tag: "a", attrs: {href: "/a", oncreate: router.link}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$window.location.href = prefix + "/a"
|
|
||||||
router.defineRoutes({"/a": {tag: A}, "/b": {tag: B}}, function(component) {
|
|
||||||
render(root, component)
|
|
||||||
})
|
|
||||||
|
|
||||||
callAsync(function() {
|
|
||||||
var e = dom.document.createEvent("MouseEvents")
|
|
||||||
e.initEvent("click", true, true)
|
|
||||||
root.firstChild.dispatchEvent(e)
|
|
||||||
|
|
||||||
callAsync(function() {
|
|
||||||
o(router.getPath()).equals("/b")
|
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
o("works after update", function(done) {
|
|
||||||
var id = "a"
|
|
||||||
var A = {
|
|
||||||
view: function() {
|
|
||||||
return {tag: "a", attrs: {href: "/" + id, oncreate: router.link}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$window.location.href = prefix + "/a"
|
|
||||||
router.defineRoutes({"/a": {tag: A}, "/b": {tag: A}}, function(component) {
|
|
||||||
render(root, {tag: A})
|
|
||||||
id = "b"
|
|
||||||
render(root, {tag: A})
|
|
||||||
})
|
|
||||||
|
|
||||||
callAsync(function() {
|
|
||||||
var e = dom.document.createEvent("MouseEvents")
|
|
||||||
e.initEvent("click", true, true)
|
|
||||||
root.firstChild.dispatchEvent(e)
|
|
||||||
|
|
||||||
callAsync(function() {
|
|
||||||
o(router.getPath()).equals("/b")
|
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
@ -14,7 +14,7 @@ o.spec("Router.setPath", function() {
|
||||||
o.beforeEach(function() {
|
o.beforeEach(function() {
|
||||||
$window = pushStateMock(env)
|
$window = pushStateMock(env)
|
||||||
router = new Router($window)
|
router = new Router($window)
|
||||||
router.setPrefix(prefix)
|
router.prefix = prefix
|
||||||
onRouteChange = o.spy()
|
onRouteChange = o.spy()
|
||||||
onFail = o.spy()
|
onFail = o.spy()
|
||||||
})
|
})
|
||||||
|
|
@ -88,7 +88,7 @@ o.spec("Router.setPath", function() {
|
||||||
$window.location.href = "file://" + prefix + "/test"
|
$window.location.href = "file://" + prefix + "/test"
|
||||||
|
|
||||||
router = new Router($window)
|
router = new Router($window)
|
||||||
router.setPrefix(prefix)
|
router.prefix = prefix
|
||||||
|
|
||||||
router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail)
|
router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,14 @@ var domMock = require("./domMock")
|
||||||
var xhrMock = require("./xhrMock")
|
var xhrMock = require("./xhrMock")
|
||||||
|
|
||||||
module.exports = function(env) {
|
module.exports = function(env) {
|
||||||
var $window = {}
|
env = env || {}
|
||||||
|
var $window = env.window = {}
|
||||||
|
|
||||||
var dom = domMock()
|
var dom = domMock()
|
||||||
var xhr = xhrMock()
|
var xhr = xhrMock()
|
||||||
var ps = pushStateMock(env)
|
|
||||||
for (var key in dom) if (!$window[key]) $window[key] = dom[key]
|
for (var key in dom) if (!$window[key]) $window[key] = dom[key]
|
||||||
for (var key in xhr) if (!$window[key]) $window[key] = xhr[key]
|
for (var key in xhr) if (!$window[key]) $window[key] = xhr[key]
|
||||||
for (var key in ps) if (!$window[key]) $window[key] = ps[key]
|
pushStateMock(env)
|
||||||
|
|
||||||
return $window
|
return $window
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ var parseURL = require("../test-utils/parseURL")
|
||||||
module.exports = function(options) {
|
module.exports = function(options) {
|
||||||
if (options == null) options = {}
|
if (options == null) options = {}
|
||||||
|
|
||||||
|
var $window = options.window || {}
|
||||||
var protocol = options.protocol || "http:"
|
var protocol = options.protocol || "http:"
|
||||||
var hostname = options.hostname || "localhost"
|
var hostname = options.hostname || "localhost"
|
||||||
var port = ""
|
var port = ""
|
||||||
|
|
@ -32,7 +33,7 @@ module.exports = function(options) {
|
||||||
}
|
}
|
||||||
return isNew
|
return isNew
|
||||||
}
|
}
|
||||||
|
|
||||||
function prefix(prefix, value) {
|
function prefix(prefix, value) {
|
||||||
if (value === "") return ""
|
if (value === "") return ""
|
||||||
return (value.charAt(0) !== prefix ? prefix : "") + value
|
return (value.charAt(0) !== prefix ? prefix : "") + value
|
||||||
|
|
@ -46,125 +47,125 @@ module.exports = function(options) {
|
||||||
function unload() {
|
function unload() {
|
||||||
if (typeof $window.onunload === "function") $window.onunload({type: "unload"})
|
if (typeof $window.onunload === "function") $window.onunload({type: "unload"})
|
||||||
}
|
}
|
||||||
var $window = {
|
|
||||||
location: {
|
|
||||||
get protocol() {
|
|
||||||
return protocol
|
|
||||||
},
|
|
||||||
get hostname() {
|
|
||||||
return hostname
|
|
||||||
},
|
|
||||||
get port() {
|
|
||||||
return port
|
|
||||||
},
|
|
||||||
get pathname() {
|
|
||||||
return pathname
|
|
||||||
},
|
|
||||||
get search() {
|
|
||||||
return search
|
|
||||||
},
|
|
||||||
get hash() {
|
|
||||||
return hash
|
|
||||||
},
|
|
||||||
get origin() {
|
|
||||||
if (protocol === "file:") return "null"
|
|
||||||
return protocol + "//" + hostname + prefix(":", port)
|
|
||||||
},
|
|
||||||
get host() {
|
|
||||||
if (protocol === "file:") return ""
|
|
||||||
return hostname + prefix(":", port)
|
|
||||||
},
|
|
||||||
get href() {
|
|
||||||
return getURL()
|
|
||||||
},
|
|
||||||
|
|
||||||
set protocol(value) {
|
$window.location = {
|
||||||
throw new Error("Protocol is read-only")
|
get protocol() {
|
||||||
},
|
return protocol
|
||||||
set hostname(value) {
|
},
|
||||||
unload()
|
get hostname() {
|
||||||
past.push({url: getURL(), isNew: true})
|
return hostname
|
||||||
future = []
|
},
|
||||||
hostname = value
|
get port() {
|
||||||
},
|
return port
|
||||||
set port(value) {
|
},
|
||||||
if (protocol === "file:") throw new Error("Port is read-only under `file://` protocol")
|
get pathname() {
|
||||||
unload()
|
return pathname
|
||||||
past.push({url: getURL(), isNew: true})
|
},
|
||||||
future = []
|
get search() {
|
||||||
port = value
|
return search
|
||||||
},
|
},
|
||||||
set pathname(value) {
|
get hash() {
|
||||||
if (protocol === "file:") throw new Error("Pathname is read-only under `file://` protocol")
|
return hash
|
||||||
unload()
|
},
|
||||||
past.push({url: getURL(), isNew: true})
|
get origin() {
|
||||||
future = []
|
if (protocol === "file:") return "null"
|
||||||
pathname = prefix("/", value)
|
return protocol + "//" + hostname + prefix(":", port)
|
||||||
},
|
},
|
||||||
set search(value) {
|
get host() {
|
||||||
unload()
|
if (protocol === "file:") return ""
|
||||||
past.push({url: getURL(), isNew: true})
|
return hostname + prefix(":", port)
|
||||||
future = []
|
},
|
||||||
search = prefix("?", value)
|
get href() {
|
||||||
},
|
return getURL()
|
||||||
set hash(value) {
|
},
|
||||||
var oldHash = hash
|
|
||||||
past.push({url: getURL(), isNew: false})
|
|
||||||
future = []
|
|
||||||
hash = prefix("#", value)
|
|
||||||
if (oldHash != hash) hashchange()
|
|
||||||
},
|
|
||||||
|
|
||||||
set origin(value) {
|
set protocol(value) {
|
||||||
//origin is writable but ignored
|
throw new Error("Protocol is read-only")
|
||||||
},
|
|
||||||
set host(value) {
|
|
||||||
//host is writable but ignored in Chrome
|
|
||||||
},
|
|
||||||
set href(value) {
|
|
||||||
var url = getURL()
|
|
||||||
var isNew = setURL(value)
|
|
||||||
if (isNew) {
|
|
||||||
setURL(url)
|
|
||||||
unload()
|
|
||||||
setURL(value)
|
|
||||||
}
|
|
||||||
past.push({url: url, isNew: isNew})
|
|
||||||
future = []
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
history: {
|
set hostname(value) {
|
||||||
pushState: function(data, title, url) {
|
unload()
|
||||||
past.push({url: getURL(), isNew: false})
|
past.push({url: getURL(), isNew: true})
|
||||||
future = []
|
future = []
|
||||||
setURL(url)
|
hostname = value
|
||||||
},
|
},
|
||||||
replaceState: function(data, title, url) {
|
set port(value) {
|
||||||
future = []
|
if (protocol === "file:") throw new Error("Port is read-only under `file://` protocol")
|
||||||
setURL(url)
|
unload()
|
||||||
},
|
past.push({url: getURL(), isNew: true})
|
||||||
back: function() {
|
future = []
|
||||||
var entry = past.pop()
|
port = value
|
||||||
if (entry != null) {
|
},
|
||||||
if (entry.isNew) unload()
|
set pathname(value) {
|
||||||
future.push({url: getURL(), isNew: false})
|
if (protocol === "file:") throw new Error("Pathname is read-only under `file://` protocol")
|
||||||
setURL(entry.url)
|
unload()
|
||||||
if (!entry.isNew) popstate()
|
past.push({url: getURL(), isNew: true})
|
||||||
}
|
future = []
|
||||||
},
|
pathname = prefix("/", value)
|
||||||
forward: function() {
|
},
|
||||||
var entry = future.pop()
|
set search(value) {
|
||||||
if (entry != null) {
|
unload()
|
||||||
if (entry.isNew) unload()
|
past.push({url: getURL(), isNew: true})
|
||||||
past.push({url: getURL(), isNew: false})
|
future = []
|
||||||
setURL(entry.url)
|
search = prefix("?", value)
|
||||||
if (!entry.isNew) popstate()
|
},
|
||||||
}
|
set hash(value) {
|
||||||
},
|
var oldHash = hash
|
||||||
|
past.push({url: getURL(), isNew: false})
|
||||||
|
future = []
|
||||||
|
hash = prefix("#", value)
|
||||||
|
if (oldHash != hash) hashchange()
|
||||||
|
},
|
||||||
|
|
||||||
|
set origin(value) {
|
||||||
|
//origin is writable but ignored
|
||||||
|
},
|
||||||
|
set host(value) {
|
||||||
|
//host is writable but ignored in Chrome
|
||||||
|
},
|
||||||
|
set href(value) {
|
||||||
|
var url = getURL()
|
||||||
|
var isNew = setURL(value)
|
||||||
|
if (isNew) {
|
||||||
|
setURL(url)
|
||||||
|
unload()
|
||||||
|
setURL(value)
|
||||||
|
}
|
||||||
|
past.push({url: url, isNew: isNew})
|
||||||
|
future = []
|
||||||
},
|
},
|
||||||
onpopstate: null,
|
|
||||||
onhashchange: null,
|
|
||||||
onunload: null,
|
|
||||||
}
|
}
|
||||||
|
$window.history = {
|
||||||
|
pushState: function(data, title, url) {
|
||||||
|
past.push({url: getURL(), isNew: false})
|
||||||
|
future = []
|
||||||
|
setURL(url)
|
||||||
|
},
|
||||||
|
replaceState: function(data, title, url) {
|
||||||
|
future = []
|
||||||
|
setURL(url)
|
||||||
|
},
|
||||||
|
back: function() {
|
||||||
|
var entry = past.pop()
|
||||||
|
if (entry != null) {
|
||||||
|
if (entry.isNew) unload()
|
||||||
|
future.push({url: getURL(), isNew: false})
|
||||||
|
setURL(entry.url)
|
||||||
|
if (!entry.isNew) popstate()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
forward: function() {
|
||||||
|
var entry = future.pop()
|
||||||
|
if (entry != null) {
|
||||||
|
if (entry.isNew) unload()
|
||||||
|
past.push({url: getURL(), isNew: false})
|
||||||
|
setURL(entry.url)
|
||||||
|
if (!entry.isNew) popstate()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
$window.onpopstate = null,
|
||||||
|
$window.onhashchange = null,
|
||||||
|
$window.onunload = null
|
||||||
|
|
||||||
return $window
|
return $window
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,13 @@
|
||||||
<script src="../../test-utils/pushStateMock.js"></script>
|
<script src="../../test-utils/pushStateMock.js"></script>
|
||||||
<script src="../../test-utils/xhrMock.js"></script>
|
<script src="../../test-utils/xhrMock.js"></script>
|
||||||
<script src="../../test-utils/domMock.js"></script>
|
<script src="../../test-utils/domMock.js"></script>
|
||||||
|
<script src="../../test-utils/browserMock.js"></script>
|
||||||
<script src="test-callAsync.js"></script>
|
<script src="test-callAsync.js"></script>
|
||||||
<script src="test-parseURL.js"></script>
|
<script src="test-parseURL.js"></script>
|
||||||
<script src="test-pushStateMock.js"></script>
|
<script src="test-pushStateMock.js"></script>
|
||||||
<script src="test-xhrMock.js"></script>
|
<script src="test-xhrMock.js"></script>
|
||||||
<script src="test-domMock.js"></script>
|
<script src="test-domMock.js"></script>
|
||||||
|
<script src="test-browserMock.js"></script>
|
||||||
|
|
||||||
<script>require("../../ospec/ospec").run()</script>
|
<script>require("../../ospec/ospec").run()</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
40
test-utils/tests/test-browserMock.js
Normal file
40
test-utils/tests/test-browserMock.js
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
var o = require("../../ospec/ospec")
|
||||||
|
var browserMock = require("../../test-utils/browserMock")
|
||||||
|
var callAsync = require("../../test-utils/callAsync")
|
||||||
|
o.spec("browserMock", function() {
|
||||||
|
|
||||||
|
var $window
|
||||||
|
o.beforeEach(function() {
|
||||||
|
$window = browserMock()
|
||||||
|
})
|
||||||
|
|
||||||
|
o("Mocks DOM, pushState and XHR", function() {
|
||||||
|
o($window.location).notEquals(undefined)
|
||||||
|
o($window.document).notEquals(undefined)
|
||||||
|
o($window.XMLHttpRequest).notEquals(undefined)
|
||||||
|
})
|
||||||
|
o("$window.onhashchange can be reached from the pushStateMock functions", function(done) {
|
||||||
|
$window.onhashchange = o.spy()
|
||||||
|
$window.location.hash = '#a'
|
||||||
|
|
||||||
|
callAsync(function(){
|
||||||
|
o($window.onhashchange.callCount).equals(1)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
o("$window.onpopstate can be reached from the pushStateMock functions", function() {
|
||||||
|
$window.onpopstate = o.spy()
|
||||||
|
$window.history.pushState(null, null, "#a")
|
||||||
|
$window.history.back()
|
||||||
|
|
||||||
|
o($window.onpopstate.callCount).equals(1)
|
||||||
|
})
|
||||||
|
o("$window.onunload can be reached from the pushStateMock functions", function() {
|
||||||
|
$window.onunload = o.spy()
|
||||||
|
$window.location.href = '/a'
|
||||||
|
|
||||||
|
o($window.onunload.callCount).equals(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
Loading…
Add table
Add a link
Reference in a new issue