diff --git a/docs/change-log.md b/docs/change-log.md index 38d86fbc..8ff0d175 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -1,11 +1,21 @@ # Change log +- [v1.0.2](#v101) - [v1.0.1](#v101) - [Migrating from v0.2.x](#migrating-from-v02x) - [Older docs](http://mithril.js.org/archive/v0.2.5/index.html) --- +### v1.0.2 + +#### Bug fixes + +- fix IE11 input[type] error - [#1610](https://github.com/lhorie/mithril.js/issues/1610) +- apply [#1609](https://github.com/lhorie/mithril.js/issues/1609) to unkeyed children case + +--- + ### v1.0.1 #### News diff --git a/mithril.js b/mithril.js index 808d5b6e..afa2ff9a 100644 --- a/mithril.js +++ b/mithril.js @@ -481,7 +481,10 @@ var coreRenderer = function($window) { } } recycling = recycling || isRecyclable(old, vnodes) - if (recycling) old = old.concat(old.pool) + if (recycling) { + var pool = old.pool + 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] @@ -489,8 +492,9 @@ var coreRenderer = function($window) { else if (o == null) oldStart++ else if (v == null) start++ else if (o.key === v.key) { + var shouldRecycle = (pool != null && oldStart >= old.length - pool.length) || ((pool == null) && recycling) oldStart++, start++ - updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns) + updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), shouldRecycle, ns) if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling) } else { @@ -499,7 +503,8 @@ var coreRenderer = function($window) { 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) + var shouldRecycle = (pool != null && oldEnd >= old.length - pool.length) || ((pool == null) && recycling) + updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), shouldRecycle, ns) if (recycling || start < end) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling)) oldEnd--, start++ } @@ -512,7 +517,8 @@ var coreRenderer = function($window) { 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) + var shouldRecycle = (pool != null && oldEnd >= old.length - pool.length) || ((pool == null) && recycling) + updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), shouldRecycle, ns) if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling) if (o.dom != null) nextSibling = o.dom oldEnd--, end-- @@ -523,6 +529,7 @@ var coreRenderer = function($window) { var oldIndex = map[v.key] if (oldIndex != null) { var movable = old[oldIndex] + var shouldRecycle = (pool != null && oldIndex >= old.length - pool.length) || ((pool == null) && recycling) updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) insertNode(parent, toFragment(movable), nextSibling) old[oldIndex].skip = true diff --git a/mithril.min.js b/mithril.min.js index 92552779..7b2c9d91 100644 --- a/mithril.min.js +++ b/mithril.min.js @@ -1,42 +1,42 @@ -new function(){function w(a,d,h,f,g,l){return{tag:a,key:d,attrs:h,children:f,text:g,dom:l,domSize:void 0,state:{},events:void 0,instance:void 0,skip:!1}}function z(a){if(null==a||"string"!==typeof a&&"function"!==typeof a.view)throw Error("The selector must be either a string or a component.");if("string"===typeof a&&void 0===G[a]){for(var d,h,f=[],g={};d=N.exec(a);){var l=d[1],k=d[2];""===l&&""!==k?h=k:"#"===l?g.id=k:"."===l?f.push(k):"["===d[3][0]&&((l=d[6])&&(l=l.replace(/\\(["'])/g,"$1").replace(/\\\\/g, -"\\")),"class"===d[4]?f.push(l):g[d[4]]=l||!0)}0b.indexOf("?")?"?":"&";b+=f+d}return b}function k(b){try{return""!==b?JSON.parse(b):null}catch(B){throw Error(b);}} -function m(b){return b.responseText}function p(b,a){if("function"===typeof b)if(Array.isArray(a))for(var d=0;dq.status||304===q.status)d(p(b.type,a));else{var n=Error(q.responseText),c;for(c in a)n[c]= -a[c];f(n)}}catch(e){f(e)}};h&&null!=b.data?q.send(b.data):q.send()});return!0===b.background?B:u(B)},jsonp:function(b,k){var m=h();b=f(b,k);var x=new d(function(d,f){var h=b.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+u++,k=a.document.createElement("script");a[h]=function(f){k.parentNode.removeChild(k);d(p(b.type,f));delete a[h]};k.onerror=function(){k.parentNode.removeChild(k);f(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?x:m(x)},setCompletionCallback:function(a){x=a}}}(window,v),M=function(a){function d(a,c,e,b,d,f,g){for(;e=n&&u>=e;){var t=c[n],y=b[e];if(t!==y||f)if(null==t)n++;else if(null==y)e++;else if(t.key===y.key)n++,e++,l(a,t,y,g,m(c,n,A),f,q),f&&t.tag===y.tag&&p(a,k(t),A);else if(t=c[r],t!==y||f)if(null== -t)r--;else if(null==y)e++;else if(t.key===y.key)l(a,t,y,g,m(c,r+1,A),f,q),(f||e=n&&u>=e;){t=c[r];y=b[u];if(t!==y||f)if(null==t)r--;else{if(null!=y)if(t.key===y.key)l(a,t,y,g,m(c,r+1,A),f,q),f&&t.tag===y.tag&&p(a,k(t),A),null!=t.dom&&(A=t.dom),r--;else{if(!B){B=c;var t=r,C={},v;for(v=0;vb.indexOf("?")?"?":"&";b+=f+d}return b}function k(b){try{return""!==b?JSON.parse(b):null}catch(u){throw Error(b);}} +function p(b){return b.responseText}function r(b,a){if("function"===typeof b)if(Array.isArray(a))for(var d=0;dn.status||304===n.status)d(r(b.type,a));else{var m=Error(n.responseText),c;for(c in a)m[c]= +a[c];f(m)}}catch(e){f(e)}};h&&null!=b.data?n.send(b.data):n.send()});return!0===b.background?u:t(u)},jsonp:function(b,k){var p=h();b=f(b,k);var q=new d(function(d,f){var h=b.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+t++,k=a.document.createElement("script");a[h]=function(f){k.parentNode.removeChild(k);d(r(b.type,f));delete a[h]};k.onerror=function(){k.parentNode.removeChild(k);f(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?q:p(q)},setCompletionCallback:function(a){q=a}}}(window,w),N=function(a){function d(a,c,e,b,d,f,g){for(;e=m&&u>=e;){var y=c[m],z=b[e];if(y!==z||f)if(null==y)m++;else if(null==z)e++;else if(y.key===z.key){var C=null!=v&&m>=c.length-v.length||null==v&&f;m++;e++;l(a,y,z,g,p(c,m,B),C,n); +f&&y.tag===z.tag&&r(a,k(y),B)}else if(y=c[t],y!==z||f)if(null==y)t--;else if(null==z)e++;else if(y.key===z.key)C=null!=v&&t>=c.length-v.length||null==v&&f,l(a,y,z,g,p(c,t+1,B),C,n),(f||e=m&&u>=e;){y=c[t];z=b[u];if(y!==z||f)if(null==y)t--;else{if(null!=z)if(y.key===z.key)C=null!=v&&t>=c.length-v.length||null==v&&f,l(a,y,z,g,p(c,t+1,B),C,n),f&&y.tag===z.tag&&r(a,k(y),B),null!=y.dom&&(B=y.dom),t--;else{if(!E){E=c;var y=t,C={}, +w;for(w=0;w= oldStart && end >= start) { @@ -156,8 +159,9 @@ module.exports = function($window) { else if (o == null) oldStart++ else if (v == null) start++ else if (o.key === v.key) { + var shouldRecycle = (pool != null && oldStart >= old.length - pool.length) || ((pool == null) && recycling) oldStart++, start++ - updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns) + updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), shouldRecycle, ns) if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling) } else { @@ -166,7 +170,8 @@ module.exports = function($window) { 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) + var shouldRecycle = (pool != null && oldEnd >= old.length - pool.length) || ((pool == null) && recycling) + updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), shouldRecycle, ns) if (recycling || start < end) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling)) oldEnd--, start++ } @@ -179,7 +184,8 @@ module.exports = function($window) { 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) + var shouldRecycle = (pool != null && oldEnd >= old.length - pool.length) || ((pool == null) && recycling) + updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), shouldRecycle, ns) if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling) if (o.dom != null) nextSibling = o.dom oldEnd--, end-- @@ -190,6 +196,7 @@ module.exports = function($window) { var oldIndex = map[v.key] if (oldIndex != null) { var movable = old[oldIndex] + var shouldRecycle = (pool != null && oldIndex >= old.length - pool.length) || ((pool == null) && recycling) updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) insertNode(parent, toFragment(movable), nextSibling) old[oldIndex].skip = true diff --git a/render/tests/test-render.js b/render/tests/test-render.js index 21f6dc3d..337a9d64 100644 --- a/render/tests/test-render.js +++ b/render/tests/test-render.js @@ -113,4 +113,40 @@ o.spec("render", function() { o(updateB.callCount).equals(0) o(removeB.callCount).equals(1) }) + o.only("update lifecycle methods work on children of recycled keyed", function() { + var createA = o.spy() + var updateA = o.spy() + var removeA = o.spy() + var createB = o.spy() + var updateB = o.spy() + var removeB = o.spy() + + var a = function() { + return {tag: "div", key: 1, children: [ + {tag: "div", attrs: {oncreate: createA, onupdate: updateA, onremove: removeA}}, + ]} + } + var b = function() { + return {tag: "div", key: 2, children: [ + {tag: "div", attrs: {oncreate: createB, onupdate: updateB, onremove: removeB}}, + ]} + } + render(root, a()) + render(root, a()) + o(createA.callCount).equals(1) + o(updateA.callCount).equals(1) + o(removeA.callCount).equals(0) + + render(root, b()) + o(createA.callCount).equals(1) + o(updateA.callCount).equals(1) + o(removeA.callCount).equals(1) + + render(root, a()) + render(root, a()) + + o(createA.callCount).equals(2) + o(updateA.callCount).equals(2) + o(removeA.callCount).equals(1) + }) })