add support for route state #1480

This commit is contained in:
Leo Horie 2016-12-17 21:50:04 -05:00
parent 75e83ea565
commit de07a54881
10 changed files with 367 additions and 196 deletions

View file

@ -34,6 +34,6 @@ There are over 4000 assertions in the test suite, and tests cover even difficult
## Modularity
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
Despite the huge improvements in performance and modularity, the new codebase is smaller than v0.2.x, currently clocking at <!-- size -->7.53 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

View file

@ -14,6 +14,9 @@
- [Typical usage](#typical-usage)
- [Navigating to different routes](#navigating-to-different-routes)
- [Routing parameters](#routing-parameters)
- [Key parameter](#key-parameter)
- [Variadic routes](#variadic-routes)
- [History state](#history-state)
- [Changing router prefix](#changing-router-prefix)
- [Advanced component resolution](#advanced-component-resolution)
- [Wrapping a layout component](#wrapping-a-layout-component)
@ -70,6 +73,8 @@ Argument | Type | Required | Description
`path` | `String` | Yes | The path to route to, without a prefix. The path may include slots for routing parameters
`data` | `Object` | No | Routing parameters. If `path` has routing parameter slots, the properties of this object are interpolated into the path string
`options.replace` | `Boolean` | No | Whether to create a new history entry or to replace the current one. Defaults to false
`options.state` | `Object` | No | The `state` object to pass to the underlying `history.pushState` / `history.replaceState` call. This state object becomes available in the `history.state` property, and is merged into the [routing parameters](#routing-parameters) object. Note that this option only works when using the pushState API, but is ignored if the router falls back to hashchange mode (i.e. if the pushState API is not available)
`options.title` | `String` | No | The `title` string to pass to the underlying `history.pushState` / `history.replaceState` call.
**returns** | | | Returns `undefined`
##### route.get
@ -254,6 +259,28 @@ It's possible to have multiple arguments in a route, for example `/edit/:project
In addition to routing parameters, the `attrs` object also includes a `path` property that contains the current route path, and a `route` property that contains the matched routed.
#### Key parameter
When a user navigates from a parameterized route to the same route with a different parameter (e.g. going from `/page/1` to `/page/2` given a route `/page/:id`, the component would not be recreated from scratch since both routes resolve to the same component, and thus result in a virtual dom in-place diff. This has the side-effect of triggering the `onupdate` hook, rather than `oninit`/`oncreate`. However, it's relatively common for a developer to want to synchronize the recreation of the component to the route change event.
To achieve that, it's possible to combine route parameterization with the virtual dom [key reconciliation](keys.md) feature:
```javascript
m.route(document.body, "/edit/1", {
"/edit/:key": Edit,
})
```
This means that the [vnode](vnodes.md) that is created for the root component of the route has a route parameter object `key`. Route parameters become `attrs` in the vnode. Thus, when jumping from one page to another, the `key` changes and causes the component to be recreated from scratch (since the key tells the virtual dom engine that old and new components are different entities).
You can take that idea further to create components that recreate themselves when reloaded:
`m.route.set(m.route.get(), {key: Date.now()})`
Or even use the [`history state`](#history-state) feature to achieve reloadable components without polluting the URL:
`m.route.set(m.route.get(), null, {state: {key: Date.now()}})`
#### Variadic routes
It's also possible to have variadic routes, i.e. a route with an argument that contains URL pathnames that contain slashes:
@ -264,6 +291,44 @@ m.route(document.body, "/edit/pictures/image.jpg", {
})
```
#### History state
It's possible to take full advantage of the underlying `history.pushState` API to improve user's navigation experience. For example, an application could "remember" the state of a large form when the user leaves a page by navigating away, such that if the user pressed the back button in the browser, they'd have the form filled rather than a blank form.
For example, you could create a form like this:
```javascript
var state = {
term: "",
search: function() {
// save the state for this route
// this is equivalent to `history.replaceState({term: state.term}, null, location.href)`
m.route.set(m.route.get(), null, {replace: true, state: {term: state.term}})
// navigate away
location.href = "https://google.com/?q=" + state.term
}
}
var Form = {
oninit: function(vnode) {
state.term = vnode.attrs.term || "" // populated from the `history.state` property if the user presses the back button
},
view: function() {
return m("form", [
m("input[placeholder='Search']", {oninput: m.withAttr("value", function(v) {state.term = v}), value: state.term}),
m("button", {onclick: state.search}, "Search")
])
}
}
m.route(document.body, "/", {
"/": Form,
})
```
This way, if the user searches and presses the back button to return to the application, the input will still be populated with the search term. This technique can improve the user experience of large forms and other apps where non-persisted state is laborious for a user to produce.
---
### Changing router prefix
@ -409,7 +474,7 @@ m.route(document.body, "/user/list", {
"/user/list": {
oninit: state.loadUsers,
view: function() {
return state.users.length > 0 ? state.users.map(function() {
return state.users.length > 0 ? state.users.map(function(user) {
return m("div", user.id)
}) : "loading"
}
@ -435,7 +500,7 @@ m.route(document.body, "/user/list", {
"/user/list": {
onmatch: state.loadUsers,
render: function() {
return state.users.length > 0 ? state.users.map(function() {
return state.users.length > 0 ? state.users.map(function(user) {
return m("div", user.id)
}) : "loading"
}

View file

@ -453,83 +453,84 @@ var coreRenderer = function($window) {
else if (old == null) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, undefined)
else if (vnodes == null) removeNodes(old, 0, old.length, vnodes)
else {
var isUnkeyed = false
for (var i = 0; i < vnodes.length; i++) {
if (vnodes[i] != null) {
isUnkeyed = vnodes[i].key == null
break
if (old.length === vnodes.length) {
var isUnkeyed = false
for (var i = 0; i < vnodes.length; i++) {
if (vnodes[i] != null && old[i] != null) {
isUnkeyed = vnodes[i].key == null && old[i].key == null
break
}
}
if (isUnkeyed) {
for (var i = 0; i < old.length; i++) {
if (old[i] === vnodes[i]) continue
else if (old[i] == null && vnodes[i] != null) insertNode(parent, createNode(vnodes[i], hooks, ns), getNextSibling(old, i + 1, nextSibling))
else if (vnodes[i] == null) removeNodes(old, i, i + 1, vnodes)
else updateNode(parent, old[i], vnodes[i], hooks, getNextSibling(old, i + 1, nextSibling), false, ns)
}
return
}
}
if (old.length === vnodes.length && isUnkeyed) {
for (var i = 0; i < old.length; i++) {
if (old[i] === vnodes[i]) continue
else if (old[i] == null && vnodes[i] != null) insertNode(parent, createNode(vnodes[i], hooks, ns), getNextSibling(old, i + 1, nextSibling))
else if (vnodes[i] == null) removeNodes(old, i, i + 1, vnodes)
else updateNode(parent, old[i], vnodes[i], hooks, getNextSibling(old, i + 1, nextSibling), false, ns)
var recycling = isRecyclable(old, vnodes)
if (recycling) old = old.concat(old.pool)
var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map
while (oldEnd >= oldStart && end >= start) {
var o = old[oldStart], v = vnodes[start]
if (o === v && !recycling) oldStart++, start++
else if (o == null) oldStart++
else if (v == null) start++
else if (o.key === v.key) {
oldStart++, start++
updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns)
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
}
}
else {
var recycling = isRecyclable(old, vnodes)
if (recycling) old = old.concat(old.pool)
var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map
while (oldEnd >= oldStart && end >= start) {
var o = old[oldStart], v = vnodes[start]
if (o === v && !recycling) oldStart++, start++
else if (o == null) oldStart++
else {
var o = old[oldEnd]
if (o === v && !recycling) oldEnd--, start++
else if (o == null) oldEnd--
else if (v == null) start++
else if (o.key === v.key) {
oldStart++, start++
updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns)
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
}
else {
var o = old[oldEnd]
if (o === v && !recycling) oldEnd--, start++
else if (o == null) oldEnd--
else if (v == null) start++
else if (o.key === v.key) {
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
if (recycling || start < end) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling))
oldEnd--, start++
}
else break
}
}
while (oldEnd >= oldStart && end >= start) {
var o = old[oldEnd], v = vnodes[end]
if (o === v && !recycling) oldEnd--, end--
else if (o == null) oldEnd--
else if (v == null) end--
else if (o.key === v.key) {
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
if (o.dom != null) nextSibling = o.dom
oldEnd--, end--
if (recycling || start < end) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling))
oldEnd--, start++
}
else {
if (!map) map = getKeyMap(old, oldEnd)
if (v != null) {
var oldIndex = map[v.key]
if (oldIndex != null) {
var movable = old[oldIndex]
updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
insertNode(parent, toFragment(movable), nextSibling)
old[oldIndex].skip = true
if (movable.dom != null) nextSibling = movable.dom
}
else {
var dom = createNode(v, hooks, undefined)
insertNode(parent, dom, nextSibling)
nextSibling = dom
}
}
end--
}
if (end < start) break
else break
}
createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)
removeNodes(old, oldStart, oldEnd + 1, vnodes)
}
while (oldEnd >= oldStart && end >= start) {
var o = old[oldEnd], v = vnodes[end]
if (o === v && !recycling) oldEnd--, end--
else if (o == null) oldEnd--
else if (v == null) end--
else if (o.key === v.key) {
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
if (o.dom != null) nextSibling = o.dom
oldEnd--, end--
}
else {
if (!map) map = getKeyMap(old, oldEnd)
if (v != null) {
var oldIndex = map[v.key]
if (oldIndex != null) {
var movable = old[oldIndex]
updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
insertNode(parent, toFragment(movable), nextSibling)
old[oldIndex].skip = true
if (movable.dom != null) nextSibling = movable.dom
}
else {
var dom = createNode(v, hooks, undefined)
insertNode(parent, dom, nextSibling)
nextSibling = dom
}
}
end--
}
if (end < start) break
}
createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)
removeNodes(old, oldStart, oldEnd + 1, vnodes)
}
}
function updateNode(parent, old, vnode, hooks, nextSibling, recycling, ns) {
@ -1042,9 +1043,11 @@ var coreRouter = function($window) {
var hash = buildQueryString(hashData)
if (hash) path += "#" + hash
if (supportsPushState) {
if (options && options.replace) $window.history.replaceState(null, null, router.prefix + path)
else $window.history.pushState(null, null, router.prefix + path)
var state = options ? options.state : null
var title = options ? options.title : null
$window.onpopstate()
if (options && options.replace) $window.history.replaceState(state, title, router.prefix + path)
else $window.history.pushState(state, title, router.prefix + path)
}
else $window.location.href = router.prefix + path
}
@ -1054,6 +1057,10 @@ var coreRouter = function($window) {
var params = {}
var pathname = parsePath(path, params, params)
var state = $window.history.state
if (state != null) {
for (var k in state) params[k] = state[k]
}
for (var route0 in routes) {
var matcher = new RegExp("^" + route0.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$")
if (matcher.test(pathname)) {

82
mithril.min.js vendored
View file

@ -1,41 +1,41 @@
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(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||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=
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,
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");
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"!==
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=
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 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();
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,
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"!==
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.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,
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,
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;
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,
"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=
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,
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]&&null!=a[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||
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!=
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;
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?(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,
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=
[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();
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");
}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):
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=
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"===
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?"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)))}}
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;
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);
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]):
"";"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=
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");
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=
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");
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",
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};
new function(){function w(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 B(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 l=c[1],k=c[2];""===l&&""!==k?h=k:"#"===l?g.id=k:"."===l?d.push(k):"["===c[3][0]&&((l=c[6])&&(l=l.replace(/\\(["'])/g,"$1").replace(/\\\\/g,"\\")),
"class"===c[4]?d.push(l):g[c[4]]=l||!0)}0<d.length&&(g.className=d.join(" "));K[a]=function(a,c){var l=!1,b,q,d=a.className||a["class"],x;for(x in g)a[x]=g[x];void 0!==d&&(void 0!==a["class"]&&(a["class"]=void 0,a.className=d),void 0!==g.className&&(a.className=g.className+" "+d));for(x in a)if("key"!==x){l=!0;break}c instanceof Array&&1==c.length&&null!=c[0]&&"#"===c[0].tag?q=c[0].children:b=c;return w(h||"div",a.key,l?a:void 0,b,q,void 0)}}var m;null!=arguments[1]&&("object"!==typeof arguments[1]||
void 0!==arguments[1].tag||arguments[1]instanceof Array)?d=1:(m=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](m||{},w.normalizeChildren(c)):w(a,m&&m.key,m||{},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=
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={};B.trust=function(a){null==a&&(a="");return w("<",void 0,void 0,a,void 0,
void 0)};B.fragment=function(a,c){return w("[",a.key,a,w.normalizeChildren(c),void 0,void 0)};var y=function(a){function c(a,b){return function n(c){var k;try{if(!b||null==c||"object"!==typeof c&&"function"!==typeof c||"function"!==typeof(k=c.then))r(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;q.state=b;q.retry=function(){n(c)}});else{if(c===d)throw new TypeError("Promise can't be resolved w/ itself");
h(k.bind(c))}}catch(G){m(G)}}}function h(a){function b(b){return function(a){0<c++||b(a)}}var c=0,d=b(m);try{a(b(k),d)}catch(x){d(x)}}if(!(this instanceof y))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=[],k=c(g,!0),m=c(l,!1),q=d._instance={resolvers:g,rejectors:l},r="function"===typeof setImmediate?setImmediate:setTimeout;h(a)};y.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(C){l&&l(C)}});"function"===typeof d.retry&&k===d.state&&d.retry()}var d=this._instance,g,l,k=new y(function(a,c){g=a;l=c});h(a,d.resolvers,g,!0);h(c,d.rejectors,l,!1);return k};y.prototype["catch"]=function(a){return this.then(null,a)};y.resolve=function(a){return a instanceof y?a:new y(function(c){c(a)})};y.reject=function(a){return new y(function(c,h){h(a)})};y.all=function(a){return new y(function(c,h){var d=a.length,g=0,l=[];if(0===a.length)c([]);else for(var k=
0;k<a.length;k++)(function(k){function q(a){g++;l[k]=a;g===d&&c(l)}null==a[k]||"object"!==typeof a[k]&&"function"!==typeof a[k]||"function"!==typeof a[k].then?q(a[k]):a[k].then(q,h)})(k)})};y.race=function(a){return new y(function(c,h){for(var d=0;d<a.length;d++)a[d].then(c,h)})};"undefined"===typeof D&&("undefined"!==typeof window?window.Promise=y:"undefined"!==typeof global&&(global.Promise=y));var H="undefined"!==typeof D?D:y,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 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 t&&t()}var a=0;return function x(c){var d=c.then;c.then=function(){a++;var g=d.apply(c,arguments);g.then(b,function(c){b();
if(0===a)throw c;});return x(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 l(a,c){var b=I(c);if(""!==b){var d=0>a.indexOf("?")?"?":"&";a+=d+b}return a}function k(a){try{return""!==a?JSON.parse(a):null}catch(C){throw Error(a);}}function m(a){return a.responseText}function q(a,
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 r=0,t;return{request:function(b,r){var t=h();b=d(b,r);var x=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=k);"function"!==typeof b.extract&&(b.extract=m);b.url=g(b.url,b.data);h?b.data=b.serialize(b.data):b.url=l(b.url,b.data);var p=new a.XMLHttpRequest;p.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&&p.setRequestHeader("Content-Type","application/json; charset=utf-8");b.deserialize===k&&p.setRequestHeader("Accept","application/json, text/*");
b.withCredentials&&(p.withCredentials=b.withCredentials);"function"===typeof b.config&&(p=b.config(p,b)||p);p.onreadystatechange=function(){if(4===p.readyState)try{var a=b.extract!==m?b.extract(p,b):b.deserialize(b.extract(p,b));if(200<=p.status&&300>p.status||304===p.status)c(q(b.type,a));else{var g=Error(p.responseText),h;for(h in a)g[h]=a[h];d(g)}}catch(F){d(F)}};h&&null!=b.data?p.send(b.data):p.send()});return!0===b.background?x:t(x)},jsonp:function(b,k){var m=h();b=d(b,k);var t=new c(function(c,
d){var h=b.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+r++,k=a.document.createElement("script");a[h]=function(d){k.parentNode.removeChild(k);c(q(b.type,d));delete a[h]};k.onerror=function(){k.parentNode.removeChild(k);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;k.src=l(b.url,b.data);a.document.documentElement.appendChild(k)});return!0===b.background?t:m(t)},setCompletionCallback:function(a){t=a}}}(window,
H),O=function(a){function c(e,f,a,b,c,d,g){for(;a<b;a++){var u=f[a];null!=u&&q(e,h(u,c,g),d)}}function h(e,f,a){var u=e.tag;null!=e.attrs&&L(e.attrs,e,f);if("string"===typeof u)switch(u){case "#":return e.dom=z.createTextNode(e.children);case "<":return d(e);case "[":var b=z.createDocumentFragment();null!=e.children&&(u=e.children,c(b,u,0,u.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 q=(u=e.attrs)&&u.is,g=a?q?z.createElementNS(a,g,{is:q}):z.createElementNS(a,g):q?z.createElement(g,{is:q}):z.createElement(g);e.dom=g;if(null!=u)for(b in q=a,u)x(e,b,null,u[b],q);null!=e.attrs&&null!=e.attrs.contenteditable?r(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&&x(e,
"value",null,f.value,void 0),"selectedIndex"in f&&x(e,"selectedIndex",null,f.selectedIndex,void 0))));return g}else{e.state||(e.state={});B(e.state,e.tag);b=e.tag.view;if(null!=b.reentrantLock)e=D;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=
0,e=D;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=z.createElement(f);f.innerHTML=e.children;e.dom=f.firstChild;e.domSize=f.childNodes.length;e=z.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)t(f,0,f.length,
a);else{if(f.length===a.length){for(var u=!1,v=0;v<a.length;v++)if(null!=a[v]&&null!=f[v]){u=null==a[v].key&&null==f[v].key;break}if(u){for(v=0;v<f.length;v++)f[v]!==a[v]&&(null==f[v]&&null!=a[v]?q(e,h(a[v],b,g),m(f,v+1,d)):null==a[v]?t(f,v,v+1,a):l(e,f[v],a[v],b,m(f,v+1,d),!1,g));return}}a:{if(null!=f.pool&&Math.abs(f.pool.length-a.length)<=Math.abs(f.length-a.length)&&(u=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)-u)<=Math.abs((f[0]&&
f[0].children&&f[0].children.length||0)-u))){u=!0;break a}u=!1}u&&(f=f.concat(f.pool));for(var A=v=0,r=f.length-1,x=a.length-1,C;r>=v&&x>=A;){var p=f[v],n=a[A];if(p!==n||u)if(null==p)v++;else if(null==n)A++;else if(p.key===n.key)v++,A++,l(e,p,n,b,m(f,v,d),u,g),u&&p.tag===n.tag&&q(e,k(p),d);else if(p=f[r],p!==n||u)if(null==p)r--;else if(null==n)A++;else if(p.key===n.key)l(e,p,n,b,m(f,r+1,d),u,g),(u||A<x)&&q(e,k(p),m(f,v,d)),r--,A++;else break;else r--,A++;else v++,A++}for(;r>=v&&x>=A;){p=f[r];n=a[x];
if(p!==n||u)if(null==p)r--;else{if(null!=n)if(p.key===n.key)l(e,p,n,b,m(f,r+1,d),u,g),u&&p.tag===n.tag&&q(e,k(p),d),null!=p.dom&&(d=p.dom),r--;else{if(!C){C=f;var p=r,E={},w;for(w=0;w<p;w++){var z=C[w];null!=z&&(z=z.key,null!=z&&(E[z]=w))}C=E}null!=n&&(p=C[n.key],null!=p?(E=f[p],l(e,E,n,b,m(f,r+1,d),u,g),q(e,k(E),d),f[p].skip=!0,null!=E.dom&&(d=E.dom)):(n=h(n,b,void 0),q(e,n,d),d=n))}x--}else r--,x--;if(x<A)break}c(e,a,A,x+1,b,d,g);t(f,v,r+1,a)}}function l(a,f,b,c,n,A,m){var e=f.tag;if(e===b.tag){b.state=
f.state;b.events=f.events;var u;var v;null!=b.attrs&&"function"===typeof b.attrs.onbeforeupdate&&(u=b.attrs.onbeforeupdate.call(b.state,b,f));"string"!==typeof b.tag&&"function"===typeof b.tag.onbeforeupdate&&(v=b.tag.onbeforeupdate.call(b.state,b,f));void 0===u&&void 0===v||u||v?u=!1:(b.dom=f.dom,b.domSize=f.domSize,b.instance=f.instance,u=!0);if(!u)if(null!=b.attrs&&p(b.attrs,b,c,A),"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?(k(f),q(a,d(b),n)):(b.dom=f.dom,b.domSize=f.domSize);break;case "[":g(a,f.children,b.children,c,n,m);f=0;c=b.children;b.dom=null;if(null!=c){for(var t=0;t<c.length;t++)a=c[t],null!=a&&null!=a.dom&&(null==b.dom&&(b.dom=a.dom),f+=a.domSize||1);1!==f&&(b.domSize=f)}break;default:a=m;n=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));A=f.attrs;m=b.attrs;e=a;if(null!=m)for(t in m)x(b,t,A&&A[t],m[t],e);if(null!=A)for(t in A)null!=m&&t in m||("className"===t&&(t="class"),"o"!==t[0]||"n"!==t[1]||y(t)?"key"!==t&&b.dom.removeAttribute(t):G(b,t,void 0));null!=b.attrs&&null!=b.attrs.contenteditable?r(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=[w("#",void 0,void 0,b.text,void 0,void 0)]),g(n,f.children,b.children,c,null,a))}else b.instance=w.normalize(b.tag.view.call(b.state,b)),p(b.tag,b,c,A),null!=b.instance?(null==f.instance?q(a,h(b.instance,c,m),n):l(a,f.instance,b.instance,c,n,A,m),b.dom=b.instance.dom,b.domSize=b.instance.domSize):null!=f.instance?(C(f.instance,null),b.dom=void 0,b.domSize=0):(b.dom=f.dom,b.domSize=f.domSize)}else C(f,null),q(a,h(b,c,m),n)}function k(b){var a=b.domSize;if(null!=a||null==
b.dom){var e=z.createDocumentFragment();if(0<a){for(b=b.dom;--a;)e.appendChild(b.nextSibling);e.insertBefore(b,e.firstChild)}return e}return b.dom}function m(b,a,c){for(;a<b.length;a++)if(null!=b[a]&&null!=b[a].dom)return b[a].dom;return c}function q(b,a,c){c&&c.parentNode?b.insertBefore(a,c):b.appendChild(a)}function r(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 t(b,a,c,d){for(;a<c;a++){var e=b[a];null!=e&&(e.skip?e.skip=!1:C(e,d))}}function b(b){var a=!1;return function(){a||(a=!0,b())}}function C(a,f){function e(){if(++d===c&&(n(a),a.dom)){var b=a.domSize||1;if(1<b)for(var e=a.dom;--b;){var g=e.nextSibling,h=g.parentNode;null!=h&&h.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,a,b(e)));"string"!==typeof a.tag&&a.tag.onbeforeremove&&(c++,a.tag.onbeforeremove.call(a.state,a,b(e)));e()}function n(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)n(a.instance);else if(a=a.children,a instanceof Array)for(var b=0;b<a.length;b++){var e=a[b];null!=e&&n(e)}}function x(a,b,c,d,g){var e=
a.dom;if("key"!==b&&(c!==d||"value"===b||"checked"===b||"selectedIndex"===b||"selected"===b&&a.dom===z.activeElement||"object"===typeof d)&&"undefined"!==typeof d&&!y(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)G(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 h in d)e.style[h]=d[h];if(null!=a&&"string"!==typeof a)for(h in a)h in d||(e.style[h]="")}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===z.activeElement||"select"===a.tag&&"value"===b&&a.dom.value===d&&a.dom===z.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 y(a){return"oninit"===a||"oncreate"===a||"onupdate"===a||"onremove"===a||"onbeforeremove"===a||"onbeforeupdate"===a}function G(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 L(a,b,c){"function"===typeof a.oninit&&a.oninit.call(b.state,b);"function"===typeof a.oncreate&&c.push(a.oncreate.bind(b.state,b))}function p(a,b,c,d){d?L(a,b,c):"function"===typeof a.onupdate&&c.push(a.onupdate.bind(b.state,b))}function B(a,b){Object.keys(b).forEach(function(c){a[c]=b[c]})}var z=a.document,D=z.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=z.activeElement;
null==a.vnodes&&(a.textContent="");b instanceof Array||(b=[b]);g(a,a.vnodes,w.normalizeChildren(b),c,null,void 0);a.vnodes=b;for(var e=0;e<c.length;e++)c[e]();z.activeElement!==d&&d.focus()},setEventCallback:function(a){return F=a}}},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);
M.setCompletionCallback(J.redraw);B.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 D=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("="),l=decodeURIComponent(g[0]),g=2===g.length?decodeURIComponent(g[1]):
"";"true"===g?g=!0:"false"===g&&(g=!1);var k=l.split(/\]\[?|\[/),m=c;-1<l.indexOf("[")&&k.pop();for(var q=0;q<k.length;q++){var l=k[q],r=k[q+1],r=""==r||!isNaN(parseInt(r,10)),t=q===k.length-1;""===l&&(l=k.slice(0,q).join(),null==h[l]&&(h[l]=0),l=h[l]++);null==m[l]&&(m[l]=t?g:r?[]:{});m=m[l]}}return c},R=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==k&&(k=l(function(){k=
null;a()}))}}function d(a,c,d){var b=a.indexOf("?"),g=a.indexOf("#"),h=-1<b?b:-1<g?g:a.length;if(-1<b){var b=N(a.slice(b+1,-1<g?g:a.length)),k;for(k in b)c[k]=b[k]}if(-1<g)for(k in c=N(a.slice(g+1)),c)d[k]=c[k];return a.slice(0,h)}var g="function"===typeof a.history.pushState,l="function"===typeof setImmediate?setImmediate:setTimeout,k,m={prefix:"#!",getPath:function(){switch(m.prefix.charAt(0)){case "#":return c("hash").slice(m.prefix.length);case "?":return c("search").slice(m.prefix.length)+c("hash");
default:return c("pathname").slice(m.prefix.length)+c("search")+c("hash")}},setPath:function(c,h,k){var b={},l={};c=d(c,b,l);if(null!=h){for(var n in h)b[n]=h[n];c=c.replace(/:([^\/]+)/g,function(a,c){delete b[c];return h[c]})}(n=I(b))&&(c+="?"+n);(l=I(l))&&(c+="#"+l);g?(l=k?k.state:null,n=k?k.title:null,a.onpopstate(),k&&k.replace?a.history.replaceState(l,n,m.prefix+c):a.history.pushState(l,n,m.prefix+c)):a.location.href=m.prefix+c},defineRoutes:function(c,k,l){function b(){var b=m.getPath(),g={},
h=d(b,g,g),t=a.history.state;if(null!=t)for(var q in t)g[q]=t[q];for(var r in c)if(t=new RegExp("^"+r.replace(/:[^\/]+?\.{3}/g,"(.*?)").replace(/:[^\/]+/g,"([^\\/]+)")+"/?$"),t.test(h)){h.replace(t,function(){for(var a=r.match(/:[^\/]+/g)||[],d=[].slice.call(arguments,1,-2),h=0;h<a.length;h++)g[a[h].replace(/:|\./g,"")]=decodeURIComponent(d[h]);k(c[r],g,b,r)});return}l(b,g)}g?a.onpopstate=h(b):"#"===m.prefix.charAt(0)&&(a.onhashchange=b);b()}};return m};B.route=function(a,c){var h=R(a),d=function(a){return a},
g,l,k,m,q=!1,r=function(a,b,r){if(null==a)throw Error("Ensure the DOM element that was passed to `m.route` is not undefined");var n=function(a,b,c,h){l=null!=b&&"function"===typeof b.view?b:"div";k=c;m=h;q=!1;g=(a.render||d).bind(a);t()},t=function(){null!=g&&c.render(a,g(w(l,k.key,k)))},y=function(){h.setPath(b)};h.defineRoutes(r,function(a,b,c){a.view?n({},a,b,c):a.onmatch?(q=!0,D.resolve(a.onmatch(b,c)).then(function(d){q&&n(a,d,b,c)},y)):n(a,"div",b,c)},y);c.subscribe(a,t)};r.set=function(a,b,
c){q&&(c={replace:!0});q=!1;h.setPath(a,b,c)};r.get=function(){return m};r.prefix=function(a){h.prefix=a};r.link=function(a){a.dom.setAttribute("href",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)),r.set(a,void 0,void 0))}};return r}(window,J);B.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);B.render=H.render;B.redraw=J.redraw;B.request=M.request;B.jsonp=M.jsonp;B.parseQueryString=N;B.buildQueryString=I;B.version="1.0.0-rc.6";"undefined"!==typeof module?module.exports=B:window.m=B};

View file

@ -124,84 +124,85 @@ module.exports = function($window) {
else if (old == null) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, undefined)
else if (vnodes == null) removeNodes(old, 0, old.length, vnodes)
else {
var isUnkeyed = false
for (var i = 0; i < vnodes.length; i++) {
if (vnodes[i] != null) {
isUnkeyed = vnodes[i].key == null
break
if (old.length === vnodes.length) {
var isUnkeyed = false
for (var i = 0; i < vnodes.length; i++) {
if (vnodes[i] != null && old[i] != null) {
isUnkeyed = vnodes[i].key == null && old[i].key == null
break
}
}
if (isUnkeyed) {
for (var i = 0; i < old.length; i++) {
if (old[i] === vnodes[i]) continue
else if (old[i] == null && vnodes[i] != null) insertNode(parent, createNode(vnodes[i], hooks, ns), getNextSibling(old, i + 1, nextSibling))
else if (vnodes[i] == null) removeNodes(old, i, i + 1, vnodes)
else updateNode(parent, old[i], vnodes[i], hooks, getNextSibling(old, i + 1, nextSibling), false, ns)
}
return
}
}
if (old.length === vnodes.length && isUnkeyed) {
for (var i = 0; i < old.length; i++) {
if (old[i] === vnodes[i]) continue
else if (old[i] == null && vnodes[i] != null) insertNode(parent, createNode(vnodes[i], hooks, ns), getNextSibling(old, i + 1, nextSibling))
else if (vnodes[i] == null) removeNodes(old, i, i + 1, vnodes)
else updateNode(parent, old[i], vnodes[i], hooks, getNextSibling(old, i + 1, nextSibling), false, ns)
}
}
else {
var recycling = isRecyclable(old, vnodes)
if (recycling) old = old.concat(old.pool)
var recycling = isRecyclable(old, vnodes)
if (recycling) old = old.concat(old.pool)
var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map
while (oldEnd >= oldStart && end >= start) {
var o = old[oldStart], v = vnodes[start]
if (o === v && !recycling) oldStart++, start++
else if (o == null) oldStart++
var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map
while (oldEnd >= oldStart && end >= start) {
var o = old[oldStart], v = vnodes[start]
if (o === v && !recycling) oldStart++, start++
else if (o == null) oldStart++
else if (v == null) start++
else if (o.key === v.key) {
oldStart++, start++
updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns)
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
}
else {
var o = old[oldEnd]
if (o === v && !recycling) oldEnd--, start++
else if (o == null) oldEnd--
else if (v == null) start++
else if (o.key === v.key) {
oldStart++, start++
updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns)
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
}
else {
var o = old[oldEnd]
if (o === v && !recycling) oldEnd--, start++
else if (o == null) oldEnd--
else if (v == null) start++
else if (o.key === v.key) {
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
if (recycling || start < end) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling))
oldEnd--, start++
}
else break
}
}
while (oldEnd >= oldStart && end >= start) {
var o = old[oldEnd], v = vnodes[end]
if (o === v && !recycling) oldEnd--, end--
else if (o == null) oldEnd--
else if (v == null) end--
else if (o.key === v.key) {
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
if (o.dom != null) nextSibling = o.dom
oldEnd--, end--
if (recycling || start < end) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling))
oldEnd--, start++
}
else {
if (!map) map = getKeyMap(old, oldEnd)
if (v != null) {
var oldIndex = map[v.key]
if (oldIndex != null) {
var movable = old[oldIndex]
updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
insertNode(parent, toFragment(movable), nextSibling)
old[oldIndex].skip = true
if (movable.dom != null) nextSibling = movable.dom
}
else {
var dom = createNode(v, hooks, undefined)
insertNode(parent, dom, nextSibling)
nextSibling = dom
}
}
end--
}
if (end < start) break
else break
}
createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)
removeNodes(old, oldStart, oldEnd + 1, vnodes)
}
while (oldEnd >= oldStart && end >= start) {
var o = old[oldEnd], v = vnodes[end]
if (o === v && !recycling) oldEnd--, end--
else if (o == null) oldEnd--
else if (v == null) end--
else if (o.key === v.key) {
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
if (o.dom != null) nextSibling = o.dom
oldEnd--, end--
}
else {
if (!map) map = getKeyMap(old, oldEnd)
if (v != null) {
var oldIndex = map[v.key]
if (oldIndex != null) {
var movable = old[oldIndex]
updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
insertNode(parent, toFragment(movable), nextSibling)
old[oldIndex].skip = true
if (movable.dom != null) nextSibling = movable.dom
}
else {
var dom = createNode(v, hooks, undefined)
insertNode(parent, dom, nextSibling)
nextSibling = dom
}
}
end--
}
if (end < start) break
}
createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)
removeNodes(old, oldStart, oldEnd + 1, vnodes)
}
}
function updateNode(parent, old, vnode, hooks, nextSibling, recycling, ns) {

View file

@ -880,7 +880,7 @@ o.spec("updateNodes", function() {
o(onupdate.callCount).equals(0)
})
o("cached, keyed nodes skip diff", function () {
var onupdate = o.spy();
var onupdate = o.spy()
var cached = {tag:"a", key:"a", attrs:{onupdate: onupdate}}
render(root, cached)
@ -926,4 +926,14 @@ o.spec("updateNodes", function() {
o(update.callCount).equals(2)
o(remove.callCount).equals(0)
})
o("component is recreated if key changes to undefined", function () {
var vnode = {tag: "b", key: 1}
var updated = {tag: "b"}
render(root, vnode)
var dom = vnode.dom
render(root, updated)
o(vnode.dom).notEquals(updated.dom)
})
})

View file

@ -67,9 +67,11 @@ module.exports = function($window) {
if (hash) path += "#" + hash
if (supportsPushState) {
if (options && options.replace) $window.history.replaceState(null, null, router.prefix + path)
else $window.history.pushState(null, null, router.prefix + path)
var state = options ? options.state : null
var title = options ? options.title : null
$window.onpopstate()
if (options && options.replace) $window.history.replaceState(state, title, router.prefix + path)
else $window.history.pushState(state, title, router.prefix + path)
}
else $window.location.href = router.prefix + path
}
@ -79,6 +81,10 @@ module.exports = function($window) {
var params = {}
var pathname = parsePath(path, params, params)
var state = $window.history.state
if (state != null) {
for (var k in state) params[k] = state[k]
}
for (var route in routes) {
var matcher = new RegExp("^" + route.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$")

View file

@ -150,6 +150,18 @@ o.spec("Router.setPath", function() {
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : "") + "test")
done()
})
})
o("state works", function(done) {
$window.location.href = prefix + "/test"
router.defineRoutes({"/test": {data: 1}, "/other": {data: 2}}, onRouteChange, onFail)
callAsync(function() {
router.setPath("/other", null, {state: {a: 1}})
o($window.history.state).deepEquals({a: 1})
done()
})
})

View file

@ -13,7 +13,7 @@ module.exports = function(options) {
var search = ""
var hash = ""
var past = [], future = []
var past = [{url: getURL(), isNew: true, state: null, title: null}], future = []
function getURL() {
if (protocol === "file:") return protocol + "//" + pathname + search + hash
@ -42,7 +42,7 @@ module.exports = function(options) {
if (typeof $window.onhashchange === "function") $window.onhashchange({type: "hashchange"})
}
function popstate() {
if (typeof $window.onpopstate === "function") $window.onpopstate({type: "popstate"})
if (typeof $window.onpopstate === "function") $window.onpopstate({type: "popstate", state: $window.history.state})
}
function unload() {
if (typeof $window.onunload === "function") $window.onunload({type: "unload"})
@ -135,20 +135,22 @@ module.exports = function(options) {
},
}
$window.history = {
pushState: function(data, title, url) {
past.push({url: getURL(), isNew: false})
pushState: function(state, title, url) {
past.push({url: getURL(), isNew: false, state: state, title: title})
future = []
setURL(url)
},
replaceState: function(data, title, url) {
future = []
replaceState: function(state, title, url) {
var entry = past[past.length - 1]
entry.state = state
entry.title = title
setURL(url)
},
back: function() {
var entry = past.pop()
if (entry != null) {
if (past.length > 1) {
var entry = past.pop()
if (entry.isNew) unload()
future.push({url: getURL(), isNew: false})
future.push({url: getURL(), isNew: false, state: entry.state, title: entry.title})
setURL(entry.url)
if (!entry.isNew) popstate()
}
@ -157,11 +159,14 @@ module.exports = function(options) {
var entry = future.pop()
if (entry != null) {
if (entry.isNew) unload()
past.push({url: getURL(), isNew: false})
past.push({url: getURL(), isNew: false, state: entry.state, title: entry.title})
setURL(entry.url)
if (!entry.isNew) popstate()
}
},
get state() {
return past.length === 0 ? null : past[past.length - 1].state
},
}
$window.onpopstate = null,
$window.onhashchange = null,

View file

@ -417,6 +417,71 @@ o.spec("pushStateMock", function() {
$window.history.pushState(null, null, "b")
$window.history.back()
})
o("replaceState does not break forward history", function() {
$window.onpopstate = o.spy()
$window.history.pushState(null, null, "b")
$window.history.back()
o($window.onpopstate.callCount).equals(1)
o($window.location.href).equals("http://localhost/")
$window.history.replaceState(null, null, "a")
o($window.location.href).equals("http://localhost/a")
$window.history.forward()
o($window.onpopstate.callCount).equals(2)
o($window.location.href).equals("http://localhost/b")
})
o("pushstate retains state", function() {
$window.onpopstate = o.spy()
$window.history.pushState({a: 1}, null, "#a")
$window.history.pushState({b: 2}, null, "#b")
o($window.onpopstate.callCount).equals(0)
$window.history.back()
o($window.onpopstate.callCount).equals(1)
o($window.onpopstate.args[0].type).equals("popstate")
o($window.onpopstate.args[0].state).deepEquals({a: 1})
$window.history.back()
o($window.onpopstate.callCount).equals(2)
o($window.onpopstate.args[0].type).equals("popstate")
o($window.onpopstate.args[0].state).equals(null)
$window.history.forward()
o($window.onpopstate.callCount).equals(3)
o($window.onpopstate.args[0].type).equals("popstate")
o($window.onpopstate.args[0].state).deepEquals({a: 1})
$window.history.forward()
o($window.onpopstate.callCount).equals(4)
o($window.onpopstate.args[0].type).equals("popstate")
o($window.onpopstate.args[0].state).deepEquals({b: 2})
})
o("replacestate replaces state", function() {
$window.onpopstate = o.spy(pop)
$window.history.replaceState({a: 1}, null, "a")
o($window.history.state).deepEquals({a: 1})
$window.history.pushState(null, null, "a")
$window.history.back()
function pop(e) {
o(e.state).deepEquals({a: 1})
o($window.history.state).deepEquals({a: 1})
}
})
})
o.spec("onhashchance", function() {
o("onhashchange triggers on location.href change", function() {