Awesome
Gaikan (2.0.0)
Gaikan is the fastest JavaScript template engine and has been designed to use data-*
attributes to implement most template features. Due to this design, the templates are completely designer- and tooling friendly. The template engine is compatible with nodejs, expressjs (a popular web application framework for nodejs) and modern browsers (IE7+). Test the template engine in the browser with the interactive Gaikan (work in progress, IE9+) tool.
Table of Contents
- 1. Quickstart
- 1.1. NodeJS
- 1.2. ExpressJS
- 1.3. Browser
- 2. Benchmarks
- 2.1. Linux Ubuntu 12.04, NodeJS 0.10.26 (100.000x)
- 2.2. Windows 7 x64 SP1, NodeJS 0.10.26 (100.000x)
- 3. Introduction
- 4. Variables
- 4.1. Escaping
- 4.2. Alterant
- 4.2.1. Break
- 4.2.2. Decode
- 4.2.3. Encode
- 4.2.4. Lower
- 4.2.5. Title
- 4.2.6. Upper
- 4.2.7. Url
- 4.3. Set
- 4.3.1. Empty
- 4.3.2. Sort
- 5. Attributes
- 5.1. If
- 5.2. For
- 5.3. Attribute
- 5.4. Extract
- 5.5. Include/Fragment
- 5.6. Evaluate
- 6. Tips/Tricks
- 6.1. Inline CSS
- 6.2. If/Else
- 7. Server API
- 7.1. Options
- 7.1.1. Caching
- 7.1.2. Compression
- 7.1.3. Directories
- 7.1.4. Extensions
- 7.1.5. Layout
- 8. Browser API
- 8.1. Configuration
- 8.1.1. Compression
- 9. Runtime
- 9.1 Alterant
- 9.2 Set
- 10. Security
- 11. Planned Future Additions
1. Quickstart
1.1. NodeJS
npm install gaikan
1.2. ExpressJS
var gaikan = require('gaikan');
app.engine('html', gaikan);
app.set('view engine', '.html');
1.3. Browser
<script src="gaikan-2.x.x-min.js" type="text/javascript"></script>
2. Benchmarks
The template engine is, at the moment, the fastest JavaScript template engine. The benchmark suite has been made available at template-benchmark (forked from an unmaintained template benchmark suite). The benchmark suite was run on an AMD Phenom(tm) II X4 955 3.2GHZ processor.
2.1. Linux Ubuntu 12.04, NodeJS 0.10.26 (100.000x)
Gaikan ( 2090ms) - fastest
ECT ( 2334ms) - 12% slower
Fest ( 2791ms) - 34% slower
Dust ( 3030ms) - 45% slower
doT ( 3940ms) - 89% slower
Hogan.js ( 3977ms) - 90% slower
EJS without `with` ( 5190ms) - 148% slower
Swig ( 5258ms) - 152% slower
Underscore ( 6154ms) - 194% slower
Handlebars.js ( 7255ms) - 247% slower
Eco ( 8315ms) - 298% slower
EJS ( 9059ms) - 333% slower
Jade without `with` (10973ms) - 425% slower
CoffeeKup (11062ms) - 429% slower
Jade (27295ms) - 1206% slower
2.2. Windows 7 x64 SP1, NodeJS 0.10.26 (100.000x)
Gaikan ( 2147ms) - fastest
Fest ( 2535ms) - 18% slower
doT ( 3524ms) - 64% slower
Underscore ( 5108ms) - 138% slower
Handlebars.js ( 5734ms) - 167% slower
ECT ( 7223ms) - 236% slower
EJS without `with` ( 8732ms) - 307% slower
Dust ( 9136ms) - 326% slower
Hogan.js ( 9960ms) - 364% slower
Swig (10240ms) - 377% slower
Eco (12292ms) - 473% slower
Jade without `with` (13510ms) - 529% slower
EJS (14917ms) - 595% slower
CoffeeKup (15319ms) - 614% slower
Jade (34000ms) - 1484% slower
3. Introduction
It is important to understand a template engine, and this template engine is essentially quite simple. The template engine consumes a template and produces a JavaScript function. That function, representing the template, is then invoked with a runtime (__r
, the helper functions), a root object (the user input) and a fragments object (__f
, more on this later). The result of the function is the output. For each example in this documentation, the following object is used as root:
{
message: '<b>Hello world!</b>',
messageMultipleLines: 'Hello\nworld!',
messagePlain: 'Hello world!',
people: [{
age: 25,
name: 'John'
}, {
age: 18,
name: 'Jane'
}]
}
Due to this nature of compiling the template to a JavaScript function, the template features (such as variables and attributes) produce JavaScript code and emit each line of code into the function. To demonstrate the behaviour of each feature, the examples in this documentation will show the template, the function and the output. For example:
Hello world!
function anonymous(__r, root, __f) {
var __o = '';
__o += 'Hello world!';
return __o;
}
Hello world!
It is recommended to use the interactive Gaikan (work in progress, IE9+) tool to play with each example.
4. Variables
A variable is the only feature that does not use a data-*
attribute.
!{root.message}
function anonymous(__r, root, __f) {
var __o = '';
__o += (typeof root.message === 'undefined' ? '' : root.message);
return __o;
}
<b>Hello world!</b>
4.1. Escaping
Escaping a variable prevents XSS and is the recommend variable usage.
#{root.message}
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var __o = '';
__o += (typeof root.message === 'undefined' ? '' : alterant.encode(root.message));
return __o;
}
<b>Hello world!</b>
4.2. Alterant
An alterant modifies a variable. Multiple alterants can be chained.
#{root.messagePlain|upper,url}
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var __o = '';
__o += (typeof root.message === 'undefined' ? '' : alterant.url(alterant.upper(alterant.encode(root.messagePlain))));
return __o;
}
HELLO%20WORLD!
4.2.1. Break
The break alterant replaces new lines with <br />
.
#{root.messageMultipleLines|break}
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var __o = '';
__o += (typeof root.messageMultipleLines === 'undefined' ? '' : alterant.break(alterant.encode(root.messageMultipleLines)));
return __o;
}
Hello<br />world!
4.2.2. Decode
The decode alterant decodes special HTML characters.
#{root.message|decode}
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var __o = '';
__o += (typeof root.message === 'undefined' ? '' : alterant.decode(alterant.encode(root.message)));
return __o;
}
<b>Hello world!</b>
4.2.3. Encode
The encode alterant encodes spcial HTML characters. The equivalent of a #
variable statement.
!{root.message|encode}
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var __o = '';
__o += (typeof root.message === 'undefined' ? '' : alterant.encode(root.message));
return __o;
}
<b>Hello world!</b>
4.2.4. Lower
The lower alterant changes the variable to lower case.
#{root.messagePlain|lower}
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var __o = '';
__o += (typeof root.messagePlain === 'undefined' ? '' : alterant.lower(alterant.encode(root.messagePlain)));
return __o;
}
hello world!
4.2.5. Title
The title alterant changes the variable to title case.
#{root.messagePlain|title}
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var __o = '';
__o += (typeof root.messagePlain === 'undefined' ? '' : alterant.title(alterant.encode(root.messagePlain)));
return __o;
}
Hello World!
4.2.6. Upper
The upper alterant changes the variable to upper case.
#{root.messagePlain|upper}
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var __o = '';
__o += (typeof root.messagePlain === 'undefined' ? '' : alterant.upper(alterant.encode(root.messagePlain)));
return __o;
}
HELLO WORLD!
4.2.7. Url
The url alterant encodes the variable as an url component.
#{root.messagePlain|url}
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var __o = '';
__o += (typeof root.messagePlain === 'undefined' ? '' : alterant.url(alterant.encode(root.messagePlain)));
return __o;
}
Hello%20world!
4.3. Set
A set operates on an array or object (with properties).
4.3.1. Empty
The empty set checks if a variable is an empty set.
#{set.empty(root.people)}
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var set = __r.set;
var __o = '';
__o += (typeof set.empty(root.people) === 'undefined' ? '' : alterant.encode(set.empty(root.people)));
return __o;
}
false
4.3.2. Sort
The sort set can sort a variable, optionally by key and reverse.
<div data-for="person in set.sort(root.people, 'age')">
#{person.name} is #{person.age}
</div>
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var set = __r.set;
var __o = '';
__o += '<div>';
var __v0 = set.sort(root.people, 'age');
if (__v0) {
for (var __k0 = 0; __k0 < __v0.length; __k0 += 1) {
var person = __v0[__k0];
__o += '\n ' + (typeof person.name === 'undefined' ? '' : alterant.encode(person.name)) + ' is ' + (typeof person.age === 'undefined' ? '' : alterant.encode(person.age)) + '\n';
}
}
__o += '</div>';
return __o;
}
<div>
Jane is 18
John is 25
</div>
5. Attributes
The data-*
attributes emits (or omits) lines of code in the function.
5.1. If
The data-if
attribute emits an if.
<div data-if="root.message.length > 2">
#{root.message}
</div>
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var __o = '';
__o += '<div>';
if (root.message.length > 2) {
__o += '\n ' + (typeof root.message === 'undefined' ? '' : alterant.encode(root.message)) + '\n';
}
__o += '</div>\n';
return __o;
}
<div>
<b>Hello world!</b>
</div>
5.2. For
The data-for
attribute emits a for. Use in
for arrays, of
for objects. Key can be omitted.
<div data-for="value, key in root.people">
#{value.name} at index #{key} is #{value.age}
</div>
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var __o = '';
__o += ' <div>';
var __v1 = root.people;
if (__v1) {
for (var key = 0; key < __v1.length; key += 1) {
var value = __v1[key];
__o += '\r\n\t ' + (typeof value.name === 'undefined' ? '' : alterant.encode(value.name)) + ' at index ' + (typeof key === 'undefined' ? '' : alterant.encode(key)) + ' is ' + (typeof value.age === 'undefined' ? '' : alterant.encode(value.age)) + '\r\n\t';
}
}
__o += '</div>';
return __o;
}
<div>
John at index 0 is 25
Jane at index 1 is 18
</div>
5.3. Attribute
The data-attribute-*
attribute conditionally adds attributes to an element.
<option data-attribute-selected="root.message.length > 10"></option>
function anonymous(__r, root, __f) {
var __o = '';
__o += '<option';
if (root.message.length > 10) {
__o += ' selected';
}
__o += '></option>';
return __o;
}
<option selected></option>
5.4. Extract
The data-extract
attribute removes the containing element.
<div data-extract>
#{root.message}
</div>
function anonymous(__r, root, __f) {
var alterant = __r.alterant;
var __o = '';
__o += '\n ' + (typeof root.message === 'undefined' ? '' : alterant.encode(root.message)) + '\n';
return __o;
}
<b>Hello world!</b>
5.5. Include/Fragment
The data-include
attribute includes another template. The root passed to the included template is by default root
, but can be changed using templateName|root
. The data-fragment
attribute is used to add a placeholder, or fill a placeholder in the included template.
<div data-include="templateName">
<div data-fragment="content">
Replaces the content fragment placeholder in test.
</div>
</div>
function anonymous(__r, root, __f) {
var __o = '';
__o += '<div>';
var __f0 = {};
__f0['content'] = function (__r, root) {
var __o = '';
__o += '\n Replaces the content fragment placeholder in test.\n ';
return __o;
};
__o += __r('templateName', root, __f0);
__o += '</div>';
return __o;
}
<div>Test template with placeholder in b: <b>
Replaces the content fragment placeholder in test.
</b></div>
5.6. Evaluate
The data-evaluate
emits the block as JavaScript code. The equivalent of a @
variable statement.
<div data-evaluate>
console.log('Executed when rendering!');
</div>
@{console.log('Executed when rendering!')}
function anonymous(__r,root,__f) {
var __o = '';
console.log('Executed when rendering!');
__o += '\n';
console.log('Executed when rendering!')
return __o;
}
6. Tips/Tricks
These include some common scenarios with simple solutions.
6.1. Inline CSS
The @
variable statement, which evaluates, can act as inline if when in an attribute.
<div class="@{root ? 'green' : 'red'}"></div>
function anonymous(__r, root, __f) {
var __o = '';
__o += '<div class="' + (root ? 'green' : 'red') + '"></div>';
return __o;
}
<div class="green"></div>
6.2. If/Else
As there is no data-else
attribute, a test can be stored and used in multiple data-if
attributes.
@{var messageValid = root.message.length > 10;}
<div data-if="messageValid">Valid!</div>
<div data-if="!messageValid">Not valid...</div>
function anonymous(__r, root, __f) {
var __o = '';
var messageValid = root.message.length > 10;
__o += '\n<div>';
if (messageValid) {
__o += 'Valid!';
}
__o += '</div>\n<div>';
if (!messageValid) {
__o += 'Not valid...';
}
__o += '</div>';
return __o;
}
<div>Valid!</div>
<div></div>
7. Server API
The server API is available in a nodejs/expressjs environment.
gaikan(input, root, fragments = {}, callback = undefined, skipLayout = false)
Compiles a function for the input file, invokes it, and returns the output.
gaikan.compileFromFile(input)
Compiles a function for the input file.
gaikan.compileFromString(input)
Compiles a function for the input string.
7.1. Options
Available as gaikan.options
.
7.1.1. Caching
Enables or disables template caching. When enabled, a compiled template is not compiled again.
gaikan.options.enableCache = isProduction;
7.1.2. Compression
Enables or disables template compression. When enabled, compiled functions compress output.
gaikan.options.enableCompression = isProduction;
7.1.3. Directories
An array with relative directories in which to search templates. Usused for expressjs.
gaikan.options.directories = ['views', '.'];
Templates resolve from the root directory, which defaults to the main script directory.
gaikan.options.rootDir = path.dirname(require.main.filename);
7.1.4. Extensions
An array with allowed template file extensions. Usused for expressjs.
gaikan.options.extensions = ['gaikan', 'html'];
7.1.5. Layout
Changes output to be the content
fragment for the layout. Used when skipLayout
is false.
gaikan.options.layout = null;
8. Browser API
The browser API is available in a modern browser environment.
gaikan(input, root, fragments = {})
Not yet implemented.
gaikan.compileFromString(input)
Compiles a function for the input string.
8.1. Configuration
Available as gaikan.options
.
8.1.1. Compression
Enables or disables template compression. When enabled, compiled functions compress output.
gaikan.options.enableCompression = false;
9. Runtime
In both the nodejs/expressjs and modern browser environment, the template engine serves as runtime.
9.1 Alterant
Alterants are available as gaikan.alterant
. Adding functions makes them available in each template.
9.2 Set
Alterants are available as gaikan.set
. Adding functions makes them available in each template.
10. Security
Escape variables to prevent XSS, and do not allow user content as template.
11. Planned Future Additions
* Improve interactive testing environment (examples/interactive).
* Implement client-side template cache.
* Implement expressjs hook to run rendering on client.
* Implement designer prototyping tooling with auto-refresh and data stubbing.