Settee.js
________
( )
()________()
||________||
' '
An s-expression template engine with no external dependencies.
Templates look like this:
(html (head (title "Hello") (script src="/js/my-app.js")) (body (article (section.main (p "Welcome to my site, " :name "!"))) (aside.sidebar (ul (li (a href="/index.html" "Home"))))))
Use it like this:
var template= html=
It's great to drink coffee while lounging on the settee!
template= settee sourcehtml= template name:"Matt"
Templates
Templates are rudimentary s-expressions (think LISP). S-expressions, if you aren't familiar, are just lists of lists. Each list is surrounded in (parenthesis).
In Settee, the first list item is the tag name.
(div)
Output:
You can set attributes using attr=value
syntax.
(div class=body)
You can use css-style syntax on the tag name to set the class names and id:
(div#main.body)
Nested lists generate nested HTML:
(article (header "Heading") (section.body (p "Content") ))
Heading Content
Note: The indented whitespace is added for readability, the generated output will not have any whitepace between tags.
Settee also supports a shorter syntax. You don't need the indentions or closing parenthesis for the last list. So that previous template could also look like this:
(article (header "Heading")(section.body (p "Content"
You can insert values from the rendering context using :varname
syntax:
(div "hello " :name)
Given a context of { name:'Matt' }
:
hello Matt
There some built in tags to help you build your templates:
(if (eq :name "Matt") (div "Hello Matt") (div "You aren't Matt!"))
Builtins: if
, unless
, eq
(aliased as =
and is
), neq
(aliased as !=
and isnt
).
There is a special helper .
that returns all the child lists with adding
anything around them. This is because Settee expects to have only one root list.
(. (div "One") (div "Two")
OneTwo
Settee also support simple looping. Each loop has access to :item
and :index
in the render context.
(ul (loop :items (li data-id=:index :item) )
settee;
A B
Method Signatures
As function:
; // source= String | Array// => returns template function
If source
isn't a String
, Settee will assume it's precompiled source and build the
template function with it. Otherwise the source is parsed first.
The resulting template function looks like this:
; // context= Object // => returns rendered template as a String
Just send the rendering context as an object.
If you want to do it in one pass, use render
:
settee; // => returns rendered template as a String
Custom Tags / Partials
You can define custom tags, or partials, in two ways. The first way is as a sub-template:
setteedefine 'layout''''(article.layout (header "My App") (section.body :block1) (footer :block2))'''
You can then use it as a tag in your templates:
(layout (div "Content Area") (div "Copyright me"))
The resulting template compiles to (whitespace added for redability):
My App Content Area Copyright me
If you need a little more control, you can specify a handler function that works at a lower level:
setteedefine 'widget' # types: tag= String, attrs= Object, children= Array # This function should return a 'rendered' String. """<div id=""></div>"""
Precompiling
Want to precompile your templates from node for added speed?
Install settee-templates
:
npm install settee-templates
Then:
settee= require 'settee-templates' source= '(html (body'output= setteeprecompile source # => function anonymous(b) {return b('html', { }, [b('body', { }, [])]); }
You'll still need to include settee in your page, but it will skip the parse phase.
You can include settee.runtime.js
if all you plan to use is precompiled templates.
The runtime version omits the parser and compiler code.
Credits
Original S-Expression parser code based on:
http://code.google.com/p/javascript-lisp-interpreter/source/browse/lisp.js