Home
seethrough is a simple XML/XHTML template engine for JavaScript based on E4X. It’s the port of a former project for Erlang.
seethrough works by pulling data from a JavaScript object and using it to expand an XML template. The template by itself does not embed any code, so it stays designer- and validator-friendly.
var src = new XML(readFile('sample.st'));
var template = seethrough.compile(src);
var data = {
site: {
title: 'Foobar'
}
};
var output = template(data);
sample.st:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:st="http://hyperstruct.net/seethrough#js">
<head>
<title st:content="site.title"/>
</head>
<body>
<h1>Welcome to <span st:replace="site.title"/>!</h1>
</body>
</html>
Output:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:st="http://hyperstruct.net/seethrough#js">
<head>
<title>Foobar</title>
</head>
<body>
<h1>Welcome to Foobar!</h1>
</body>
</html>
For a list of supported tag- and attribute-processors, see examples.js
seethrough ships with just a few processors. You can add your own by adding properties to the seethrough.processors
object. See the Extension Library for some extra processors (and feel free to add your own).
If you want to output text/html (thank you IE) you’ll want to set XML.ignoreWhitespace = false
and type your <script>
tags like this:
<script src="/scripts/foo.js"> </script>
That is, make sure there’s an opening and closing tag, and a space in between, otherwise you end up with a XML-correct but IE-upsetting <script src="/scripts/foo.js"/>
and are most likely left to stare at a blank page.
Currently, both tag and attribute processors live in the seethrough.processors
object. If you use a tag for a processor that was meant to handle attributes, and vice-versa, chaos will ensue. You’ve been warned. :)
I save templates as <action>.st
, i.e. for main.hac
I’ll have a main.st
template, then at the end of main.hac
call the following:
data = { ... };
res.write(this.render(data));
The render
function is defined in HopObject/extra.js
and has code to serve the appropriate content type to different browsers.
Note that it re-compiles the template on each request — for production, you’ll want to compile once and cache it instead.
function render(env) {
var m = req.getHeader('Accept').match(/(application\/xhtml\+xml|application\/xml|text\/xml)/);
var header;
// Only serve content as XML to those browsers who can handle it
// (basically every modern browser except IE).
//
// Don't serve XML prologue to IE6, since it causes it to enter
// Quirks mode.
//
// Force IE into standards mode with the HTML doctype.
if(m) {
res.contentType = m[1];
res.contentType = 'application/xhtml+xml';
header = '<?xml version="1.0" encoding="utf-8"?>';
} else {
res.contentType = 'text/html';
header = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">';
}
// Needed to avoid collapsing '<script src="..."> </script>' into
// '<script/>' and upsetting IE
XML.ignoreWhitespace = false;
XML.prettyPrinting = false;
// Trim, otherwise leading and trailing whitespaces are assumed to
// be top-level nodes and we can't have more than one top-level
// node.
var templateSource = this
.getResource(req.action + '.st')
.getContent()
.trim();
var template = new XML(templateSource);
return header + '\n' + seethrough.compile(template).call(null, env);
}