From 3dc6f1e16c120e5258a3b1be016a360f67c9ed86 Mon Sep 17 00:00:00 2001 From: Zolmeister Date: Sun, 31 Aug 2014 01:22:05 -0700 Subject: [PATCH] m: Added splat support for m() constructor --- mithril.js | 34 +++++++++++++++++++++++++++------- tests/mithril-tests.js | 25 ++++++++++++++----------- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/mithril.js b/mithril.js index 782c9aeb..4d161ef0 100644 --- a/mithril.js +++ b/mithril.js @@ -3,8 +3,21 @@ Mithril = m = new function app(window, undefined) { var parser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g, attrParser = /\[(.+?)(?:=("|'|)(.*?)\2)?\]/ var voidElements = /AREA|BASE|BR|COL|COMMAND|EMBED|HR|IMG|INPUT|KEYGEN|LINK|META|PARAM|SOURCE|TR‌​ACK|WBR/ + /* + * @typedef {String} Tag + * A string that looks like -> div.classname#id[param=one][param2=two] + * Which describes a DOM node + */ + + /* + * + * @param {Tag} The DOM node tag + * @param {Object=[]} optional key-value pairs to be mapped to DOM attrs + * @param {...mNode=[]} Zero or more Mithril child nodes. Can be an array, or splat (optional) + * + */ function m() { - var args = arguments + var args = Array.prototype.slice.call(arguments, 0) var hasAttrs = args[1] != null && type.call(args[1]) == "[object Object]" && !("tag" in args[1]) && !("subtree" in args[1]) var attrs = hasAttrs ? args[1] : {} var classAttrName = "class" in attrs ? "class" : "className" @@ -21,7 +34,14 @@ Mithril = m = new function app(window, undefined) { } if (classes.length > 0) cell.attrs[classAttrName] = classes.join(" ") - cell.children = hasAttrs ? args[2] : args[1] + + var children = hasAttrs ? args[2] : args[1] + if (children instanceof Array) { + cell.children = children + } + else { + cell.children = hasAttrs ? args.slice(2) : args.slice(1) + } for (var attrName in attrs) { if (attrName == classAttrName) cell.attrs[attrName] = (cell.attrs[attrName] || "") + " " + attrs[attrName] @@ -197,7 +217,7 @@ Mithril = m = new function app(window, undefined) { //schedule configs to be called. They are called after `build` finishes running if (typeof data.attrs["config"] === "function") { var context = cached.configContext = cached.configContext || {} - + // bind configs.push((function (data, node, isNew, context, cached) { var args = [node, !isNew, context, cached] @@ -808,12 +828,12 @@ Mithril = m = new function app(window, undefined) { if (options.dataType && options.dataType.toLowerCase() === "jsonp") { var callbackKey = "mithril_callback_" + new Date().getTime() + "_" + (Math.round(Math.random() * 1e16)).toString(36); var script = window.document.createElement("script"); - + window[callbackKey] = function(resp){ delete window[callbackKey]; window.document.body.removeChild(script); options.onload({ type: "load", target: { - responseText: resp + responseText: resp } }); }; @@ -834,7 +854,7 @@ Mithril = m = new function app(window, undefined) { e.preventDefault(); e.stopPropagation(); }; - + script.src = options.url + (options.url.indexOf("?") > 0 ? "&" : "?") @@ -889,7 +909,7 @@ Mithril = m = new function app(window, undefined) { var deferred = m.deferred() var serialize = xhrOptions.serialize = xhrOptions.dataType && xhrOptions.dataType.toLowerCase() === "jsonp" ? identity : xhrOptions.serialize || JSON.stringify - var deserialize = xhrOptions.deserialize = xhrOptions.dataType && xhrOptions.dataType.toLowerCase() === "jsonp" + var deserialize = xhrOptions.deserialize = xhrOptions.dataType && xhrOptions.dataType.toLowerCase() === "jsonp" ? identity : xhrOptions.deserialize || JSON.parse var extract = xhrOptions.extract || function(xhr) { return xhr.responseText.length === 0 && deserialize === JSON.parse ? null : xhr.responseText diff --git a/tests/mithril-tests.js b/tests/mithril-tests.js index 3d796eb1..1b87dd7f 100644 --- a/tests/mithril-tests.js +++ b/tests/mithril-tests.js @@ -9,16 +9,19 @@ function testMithril(mock) { test(function() {return m("[title=bar]").attrs.title === "bar"}) test(function() {return m("[title=\'bar\']").attrs.title === "bar"}) test(function() {return m("[title=\"bar\"]").attrs.title === "bar"}) - test(function() {return m("div", "test").children === "test"}) + test(function() {return m("div", "test").children[0] === "test"}) + test(function() {return m("div", "test", "test2").children[1] === "test2"}) test(function() {return m("div", ["test"]).children[0] === "test"}) test(function() {return m("div", {title: "bar"}, "test").attrs.title === "bar"}) - test(function() {return m("div", {title: "bar"}, "test").children === "test"}) + test(function() {return m("div", {title: "bar"}, "test").children[0] === "test"}) test(function() {return m("div", {title: "bar"}, ["test"]).children[0] === "test"}) - test(function() {return m("div", {title: "bar"}, m("div")).children.tag === "div"}) + test(function() {return m("div", {title: "bar"}, m("div")).children[0].tag === "div"}) test(function() {return m("div", {title: "bar"}, [m("div")]).children[0].tag === "div"}) + test(function() {return m("div", {title: "bar"}, "test0", "test1", "test2", "test3").children[3] === "test3"}) // splat + test(function() {return m("div", {title: "bar"}, m("div"), m("i"), m("span")).children[2].tag === "span"}) test(function() {return m("div", ["a", "b"]).children.length === 2}) test(function() {return m("div", [m("div")]).children[0].tag === "div"}) - test(function() {return m("div", m("div")).children.tag === "div"}) //yes, this is expected behavior: see method signature + test(function() {return m("div", m("div")).children[0].tag === "div"}) //yes, this is expected behavior: see method signature test(function() {return m("div", [undefined]).tag === "div"}) test(function() {return m("div", [{foo: "bar"}])}) //as long as it doesn't throw errors, it's fine test(function() {return m("svg", [m("g")])}) @@ -723,7 +726,7 @@ function testMithril(mock) { test(function() { //https://github.com/lhorie/mithril.js/issues/200 var root = mock.document.createElement("div") - + var unloaded1 = false function unloadable1(element, isInit, context) { context.onunload = function() { @@ -732,7 +735,7 @@ function testMithril(mock) { } m.render(root, [ m("div", {config: unloadable1}) ]) m.render(root, [ ]) - + var unloaded2 = false function unloadable2(element, isInit, context) { context.onunload = function() { @@ -741,7 +744,7 @@ function testMithril(mock) { } m.render(root, [ m("div", {config: unloadable2}) ]) m.render(root, [ ]) - + return unloaded1 === true && unloaded2 === true }) test(function() { @@ -1555,7 +1558,7 @@ function testMithril(mock) { xhr.onreadystatechange() return xhr.$headers["Content-Type"] == "application/json; charset=utf-8" }) - + // m.request over jsonp test(function(){ // script tags cannot be appended directly on the document @@ -1565,8 +1568,8 @@ function testMithril(mock) { var _window = mock; var error = m.prop("no error"); - var req = m.request({ - url: "/test", + var req = m.request({ + url: "/test", dataType: "jsonp", background: true }).then(null, error); @@ -1646,7 +1649,7 @@ function testMithril(mock) { _window[callbackKeys[0]](out); mock.document.body.removeChild(scriptTag); delete _window[callbackKeys[0]]; - } + } mock.document.removeChild(body); return JSON.stringify(out) === JSON.stringify(req()); })