-
Notifications
You must be signed in to change notification settings - Fork 16
/
whiskers.js
115 lines (102 loc) · 4.16 KB
/
whiskers.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// whiskers.js templating library
;(function(whiskers) {
// for compiled templates
whiskers.cache = {};
// main function
whiskers.render = function(template, context) {
// compile if not cached
if (!whiskers.cache[template]) {
whiskers.cache[template] = whiskers.compile(template);
}
return whiskers.cache[template](context);
};
// compile template to function
whiskers.compile = function(template) {
var stack = [], block, i, fn, safeIterVar;
// allow functions as partials
if (template instanceof Function) return template;
// convert to string, empty if false
template = (template || '') + '';
// escape backslashes, single quotes, and newlines
template = template.replace(/\\/g, '\\\\').replace(/\'/g, '\\\'').replace(/\n/g, '\\n').replace(/\r/g, '');
// replace comments (like {!foo!})
template = template.replace(/(\\*){![\s\S]*?!}/g, function(str, escapeChar) {
if (escapeChar) return str.replace('\\\\', '');
return '';
});
// replace tags
template = template.replace(/(\\*){(?:([\w_.\-@:]+)|>([\w_.\-@:]+)|for +([\w_\-@:]+) +in +([\w_.\-@:]+)|if +(not +|)([\w_.\-@:]+)|\/(for|if))}/g, function(str, escapeChar, key, partial, iterVar, forKey, ifNot, ifKey, closeStatement, offset, s) {
if (escapeChar) return str.replace('\\\\', '');
// {foo}
if (key) {
// {else}
if (key == 'else') {
block = stack[stack.length-1];
if (block && !block.elsed) {
block.elsed = true;
if (block.statement == 'if') return '\'}else{b+=\'';
if (block.statement == 'for') return '\'}if(!g(c,\''+block.forKey+'\')){b+=\'';
}
console.warn('extra {else} ignored');
return '';
}
// {anything.but.else}
return '\'+g(c,\''+key+'\')+\'';
}
// {>foo}
if (partial) return '\'+r(g(c,\''+partial+'\'),c)+\'';
// {for foo in bar}
if (forKey) {
safeIterVar = iterVar.replace('-', '__');
stack.push({statement:'for', forKey:forKey, iterVar:iterVar, safeIterVar:safeIterVar});
return '\';var __'+safeIterVar+'=g(c,\''+iterVar+'\');var '+safeIterVar+'A=g(c,\''+forKey+'\');for(var '+safeIterVar+'I=0;'+safeIterVar+'I<'+safeIterVar+'A.length;'+safeIterVar+'I++){c[\''+iterVar+'\']='+safeIterVar+'A['+safeIterVar+'I];b+=\'';
}
// {if foo} or {if not foo}
if (ifKey) {
stack.push({statement:'if'});
return '\';if('+(ifNot?'!':'')+'g(c,\''+ifKey+'\')){b+=\'';
}
// {/for} or {/if}
if (closeStatement) {
block = stack[stack.length-1];
if (block && block.statement == closeStatement) {
stack.pop();
return '\'}'+(block.statement == 'for' ? 'c[\''+block.iterVar+'\']=__'+block.safeIterVar+';' : '')+'b+=\'';
}
console.warn('extra {/'+closeStatement+'} ignored');
return '';
}
// not a valid tag, don't replace
return str;
});
// close extra fors and ifs
for (i=stack.length-1; i>-1; i--) {
block = stack[i];
console.warn('extra {'+block.statement+'} closed at end of template');
template = template+'\'}b+=\'';
}
// c is context, b is buffer
fn = new Function('g', 'r', 'return function(c){var b=\''+template+'\';return b}');
return fn(get, whiskers.render);
};
// get value with dot notation, e.g. get(obj, 'key.for.something')
function get(obj, key) {
var i, accessor = key.split('.'), empty = true;
for (i=0; i<accessor.length; i++) {
// empty string for key.that.does.not.exist
if (!obj) return '';
obj = obj[accessor[i]];
}
// empty string for every falsy value except 0
if (obj === undefined || obj === null || obj === false) return '';
// treat [] and {} as falsy also
if (obj instanceof Array && obj.length == 0) return '';
if (obj.constructor === Object) {
for (i in obj) if (obj.hasOwnProperty(i)) empty = !i;
if (empty) return '';
}
return obj;
}
// for Express
whiskers.__express = function() {try {return require('./__express')} catch (e) {}}();
}(typeof module == 'object' ? module.exports : window.whiskers = {}));