BASIC USAGE: ============== var T = new Zen(syntax, lazy_load): syntax: zen/emmet syntax lazy_load: if true, delays syntax parsing until template is built var T = new Zen(zen_object) clone a template T.DOM: get the cached dom use T.DOM.cloneNode(true) to reuse the template T.build(anonvars, ..., {namedvars}): for one time usage (or to set default parameters) returns T.DOM SYNTAX OVERVIEW: ================ Template syntax is based on Emmet/Zen syntax. It is basically CSS selectors, used to define html documents. See http://docs.emmet.io/abbreviations/syntax/ to learn Emmet/Zen. Currently supports the following operators: +: sibling >: child ^: climb-up (parent's sibling) (): grouping []: attributes =: set attribute value #: id attribute shortcut .: class attribute shortcut {}: text content "': quoted strings %: template variable \t\n\r\v\f : whitespace \: escape character \0: reserved character, DO NOT USE!!! ~,;*@$&<|!?: in the future, I may extend the grammar to include additional operators; so, its probably a good idea to treat these symbols as reserved characters (if you're worried about forward compatability) FORMAL GRAMMAR: ================== Emmet/Zen's documentation is a little ambiguous, so here is a formal grammar: START ::= START "+" START | START ">" START ("^" START)? | "(" START ")" | WHITESPACE* START WHITESPACE* | NODE NODE ::= TAGNAME? ATTR* TEXT? TAGNAME ::= CHARACTER* ATTR ::= ID | CLASS | "[" LIST? "]" | WHITESPACE* ID ::= "#" CHARACTER* CLASS ::= "." CHARACTER* LIST ::= VAL WHITESPACE+ LIST | VAL VAL ::= STRING | STRING WHITESPACE* "=" WHITESPACE* STRING TEXT ::= "{" WHITESPACE* STRING_WS? WHITESPACE* "}" WHITESPACE ::= /[\t\n\r\v\f ]/ STRING_WS ::= STRING | WHITESPACE if no WHITESPACE lookahead STRING ::= "'" CHARACTER+ "'" | '"' CHARACTER '"' | CHARACTER STRING CHARACTER ::= non-operator character | operator character not in lookahead | escaped operator character except \0 | VAR VAR ::= "%" /\w/? "%" A couple things to note about this grammar, especially changes I made to Emmet/Zen's original syntax: - multiple attribute definitions are concatenated together into a single space-separated value; they are concatenated in the order they were defined; for example: #id1#id2 = #id1[id=id2] = [id=id1 id=id2] = #id1\ id2 .class1.class2 = .class1\ class2 - whitespace: - for pretty formatting, you may insert whitespace anywhere outside a node definition (TAGNAME, ATTR, TEXT) - whitespace at start/end of TEXT definition is ignored - to include whitespace in a node definition (TAGNAME, ATTR, TEXT), use a backslash to escape the whitespace; for LIST attributes (stuff inside brackets) or TEXT definitions, you can also use a quoted string - template variables can be inserted anywhere in a node definition (TAGNAME, ATTR, TEXT) - ID and CLASS can be empty; however, attribute names/values cannot be empty div[=test]: INVALID div[blah=]: INVALID, use div[blah] instead div#.##.[blah=test]: VALID The minified version does no error checking, so be careful with empty values! div[blah=test =man] means test=man, not blah=test - if no TAGNAME is specified, "div" is used by default - NODE can be empty; if NODE is empty, it is treated as "div" "p>>" = "p>div>div" - to be forward/backward-compatible with HTML/XML, there are no character restrictions for TAGNAME/ATTR - strings can be a mix of quoted and unquoted text; use a backslash to interpret quotes literally unquote"quote"unquote = unquotequoteunquote unquote\"quote\"unquote = unquote"quote"unquote - TEXT is interpreted as a DOM TextNode; this means div{"test"} is just a shortcut for div>{"test"}^; since it's a TextNode, it cannot contain any HTML code; use div[innerHTML="test"] instead- be warned though, that this will have undefined behavior if the node has children: div[innerHTML="X"]{Y} = ???? div[innerHTML="X"]>div.Y = ???? Likewise, textContent/nodeValue attributes will have similar undefined behavior. QUOTED VALUES: ================ Attribute values and text content can contain a quoted string: ["type"="javascript"]{"content"} Backslashes are special characters; so you need to escape them: {"backslash\\"} That means you'll need to double escape them inside a javascript string: "{\"backslash\\\\\"}" If you need a lot of backslashes, use a template variable instead "{%backslash%}" Or use the Template.escape method: "{"+Template.escape("backslash\\")+"}" UNQUOTED VALUES: ==================== If you don't want to quote the string, but the content begins with a quote, escape the starting quote: [type=\'sup]{\'cuz} Make sure you have the correct number of backslashes: {'test'} = "test" {\'test} = "'test" {\\'test} = "\'test" Or you can just use the Template.escape method, as shown above TEMPLATE VARIABLES: ===================== To define template variables, use %VAR_NAME% - VAR_NAME can be [-\._a-zA-Z0-9] var t = new Zen("div{%text%}"); var dom = t.build({text: "Hello world!"}); document.body.appendChild(dom); To reuse the template, you can clone the output DOM: var dom = t.build({text: "Hello world!"}).cloneNode() If you need a fresh copy of the template (assuming no variables have been applied), use the copy constructor var t = new Zen(other_zen_template) To access the cached template DOM: var cache = t.DOM; If you append the cached DOM directly, you can update variables still: document.body.appendChild(t.DOM); t.setValue("name", "dynamically updated on page") Text variables rely on text nodes. If text nodes on your page could be normalized, and you need to update the variable values after they are added to the DOM, wrap the variables in a separate tag: var t = new Zen("{Name = }+span{%name%}"); You don't have to have named variables; use %% for unnamed vars: var t = new Zen("div{%%}"); var dom = t.build("Hello world!"); To use unnamed and named, use this syntax: t.build(unnamed, ..., {named}); Variables for "dataset", "style", and "attributes" must be specified as objects (except style, which can be a string, in which case it is set to the string directly): var t = new Zen("[dataset=%data% style=%styles% attributes=%attrs%]") var dom = t.build({ data: {x: "data-x value"}, style: {border: "1px solid black"}, attributes: {contentEditable: true} }) Note that these values may overwrite ones explicitly declared, if they are set afterwards: var t = new Zen("[data-x=test dataset=%data%]") var dom = t.build({ data: {x: "this will override the previous value, 'test'"} }) If the template variable is not surrounded by text, it will be interpreted literally (only for attribute values): div[data=%%]: data can be any JS object div[data=text%%]: data will be a JS string, since "text" gets prepended Currently, variable attribute names cannot be combined as a space-separated list. They simply override any previous values. div[%x%=7 %y%=8]: when %x% == %y%, %y% overrides %x% #id[%x%=override]: when %x% == id, the previous value ("id") gets overriden You may use template variables for attribute names, and attribute values. tagname #%id% [%name% = %value%] {%text%} Variable attribute names are a bit more expensive, than variable values. If performance is a concern, avoid variable attribute names. Of course, using no template variables all together will be fastest. Variables inside tagnames used to be allowed, but I have since removed it, since it wouldn't have had very many use cases (and would be an expensive computation). FRAGMENT MULTIPLIERS (NOT IMPLEMENTED YET): ============================================ You can repeat a fragment of your template using a multiplier operator: "div*5" -> repeats div five times "(div+button)*5" -> repeats the group five times You can specify a variable as the multiplier: var t = new Zen("div*%x%") t.build({x: 5}); -> repeats div five times The multipler value can also be zero, thus making the fragment optional: t.build({x: 0}); -> div will not be included in the template If there are variables inside fragment, you can specify their values using an array: var t = new Zen("#%id%*%x%"); t.build({x:[ {id: "id0"}, {id: "id1"}, {id: "id2"}, ]}); -> creates three divs, each with different ids TODO: ======== *: for multiplication !{}: for html comments named DOM nodes for adding listeners and stuff, maybe with & or @ symbol before node definition disable quotes in {} ??? maybe make values either quoted or not quoted; can't be both quotes for states 1-2 ??? allow