diff --git a/.eslintrc.js b/.eslintrc.js index 523eeb4c..25278201 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -182,7 +182,7 @@ module.exports = { "prefer-const": "error", "prefer-reflect": "off", "prefer-rest-params": "off", - "prefer-spread": "error", + "prefer-spread": "off", "prefer-template": "off", "quote-props": "off", "quotes": [ diff --git a/README.md b/README.md index 11a87ed7..ef729c3b 100644 --- a/README.md +++ b/README.md @@ -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 7.47 KB min+gzip +Despite the huge improvements in performance and modularity, the new codebase is smaller than v0.2.x, currently clocking at 7.44 KB 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 diff --git a/api/router.js b/api/router.js index 26acf219..91f4eade 100644 --- a/api/router.js +++ b/api/router.js @@ -7,36 +7,37 @@ module.exports = function($window, redrawService) { var routeService = coreRouter($window) var identity = function(v) {return v} - var current = {render: identity, component: null, path: null, resolve: null} + var current = {} 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") - var render = function(resolver, component, params, path) { - current.render = resolver.render || identity - current.component = component - current.path = path - current.resolve = null - redrawService.render(root, current.render.call(resolver, Vnode(component, undefined, params))) + var update = function(resolver, component, params, path) { + current = {resolver: resolver, component: component, params: params, path: path, resolve: null} + current.resolver.render = resolver.render || identity + render() } - var run = routeService.defineRoutes(routes, function(component, params, path, route, isAction) { - if (component.view) render({}, component, params, path) + var render = function() { + redrawService.render(root, current.resolver.render(Vnode(current.component, current.params.key, current.params))) + } + routeService.defineRoutes(routes, function(component, params, path) { + if (component.view) update({}, component, params, path) else { if (component.onmatch) { - if (isAction === false && current.path === path || current.resolve != null) render(current, current.component, params) + if (current.resolve != null) update(component, current.component, params, path) else { current.resolve = function(resolved) { - render(component, resolved, params, path) + update(component, resolved, params, path) } component.onmatch(function(resolved) { if (current.resolve != null) current.resolve(resolved) }, params, path) } } - else render(component, "div", params, path) + else update(component, "div", params, path) } }, function() { routeService.setPath(defaultRoute) }) - redrawService.subscribe(root, run) + redrawService.subscribe(root, render) } route.set = routeService.setPath route.get = function() {return current.path} diff --git a/api/tests/test-router.js b/api/tests/test-router.js index 30914607..09c46eee 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -521,7 +521,7 @@ o.spec("route", function() { $window.location.href = prefix + "/" route(root, '/', { - "/":{ + "/": { onmatch: onmatch, render: render } diff --git a/mithril.js b/mithril.js index 8785809d..75a366ce 100644 --- a/mithril.js +++ b/mithril.js @@ -960,21 +960,21 @@ var parseQueryString = function(string) { var entries = string.split("&"), data0 = {}, counters = {} for (var i = 0; i < entries.length; i++) { var entry = entries[i].split("=") - var key4 = decodeURIComponent(entry[0]) + var key5 = decodeURIComponent(entry[0]) var value = entry.length === 2 ? decodeURIComponent(entry[1]) : "" if (value === "true") value = true else if (value === "false") value = false - var levels = key4.split(/\]\[?|\[/) + var levels = key5.split(/\]\[?|\[/) var cursor = data0 - if (key4.indexOf("[") > -1) levels.pop() + if (key5.indexOf("[") > -1) levels.pop() for (var j = 0; j < levels.length; j++) { var level = levels[j], nextLevel = levels[j + 1] var isNumber = nextLevel == "" || !isNaN(parseInt(nextLevel, 10)) var isValue = j === levels.length - 1 if (level === "") { - var key4 = levels.slice(0, j).join() - if (counters[key4] == null) counters[key4] = 0 - level = counters[key4]++ + var key5 = levels.slice(0, j).join() + if (counters[key5] == null) counters[key5] = 0 + level = counters[key5]++ } if (cursor[level] == null) { cursor[level] = isValue ? value : isNumber ? [] : {} @@ -996,11 +996,11 @@ var coreRouter = function($window) { } var asyncId function debounceAsync(f) { - return function(e) { + return function() { if (asyncId != null) return asyncId = callAsync0(function() { asyncId = null - f(e) + f() }) } } @@ -1011,11 +1011,11 @@ var coreRouter = function($window) { if (queryIndex > -1) { var queryEnd = hashIndex > -1 ? hashIndex : path.length var queryParams = parseQueryString(path.slice(queryIndex + 1, queryEnd)) - for (var key3 in queryParams) queryData[key3] = queryParams[key3] + for (var key4 in queryParams) queryData[key4] = queryParams[key4] } if (hashIndex > -1) { var hashParams = parseQueryString(path.slice(hashIndex + 1)) - for (var key3 in hashParams) hashData[key3] = hashParams[key3] + for (var key4 in hashParams) hashData[key4] = hashParams[key4] } return path.slice(0, pathEnd) } @@ -1031,7 +1031,7 @@ var coreRouter = function($window) { var queryData = {}, hashData = {} path = parsePath(path, queryData, hashData) if (data != null) { - for (var key3 in data) queryData[key3] = data[key3] + for (var key4 in data) queryData[key4] = data[key4] path = path.replace(/:([^\/]+)/g, function(match2, token) { delete queryData[token] return data[token] @@ -1049,7 +1049,7 @@ var coreRouter = function($window) { else $window.location.href = prefix1 + path } function defineRoutes(routes, resolve, reject) { - function resolveRoute(isAction) { + function resolveRoute() { var path = getPath() var params = {} var pathname = parsePath(path, params, params) @@ -1063,7 +1063,7 @@ var coreRouter = function($window) { for (var i = 0; i < keys.length; i++) { params[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i]) } - resolve(routes[route0], params, path, route0, Boolean(isAction)) + resolve(routes[route0], params, path, route0) }) return } @@ -1073,9 +1073,7 @@ var coreRouter = function($window) { if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute) else if (prefix1.charAt(0) === "#") $window.onhashchange = resolveRoute - resolveRoute(true) - - return resolveRoute + resolveRoute() } function link(vnode2) { vnode2.dom.setAttribute("href", prefix1 + vnode2.attrs.href) @@ -1094,36 +1092,37 @@ var _20 = function($window, redrawService0) { var routeService = coreRouter($window) var identity = function(v) {return v} - var current = {render: identity, component: null, path: null, resolve: null} + var current = {} 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") - var render1 = function(resolver, component, params, path) { - current.render = resolver.render || identity - current.component = component - current.path = path - current.resolve = null - redrawService0.render(root, current.render.call(resolver, Vnode(component, undefined, params))) + var update = function(resolver, component, params, path) { + current = {resolver: resolver, component: component, params: params, path: path, resolve: null} + current.resolver.render = resolver.render || identity + render1() } - var run1 = routeService.defineRoutes(routes, function(component, params, path, route, isAction) { - if (component.view) render1({}, component, params, path) + var render1 = function() { + redrawService0.render(root, current.resolver.render(Vnode(current.component, current.params.key, current.params))) + } + routeService.defineRoutes(routes, function(component, params, path, route) { + if (component.view) update({}, component, params, path) else { if (component.onmatch) { - if (isAction === false && current.path === path || current.resolve != null) render1(current, current.component, params) + if (current.resolve != null) update(component, current.component, params, path) else { current.resolve = function(resolved) { - render1(component, resolved, params, path) + update(component, resolved, params, path) } component.onmatch(function(resolved) { if (current.resolve != null) current.resolve(resolved) }, params, path) } } - else render1(component, "div", params, path) + else update(component, "div", params, path) } }, function() { routeService.setPath(defaultRoute) }) - redrawService0.subscribe(root, run1) + redrawService0.subscribe(root, render1) } route.set = routeService.setPath route.get = function() {return current.path} diff --git a/mithril.min.js b/mithril.min.js index e7b9b7d0..011140f3 100644 --- a/mithril.min.js +++ b/mithril.min.js @@ -1,41 +1,41 @@ -new function(){function m(a,c,g,f,d,k){return{tag:a,key:c,attrs:g,children:f,text:d,dom:k,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===I[a]){for(var c,g,f=[],d={};c=N.exec(a);){var k=c[1],n=c[2];""===k&&""!==n?g=n:"#"===k?d.id=n:"."===k?f.push(n):"["===c[3][0]&&((k=c[6])&&(k=k.replace(/\\(["'])/g,"$1").replace(/\\\\/g,"\\")), -"class"===c[4]?f.push(k):d[c[4]]=k||!0)}0b.indexOf("?")?"?":"&";b+=d+c}return b}function n(b){try{return""!==b?JSON.parse(b):null}catch(B){throw Error(b);}}function r(b){return b.responseText}function p(b,a){if("function"=== -typeof b)if(a instanceof Array)for(var c=0;cg.status||304===g.status)c(p(b.type,a));else{var d=Error(g.responseText),h;for(h in a)d[h]=a[h];f(d)}}catch(F){f(F)}};h&&null!=b.data?g.send(b.data):g.send()});return!0===b.background?t:v(t)},jsonp:function(b,h){var n=g();b=f(b,h);var t=new c(function(c,f){var g=b.callbackName|| -"_mithril_"+Math.round(1E16*Math.random())+"_"+m++,h=a.document.createElement("script");a[g]=function(d){h.parentNode.removeChild(h);c(p(b.type,d));delete a[g]};h.onerror=function(){h.parentNode.removeChild(h);f(Error("JSONP request failed"));delete a[g]};null==b.data&&(b.data={});b.url=d(b.url,b.data);b.data[b.callbackKey||"callback"]=g;h.src=k(b.url,b.data);a.document.documentElement.appendChild(h)});return!0===b.background?t:n(t)},setCompletionCallback:function(a){h=a}}}(window,"undefined"!==typeof Promise? -Promise:u),M=function(a){function c(e,l,a,b,c,d,f){for(;a=w&&v>=y;){var x=l[w],m=a[y];if(x!==m||q)if(null==x)w++;else if(null==m)y++;else if(x.key===m.key)w++,y++,k(e,x,m,b,r(l,w,d),q,f),q&&x.tag===m.tag&&p(e,n(x),d);else if(x=l[t],x!==m||q)if(null==x)t--;else if(null==m)y++;else if(x.key===m.key)k(e,x,m,b,r(l,t+1,d),q,f),(q||y=w&&v>=y;){x=l[t];m=a[v];if(x!==m||q)if(null==x)t--;else{if(null!= -m)if(x.key===m.key)k(e,x,m,b,r(l,t+1,d),q,f),q&&x.tag===m.tag&&p(e,n(x),d),null!=x.dom&&(d=x.dom),t--;else{if(!G){G=l;var x=t,B={},u;for(u=0;ub.indexOf("?")?"?":"&";b+=d+c}return b}function n(b){try{return""!==b?JSON.parse(b):null}catch(B){throw Error(b);}}function r(b){return b.responseText}function q(b,a){if("function"=== +typeof b)if(a instanceof Array)for(var c=0;ch.status||304===h.status)c(q(b.type,a));else{var d=Error(h.responseText),f;for(f in a)d[f]=a[f];g(d)}}catch(E){g(E)}};k&&null!=b.data?h.send(b.data):h.send()});return!0===b.background?u:v(u)},jsonp:function(b,h){var n=k();b=g(b,h);var u=new c(function(c,g){var h=b.callbackName|| +"_mithril_"+Math.round(1E16*Math.random())+"_"+m++,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);g(Error("JSONP request failed"));delete a[h]};null==b.data&&(b.data={});b.url=d(b.url,b.data);b.data[b.callbackKey||"callback"]=h;k.src=f(b.url,b.data);a.document.documentElement.appendChild(k)});return!0===b.background?u:n(u)},setCompletionCallback:function(a){h=a}}}(window,"undefined"!==typeof Promise? +Promise:t),N=function(a){function c(e,l,a,b,c,d,f){for(;a=w&&v>=u;){var x=l[w],m=a[u];if(x!==m||p)if(null==x)w++;else if(null==m)u++;else if(x.key===m.key)w++,u++,f(e,x,m,b,r(l,w,d),p,g),p&&x.tag===m.tag&&q(e,n(x),d);else if(x=l[y],x!==m||p)if(null==x)y--;else if(null==m)u++;else if(x.key===m.key)f(e,x,m,b,r(l,y+1,d),p,g),(p||u=w&&v>=u;){x=l[y];m=a[v];if(x!==m||p)if(null==x)y--;else{if(null!= +m)if(x.key===m.key)f(e,x,m,b,r(l,y+1,d),p,g),p&&x.tag===m.tag&&q(e,n(x),d),null!=x.dom&&(d=x.dom),y--;else{if(!F){F=l;var x=y,B={},C;for(C=0;C