Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: aa085b9bee
Fetching contributors…

Cannot retrieve contributors at this time

336 lines (284 sloc) 15.128 kb
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Mold</title>
<link rel="stylesheet" type="text/css" href="css/docs.css"/>
<style type="text/css">
body {
margin: 0;
font-family: tahoma, arial, sans-serif;
padding: 3em 6em;
color: black;
}
h1 { font-size: 22pt; border-bottom: 3px solid #A19; }
h2 { font-size: 14pt; border-bottom: 1px solid #A19; }
code {
font-family: courier, monospace;
font-size: 80%;
color: #448888;
}
pre.code {
margin: 1.1em 12px;
border: 1px solid #CCCCCC;
color: black;
padding: .4em;
font-family: courier, monospace;
}
</style>
</head>
<body>
<h1>Mold</h1>
<p>Mold is a very light-weight (200 lines of JavaScript)
client-side JavaScript-based templating library. It compiles
('bakes') plain-text templates to functions that map JavaScript
values to strings (the filling in of the template) and provides an
interface for directly inserting the result of such functions into
documents ('casting' the molds).</p>
<p>The majority of Mold's operators are concerned with plain text,
but a few of them are HTML-specific, in that they refer to the
nodes created by the template. Some magic is involved in this (see
section <a href="#implementation">'Implementation'</a>), but <a
href="#cast"><code>Mold.cast</code></a> encapsulates that
magic.</p>
<p>In a typical use scenario, the web application somehow gets
template strings from the server (it could directly fetch text
files, or the strings might be in script files), compiles them
once, and then uses the resulting 'molds' to build up parts of the
document.</p>
<h2>Contents</h2>
<ul>
<li><a href="#download">Download and license</a></li>
<li><a href="#support">Support</a></li>
<li><a href="#example">Example template</a></li>
<li><a href="useage">Useage</a></li>
<li><a href="implementation">Implementation</a></li>
</ul>
<h2 id="download">Download and license</h2>
<p>Mold consists of only a single (small) script file: <a
href="mold.js"><code>mold.js</code></a>. The library is licenced
under a zlib-style <a href="LICENSE">license</a>. For those
wanting to work on it, the <a href="http://git-scm.com">git</a>
repository can be gotten from
<code>http://marijnhaverbeke.nl/git/mold</code>. The code is also
on <a href="http://github.com/marijnh/mold">github</a>.</p>
<p>I am not doing numbered releases, since the userbase is still
small. The library interface can be considered stable, though.</p>
<h2 id="support">Support</h2>
<p>You are welcome to mail me (<a
href="mailto:marijnh@gmail.com">Marijn Haverbeke</a>) with any
questions or problems you have.</p>
<h2 id="example">Example template</h2>
<p>A template to build a simple multiple choice widget, for
example, might look like this (refer to the explanation below if
something confuses you):</p>
<pre class="code">
&lt;div class="choice"&gt;
&lt;h2&gt;Choose one:&lt;/h2&gt;
&lt;?do var odd = false;?&gt;
&lt;ol&gt;
&lt;?label choiceList?&gt;
&lt;?for choice $arg?&gt;
&lt;li class="option [?text (odd = !odd) ? "odd" : "even"?]"&gt;
&lt;?event click handleChoice(choice);?&gt;
&lt;?if typeof choice == "string"?&gt;
&lt;strong&gt;&lt;?text choice?&gt;&lt;/strong&gt;
&lt;?else?&gt;
&lt;?html choice.render()?&gt;
&lt;?if.?&gt;
&lt;/li&gt;
&lt;?for.?&gt;
&lt;/ol&gt;
&lt;/div&gt;</pre>
<p>This loops over an array of choices, building up a
representation of each choice, with a special case for choices
that are simple strings. Note that <code>choice.render</code> can
also be a mold function. Templates can easily be composed like
this.</p>
<h2 id="useage">Useage</h2>
<p>To use Mold, you include <a
href="mold.js"><code>mold.js</code></a> in your document, which
creates a <code>Mold</code> object that is the interface to the
library.</p>
<p id="bake">The <code>Mold.bake</code> function, which takes a
single string as an argument, is used to transform a template
string into a mold. A mold is a function that takes a single
argument (or none, if your template does not use any parameter
data) and returns a string.</p>
<p>Mold uses XML processing instruction syntax for template
operators. This means '<code>&lt;?</code>' and
'<code>?&gt;</code>' are used around all instructions, as in PHP.
Contrary to PHP, though, Mold uses several different processing
instructions which serve different roles. After the
'<code>&lt;?</code>', an instruction name should follow (see
below). In cases where you don't want to use angle brackets
(inside attributes in a valid XML document, for example),
'<code>[?</code>' and '<code>?]</code>' can be used as
equivalents.</p>
<p>(Note that the scanner used by Mold is rather simplistic. If
you have a string or regular expression containing
'<code>?&gt;</code>' inside your instruction, you should use
'<code>?\&gt;</code>' instead, to prevent it from being recognised
as the end of the instruction.)</p>
<p>The following instructions are recognised:</p>
<dl>
<dt><code>text</code> (or <code>t</code>)</dt>
<dd>Should be followed by a JavaScript expression. On template
expansion, this expression will be evaluated (the argument given
to the template will be available as <code>$arg</code>
throughout), and inserted as text &#x2015; meaning any HTML
special character in it will be escaped.</dd>
<dt><code>html</code> (or <code>h</code>)</dt>
<dd>Works like <code>text</code>, but the result of the
expression is not escaped, so HTML tags can be inserted.</dd>
<dt><code>do</code> (or <code>d</code>)</dt>
<dd>Evaluates a block of code (given after the <code>do</code>)
without producing output. Can be useful for defining local
variables (any variables defined in such a block will be visible
to instructions coming after it.)</dd>
<dt><code>if</code></dt>
<dd>Should be given a JavaScript expression. If this expression
evaluates to false, the block (up to the matching
<code>if.</code>, <code>elif</code>, or <code>else</code>) will
not be evaluated/expanded.</dd>
<dt><code>elif</code></dt>
<dd>If following an <code>if</code> or other <code>elif</code>
that did not fire, evaluates the expression it is given, and
only expands the block following it if this produces a true
value.</dd>
<dt><code>else</code></dt>
<dd>Allowed at the end of a chain of <code>if</code>s and
<code>elif</code>s. The block following <code>else</code> is
expanded only if none of the previous conditions fired.</dd>
<dt><code>if.</code></dt>
<dd>Closes a chain of
<code>if</code>/<code>elif</code>/<code>else</code>
instructions.</dd>
<dt><code>for</code></dt>
<dd>Used to loop over arrays or objects. Can follow several
forms: <code>&lt;?for x [expr]?&gt;</code> loops over the array
created by the given expression. <code>&lt;?for key, value in
[expr]?&gt;</code> loops over an object. The <code>value</code>
variable in the second form can be omitted when not needed. The
template fragment in the loop will be repeated once for every
element in the collection, with the named variables bound to the
relevant values (bound by function call, not reassigned, so they
can be closed over). The variable <code>$i</code>, inside the
loop body, refers to the current index, starting at 0.</dd>
<dt><code>for.</code></dt>
<dd>Closes a <code>for</code> block.</dd>
<dt><code>event</code></dt>
<dd>Used to attach an event to an HTML node. The instruction
should not be placed between the '<code>&lt;</code>' and
'<code>&gt;</code>' of an HTML tag. The tag it applies to is
determined as follows (the same applies for the instructions
after this): When it sits before all non-text nodes in the
enclosing node, it applies to the enclosing node. Otherwise, it
applies to the first non-text node before it. Directly after the
word <code>event</code>, an event type should be given (without
'<code>on</code>', so something like '<code>click</code>' or
'<code>submit</code>'). After that the handler body follows,
which may refer to the <code>$event</code> variable to get the
event object. Mold supports one non-standard event,
<code>enter</code>, which will fire for <code>keypress</code>
events with a key-code of 13.</dd>
<dt><code>run</code> (or <code>r</code>)</dt>
<dd>This works like <code>do</code>, except that the
<code>$node</code> variable refers to the nearest HTML node.
Having access to this node means that it has to be run 'out of
line', after the template has been expanded, so variables
defined in this instruction are not visible to the rest of the
template code. Variables defined during template expansion, on
the other hand, <em>are</em> visible in this block.</dd>
<dt><code>label</code> (or <code>l</code>)</dt>
<dd>This is used to get a reference to the nearest (as in
<code>event</code>) HTML node. It takes a name as argument,
which will become a property in the object that <a
href="#cast"><code>Mold.cast</code></a> (see below) returns,
pointing to the node. When a label occurs inside a loop, the
property will refer to an array of nodes. (Note that nested
loops will still produce a single, flat array.)</dd>
</dl>
<p>It should be noted that, while <a
href="#bake"><code>Mold.bake</code></a> does some error checking,
it is still rather easy to pass nonsense parameters to
instructions. This can result in rather hard to debug run-time
failures (see <a href="#implementation">'Implementation'</a>), but
could, if you really want to, be used to make the library do
things it is not designed for &#x2015; you could create your own
PHP-style loops, for example, with two <code>do</code>
instructions, one opening the loop and one closing it.</p>
<p id="cast">To build HTML from a compiled template, the
<code>Mold.cast</code> function is used. It takes three arguments
<code>(node, mold, arg)</code>, and replaces the content of
<code>node</code> with the result of applying <code>mold</code> to
<code>arg</code>. The last argument can be left off for molds that
do not require data. If there were any labels defined in the mold
used (or molds called from that mold), the value returned by
<code>Mold.cast</code> will be an object containing links to the
labeled nodes.</p>
<p id="castAppend">As an alternative, there is also
<code>Mold.castAppend</code>, which takes the same arguments as <a
href="#cast"><code>cast</code></a>, but appends the new nodes to
the target node, instead of replacing its content.</p>
<p id="define">It is possible to define custom instructions with
<code>Mold.define</code>, which accepts a string (the instruction
name) and a mold-like function (takes one argument, returns a
string) and defines a new instruction that takes an (optional)
expression as its argument and expands to the result of calling
the given function with the value of this expression. This is
basically a way to define pleasant shortcuts for things like
<code>&lt;?html myTemplate("something")?&gt;</code>.</p>
<p>There are a few more properties of the <code>Mold</code> object
that can be useful. The most important one is
<code>Mold.attachEvent</code>. By default, Mold uses a very
simplistic event handler framework, which leaves most of the
browser incompatibilities intact (it only chooses between
<code>addEventListener</code> and <code>attachEvent</code>, and
makes sure the event object arrives properly). Most JavaScript
libraries provide a nicer wrapper, which can be wired into Mold by
setting <code>Mold.attachEvent</code> to a function of three
arguments: <code>(node, eventName, func)</code>, where
<code>eventName</code> is the type of event (without
'<code>on</code>' prefix), and <code>func</code> is a function of
one argument (the event object).</p>
<p>When using <a href="http://www.prototypejs.org">Prototype</a>,
for example, you might do this:</p>
<pre class="code">
Mold.attachEvent = Event.observe;</pre>
<p>There is also <code>Mold.escapeHTML</code>, which HTML-escapes
the characters '<code>&lt;&gt;&quot;&amp;</code>'. This is not
directly needed for using Mold, but since it was defined
internally anyway, and might come in useful, it is exported.</p>
<h2 id="implementation">Implementation</h2>
<p>What <a href="#bake"><code>Mold.bake</code></a> does is
decompose the template into instructions and pieces of plain text,
and then build a (textual) JavaScript function from these pieces,
and use <code>eval</code> to turn it into a real function. This
way, loops and conditionals are straightforwardly borrowed from
the underlying interpreter, and a lot of work must only be done
once.</p>
<p>The <code>event</code>, <code>run</code>, and
<code>label</code> instructions need information about the nodes
in the resulting DOM tree. Rather than implement some kind of HTML
parser, Mold delays their execution until after the text has been
parsed by the browser. At expansion time their code is stored in a
(numbered) function, which closes over the variables that are
active in the template. At the place where they occur, a dummy tag
(I used empty <code>var</code> tags with a special class) in
inserted. Then, after using <code>innerHTML</code> to transform
the text into a DOM tree, <a
href="#cast"><code>Mold.cast</code></a> searches for such tags,
removes them, and runs the code on the relevant nodes. (If you
directly call mold functions that use these instructions, you will
see the dummy tags.)</p>
<p>Whether a system like this &#x2015; intricately connected to
the dodgy <code>innerHTML</code> property &#x2015; is the most
ideal is debatable. My initial intention was to use XHTML
templates with a bunch of special tags, a kind of XSLT for
JavaScript/JSON data, and use DOM manipulation for all the
transformations. But, while <code>innerHTML</code> is implemented
pretty well in most browsers, stuff like <code>cloneNode</code>
and transforming XML trees to HTML is slightly flaky in a lot of
them (and <em>extremely</em> flaky in one particular browser...
you guess which).</p>
</body>
</html>
Jump to Line
Something went wrong with that request. Please try again.