How HTML Templates are Compiled
A quick note before we begin: If tags and filters within code are wrapped in comment tags, as in <!--{% tag %}-->
then some browsers treat them as full nodes and will help a lot with node traversal. Just something worth knowing.
There are two steps to compiling a template: tokenizing a template, and parsing the tokens. The types of tokens can be found in dojox.dtl.text.types as well as dojox.dtl.html.types. They are: tag, variable, text, change, attribute, and element. Tags and variables are of course the foundation of our markup, text is just plain text that has no real bearing on markup, a change is when the parent node changes, an attribute is an HTML attribute, and an element is a DOMNode. During tokenizing, we actually disassemble the tree structure so that when the tokenization is done, none of the Nodes have a parent Node. The first Node encountered is the "root" Node. This is what we will use to insert our template into our document.
We build these tokens by traversing the Node tree, and when we reach a text node, tokenizing the contents if it has any tags or filters (note: this is where, if you use comments, this will never have to happen). After we're done tokenizing, we move on to parsing.
During parsing, all of the tokens become objects. Many of the tokens are always replaced with the same object. For example, the change type always becomes a dojox.dtl.html.ChangeNode, the element type always becomes a dojox.dtl.html.Node, a variable always becomes a dojox.dtl.html.VarNode, and text always becomes a dojox.dtl.html.TextNode. This leaves us with the attribute and tag types. An attribute token can go in two directions: If there is a tag registered for the name of that attribute, we call that function and the returned object replaces that token. If not, the token becomes a dojox.dtl.html.AttributeNode.
The major player here is the tag token. When the registered tag function is called, the tag doesn't always simply replace the token, it can actually "swallow" tokens until it finds a tag that it's looking for. A good example of this is the if/else/endif structure, where the function parses tokens until it reaches and endif tag. This is how nesting tags is possible. In the case of the if tag, while the first if tag is parsing tokens, looking for an else or endif tag, the parser might run into another if tag. This if tag now the one parsing tokens, looking until it finds the endif tag. Once it's done, that object replaces a whole range of tokens in the content that first if tag has been building. The parent if tag, when finished, returns an object to the parser that replaces a range of tokens, all of which are now in either one of the new objects returned by the if tags. No tokens ever disappear, they can all be accounted for in the various objects.
What we end up with is a very linear set of instructions. We have an array of objects, all our objects have a render function, and many of the objects contain lists of objects, and when their render function is called, they will call the render function on the objects they have swallowed. We have what's basically, a tree that always executes linearly, and is always called in the same order.
- Printer-friendly version
- Login or register to post comments
- Unsubscribe post