├── .gitignore ├── .modulusignore ├── LICENSE ├── handlebars.js ├── main.js ├── package.json ├── readme.md ├── rfc822.js └── tako.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | ssl* 3 | *.pem 4 | options.js -------------------------------------------------------------------------------- /.modulusignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Max Ogden 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /handlebars.js: -------------------------------------------------------------------------------- 1 | // lib/handlebars/parser.js 2 | /* Jison generated parser */ 3 | var handlebars = (function(){ 4 | var parser = {trace: function trace() { }, 5 | yy: {}, 6 | symbols_: {"error":2,"root":3,"program":4,"EOF":5,"statements":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"OPEN_PARTIAL":24,"params":25,"hash":26,"param":27,"STRING":28,"INTEGER":29,"BOOLEAN":30,"hashSegments":31,"hashSegment":32,"ID":33,"EQUALS":34,"pathSegments":35,"SEP":36,"$accept":0,"$end":1}, 7 | terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"OPEN_PARTIAL",28:"STRING",29:"INTEGER",30:"BOOLEAN",33:"ID",34:"EQUALS",36:"SEP"}, 8 | productions_: [0,[3,2],[4,3],[4,1],[4,0],[6,1],[6,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,3],[13,4],[7,2],[17,3],[17,2],[17,2],[17,1],[25,2],[25,1],[27,1],[27,1],[27,1],[27,1],[26,1],[31,2],[31,1],[32,3],[32,3],[32,3],[32,3],[21,1],[35,3],[35,1]], 9 | performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { 10 | 11 | var $0 = $$.length - 1; 12 | switch (yystate) { 13 | case 1: return $$[$0-1] 14 | break; 15 | case 2: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]) 16 | break; 17 | case 3: this.$ = new yy.ProgramNode($$[$0]) 18 | break; 19 | case 4: this.$ = new yy.ProgramNode([]) 20 | break; 21 | case 5: this.$ = [$$[$0]] 22 | break; 23 | case 6: $$[$0-1].push($$[$0]); this.$ = $$[$0-1] 24 | break; 25 | case 7: this.$ = new yy.InverseNode($$[$0-2], $$[$0-1], $$[$0]) 26 | break; 27 | case 8: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0]) 28 | break; 29 | case 9: this.$ = $$[$0] 30 | break; 31 | case 10: this.$ = $$[$0] 32 | break; 33 | case 11: this.$ = new yy.ContentNode($$[$0]) 34 | break; 35 | case 12: this.$ = new yy.CommentNode($$[$0]) 36 | break; 37 | case 13: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]) 38 | break; 39 | case 14: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]) 40 | break; 41 | case 15: this.$ = $$[$0-1] 42 | break; 43 | case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]) 44 | break; 45 | case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true) 46 | break; 47 | case 18: this.$ = new yy.PartialNode($$[$0-1]) 48 | break; 49 | case 19: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]) 50 | break; 51 | case 20: 52 | break; 53 | case 21: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]] 54 | break; 55 | case 22: this.$ = [[$$[$0-1]].concat($$[$0]), null] 56 | break; 57 | case 23: this.$ = [[$$[$0-1]], $$[$0]] 58 | break; 59 | case 24: this.$ = [[$$[$0]], null] 60 | break; 61 | case 25: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]; 62 | break; 63 | case 26: this.$ = [$$[$0]] 64 | break; 65 | case 27: this.$ = $$[$0] 66 | break; 67 | case 28: this.$ = new yy.StringNode($$[$0]) 68 | break; 69 | case 29: this.$ = new yy.IntegerNode($$[$0]) 70 | break; 71 | case 30: this.$ = new yy.BooleanNode($$[$0]) 72 | break; 73 | case 31: this.$ = new yy.HashNode($$[$0]) 74 | break; 75 | case 32: $$[$0-1].push($$[$0]); this.$ = $$[$0-1] 76 | break; 77 | case 33: this.$ = [$$[$0]] 78 | break; 79 | case 34: this.$ = [$$[$0-2], $$[$0]] 80 | break; 81 | case 35: this.$ = [$$[$0-2], new yy.StringNode($$[$0])] 82 | break; 83 | case 36: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])] 84 | break; 85 | case 37: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])] 86 | break; 87 | case 38: this.$ = new yy.IdNode($$[$0]) 88 | break; 89 | case 39: $$[$0-2].push($$[$0]); this.$ = $$[$0-2]; 90 | break; 91 | case 40: this.$ = [$$[$0]] 92 | break; 93 | } 94 | }, 95 | table: [{3:1,4:2,5:[2,4],6:3,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],24:[1,15]},{1:[3]},{5:[1,16]},{5:[2,3],7:17,8:18,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,19],20:[2,3],22:[1,13],23:[1,14],24:[1,15]},{5:[2,5],14:[2,5],15:[2,5],16:[2,5],19:[2,5],20:[2,5],22:[2,5],23:[2,5],24:[2,5]},{4:20,6:3,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],24:[1,15]},{4:21,6:3,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],24:[1,15]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],24:[2,9]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],24:[2,10]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],24:[2,11]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],24:[2,12]},{17:22,21:23,33:[1,25],35:24},{17:26,21:23,33:[1,25],35:24},{17:27,21:23,33:[1,25],35:24},{17:28,21:23,33:[1,25],35:24},{21:29,33:[1,25],35:24},{1:[2,1]},{6:30,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],24:[1,15]},{5:[2,6],14:[2,6],15:[2,6],16:[2,6],19:[2,6],20:[2,6],22:[2,6],23:[2,6],24:[2,6]},{17:22,18:[1,31],21:23,33:[1,25],35:24},{10:32,20:[1,33]},{10:34,20:[1,33]},{18:[1,35]},{18:[2,24],21:40,25:36,26:37,27:38,28:[1,41],29:[1,42],30:[1,43],31:39,32:44,33:[1,45],35:24},{18:[2,38],28:[2,38],29:[2,38],30:[2,38],33:[2,38],36:[1,46]},{18:[2,40],28:[2,40],29:[2,40],30:[2,40],33:[2,40],36:[2,40]},{18:[1,47]},{18:[1,48]},{18:[1,49]},{18:[1,50],21:51,33:[1,25],35:24},{5:[2,2],8:18,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,2],22:[1,13],23:[1,14],24:[1,15]},{14:[2,20],15:[2,20],16:[2,20],19:[2,20],22:[2,20],23:[2,20],24:[2,20]},{5:[2,7],14:[2,7],15:[2,7],16:[2,7],19:[2,7],20:[2,7],22:[2,7],23:[2,7],24:[2,7]},{21:52,33:[1,25],35:24},{5:[2,8],14:[2,8],15:[2,8],16:[2,8],19:[2,8],20:[2,8],22:[2,8],23:[2,8],24:[2,8]},{14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],24:[2,14]},{18:[2,22],21:40,26:53,27:54,28:[1,41],29:[1,42],30:[1,43],31:39,32:44,33:[1,45],35:24},{18:[2,23]},{18:[2,26],28:[2,26],29:[2,26],30:[2,26],33:[2,26]},{18:[2,31],32:55,33:[1,56]},{18:[2,27],28:[2,27],29:[2,27],30:[2,27],33:[2,27]},{18:[2,28],28:[2,28],29:[2,28],30:[2,28],33:[2,28]},{18:[2,29],28:[2,29],29:[2,29],30:[2,29],33:[2,29]},{18:[2,30],28:[2,30],29:[2,30],30:[2,30],33:[2,30]},{18:[2,33],33:[2,33]},{18:[2,40],28:[2,40],29:[2,40],30:[2,40],33:[2,40],34:[1,57],36:[2,40]},{33:[1,58]},{14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],24:[2,13]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],24:[2,16]},{5:[2,17],14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],24:[2,17]},{5:[2,18],14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],24:[2,18]},{18:[1,59]},{18:[1,60]},{18:[2,21]},{18:[2,25],28:[2,25],29:[2,25],30:[2,25],33:[2,25]},{18:[2,32],33:[2,32]},{34:[1,57]},{21:61,28:[1,62],29:[1,63],30:[1,64],33:[1,25],35:24},{18:[2,39],28:[2,39],29:[2,39],30:[2,39],33:[2,39],36:[2,39]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],24:[2,19]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],24:[2,15]},{18:[2,34],33:[2,34]},{18:[2,35],33:[2,35]},{18:[2,36],33:[2,36]},{18:[2,37],33:[2,37]}], 96 | defaultActions: {16:[2,1],37:[2,23],53:[2,21]}, 97 | parseError: function parseError(str, hash) { 98 | throw new Error(str); 99 | }, 100 | parse: function parse(input) { 101 | var self = this, 102 | stack = [0], 103 | vstack = [null], // semantic value stack 104 | lstack = [], // location stack 105 | table = this.table, 106 | yytext = '', 107 | yylineno = 0, 108 | yyleng = 0, 109 | recovering = 0, 110 | TERROR = 2, 111 | EOF = 1; 112 | 113 | //this.reductionCount = this.shiftCount = 0; 114 | 115 | this.lexer.setInput(input); 116 | this.lexer.yy = this.yy; 117 | this.yy.lexer = this.lexer; 118 | if (typeof this.lexer.yylloc == 'undefined') 119 | this.lexer.yylloc = {}; 120 | var yyloc = this.lexer.yylloc; 121 | lstack.push(yyloc); 122 | 123 | if (typeof this.yy.parseError === 'function') 124 | this.parseError = this.yy.parseError; 125 | 126 | function popStack (n) { 127 | stack.length = stack.length - 2*n; 128 | vstack.length = vstack.length - n; 129 | lstack.length = lstack.length - n; 130 | } 131 | 132 | function lex() { 133 | var token; 134 | token = self.lexer.lex() || 1; // $end = 1 135 | // if token isn't its numeric value, convert 136 | if (typeof token !== 'number') { 137 | token = self.symbols_[token] || token; 138 | } 139 | return token; 140 | }; 141 | 142 | var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected; 143 | while (true) { 144 | // retreive state number from top of stack 145 | state = stack[stack.length-1]; 146 | 147 | // use default actions if available 148 | if (this.defaultActions[state]) { 149 | action = this.defaultActions[state]; 150 | } else { 151 | if (symbol == null) 152 | symbol = lex(); 153 | // read action for current state and first input 154 | action = table[state] && table[state][symbol]; 155 | } 156 | 157 | // handle parse error 158 | if (typeof action === 'undefined' || !action.length || !action[0]) { 159 | 160 | if (!recovering) { 161 | // Report error 162 | expected = []; 163 | for (p in table[state]) if (this.terminals_[p] && p > 2) { 164 | expected.push("'"+this.terminals_[p]+"'"); 165 | } 166 | var errStr = ''; 167 | if (this.lexer.showPosition) { 168 | errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+'\nExpecting '+expected.join(', '); 169 | } else { 170 | errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + 171 | (symbol == 1 /*EOF*/ ? "end of input" : 172 | ("'"+(this.terminals_[symbol] || symbol)+"'")); 173 | } 174 | this.parseError(errStr, 175 | {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); 176 | } 177 | 178 | // just recovered from another error 179 | if (recovering == 3) { 180 | if (symbol == EOF) { 181 | throw new Error(errStr || 'Parsing halted.'); 182 | } 183 | 184 | // discard current lookahead and grab another 185 | yyleng = this.lexer.yyleng; 186 | yytext = this.lexer.yytext; 187 | yylineno = this.lexer.yylineno; 188 | yyloc = this.lexer.yylloc; 189 | symbol = lex(); 190 | } 191 | 192 | // try to recover from error 193 | while (1) { 194 | // check for error recovery rule in this state 195 | if ((TERROR.toString()) in table[state]) { 196 | break; 197 | } 198 | if (state == 0) { 199 | throw new Error(errStr || 'Parsing halted.'); 200 | } 201 | popStack(1); 202 | state = stack[stack.length-1]; 203 | } 204 | 205 | preErrorSymbol = symbol; // save the lookahead token 206 | symbol = TERROR; // insert generic error symbol as new lookahead 207 | state = stack[stack.length-1]; 208 | action = table[state] && table[state][TERROR]; 209 | recovering = 3; // allow 3 real symbols to be shifted before reporting a new error 210 | } 211 | 212 | // this shouldn't happen, unless resolve defaults are off 213 | if (action[0] instanceof Array && action.length > 1) { 214 | throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); 215 | } 216 | 217 | switch (action[0]) { 218 | 219 | case 1: // shift 220 | //this.shiftCount++; 221 | 222 | stack.push(symbol); 223 | vstack.push(this.lexer.yytext); 224 | lstack.push(this.lexer.yylloc); 225 | stack.push(action[1]); // push state 226 | symbol = null; 227 | if (!preErrorSymbol) { // normal execution/no error 228 | yyleng = this.lexer.yyleng; 229 | yytext = this.lexer.yytext; 230 | yylineno = this.lexer.yylineno; 231 | yyloc = this.lexer.yylloc; 232 | if (recovering > 0) 233 | recovering--; 234 | } else { // error just occurred, resume old lookahead f/ before error 235 | symbol = preErrorSymbol; 236 | preErrorSymbol = null; 237 | } 238 | break; 239 | 240 | case 2: // reduce 241 | //this.reductionCount++; 242 | 243 | len = this.productions_[action[1]][1]; 244 | 245 | // perform semantic action 246 | yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 247 | // default location, uses first token for firsts, last for lasts 248 | yyval._$ = { 249 | first_line: lstack[lstack.length-(len||1)].first_line, 250 | last_line: lstack[lstack.length-1].last_line, 251 | first_column: lstack[lstack.length-(len||1)].first_column, 252 | last_column: lstack[lstack.length-1].last_column 253 | }; 254 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); 255 | 256 | if (typeof r !== 'undefined') { 257 | return r; 258 | } 259 | 260 | // pop off stack 261 | if (len) { 262 | stack = stack.slice(0,-1*len*2); 263 | vstack = vstack.slice(0, -1*len); 264 | lstack = lstack.slice(0, -1*len); 265 | } 266 | 267 | stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) 268 | vstack.push(yyval.$); 269 | lstack.push(yyval._$); 270 | // goto new state = table[STATE][NONTERMINAL] 271 | newState = table[stack[stack.length-2]][stack[stack.length-1]]; 272 | stack.push(newState); 273 | break; 274 | 275 | case 3: // accept 276 | return true; 277 | } 278 | 279 | } 280 | 281 | return true; 282 | }};/* Jison generated lexer */ 283 | var lexer = (function(){var lexer = ({EOF:1, 284 | parseError:function parseError(str, hash) { 285 | if (this.yy.parseError) { 286 | this.yy.parseError(str, hash); 287 | } else { 288 | throw new Error(str); 289 | } 290 | }, 291 | setInput:function (input) { 292 | this._input = input; 293 | this._more = this._less = this.done = false; 294 | this.yylineno = this.yyleng = 0; 295 | this.yytext = this.matched = this.match = ''; 296 | this.conditionStack = ['INITIAL']; 297 | this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; 298 | return this; 299 | }, 300 | input:function () { 301 | var ch = this._input[0]; 302 | this.yytext+=ch; 303 | this.yyleng++; 304 | this.match+=ch; 305 | this.matched+=ch; 306 | var lines = ch.match(/\n/); 307 | if (lines) this.yylineno++; 308 | this._input = this._input.slice(1); 309 | return ch; 310 | }, 311 | unput:function (ch) { 312 | this._input = ch + this._input; 313 | return this; 314 | }, 315 | more:function () { 316 | this._more = true; 317 | return this; 318 | }, 319 | pastInput:function () { 320 | var past = this.matched.substr(0, this.matched.length - this.match.length); 321 | return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); 322 | }, 323 | upcomingInput:function () { 324 | var next = this.match; 325 | if (next.length < 20) { 326 | next += this._input.substr(0, 20-next.length); 327 | } 328 | return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); 329 | }, 330 | showPosition:function () { 331 | var pre = this.pastInput(); 332 | var c = new Array(pre.length + 1).join("-"); 333 | return pre + this.upcomingInput() + "\n" + c+"^"; 334 | }, 335 | next:function () { 336 | if (this.done) { 337 | return this.EOF; 338 | } 339 | if (!this._input) this.done = true; 340 | 341 | var token, 342 | match, 343 | col, 344 | lines; 345 | if (!this._more) { 346 | this.yytext = ''; 347 | this.match = ''; 348 | } 349 | var rules = this._currentRules(); 350 | for (var i=0;i < rules.length; i++) { 351 | match = this._input.match(this.rules[rules[i]]); 352 | if (match) { 353 | lines = match[0].match(/\n.*/g); 354 | if (lines) this.yylineno += lines.length; 355 | this.yylloc = {first_line: this.yylloc.last_line, 356 | last_line: this.yylineno+1, 357 | first_column: this.yylloc.last_column, 358 | last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length} 359 | this.yytext += match[0]; 360 | this.match += match[0]; 361 | this.matches = match; 362 | this.yyleng = this.yytext.length; 363 | this._more = false; 364 | this._input = this._input.slice(match[0].length); 365 | this.matched += match[0]; 366 | token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]); 367 | if (token) return token; 368 | else return; 369 | } 370 | } 371 | if (this._input === "") { 372 | return this.EOF; 373 | } else { 374 | this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), 375 | {text: "", token: null, line: this.yylineno}); 376 | } 377 | }, 378 | lex:function lex() { 379 | var r = this.next(); 380 | if (typeof r !== 'undefined') { 381 | return r; 382 | } else { 383 | return this.lex(); 384 | } 385 | }, 386 | begin:function begin(condition) { 387 | this.conditionStack.push(condition); 388 | }, 389 | popState:function popState() { 390 | return this.conditionStack.pop(); 391 | }, 392 | _currentRules:function _currentRules() { 393 | return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; 394 | }}); 395 | lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { 396 | 397 | var YYSTATE=YY_START 398 | switch($avoiding_name_collisions) { 399 | case 0: this.begin("mu"); if (yy_.yytext) return 14; 400 | break; 401 | case 1: return 14; 402 | break; 403 | case 2: return 24; 404 | break; 405 | case 3: return 16; 406 | break; 407 | case 4: return 20; 408 | break; 409 | case 5: return 19; 410 | break; 411 | case 6: return 19; 412 | break; 413 | case 7: return 23; 414 | break; 415 | case 8: return 23; 416 | break; 417 | case 9: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.begin("INITIAL"); return 15; 418 | break; 419 | case 10: return 22; 420 | break; 421 | case 11: return 34; 422 | break; 423 | case 12: return 33; 424 | break; 425 | case 13: return 33; 426 | break; 427 | case 14: return 36; 428 | break; 429 | case 15: /*ignore whitespace*/ 430 | break; 431 | case 16: this.begin("INITIAL"); return 18; 432 | break; 433 | case 17: this.begin("INITIAL"); return 18; 434 | break; 435 | case 18: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 28; 436 | break; 437 | case 19: return 30; 438 | break; 439 | case 20: return 30; 440 | break; 441 | case 21: return 29; 442 | break; 443 | case 22: return 33; 444 | break; 445 | case 23: return 'INVALID'; 446 | break; 447 | case 24: return 5; 448 | break; 449 | } 450 | }; 451 | lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^\{\{>/,/^\{\{#/,/^\{\{\//,/^\{\{\^/,/^\{\{\s*else\b/,/^\{\{\{/,/^\{\{&/,/^\{\{![\s\S]*?\}\}/,/^\{\{/,/^=/,/^\.(?=[} ])/,/^\.\./,/^[/.]/,/^\s+/,/^\}\}\}/,/^\}\}/,/^"(\\["]|[^"])*"/,/^true(?=[}\s])/,/^false(?=[}\s])/,/^[0-9]+(?=[}\s])/,/^[a-zA-Z0-9_$-]+(?=[=}\s/.])/,/^./,/^$/]; 452 | lexer.conditions = {"mu":{"rules":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"inclusive":false},"INITIAL":{"rules":[0,1,24],"inclusive":true}};return lexer;})() 453 | parser.lexer = lexer; 454 | return parser; 455 | })(); 456 | 457 | // lib/handlebars/base.js 458 | var Handlebars = {}; 459 | 460 | if (typeof require !== 'undefined' && typeof exports !== 'undefined') { 461 | module.exports = Handlebars; 462 | } 463 | 464 | Handlebars.VERSION = "1.0.beta.2"; 465 | 466 | Handlebars.Parser = handlebars; 467 | 468 | Handlebars.parse = function(string) { 469 | Handlebars.Parser.yy = Handlebars.AST; 470 | return Handlebars.Parser.parse(string); 471 | }; 472 | 473 | Handlebars.print = function(ast) { 474 | return new Handlebars.PrintVisitor().accept(ast); 475 | }; 476 | 477 | Handlebars.helpers = {}; 478 | Handlebars.partials = {}; 479 | 480 | Handlebars.registerHelper = function(name, fn, inverse) { 481 | if(inverse) { fn.not = inverse; } 482 | this.helpers[name] = fn; 483 | }; 484 | 485 | Handlebars.registerPartial = function(name, str) { 486 | this.partials[name] = str; 487 | }; 488 | 489 | Handlebars.registerHelper('helperMissing', function(arg) { 490 | if(arguments.length === 2) { 491 | return undefined; 492 | } else { 493 | throw new Error("Could not find property '" + arg + "'"); 494 | } 495 | }); 496 | 497 | Handlebars.registerHelper('blockHelperMissing', function(context, fn, inverse) { 498 | inverse = inverse || function() {}; 499 | 500 | var ret = ""; 501 | var type = Object.prototype.toString.call(context); 502 | 503 | if(type === "[object Function]") { 504 | context = context(); 505 | } 506 | 507 | if(context === true) { 508 | return fn(this); 509 | } else if(context === false || context == null) { 510 | return inverse(this); 511 | } else if(type === "[object Array]") { 512 | if(context.length > 0) { 513 | for(var i=0, j=context.length; i 0) { 531 | for(var i=0, j=context.length; i": ">", 690 | '"': """, 691 | "'": "'", 692 | "`": "`" 693 | }; 694 | 695 | var badChars = /&(?!\w+;)|[<>"'`]/g; 696 | var possible = /[&<>"'`]/; 697 | 698 | var escapeChar = function(chr) { 699 | return escape[chr] || "&" 700 | }; 701 | 702 | Handlebars.Utils = { 703 | escapeExpression: function(string) { 704 | // don't escape SafeStrings, since they're already safe 705 | if (string instanceof Handlebars.SafeString) { 706 | return string.toString(); 707 | } else if (string == null || string === false) { 708 | return ""; 709 | } 710 | 711 | if(!possible.test(string)) { return string; } 712 | return string.replace(badChars, escapeChar); 713 | }, 714 | 715 | isEmpty: function(value) { 716 | if (typeof value === "undefined") { 717 | return true; 718 | } else if (value === null) { 719 | return true; 720 | } else if (value === false) { 721 | return true; 722 | } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) { 723 | return true; 724 | } else { 725 | return false; 726 | } 727 | } 728 | }; 729 | })();; 730 | // lib/handlebars/compiler.js 731 | Handlebars.Compiler = function() {}; 732 | Handlebars.JavaScriptCompiler = function() {}; 733 | 734 | (function(Compiler, JavaScriptCompiler) { 735 | Compiler.OPCODE_MAP = { 736 | appendContent: 1, 737 | getContext: 2, 738 | lookupWithHelpers: 3, 739 | lookup: 4, 740 | append: 5, 741 | invokeMustache: 6, 742 | appendEscaped: 7, 743 | pushString: 8, 744 | truthyOrFallback: 9, 745 | functionOrFallback: 10, 746 | invokeProgram: 11, 747 | invokePartial: 12, 748 | push: 13, 749 | invokeInverse: 14, 750 | assignToHash: 15, 751 | pushStringParam: 16 752 | }; 753 | 754 | Compiler.MULTI_PARAM_OPCODES = { 755 | appendContent: 1, 756 | getContext: 1, 757 | lookupWithHelpers: 1, 758 | lookup: 1, 759 | invokeMustache: 2, 760 | pushString: 1, 761 | truthyOrFallback: 1, 762 | functionOrFallback: 1, 763 | invokeProgram: 2, 764 | invokePartial: 1, 765 | push: 1, 766 | invokeInverse: 1, 767 | assignToHash: 1, 768 | pushStringParam: 1 769 | }; 770 | 771 | Compiler.DISASSEMBLE_MAP = {}; 772 | 773 | for(var prop in Compiler.OPCODE_MAP) { 774 | var value = Compiler.OPCODE_MAP[prop]; 775 | Compiler.DISASSEMBLE_MAP[value] = prop; 776 | } 777 | 778 | Compiler.multiParamSize = function(code) { 779 | return Compiler.MULTI_PARAM_OPCODES[Compiler.DISASSEMBLE_MAP[code]]; 780 | }; 781 | 782 | Compiler.prototype = { 783 | compiler: Compiler, 784 | 785 | disassemble: function() { 786 | var opcodes = this.opcodes, opcode, nextCode; 787 | var out = [], str, name, value; 788 | 789 | for(var i=0, l=opcodes.length; i 0) { 1139 | this.source[0] = this.source[0] + ", " + locals.join(", "); 1140 | } 1141 | 1142 | this.source[0] = this.source[0] + ";"; 1143 | 1144 | this.source.push("return buffer;"); 1145 | 1146 | var params = ["Handlebars", "context", "helpers", "partials"]; 1147 | 1148 | if(this.options.data) { params.push("data"); } 1149 | 1150 | for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } 1402 | return "stack" + this.stackSlot; 1403 | }, 1404 | 1405 | popStack: function() { 1406 | return "stack" + this.stackSlot--; 1407 | }, 1408 | 1409 | topStack: function() { 1410 | return "stack" + this.stackSlot; 1411 | }, 1412 | 1413 | quotedString: function(str) { 1414 | return '"' + str 1415 | .replace(/\\/g, '\\\\') 1416 | .replace(/"/g, '\\"') 1417 | .replace(/\n/g, '\\n') 1418 | .replace(/\r/g, '\\r') + '"'; 1419 | } 1420 | }; 1421 | 1422 | var reservedWords = ("break case catch continue default delete do else finally " + 1423 | "for function if in instanceof new return switch this throw " + 1424 | "try typeof var void while with null true false").split(" "); 1425 | 1426 | compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; 1427 | 1428 | for(var i=0, l=reservedWords.length; i 0) ? "-" : "+"; 37 | var absHours = Math.abs(hours) 38 | s += (absHours < 10) ? "0" + absHours :absHours; 39 | s += ((modMin == 0) ? "00" : modMin); 40 | return(s); 41 | } 42 | 43 | exports.getRFC822Date = getRFC822Date; 44 | -------------------------------------------------------------------------------- /tako.js: -------------------------------------------------------------------------------- 1 | var routes = require('routes') 2 | , util = require('util') 3 | , events = require('events') 4 | , crypto = require('crypto') 5 | , rfc822 = require('./rfc822') 6 | , filed = require('filed') 7 | , path = require('path') 8 | , url = require('url') 9 | , fs = require('fs') 10 | , util = require('util') 11 | , stream = require('stream') 12 | , qs = require('querystring') 13 | , handlebars = require('./handlebars') 14 | ; 15 | 16 | var cap = function (stream, limit) { 17 | if (!limit) limit = Infinity 18 | stream.caplimit = limit 19 | stream.bufferedData = [] 20 | stream.bufferedLength = 0 21 | 22 | stream._capemit = stream.emit 23 | stream.emit = function () { 24 | if (arguments[0] === 'data') { 25 | stream.bufferedData.push(arguments) 26 | stream.bufferedLength += arguments[1].length 27 | if (stream.bufferedLength > stream.caplimit) { 28 | stream.pause() 29 | } 30 | } else if (arguments[0] === 'end') { 31 | stream.ended = true 32 | } else { 33 | stream._capemit.apply(stream, arguments) 34 | } 35 | } 36 | 37 | stream.release = function () { 38 | stream.emit = stream._capemit 39 | while (stream.bufferedData.length) { 40 | stream.emit.apply(stream, stream.bufferedData.shift()) 41 | } 42 | if (stream.ended) stream.emit('end') 43 | stream.resume() 44 | } 45 | 46 | return stream 47 | } 48 | 49 | module.exports = function (options) { 50 | return new Application(options) 51 | } 52 | exports.globalMiddles = {} 53 | 54 | function BufferResponse (buffer, mimetype) { 55 | if (!Buffer.isBuffer(buffer)) this.body = new Buffer(buffer) 56 | else this.body = buffer 57 | this.timestamp = rfc822.getRFC822Date(new Date()) 58 | this.etag = crypto.createHash('md5').update(buffer).digest("hex") 59 | this.mimetype = mimetype 60 | } 61 | BufferResponse.prototype.request = function (req, resp) { 62 | resp.setHeader('content-type', this.mimetype) 63 | resp.setHeader('last-modified', this.timestamp) 64 | resp.setHeader('etag', this.etag) 65 | if (req.method !== 'GET' && req.method !== 'HEAD') { 66 | resp.statusCode = 405 67 | return resp.end() 68 | } 69 | if (req.headers['if-none-match'] === this.etag || 70 | req.headers['if-modified-since'] === this.timestamp) { 71 | resp.statusCode = 304 72 | return resp.end() 73 | } 74 | resp.statusCode = 200 75 | return resp.end(this.body) 76 | } 77 | 78 | function Page (templatename) { 79 | var self = this 80 | self.promises = {} 81 | self.counter = 0 82 | self.results = {} 83 | self.dests = [] 84 | self.on('pipe', function (src) { 85 | if (src.method && (src.method === 'PUT' || src.method == 'POST')) { 86 | var p = self.promise('body') 87 | src.on('error', function (e) { 88 | p(e) 89 | }) 90 | src.on('body', function (body) { 91 | p(null, body) 92 | }) 93 | if (src.json) { 94 | var jp = self.promise('json') 95 | src.on('json', function (obj) { 96 | jp(null, obj) 97 | }) 98 | } 99 | } 100 | }) 101 | process.nextTick(function () { 102 | if (templatename) { 103 | self.template(templatename) 104 | } 105 | if (self.counter === 0) self.emit('finish') 106 | }) 107 | } 108 | util.inherits(Page, stream.Stream) 109 | Page.prototype.promise = function (name) { 110 | var self = this; 111 | self.counter += 1 112 | self.promises[name] = function (e, result) { 113 | self.emit('resolved', name, e, result) 114 | if (e) return self.emit('error', e, name) 115 | self.results[name] = result 116 | self.counter = self.counter - 1 117 | if (self.counter === 0) self.emit('finish') 118 | } 119 | return self.promises[name] 120 | } 121 | Page.prototype.pipe = function (dest) { 122 | this.dests.push(dest) 123 | } 124 | Page.prototype.write = function () {} 125 | Page.prototype.end = function () {} 126 | 127 | // Templates implementation 128 | function Templates (app) { 129 | this.files = {} 130 | this.loaded = true 131 | this.after = {} 132 | this.app = app 133 | this.names = {} 134 | this.loading = 0 135 | this.tempcache = {} 136 | this.Template = function (text) { 137 | this.compiled = handlebars.compile(text) 138 | } 139 | this.Template.prototype.render = function (obj) { 140 | return new Buffer(this.compiled(obj)) 141 | } 142 | } 143 | util.inherits(Templates, events.EventEmitter) 144 | Templates.prototype.get = function (name, cb) { 145 | var self = this 146 | 147 | if (name.indexOf(' ') !== -1 || name[0] === '<') { 148 | process.nextTick(function () { 149 | if (!self.tempcache[name]) { 150 | self.tempcache[name] = new self.Template(name) 151 | } 152 | cb(null, self.tempcache[name]) 153 | }) 154 | return 155 | } 156 | 157 | function finish () { 158 | if (name in self.names) { 159 | cb(null, self.files[self.names[name]]) 160 | } else { 161 | cb(new Error("Cannot find template")) 162 | } 163 | } 164 | if (this.loaded) { 165 | process.nextTick(finish) 166 | } else { 167 | self.once('loaded', finish) 168 | } 169 | } 170 | Templates.prototype.directory = function (dir) { 171 | var self = this 172 | this.loaded = false 173 | this.loading += 1 174 | loadfiles(dir, function (e, filemap) { 175 | if (e) return self.emit('error', e) 176 | for (i in filemap) { 177 | self.files[i] = new self.Template(filemap[i]) 178 | self.names[path.basename(i)] = i 179 | self.names[path.basename(i, path.extname(i))] = i 180 | } 181 | self.loading -= 1 182 | self.loaded = true 183 | if (self.loading === 0) self.emit('loaded') 184 | }) 185 | } 186 | 187 | function loadfiles (f, cb) { 188 | var filesmap = {} 189 | fs.readdir(f, function (e, files) { 190 | if (e) return cb(e) 191 | var counter = 0 192 | files.forEach(function (filename) { 193 | counter += 1 194 | fs.stat(path.join(f, filename), function (e, stat) { 195 | if (stat.isDirectory()) { 196 | loadfiles(path.join(f, filename), function (e, files) { 197 | if (e) return cb(e) 198 | for (i in files) { 199 | filesmap[i] = files[i] 200 | } 201 | counter -= 1 202 | if (counter === 0) cb(null, filesmap) 203 | }) 204 | } else { 205 | fs.readFile(path.join(f, filename), function (e, data) { 206 | filesmap[path.join(f, filename)] = data.toString() 207 | counter -= 1 208 | if (counter === 0) cb(null, filesmap) 209 | }) 210 | } 211 | }) 212 | }) 213 | }) 214 | } 215 | 216 | function Application (options) { 217 | var self = this 218 | self.options = options 219 | self.middles = [] 220 | self.handler = function (req, resp) { 221 | // Add a nicer accept function 222 | self._currentRequest = req 223 | self._currentResponse = resp 224 | req.accept = function () { 225 | if (!req.headers.accept) return null 226 | var cc = null 227 | var pos = 99999999 228 | for (var i=arguments.length-1;i!==-1;i--) { 229 | var ipos = req.headers.accept.indexOf(arguments[i]) 230 | if ( ipos !== -1 && ipos < pos ) cc = arguments[i] 231 | } 232 | return cc 233 | } 234 | 235 | resp.error = function (err) { 236 | if (!err.statusCode) err.statusCode = 500 237 | resp.statusCode = err.statusCode 238 | resp.end(err) // this should be better 239 | } 240 | 241 | // Get all the parsed url properties on the request 242 | // This is the same style express uses and it's quite nice 243 | var parsed = url.parse(req.url) 244 | for (i in parsed) { 245 | req[i] = parsed[i] 246 | } 247 | 248 | if (req.query) req.qs = qs.parse(req.query) 249 | 250 | req.route = self.router.match(req.pathname) 251 | 252 | if (!req.route) return self.notfound(req, resp) 253 | 254 | req.params = req.route.params 255 | 256 | var onWrites = [] 257 | resp._write = resp.write 258 | resp.write = function () { 259 | if (onWrites.length === 0) return resp._write.apply(resp, arguments) 260 | var args = arguments 261 | onWrites.forEach(function (onWrite) { 262 | var c = onWrite.apply(resp, args) 263 | if (c !== undefined) args[0] = c 264 | }) 265 | return resp._write.apply(resp, args) 266 | } 267 | 268 | // Fix for node's premature header check in end() 269 | resp._end = resp.end 270 | resp.end = function (chunk) { 271 | if (chunk) resp.write(chunk) 272 | resp._end() 273 | } 274 | 275 | self.emit('request', req, resp) 276 | 277 | self.middles.forEach(function (middle) { 278 | middle.emit('request', req, resp) 279 | if (resp.onWrite) { 280 | onWrites.push(resp.onWrite) 281 | resp.onWrite = null 282 | } 283 | }) 284 | 285 | if (req.listeners('body').length) { 286 | var buffer = '' 287 | req.on('data', function (chunk) { 288 | buffer += chunk 289 | }) 290 | req.on('end', function (chunk) { 291 | if (chunk) buffer += chunk 292 | req.emit('body', buffer) 293 | }) 294 | } 295 | 296 | req.route.fn.call(req.route, req, resp, self.authHandler) 297 | } 298 | 299 | self.router = new routes.Router() 300 | self.on('newroute', function (route) { 301 | self.router.addRoute(route.path, function (req, resp, authHandler) { 302 | route.handler(req, resp, authHandler) 303 | }) 304 | }) 305 | 306 | self.templates = new Templates(self) 307 | } 308 | util.inherits(Application, events.EventEmitter) 309 | Application.prototype.route = function (path, cb) { 310 | var r = new Route(path, this) 311 | if (cb) r.on('request', cb) 312 | return r 313 | } 314 | Application.prototype.middle = function (mid) { 315 | if (typeof mid === 'string') { 316 | if (!exports.globalMiddles[mid]) throw new Error('No known global middleware of type "'+mid+'"') 317 | mid = exports.globalMiddles[mid] 318 | } 319 | this.middles.push(mid) 320 | return this 321 | } 322 | Application.prototype.listen = function (createServer, port, cb) { 323 | var self = this 324 | self.server = createServer(function (req, resp) { 325 | self.handler(req, resp) 326 | }) 327 | self.server.listen(port, cb) 328 | return this 329 | } 330 | Application.prototype.close = function () { 331 | this.server.close() 332 | return this 333 | } 334 | Application.prototype.notfound = function (req, resp) { 335 | var cc = req.accept('text/html', 'application/json', 'text/plain') || 'text/plain' 336 | resp.statusCode = 404 337 | resp.setHeader('content-type', cc) 338 | if (cc === 'text/html') { 339 | body = 'Not Found' 340 | } else if (cc === 'application/json') { 341 | body = JSON.stringify({status:404, reason:'not found', message:'not found'}) 342 | } else { 343 | body = 'Not Found' 344 | } 345 | resp.end(body) 346 | } 347 | Application.prototype.auth = function (handler) { 348 | if (!handler) return this.authHandler 349 | this.authHandler = handler 350 | } 351 | Application.prototype.page = function () { 352 | var page = new Page() 353 | , self = this 354 | ; 355 | process.nextTick(function () { 356 | if (!page.src && self._currentRequest) { 357 | self._currentRequest.pipe(page) 358 | page.pipe(self._currentResponse) 359 | } 360 | }) 361 | page.template = function (name) { 362 | var p = page.promise("template") 363 | self.templates.get(name, function (e, template) { 364 | if (e) return p(e) 365 | if (p.src) p.src.pipe(template) 366 | page.on('finish', function () { 367 | process.nextTick(function () { 368 | var text = template.render(page.results) 369 | page.dests.forEach(function (d) { 370 | if (d.writeHead) { 371 | d.statusCode = 200 372 | d.setHeader('content-type', page.mimetype || 'text/html') 373 | d.setHeader('content-length', text.length) 374 | } 375 | d.write(text) 376 | d.end() 377 | }) 378 | }) 379 | }) 380 | p(null, template) 381 | }) 382 | } 383 | return page 384 | } 385 | 386 | function JSONMiddleware () { 387 | this.on('request', function (req, resp) { 388 | resp.onWrite = function (chunk) { 389 | // bail fast for chunks to limit impact on streaming 390 | if (Buffer.isBuffer(chunk)) return chunk 391 | // if it's an object serialize it and set proper headers 392 | if (typeof chunk === 'object') { 393 | chunk = new Buffer(JSON.stringify(chunk)) 394 | resp.setHeader('content-type', 'application/json') 395 | resp.setHeader('content-length', chunk.length) 396 | if (!resp.statusCode && (req.method === 'GET' || req.method === 'HEAD')) { 397 | resp.statusCode = 200 398 | } 399 | return chunk 400 | } 401 | return chunk 402 | } 403 | 404 | if (req.method === "PUT" || req.method === "POST") { 405 | if (req.headers['content-type'] === 'application/json') { 406 | req.on('body', function (body) { 407 | req.emit('json', JSON.parse(body)) 408 | }) 409 | } 410 | } 411 | }) 412 | } 413 | util.inherits(JSONMiddleware, events.EventEmitter) 414 | exports.globalMiddles['json'] = new JSONMiddleware() 415 | 416 | function Route (path, application) { 417 | var self = this 418 | self.path = path 419 | self.app = application 420 | self.byContentType = {} 421 | 422 | var returnEarly = function (req, resp, keys, authHandler) { 423 | if (self._events && self._events['request']) { 424 | if (authHandler) { 425 | cap(req) 426 | authHandler(req, resp, function (user) { 427 | req.user = user 428 | self.emit('request', req, resp) 429 | req.release() 430 | }) 431 | } else { 432 | self.emit('request', req, resp) 433 | } 434 | } else { 435 | resp.statusCode = 406 436 | resp.setHeader('content-type', 'text/plain') 437 | resp.end('Request does not include a valid mime-type for this resource: '+keys.join(', ')) 438 | } 439 | } 440 | 441 | self.handler = function (req, resp, authHandler) { 442 | self.emit('before', req, resp) 443 | if (self.authHandler) { 444 | authHandler = self.authHandler 445 | } 446 | 447 | var keys = Object.keys(self.byContentType) 448 | if (keys.length) { 449 | if (req.method !== 'PUT' && req.method !== 'POST') { 450 | var cc = req.accept.apply(req, keys) 451 | } else { 452 | var cc = req.headers['content-type'] 453 | } 454 | 455 | if (!cc) return returnEarly(req, resp, keys, authHandler) 456 | var h = this.byContentType[cc] 457 | if (!h) return returnEarly(req, resp, keys, authHandler) 458 | resp.setHeader('content-type', cc) 459 | 460 | var run = function () { 461 | if (h.request) { 462 | return h.request(req, resp) 463 | } 464 | if (h.pipe) { 465 | req.pipe(h) 466 | h.pipe(resp) 467 | return 468 | } 469 | h.call(req.route, req, resp) 470 | } 471 | 472 | if (authHandler) { 473 | cap(req) 474 | authHandler(req, resp, function (user) { 475 | req.user = user 476 | run() 477 | req.release() 478 | }) 479 | } else { 480 | run() 481 | } 482 | 483 | } else { 484 | returnEarly(req, resp, keys, authHandler) 485 | } 486 | 487 | } 488 | 489 | application.emit('newroute', self) 490 | } 491 | util.inherits(Route, events.EventEmitter) 492 | Route.prototype.json = function (cb) { 493 | if (Buffer.isBuffer(cb)) cb = new BufferResponse(cb, 'application/json') 494 | else if (typeof cb === 'object') cb = new BufferResponse(JSON.stringify(cb), 'application/json') 495 | else if (typeof cb === 'string') { 496 | if (cb[0] === '/') cb = filed(cb) 497 | else cb = new BufferResponse(cb, 'application/json') 498 | } 499 | this.byContentType['application/json'] = cb 500 | return this 501 | } 502 | Route.prototype.html = function (cb) { 503 | if (Buffer.isBuffer(cb)) cb = new BufferResponse(cb, 'text/html') 504 | else if (typeof cb === 'string') { 505 | if (cb[0] === '/') cb = filed(cb) 506 | else cb = new BufferResponse(cb, 'text/html') 507 | } 508 | this.byContentType['text/html'] = cb 509 | return this 510 | } 511 | Route.prototype.file = function (filepath) { 512 | this.on('request', function (req, resp) { 513 | var f = filed(filepath) 514 | req.pipe(f) 515 | f.pipe(resp) 516 | }) 517 | return this 518 | } 519 | Route.prototype.files = function (filepath) { 520 | this.on('request', function (req, resp) { 521 | req.route.splats.unshift(filepath) 522 | var f = filed(path.join.apply(path.join, req.route.splats)) 523 | req.pipe(f) 524 | f.pipe(resp) 525 | }) 526 | return this 527 | } 528 | Route.prototype.auth = function (handler) { 529 | if (!handler) return this.authHandler 530 | this.authHandler = handler 531 | } 532 | --------------------------------------------------------------------------------