Wednesday, September 30, 2009

A DSL for generating HTML with JS

Maybe some people find this interesting. I created a little DSL to generate HTML with JavaScript. Here is an example:
var things = [{text: "Hello"}, {text: "World"}];

with(HTML) {

var html = div({
id: "foo",
content: a({
href: "#",
content: ["test", span("bar")]
})
}) +

div({
className: "baz",
content: map(things, function (thing) {
return p(thing.text)
})
});

alert(html)

document.write(html)
}

Yes, I know, with is very evil but it is cool in this situation.

Here is the full source. I hacked it up in 30 minutes and it is in bad need for a cleanup, but otherwise it seems to work alright.

var HTML = (function () {
function encode(text) {
text = text.replace(/&/g, "&", "g");
text = text.replace(/</g, "&lt;", "g");
text = text.replace(/>/g, "&gt;", "g");
text = text.replace(/"/g, "&quot;", "g");
text = text.replace(/'/g, "&apos;", "g");
return text;
}

function renderAttr(attr) {
if(!attr) return '';
var html = '';
for(var name in attr) {
var value = attr[name];
if(name === "className") name == "class";
if(name === "content") continue;
html += ' '+name+'="'+encode(value)+'"'
}
html.encoded = true;
return html
}

function makeTag (name) {
return function (paras, content) {
if(arguments.length < 2) {
content = [];
}
if(typeof content === "string") content = [content]
var attr = {};
if(typeof paras === "string") {
content = [encode(paras)];
} else if(paras == null) {
content = [];
} else if(paras.content) {
var c = paras.content;
delete paras.content;
return arguments.callee(paras, c);
} else if(paras.length > 0) {
content = paras;
} else {
attr = paras;
}
var empty = content.length === 0 ? true : false;

var html = '<'+name+renderAttr(attr)+(empty ? '/' : '')+'>';
if(!empty) {
html += content.join("");

html += '';
}
return html;
}
}

function map(enumerable, fn) {
var result = [];
for(var i = 0, len = enumerable.length; i < len; ++i) {
result.push(fn(enumerable[i]))
}
return result;
}

var tags = ["div", "a", "p", "span"]

var HTML = {
map: map,
encode: encode
};

map(tags, function (name) {
HTML[name] = makeTag(name)
});

return HTML;
})();
Post a Comment