Skip to content
bard edited this page Sep 13, 2010 · 15 revisions

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.

Example


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>

More examples

For a list of supported tag- and attribute-processors, see examples.js

Extensions

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).

Gotchas

Outputting HTML

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.

Tag vs. attribute processors

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. :)

Using in Helma

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);
    }