handlebars.js 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920
  1. // lib/handlebars/base.js
  2. /*jshint eqnull:true*/
  3. this.Handlebars = {};
  4. (function(Handlebars) {
  5. Handlebars.VERSION = "1.0.rc.1";
  6. Handlebars.helpers = {};
  7. Handlebars.partials = {};
  8. Handlebars.registerHelper = function(name, fn, inverse) {
  9. if(inverse) { fn.not = inverse; }
  10. this.helpers[name] = fn;
  11. };
  12. Handlebars.registerPartial = function(name, str) {
  13. this.partials[name] = str;
  14. };
  15. Handlebars.registerHelper('helperMissing', function(arg) {
  16. if(arguments.length === 2) {
  17. return undefined;
  18. } else {
  19. throw new Error("Could not find property '" + arg + "'");
  20. }
  21. });
  22. var toString = Object.prototype.toString, functionType = "[object Function]";
  23. Handlebars.registerHelper('blockHelperMissing', function(context, options) {
  24. var inverse = options.inverse || function() {}, fn = options.fn;
  25. var ret = "";
  26. var type = toString.call(context);
  27. if(type === functionType) { context = context.call(this); }
  28. if(context === true) {
  29. return fn(this);
  30. } else if(context === false || context == null) {
  31. return inverse(this);
  32. } else if(type === "[object Array]") {
  33. if(context.length > 0) {
  34. return Handlebars.helpers.each(context, options);
  35. } else {
  36. return inverse(this);
  37. }
  38. } else {
  39. return fn(context);
  40. }
  41. });
  42. Handlebars.K = function() {};
  43. Handlebars.createFrame = Object.create || function(object) {
  44. Handlebars.K.prototype = object;
  45. var obj = new Handlebars.K();
  46. Handlebars.K.prototype = null;
  47. return obj;
  48. };
  49. Handlebars.registerHelper('each', function(context, options) {
  50. var fn = options.fn, inverse = options.inverse;
  51. var ret = "", data;
  52. if (options.data) {
  53. data = Handlebars.createFrame(options.data);
  54. }
  55. if(context && context.length > 0) {
  56. for(var i=0, j=context.length; i<j; i++) {
  57. if (data) { data.index = i; }
  58. ret = ret + fn(context[i], { data: data });
  59. }
  60. } else {
  61. ret = inverse(this);
  62. }
  63. return ret;
  64. });
  65. Handlebars.registerHelper('if', function(context, options) {
  66. var type = toString.call(context);
  67. if(type === functionType) { context = context.call(this); }
  68. if(!context || Handlebars.Utils.isEmpty(context)) {
  69. return options.inverse(this);
  70. } else {
  71. return options.fn(this);
  72. }
  73. });
  74. Handlebars.registerHelper('unless', function(context, options) {
  75. var fn = options.fn, inverse = options.inverse;
  76. options.fn = inverse;
  77. options.inverse = fn;
  78. return Handlebars.helpers['if'].call(this, context, options);
  79. });
  80. Handlebars.registerHelper('with', function(context, options) {
  81. return options.fn(context);
  82. });
  83. Handlebars.registerHelper('log', function(context) {
  84. Handlebars.log(context);
  85. });
  86. }(this.Handlebars));
  87. ;
  88. // lib/handlebars/compiler/parser.js
  89. /* Jison generated parser */
  90. var handlebars = (function(){
  91. var parser = {trace: function trace() { },
  92. yy: {},
  93. 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,"DATA":27,"param":28,"STRING":29,"INTEGER":30,"BOOLEAN":31,"hashSegments":32,"hashSegment":33,"ID":34,"EQUALS":35,"pathSegments":36,"SEP":37,"$accept":0,"$end":1},
  94. 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",27:"DATA",29:"STRING",30:"INTEGER",31:"BOOLEAN",34:"ID",35:"EQUALS",37:"SEP"},
  95. 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],[17,1],[25,2],[25,1],[28,1],[28,1],[28,1],[28,1],[28,1],[26,1],[32,2],[32,1],[33,3],[33,3],[33,3],[33,3],[33,3],[21,1],[36,3],[36,1]],
  96. performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
  97. var $0 = $$.length - 1;
  98. switch (yystate) {
  99. case 1: return $$[$0-1];
  100. break;
  101. case 2: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]);
  102. break;
  103. case 3: this.$ = new yy.ProgramNode($$[$0]);
  104. break;
  105. case 4: this.$ = new yy.ProgramNode([]);
  106. break;
  107. case 5: this.$ = [$$[$0]];
  108. break;
  109. case 6: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
  110. break;
  111. case 7: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]);
  112. break;
  113. case 8: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]);
  114. break;
  115. case 9: this.$ = $$[$0];
  116. break;
  117. case 10: this.$ = $$[$0];
  118. break;
  119. case 11: this.$ = new yy.ContentNode($$[$0]);
  120. break;
  121. case 12: this.$ = new yy.CommentNode($$[$0]);
  122. break;
  123. case 13: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
  124. break;
  125. case 14: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
  126. break;
  127. case 15: this.$ = $$[$0-1];
  128. break;
  129. case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
  130. break;
  131. case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true);
  132. break;
  133. case 18: this.$ = new yy.PartialNode($$[$0-1]);
  134. break;
  135. case 19: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]);
  136. break;
  137. case 20:
  138. break;
  139. case 21: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]];
  140. break;
  141. case 22: this.$ = [[$$[$0-1]].concat($$[$0]), null];
  142. break;
  143. case 23: this.$ = [[$$[$0-1]], $$[$0]];
  144. break;
  145. case 24: this.$ = [[$$[$0]], null];
  146. break;
  147. case 25: this.$ = [[new yy.DataNode($$[$0])], null];
  148. break;
  149. case 26: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
  150. break;
  151. case 27: this.$ = [$$[$0]];
  152. break;
  153. case 28: this.$ = $$[$0];
  154. break;
  155. case 29: this.$ = new yy.StringNode($$[$0]);
  156. break;
  157. case 30: this.$ = new yy.IntegerNode($$[$0]);
  158. break;
  159. case 31: this.$ = new yy.BooleanNode($$[$0]);
  160. break;
  161. case 32: this.$ = new yy.DataNode($$[$0]);
  162. break;
  163. case 33: this.$ = new yy.HashNode($$[$0]);
  164. break;
  165. case 34: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
  166. break;
  167. case 35: this.$ = [$$[$0]];
  168. break;
  169. case 36: this.$ = [$$[$0-2], $$[$0]];
  170. break;
  171. case 37: this.$ = [$$[$0-2], new yy.StringNode($$[$0])];
  172. break;
  173. case 38: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])];
  174. break;
  175. case 39: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])];
  176. break;
  177. case 40: this.$ = [$$[$0-2], new yy.DataNode($$[$0])];
  178. break;
  179. case 41: this.$ = new yy.IdNode($$[$0]);
  180. break;
  181. case 42: $$[$0-2].push($$[$0]); this.$ = $$[$0-2];
  182. break;
  183. case 43: this.$ = [$$[$0]];
  184. break;
  185. }
  186. },
  187. 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,27:[1,24],34:[1,26],36:25},{17:27,21:23,27:[1,24],34:[1,26],36:25},{17:28,21:23,27:[1,24],34:[1,26],36:25},{17:29,21:23,27:[1,24],34:[1,26],36:25},{21:30,34:[1,26],36:25},{1:[2,1]},{6:31,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,32],21:23,27:[1,24],34:[1,26],36:25},{10:33,20:[1,34]},{10:35,20:[1,34]},{18:[1,36]},{18:[2,24],21:41,25:37,26:38,27:[1,45],28:39,29:[1,42],30:[1,43],31:[1,44],32:40,33:46,34:[1,47],36:25},{18:[2,25]},{18:[2,41],27:[2,41],29:[2,41],30:[2,41],31:[2,41],34:[2,41],37:[1,48]},{18:[2,43],27:[2,43],29:[2,43],30:[2,43],31:[2,43],34:[2,43],37:[2,43]},{18:[1,49]},{18:[1,50]},{18:[1,51]},{18:[1,52],21:53,34:[1,26],36:25},{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:54,34:[1,26],36:25},{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:41,26:55,27:[1,45],28:56,29:[1,42],30:[1,43],31:[1,44],32:40,33:46,34:[1,47],36:25},{18:[2,23]},{18:[2,27],27:[2,27],29:[2,27],30:[2,27],31:[2,27],34:[2,27]},{18:[2,33],33:57,34:[1,58]},{18:[2,28],27:[2,28],29:[2,28],30:[2,28],31:[2,28],34:[2,28]},{18:[2,29],27:[2,29],29:[2,29],30:[2,29],31:[2,29],34:[2,29]},{18:[2,30],27:[2,30],29:[2,30],30:[2,30],31:[2,30],34:[2,30]},{18:[2,31],27:[2,31],29:[2,31],30:[2,31],31:[2,31],34:[2,31]},{18:[2,32],27:[2,32],29:[2,32],30:[2,32],31:[2,32],34:[2,32]},{18:[2,35],34:[2,35]},{18:[2,43],27:[2,43],29:[2,43],30:[2,43],31:[2,43],34:[2,43],35:[1,59],37:[2,43]},{34:[1,60]},{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,61]},{18:[1,62]},{18:[2,21]},{18:[2,26],27:[2,26],29:[2,26],30:[2,26],31:[2,26],34:[2,26]},{18:[2,34],34:[2,34]},{35:[1,59]},{21:63,27:[1,67],29:[1,64],30:[1,65],31:[1,66],34:[1,26],36:25},{18:[2,42],27:[2,42],29:[2,42],30:[2,42],31:[2,42],34:[2,42],37:[2,42]},{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,36],34:[2,36]},{18:[2,37],34:[2,37]},{18:[2,38],34:[2,38]},{18:[2,39],34:[2,39]},{18:[2,40],34:[2,40]}],
  188. defaultActions: {16:[2,1],24:[2,25],38:[2,23],55:[2,21]},
  189. parseError: function parseError(str, hash) {
  190. throw new Error(str);
  191. },
  192. parse: function parse(input) {
  193. var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
  194. this.lexer.setInput(input);
  195. this.lexer.yy = this.yy;
  196. this.yy.lexer = this.lexer;
  197. this.yy.parser = this;
  198. if (typeof this.lexer.yylloc == "undefined")
  199. this.lexer.yylloc = {};
  200. var yyloc = this.lexer.yylloc;
  201. lstack.push(yyloc);
  202. var ranges = this.lexer.options && this.lexer.options.ranges;
  203. if (typeof this.yy.parseError === "function")
  204. this.parseError = this.yy.parseError;
  205. function popStack(n) {
  206. stack.length = stack.length - 2 * n;
  207. vstack.length = vstack.length - n;
  208. lstack.length = lstack.length - n;
  209. }
  210. function lex() {
  211. var token;
  212. token = self.lexer.lex() || 1;
  213. if (typeof token !== "number") {
  214. token = self.symbols_[token] || token;
  215. }
  216. return token;
  217. }
  218. var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
  219. while (true) {
  220. state = stack[stack.length - 1];
  221. if (this.defaultActions[state]) {
  222. action = this.defaultActions[state];
  223. } else {
  224. if (symbol === null || typeof symbol == "undefined") {
  225. symbol = lex();
  226. }
  227. action = table[state] && table[state][symbol];
  228. }
  229. if (typeof action === "undefined" || !action.length || !action[0]) {
  230. var errStr = "";
  231. if (!recovering) {
  232. expected = [];
  233. for (p in table[state])
  234. if (this.terminals_[p] && p > 2) {
  235. expected.push("'" + this.terminals_[p] + "'");
  236. }
  237. if (this.lexer.showPosition) {
  238. errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
  239. } else {
  240. errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
  241. }
  242. this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
  243. }
  244. }
  245. if (action[0] instanceof Array && action.length > 1) {
  246. throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
  247. }
  248. switch (action[0]) {
  249. case 1:
  250. stack.push(symbol);
  251. vstack.push(this.lexer.yytext);
  252. lstack.push(this.lexer.yylloc);
  253. stack.push(action[1]);
  254. symbol = null;
  255. if (!preErrorSymbol) {
  256. yyleng = this.lexer.yyleng;
  257. yytext = this.lexer.yytext;
  258. yylineno = this.lexer.yylineno;
  259. yyloc = this.lexer.yylloc;
  260. if (recovering > 0)
  261. recovering--;
  262. } else {
  263. symbol = preErrorSymbol;
  264. preErrorSymbol = null;
  265. }
  266. break;
  267. case 2:
  268. len = this.productions_[action[1]][1];
  269. yyval.$ = vstack[vstack.length - len];
  270. yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
  271. if (ranges) {
  272. yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
  273. }
  274. r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
  275. if (typeof r !== "undefined") {
  276. return r;
  277. }
  278. if (len) {
  279. stack = stack.slice(0, -1 * len * 2);
  280. vstack = vstack.slice(0, -1 * len);
  281. lstack = lstack.slice(0, -1 * len);
  282. }
  283. stack.push(this.productions_[action[1]][0]);
  284. vstack.push(yyval.$);
  285. lstack.push(yyval._$);
  286. newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
  287. stack.push(newState);
  288. break;
  289. case 3:
  290. return true;
  291. }
  292. }
  293. return true;
  294. }
  295. };
  296. /* Jison generated lexer */
  297. var lexer = (function(){
  298. var lexer = ({EOF:1,
  299. parseError:function parseError(str, hash) {
  300. if (this.yy.parser) {
  301. this.yy.parser.parseError(str, hash);
  302. } else {
  303. throw new Error(str);
  304. }
  305. },
  306. setInput:function (input) {
  307. this._input = input;
  308. this._more = this._less = this.done = false;
  309. this.yylineno = this.yyleng = 0;
  310. this.yytext = this.matched = this.match = '';
  311. this.conditionStack = ['INITIAL'];
  312. this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
  313. if (this.options.ranges) this.yylloc.range = [0,0];
  314. this.offset = 0;
  315. return this;
  316. },
  317. input:function () {
  318. var ch = this._input[0];
  319. this.yytext += ch;
  320. this.yyleng++;
  321. this.offset++;
  322. this.match += ch;
  323. this.matched += ch;
  324. var lines = ch.match(/(?:\r\n?|\n).*/g);
  325. if (lines) {
  326. this.yylineno++;
  327. this.yylloc.last_line++;
  328. } else {
  329. this.yylloc.last_column++;
  330. }
  331. if (this.options.ranges) this.yylloc.range[1]++;
  332. this._input = this._input.slice(1);
  333. return ch;
  334. },
  335. unput:function (ch) {
  336. var len = ch.length;
  337. var lines = ch.split(/(?:\r\n?|\n)/g);
  338. this._input = ch + this._input;
  339. this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
  340. //this.yyleng -= len;
  341. this.offset -= len;
  342. var oldLines = this.match.split(/(?:\r\n?|\n)/g);
  343. this.match = this.match.substr(0, this.match.length-1);
  344. this.matched = this.matched.substr(0, this.matched.length-1);
  345. if (lines.length-1) this.yylineno -= lines.length-1;
  346. var r = this.yylloc.range;
  347. this.yylloc = {first_line: this.yylloc.first_line,
  348. last_line: this.yylineno+1,
  349. first_column: this.yylloc.first_column,
  350. last_column: lines ?
  351. (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
  352. this.yylloc.first_column - len
  353. };
  354. if (this.options.ranges) {
  355. this.yylloc.range = [r[0], r[0] + this.yyleng - len];
  356. }
  357. return this;
  358. },
  359. more:function () {
  360. this._more = true;
  361. return this;
  362. },
  363. less:function (n) {
  364. this.unput(this.match.slice(n));
  365. },
  366. pastInput:function () {
  367. var past = this.matched.substr(0, this.matched.length - this.match.length);
  368. return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
  369. },
  370. upcomingInput:function () {
  371. var next = this.match;
  372. if (next.length < 20) {
  373. next += this._input.substr(0, 20-next.length);
  374. }
  375. return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
  376. },
  377. showPosition:function () {
  378. var pre = this.pastInput();
  379. var c = new Array(pre.length + 1).join("-");
  380. return pre + this.upcomingInput() + "\n" + c+"^";
  381. },
  382. next:function () {
  383. if (this.done) {
  384. return this.EOF;
  385. }
  386. if (!this._input) this.done = true;
  387. var token,
  388. match,
  389. tempMatch,
  390. index,
  391. col,
  392. lines;
  393. if (!this._more) {
  394. this.yytext = '';
  395. this.match = '';
  396. }
  397. var rules = this._currentRules();
  398. for (var i=0;i < rules.length; i++) {
  399. tempMatch = this._input.match(this.rules[rules[i]]);
  400. if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
  401. match = tempMatch;
  402. index = i;
  403. if (!this.options.flex) break;
  404. }
  405. }
  406. if (match) {
  407. lines = match[0].match(/(?:\r\n?|\n).*/g);
  408. if (lines) this.yylineno += lines.length;
  409. this.yylloc = {first_line: this.yylloc.last_line,
  410. last_line: this.yylineno+1,
  411. first_column: this.yylloc.last_column,
  412. last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
  413. this.yytext += match[0];
  414. this.match += match[0];
  415. this.matches = match;
  416. this.yyleng = this.yytext.length;
  417. if (this.options.ranges) {
  418. this.yylloc.range = [this.offset, this.offset += this.yyleng];
  419. }
  420. this._more = false;
  421. this._input = this._input.slice(match[0].length);
  422. this.matched += match[0];
  423. token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
  424. if (this.done && this._input) this.done = false;
  425. if (token) return token;
  426. else return;
  427. }
  428. if (this._input === "") {
  429. return this.EOF;
  430. } else {
  431. return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
  432. {text: "", token: null, line: this.yylineno});
  433. }
  434. },
  435. lex:function lex() {
  436. var r = this.next();
  437. if (typeof r !== 'undefined') {
  438. return r;
  439. } else {
  440. return this.lex();
  441. }
  442. },
  443. begin:function begin(condition) {
  444. this.conditionStack.push(condition);
  445. },
  446. popState:function popState() {
  447. return this.conditionStack.pop();
  448. },
  449. _currentRules:function _currentRules() {
  450. return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
  451. },
  452. topState:function () {
  453. return this.conditionStack[this.conditionStack.length-2];
  454. },
  455. pushState:function begin(condition) {
  456. this.begin(condition);
  457. }});
  458. lexer.options = {};
  459. lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
  460. var YYSTATE=YY_START
  461. switch($avoiding_name_collisions) {
  462. case 0:
  463. if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
  464. if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu");
  465. if(yy_.yytext) return 14;
  466. break;
  467. case 1: return 14;
  468. break;
  469. case 2:
  470. if(yy_.yytext.slice(-1) !== "\\") this.popState();
  471. if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1);
  472. return 14;
  473. break;
  474. case 3: return 24;
  475. break;
  476. case 4: return 16;
  477. break;
  478. case 5: return 20;
  479. break;
  480. case 6: return 19;
  481. break;
  482. case 7: return 19;
  483. break;
  484. case 8: return 23;
  485. break;
  486. case 9: return 23;
  487. break;
  488. case 10: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
  489. break;
  490. case 11: return 22;
  491. break;
  492. case 12: return 35;
  493. break;
  494. case 13: return 34;
  495. break;
  496. case 14: return 34;
  497. break;
  498. case 15: return 37;
  499. break;
  500. case 16: /*ignore whitespace*/
  501. break;
  502. case 17: this.popState(); return 18;
  503. break;
  504. case 18: this.popState(); return 18;
  505. break;
  506. case 19: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 29;
  507. break;
  508. case 20: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 29;
  509. break;
  510. case 21: yy_.yytext = yy_.yytext.substr(1); return 27;
  511. break;
  512. case 22: return 31;
  513. break;
  514. case 23: return 31;
  515. break;
  516. case 24: return 30;
  517. break;
  518. case 25: return 34;
  519. break;
  520. case 26: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 34;
  521. break;
  522. case 27: return 'INVALID';
  523. break;
  524. case 28: return 5;
  525. break;
  526. }
  527. };
  528. lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[} ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
  529. lexer.conditions = {"mu":{"rules":[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],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"INITIAL":{"rules":[0,1,28],"inclusive":true}};
  530. return lexer;})()
  531. parser.lexer = lexer;
  532. function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
  533. return new Parser;
  534. })();
  535. if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
  536. exports.parser = handlebars;
  537. exports.Parser = handlebars.Parser;
  538. exports.parse = function () { return handlebars.parse.apply(handlebars, arguments); }
  539. exports.main = function commonjsMain(args) {
  540. if (!args[1])
  541. throw new Error('Usage: '+args[0]+' FILE');
  542. var source, cwd;
  543. if (typeof process !== 'undefined') {
  544. source = require('fs').readFileSync(require('path').resolve(args[1]), "utf8");
  545. } else {
  546. source = require("file").path(require("file").cwd()).join(args[1]).read({charset: "utf-8"});
  547. }
  548. return exports.parser.parse(source);
  549. }
  550. if (typeof module !== 'undefined' && require.main === module) {
  551. exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
  552. }
  553. };
  554. ;
  555. // lib/handlebars/compiler/base.js
  556. Handlebars.Parser = handlebars;
  557. Handlebars.parse = function(string) {
  558. Handlebars.Parser.yy = Handlebars.AST;
  559. return Handlebars.Parser.parse(string);
  560. };
  561. Handlebars.print = function(ast) {
  562. return new Handlebars.PrintVisitor().accept(ast);
  563. };
  564. Handlebars.logger = {
  565. DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
  566. // override in the host environment
  567. log: function(level, str) {}
  568. };
  569. Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
  570. ;
  571. // lib/handlebars/compiler/ast.js
  572. (function() {
  573. Handlebars.AST = {};
  574. Handlebars.AST.ProgramNode = function(statements, inverse) {
  575. this.type = "program";
  576. this.statements = statements;
  577. if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
  578. };
  579. Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
  580. this.type = "mustache";
  581. this.escaped = !unescaped;
  582. this.hash = hash;
  583. var id = this.id = rawParams[0];
  584. var params = this.params = rawParams.slice(1);
  585. // a mustache is an eligible helper if:
  586. // * its id is simple (a single part, not `this` or `..`)
  587. var eligibleHelper = this.eligibleHelper = id.isSimple;
  588. // a mustache is definitely a helper if:
  589. // * it is an eligible helper, and
  590. // * it has at least one parameter or hash segment
  591. this.isHelper = eligibleHelper && (params.length || hash);
  592. // if a mustache is an eligible helper but not a definite
  593. // helper, it is ambiguous, and will be resolved in a later
  594. // pass or at runtime.
  595. };
  596. Handlebars.AST.PartialNode = function(id, context) {
  597. this.type = "partial";
  598. // TODO: disallow complex IDs
  599. this.id = id;
  600. this.context = context;
  601. };
  602. var verifyMatch = function(open, close) {
  603. if(open.original !== close.original) {
  604. throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
  605. }
  606. };
  607. Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
  608. verifyMatch(mustache.id, close);
  609. this.type = "block";
  610. this.mustache = mustache;
  611. this.program = program;
  612. this.inverse = inverse;
  613. if (this.inverse && !this.program) {
  614. this.isInverse = true;
  615. }
  616. };
  617. Handlebars.AST.ContentNode = function(string) {
  618. this.type = "content";
  619. this.string = string;
  620. };
  621. Handlebars.AST.HashNode = function(pairs) {
  622. this.type = "hash";
  623. this.pairs = pairs;
  624. };
  625. Handlebars.AST.IdNode = function(parts) {
  626. this.type = "ID";
  627. this.original = parts.join(".");
  628. var dig = [], depth = 0;
  629. for(var i=0,l=parts.length; i<l; i++) {
  630. var part = parts[i];
  631. if(part === "..") { depth++; }
  632. else if(part === "." || part === "this") { this.isScoped = true; }
  633. else { dig.push(part); }
  634. }
  635. this.parts = dig;
  636. this.string = dig.join('.');
  637. this.depth = depth;
  638. // an ID is simple if it only has one part, and that part is not
  639. // `..` or `this`.
  640. this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
  641. };
  642. Handlebars.AST.DataNode = function(id) {
  643. this.type = "DATA";
  644. this.id = id;
  645. };
  646. Handlebars.AST.StringNode = function(string) {
  647. this.type = "STRING";
  648. this.string = string;
  649. };
  650. Handlebars.AST.IntegerNode = function(integer) {
  651. this.type = "INTEGER";
  652. this.integer = integer;
  653. };
  654. Handlebars.AST.BooleanNode = function(bool) {
  655. this.type = "BOOLEAN";
  656. this.bool = bool;
  657. };
  658. Handlebars.AST.CommentNode = function(comment) {
  659. this.type = "comment";
  660. this.comment = comment;
  661. };
  662. })();;
  663. // lib/handlebars/utils.js
  664. Handlebars.Exception = function(message) {
  665. var tmp = Error.prototype.constructor.apply(this, arguments);
  666. for (var p in tmp) {
  667. if (tmp.hasOwnProperty(p)) { this[p] = tmp[p]; }
  668. }
  669. this.message = tmp.message;
  670. };
  671. Handlebars.Exception.prototype = new Error();
  672. // Build out our basic SafeString type
  673. Handlebars.SafeString = function(string) {
  674. this.string = string;
  675. };
  676. Handlebars.SafeString.prototype.toString = function() {
  677. return this.string.toString();
  678. };
  679. (function() {
  680. var escape = {
  681. "&": "&amp;",
  682. "<": "&lt;",
  683. ">": "&gt;",
  684. '"': "&quot;",
  685. "'": "&#x27;",
  686. "`": "&#x60;"
  687. };
  688. var badChars = /[&<>"'`]/g;
  689. var possible = /[&<>"'`]/;
  690. var escapeChar = function(chr) {
  691. return escape[chr] || "&amp;";
  692. };
  693. Handlebars.Utils = {
  694. escapeExpression: function(string) {
  695. // don't escape SafeStrings, since they're already safe
  696. if (string instanceof Handlebars.SafeString) {
  697. return string.toString();
  698. } else if (string == null || string === false) {
  699. return "";
  700. }
  701. if(!possible.test(string)) { return string; }
  702. return string.replace(badChars, escapeChar);
  703. },
  704. isEmpty: function(value) {
  705. if (typeof value === "undefined") {
  706. return true;
  707. } else if (value === null) {
  708. return true;
  709. } else if (value === false) {
  710. return true;
  711. } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
  712. return true;
  713. } else {
  714. return false;
  715. }
  716. }
  717. };
  718. })();;
  719. // lib/handlebars/compiler/compiler.js
  720. /*jshint eqnull:true*/
  721. Handlebars.Compiler = function() {};
  722. Handlebars.JavaScriptCompiler = function() {};
  723. (function(Compiler, JavaScriptCompiler) {
  724. // the foundHelper register will disambiguate helper lookup from finding a
  725. // function in a context. This is necessary for mustache compatibility, which
  726. // requires that context functions in blocks are evaluated by blockHelperMissing,
  727. // and then proceed as if the resulting value was provided to blockHelperMissing.
  728. Compiler.prototype = {
  729. compiler: Compiler,
  730. disassemble: function() {
  731. var opcodes = this.opcodes, opcode, out = [], params, param;
  732. for (var i=0, l=opcodes.length; i<l; i++) {
  733. opcode = opcodes[i];
  734. if (opcode.opcode === 'DECLARE') {
  735. out.push("DECLARE " + opcode.name + "=" + opcode.value);
  736. } else {
  737. params = [];
  738. for (var j=0; j<opcode.args.length; j++) {
  739. param = opcode.args[j];
  740. if (typeof param === "string") {
  741. param = "\"" + param.replace("\n", "\\n") + "\"";
  742. }
  743. params.push(param);
  744. }
  745. out.push(opcode.opcode + " " + params.join(" "));
  746. }
  747. }
  748. return out.join("\n");
  749. },
  750. guid: 0,
  751. compile: function(program, options) {
  752. this.children = [];
  753. this.depths = {list: []};
  754. this.options = options;
  755. // These changes will propagate to the other compiler components
  756. var knownHelpers = this.options.knownHelpers;
  757. this.options.knownHelpers = {
  758. 'helperMissing': true,
  759. 'blockHelperMissing': true,
  760. 'each': true,
  761. 'if': true,
  762. 'unless': true,
  763. 'with': true,
  764. 'log': true
  765. };
  766. if (knownHelpers) {
  767. for (var name in knownHelpers) {
  768. this.options.knownHelpers[name] = knownHelpers[name];
  769. }
  770. }
  771. return this.program(program);
  772. },
  773. accept: function(node) {
  774. return this[node.type](node);
  775. },
  776. program: function(program) {
  777. var statements = program.statements, statement;
  778. this.opcodes = [];
  779. for(var i=0, l=statements.length; i<l; i++) {
  780. statement = statements[i];
  781. this[statement.type](statement);
  782. }
  783. this.isSimple = l === 1;
  784. this.depths.list = this.depths.list.sort(function(a, b) {
  785. return a - b;
  786. });
  787. return this;
  788. },
  789. compileProgram: function(program) {
  790. var result = new this.compiler().compile(program, this.options);
  791. var guid = this.guid++, depth;
  792. this.usePartial = this.usePartial || result.usePartial;
  793. this.children[guid] = result;
  794. for(var i=0, l=result.depths.list.length; i<l; i++) {
  795. depth = result.depths.list[i];
  796. if(depth < 2) { continue; }
  797. else { this.addDepth(depth - 1); }
  798. }
  799. return guid;
  800. },
  801. block: function(block) {
  802. var mustache = block.mustache,
  803. program = block.program,
  804. inverse = block.inverse;
  805. if (program) {
  806. program = this.compileProgram(program);
  807. }
  808. if (inverse) {
  809. inverse = this.compileProgram(inverse);
  810. }
  811. var type = this.classifyMustache(mustache);
  812. if (type === "helper") {
  813. this.helperMustache(mustache, program, inverse);
  814. } else if (type === "simple") {
  815. this.simpleMustache(mustache);
  816. // now that the simple mustache is resolved, we need to
  817. // evaluate it by executing `blockHelperMissing`
  818. this.opcode('pushProgram', program);
  819. this.opcode('pushProgram', inverse);
  820. this.opcode('pushLiteral', '{}');
  821. this.opcode('blockValue');
  822. } else {
  823. this.ambiguousMustache(mustache, program, inverse);
  824. // now that the simple mustache is resolved, we need to
  825. // evaluate it by executing `blockHelperMissing`
  826. this.opcode('pushProgram', program);
  827. this.opcode('pushProgram', inverse);
  828. this.opcode('pushLiteral', '{}');
  829. this.opcode('ambiguousBlockValue');
  830. }
  831. this.opcode('append');
  832. },
  833. hash: function(hash) {
  834. var pairs = hash.pairs, pair, val;
  835. this.opcode('push', '{}');
  836. for(var i=0, l=pairs.length; i<l; i++) {
  837. pair = pairs[i];
  838. val = pair[1];
  839. this.accept(val);
  840. this.opcode('assignToHash', pair[0]);
  841. }
  842. },
  843. partial: function(partial) {
  844. var id = partial.id;
  845. this.usePartial = true;
  846. if(partial.context) {
  847. this.ID(partial.context);
  848. } else {
  849. this.opcode('push', 'depth0');
  850. }
  851. this.opcode('invokePartial', id.original);
  852. this.opcode('append');
  853. },
  854. content: function(content) {
  855. this.opcode('appendContent', content.string);
  856. },
  857. mustache: function(mustache) {
  858. var options = this.options;
  859. var type = this.classifyMustache(mustache);
  860. if (type === "simple") {
  861. this.simpleMustache(mustache);
  862. } else if (type === "helper") {
  863. this.helperMustache(mustache);
  864. } else {
  865. this.ambiguousMustache(mustache);
  866. }
  867. if(mustache.escaped && !options.noEscape) {
  868. this.opcode('appendEscaped');
  869. } else {
  870. this.opcode('append');
  871. }
  872. },
  873. ambiguousMustache: function(mustache, program, inverse) {
  874. var id = mustache.id, name = id.parts[0];
  875. this.opcode('getContext', id.depth);
  876. this.opcode('pushProgram', program);
  877. this.opcode('pushProgram', inverse);
  878. this.opcode('invokeAmbiguous', name);
  879. },
  880. simpleMustache: function(mustache, program, inverse) {
  881. var id = mustache.id;
  882. if (id.type === 'DATA') {
  883. this.DATA(id);
  884. } else if (id.parts.length) {
  885. this.ID(id);
  886. } else {
  887. // Simplified ID for `this`
  888. this.addDepth(id.depth);
  889. this.opcode('getContext', id.depth);
  890. this.opcode('pushContext');
  891. }
  892. this.opcode('resolvePossibleLambda');
  893. },
  894. helperMustache: function(mustache, program, inverse) {
  895. var params = this.setupFullMustacheParams(mustache, program, inverse),
  896. name = mustache.id.parts[0];
  897. if (this.options.knownHelpers[name]) {
  898. this.opcode('invokeKnownHelper', params.length, name);
  899. } else if (this.knownHelpersOnly) {
  900. throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name);
  901. } else {
  902. this.opcode('invokeHelper', params.length, name);
  903. }
  904. },
  905. ID: function(id) {
  906. this.addDepth(id.depth);
  907. this.opcode('getContext', id.depth);
  908. var name = id.parts[0];
  909. if (!name) {
  910. this.opcode('pushContext');
  911. } else {
  912. this.opcode('lookupOnContext', id.parts[0]);
  913. }
  914. for(var i=1, l=id.parts.length; i<l; i++) {
  915. this.opcode('lookup', id.parts[i]);
  916. }
  917. },
  918. DATA: function(data) {
  919. this.options.data = true;
  920. this.opcode('lookupData', data.id);
  921. },
  922. STRING: function(string) {
  923. this.opcode('pushString', string.string);
  924. },
  925. INTEGER: function(integer) {
  926. this.opcode('pushLiteral', integer.integer);
  927. },
  928. BOOLEAN: function(bool) {
  929. this.opcode('pushLiteral', bool.bool);
  930. },
  931. comment: function() {},
  932. // HELPERS
  933. opcode: function(name) {
  934. this.opcodes.push({ opcode: name, args: [].slice.call(arguments, 1) });
  935. },
  936. declare: function(name, value) {
  937. this.opcodes.push({ opcode: 'DECLARE', name: name, value: value });
  938. },
  939. addDepth: function(depth) {
  940. if(isNaN(depth)) { throw new Error("EWOT"); }
  941. if(depth === 0) { return; }
  942. if(!this.depths[depth]) {
  943. this.depths[depth] = true;
  944. this.depths.list.push(depth);
  945. }
  946. },
  947. classifyMustache: function(mustache) {
  948. var isHelper = mustache.isHelper;
  949. var isEligible = mustache.eligibleHelper;
  950. var options = this.options;
  951. // if ambiguous, we can possibly resolve the ambiguity now
  952. if (isEligible && !isHelper) {
  953. var name = mustache.id.parts[0];
  954. if (options.knownHelpers[name]) {
  955. isHelper = true;
  956. } else if (options.knownHelpersOnly) {
  957. isEligible = false;
  958. }
  959. }
  960. if (isHelper) { return "helper"; }
  961. else if (isEligible) { return "ambiguous"; }
  962. else { return "simple"; }
  963. },
  964. pushParams: function(params) {
  965. var i = params.length, param;
  966. while(i--) {
  967. param = params[i];
  968. if(this.options.stringParams) {
  969. if(param.depth) {
  970. this.addDepth(param.depth);
  971. }
  972. this.opcode('getContext', param.depth || 0);
  973. this.opcode('pushStringParam', param.string);
  974. } else {
  975. this[param.type](param);
  976. }
  977. }
  978. },
  979. setupMustacheParams: function(mustache) {
  980. var params = mustache.params;
  981. this.pushParams(params);
  982. if(mustache.hash) {
  983. this.hash(mustache.hash);
  984. } else {
  985. this.opcode('pushLiteral', '{}');
  986. }
  987. return params;
  988. },
  989. // this will replace setupMustacheParams when we're done
  990. setupFullMustacheParams: function(mustache, program, inverse) {
  991. var params = mustache.params;
  992. this.pushParams(params);
  993. this.opcode('pushProgram', program);
  994. this.opcode('pushProgram', inverse);
  995. if(mustache.hash) {
  996. this.hash(mustache.hash);
  997. } else {
  998. this.opcode('pushLiteral', '{}');
  999. }
  1000. return params;
  1001. }
  1002. };
  1003. var Literal = function(value) {
  1004. this.value = value;
  1005. };
  1006. JavaScriptCompiler.prototype = {
  1007. // PUBLIC API: You can override these methods in a subclass to provide
  1008. // alternative compiled forms for name lookup and buffering semantics
  1009. nameLookup: function(parent, name, type) {
  1010. if (/^[0-9]+$/.test(name)) {
  1011. return parent + "[" + name + "]";
  1012. } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
  1013. return parent + "." + name;
  1014. }
  1015. else {
  1016. return parent + "['" + name + "']";
  1017. }
  1018. },
  1019. appendToBuffer: function(string) {
  1020. if (this.environment.isSimple) {
  1021. return "return " + string + ";";
  1022. } else {
  1023. return "buffer += " + string + ";";
  1024. }
  1025. },
  1026. initializeBuffer: function() {
  1027. return this.quotedString("");
  1028. },
  1029. namespace: "Handlebars",
  1030. // END PUBLIC API
  1031. compile: function(environment, options, context, asObject) {
  1032. this.environment = environment;
  1033. this.options = options || {};
  1034. Handlebars.log(Handlebars.logger.DEBUG, this.environment.disassemble() + "\n\n");
  1035. this.name = this.environment.name;
  1036. this.isChild = !!context;
  1037. this.context = context || {
  1038. programs: [],
  1039. aliases: { }
  1040. };
  1041. this.preamble();
  1042. this.stackSlot = 0;
  1043. this.stackVars = [];
  1044. this.registers = { list: [] };
  1045. this.compileStack = [];
  1046. this.compileChildren(environment, options);
  1047. var opcodes = environment.opcodes, opcode;
  1048. this.i = 0;
  1049. for(l=opcodes.length; this.i<l; this.i++) {
  1050. opcode = opcodes[this.i];
  1051. if(opcode.opcode === 'DECLARE') {
  1052. this[opcode.name] = opcode.value;
  1053. } else {
  1054. this[opcode.opcode].apply(this, opcode.args);
  1055. }
  1056. }
  1057. return this.createFunctionContext(asObject);
  1058. },
  1059. nextOpcode: function() {
  1060. var opcodes = this.environment.opcodes, opcode = opcodes[this.i + 1];
  1061. return opcodes[this.i + 1];
  1062. },
  1063. eat: function(opcode) {
  1064. this.i = this.i + 1;
  1065. },
  1066. preamble: function() {
  1067. var out = [];
  1068. if (!this.isChild) {
  1069. var namespace = this.namespace;
  1070. var copies = "helpers = helpers || " + namespace + ".helpers;";
  1071. if (this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
  1072. if (this.options.data) { copies = copies + " data = data || {};"; }
  1073. out.push(copies);
  1074. } else {
  1075. out.push('');
  1076. }
  1077. if (!this.environment.isSimple) {
  1078. out.push(", buffer = " + this.initializeBuffer());
  1079. } else {
  1080. out.push("");
  1081. }
  1082. // track the last context pushed into place to allow skipping the
  1083. // getContext opcode when it would be a noop
  1084. this.lastContext = 0;
  1085. this.source = out;
  1086. },
  1087. createFunctionContext: function(asObject) {
  1088. var locals = this.stackVars.concat(this.registers.list);
  1089. if(locals.length > 0) {
  1090. this.source[1] = this.source[1] + ", " + locals.join(", ");
  1091. }
  1092. // Generate minimizer alias mappings
  1093. if (!this.isChild) {
  1094. var aliases = [];
  1095. for (var alias in this.context.aliases) {
  1096. this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
  1097. }
  1098. }
  1099. if (this.source[1]) {
  1100. this.source[1] = "var " + this.source[1].substring(2) + ";";
  1101. }
  1102. // Merge children
  1103. if (!this.isChild) {
  1104. this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
  1105. }
  1106. if (!this.environment.isSimple) {
  1107. this.source.push("return buffer;");
  1108. }
  1109. var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
  1110. for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
  1111. params.push("depth" + this.environment.depths.list[i]);
  1112. }
  1113. if (asObject) {
  1114. params.push(this.source.join("\n "));
  1115. return Function.apply(this, params);
  1116. } else {
  1117. var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + this.source.join("\n ") + '}';
  1118. Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
  1119. return functionSource;
  1120. }
  1121. },
  1122. // [blockValue]
  1123. //
  1124. // On stack, before: hash, inverse, program, value
  1125. // On stack, after: return value of blockHelperMissing
  1126. //
  1127. // The purpose of this opcode is to take a block of the form
  1128. // `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and
  1129. // replace it on the stack with the result of properly
  1130. // invoking blockHelperMissing.
  1131. blockValue: function() {
  1132. this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
  1133. var params = ["depth0"];
  1134. this.setupParams(0, params);
  1135. this.replaceStack(function(current) {
  1136. params.splice(1, 0, current);
  1137. return current + " = blockHelperMissing.call(" + params.join(", ") + ")";
  1138. });
  1139. },
  1140. // [ambiguousBlockValue]
  1141. //
  1142. // On stack, before: hash, inverse, program, value
  1143. // Compiler value, before: lastHelper=value of last found helper, if any
  1144. // On stack, after, if no lastHelper: same as [blockValue]
  1145. // On stack, after, if lastHelper: value
  1146. ambiguousBlockValue: function() {
  1147. this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
  1148. var params = ["depth0"];
  1149. this.setupParams(0, params);
  1150. var current = this.topStack();
  1151. params.splice(1, 0, current);
  1152. this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
  1153. },
  1154. // [appendContent]
  1155. //
  1156. // On stack, before: ...
  1157. // On stack, after: ...
  1158. //
  1159. // Appends the string value of `content` to the current buffer
  1160. appendContent: function(content) {
  1161. this.source.push(this.appendToBuffer(this.quotedString(content)));
  1162. },
  1163. // [append]
  1164. //
  1165. // On stack, before: value, ...
  1166. // On stack, after: ...
  1167. //
  1168. // Coerces `value` to a String and appends it to the current buffer.
  1169. //
  1170. // If `value` is truthy, or 0, it is coerced into a string and appended
  1171. // Otherwise, the empty string is appended
  1172. append: function() {
  1173. var local = this.popStack();
  1174. this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
  1175. if (this.environment.isSimple) {
  1176. this.source.push("else { " + this.appendToBuffer("''") + " }");
  1177. }
  1178. },
  1179. // [appendEscaped]
  1180. //
  1181. // On stack, before: value, ...
  1182. // On stack, after: ...
  1183. //
  1184. // Escape `value` and append it to the buffer
  1185. appendEscaped: function() {
  1186. var opcode = this.nextOpcode(), extra = "";
  1187. this.context.aliases.escapeExpression = 'this.escapeExpression';
  1188. if(opcode && opcode.opcode === 'appendContent') {
  1189. extra = " + " + this.quotedString(opcode.args[0]);
  1190. this.eat(opcode);
  1191. }
  1192. this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
  1193. },
  1194. // [getContext]
  1195. //
  1196. // On stack, before: ...
  1197. // On stack, after: ...
  1198. // Compiler value, after: lastContext=depth
  1199. //
  1200. // Set the value of the `lastContext` compiler value to the depth
  1201. getContext: function(depth) {
  1202. if(this.lastContext !== depth) {
  1203. this.lastContext = depth;
  1204. }
  1205. },
  1206. // [lookupOnContext]
  1207. //
  1208. // On stack, before: ...
  1209. // On stack, after: currentContext[name], ...
  1210. //
  1211. // Looks up the value of `name` on the current context and pushes
  1212. // it onto the stack.
  1213. lookupOnContext: function(name) {
  1214. this.pushStack(this.nameLookup('depth' + this.lastContext, name, 'context'));
  1215. },
  1216. // [pushContext]
  1217. //
  1218. // On stack, before: ...
  1219. // On stack, after: currentContext, ...
  1220. //
  1221. // Pushes the value of the current context onto the stack.
  1222. pushContext: function() {
  1223. this.pushStackLiteral('depth' + this.lastContext);
  1224. },
  1225. // [resolvePossibleLambda]
  1226. //
  1227. // On stack, before: value, ...
  1228. // On stack, after: resolved value, ...
  1229. //
  1230. // If the `value` is a lambda, replace it on the stack by
  1231. // the return value of the lambda
  1232. resolvePossibleLambda: function() {
  1233. this.context.aliases.functionType = '"function"';
  1234. this.replaceStack(function(current) {
  1235. return "typeof " + current + " === functionType ? " + current + "() : " + current;
  1236. });
  1237. },
  1238. // [lookup]
  1239. //
  1240. // On stack, before: value, ...
  1241. // On stack, after: value[name], ...
  1242. //
  1243. // Replace the value on the stack with the result of looking
  1244. // up `name` on `value`
  1245. lookup: function(name) {
  1246. this.replaceStack(function(current) {
  1247. return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context');
  1248. });
  1249. },
  1250. // [lookupData]
  1251. //
  1252. // On stack, before: ...
  1253. // On stack, after: data[id], ...
  1254. //
  1255. // Push the result of looking up `id` on the current data
  1256. lookupData: function(id) {
  1257. this.pushStack(this.nameLookup('data', id, 'data'));
  1258. },
  1259. // [pushStringParam]
  1260. //
  1261. // On stack, before: ...
  1262. // On stack, after: string, currentContext, ...
  1263. //
  1264. // This opcode is designed for use in string mode, which
  1265. // provides the string value of a parameter along with its
  1266. // depth rather than resolving it immediately.
  1267. pushStringParam: function(string) {
  1268. this.pushStackLiteral('depth' + this.lastContext);
  1269. this.pushString(string);
  1270. },
  1271. // [pushString]
  1272. //
  1273. // On stack, before: ...
  1274. // On stack, after: quotedString(string), ...
  1275. //
  1276. // Push a quoted version of `string` onto the stack
  1277. pushString: function(string) {
  1278. this.pushStackLiteral(this.quotedString(string));
  1279. },
  1280. // [push]
  1281. //
  1282. // On stack, before: ...
  1283. // On stack, after: expr, ...
  1284. //
  1285. // Push an expression onto the stack
  1286. push: function(expr) {
  1287. this.pushStack(expr);
  1288. },
  1289. // [pushLiteral]
  1290. //
  1291. // On stack, before: ...
  1292. // On stack, after: value, ...
  1293. //
  1294. // Pushes a value onto the stack. This operation prevents
  1295. // the compiler from creating a temporary variable to hold
  1296. // it.
  1297. pushLiteral: function(value) {
  1298. this.pushStackLiteral(value);
  1299. },
  1300. // [pushProgram]
  1301. //
  1302. // On stack, before: ...
  1303. // On stack, after: program(guid), ...
  1304. //
  1305. // Push a program expression onto the stack. This takes
  1306. // a compile-time guid and converts it into a runtime-accessible
  1307. // expression.
  1308. pushProgram: function(guid) {
  1309. if (guid != null) {
  1310. this.pushStackLiteral(this.programExpression(guid));
  1311. } else {
  1312. this.pushStackLiteral(null);
  1313. }
  1314. },
  1315. // [invokeHelper]
  1316. //
  1317. // On stack, before: hash, inverse, program, params..., ...
  1318. // On stack, after: result of helper invocation
  1319. //
  1320. // Pops off the helper's parameters, invokes the helper,
  1321. // and pushes the helper's return value onto the stack.
  1322. //
  1323. // If the helper is not found, `helperMissing` is called.
  1324. invokeHelper: function(paramSize, name) {
  1325. this.context.aliases.helperMissing = 'helpers.helperMissing';
  1326. var helper = this.lastHelper = this.setupHelper(paramSize, name);
  1327. this.register('foundHelper', helper.name);
  1328. this.pushStack("foundHelper ? foundHelper.call(" +
  1329. helper.callParams + ") " + ": helperMissing.call(" +
  1330. helper.helperMissingParams + ")");
  1331. },
  1332. // [invokeKnownHelper]
  1333. //
  1334. // On stack, before: hash, inverse, program, params..., ...
  1335. // On stack, after: result of helper invocation
  1336. //
  1337. // This operation is used when the helper is known to exist,
  1338. // so a `helperMissing` fallback is not required.
  1339. invokeKnownHelper: function(paramSize, name) {
  1340. var helper = this.setupHelper(paramSize, name);
  1341. this.pushStack(helper.name + ".call(" + helper.callParams + ")");
  1342. },
  1343. // [invokeAmbiguous]
  1344. //
  1345. // On stack, before: hash, inverse, program, params..., ...
  1346. // On stack, after: result of disambiguation
  1347. //
  1348. // This operation is used when an expression like `{{foo}}`
  1349. // is provided, but we don't know at compile-time whether it
  1350. // is a helper or a path.
  1351. //
  1352. // This operation emits more code than the other options,
  1353. // and can be avoided by passing the `knownHelpers` and
  1354. // `knownHelpersOnly` flags at compile-time.
  1355. invokeAmbiguous: function(name) {
  1356. this.context.aliases.functionType = '"function"';
  1357. this.pushStackLiteral('{}');
  1358. var helper = this.setupHelper(0, name);
  1359. var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
  1360. this.register('foundHelper', helperName);
  1361. var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
  1362. var nextStack = this.nextStack();
  1363. this.source.push('if (foundHelper) { ' + nextStack + ' = foundHelper.call(' + helper.callParams + '); }');
  1364. this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '() : ' + nextStack + '; }');
  1365. },
  1366. // [invokePartial]
  1367. //
  1368. // On stack, before: context, ...
  1369. // On stack after: result of partial invocation
  1370. //
  1371. // This operation pops off a context, invokes a partial with that context,
  1372. // and pushes the result of the invocation back.
  1373. invokePartial: function(name) {
  1374. var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"];
  1375. if (this.options.data) {
  1376. params.push("data");
  1377. }
  1378. this.context.aliases.self = "this";
  1379. this.pushStack("self.invokePartial(" + params.join(", ") + ");");
  1380. },
  1381. // [assignToHash]
  1382. //
  1383. // On stack, before: value, hash, ...
  1384. // On stack, after: hash, ...
  1385. //
  1386. // Pops a value and hash off the stack, assigns `hash[key] = value`
  1387. // and pushes the hash back onto the stack.
  1388. assignToHash: function(key) {
  1389. var value = this.popStack();
  1390. var hash = this.topStack();
  1391. this.source.push(hash + "['" + key + "'] = " + value + ";");
  1392. },
  1393. // HELPERS
  1394. compiler: JavaScriptCompiler,
  1395. compileChildren: function(environment, options) {
  1396. var children = environment.children, child, compiler;
  1397. for(var i=0, l=children.length; i<l; i++) {
  1398. child = children[i];
  1399. compiler = new this.compiler();
  1400. this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
  1401. var index = this.context.programs.length;
  1402. child.index = index;
  1403. child.name = 'program' + index;
  1404. this.context.programs[index] = compiler.compile(child, options, this.context);
  1405. }
  1406. },
  1407. programExpression: function(guid) {
  1408. this.context.aliases.self = "this";
  1409. if(guid == null) {
  1410. return "self.noop";
  1411. }
  1412. var child = this.environment.children[guid],
  1413. depths = child.depths.list, depth;
  1414. var programParams = [child.index, child.name, "data"];
  1415. for(var i=0, l = depths.length; i<l; i++) {
  1416. depth = depths[i];
  1417. if(depth === 1) { programParams.push("depth0"); }
  1418. else { programParams.push("depth" + (depth - 1)); }
  1419. }
  1420. if(depths.length === 0) {
  1421. return "self.program(" + programParams.join(", ") + ")";
  1422. } else {
  1423. programParams.shift();
  1424. return "self.programWithDepth(" + programParams.join(", ") + ")";
  1425. }
  1426. },
  1427. register: function(name, val) {
  1428. this.useRegister(name);
  1429. this.source.push(name + " = " + val + ";");
  1430. },
  1431. useRegister: function(name) {
  1432. if(!this.registers[name]) {
  1433. this.registers[name] = true;
  1434. this.registers.list.push(name);
  1435. }
  1436. },
  1437. pushStackLiteral: function(item) {
  1438. this.compileStack.push(new Literal(item));
  1439. return item;
  1440. },
  1441. pushStack: function(item) {
  1442. this.source.push(this.incrStack() + " = " + item + ";");
  1443. this.compileStack.push("stack" + this.stackSlot);
  1444. return "stack" + this.stackSlot;
  1445. },
  1446. replaceStack: function(callback) {
  1447. var item = callback.call(this, this.topStack());
  1448. this.source.push(this.topStack() + " = " + item + ";");
  1449. return "stack" + this.stackSlot;
  1450. },
  1451. nextStack: function(skipCompileStack) {
  1452. var name = this.incrStack();
  1453. this.compileStack.push("stack" + this.stackSlot);
  1454. return name;
  1455. },
  1456. incrStack: function() {
  1457. this.stackSlot++;
  1458. if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
  1459. return "stack" + this.stackSlot;
  1460. },
  1461. popStack: function() {
  1462. var item = this.compileStack.pop();
  1463. if (item instanceof Literal) {
  1464. return item.value;
  1465. } else {
  1466. this.stackSlot--;
  1467. return item;
  1468. }
  1469. },
  1470. topStack: function() {
  1471. var item = this.compileStack[this.compileStack.length - 1];
  1472. if (item instanceof Literal) {
  1473. return item.value;
  1474. } else {
  1475. return item;
  1476. }
  1477. },
  1478. quotedString: function(str) {
  1479. return '"' + str
  1480. .replace(/\\/g, '\\\\')
  1481. .replace(/"/g, '\\"')
  1482. .replace(/\n/g, '\\n')
  1483. .replace(/\r/g, '\\r') + '"';
  1484. },
  1485. setupHelper: function(paramSize, name) {
  1486. var params = [];
  1487. this.setupParams(paramSize, params);
  1488. var foundHelper = this.nameLookup('helpers', name, 'helper');
  1489. return {
  1490. params: params,
  1491. name: foundHelper,
  1492. callParams: ["depth0"].concat(params).join(", "),
  1493. helperMissingParams: ["depth0", this.quotedString(name)].concat(params).join(", ")
  1494. };
  1495. },
  1496. // the params and contexts arguments are passed in arrays
  1497. // to fill in
  1498. setupParams: function(paramSize, params) {
  1499. var options = [], contexts = [], param, inverse, program;
  1500. options.push("hash:" + this.popStack());
  1501. inverse = this.popStack();
  1502. program = this.popStack();
  1503. // Avoid setting fn and inverse if neither are set. This allows
  1504. // helpers to do a check for `if (options.fn)`
  1505. if (program || inverse) {
  1506. if (!program) {
  1507. this.context.aliases.self = "this";
  1508. program = "self.noop";
  1509. }
  1510. if (!inverse) {
  1511. this.context.aliases.self = "this";
  1512. inverse = "self.noop";
  1513. }
  1514. options.push("inverse:" + inverse);
  1515. options.push("fn:" + program);
  1516. }
  1517. for(var i=0; i<paramSize; i++) {
  1518. param = this.popStack();
  1519. params.push(param);
  1520. if(this.options.stringParams) {
  1521. contexts.push(this.popStack());
  1522. }
  1523. }
  1524. if (this.options.stringParams) {
  1525. options.push("contexts:[" + contexts.join(",") + "]");
  1526. }
  1527. if(this.options.data) {
  1528. options.push("data:data");
  1529. }
  1530. params.push("{" + options.join(",") + "}");
  1531. return params.join(", ");
  1532. }
  1533. };
  1534. var reservedWords = (
  1535. "break else new var" +
  1536. " case finally return void" +
  1537. " catch for switch while" +
  1538. " continue function this with" +
  1539. " default if throw" +
  1540. " delete in try" +
  1541. " do instanceof typeof" +
  1542. " abstract enum int short" +
  1543. " boolean export interface static" +
  1544. " byte extends long super" +
  1545. " char final native synchronized" +
  1546. " class float package throws" +
  1547. " const goto private transient" +
  1548. " debugger implements protected volatile" +
  1549. " double import public let yield"
  1550. ).split(" ");
  1551. var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
  1552. for(var i=0, l=reservedWords.length; i<l; i++) {
  1553. compilerWords[reservedWords[i]] = true;
  1554. }
  1555. JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
  1556. if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
  1557. return true;
  1558. }
  1559. return false;
  1560. };
  1561. })(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
  1562. Handlebars.precompile = function(string, options) {
  1563. options = options || {};
  1564. var ast = Handlebars.parse(string);
  1565. var environment = new Handlebars.Compiler().compile(ast, options);
  1566. return new Handlebars.JavaScriptCompiler().compile(environment, options);
  1567. };
  1568. Handlebars.compile = function(string, options) {
  1569. options = options || {};
  1570. var compiled;
  1571. function compile() {
  1572. var ast = Handlebars.parse(string);
  1573. var environment = new Handlebars.Compiler().compile(ast, options);
  1574. var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
  1575. return Handlebars.template(templateSpec);
  1576. }
  1577. // Template is only compiled on first use and cached after that point.
  1578. return function(context, options) {
  1579. if (!compiled) {
  1580. compiled = compile();
  1581. }
  1582. return compiled.call(this, context, options);
  1583. };
  1584. };
  1585. ;
  1586. // lib/handlebars/runtime.js
  1587. Handlebars.VM = {
  1588. template: function(templateSpec) {
  1589. // Just add water
  1590. var container = {
  1591. escapeExpression: Handlebars.Utils.escapeExpression,
  1592. invokePartial: Handlebars.VM.invokePartial,
  1593. programs: [],
  1594. program: function(i, fn, data) {
  1595. var programWrapper = this.programs[i];
  1596. if(data) {
  1597. return Handlebars.VM.program(fn, data);
  1598. } else if(programWrapper) {
  1599. return programWrapper;
  1600. } else {
  1601. programWrapper = this.programs[i] = Handlebars.VM.program(fn);
  1602. return programWrapper;
  1603. }
  1604. },
  1605. programWithDepth: Handlebars.VM.programWithDepth,
  1606. noop: Handlebars.VM.noop
  1607. };
  1608. return function(context, options) {
  1609. options = options || {};
  1610. return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
  1611. };
  1612. },
  1613. programWithDepth: function(fn, data, $depth) {
  1614. var args = Array.prototype.slice.call(arguments, 2);
  1615. return function(context, options) {
  1616. options = options || {};
  1617. return fn.apply(this, [context, options.data || data].concat(args));
  1618. };
  1619. },
  1620. program: function(fn, data) {
  1621. return function(context, options) {
  1622. options = options || {};
  1623. return fn(context, options.data || data);
  1624. };
  1625. },
  1626. noop: function() { return ""; },
  1627. invokePartial: function(partial, name, context, helpers, partials, data) {
  1628. var options = { helpers: helpers, partials: partials, data: data };
  1629. if(partial === undefined) {
  1630. throw new Handlebars.Exception("The partial " + name + " could not be found");
  1631. } else if(partial instanceof Function) {
  1632. return partial(context, options);
  1633. } else if (!Handlebars.compile) {
  1634. throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
  1635. } else {
  1636. partials[name] = Handlebars.compile(partial, {data: data !== undefined});
  1637. return partials[name](context, options);
  1638. }
  1639. }
  1640. };
  1641. Handlebars.template = Handlebars.VM.template;
  1642. ;