From 834dd0261043db05f3071bcceac510646cb3dca8 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Wed, 11 Jan 2017 14:23:42 -0800 Subject: [PATCH 1/6] feat: bring back m.route.params() Because it's annoying only having route params available to the top-level components within a route. --- api/router.js | 4 ++++ api/tests/test-router.js | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/api/router.js b/api/router.js index cf406737..c25ac257 100644 --- a/api/router.js +++ b/api/router.js @@ -54,6 +54,10 @@ module.exports = function($window, redrawService) { route.set(href, undefined, undefined) } } + route.params = function(key) { + if(typeof attrs !== "undefined" && typeof key !== "undefined") return attrs[key] + return attrs + } return route } diff --git a/api/tests/test-router.js b/api/tests/test-router.js index 6a281237..f97fc5db 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -1194,6 +1194,27 @@ o.spec("route", function() { done() }, FRAME_BUDGET * 2) }) + + o("m.route.params is available outside of route handlers", function(done) { + $window.location.href = prefix + "/" + + route(root, "/1", { + "/:id" : { + view : function() { + o(route.params("id")).equals("1") + + return m("div") + } + } + }) + + callAsync(function() { + o(route.params("id")).equals("1") + o(route.params()).deepEquals({id:"1"}) + + done() + }) + }) }) }) }) From 2cdd8a96d1c531aeca66e890621d44374d6b5238 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Wed, 11 Jan 2017 14:25:19 -0800 Subject: [PATCH 2/6] tests: undefined until a route has been matched --- api/tests/test-router.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/tests/test-router.js b/api/tests/test-router.js index f97fc5db..b3975f21 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -1208,6 +1208,9 @@ o.spec("route", function() { } }) + o(route.params("id")).equals(undefined); + o(route.params()).deepEquals(undefined); + callAsync(function() { o(route.params("id")).equals("1") o(route.params()).deepEquals({id:"1"}) From d119accb21e3b7f9ffb199e451ec8a3e464df549 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Wed, 11 Jan 2017 15:14:29 -0800 Subject: [PATCH 3/6] fix: m.route.param, not m.route.params :sweat: --- api/router.js | 2 +- api/tests/test-router.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api/router.js b/api/router.js index c25ac257..1d267bb8 100644 --- a/api/router.js +++ b/api/router.js @@ -54,7 +54,7 @@ module.exports = function($window, redrawService) { route.set(href, undefined, undefined) } } - route.params = function(key) { + route.param = function(key) { if(typeof attrs !== "undefined" && typeof key !== "undefined") return attrs[key] return attrs } diff --git a/api/tests/test-router.js b/api/tests/test-router.js index b3975f21..8fec9042 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -1195,25 +1195,25 @@ o.spec("route", function() { }, FRAME_BUDGET * 2) }) - o("m.route.params is available outside of route handlers", function(done) { + o("m.route.param is available outside of route handlers", function(done) { $window.location.href = prefix + "/" route(root, "/1", { "/:id" : { view : function() { - o(route.params("id")).equals("1") + o(route.param("id")).equals("1") return m("div") } } }) - o(route.params("id")).equals(undefined); - o(route.params()).deepEquals(undefined); + o(route.param("id")).equals(undefined); + o(route.param()).deepEquals(undefined); callAsync(function() { - o(route.params("id")).equals("1") - o(route.params()).deepEquals({id:"1"}) + o(route.param("id")).equals("1") + o(route.param()).deepEquals({id:"1"}) done() }) From 7a4349c3320b1d0c3cc46f5fdda1c48bbe97ce3f Mon Sep 17 00:00:00 2001 From: Patrik Johnson Date: Thu, 12 Jan 2017 01:41:15 +0200 Subject: [PATCH 4/6] Fix small typos in the rewrite docs (#1533) --- docs/autoredraw.md | 2 +- docs/hyperscript.md | 2 +- docs/redraw.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/autoredraw.md b/docs/autoredraw.md index 97a4575d..a3a1360a 100644 --- a/docs/autoredraw.md +++ b/docs/autoredraw.md @@ -87,7 +87,7 @@ m.route(document.body, "/", { --- -### When Mithril does not redraws +### When Mithril does not redraw Mithril does not redraw after `setTimeout`, `setInterval`, `requestAnimationFrame` and 3rd party library event handlers (e.g. Socket.io callbacks). In those cases, you must manually call [`m.redraw()`](redraw.md). diff --git a/docs/hyperscript.md b/docs/hyperscript.md index 6108f2d5..f1fb04ca 100644 --- a/docs/hyperscript.md +++ b/docs/hyperscript.md @@ -170,7 +170,7 @@ Mithril supports both strings and objects as valid `style` values. In other word ```javascript m("div", {style: "background:red;"}) m("div", {style: {background: "red"}}) -m("div[style=background:red") +m("div[style=background:red]") ``` Using a string as a `style` would overwrite all inline styles in the element if it is redrawn, and not only CSS rules whose values have changed. diff --git a/docs/redraw.md b/docs/redraw.md index be215e18..85b397dc 100644 --- a/docs/redraw.md +++ b/docs/redraw.md @@ -14,7 +14,7 @@ You DON'T need to call it if data is modified within the execution context of an You DO need to call it in `setTimeout`/`setInterval`/`requestAnimationFrame` callbacks, or callbacks from 3rd party libraries. -Typically, `m.redraw` triggers an asynchronous redraws, but it may trigger synchronously if Mithril detects it's possible to improves performance by doing so (i.e. if no redraw was requested within the last animation frame). You should write code assuming that it always redraws asynchronously. +Typically, `m.redraw` triggers an asynchronous redraws, but it may trigger synchronously if Mithril detects it's possible to improve performance by doing so (i.e. if no redraw was requested within the last animation frame). You should write code assuming that it always redraws asynchronously. --- From d63a88618ee5d87f7254a9336eae48fd589aa38a Mon Sep 17 00:00:00 2001 From: Patrik Johnson Date: Thu, 12 Jan 2017 01:57:14 +0200 Subject: [PATCH 5/6] Rewrite docs clarity (#1534) * Add a few words and bits for clarity and better text flow in the rewrite docs * Rephrase some helpful wording --- docs/change-log.md | 4 ++-- docs/request.md | 2 +- docs/simple-application.md | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/change-log.md b/docs/change-log.md index c97d7d12..2652b700 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -39,7 +39,7 @@ If you are migrating, consider using the [mithril-codemods](https://www.npmjs.co ## `m.prop` removed -In `v1.x`, `m.prop()` is now a more powerful stream micro-library, but it's no longer part of core. +In `v1.x`, `m.prop()` is now a more powerful stream micro-library, but it's no longer part of core. You can read about how to use the optional Streams module in [the documentation](streams.md). ### `v0.2.x` @@ -499,7 +499,7 @@ setTimeout(function() { }, 1000) ``` -Additionally, if the `extract` option is passed to `m.request` the return value of the provided function will be used directly to resolve its promise, and the `deserialize` callback is ignored. +Additionally, if the `extract` option is passed to `m.request` the return value of the provided function will be used directly to resolve the request promise, and the `deserialize` callback is ignored. --- diff --git a/docs/request.md b/docs/request.md index 3671f957..cd6851af 100644 --- a/docs/request.md +++ b/docs/request.md @@ -54,7 +54,7 @@ Argument | Type | Required | Descr `options.type` | `any = Function(any)` | No | A constructor to be applied to each object in the response. Defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function). `options.serialize` | `string = Function(any)` | No | A serialization method to be applied to `data`. Defaults to `JSON.stringify`, or if `options.data` is an instance of [`FormData`](https://developer.mozilla.org/en/docs/Web/API/FormData), defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function) (i.e. `function(value) {return value}`). `options.deserialize` | `any = Function(string)` | No | A deserialization method to be applied to the response. Defaults to a small wrapper around `JSON.parse` that returns `null` for empty responses. -`options.extract` | `string = Function(xhr, options)` | No | A hook to specify how the XMLHttpRequest response should be read. Useful for reading response headers and cookies. Defaults to a function that returns `xhr.responseText`. If defined, the `xhr` parameter is the XMLHttpRequest instance used for the request, and `options` is the object that was passed to the `m.request` call. If a custom `extract` callback is set, `options.deserialize` is ignored and the string returned from the extract callback will not be parsed as JSON. +`options.extract` | `string = Function(xhr, options)` | No | A hook to specify how the XMLHttpRequest response should be read. Useful for reading response headers and cookies. Defaults to a function that returns `xhr.responseText`. If defined, the `xhr` parameter is the XMLHttpRequest instance used for the request, and `options` is the object that was passed to the `m.request` call. If a custom `extract` callback is set, `options.deserialize` is ignored and the string returned from the extract callback will not automatically be parsed as JSON. `options.useBody` | `Boolean` | No | Force the use of the HTTP body section for `data` in `GET` requests when set to `true`, or the use of querystring for other HTTP methods when set to `false`. Defaults to `false` for `GET` requests and `true` for other methods. `options.background` | `Boolean` | No | If `false`, redraws mounted components upon completion of the request. If `true`, it does not. Defaults to `false`. **returns** | `Promise` | | A promise that resolves to the response data, after it has been piped through the `extract`, `deserialize` and `type` methods diff --git a/docs/simple-application.md b/docs/simple-application.md index f23ff82b..fd759021 100644 --- a/docs/simple-application.md +++ b/docs/simple-application.md @@ -185,7 +185,7 @@ module.exports = { Notice that we added an `oninit` method to the component, which references `User.loadList`. This means that when the component initializes, User.loadList will be called, triggering an XHR request. When the server returns a response, `User.list` gets populated. -Also notice we **didn't** do `oninit: User.loadList()` (with parentheses at the end). The difference is that `oninit: User.loadList()` calls the function once and immediately, but `oninit: User.loadList` only calls that function when the component renders. This is an important difference and a common newbie mistake: calling the function immediately means that the XHR request will fire even if the component never renders. Also, if the component is ever recreated (through navigating back and forth through the application), the function won't be called again as expected. +Also notice we **didn't** do `oninit: User.loadList()` (with parentheses at the end). The difference is that `oninit: User.loadList()` calls the function once and immediately, but `oninit: User.loadList` only calls that function when the component renders. This is an important difference and a common pitfall for developers new to javascript: calling the function immediately means that the XHR request will fire as soon as the source code is evaluated, even if the component never renders. Also, if the component is ever recreated (through navigating back and forth through the application), the function won't be called again as expected. --- @@ -199,7 +199,7 @@ var UserList = require("./view/UserList") m.mount(document.body, UserList) ``` -The `m.mount` call renders the specified component (`UserList`) into a DOM element (`document.body`), erasing any DOM that were there previously. Opening the HTML file in a browser should now display a list of person names. +The `m.mount` call renders the specified component (`UserList`) into a DOM element (`document.body`), erasing any DOM that was there previously. Opening the HTML file in a browser should now display a list of person names. --- @@ -207,9 +207,9 @@ Right now, the list looks rather plain because we have not defined any styles. There are many similar conventions and libraries that help organize application styles nowadays. Some, like [Bootstrap](http://getbootstrap.com/) dictate a specific set of HTML structures and semantically meaningful class names, which has the upside of providing low cognitive dissonance, but the downside of making customization more difficult. Others, like [Tachyons](http://tachyons.io/) provide a large number of self-describing, atomic class names at the cost of making the class names themselves non-semantic. "CSS-in-JS" is another type of CSS system that is growing in popularity, which basically consists of scoping CSS via transpilation tooling. CSS-in-JS libraries achieve maintainability by reducing the size of the problem space, but come at the cost of having high complexity. -Regardless of what CSS convention/library you choose, a good rule of thumb is to avoid the cascading aspect of CSS. To keep this tutorial simple, we'll just use plain CSS with overly explicit class names, so that the styles themselves provide the atomicity of Tachyons, and class name collisions are made unlikely through the verbosity of the class names. Plain CSS can be sufficient for low-complexity projects (e.g. 3 to 6 man-months of initial implementation time and few project phases) +Regardless of what CSS convention/library you choose, a good rule of thumb is to avoid the cascading aspect of CSS. To keep this tutorial simple, we'll just use plain CSS with overly explicit class names, so that the styles themselves provide the atomicity of Tachyons, and class name collisions are made unlikely through the verbosity of the class names. Plain CSS can be sufficient for low-complexity projects (e.g. 3 to 6 man-months of initial implementation time and few project phases). -To add styles, let's first create a file called `styles.css` and include it in the `index.html` file +To add styles, let's first create a file called `styles.css` and include it in the `index.html` file: ```markup From ca5eedc8a653f8a211cf6568b4ced470197c17c9 Mon Sep 17 00:00:00 2001 From: Gandalf-the-Bot Date: Thu, 12 Jan 2017 13:03:29 +0000 Subject: [PATCH 6/6] Bundled output for commit 6d5e8225cc9810e7fb439810cd443f954b568e47 [skip ci] --- README.md | 2 +- mithril.js | 4 +++ mithril.min.js | 80 +++++++++++++++++++++++++------------------------- 3 files changed, 45 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index bfe22f41..4fa9686e 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.60 KB min+gzip +Despite the huge improvements in performance and modularity, the new codebase is smaller than v0.2.x, currently clocking at 7.61 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/mithril.js b/mithril.js index cbc385e9..db5289b6 100644 --- a/mithril.js +++ b/mithril.js @@ -1133,6 +1133,10 @@ var _20 = function($window, redrawService0) { route.set(href, undefined, undefined) } } + route.param = function(key3) { + if(typeof attrs3 !== "undefined" && typeof key3 !== "undefined") return attrs3[key3] + return attrs3 + } return route } m.route = _20(window, redrawService) diff --git a/mithril.min.js b/mithril.min.js index ad515a32..47eda8ed 100644 --- a/mithril.min.js +++ b/mithril.min.js @@ -1,42 +1,42 @@ -new function(){function w(a,c,k,d,h,m){return{tag:a,key:c,attrs:k,children:d,text:h,dom:m,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===G[a]){for(var c,k,d=[],h={};c=N.exec(a);){var m=c[1],q=c[2];""===m&&""!==q?k=q:"#"===m?h.id=q:"."===m?d.push(q):"["===c[3][0]&&((m=c[6])&&(m=m.replace(/\\(["'])/g,"$1").replace(/\\\\/g,"\\")), -"class"===c[4]?d.push(m):h[c[4]]=m||!0)}0b.indexOf("?")?"?":"&";b+=d+c}return b}function q(a){try{return""!==a?JSON.parse(a):null}catch(r){throw Error(a); -}}function l(a){return a.responseText}function t(a,c){if("function"===typeof a)if(Array.isArray(c))for(var b=0;bn.status||304===n.status)c(t(b.type,a));else{var g=Error(n.responseText),e;for(e in a)g[e]=a[e]; -d(g)}}catch(f){d(f)}};k&&null!=b.data?n.send(b.data):n.send()});return!0===b.background?r:z(r)},jsonp:function(b,l){var q=k();b=d(b,l);var z=new c(function(c,d){var k=b.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+p++,l=a.document.createElement("script");a[k]=function(d){l.parentNode.removeChild(l);c(t(b.type,d));delete a[k]};l.onerror=function(){l.parentNode.removeChild(l);d(Error("JSONP request failed"));delete a[k]};null==b.data&&(b.data={});b.url=h(b.url,b.data);b.data[b.callbackKey|| -"callback"]=k;l.src=m(b.url,b.data);a.document.documentElement.appendChild(l)});return!0===b.background?z:q(z)},setCompletionCallback:function(a){z=a}}}(window,x),M=function(a){function c(g,e,a,b,c,d,h){for(;ab.indexOf("?")?"?":"&";b+=d+c}return b}function l(a){try{return""!==a?JSON.parse(a):null}catch(A){throw Error(a); +}}function p(a){return a.responseText}function r(a,c){if("function"===typeof a)if(Array.isArray(c))for(var b=0;bn.status||304===n.status)c(r(b.type,a));else{var g=Error(n.responseText),e;for(e in a)g[e]=a[e]; +d(g)}}catch(f){d(f)}};k&&null!=b.data?n.send(b.data):n.send()});return!0===b.background?A:q(A)},jsonp:function(b,l){var p=k();b=d(b,l);var u=new c(function(c,d){var k=b.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+q++,l=a.document.createElement("script");a[k]=function(d){l.parentNode.removeChild(l);c(r(b.type,d));delete a[k]};l.onerror=function(){l.parentNode.removeChild(l);d(Error("JSONP request failed"));delete a[k]};null==b.data&&(b.data={});b.url=h(b.url,b.data);b.data[b.callbackKey|| +"callback"]=k;l.src=m(b.url,b.data);a.document.documentElement.appendChild(l)});return!0===b.background?u:p(u)},setCompletionCallback:function(a){u=a}}}(window,w),M=function(a){function c(g,e,a,b,c,d,h){for(;a=u&&r>=n;){var y=e[u],v=a[n];if(y!==v||f)if(null==y)u++;else if(null==v)n++;else if(y.key===v.key)u++,n++,m(g,y,v,b,l(e,u,d),f,h),f&&y.tag===v.tag&&t(g,q(y),d);else if(y=e[p],y!==v||f)if(null==y)p--;else if(null==v)n++;else if(y.key===v.key)m(g,y,v,b,l(e, -p+1,d),f,h),(f||n=u&&r>=n;){y=e[p];v=a[r];if(y!==v||f)if(null==y)p--;else{if(null!=v)if(y.key===v.key)m(g,y,v,b,l(e,p+1,d),f,h),f&&y.tag===v.tag&&t(g,q(y),d),null!=y.dom&&(d=y.dom),p--;else{if(!A){A=e;var y=p,C={},w;for(w=0;w=t&&A>=n;){var y=e[t],v=a[n];if(y!==v||f)if(null==y)t++;else if(null==v)n++;else if(y.key===v.key)t++,n++,m(g,y,v,b,p(e,t,d),f,h),f&&y.tag===v.tag&&r(g,l(y),d);else if(y=e[q],y!==v||f)if(null==y)q--;else if(null==v)n++;else if(y.key===v.key)m(g,y,v,b,p(e, +q+1,d),f,h),(f||n=t&&A>=n;){y=e[q];v=a[A];if(y!==v||f)if(null==y)q--;else{if(null!=v)if(y.key===v.key)m(g,y,v,b,p(e,q+1,d),f,h),f&&y.tag===v.tag&&r(g,l(y),d),null!=y.dom&&(d=y.dom),q--;else{if(!z){z=e;var y=q,C={},x;for(x=0;x