walrus-rb 0.10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Guardfile +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +45 -0
- data/Rakefile +17 -0
- data/lib/walrus.rb +7 -0
- data/lib/walrus/config.rb +29 -0
- data/lib/walrus/context.rb +42 -0
- data/lib/walrus/version.rb +3 -0
- data/spec/blocks_spec.rb +13 -0
- data/spec/collections_spec.rb +15 -0
- data/spec/context_spec.rb +13 -0
- data/spec/currencies_spec.rb +13 -0
- data/spec/dates_spec.rb +13 -0
- data/spec/domain_spec.rb +20 -0
- data/spec/filters_spec.rb +13 -0
- data/spec/inflections_spec.rb +13 -0
- data/spec/math_spec.rb +14 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/strings_spec.rb +11 -0
- data/spec/support/javascripts/walrus_domain_objects.js +22 -0
- data/vendor/walrus.collections.js +234 -0
- data/vendor/walrus.currencies.js +57 -0
- data/vendor/walrus.dates.js +296 -0
- data/vendor/walrus.inflections.js +120 -0
- data/vendor/walrus.js +1304 -0
- data/vendor/walrus.math.js +144 -0
- data/vendor/walrus.strings.js +127 -0
- data/walrus.gemspec +30 -0
- metadata +187 -0
data/vendor/walrus.js
ADDED
@@ -0,0 +1,1304 @@
|
|
1
|
+
|
2
|
+
/**
|
3
|
+
* walrus 0.10.1
|
4
|
+
* A bolder kind of mustache.
|
5
|
+
* (c) 2012 Jeremy Ruppel
|
6
|
+
* Released under the MIT license.
|
7
|
+
* For all details and documentation:
|
8
|
+
* https://github.com/jeremyruppel/walrus
|
9
|
+
*/
|
10
|
+
|
11
|
+
(function() {
|
12
|
+
var AST, Utils, Walrus,
|
13
|
+
__hasProp = Object.prototype.hasOwnProperty,
|
14
|
+
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; },
|
15
|
+
__slice = Array.prototype.slice;
|
16
|
+
|
17
|
+
Walrus = {
|
18
|
+
VERSION: '0.10.1'
|
19
|
+
/**
|
20
|
+
* This object will get mixed in to every object
|
21
|
+
* passed to a compiled template. This is a good
|
22
|
+
* place to put any methods you need to access
|
23
|
+
* in all of your templates.
|
24
|
+
*/
|
25
|
+
};
|
26
|
+
|
27
|
+
Walrus.Context = {};
|
28
|
+
|
29
|
+
|
30
|
+
/* Jison generated parser */
|
31
|
+
var walrus = (function(){
|
32
|
+
var parser = {trace: function trace() { },
|
33
|
+
yy: {},
|
34
|
+
symbols_: {"error":2,"document":3,"text":4,"EOF":5,"statements":6,"statement":7,"OPEN":8,"helper":9,"mustache":10,"block":11,"SAFE":12,"CLOSE":13,"CONTENT":14,"expression":15,"PIPE":16,"filters":17,"ATTR":18,"paths":19,"DOT":20,"primitive":21,"OPEN_BLOCK":22,"CLOSE_BLOCK":23,"HELP":24,"MEMBER":25,"filter":26,"OPEN_PAREN":27,"arguments":28,"CLOSE_PAREN":29,"path":30,"method":31,"member":32,"COMMA":33,"argument":34,"SINGLE_QUOTE_STRING_LITERAL":35,"DOUBLE_QUOTE_STRING_LITERAL":36,"BOOLEAN_FALSE":37,"BOOLEAN_TRUE":38,"NUMBER":39,"$accept":0,"$end":1},
|
35
|
+
terminals_: {2:"error",5:"EOF",8:"OPEN",12:"SAFE",13:"CLOSE",14:"CONTENT",16:"PIPE",18:"ATTR",20:"DOT",22:"OPEN_BLOCK",23:"CLOSE_BLOCK",24:"HELP",25:"MEMBER",27:"OPEN_PAREN",29:"CLOSE_PAREN",33:"COMMA",35:"SINGLE_QUOTE_STRING_LITERAL",36:"DOUBLE_QUOTE_STRING_LITERAL",37:"BOOLEAN_FALSE",38:"BOOLEAN_TRUE",39:"NUMBER"},
|
36
|
+
productions_: [0,[3,2],[3,1],[4,1],[6,2],[6,1],[7,4],[7,3],[7,3],[7,1],[10,3],[10,1],[15,2],[15,1],[15,1],[15,1],[11,4],[9,2],[17,2],[17,1],[26,5],[26,2],[19,3],[19,1],[30,1],[30,1],[31,4],[31,3],[28,3],[28,1],[34,1],[21,1],[21,1],[21,1],[21,1],[21,1],[32,1]],
|
37
|
+
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
|
38
|
+
|
39
|
+
var $0 = $$.length - 1;
|
40
|
+
switch (yystate) {
|
41
|
+
case 1: return new yy.DocumentNode( $$[$0-1] )
|
42
|
+
break;
|
43
|
+
case 2: return new yy.DocumentNode( [ ] )
|
44
|
+
break;
|
45
|
+
case 3: this.$ = $$[$0]
|
46
|
+
break;
|
47
|
+
case 4: $$[$0-1].push( $$[$0] ); this.$ = $$[$0-1]
|
48
|
+
break;
|
49
|
+
case 5: this.$ = [ $$[$0] ]
|
50
|
+
break;
|
51
|
+
case 6: this.$ = new yy.BlockNode( $$[$0-2], $$[$0-1], $$[$0] )
|
52
|
+
break;
|
53
|
+
case 7: this.$ = new yy.SafeNode( $$[$0-1] )
|
54
|
+
break;
|
55
|
+
case 8: this.$ = $$[$0-1]
|
56
|
+
break;
|
57
|
+
case 9: this.$ = new yy.ContentNode( $$[$0] )
|
58
|
+
break;
|
59
|
+
case 10: this.$ = new yy.ExpressionNode( $$[$0-2], new yy.FilterCollection( $$[$0] ) )
|
60
|
+
break;
|
61
|
+
case 11: this.$ = new yy.ExpressionNode( $$[$0], new yy.FilterCollection( [ ] ) )
|
62
|
+
break;
|
63
|
+
case 12: this.$ = new yy.PathNode( $$[$0], false )
|
64
|
+
break;
|
65
|
+
case 13: this.$ = new yy.PathNode( $$[$0], true )
|
66
|
+
break;
|
67
|
+
case 14: this.$ = new yy.ThisNode( )
|
68
|
+
break;
|
69
|
+
case 15: this.$ = $$[$0]
|
70
|
+
break;
|
71
|
+
case 16: this.$ = new yy.JoinedNodeCollection( $$[$0-2] )
|
72
|
+
break;
|
73
|
+
case 17: this.$ = $$[$0]
|
74
|
+
break;
|
75
|
+
case 18: $$[$0-1].push( $$[$0] ); this.$ = $$[$0-1]
|
76
|
+
break;
|
77
|
+
case 19: this.$ = [ $$[$0] ]
|
78
|
+
break;
|
79
|
+
case 20: this.$ = new yy.FilterNode( $$[$0-3], new yy.NodeCollection( $$[$0-1] ) )
|
80
|
+
break;
|
81
|
+
case 21: this.$ = new yy.FilterNode( $$[$0], new yy.NodeCollection( [ ] ) )
|
82
|
+
break;
|
83
|
+
case 22: $$[$0-2].push( $$[$0] ); this.$ = $$[$0-2]
|
84
|
+
break;
|
85
|
+
case 23: this.$ = [ $$[$0] ]
|
86
|
+
break;
|
87
|
+
case 24: this.$ = $$[$0]
|
88
|
+
break;
|
89
|
+
case 25: this.$ = $$[$0]
|
90
|
+
break;
|
91
|
+
case 26: this.$ = new yy.MethodNode( $$[$0-3], new yy.NodeCollection( $$[$0-1] ) )
|
92
|
+
break;
|
93
|
+
case 27: this.$ = new yy.MethodNode( $$[$0-2], new yy.NodeCollection( [ ] ) )
|
94
|
+
break;
|
95
|
+
case 28: $$[$0-2].push( $$[$0] ); this.$ = $$[$0-2]
|
96
|
+
break;
|
97
|
+
case 29: this.$ = [ $$[$0] ]
|
98
|
+
break;
|
99
|
+
case 30: this.$ = $$[$0]
|
100
|
+
break;
|
101
|
+
case 31: this.$ = new yy.PrimitiveNode( $$[$0] )
|
102
|
+
break;
|
103
|
+
case 32: this.$ = new yy.PrimitiveNode( $$[$0] )
|
104
|
+
break;
|
105
|
+
case 33: this.$ = new yy.PrimitiveNode( false )
|
106
|
+
break;
|
107
|
+
case 34: this.$ = new yy.PrimitiveNode( true )
|
108
|
+
break;
|
109
|
+
case 35: this.$ = new yy.PrimitiveNode( parseFloat( $$[$0] ) )
|
110
|
+
break;
|
111
|
+
case 36: this.$ = new yy.MemberNode( $$[$0], false )
|
112
|
+
break;
|
113
|
+
}
|
114
|
+
},
|
115
|
+
table: [{3:1,4:2,5:[1,3],6:4,7:5,8:[1,6],12:[1,7],14:[1,8]},{1:[3]},{5:[1,9]},{1:[2,2]},{5:[2,3],7:10,8:[1,6],12:[1,7],14:[1,8],23:[2,3]},{5:[2,5],8:[2,5],12:[2,5],14:[2,5],23:[2,5]},{9:11,10:12,15:14,18:[1,15],19:16,20:[1,17],21:18,24:[1,13],25:[1,27],30:19,31:25,32:26,35:[1,20],36:[1,21],37:[1,22],38:[1,23],39:[1,24]},{10:28,15:14,18:[1,15],19:16,20:[1,17],21:18,25:[1,27],30:19,31:25,32:26,35:[1,20],36:[1,21],37:[1,22],38:[1,23],39:[1,24]},{5:[2,9],8:[2,9],12:[2,9],14:[2,9],23:[2,9]},{1:[2,1]},{5:[2,4],8:[2,4],12:[2,4],14:[2,4],23:[2,4]},{10:29,15:14,18:[1,15],19:16,20:[1,17],21:18,25:[1,27],30:19,31:25,32:26,35:[1,20],36:[1,21],37:[1,22],38:[1,23],39:[1,24]},{13:[1,30]},{25:[1,31]},{13:[2,11],16:[1,32],22:[2,11]},{19:33,25:[1,27],30:19,31:25,32:26},{13:[2,13],16:[2,13],20:[1,34],22:[2,13],29:[2,13],33:[2,13]},{13:[2,14],16:[2,14],22:[2,14],29:[2,14],33:[2,14]},{13:[2,15],16:[2,15],22:[2,15],29:[2,15],33:[2,15]},{13:[2,23],16:[2,23],20:[2,23],22:[2,23],29:[2,23],33:[2,23]},{13:[2,31],16:[2,31],22:[2,31],29:[2,31],33:[2,31]},{13:[2,32],16:[2,32],22:[2,32],29:[2,32],33:[2,32]},{13:[2,33],16:[2,33],22:[2,33],29:[2,33],33:[2,33]},{13:[2,34],16:[2,34],22:[2,34],29:[2,34],33:[2,34]},{13:[2,35],16:[2,35],22:[2,35],29:[2,35],33:[2,35]},{13:[2,24],16:[2,24],20:[2,24],22:[2,24],29:[2,24],33:[2,24]},{13:[2,25],16:[2,25],20:[2,25],22:[2,25],29:[2,25],33:[2,25]},{13:[2,36],16:[2,36],20:[2,36],22:[2,36],27:[1,35],29:[2,36],33:[2,36]},{13:[1,36]},{11:37,22:[1,38]},{5:[2,8],8:[2,8],12:[2,8],14:[2,8],23:[2,8]},{18:[2,17],20:[2,17],25:[2,17],35:[2,17],36:[2,17],37:[2,17],38:[2,17],39:[2,17]},{17:39,24:[1,41],26:40},{13:[2,12],16:[2,12],20:[1,34],22:[2,12],29:[2,12],33:[2,12]},{25:[1,27],30:42,31:25,32:26},{15:46,18:[1,15],19:16,20:[1,17],21:18,25:[1,27],28:43,29:[1,44],30:19,31:25,32:26,34:45,35:[1,20],36:[1,21],37:[1,22],38:[1,23],39:[1,24]},{5:[2,7],8:[2,7],12:[2,7],14:[2,7],23:[2,7]},{5:[2,6],8:[2,6],12:[2,6],14:[2,6],23:[2,6]},{4:47,6:4,7:5,8:[1,6],12:[1,7],14:[1,8]},{13:[2,10],22:[2,10],24:[1,41],26:48},{13:[2,19],22:[2,19],24:[2,19]},{25:[1,49]},{13:[2,22],16:[2,22],20:[2,22],22:[2,22],29:[2,22],33:[2,22]},{29:[1,50],33:[1,51]},{13:[2,27],16:[2,27],20:[2,27],22:[2,27],29:[2,27],33:[2,27]},{29:[2,29],33:[2,29]},{29:[2,30],33:[2,30]},{23:[1,52]},{13:[2,18],22:[2,18],24:[2,18]},{13:[2,21],22:[2,21],24:[2,21],27:[1,53]},{13:[2,26],16:[2,26],20:[2,26],22:[2,26],29:[2,26],33:[2,26]},{15:46,18:[1,15],19:16,20:[1,17],21:18,25:[1,27],30:19,31:25,32:26,34:54,35:[1,20],36:[1,21],37:[1,22],38:[1,23],39:[1,24]},{13:[1,55]},{15:46,18:[1,15],19:16,20:[1,17],21:18,25:[1,27],28:56,30:19,31:25,32:26,34:45,35:[1,20],36:[1,21],37:[1,22],38:[1,23],39:[1,24]},{29:[2,28],33:[2,28]},{5:[2,16],8:[2,16],12:[2,16],14:[2,16],23:[2,16]},{29:[1,57],33:[1,51]},{13:[2,20],22:[2,20],24:[2,20]}],
|
116
|
+
defaultActions: {3:[2,2],9:[2,1]},
|
117
|
+
parseError: function parseError(str, hash) {
|
118
|
+
throw new Error(str);
|
119
|
+
},
|
120
|
+
parse: function parse(input) {
|
121
|
+
var self = this,
|
122
|
+
stack = [0],
|
123
|
+
vstack = [null], // semantic value stack
|
124
|
+
lstack = [], // location stack
|
125
|
+
table = this.table,
|
126
|
+
yytext = '',
|
127
|
+
yylineno = 0,
|
128
|
+
yyleng = 0,
|
129
|
+
recovering = 0,
|
130
|
+
TERROR = 2,
|
131
|
+
EOF = 1;
|
132
|
+
|
133
|
+
//this.reductionCount = this.shiftCount = 0;
|
134
|
+
|
135
|
+
this.lexer.setInput(input);
|
136
|
+
this.lexer.yy = this.yy;
|
137
|
+
this.yy.lexer = this.lexer;
|
138
|
+
if (typeof this.lexer.yylloc == 'undefined')
|
139
|
+
this.lexer.yylloc = {};
|
140
|
+
var yyloc = this.lexer.yylloc;
|
141
|
+
lstack.push(yyloc);
|
142
|
+
|
143
|
+
if (typeof this.yy.parseError === 'function')
|
144
|
+
this.parseError = this.yy.parseError;
|
145
|
+
|
146
|
+
function popStack (n) {
|
147
|
+
stack.length = stack.length - 2*n;
|
148
|
+
vstack.length = vstack.length - n;
|
149
|
+
lstack.length = lstack.length - n;
|
150
|
+
}
|
151
|
+
|
152
|
+
function lex() {
|
153
|
+
var token;
|
154
|
+
token = self.lexer.lex() || 1; // $end = 1
|
155
|
+
// if token isn't its numeric value, convert
|
156
|
+
if (typeof token !== 'number') {
|
157
|
+
token = self.symbols_[token] || token;
|
158
|
+
}
|
159
|
+
return token;
|
160
|
+
}
|
161
|
+
|
162
|
+
var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
|
163
|
+
while (true) {
|
164
|
+
// retreive state number from top of stack
|
165
|
+
state = stack[stack.length-1];
|
166
|
+
|
167
|
+
// use default actions if available
|
168
|
+
if (this.defaultActions[state]) {
|
169
|
+
action = this.defaultActions[state];
|
170
|
+
} else {
|
171
|
+
if (symbol == null)
|
172
|
+
symbol = lex();
|
173
|
+
// read action for current state and first input
|
174
|
+
action = table[state] && table[state][symbol];
|
175
|
+
}
|
176
|
+
|
177
|
+
// handle parse error
|
178
|
+
_handle_error:
|
179
|
+
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
180
|
+
|
181
|
+
if (!recovering) {
|
182
|
+
// Report error
|
183
|
+
expected = [];
|
184
|
+
for (p in table[state]) if (this.terminals_[p] && p > 2) {
|
185
|
+
expected.push("'"+this.terminals_[p]+"'");
|
186
|
+
}
|
187
|
+
var errStr = '';
|
188
|
+
if (this.lexer.showPosition) {
|
189
|
+
errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'";
|
190
|
+
} else {
|
191
|
+
errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
|
192
|
+
(symbol == 1 /*EOF*/ ? "end of input" :
|
193
|
+
("'"+(this.terminals_[symbol] || symbol)+"'"));
|
194
|
+
}
|
195
|
+
this.parseError(errStr,
|
196
|
+
{text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
|
197
|
+
}
|
198
|
+
|
199
|
+
// just recovered from another error
|
200
|
+
if (recovering == 3) {
|
201
|
+
if (symbol == EOF) {
|
202
|
+
throw new Error(errStr || 'Parsing halted.');
|
203
|
+
}
|
204
|
+
|
205
|
+
// discard current lookahead and grab another
|
206
|
+
yyleng = this.lexer.yyleng;
|
207
|
+
yytext = this.lexer.yytext;
|
208
|
+
yylineno = this.lexer.yylineno;
|
209
|
+
yyloc = this.lexer.yylloc;
|
210
|
+
symbol = lex();
|
211
|
+
}
|
212
|
+
|
213
|
+
// try to recover from error
|
214
|
+
while (1) {
|
215
|
+
// check for error recovery rule in this state
|
216
|
+
if ((TERROR.toString()) in table[state]) {
|
217
|
+
break;
|
218
|
+
}
|
219
|
+
if (state == 0) {
|
220
|
+
throw new Error(errStr || 'Parsing halted.');
|
221
|
+
}
|
222
|
+
popStack(1);
|
223
|
+
state = stack[stack.length-1];
|
224
|
+
}
|
225
|
+
|
226
|
+
preErrorSymbol = symbol; // save the lookahead token
|
227
|
+
symbol = TERROR; // insert generic error symbol as new lookahead
|
228
|
+
state = stack[stack.length-1];
|
229
|
+
action = table[state] && table[state][TERROR];
|
230
|
+
recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
|
231
|
+
}
|
232
|
+
|
233
|
+
// this shouldn't happen, unless resolve defaults are off
|
234
|
+
if (action[0] instanceof Array && action.length > 1) {
|
235
|
+
throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
|
236
|
+
}
|
237
|
+
|
238
|
+
switch (action[0]) {
|
239
|
+
|
240
|
+
case 1: // shift
|
241
|
+
//this.shiftCount++;
|
242
|
+
|
243
|
+
stack.push(symbol);
|
244
|
+
vstack.push(this.lexer.yytext);
|
245
|
+
lstack.push(this.lexer.yylloc);
|
246
|
+
stack.push(action[1]); // push state
|
247
|
+
symbol = null;
|
248
|
+
if (!preErrorSymbol) { // normal execution/no error
|
249
|
+
yyleng = this.lexer.yyleng;
|
250
|
+
yytext = this.lexer.yytext;
|
251
|
+
yylineno = this.lexer.yylineno;
|
252
|
+
yyloc = this.lexer.yylloc;
|
253
|
+
if (recovering > 0)
|
254
|
+
recovering--;
|
255
|
+
} else { // error just occurred, resume old lookahead f/ before error
|
256
|
+
symbol = preErrorSymbol;
|
257
|
+
preErrorSymbol = null;
|
258
|
+
}
|
259
|
+
break;
|
260
|
+
|
261
|
+
case 2: // reduce
|
262
|
+
//this.reductionCount++;
|
263
|
+
|
264
|
+
len = this.productions_[action[1]][1];
|
265
|
+
|
266
|
+
// perform semantic action
|
267
|
+
yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
|
268
|
+
// default location, uses first token for firsts, last for lasts
|
269
|
+
yyval._$ = {
|
270
|
+
first_line: lstack[lstack.length-(len||1)].first_line,
|
271
|
+
last_line: lstack[lstack.length-1].last_line,
|
272
|
+
first_column: lstack[lstack.length-(len||1)].first_column,
|
273
|
+
last_column: lstack[lstack.length-1].last_column
|
274
|
+
};
|
275
|
+
r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
|
276
|
+
|
277
|
+
if (typeof r !== 'undefined') {
|
278
|
+
return r;
|
279
|
+
}
|
280
|
+
|
281
|
+
// pop off stack
|
282
|
+
if (len) {
|
283
|
+
stack = stack.slice(0,-1*len*2);
|
284
|
+
vstack = vstack.slice(0, -1*len);
|
285
|
+
lstack = lstack.slice(0, -1*len);
|
286
|
+
}
|
287
|
+
|
288
|
+
stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce)
|
289
|
+
vstack.push(yyval.$);
|
290
|
+
lstack.push(yyval._$);
|
291
|
+
// goto new state = table[STATE][NONTERMINAL]
|
292
|
+
newState = table[stack[stack.length-2]][stack[stack.length-1]];
|
293
|
+
stack.push(newState);
|
294
|
+
break;
|
295
|
+
|
296
|
+
case 3: // accept
|
297
|
+
return true;
|
298
|
+
}
|
299
|
+
|
300
|
+
}
|
301
|
+
|
302
|
+
return true;
|
303
|
+
}};
|
304
|
+
/* Jison generated lexer */
|
305
|
+
var lexer = (function(){
|
306
|
+
var lexer = ({EOF:1,
|
307
|
+
parseError:function parseError(str, hash) {
|
308
|
+
if (this.yy.parseError) {
|
309
|
+
this.yy.parseError(str, hash);
|
310
|
+
} else {
|
311
|
+
throw new Error(str);
|
312
|
+
}
|
313
|
+
},
|
314
|
+
setInput:function (input) {
|
315
|
+
this._input = input;
|
316
|
+
this._more = this._less = this.done = false;
|
317
|
+
this.yylineno = this.yyleng = 0;
|
318
|
+
this.yytext = this.matched = this.match = '';
|
319
|
+
this.conditionStack = ['INITIAL'];
|
320
|
+
this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
|
321
|
+
return this;
|
322
|
+
},
|
323
|
+
input:function () {
|
324
|
+
var ch = this._input[0];
|
325
|
+
this.yytext+=ch;
|
326
|
+
this.yyleng++;
|
327
|
+
this.match+=ch;
|
328
|
+
this.matched+=ch;
|
329
|
+
var lines = ch.match(/\n/);
|
330
|
+
if (lines) this.yylineno++;
|
331
|
+
this._input = this._input.slice(1);
|
332
|
+
return ch;
|
333
|
+
},
|
334
|
+
unput:function (ch) {
|
335
|
+
this._input = ch + this._input;
|
336
|
+
return this;
|
337
|
+
},
|
338
|
+
more:function () {
|
339
|
+
this._more = true;
|
340
|
+
return this;
|
341
|
+
},
|
342
|
+
pastInput:function () {
|
343
|
+
var past = this.matched.substr(0, this.matched.length - this.match.length);
|
344
|
+
return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
|
345
|
+
},
|
346
|
+
upcomingInput:function () {
|
347
|
+
var next = this.match;
|
348
|
+
if (next.length < 20) {
|
349
|
+
next += this._input.substr(0, 20-next.length);
|
350
|
+
}
|
351
|
+
return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
|
352
|
+
},
|
353
|
+
showPosition:function () {
|
354
|
+
var pre = this.pastInput();
|
355
|
+
var c = new Array(pre.length + 1).join("-");
|
356
|
+
return pre + this.upcomingInput() + "\n" + c+"^";
|
357
|
+
},
|
358
|
+
next:function () {
|
359
|
+
if (this.done) {
|
360
|
+
return this.EOF;
|
361
|
+
}
|
362
|
+
if (!this._input) this.done = true;
|
363
|
+
|
364
|
+
var token,
|
365
|
+
match,
|
366
|
+
tempMatch,
|
367
|
+
index,
|
368
|
+
col,
|
369
|
+
lines;
|
370
|
+
if (!this._more) {
|
371
|
+
this.yytext = '';
|
372
|
+
this.match = '';
|
373
|
+
}
|
374
|
+
var rules = this._currentRules();
|
375
|
+
for (var i=0;i < rules.length; i++) {
|
376
|
+
tempMatch = this._input.match(this.rules[rules[i]]);
|
377
|
+
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
|
378
|
+
match = tempMatch;
|
379
|
+
index = i;
|
380
|
+
if (!this.options.flex) break;
|
381
|
+
}
|
382
|
+
}
|
383
|
+
if (match) {
|
384
|
+
lines = match[0].match(/\n.*/g);
|
385
|
+
if (lines) this.yylineno += lines.length;
|
386
|
+
this.yylloc = {first_line: this.yylloc.last_line,
|
387
|
+
last_line: this.yylineno+1,
|
388
|
+
first_column: this.yylloc.last_column,
|
389
|
+
last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
|
390
|
+
this.yytext += match[0];
|
391
|
+
this.match += match[0];
|
392
|
+
this.yyleng = this.yytext.length;
|
393
|
+
this._more = false;
|
394
|
+
this._input = this._input.slice(match[0].length);
|
395
|
+
this.matched += match[0];
|
396
|
+
token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
|
397
|
+
if (token) return token;
|
398
|
+
else return;
|
399
|
+
}
|
400
|
+
if (this._input === "") {
|
401
|
+
return this.EOF;
|
402
|
+
} else {
|
403
|
+
this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
|
404
|
+
{text: "", token: null, line: this.yylineno});
|
405
|
+
}
|
406
|
+
},
|
407
|
+
lex:function lex() {
|
408
|
+
var r = this.next();
|
409
|
+
if (typeof r !== 'undefined') {
|
410
|
+
return r;
|
411
|
+
} else {
|
412
|
+
return this.lex();
|
413
|
+
}
|
414
|
+
},
|
415
|
+
begin:function begin(condition) {
|
416
|
+
this.conditionStack.push(condition);
|
417
|
+
},
|
418
|
+
popState:function popState() {
|
419
|
+
return this.conditionStack.pop();
|
420
|
+
},
|
421
|
+
_currentRules:function _currentRules() {
|
422
|
+
return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
|
423
|
+
},
|
424
|
+
topState:function () {
|
425
|
+
return this.conditionStack[this.conditionStack.length-2];
|
426
|
+
},
|
427
|
+
pushState:function begin(condition) {
|
428
|
+
this.begin(condition);
|
429
|
+
}});
|
430
|
+
lexer.options = {};
|
431
|
+
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
|
432
|
+
|
433
|
+
var YYSTATE=YY_START
|
434
|
+
switch($avoiding_name_collisions) {
|
435
|
+
case 0: this.begin( 'mu' ); if( yy_.yytext ) return 14;
|
436
|
+
break;
|
437
|
+
case 1: return 14;
|
438
|
+
break;
|
439
|
+
case 2: this.begin( 'INITIAL' ); return 22;
|
440
|
+
break;
|
441
|
+
case 3: return 23;
|
442
|
+
break;
|
443
|
+
case 4: return 12;
|
444
|
+
break;
|
445
|
+
case 5: return 8;
|
446
|
+
break;
|
447
|
+
case 6: this.begin( 'INITIAL' ); return 13;
|
448
|
+
break;
|
449
|
+
case 7: return 18;
|
450
|
+
break;
|
451
|
+
case 8: return 24;
|
452
|
+
break;
|
453
|
+
case 9: return 16;
|
454
|
+
break;
|
455
|
+
case 10: return 20;
|
456
|
+
break;
|
457
|
+
case 11: return 33;
|
458
|
+
break;
|
459
|
+
case 12: /* ignore whitespace */
|
460
|
+
break;
|
461
|
+
case 13: yy_.yytext = yy_.yytext.substr( 1, yy_.yyleng - 2 ); return 35;
|
462
|
+
break;
|
463
|
+
case 14: yy_.yytext = yy_.yytext.substr( 1, yy_.yyleng - 2 ); return 36;
|
464
|
+
break;
|
465
|
+
case 15: return 27;
|
466
|
+
break;
|
467
|
+
case 16: return 29;
|
468
|
+
break;
|
469
|
+
case 17: return 38;
|
470
|
+
break;
|
471
|
+
case 18: return 37;
|
472
|
+
break;
|
473
|
+
case 19: return 39;
|
474
|
+
break;
|
475
|
+
case 20: return 25;
|
476
|
+
break;
|
477
|
+
case 21: return 5;
|
478
|
+
break;
|
479
|
+
}
|
480
|
+
};
|
481
|
+
lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^do\s*\}\}\n*/,/^\{\{\s*end\b/,/^\{\{=/,/^\{\{/,/^\}\}\n*/,/^@/,/^:/,/^\|/,/^\./,/^,/,/^\s+/,/^'[^\']*?'/,/^"[^\"]*?"/,/^\(/,/^\)/,/^true\b/,/^false\b/,/^-?\d+(\.\d+)?/,/^[a-zA-Z0-9\_\$]+/,/^$/];
|
482
|
+
lexer.conditions = {"mu":{"rules":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21],"inclusive":false},"INITIAL":{"rules":[0,1,21],"inclusive":true}};
|
483
|
+
return lexer;})()
|
484
|
+
parser.lexer = lexer;
|
485
|
+
return parser;
|
486
|
+
})();
|
487
|
+
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
|
488
|
+
exports.parser = walrus;
|
489
|
+
exports.parse = function () { return walrus.parse.apply(walrus, arguments); }
|
490
|
+
exports.main = function commonjsMain(args) {
|
491
|
+
if (!args[1])
|
492
|
+
throw new Error('Usage: '+args[0]+' FILE');
|
493
|
+
if (typeof process !== 'undefined') {
|
494
|
+
var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
|
495
|
+
} else {
|
496
|
+
var cwd = require("file").path(require("file").cwd());
|
497
|
+
var source = cwd.join(args[1]).read({charset: "utf-8"});
|
498
|
+
}
|
499
|
+
return exports.parser.parse(source);
|
500
|
+
}
|
501
|
+
if (typeof module !== 'undefined' && require.main === module) {
|
502
|
+
exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
|
503
|
+
}
|
504
|
+
}
|
505
|
+
;
|
506
|
+
|
507
|
+
Walrus.i18n = {
|
508
|
+
/**
|
509
|
+
* the current locale key
|
510
|
+
*/
|
511
|
+
locale: 'en',
|
512
|
+
/**
|
513
|
+
* looks up the translation at `keypath` for the current locale,
|
514
|
+
* optionally interpolating values from `context`
|
515
|
+
*/
|
516
|
+
t: function(keypath, context) {
|
517
|
+
var value;
|
518
|
+
if (context == null) context = {};
|
519
|
+
try {
|
520
|
+
value = Walrus.Utils.keypath(keypath, Walrus.i18n[Walrus.i18n.locale]);
|
521
|
+
if (typeof value === 'string') {
|
522
|
+
return Walrus.Utils.interpolate(value, context);
|
523
|
+
} else {
|
524
|
+
return value;
|
525
|
+
}
|
526
|
+
} catch (error) {
|
527
|
+
throw new Error("Missing translation: " + Walrus.i18n.locale + "." + keypath);
|
528
|
+
}
|
529
|
+
},
|
530
|
+
/**
|
531
|
+
* default en translations
|
532
|
+
* TODO this hash should probably be opt-in, either explicitly
|
533
|
+
* or by breaking it up and including each portion with its filter bundle
|
534
|
+
*/
|
535
|
+
en: {
|
536
|
+
currencies: {
|
537
|
+
symbol: '$',
|
538
|
+
precision: 2,
|
539
|
+
decimal: '.',
|
540
|
+
thousand: ','
|
541
|
+
},
|
542
|
+
dates: {
|
543
|
+
full_daynames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
544
|
+
abbr_daynames: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
545
|
+
full_monthnames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
546
|
+
abbr_monthnames: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
547
|
+
am: 'AM',
|
548
|
+
pm: 'PM',
|
549
|
+
distance_in_words: {
|
550
|
+
half_a_minute: 'half a minute',
|
551
|
+
less_than_x_seconds: {
|
552
|
+
one: 'less than a second',
|
553
|
+
other: 'less than %{count} seconds'
|
554
|
+
},
|
555
|
+
less_than_x_minutes: {
|
556
|
+
one: 'less than a minute',
|
557
|
+
other: 'less than %{count} minutes'
|
558
|
+
},
|
559
|
+
x_minutes: {
|
560
|
+
one: '1 minute',
|
561
|
+
other: '%{count} minutes'
|
562
|
+
},
|
563
|
+
about_x_hours: {
|
564
|
+
one: 'about 1 hour',
|
565
|
+
other: 'about %{count} hours'
|
566
|
+
},
|
567
|
+
x_days: {
|
568
|
+
one: '1 day',
|
569
|
+
other: '%{count} days'
|
570
|
+
},
|
571
|
+
about_x_months: {
|
572
|
+
one: 'about 1 month',
|
573
|
+
other: 'about %{count} months'
|
574
|
+
},
|
575
|
+
x_months: {
|
576
|
+
one: '1 month',
|
577
|
+
other: '%{count} months'
|
578
|
+
},
|
579
|
+
about_x_years: {
|
580
|
+
one: 'about 1 year',
|
581
|
+
other: 'about %{count} years'
|
582
|
+
},
|
583
|
+
over_x_years: {
|
584
|
+
one: 'over 1 year',
|
585
|
+
other: 'over %{count} years'
|
586
|
+
},
|
587
|
+
almost_x_years: {
|
588
|
+
one: 'almost 1 year',
|
589
|
+
other: 'almost %{count} years'
|
590
|
+
}
|
591
|
+
}
|
592
|
+
}
|
593
|
+
}
|
594
|
+
};
|
595
|
+
|
596
|
+
/**
|
597
|
+
* Allow Walrus.Context to use the `t` method
|
598
|
+
*/
|
599
|
+
|
600
|
+
Walrus.Context.t = Walrus.i18n.t;
|
601
|
+
|
602
|
+
Utils = {
|
603
|
+
/**
|
604
|
+
* trims leading and trailing whitespace
|
605
|
+
*/
|
606
|
+
trim: function(str) {
|
607
|
+
return str.replace(/^\s+|\s+$/g, '');
|
608
|
+
},
|
609
|
+
/**
|
610
|
+
* reduces a list into a single result
|
611
|
+
*/
|
612
|
+
reduce: function(array, initial, method) {
|
613
|
+
var item, memo, _i, _len;
|
614
|
+
memo = initial;
|
615
|
+
for (_i = 0, _len = array.length; _i < _len; _i++) {
|
616
|
+
item = array[_i];
|
617
|
+
memo = method(memo, item);
|
618
|
+
}
|
619
|
+
return memo;
|
620
|
+
},
|
621
|
+
/**
|
622
|
+
* returns the string representation of `foo`
|
623
|
+
*/
|
624
|
+
toString: function(foo) {
|
625
|
+
return Object.prototype.toString.call(foo);
|
626
|
+
},
|
627
|
+
/**
|
628
|
+
* returns whether or not `foo` is an array
|
629
|
+
*/
|
630
|
+
isArray: function(foo) {
|
631
|
+
return this.toString(foo) === '[object Array]';
|
632
|
+
},
|
633
|
+
/**
|
634
|
+
* applies all properties from `bar` onto `foo`
|
635
|
+
*/
|
636
|
+
extend: function(foo, bar) {
|
637
|
+
var key, value, _results;
|
638
|
+
_results = [];
|
639
|
+
for (key in bar) {
|
640
|
+
value = bar[key];
|
641
|
+
_results.push(foo[key] = value);
|
642
|
+
}
|
643
|
+
return _results;
|
644
|
+
},
|
645
|
+
/**
|
646
|
+
* all of the nasty html characters to escape
|
647
|
+
*/
|
648
|
+
escapees: /[&'"<>]/g,
|
649
|
+
escapes: {
|
650
|
+
'&': '&',
|
651
|
+
"'": ''',
|
652
|
+
'"': '"',
|
653
|
+
'<': '<',
|
654
|
+
'>': '>'
|
655
|
+
},
|
656
|
+
/**
|
657
|
+
* escapes nasty html characters
|
658
|
+
*/
|
659
|
+
escape: function(value) {
|
660
|
+
var _this = this;
|
661
|
+
if ((value != null) && (value.replace != null)) {
|
662
|
+
return value.replace(this.escapees, function(c) {
|
663
|
+
return _this.escapes[c] || c;
|
664
|
+
});
|
665
|
+
} else {
|
666
|
+
return value;
|
667
|
+
}
|
668
|
+
},
|
669
|
+
/**
|
670
|
+
* looks up a nested property on an object
|
671
|
+
*/
|
672
|
+
keypath: function(path, object) {
|
673
|
+
return this.reduce(path.split('.'), object, function(memo, key) {
|
674
|
+
return memo[key];
|
675
|
+
});
|
676
|
+
},
|
677
|
+
/**
|
678
|
+
* inserts values into a string
|
679
|
+
*/
|
680
|
+
interpolate: function(string, context) {
|
681
|
+
if (context == null) context = {};
|
682
|
+
return string.replace(/%{(\w+)}/g, function(match, key) {
|
683
|
+
return context[key] || match;
|
684
|
+
});
|
685
|
+
}
|
686
|
+
};
|
687
|
+
|
688
|
+
Walrus.Utils = Utils;
|
689
|
+
|
690
|
+
AST = {};
|
691
|
+
|
692
|
+
/**
|
693
|
+
* AST.SafeNode
|
694
|
+
* A simple wrapper node to signify safe compilation for the rest of
|
695
|
+
* the nodes in the tree.
|
696
|
+
*/
|
697
|
+
|
698
|
+
AST.SafeNode = (function() {
|
699
|
+
|
700
|
+
function SafeNode(node) {
|
701
|
+
this.node = node;
|
702
|
+
}
|
703
|
+
|
704
|
+
SafeNode.prototype.compile = function(context, root, safe) {
|
705
|
+
return this.node.compile(context, root, true);
|
706
|
+
};
|
707
|
+
|
708
|
+
return SafeNode;
|
709
|
+
|
710
|
+
})();
|
711
|
+
|
712
|
+
/**
|
713
|
+
* AST.NodeCollection
|
714
|
+
* A collection of nodes with the #compile interface, simply compiles
|
715
|
+
* each of its nodes and returns the resulting array.
|
716
|
+
*/
|
717
|
+
|
718
|
+
AST.NodeCollection = (function() {
|
719
|
+
|
720
|
+
function NodeCollection(nodes) {
|
721
|
+
this.nodes = nodes;
|
722
|
+
}
|
723
|
+
|
724
|
+
NodeCollection.prototype.compile = function(context, root, safe) {
|
725
|
+
var node, _i, _len, _ref, _results;
|
726
|
+
_ref = this.nodes;
|
727
|
+
_results = [];
|
728
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
729
|
+
node = _ref[_i];
|
730
|
+
_results.push(node.compile(context, root, safe));
|
731
|
+
}
|
732
|
+
return _results;
|
733
|
+
};
|
734
|
+
|
735
|
+
return NodeCollection;
|
736
|
+
|
737
|
+
})();
|
738
|
+
|
739
|
+
/**
|
740
|
+
* AST.JoinedNodeCollection
|
741
|
+
* Compiles all of its nodes, then joins them.
|
742
|
+
*/
|
743
|
+
|
744
|
+
AST.JoinedNodeCollection = (function(_super) {
|
745
|
+
|
746
|
+
__extends(JoinedNodeCollection, _super);
|
747
|
+
|
748
|
+
function JoinedNodeCollection(nodes) {
|
749
|
+
this.nodes = nodes;
|
750
|
+
}
|
751
|
+
|
752
|
+
JoinedNodeCollection.prototype.compile = function(context, root, safe) {
|
753
|
+
return JoinedNodeCollection.__super__.compile.call(this, context, root, safe).join('');
|
754
|
+
};
|
755
|
+
|
756
|
+
return JoinedNodeCollection;
|
757
|
+
|
758
|
+
})(AST.NodeCollection);
|
759
|
+
|
760
|
+
/**
|
761
|
+
* AST.DocumentNode
|
762
|
+
* The root node of the document. Defaults to escaping its content.
|
763
|
+
*/
|
764
|
+
|
765
|
+
AST.DocumentNode = (function(_super) {
|
766
|
+
|
767
|
+
__extends(DocumentNode, _super);
|
768
|
+
|
769
|
+
function DocumentNode() {
|
770
|
+
DocumentNode.__super__.constructor.apply(this, arguments);
|
771
|
+
}
|
772
|
+
|
773
|
+
DocumentNode.prototype.compile = function(context) {
|
774
|
+
Walrus.Utils.extend(context, Walrus.Context);
|
775
|
+
return DocumentNode.__super__.compile.call(this, context, context, false);
|
776
|
+
};
|
777
|
+
|
778
|
+
return DocumentNode;
|
779
|
+
|
780
|
+
})(AST.JoinedNodeCollection);
|
781
|
+
|
782
|
+
/**
|
783
|
+
* AST.ContentNode
|
784
|
+
* A node with non-mustache content, probably HTML. We simply pass the content through.
|
785
|
+
*/
|
786
|
+
|
787
|
+
AST.ContentNode = (function() {
|
788
|
+
|
789
|
+
function ContentNode(content) {
|
790
|
+
this.content = content;
|
791
|
+
}
|
792
|
+
|
793
|
+
ContentNode.prototype.compile = function(context, root, safe) {
|
794
|
+
return this.content;
|
795
|
+
};
|
796
|
+
|
797
|
+
return ContentNode;
|
798
|
+
|
799
|
+
})();
|
800
|
+
|
801
|
+
/**
|
802
|
+
* AST.MemberNode
|
803
|
+
* A node that refers to a member of the context. We don't explicitly check that this
|
804
|
+
* member exists in case the developer wants to check that in a conditional.
|
805
|
+
*
|
806
|
+
* `{{member}}`, for instance, will compile to `index[ 'member' ]`.
|
807
|
+
*/
|
808
|
+
|
809
|
+
AST.MemberNode = (function() {
|
810
|
+
|
811
|
+
function MemberNode(path) {
|
812
|
+
this.path = path;
|
813
|
+
}
|
814
|
+
|
815
|
+
MemberNode.prototype.compile = function(index, context, root, safe) {
|
816
|
+
if (safe) {
|
817
|
+
return index[this.path];
|
818
|
+
} else {
|
819
|
+
return Walrus.Utils.escape(index[this.path]);
|
820
|
+
}
|
821
|
+
};
|
822
|
+
|
823
|
+
return MemberNode;
|
824
|
+
|
825
|
+
})();
|
826
|
+
|
827
|
+
/**
|
828
|
+
* AST.MethodNode
|
829
|
+
* A node that refers to a member of the context, specifically a method, and will
|
830
|
+
* call that method with any given arguments after they compile. We explicitly
|
831
|
+
* check that the method exists to avoid 'foo is not a function' or other cryptic
|
832
|
+
* errors.
|
833
|
+
*
|
834
|
+
* `{{member( )}}`, for instance, will compile to `index[ 'member' ]( )`.
|
835
|
+
*/
|
836
|
+
|
837
|
+
AST.MethodNode = (function() {
|
838
|
+
|
839
|
+
function MethodNode(path, _arguments) {
|
840
|
+
this.path = path;
|
841
|
+
this.arguments = _arguments;
|
842
|
+
}
|
843
|
+
|
844
|
+
MethodNode.prototype.compile = function(index, context, root, safe) {
|
845
|
+
if (index[this.path] == null) {
|
846
|
+
throw new Error("Cannot find any method named '" + this.path + "' in " + index + ".");
|
847
|
+
}
|
848
|
+
return index[this.path].apply(index, this.arguments.compile(context, root, safe));
|
849
|
+
};
|
850
|
+
|
851
|
+
return MethodNode;
|
852
|
+
|
853
|
+
})();
|
854
|
+
|
855
|
+
/**
|
856
|
+
* AST.ThisNode
|
857
|
+
* A node that simply returns the current context to be evaluated. This is most useful
|
858
|
+
* during implicit iteration and is denoted with a dot, like `{{.}}`.
|
859
|
+
*
|
860
|
+
* One example is when you've got an array of primitive types, like strings:
|
861
|
+
*
|
862
|
+
* var names = [ 'Antonio', 'Ben', 'Curtis' ];
|
863
|
+
*
|
864
|
+
* {{:each names do}}
|
865
|
+
* <li>{{.}}</li>
|
866
|
+
* {{end}}
|
867
|
+
*
|
868
|
+
* A similar use case is when the root view object is an array:
|
869
|
+
*
|
870
|
+
* var view = [ { name : 'Antonio' }, { name : 'Ben' }, { name : 'Curtis' } ];
|
871
|
+
*
|
872
|
+
* {{:each . do}}
|
873
|
+
* <li>{{name}}</li>
|
874
|
+
* {{end}}
|
875
|
+
*/
|
876
|
+
|
877
|
+
AST.ThisNode = (function() {
|
878
|
+
|
879
|
+
function ThisNode() {}
|
880
|
+
|
881
|
+
ThisNode.prototype.compile = function(context, root, safe) {
|
882
|
+
return context;
|
883
|
+
};
|
884
|
+
|
885
|
+
return ThisNode;
|
886
|
+
|
887
|
+
})();
|
888
|
+
|
889
|
+
/**
|
890
|
+
* AST.PathNode
|
891
|
+
* A node that represents an object path. The path segments are typically
|
892
|
+
* `AST.MemberNode`s and `AST.MethodNode`s.
|
893
|
+
*
|
894
|
+
* A `PathNode` may be evaluated in two contexts: the current "local" context
|
895
|
+
* and the "root" context. For example, `{{foo.bar.baz}}` will try to evaluate
|
896
|
+
* the object path from the current context, while `{{@foo.bar.baz}}` will
|
897
|
+
* start back up at the root view object.
|
898
|
+
*/
|
899
|
+
|
900
|
+
AST.PathNode = (function() {
|
901
|
+
|
902
|
+
function PathNode(paths, local) {
|
903
|
+
this.paths = paths;
|
904
|
+
this.local = local;
|
905
|
+
}
|
906
|
+
|
907
|
+
PathNode.prototype.compile = function(context, root, safe) {
|
908
|
+
var scope;
|
909
|
+
scope = this.local ? context : root;
|
910
|
+
return Walrus.Utils.reduce(this.paths, scope, function(scope, path) {
|
911
|
+
return path.compile(scope, context, root, safe);
|
912
|
+
});
|
913
|
+
};
|
914
|
+
|
915
|
+
return PathNode;
|
916
|
+
|
917
|
+
})();
|
918
|
+
|
919
|
+
/**
|
920
|
+
* AST.PrimitiveNode
|
921
|
+
* A node whose value is a javascript primitive or literal. Supported
|
922
|
+
* primitives are:
|
923
|
+
*
|
924
|
+
* - Strings with "double quotes"
|
925
|
+
* - Strings with 'single quotes'
|
926
|
+
* - Numbers, like 45 or 987.654
|
927
|
+
* - Booleans, true or false
|
928
|
+
*
|
929
|
+
* TODO These primitives are currently parsed in the lexing phase. I'm
|
930
|
+
* kinda feeling like a bit of that logic should be moved to the AST instead.
|
931
|
+
* Perhaps declare the literal type in the lexer and determine the conversion
|
932
|
+
* here, or create distinct classes for each primitive type, like `BooleanNode`
|
933
|
+
* and `NumberNode`.
|
934
|
+
*/
|
935
|
+
|
936
|
+
AST.PrimitiveNode = (function() {
|
937
|
+
|
938
|
+
function PrimitiveNode(value) {
|
939
|
+
this.value = value;
|
940
|
+
}
|
941
|
+
|
942
|
+
PrimitiveNode.prototype.compile = function(context, root, safe) {
|
943
|
+
return this.value;
|
944
|
+
};
|
945
|
+
|
946
|
+
return PrimitiveNode;
|
947
|
+
|
948
|
+
})();
|
949
|
+
|
950
|
+
/**
|
951
|
+
* AST.ExpressionNode
|
952
|
+
* An expression is the main part of a mustache. It typically consists of a path,
|
953
|
+
* which gets compiled, then passed to any filters to be manipulated further.
|
954
|
+
* The subject of an expression may also be a primitive.
|
955
|
+
*
|
956
|
+
* The breakdown of a single-line mustache is:
|
957
|
+
*
|
958
|
+
* {{ expression | :filter(s) }}
|
959
|
+
*
|
960
|
+
* And a block mustache with a helper, like:
|
961
|
+
*
|
962
|
+
* {{ :helper expression | :filters(s) do }}
|
963
|
+
* // block content
|
964
|
+
* {{ end }}
|
965
|
+
*/
|
966
|
+
|
967
|
+
AST.ExpressionNode = (function() {
|
968
|
+
|
969
|
+
function ExpressionNode(paths, filters) {
|
970
|
+
this.paths = paths;
|
971
|
+
this.filters = filters;
|
972
|
+
}
|
973
|
+
|
974
|
+
ExpressionNode.prototype.compile = function(context, root, safe) {
|
975
|
+
return this.filters.apply(this.paths.compile(context, root, safe), context, root, safe);
|
976
|
+
};
|
977
|
+
|
978
|
+
return ExpressionNode;
|
979
|
+
|
980
|
+
})();
|
981
|
+
|
982
|
+
/**
|
983
|
+
* AST.BlockNode
|
984
|
+
* A node that contains other statements within a block. `BlockNode`s are denoted
|
985
|
+
* by the use of a _:helper_, and the presence of `do`/`end` to signify the start
|
986
|
+
* and end of the node's block. It is the helper's responsibility to determine
|
987
|
+
* how to compile the block.
|
988
|
+
*
|
989
|
+
* Will throw an error if the named helper is not defined in `Walrus.Helpers`.
|
990
|
+
*/
|
991
|
+
|
992
|
+
AST.BlockNode = (function() {
|
993
|
+
|
994
|
+
function BlockNode(name, expression, block) {
|
995
|
+
this.name = name;
|
996
|
+
this.expression = expression;
|
997
|
+
this.block = block;
|
998
|
+
if (Walrus.Helpers[this.name] == null) {
|
999
|
+
throw new Error("Cannot find any helper named '" + this.name + "'.");
|
1000
|
+
}
|
1001
|
+
}
|
1002
|
+
|
1003
|
+
BlockNode.prototype.compile = function(context, root, safe) {
|
1004
|
+
return Walrus.Helpers[this.name](this.expression, context, root, safe, this.block);
|
1005
|
+
};
|
1006
|
+
|
1007
|
+
return BlockNode;
|
1008
|
+
|
1009
|
+
})();
|
1010
|
+
|
1011
|
+
/**
|
1012
|
+
* AST.FilterNode
|
1013
|
+
* A node that specifies a _filter_ used to post-process the result of the expression.
|
1014
|
+
* Filters may also accept arguments, just like methods. These arguments are determined
|
1015
|
+
* and handled by the filter itself.
|
1016
|
+
*
|
1017
|
+
* Will throw an error if the named filter is not defined in `Walrus.Filters`.
|
1018
|
+
*/
|
1019
|
+
|
1020
|
+
AST.FilterNode = (function() {
|
1021
|
+
|
1022
|
+
function FilterNode(name, _arguments) {
|
1023
|
+
this.name = name;
|
1024
|
+
this.arguments = _arguments;
|
1025
|
+
if (Walrus.Filters[this.name] == null) {
|
1026
|
+
throw new Error("Cannot find any filter named '" + this.name + "'.");
|
1027
|
+
}
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
FilterNode.prototype.apply = function(value, context, root, safe) {
|
1031
|
+
var _ref;
|
1032
|
+
return (_ref = Walrus.Filters)[this.name].apply(_ref, [value].concat(__slice.call(this.arguments.compile(context, root, safe))));
|
1033
|
+
};
|
1034
|
+
|
1035
|
+
return FilterNode;
|
1036
|
+
|
1037
|
+
})();
|
1038
|
+
|
1039
|
+
/**
|
1040
|
+
* AST.FilterCollection
|
1041
|
+
* A collection of filters. Filters are applied to the expression
|
1042
|
+
* in order from left to right.
|
1043
|
+
*/
|
1044
|
+
|
1045
|
+
AST.FilterCollection = (function() {
|
1046
|
+
|
1047
|
+
function FilterCollection(filters) {
|
1048
|
+
this.filters = filters;
|
1049
|
+
}
|
1050
|
+
|
1051
|
+
FilterCollection.prototype.apply = function(expression, context, root, safe) {
|
1052
|
+
return Walrus.Utils.reduce(this.filters, expression, function(memo, filter) {
|
1053
|
+
return filter.apply(memo, context, root, safe);
|
1054
|
+
});
|
1055
|
+
};
|
1056
|
+
|
1057
|
+
return FilterCollection;
|
1058
|
+
|
1059
|
+
})();
|
1060
|
+
|
1061
|
+
Walrus.AST = AST;
|
1062
|
+
|
1063
|
+
Walrus.Helpers = {};
|
1064
|
+
|
1065
|
+
Walrus.addHelper = function(name, fn) {
|
1066
|
+
if (this.Helpers[name] != null) {
|
1067
|
+
throw "Cannot override existing helper named '" + name + "'.";
|
1068
|
+
}
|
1069
|
+
return this.Helpers[name] = fn;
|
1070
|
+
};
|
1071
|
+
|
1072
|
+
/**
|
1073
|
+
* *:if*
|
1074
|
+
* Evaluates a block only if the result of `expression` is truthy. Opposite of `:unless`.
|
1075
|
+
*
|
1076
|
+
* Usage:
|
1077
|
+
*
|
1078
|
+
* {{:if expression do}}
|
1079
|
+
* // block content
|
1080
|
+
* {{end}}
|
1081
|
+
*/
|
1082
|
+
|
1083
|
+
Walrus.addHelper('if', function(expression, context, root, safe, block) {
|
1084
|
+
if (expression.compile(context, root, safe)) {
|
1085
|
+
return block.compile(context, root, safe);
|
1086
|
+
} else {
|
1087
|
+
return '';
|
1088
|
+
}
|
1089
|
+
});
|
1090
|
+
|
1091
|
+
/**
|
1092
|
+
* *:unless*
|
1093
|
+
* Evaluates a block only if the result of `expression` is falsy. Opposite of `:if`.
|
1094
|
+
*
|
1095
|
+
* Usage:
|
1096
|
+
*
|
1097
|
+
* {{:unless expression do}}
|
1098
|
+
* // block content
|
1099
|
+
* {{end}}
|
1100
|
+
*/
|
1101
|
+
|
1102
|
+
Walrus.addHelper('unless', function(expression, context, root, safe, block) {
|
1103
|
+
if (!expression.compile(context, root, safe)) {
|
1104
|
+
return block.compile(context, root, safe);
|
1105
|
+
} else {
|
1106
|
+
return '';
|
1107
|
+
}
|
1108
|
+
});
|
1109
|
+
|
1110
|
+
/**
|
1111
|
+
* *:each*
|
1112
|
+
* Iterates over the array returned by `expression` and evaluates the block
|
1113
|
+
* for each member of the array. The compiled blocks are then joined.
|
1114
|
+
*/
|
1115
|
+
|
1116
|
+
Walrus.addHelper('each', function(expression, context, root, safe, block) {
|
1117
|
+
var array, index, item, items;
|
1118
|
+
array = expression.compile(context, root, safe);
|
1119
|
+
if (!Walrus.Utils.isArray(array)) array = [array];
|
1120
|
+
items = (function() {
|
1121
|
+
var _len, _results;
|
1122
|
+
_results = [];
|
1123
|
+
for (index = 0, _len = array.length; index < _len; index++) {
|
1124
|
+
item = array[index];
|
1125
|
+
item['$index'] = index;
|
1126
|
+
item['$parent'] = context;
|
1127
|
+
item['$length'] = array.length;
|
1128
|
+
_results.push(block.compile(item, root, safe));
|
1129
|
+
}
|
1130
|
+
return _results;
|
1131
|
+
})();
|
1132
|
+
return items.join('');
|
1133
|
+
});
|
1134
|
+
|
1135
|
+
/**
|
1136
|
+
* *:with*
|
1137
|
+
* Changes the local context of the block to the result of `expression`.
|
1138
|
+
*
|
1139
|
+
* Usage:
|
1140
|
+
*
|
1141
|
+
* {{:with expression do}}
|
1142
|
+
* <p>{{name}}</p>
|
1143
|
+
* {{end}}
|
1144
|
+
*/
|
1145
|
+
|
1146
|
+
Walrus.addHelper('with', function(expression, context, root, safe, block) {
|
1147
|
+
var subcontext;
|
1148
|
+
subcontext = expression.compile(context, root, safe);
|
1149
|
+
subcontext['$parent'] = context;
|
1150
|
+
return block.compile(subcontext, root, safe);
|
1151
|
+
});
|
1152
|
+
|
1153
|
+
Walrus.Filters = {};
|
1154
|
+
|
1155
|
+
Walrus.addFilter = function(name, fn) {
|
1156
|
+
if (this.Filters[name] != null) {
|
1157
|
+
throw "Cannot override existing filter named '" + name + "'.";
|
1158
|
+
}
|
1159
|
+
return this.Filters[name] = fn;
|
1160
|
+
};
|
1161
|
+
|
1162
|
+
/**
|
1163
|
+
* Create a namespace for all of our domain methods
|
1164
|
+
*/
|
1165
|
+
|
1166
|
+
Walrus.Domain = {};
|
1167
|
+
|
1168
|
+
/**
|
1169
|
+
* *:as*
|
1170
|
+
* Decorates a view object or collection of view objects with
|
1171
|
+
* custom domain methods.
|
1172
|
+
*
|
1173
|
+
* Parameters:
|
1174
|
+
* name - The key for the object on `Walrus.Domain` to decorate with
|
1175
|
+
*/
|
1176
|
+
|
1177
|
+
Walrus.addFilter('as', function(value, name) {
|
1178
|
+
var item, _i, _len;
|
1179
|
+
if (Walrus.Utils.isArray(value)) {
|
1180
|
+
for (_i = 0, _len = value.length; _i < _len; _i++) {
|
1181
|
+
item = value[_i];
|
1182
|
+
Walrus.Utils.extend(item, Walrus.Domain[name]);
|
1183
|
+
}
|
1184
|
+
} else {
|
1185
|
+
Walrus.Utils.extend(value, Walrus.Domain[name]);
|
1186
|
+
}
|
1187
|
+
return value;
|
1188
|
+
});
|
1189
|
+
|
1190
|
+
/**
|
1191
|
+
* *:equals*
|
1192
|
+
* Returns whether the expression is strictly equal to the given value.
|
1193
|
+
*
|
1194
|
+
* Parameters:
|
1195
|
+
* foo - the value to compare to the expression
|
1196
|
+
*
|
1197
|
+
* Usage:
|
1198
|
+
*
|
1199
|
+
* {{ :if "foo" | :equals( "bar" ) do }}
|
1200
|
+
* // block will not be evaluated
|
1201
|
+
* {{ end }}
|
1202
|
+
*/
|
1203
|
+
|
1204
|
+
Walrus.addFilter('equals', function(value, foo) {
|
1205
|
+
return value === foo;
|
1206
|
+
});
|
1207
|
+
|
1208
|
+
/**
|
1209
|
+
* *:if*
|
1210
|
+
* Returns the expression if _condition_ is truthy, or nothing if _condition_ is falsy.
|
1211
|
+
*
|
1212
|
+
* Parameters:
|
1213
|
+
*
|
1214
|
+
* condition - the condition to test against
|
1215
|
+
*
|
1216
|
+
* Usage:
|
1217
|
+
*
|
1218
|
+
* {{ 'active' | :if( true ) }} // => "active"
|
1219
|
+
*/
|
1220
|
+
|
1221
|
+
Walrus.addFilter('if', function(value, condition) {
|
1222
|
+
if (condition) {
|
1223
|
+
return value;
|
1224
|
+
} else {
|
1225
|
+
return '';
|
1226
|
+
}
|
1227
|
+
});
|
1228
|
+
|
1229
|
+
/**
|
1230
|
+
* *:unless*
|
1231
|
+
* Returns the expression if _condition_ is falsy, or nothing if _condition_ is truthy.
|
1232
|
+
*
|
1233
|
+
* Parameters:
|
1234
|
+
*
|
1235
|
+
* condition - the condition to test against
|
1236
|
+
*
|
1237
|
+
* Usage:
|
1238
|
+
*
|
1239
|
+
* {{ 'active' | :unless( true ) }} // => ""
|
1240
|
+
*/
|
1241
|
+
|
1242
|
+
Walrus.addFilter('unless', function(value, condition) {
|
1243
|
+
if (!condition) {
|
1244
|
+
return value;
|
1245
|
+
} else {
|
1246
|
+
return '';
|
1247
|
+
}
|
1248
|
+
});
|
1249
|
+
|
1250
|
+
/**
|
1251
|
+
* *:or*
|
1252
|
+
* Returns the result of the expression if it is truthy, the given value if not.
|
1253
|
+
*
|
1254
|
+
* Parameters:
|
1255
|
+
* foo - the value to default to if the expression is falsy.
|
1256
|
+
*
|
1257
|
+
* Usage:
|
1258
|
+
*
|
1259
|
+
* {{ false | :or( "Not Specified" ) }} // => "Not Specified"
|
1260
|
+
*/
|
1261
|
+
|
1262
|
+
Walrus.addFilter('or', function(value, foo) {
|
1263
|
+
return value || foo;
|
1264
|
+
});
|
1265
|
+
|
1266
|
+
/**
|
1267
|
+
* *:log*
|
1268
|
+
* Logs whatever you want to the console if `console.log` is available.
|
1269
|
+
* Helpful for debugging your view object especially if your console
|
1270
|
+
* supports object inspection.
|
1271
|
+
*
|
1272
|
+
* Parameters: any
|
1273
|
+
*
|
1274
|
+
* Usage:
|
1275
|
+
*
|
1276
|
+
* {{ @root | :log( 'wtf' ) }} // => Console logs: [object Object], 'wtf'
|
1277
|
+
*/
|
1278
|
+
|
1279
|
+
Walrus.addFilter('log', function() {
|
1280
|
+
if ((typeof console !== "undefined" && console !== null) && (console.log != null)) {
|
1281
|
+
return console.log.apply(console, ['[Walrus]'].concat(__slice.call(arguments)));
|
1282
|
+
}
|
1283
|
+
});
|
1284
|
+
|
1285
|
+
/**
|
1286
|
+
* Setup
|
1287
|
+
*/
|
1288
|
+
|
1289
|
+
Walrus.Parser = {
|
1290
|
+
parser: walrus,
|
1291
|
+
parse: function() {
|
1292
|
+
return walrus.parse.apply(walrus, arguments);
|
1293
|
+
}
|
1294
|
+
};
|
1295
|
+
|
1296
|
+
Walrus.Parser.parser.yy = Walrus.AST;
|
1297
|
+
|
1298
|
+
/**
|
1299
|
+
* Export
|
1300
|
+
*/
|
1301
|
+
|
1302
|
+
(typeof exports !== "undefined" && exports !== null ? exports : this).Walrus = Walrus;
|
1303
|
+
|
1304
|
+
}).call(this);
|