diff --git a/hyperscript.js b/hyperscript.js new file mode 100644 index 00000000..48f06c9e --- /dev/null +++ b/hyperscript.js @@ -0,0 +1,6 @@ +var hyperscript = require("./render/hyperscript") +hyperscript.trust = require("./render/trust") +hyperscript.fragment = require("./render/fragment") + + +module.exports = hyperscript diff --git a/index.js b/index.js index 84e06db6..089d390b 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ "use strict" -var m = require("./render/hyperscript") +var m = require("./hyperscript") var renderService = require("./render") var requestService = require("./request") var redrawService = require("./redraw") @@ -11,7 +11,6 @@ requestService.setCompletionCallback(redrawService.publish) m.route = require("./route") m.mount = require("./mount") -m.trust = require("./render/trust") m.withAttr = require("./util/withAttr") m.prop = require("./stream") m.render = renderService.render diff --git a/mithril.js b/mithril.js index 84b2f8b6..d1e31fa8 100644 --- a/mithril.js +++ b/mithril.js @@ -72,6 +72,12 @@ function hyperscript(selector) { if (typeof selector === "string") return selectorCache[selector](attrs || {}, Vnode.normalizeChildren(children)) return Vnode(selector, attrs && attrs.key, attrs || {}, Vnode.normalizeChildren(children), undefined, undefined) } +hyperscript.trust = function(html) { + return Vnode("<", undefined, undefined, html, undefined, undefined) +} +hyperscript.fragment = function(attrs, children) { + return Vnode("[", attrs.key, attrs, Vnode.normalizeChildren(children), undefined, undefined) +} var m = hyperscript var renderService = function($window) { var $doc = $window.document @@ -160,6 +166,8 @@ var renderService = function($window) { initLifecycle(vnode.tag, vnode, hooks) vnode.instance = Vnode.normalize(vnode.tag.view.call(vnode.state, vnode)) if (vnode.instance != null) { + if(vnode.instance === vnode) + throw Error("A component view mustn't return the vnode that was supplied to it.") var element = createNode(vnode.instance, hooks, ns) vnode.dom = vnode.instance.dom vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0 @@ -178,7 +186,6 @@ var renderService = function($window) { else { var recycling = isRecyclable(old, vnodes) if (recycling) old = old.concat(old.pool) - if (old.length === vnodes.length && vnodes[0] != null && vnodes[0].key == null) { for (var i = 0; i < old.length; i++) { if (old[i] === vnodes[i] || old[i] == null && vnodes[i] == null) continue @@ -569,7 +576,6 @@ var renderService = function($window) { function render(dom, vnodes) { var hooks = [] var active = $doc.activeElement - // First time rendering into a node clears it out if (dom.vnodes == null) dom.textContent = "" if (!(vnodes instanceof Array)) vnodes = [vnodes] @@ -1136,9 +1142,6 @@ m.mount = function(renderer, pubsub) { run() } }(renderService, redrawService) -m.trust = function(html) { - return Vnode("<", undefined, undefined, html, undefined, undefined) -} m.withAttr = function(attrName, callback, context) { return function(e) { return callback.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName)) diff --git a/mithril.min.js b/mithril.min.js index 00fa3427..ee4750c5 100644 --- a/mithril.min.js +++ b/mithril.min.js @@ -1,41 +1,41 @@ -new function(){function u(b,d,r,g,n,k){return{tag:b,key:d,attrs:r,children:g,text:n,dom:k,domSize:void 0,state:{},events:void 0,instance:void 0,skip:!1}}u.normalize=function(b){return b instanceof Array?u("[",void 0,void 0,u.normalizeChildren(b),void 0,void 0):null!=b&&"object"!==typeof b?u("#",void 0,void 0,b,void 0,void 0):b};u.normalizeChildren=function(b){for(var d=0;d=t&&A>=B;){var h=a[t],v=f[B];if(h===v)t++,B++;else if(null!=h&&null!=v&&h.key===v.key)t++,B++,k(c,h,v,e,l(a,t,b),q,p),q&&h.tag===v.tag&&w(c,m(h),b);else if(h=a[g],h===v)g--,B++;else if(null!=h&&null!=v&&h.key===v.key)k(c,h,v,e,l(a,g+1,b),q,p),B=t&&A>=B;){h=a[g];v=f[A];if(h===v)g--;else if(null!=h&&null!=v&&h.key===v.key)k(c,h,v,e,l(a,g+1,b),q,p),q&&h.tag===v.tag&&w(c,m(h),b), -null!=h.dom&&(b=h.dom),g--;else{if(!n){n=a;var h=g,y={},u;for(u=0;ub.indexOf("?")?"?":"&";b+=p+g}return b}function n(b){try{return""!==b?JSON.parse(b):null}catch(d){throw Error(b);}}function k(b){return b.responseText}function m(b,d){if("function"===typeof b)if(d instanceof Array)for(var g=0;gp.status)d(m(e.type, -b));else{var g=Error(p.responseText),h;for(h in b)g[h]=b[h];d.error(g)}}catch(w){d.error(w)}"function"===typeof z&&z()}};h?p.send(e.data):p.send();return d},jsonp:function(e){var d=l();void 0!==e.initialValue&&d(e.initialValue);var h=e.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+w++,p=b.document.createElement("script");b[h]=function(g){p.parentNode.removeChild(p);d(m(e.type,g));"function"===typeof z&&z();delete b[h]};p.onerror=function(){p.parentNode.removeChild(p);d.error(Error("JSONP request failed")); -"function"===typeof z&&z();delete b[h]};null==e.data&&(e.data={});e.url=r(e.url,e.data);e.data[e.callbackKey||"callback"]=h;p.src=g(e.url,e.data);b.document.documentElement.appendChild(p);return d},setCompletionCallback:function(b){z=b}}}(window,console.error.bind(console)),F=function(){var b=[];return{subscribe:b.push.bind(b),unsubscribe:function(d){d=b.indexOf(d);-1=r&&A>=B;){var k=a[r],w=f[B];if(k===w)r++,B++;else if(null!=k&&null!=w&&k.key===w.key)r++,B++,h(b,k,w,d,l(a,r,c),p,u),p&&k.tag===w.tag&&x(b,m(k),c);else if(k=a[g],k===w)g--,B++;else if(null!=k&&null!=w&&k.key===w.key)h(b,k,w,d,l(a,g+1,c),p,u),B=r&&A>=B;){k=a[g];w=f[A];if(k===w)g--;else if(null!=k&&null!=w&&k.key===w.key)h(b,k,w,d,l(a,g+1,c),p,u),p&&k.tag===w.tag&&x(b,m(k),c),null!=k.dom&&(c=k.dom),g--;else{if(!n){n=a;var k=g,t={},y;for(y=0;yc.indexOf("?")?"?":"&";c+=g+e}return c}function n(c){try{return""!==c?JSON.parse(c):null}catch(d){throw Error(c);}}function h(c){return c.responseText}function m(c,d){if("function"=== +typeof c)if(d instanceof Array)for(var e=0;eu.status)e(m(c.type,d));else{var g=Error(u.responseText),k;for(k in d)g[k]=d[k];e.error(g)}}catch(x){e.error(x)}"function"===typeof z&&z()}};k?u.send(c.data):u.send();return e},jsonp:function(c){var e=l();void 0!==c.initialValue&&e(c.initialValue);var k=c.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+x++,h=d.document.createElement("script");d[k]=function(g){h.parentNode.removeChild(h); +e(m(c.type,g));"function"===typeof z&&z();delete d[k]};h.onerror=function(){h.parentNode.removeChild(h);e.error(Error("JSONP request failed"));"function"===typeof z&&z();delete d[k]};null==c.data&&(c.data={});c.url=q(c.url,c.data);c.data[c.callbackKey||"callback"]=k;h.src=g(c.url,c.data);d.document.documentElement.appendChild(h);return e},setCompletionCallback:function(c){z=c}}}(window,console.error.bind(console)),F=function(){var d=[];return{subscribe:d.push.bind(d),unsubscribe:function(e){e=d.indexOf(e); +-1", - "license": "MIT" + "license": "MIT", + "bin": "./bin/ospec", + "repository": "lhorie/mithril.js#rewrite" } diff --git a/package.json b/package.json index 14d8f92a..0dc53dd5 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "author": "Leo Horie", "license": "MIT", "main": "index.js", + "repository": "lhorie/mithril.js", "scripts": { "dev": "node bundler/cli browser.js -o mithril.js -w", "build": "npm run build-browser & npm run build-min", @@ -21,8 +22,5 @@ }, "publishConfig": { "tag": "beta" - }, - "bin": { - "ospec": "./ospec/bin/ospec" } } diff --git a/render/fragment.js b/render/fragment.js new file mode 100644 index 00000000..82918975 --- /dev/null +++ b/render/fragment.js @@ -0,0 +1,7 @@ +"use strict" + +var Vnode = require("../render/vnode") + +module.exports = function(attrs, children) { + return Vnode("[", attrs.key, attrs, Vnode.normalizeChildren(children), undefined, undefined) +} diff --git a/render/render.js b/render/render.js index 40874142..8c19c75d 100644 --- a/render/render.js +++ b/render/render.js @@ -98,6 +98,8 @@ module.exports = function($window) { initLifecycle(vnode.tag, vnode, hooks) vnode.instance = Vnode.normalize(vnode.tag.view.call(vnode.state, vnode)) if (vnode.instance != null) { + if(vnode.instance === vnode) + throw Error("A component view mustn't return the vnode that was supplied to it.") var element = createNode(vnode.instance, hooks, ns) vnode.dom = vnode.instance.dom vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0 @@ -117,7 +119,7 @@ module.exports = function($window) { else { var recycling = isRecyclable(old, vnodes) if (recycling) old = old.concat(old.pool) - + if (old.length === vnodes.length && vnodes[0] != null && vnodes[0].key == null) { for (var i = 0; i < old.length; i++) { if (old[i] === vnodes[i] || old[i] == null && vnodes[i] == null) continue @@ -516,7 +518,7 @@ module.exports = function($window) { function render(dom, vnodes) { var hooks = [] var active = $doc.activeElement - + // First time rendering into a node clears it out if (dom.vnodes == null) dom.textContent = "" diff --git a/render/tests/test-component.js b/render/tests/test-component.js index c6078469..13eb0bcc 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -254,6 +254,22 @@ o.spec("component", function() { o(root.childNodes.length).equals(0) }) + o("throws a custom error if it returns itself", function() { + // A view that returns its vnode would otherwise trigger an infinite loop + var component = { + view: function(vnode) { + return vnode + } + } + try { + render(root, [{tag: component}]) + } + catch(error){ + o(error instanceof Error).equals(true) + // Call stack excession is a RangeError + o(error instanceof RangeError).equals(false) + } + }) o("can update when returning fragments", function() { var component = { view: function(vnode) { diff --git a/render/tests/test-fragment.js b/render/tests/test-fragment.js new file mode 100644 index 00000000..f0403bbc --- /dev/null +++ b/render/tests/test-fragment.js @@ -0,0 +1,35 @@ +"use strict" + +var o = require("../../ospec/ospec") +var fragment = require("../../render/fragment") + +o.spec("fragment", function() { + o("works", function() { + var attrs = {foo: 5} + var child = {tag: "p"} + var frag = fragment(attrs, [child]) + + o(frag.tag).equals("[") + + o(frag.children instanceof Array).equals(true) + o(frag.children.length).equals(1) + o(frag.children[0]).equals(child) + + o(frag.attrs).equals(attrs) + + o(frag.key).equals(undefined) + }) + o("supports keys", function() { + var attrs = {key: 7} + var frag = fragment(attrs, []) + o(frag.tag).equals("[") + + o(frag.children instanceof Array).equals(true) + o(frag.children.length).equals(0) + + o(frag.attrs).equals(attrs) + o(frag.attrs.key).equals(7) + + o(frag.key).equals(7) + }) +})