From b4856a9ecd946356e882f1b6b7d7c368aa537e40 Mon Sep 17 00:00:00 2001 From: tkilliansc Date: Thu, 3 Aug 2017 13:48:19 -0700 Subject: [PATCH 1/7] fix issue 1919 --- docs/change-log.md | 1 + request/request.js | 4 ++-- test-utils/tests/test-xhrMock.js | 8 ++++++++ test-utils/xhrMock.js | 12 +++++++++++- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/docs/change-log.md b/docs/change-log.md index cb2fabf0..de0b3769 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -24,6 +24,7 @@ #### Bug fixes - API: `m.route.set()` causes all mount points to be redrawn ([#1592](https://github.com/MithrilJS/mithril.js/pull/1592)) +- API: If a user sets the Content-Type header within a request's options, that value will be the entire header value rather than being appended to the default value ([#1924](https://github.com/MithrilJS/mithril.js/pull/1924)) --- diff --git a/request/request.js b/request/request.js index ac5f3391..7e4ec744 100644 --- a/request/request.js +++ b/request/request.js @@ -67,10 +67,10 @@ module.exports = function($window, Promise) { xhr.open(args.method, args.url, typeof args.async === "boolean" ? args.async : true, typeof args.user === "string" ? args.user : undefined, typeof args.password === "string" ? args.password : undefined) - if (args.serialize === JSON.stringify && useBody) { + if (args.serialize === JSON.stringify && useBody && !(args.headers && args.headers.hasOwnProperty("Content-Type"))) { xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8") } - if (args.deserialize === deserialize) { + if (args.deserialize === deserialize && !(args.headers && args.headers.hasOwnProperty("Accept"))) { xhr.setRequestHeader("Accept", "application/json, text/*") } if (args.withCredentials) xhr.withCredentials = args.withCredentials diff --git a/test-utils/tests/test-xhrMock.js b/test-utils/tests/test-xhrMock.js index 8bdfcf21..8c64a308 100644 --- a/test-utils/tests/test-xhrMock.js +++ b/test-utils/tests/test-xhrMock.js @@ -72,6 +72,14 @@ o.spec("xhrMock", function() { } xhr.send("a=b") }) + o("Setting a header twice merges the header", function() { + // Source: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader + var xhr = new $window.XMLHttpRequest() + xhr.open("POST", "/test") + xhr.setRequestHeader("Content-Type", "foo") + xhr.setRequestHeader("Content-Type", "bar") + o(xhr.getRequestHeader("Content-Type")).equals("foo, bar") + }) }) o.spec("jsonp", function() { o("works", function(done) { diff --git a/test-utils/xhrMock.js b/test-utils/xhrMock.js index d3bbf5b8..379bce28 100644 --- a/test-utils/xhrMock.js +++ b/test-utils/xhrMock.js @@ -17,7 +17,17 @@ module.exports = function() { var headers = {} var aborted = false this.setRequestHeader = function(header, value) { - headers[header] = value + /* + the behavior of setHeader is not your expected setX API. + If the header is already set, it'll merge with whatever you add + rather than overwrite + Source: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader + */ + if (headers[header]) { + headers[header] += ", " + value; + } else { + headers[header] = value + } } this.getRequestHeader = function(header) { return headers[header] From 771dcd4738ff21468489cff015ae293b63b31777 Mon Sep 17 00:00:00 2001 From: Gandalf-the-Bot Date: Thu, 3 Aug 2017 23:07:49 +0000 Subject: [PATCH 2/7] Bundled output for commit 2032131340e51aa12ada12b89099c00de5b9d6e8 [skip ci] --- README.md | 2 +- mithril.js | 4 +-- mithril.min.js | 66 +++++++++++++++++++++++++------------------------- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 4638fa31..0e5959d8 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ mithril.js [![NPM Version](https://img.shields.io/npm/v/mithril.svg)](https://ww ## What is Mithril? -A modern client-side Javascript framework for building Single Page Applications. It's small (8.22 KB gzipped), fast and provides routing and XHR utilities out of the box. +A modern client-side Javascript framework for building Single Page Applications. It's small (8.25 KB gzipped), fast and provides routing and XHR utilities out of the box. Mithril is used by companies like Vimeo and Nike, and open source platforms like Lichess 👍. diff --git a/mithril.js b/mithril.js index 77e3c6d9..f6bd38af 100644 --- a/mithril.js +++ b/mithril.js @@ -274,10 +274,10 @@ var _8 = function($window, Promise) { _abort.call(xhr) } xhr.open(args.method, args.url, typeof args.async === "boolean" ? args.async : true, typeof args.user === "string" ? args.user : undefined, typeof args.password === "string" ? args.password : undefined) - if (args.serialize === JSON.stringify && useBody) { + if (args.serialize === JSON.stringify && useBody && !(args.headers && args.headers.hasOwnProperty("Content-Type"))) { xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8") } - if (args.deserialize === deserialize) { + if (args.deserialize === deserialize && !(args.headers && args.headers.hasOwnProperty("Accept"))) { xhr.setRequestHeader("Accept", "application/json, text/*") } if (args.withCredentials) xhr.withCredentials = args.withCredentials diff --git a/mithril.min.js b/mithril.min.js index c68ee276..edc24225 100644 --- a/mithril.min.js +++ b/mithril.min.js @@ -9,36 +9,36 @@ g[h]=b;n===l&&d(g)}null==b[h]||"object"!==typeof b[h]&&"function"!==typeof b[h]| 0;ha.indexOf("?")?"?":"&";a+=e+d}return a}function h(a){try{return""!==a?JSON.parse(a):null}catch(y){throw Error(a);}}function r(a){return a.responseText} function f(a,b){if("function"===typeof a)if(Array.isArray(b))for(var d=0;dm.status||304===m.status||S.test(a.url))d(f(a.type,b));else{var k=Error(m.responseText);k.code= -m.status;k.response=b;e(k)}}catch(c){e(c)}};l&&null!=a.data?m.send(a.data):m.send()});return!0===a.background?y:p(y)},jsonp:function(a,h){var r=e();a=l(a,h);var p=new d(function(d,e){var h=a.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+m++,l=b.document.createElement("script");b[h]=function(e){l.parentNode.removeChild(l);d(f(a.type,e));delete b[h]};l.onerror=function(){l.parentNode.removeChild(l);e(Error("JSONP request failed"));delete b[h]};null==a.data&&(a.data={});a.url=n(a.url, -a.data);a.data[a.callbackKey||"callback"]=h;l.src=g(a.url,a.data);b.document.documentElement.appendChild(l)});return!0===a.background?p:r(p)},setCompletionCallback:function(a){p=a}}}(window,w),P=function(b){function d(k,c,q,a,b,d,f){for(;q=p&&E>=B;){var t=c[p];u=q[B];if(t!==u||b)if(null==t)p++;else if(null==u)B++;else if(t.key===u.key){var x=null!=y&&p>=c.length-y.length||null==y&&b;p++;B++;h(k,t,u,l,f(c,p,g),x,n);b&&t.tag===u.tag&&m(k,r(t),g)}else if(t=c[v],t!==u||b)if(null==t)v--;else if(null==u)B++;else if(t.key===u.key)x=null!=y&&v>=c.length-y.length||null==y&&b,h(k,t,u,l,f(c,v+1,g),x,n),(b||B=p&&E>=B;){t=c[v];u=q[E];if(t!==u||b)if(null==t)v--;else{if(null!=u)if(t.key===u.key)x=null!=y&&v>=c.length-y.length||null==y&&b,h(k,t,u,l,f(c,v+1,g),x,n),b&&t.tag===u.tag&&m(k,r(t),g),null!=t.dom&&(g=t.dom),v--;else{if(!F){F=c;x=v;t={};var C;for(C=0;Cm.status||304===m.status||S.test(a.url))d(f(a.type,b));else{var k=Error(m.responseText);k.code=m.status;k.response=b;e(k)}}catch(c){e(c)}};l&&null!=a.data?m.send(a.data):m.send()});return!0===a.background?y:p(y)},jsonp:function(a,h){var r=e();a=l(a,h);var p=new d(function(d,e){var h=a.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+m++,l=b.document.createElement("script");b[h]=function(e){l.parentNode.removeChild(l);d(f(a.type,e));delete b[h]};l.onerror=function(){l.parentNode.removeChild(l); +e(Error("JSONP request failed"));delete b[h]};null==a.data&&(a.data={});a.url=n(a.url,a.data);a.data[a.callbackKey||"callback"]=h;l.src=g(a.url,a.data);b.document.documentElement.appendChild(l)});return!0===a.background?p:r(p)},setCompletionCallback:function(a){p=a}}}(window,w),P=function(b){function d(k,c,q,a,b,d,f){for(;q=p&&E>=B;){var t=c[p];u=q[B];if(t!==u||b)if(null==t)p++;else if(null==u)B++;else if(t.key===u.key){var x=null!=y&&p>=c.length-y.length||null==y&&b;p++;B++;h(k,t,u,l,f(c,p,g),x,n);b&&t.tag===u.tag&&m(k,r(t),g)}else if(t=c[v],t!==u||b)if(null==t)v--;else if(null==u)B++;else if(t.key===u.key)x=null!=y&&v>=c.length- +y.length||null==y&&b,h(k,t,u,l,f(c,v+1,g),x,n),(b||B=p&&E>=B;){t=c[v];u=q[E];if(t!==u||b)if(null==t)v--;else{if(null!=u)if(t.key===u.key)x=null!=y&&v>=c.length-y.length||null==y&&b,h(k,t,u,l,f(c,v+1,g),x,n),b&&t.tag===u.tag&&m(k,r(t),g),null!=t.dom&&(g=t.dom),v--;else{if(!F){F=c;x=v;t={};var C;for(C=0;C Date: Sun, 6 Aug 2017 22:18:57 -0700 Subject: [PATCH 3/7] chore: ignore test-ospec when checking PRs --- dangerfile.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dangerfile.js b/dangerfile.js index 89ea4277..bd9cd30b 100644 --- a/dangerfile.js +++ b/dangerfile.js @@ -43,6 +43,8 @@ if(appfiles.length && !changelog) { // Call out if `o.only(...)` was left in jsfiles .filter((file) => file.indexOf("tests/") > -1) + // Have to exclude test-ospec.js because it specifically has a purposeful "o.only" in it + .filter((file) => file.indexOf("test-ospec") === -1) .forEach((file) => { var code = fs.readFileSync(file, "utf8"), locs = locater.find("o.only", code); From 1e56f7763e85ff6f0178fc12a0f9a01c56b41503 Mon Sep 17 00:00:00 2001 From: spacejack Date: Sun, 13 Aug 2017 18:32:32 -0400 Subject: [PATCH 4/7] More accurate style object diffs --- render/render.js | 12 ++++++++++++ render/tests/test-updateElement.js | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/render/render.js b/render/render.js index 6bebd212..40eca443 100644 --- a/render/render.js +++ b/render/render.js @@ -552,6 +552,18 @@ module.exports = function($window) { //style function updateStyle(element, old, style) { + if (old != null && style != null && typeof old === "object" && typeof style === "object" && style !== old) { + // Both old & new are (different) objects. + // Update style properties that have changed + for (var key in style) { + if (style[key] !== old[key]) element.style[key] = style[key] + } + // Remove style properties that no longer exist + for (var key in old) { + if (!(key in style)) element.style[key] = "" + } + return + } if (old === style) element.style.cssText = "", old = null if (style == null) element.style.cssText = "" else if (typeof style === "string") element.style.cssText = style diff --git a/render/tests/test-updateElement.js b/render/tests/test-updateElement.js index 313fe1a9..a2ae0fcb 100644 --- a/render/tests/test-updateElement.js +++ b/render/tests/test-updateElement.js @@ -192,6 +192,19 @@ o.spec("updateElement", function() { o(updated.dom.style.backgroundColor).equals("") o(updated.dom.style.color).equals("gold") }) + o("does not re-render element styles for equivalent style objects", function() { + var style = {color: "gold"} + var vnode = {tag: "a", attrs: {style: style}} + + render(root, [vnode]) + + root.firstChild.style.color = "red" + style = {color: "gold"} + var updated = {tag: "a", attrs: {style: style}} + render(root, [updated]) + + o(updated.dom.style.color).equals("red") + }) o("replaces el", function() { var vnode = {tag: "a"} var updated = {tag: "b"} From ae0b7915300881d8c5a685d8df36176416a70879 Mon Sep 17 00:00:00 2001 From: spacejack Date: Tue, 15 Aug 2017 15:53:23 -0400 Subject: [PATCH 5/7] Eleminate now-redundant if block. Add change-log notes. --- docs/change-log.md | 1 + render/render.js | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/change-log.md b/docs/change-log.md index 6ea8b86a..82686f0e 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -27,6 +27,7 @@ - API: `m.route.set()` causes all mount points to be redrawn ([#1592](https://github.com/MithrilJS/mithril.js/pull/1592)) - API: If a user sets the Content-Type header within a request's options, that value will be the entire header value rather than being appended to the default value ([#1924](https://github.com/MithrilJS/mithril.js/pull/1924)) +- API: Using style objects in hyperscript calls will now properly diff style properties from one render to another as opposed to re-writing all element style properties every render. --- diff --git a/render/render.js b/render/render.js index 40eca443..f6782297 100644 --- a/render/render.js +++ b/render/render.js @@ -572,11 +572,6 @@ module.exports = function($window) { for (var key in style) { element.style[key] = style[key] } - if (old != null && typeof old !== "string") { - for (var key in old) { - if (!(key in style)) element.style[key] = "" - } - } } } From 96aa1853020998c584a22883fe8b417bc82b0c89 Mon Sep 17 00:00:00 2001 From: Gandalf-the-Bot Date: Wed, 16 Aug 2017 07:14:17 +0000 Subject: [PATCH 6/7] Bundled output for commit 457dd92d467045b04fbbc1d1bb13e50b4c8f0a8c [skip ci] --- README.md | 2 +- mithril.js | 17 +++++++--- mithril.min.js | 88 +++++++++++++++++++++++++------------------------- 3 files changed, 57 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 0e5959d8..6ddbbb0a 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ mithril.js [![NPM Version](https://img.shields.io/npm/v/mithril.svg)](https://ww ## What is Mithril? -A modern client-side Javascript framework for building Single Page Applications. It's small (8.25 KB gzipped), fast and provides routing and XHR utilities out of the box. +A modern client-side Javascript framework for building Single Page Applications. It's small (8.29 KB gzipped), fast and provides routing and XHR utilities out of the box. Mithril is used by companies like Vimeo and Nike, and open source platforms like Lichess 👍. diff --git a/mithril.js b/mithril.js index f6bd38af..e2c8944e 100644 --- a/mithril.js +++ b/mithril.js @@ -905,6 +905,18 @@ var coreRenderer = function($window) { } //style function updateStyle(element, old, style) { + if (old != null && style != null && typeof old === "object" && typeof style === "object" && style !== old) { + // Both old & new are (different) objects. + // Update style properties that have changed + for (var key2 in style) { + if (style[key2] !== old[key2]) element.style[key2] = style[key2] + } + // Remove style properties that no longer exist + for (var key2 in old) { + if (!(key2 in style)) element.style[key2] = "" + } + return + } if (old === style) element.style.cssText = "", old = null if (style == null) element.style.cssText = "" else if (typeof style === "string") element.style.cssText = style @@ -913,11 +925,6 @@ var coreRenderer = function($window) { for (var key2 in style) { element.style[key2] = style[key2] } - if (old != null && typeof old !== "string") { - for (var key2 in old) { - if (!(key2 in style)) element.style[key2] = "" - } - } } } //event diff --git a/mithril.min.js b/mithril.min.js index edc24225..05686d0c 100644 --- a/mithril.min.js +++ b/mithril.min.js @@ -1,44 +1,44 @@ -(function(){function z(b,d,e,l,n,g){return{tag:b,key:d,attrs:e,children:l,text:n,dom:g,domSize:void 0,state:void 0,_state:void 0,events:void 0,instance:void 0,skip:!1}}function A(b){var d,e=arguments[1],l=2;if(null==b||"string"!==typeof b&&"function"!==typeof b&&"function"!==typeof b.view)throw Error("The selector must be either a string or a component.");if("string"===typeof b&&!(d=M[b])){var n="div";for(var g=[],h={};d=Q.exec(b);){var r=d[1],f=d[2];""===r&&""!==f?n=f:"#"===r?h.id=f:"."===r?g.push(f): -"["===d[3][0]&&((r=d[6])&&(r=r.replace(/\\(["'])/g,"$1").replace(/\\\\/g,"\\")),"class"===d[4]?g.push(r):h[d[4]]=""===r?r:r||!0)}0a.indexOf("?")?"?":"&";a+=e+d}return a}function h(a){try{return""!==a?JSON.parse(a):null}catch(y){throw Error(a);}}function r(a){return a.responseText} -function f(a,b){if("function"===typeof a)if(Array.isArray(b))for(var d=0;dm.status||304===m.status||S.test(a.url))d(f(a.type,b));else{var k=Error(m.responseText);k.code=m.status;k.response=b;e(k)}}catch(c){e(c)}};l&&null!=a.data?m.send(a.data):m.send()});return!0===a.background?y:p(y)},jsonp:function(a,h){var r=e();a=l(a,h);var p=new d(function(d,e){var h=a.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+m++,l=b.document.createElement("script");b[h]=function(e){l.parentNode.removeChild(l);d(f(a.type,e));delete b[h]};l.onerror=function(){l.parentNode.removeChild(l); -e(Error("JSONP request failed"));delete b[h]};null==a.data&&(a.data={});a.url=n(a.url,a.data);a.data[a.callbackKey||"callback"]=h;l.src=g(a.url,a.data);b.document.documentElement.appendChild(l)});return!0===a.background?p:r(p)},setCompletionCallback:function(a){p=a}}}(window,w),P=function(b){function d(k,c,q,a,b,d,f){for(;q=p&&E>=B;){var t=c[p];u=q[B];if(t!==u||b)if(null==t)p++;else if(null==u)B++;else if(t.key===u.key){var x=null!=y&&p>=c.length-y.length||null==y&&b;p++;B++;h(k,t,u,l,f(c,p,g),x,n);b&&t.tag===u.tag&&m(k,r(t),g)}else if(t=c[v],t!==u||b)if(null==t)v--;else if(null==u)B++;else if(t.key===u.key)x=null!=y&&v>=c.length- -y.length||null==y&&b,h(k,t,u,l,f(c,v+1,g),x,n),(b||B=p&&E>=B;){t=c[v];u=q[E];if(t!==u||b)if(null==t)v--;else{if(null!=u)if(t.key===u.key)x=null!=y&&v>=c.length-y.length||null==y&&b,h(k,t,u,l,f(c,v+1,g),x,n),b&&t.tag===u.tag&&m(k,r(t),g),null!=t.dom&&(g=t.dom),v--;else{if(!F){F=c;x=v;t={};var C;for(C=0;Ca.indexOf("?")?"?":"&";a+=f+d}return a}function k(a){try{return""!==a?JSON.parse(a):null}catch(z){throw Error(a);}}function r(a){return a.responseText} +function g(a,b){if("function"===typeof a)if(Array.isArray(b))for(var d=0;dn.status||304===n.status||S.test(a.url))d(g(a.type,b));else{var l=Error(n.responseText);l.code=n.status;l.response=b;f(l)}}catch(c){f(c)}};m&&null!=a.data?n.send(a.data):n.send()});return!0===a.background?z:p(z)},jsonp:function(a,k){var r=f();a=m(a,k);var p=new d(function(d,f){var k=a.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+n++,m=b.document.createElement("script");b[k]=function(f){m.parentNode.removeChild(m);d(g(a.type,f));delete b[k]};m.onerror=function(){m.parentNode.removeChild(m); +f(Error("JSONP request failed"));delete b[k]};null==a.data&&(a.data={});a.url=q(a.url,a.data);a.data[a.callbackKey||"callback"]=k;m.src=h(a.url,a.data);b.document.documentElement.appendChild(m)});return!0===a.background?p:r(p)},setCompletionCallback:function(a){p=a}}}(window,w),P=function(b){function d(l,c,e,a,b,d,g){for(;e=p&&E>=B;){var u=c[p];t=e[B];if(u!==t||b)if(null==u)p++;else if(null==t)B++;else if(u.key===t.key){var x=null!=z&&p>=c.length-z.length||null==z&&b;p++;B++;k(l,u,t,m,g(c,p,h),x,q);b&&u.tag===t.tag&&n(l,r(u),h)}else if(u=c[v],u!==t||b)if(null==u)v--;else if(null==t)B++;else if(u.key===t.key)x=null!=z&&v>=c.length- +z.length||null==z&&b,k(l,u,t,m,g(c,v+1,h),x,q),(b||B=p&&E>=B;){u=c[v];t=e[E];if(u!==t||b)if(null==u)v--;else{if(null!=t)if(u.key===t.key)x=null!=z&&v>=c.length-z.length||null==z&&b,k(l,u,t,m,g(c,v+1,h),x,q),b&&u.tag===t.tag&&n(l,r(u),h),null!=u.dom&&(h=u.dom),v--;else{if(!F){F=c;x=v;u={};var w;for(w=0;w Date: Fri, 18 Aug 2017 23:02:38 -0700 Subject: [PATCH 7/7] docs: Fix typo (#1940) --- docs/route.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/route.md b/docs/route.md index a8f7a2fe..8706119e 100644 --- a/docs/route.md +++ b/docs/route.md @@ -107,7 +107,7 @@ This function can be used as the `oncreate` (and `onupdate`) hook in a `m("a")` m("a[href=/]", {oncreate: m.route.link})`. ``` -Using `m.route.link` as a `oncreate` hook causes the link to behave as a router link (i.e. it navigates to the route specified in `href`, instead of nagivating away from the current page to the URL specified in `href`. +Using `m.route.link` as a `oncreate` hook causes the link to behave as a router link (i.e. it navigates to the route specified in `href`, instead of navigating away from the current page to the URL specified in `href`. If the `href` attribute is not static, the `onupdate` hook must also be set: