diff --git a/README.md b/README.md index 5b67f110..58a263eb 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.34 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.41 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 42d5cbb4..ba541f5e 100644 --- a/mithril.js +++ b/mithril.js @@ -839,13 +839,11 @@ var coreRenderer = function($window) { } } function setAttr(vnode, key2, old, value, ns) { + if (key2 === "key" || key2 === "is" || isLifecycleMethod(key2)) return + if (key2[0] === "o" && key2[1] === "n") return updateEvent(vnode, key2, value) + if ((old === value && !isFormAttribute(vnode, key2)) && typeof value !== "object" || value === undefined) return var element = vnode.dom - if (key2 === "key" || key2 === "is" || (old === value && !isFormAttribute(vnode, key2)) && typeof value !== "object" || typeof value === "undefined" || isLifecycleMethod(key2)) return - var nsLastIndex = key2.indexOf(":") - if (nsLastIndex > -1 && key2.substr(0, nsLastIndex) === "xlink") { - element.setAttributeNS("http://www.w3.org/1999/xlink", key2.slice(nsLastIndex + 1), value) - } - else if (key2[0] === "o" && key2[1] === "n" && typeof value === "function") updateEvent(vnode, key2, value) + if (key2.slice(0, 6) === "xlink:") element.setAttributeNS("http://www.w3.org/1999/xlink", key2.slice(6), value) else if (key2 === "style") updateStyle(element, old, value) else if (key2 in element && !isAttribute(key2) && ns === undefined && !isCustomElement(vnode)) { if (key2 === "value") { @@ -940,24 +938,38 @@ var coreRenderer = function($window) { } } } + // Here's an explanation of how this works: + // 1. The event names are always (by design) prefixed by `on`. + // 2. The EventListener interface accepts either a function or an object + // with a `handleEvent` method. + // 3. The object does not inherit from `Object.prototype`, to avoid + // any potential interference with that (e.g. setters). + // 4. The event name is remapped to the handler0 before calling it. + // 5. In function-based event handlers, `ev.target === this`. We replicate + // that below. + function EventDict() {} + EventDict.prototype = Object.create(null) + EventDict.prototype.handleEvent = function (ev) { + var handler0 = this["on" + ev.type] + if (typeof handler0 === "function") handler0.call(ev.target, ev) + else if (typeof handler0.handleEvent === "function") handler0.handleEvent(ev) + if (typeof onevent === "function") onevent.call(ev.target, ev) + } //event function updateEvent(vnode, key2, value) { - var element = vnode.dom - var callback = typeof onevent !== "function" ? value : function(e) { - var result = value.call(element, e) - onevent.call(element, e) - return result - } - if (key2 in element) element[key2] = typeof value === "function" ? callback : null - else { - var eventName = key2.slice(2) - if (vnode.events === undefined) vnode.events = {} - if (vnode.events[key2] === callback) return - if (vnode.events[key2] != null) element.removeEventListener(eventName, vnode.events[key2], false) - if (typeof value === "function") { - vnode.events[key2] = callback - element.addEventListener(eventName, vnode.events[key2], false) + if (vnode.events != null) { + if (vnode.events[key2] === value) return + if (value != null && (typeof value === "function" || typeof value === "object")) { + if (vnode.events[key2] == null) vnode.dom.addEventListener(key2.slice(2), vnode.events, false) + vnode.events[key2] = value + } else { + if (vnode.events[key2] != null) vnode.dom.removeEventListener(key2.slice(2), vnode.events, false) + vnode.events[key2] = undefined } + } else if (value != null && (typeof value === "function" || typeof value === "object")) { + vnode.events = new EventDict() + vnode.dom.addEventListener(key2.slice(2), vnode.events, false) + vnode.events[key2] = value } } //lifecycle @@ -1098,12 +1110,12 @@ var coreRouter = function($window) { return data } var asyncId - function debounceAsync(callback0) { + function debounceAsync(callback) { return function() { if (asyncId != null) return asyncId = callAsync0(function() { asyncId = null - callback0() + callback() }) } } @@ -1250,9 +1262,9 @@ var _20 = function($window, redrawService0) { return route } m.route = _20(window, redrawService) -m.withAttr = function(attrName, callback1, context) { +m.withAttr = function(attrName, callback, context) { return function(e) { - callback1.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName)) + callback.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName)) } } var _28 = coreRenderer(window) diff --git a/mithril.min.js b/mithril.min.js index 1d996296..bfb4f117 100644 --- a/mithril.min.js +++ b/mithril.min.js @@ -1,45 +1,45 @@ -(function(){function y(b,d,g,h,p,l){return{tag:b,key:d,attrs:g,children:h,text:p,dom:l,domSize:void 0,state:void 0,_state:void 0,events:void 0,instance:void 0,skip:!1}}function M(b){for(var d in b)if(C.call(b,d))return!1;return!0}function A(b){var d=arguments[1],g=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){var h;if(!(h=N[b])){var p="div";for(var l=[],f={};h=Q.exec(b);){var r= -h[1],e=h[2];""===r&&""!==e?p=e:"#"===r?f.id=e:"."===r?l.push(e):"["===h[3][0]&&((r=h[6])&&(r=r.replace(/\\(["'])/g,"$1").replace(/\\\\/g,"\\")),"class"===h[4]?l.push(r):f[h[4]]=""===r?r:r||!0)}0a.indexOf("?")?"?":"&";a+=e+d}return a}function f(a){try{return""!==a?JSON.parse(a):null}catch(z){throw Error(a);}}function r(a){return a.responseText}function e(a,b){if("function"===typeof a)if(Array.isArray(b))for(var d=0;dm.status||304===m.status||T.test(a.url))d(e(a.type,b));else{var k=Error(m.responseText);k.code=m.status;k.response=b;g(k)}}catch(c){g(c)}};h&&null!=a.data?m.send(a.data):m.send()});return!0===a.background?z:n(z)},jsonp:function(a,f){var r=g();a=h(a,f);var n=new d(function(d,g){var h=a.callbackName||"_mithril_"+ -Math.round(1E16*Math.random())+"_"+m++,f=b.document.createElement("script");b[h]=function(g){f.parentNode.removeChild(f);d(e(a.type,g));delete b[h]};f.onerror=function(){f.parentNode.removeChild(f);g(Error("JSONP request failed"));delete b[h]};null==a.data&&(a.data={});a.url=p(a.url,a.data);a.data[a.callbackKey||"callback"]=h;f.src=l(a.url,a.data);b.document.documentElement.appendChild(f)});return!0===a.background?n:r(n)},setCompletionCallback:function(a){n=a}}}(window,v),P=function(b){function d(k, -c,q,a,b,d,e){for(;q=x&&D>=n;){var u=c[x],t=q[n];if(u!==t||b)if(null==u)x++;else if(null==t)n++;else if(u.key===t.key){var v=null!= -z&&x>=c.length-z.length||null==z&&b;x++;n++;f(k,u,t,h,e(c,x,l),v,p);b&&u.tag===t.tag&&m(k,r(u),l)}else if(u=c[w],u!==t||b)if(null==u)w--;else if(null==t)n++;else if(u.key===t.key)v=null!=z&&w>=c.length-z.length||null==z&&b,f(k,u,t,h,e(c,w+1,l),v,p),(b||n=x&&D>=n;){u=c[w];t=q[D];if(u!==t||b)if(null==u)w--;else{if(null!=t)if(u.key===t.key)v=null!=z&&w>=c.length-z.length||null==z&&b,f(k,u,t,h,e(c,w+1,l),v,p),b&&u.tag===t.tag&& -m(k,r(u),l),null!=u.dom&&(l=u.dom),w--;else{if(!E){E=c;u=w;v={};var y;for(y=0;ya.indexOf("?")?"?":"&";a+=f+d}return a}function l(a){try{return""!==a?JSON.parse(a):null}catch(v){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;dn.status||304===n.status||U.test(a.url))d(f(a.type,b));else{var k=Error(n.responseText);k.code=n.status;k.response=b;h(k)}}catch(g){h(g)}};k&&null!=a.data?n.send(a.data):n.send()});return!0===a.background?v:p(v)},jsonp:function(a,l){var r=h();a=k(a,l);var p=new d(function(d,h){var k=a.callbackName||"_mithril_"+ +Math.round(1E16*Math.random())+"_"+n++,l=b.document.createElement("script");b[k]=function(h){l.parentNode.removeChild(l);d(f(a.type,h));delete b[k]};l.onerror=function(){l.parentNode.removeChild(l);h(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;l.src=m(a.url,a.data);b.document.documentElement.appendChild(l)});return!0===a.background?p:r(p)},setCompletionCallback:function(a){p=a}}}(window,w),Q=function(b){function d(g, +c,e,a,b,d,k){for(;e=x&&F>=p;){var z=c[x],t=e[p];if(z!==t||b)if(null==z)x++;else if(null==t)p++;else if(z.key===t.key){var B=null!= +C&&x>=c.length-C.length||null==C&&b;x++;p++;l(g,z,t,k,f(c,x,m),B,q);b&&z.tag===t.tag&&n(g,r(z),m)}else if(z=c[v],z!==t||b)if(null==z)v--;else if(null==t)p++;else if(z.key===t.key)B=null!=C&&v>=c.length-C.length||null==C&&b,l(g,z,t,k,f(c,v+1,m),B,q),(b||p=x&&F>=p;){z=c[v];t=e[F];if(z!==t||b)if(null==z)v--;else{if(null!=t)if(z.key===t.key)B=null!=C&&v>=c.length-C.length||null==C&&b,l(g,z,t,k,f(c,v+1,m),B,q),b&&z.tag===t.tag&& +n(g,r(z),m),null!=z.dom&&(m=z.dom),v--;else{if(!u){u=c;z=v;B={};var w;for(w=0;w