diff --git a/api-samples/sandbox/sandbox/LICENSE.handlebars b/api-samples/sandbox/sandbox/LICENSE.handlebars new file mode 100644 index 00000000..237cd034 --- /dev/null +++ b/api-samples/sandbox/sandbox/LICENSE.handlebars @@ -0,0 +1,20 @@ +Copyright (C) 2011 by Yehuda Katz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/api-samples/sandbox/sandbox/assets/screenshot_1280_800.png b/api-samples/sandbox/sandbox/assets/screenshot_1280_800.png new file mode 100644 index 00000000..3266c14e Binary files /dev/null and b/api-samples/sandbox/sandbox/assets/screenshot_1280_800.png differ diff --git a/api-samples/sandbox/sandbox/handlebars-1.0.0.beta.6.js b/api-samples/sandbox/sandbox/handlebars-1.0.0.beta.6.js new file mode 100644 index 00000000..87a9e189 --- /dev/null +++ b/api-samples/sandbox/sandbox/handlebars-1.0.0.beta.6.js @@ -0,0 +1,2450 @@ +// Copyright (C) 2011 by Yehuda Katz +// Licensing details in LICENSE.handlebars + +// lib/handlebars/base.js +/* eslint-disable */ +var Handlebars = {}; + +Handlebars.VERSION = '1.0.beta.6'; + +Handlebars.helpers = {}; +Handlebars.partials = {}; + +Handlebars.registerHelper = function (name, fn, inverse) { + if (inverse) { + fn.not = inverse; + } + this.helpers[name] = fn; +}; + +Handlebars.registerPartial = function (name, str) { + this.partials[name] = str; +}; + +Handlebars.registerHelper('helperMissing', function (arg) { + if (arguments.length === 2) { + return undefined; + } else { + throw new Error("Could not find property '" + arg + "'"); + } +}); + +var toString = Object.prototype.toString, + functionType = '[object Function]'; + +Handlebars.registerHelper('blockHelperMissing', function (context, options) { + var inverse = options.inverse || function () {}, + fn = options.fn; + + var ret = ''; + var type = toString.call(context); + + if (type === functionType) { + context = context.call(this); + } + + if (context === true) { + return fn(this); + } else if (context === false || context == null) { + return inverse(this); + } else if (type === '[object Array]') { + if (context.length > 0) { + for (var i = 0, j = context.length; i < j; i++) { + ret = ret + fn(context[i]); + } + } else { + ret = inverse(this); + } + return ret; + } else { + return fn(context); + } +}); + +Handlebars.registerHelper('each', function (context, options) { + var fn = options.fn, + inverse = options.inverse; + var ret = ''; + + if (context && context.length > 0) { + for (var i = 0, j = context.length; i < j; i++) { + ret = ret + fn(context[i]); + } + } else { + ret = inverse(this); + } + return ret; +}); + +Handlebars.registerHelper('if', function (context, options) { + var type = toString.call(context); + if (type === functionType) { + context = context.call(this); + } + + if (!context || Handlebars.Utils.isEmpty(context)) { + return options.inverse(this); + } else { + return options.fn(this); + } +}); + +Handlebars.registerHelper('unless', function (context, options) { + var fn = options.fn, + inverse = options.inverse; + options.fn = inverse; + options.inverse = fn; + + return Handlebars.helpers['if'].call(this, context, options); +}); + +Handlebars.registerHelper('with', function (context, options) { + return options.fn(context); +}); + +Handlebars.registerHelper('log', function (context) { + Handlebars.log(context); +}); +// lib/handlebars/compiler/parser.js +/* Jison generated parser */ +var handlebars = (function () { + var parser = { + trace: function trace() {}, + yy: {}, + 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 + }, + 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' + }, + 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] + ], + performAction: function anonymous( + yytext, + yyleng, + yylineno, + yy, + yystate, + $$, + _$ + ) { + var $0 = $$.length - 1; + switch (yystate) { + case 1: + return $$[$0 - 1]; + break; + case 2: + this.$ = new yy.ProgramNode($$[$0 - 2], $$[$0]); + break; + case 3: + this.$ = new yy.ProgramNode($$[$0]); + break; + case 4: + this.$ = new yy.ProgramNode([]); + break; + case 5: + this.$ = [$$[$0]]; + break; + case 6: + $$[$0 - 1].push($$[$0]); + this.$ = $$[$0 - 1]; + break; + case 7: + this.$ = new yy.InverseNode($$[$0 - 2], $$[$0 - 1], $$[$0]); + break; + case 8: + this.$ = new yy.BlockNode($$[$0 - 2], $$[$0 - 1], $$[$0]); + break; + case 9: + this.$ = $$[$0]; + break; + case 10: + this.$ = $$[$0]; + break; + case 11: + this.$ = new yy.ContentNode($$[$0]); + break; + case 12: + this.$ = new yy.CommentNode($$[$0]); + break; + case 13: + this.$ = new yy.MustacheNode($$[$0 - 1][0], $$[$0 - 1][1]); + break; + case 14: + this.$ = new yy.MustacheNode($$[$0 - 1][0], $$[$0 - 1][1]); + break; + case 15: + this.$ = $$[$0 - 1]; + break; + case 16: + this.$ = new yy.MustacheNode($$[$0 - 1][0], $$[$0 - 1][1]); + break; + case 17: + this.$ = new yy.MustacheNode($$[$0 - 1][0], $$[$0 - 1][1], true); + break; + case 18: + this.$ = new yy.PartialNode($$[$0 - 1]); + break; + case 19: + this.$ = new yy.PartialNode($$[$0 - 2], $$[$0 - 1]); + break; + case 20: + break; + case 21: + this.$ = [[$$[$0 - 2]].concat($$[$0 - 1]), $$[$0]]; + break; + case 22: + this.$ = [[$$[$0 - 1]].concat($$[$0]), null]; + break; + case 23: + this.$ = [[$$[$0 - 1]], $$[$0]]; + break; + case 24: + this.$ = [[$$[$0]], null]; + break; + case 25: + $$[$0 - 1].push($$[$0]); + this.$ = $$[$0 - 1]; + break; + case 26: + this.$ = [$$[$0]]; + break; + case 27: + this.$ = $$[$0]; + break; + case 28: + this.$ = new yy.StringNode($$[$0]); + break; + case 29: + this.$ = new yy.IntegerNode($$[$0]); + break; + case 30: + this.$ = new yy.BooleanNode($$[$0]); + break; + case 31: + this.$ = new yy.HashNode($$[$0]); + break; + case 32: + $$[$0 - 1].push($$[$0]); + this.$ = $$[$0 - 1]; + break; + case 33: + this.$ = [$$[$0]]; + break; + case 34: + this.$ = [$$[$0 - 2], $$[$0]]; + break; + case 35: + this.$ = [$$[$0 - 2], new yy.StringNode($$[$0])]; + break; + case 36: + this.$ = [$$[$0 - 2], new yy.IntegerNode($$[$0])]; + break; + case 37: + this.$ = [$$[$0 - 2], new yy.BooleanNode($$[$0])]; + break; + case 38: + this.$ = new yy.IdNode($$[$0]); + break; + case 39: + $$[$0 - 2].push($$[$0]); + this.$ = $$[$0 - 2]; + break; + case 40: + this.$ = [$$[$0]]; + break; + } + }, + 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] } + ], + defaultActions: { 16: [2, 1], 37: [2, 23], 53: [2, 21] }, + parseError: function parseError(str, hash) { + throw new Error(str); + }, + parse: function parse(input) { + var self = this, + stack = [0], + vstack = [null], + lstack = [], + table = this.table, + yytext = '', + yylineno = 0, + yyleng = 0, + recovering = 0, + TERROR = 2, + EOF = 1; + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + if (typeof this.lexer.yylloc == 'undefined') this.lexer.yylloc = {}; + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + if (typeof this.yy.parseError === 'function') + this.parseError = this.yy.parseError; + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + function lex() { + var token; + token = self.lexer.lex() || 1; + if (typeof token !== 'number') { + token = self.symbols_[token] || token; + } + return token; + } + var symbol, + preErrorSymbol, + state, + action, + a, + r, + yyval = {}, + p, + len, + newState, + expected; + while (true) { + state = stack[stack.length - 1]; + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol == null) symbol = lex(); + action = table[state] && table[state][symbol]; + } + if (typeof action === 'undefined' || !action.length || !action[0]) { + if (!recovering) { + expected = []; + for (p in table[state]) + if (this.terminals_[p] && p > 2) { + expected.push("'" + this.terminals_[p] + "'"); + } + var errStr = ''; + if (this.lexer.showPosition) { + errStr = + 'Parse error on line ' + + (yylineno + 1) + + ':\n' + + this.lexer.showPosition() + + '\nExpecting ' + + expected.join(', ') + + ", got '" + + this.terminals_[symbol] + + "'"; + } else { + errStr = + 'Parse error on line ' + + (yylineno + 1) + + ': Unexpected ' + + (symbol == 1 + ? 'end of input' + : "'" + (this.terminals_[symbol] || symbol) + "'"); + } + this.parseError(errStr, { + text: this.lexer.match, + token: this.terminals_[symbol] || symbol, + line: this.lexer.yylineno, + loc: yyloc, + expected: expected + }); + } + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error( + 'Parse Error: multiple actions possible at state: ' + + state + + ', token: ' + + symbol + ); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) recovering--; + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + 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 + }; + r = this.performAction.call( + yyval, + yytext, + yyleng, + yylineno, + this.yy, + action[1], + vstack, + lstack + ); + if (typeof r !== 'undefined') { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; + } + }; /* Jison generated lexer */ + var lexer = (function () { + var lexer = { + EOF: 1, + parseError: function parseError(str, hash) { + if (this.yy.parseError) { + this.yy.parseError(str, hash); + } else { + throw new Error(str); + } + }, + setInput: function (input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = { + first_line: 1, + first_column: 0, + last_line: 1, + last_column: 0 + }; + return this; + }, + input: function () { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/\n/); + if (lines) this.yylineno++; + this._input = this._input.slice(1); + return ch; + }, + unput: function (ch) { + this._input = ch + this._input; + return this; + }, + more: function () { + this._more = true; + return this; + }, + pastInput: function () { + var past = this.matched.substr( + 0, + this.matched.length - this.match.length + ); + return ( + (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, '') + ); + }, + upcomingInput: function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20 - next.length); + } + return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace( + /\n/g, + '' + ); + }, + showPosition: function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join('-'); + return pre + this.upcomingInput() + '\n' + c + '^'; + }, + next: function () { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, match, col, lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i = 0; i < rules.length; i++) { + match = this._input.match(this.rules[rules[i]]); + if (match) { + lines = match[0].match(/\n.*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = { + first_line: this.yylloc.last_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.last_column, + last_column: lines + ? lines[lines.length - 1].length - 1 + : this.yylloc.last_column + match[0].length + }; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call( + this, + this.yy, + this, + rules[i], + this.conditionStack[this.conditionStack.length - 1] + ); + if (token) return token; + else return; + } + } + if (this._input === '') { + return this.EOF; + } else { + this.parseError( + 'Lexical error on line ' + + (this.yylineno + 1) + + '. Unrecognized text.\n' + + this.showPosition(), + { text: '', token: null, line: this.yylineno } + ); + } + }, + lex: function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, + begin: function begin(condition) { + this.conditionStack.push(condition); + }, + popState: function popState() { + return this.conditionStack.pop(); + }, + _currentRules: function _currentRules() { + return this.conditions[ + this.conditionStack[this.conditionStack.length - 1] + ].rules; + }, + topState: function () { + return this.conditionStack[this.conditionStack.length - 2]; + }, + pushState: function begin(condition) { + this.begin(condition); + } + }; + lexer.performAction = function anonymous( + yy, + yy_, + $avoiding_name_collisions, + YY_START + ) { + var YYSTATE = YY_START; + switch ($avoiding_name_collisions) { + case 0: + if (yy_.yytext.slice(-1) !== '\\') this.begin('mu'); + if (yy_.yytext.slice(-1) === '\\') + (yy_.yytext = yy_.yytext.substr(0, yy_.yyleng - 1)), + this.begin('emu'); + if (yy_.yytext) return 14; + + break; + case 1: + return 14; + break; + case 2: + this.popState(); + return 14; + break; + case 3: + return 24; + break; + case 4: + return 16; + break; + case 5: + return 20; + break; + case 6: + return 19; + break; + case 7: + return 19; + break; + case 8: + return 23; + break; + case 9: + return 23; + break; + case 10: + yy_.yytext = yy_.yytext.substr(3, yy_.yyleng - 5); + this.popState(); + return 15; + break; + case 11: + return 22; + break; + case 12: + return 34; + break; + case 13: + return 33; + break; + case 14: + return 33; + break; + case 15: + return 36; + break; + case 16 /*ignore whitespace*/: + break; + case 17: + this.popState(); + return 18; + break; + case 18: + this.popState(); + return 18; + break; + case 19: + yy_.yytext = yy_.yytext + .substr(1, yy_.yyleng - 2) + .replace(/\\"/g, '"'); + return 28; + break; + case 20: + return 30; + break; + case 21: + return 30; + break; + case 22: + return 29; + break; + case 23: + return 33; + break; + case 24: + yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2); + return 33; + break; + case 25: + return 'INVALID'; + break; + case 26: + return 5; + break; + } + }; + lexer.rules = [ + /^[^\x00]*?(?=(\{\{))/, + /^[^\x00]+/, + /^[^\x00]{2,}?(?=(\{\{))/, + /^\{\{>/, + /^\{\{#/, + /^\{\{\//, + /^\{\{\^/, + /^\{\{\s*else\b/, + /^\{\{\{/, + /^\{\{&/, + /^\{\{![\s\S]*?\}\}/, + /^\{\{/, + /^=/, + /^\.(?=[} ])/, + /^\.\./, + /^[\/.]/, + /^\s+/, + /^\}\}\}/, + /^\}\}/, + /^"(\\["]|[^"])*"/, + /^true(?=[}\s])/, + /^false(?=[}\s])/, + /^[0-9]+(?=[}\s])/, + /^[a-zA-Z0-9_$-]+(?=[=}\s\/.])/, + /^\[[^\]]*\]/, + /^./, + /^$/ + ]; + 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 + ], + inclusive: false + }, + emu: { rules: [2], inclusive: false }, + INITIAL: { rules: [0, 1, 26], inclusive: true } + }; + return lexer; + })(); + parser.lexer = lexer; + return parser; +})(); +if (typeof require !== 'undefined' && typeof exports !== 'undefined') { + exports.parser = handlebars; + exports.parse = function () { + return handlebars.parse.apply(handlebars, arguments); + }; + exports.main = function commonjsMain(args) { + if (!args[1]) throw new Error('Usage: ' + args[0] + ' FILE'); + if (typeof process !== 'undefined') { + var source = require('fs').readFileSync( + require('path').join(process.cwd(), args[1]), + 'utf8' + ); + } else { + var cwd = require('file').path(require('file').cwd()); + var source = cwd.join(args[1]).read({ charset: 'utf-8' }); + } + return exports.parser.parse(source); + }; + if (typeof module !== 'undefined' && require.main === module) { + exports.main( + typeof process !== 'undefined' + ? process.argv.slice(1) + : require('system').args + ); + } +} +// lib/handlebars/compiler/base.js +Handlebars.Parser = handlebars; + +Handlebars.parse = function (string) { + Handlebars.Parser.yy = Handlebars.AST; + return Handlebars.Parser.parse(string); +}; + +Handlebars.print = function (ast) { + return new Handlebars.PrintVisitor().accept(ast); +}; + +Handlebars.logger = { + DEBUG: 0, + INFO: 1, + WARN: 2, + ERROR: 3, + level: 3, + + // override in the host environment + log: function (level, str) {} +}; + +Handlebars.log = function (level, str) { + Handlebars.logger.log(level, str); +}; +// lib/handlebars/compiler/ast.js +(function () { + Handlebars.AST = {}; + + Handlebars.AST.ProgramNode = function (statements, inverse) { + this.type = 'program'; + this.statements = statements; + if (inverse) { + this.inverse = new Handlebars.AST.ProgramNode(inverse); + } + }; + + Handlebars.AST.MustacheNode = function (params, hash, unescaped) { + this.type = 'mustache'; + this.id = params[0]; + this.params = params.slice(1); + this.hash = hash; + this.escaped = !unescaped; + }; + + Handlebars.AST.PartialNode = function (id, context) { + this.type = 'partial'; + + // TODO: disallow complex IDs + + this.id = id; + this.context = context; + }; + + var verifyMatch = function (open, close) { + if (open.original !== close.original) { + throw new Handlebars.Exception( + open.original + " doesn't match " + close.original + ); + } + }; + + Handlebars.AST.BlockNode = function (mustache, program, close) { + verifyMatch(mustache.id, close); + this.type = 'block'; + this.mustache = mustache; + this.program = program; + }; + + Handlebars.AST.InverseNode = function (mustache, program, close) { + verifyMatch(mustache.id, close); + this.type = 'inverse'; + this.mustache = mustache; + this.program = program; + }; + + Handlebars.AST.ContentNode = function (string) { + this.type = 'content'; + this.string = string; + }; + + Handlebars.AST.HashNode = function (pairs) { + this.type = 'hash'; + this.pairs = pairs; + }; + + Handlebars.AST.IdNode = function (parts) { + this.type = 'ID'; + this.original = parts.join('.'); + + var dig = [], + depth = 0; + + for (var i = 0, l = parts.length; i < l; i++) { + var part = parts[i]; + + if (part === '..') { + depth++; + } else if (part === '.' || part === 'this') { + this.isScoped = true; + } else { + dig.push(part); + } + } + + this.parts = dig; + this.string = dig.join('.'); + this.depth = depth; + this.isSimple = dig.length === 1 && depth === 0; + }; + + Handlebars.AST.StringNode = function (string) { + this.type = 'STRING'; + this.string = string; + }; + + Handlebars.AST.IntegerNode = function (integer) { + this.type = 'INTEGER'; + this.integer = integer; + }; + + Handlebars.AST.BooleanNode = function (bool) { + this.type = 'BOOLEAN'; + this.bool = bool; + }; + + Handlebars.AST.CommentNode = function (comment) { + this.type = 'comment'; + this.comment = comment; + }; +})(); +// lib/handlebars/utils.js +Handlebars.Exception = function (message) { + var tmp = Error.prototype.constructor.apply(this, arguments); + + for (var p in tmp) { + if (tmp.hasOwnProperty(p)) { + this[p] = tmp[p]; + } + } + + this.message = tmp.message; +}; +Handlebars.Exception.prototype = new Error(); + +// Build out our basic SafeString type +Handlebars.SafeString = function (string) { + this.string = string; +}; +Handlebars.SafeString.prototype.toString = function () { + return this.string.toString(); +}; + +(function () { + var escape = { + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`' + }; + + var badChars = /&(?!\w+;)|[<>"'`]/g; + var possible = /[&<>"'`]/; + + var escapeChar = function (chr) { + return escape[chr] || '&'; + }; + + Handlebars.Utils = { + escapeExpression: function (string) { + // don't escape SafeStrings, since they're already safe + if (string instanceof Handlebars.SafeString) { + return string.toString(); + } else if (string == null || string === false) { + return ''; + } + + if (!possible.test(string)) { + return string; + } + return string.replace(badChars, escapeChar); + }, + + isEmpty: function (value) { + if (typeof value === 'undefined') { + return true; + } else if (value === null) { + return true; + } else if (value === false) { + return true; + } else if ( + Object.prototype.toString.call(value) === '[object Array]' && + value.length === 0 + ) { + return true; + } else { + return false; + } + } + }; +})(); +// lib/handlebars/compiler/compiler.js +Handlebars.Compiler = function () {}; +Handlebars.JavaScriptCompiler = function () {}; + +(function (Compiler, JavaScriptCompiler) { + Compiler.OPCODE_MAP = { + appendContent: 1, + getContext: 2, + lookupWithHelpers: 3, + lookup: 4, + append: 5, + invokeMustache: 6, + appendEscaped: 7, + pushString: 8, + truthyOrFallback: 9, + functionOrFallback: 10, + invokeProgram: 11, + invokePartial: 12, + push: 13, + assignToHash: 15, + pushStringParam: 16 + }; + + Compiler.MULTI_PARAM_OPCODES = { + appendContent: 1, + getContext: 1, + lookupWithHelpers: 2, + lookup: 1, + invokeMustache: 3, + pushString: 1, + truthyOrFallback: 1, + functionOrFallback: 1, + invokeProgram: 3, + invokePartial: 1, + push: 1, + assignToHash: 1, + pushStringParam: 1 + }; + + Compiler.DISASSEMBLE_MAP = {}; + + for (var prop in Compiler.OPCODE_MAP) { + var value = Compiler.OPCODE_MAP[prop]; + Compiler.DISASSEMBLE_MAP[value] = prop; + } + + Compiler.multiParamSize = function (code) { + return Compiler.MULTI_PARAM_OPCODES[Compiler.DISASSEMBLE_MAP[code]]; + }; + + Compiler.prototype = { + compiler: Compiler, + + disassemble: function () { + var opcodes = this.opcodes, + opcode, + nextCode; + var out = [], + str, + name, + value; + + for (var i = 0, l = opcodes.length; i < l; i++) { + opcode = opcodes[i]; + + if (opcode === 'DECLARE') { + name = opcodes[++i]; + value = opcodes[++i]; + out.push('DECLARE ' + name + ' = ' + value); + } else { + str = Compiler.DISASSEMBLE_MAP[opcode]; + + var extraParams = Compiler.multiParamSize(opcode); + var codes = []; + + for (var j = 0; j < extraParams; j++) { + nextCode = opcodes[++i]; + + if (typeof nextCode === 'string') { + nextCode = '"' + nextCode.replace('\n', '\\n') + '"'; + } + + codes.push(nextCode); + } + + str = str + ' ' + codes.join(' '); + + out.push(str); + } + } + + return out.join('\n'); + }, + + guid: 0, + + compile: function (program, options) { + this.children = []; + this.depths = { list: [] }; + this.options = options; + + // These changes will propagate to the other compiler components + var knownHelpers = this.options.knownHelpers; + this.options.knownHelpers = { + helperMissing: true, + blockHelperMissing: true, + each: true, + if: true, + unless: true, + with: true, + log: true + }; + if (knownHelpers) { + for (var name in knownHelpers) { + this.options.knownHelpers[name] = knownHelpers[name]; + } + } + + return this.program(program); + }, + + accept: function (node) { + return this[node.type](node); + }, + + program: function (program) { + var statements = program.statements, + statement; + this.opcodes = []; + + for (var i = 0, l = statements.length; i < l; i++) { + statement = statements[i]; + this[statement.type](statement); + } + this.isSimple = l === 1; + + this.depths.list = this.depths.list.sort(function (a, b) { + return a - b; + }); + + return this; + }, + + compileProgram: function (program) { + var result = new this.compiler().compile(program, this.options); + var guid = this.guid++; + + this.usePartial = this.usePartial || result.usePartial; + + this.children[guid] = result; + + for (var i = 0, l = result.depths.list.length; i < l; i++) { + depth = result.depths.list[i]; + + if (depth < 2) { + continue; + } else { + this.addDepth(depth - 1); + } + } + + return guid; + }, + + block: function (block) { + var mustache = block.mustache; + var depth, child, inverse, inverseGuid; + + var params = this.setupStackForMustache(mustache); + + var programGuid = this.compileProgram(block.program); + + if (block.program.inverse) { + inverseGuid = this.compileProgram(block.program.inverse); + this.declare('inverse', inverseGuid); + } + + this.opcode('invokeProgram', programGuid, params.length, !!mustache.hash); + this.declare('inverse', null); + this.opcode('append'); + }, + + inverse: function (block) { + var params = this.setupStackForMustache(block.mustache); + + var programGuid = this.compileProgram(block.program); + + this.declare('inverse', programGuid); + + this.opcode('invokeProgram', null, params.length, !!block.mustache.hash); + this.declare('inverse', null); + this.opcode('append'); + }, + + hash: function (hash) { + var pairs = hash.pairs, + pair, + val; + + this.opcode('push', '{}'); + + for (var i = 0, l = pairs.length; i < l; i++) { + pair = pairs[i]; + val = pair[1]; + + this.accept(val); + this.opcode('assignToHash', pair[0]); + } + }, + + partial: function (partial) { + var id = partial.id; + this.usePartial = true; + + if (partial.context) { + this.ID(partial.context); + } else { + this.opcode('push', 'depth0'); + } + + this.opcode('invokePartial', id.original); + this.opcode('append'); + }, + + content: function (content) { + this.opcode('appendContent', content.string); + }, + + mustache: function (mustache) { + var params = this.setupStackForMustache(mustache); + + this.opcode( + 'invokeMustache', + params.length, + mustache.id.original, + !!mustache.hash + ); + + if (mustache.escaped && !this.options.noEscape) { + this.opcode('appendEscaped'); + } else { + this.opcode('append'); + } + }, + + ID: function (id) { + this.addDepth(id.depth); + + this.opcode('getContext', id.depth); + + this.opcode( + 'lookupWithHelpers', + id.parts[0] || null, + id.isScoped || false + ); + + for (var i = 1, l = id.parts.length; i < l; i++) { + this.opcode('lookup', id.parts[i]); + } + }, + + STRING: function (string) { + this.opcode('pushString', string.string); + }, + + INTEGER: function (integer) { + this.opcode('push', integer.integer); + }, + + BOOLEAN: function (bool) { + this.opcode('push', bool.bool); + }, + + comment: function () {}, + + // HELPERS + pushParams: function (params) { + var i = params.length, + param; + + while (i--) { + param = params[i]; + + if (this.options.stringParams) { + if (param.depth) { + this.addDepth(param.depth); + } + + this.opcode('getContext', param.depth || 0); + this.opcode('pushStringParam', param.string); + } else { + this[param.type](param); + } + } + }, + + opcode: function (name, val1, val2, val3) { + this.opcodes.push(Compiler.OPCODE_MAP[name]); + if (val1 !== undefined) { + this.opcodes.push(val1); + } + if (val2 !== undefined) { + this.opcodes.push(val2); + } + if (val3 !== undefined) { + this.opcodes.push(val3); + } + }, + + declare: function (name, value) { + this.opcodes.push('DECLARE'); + this.opcodes.push(name); + this.opcodes.push(value); + }, + + addDepth: function (depth) { + if (depth === 0) { + return; + } + + if (!this.depths[depth]) { + this.depths[depth] = true; + this.depths.list.push(depth); + } + }, + + setupStackForMustache: function (mustache) { + var params = mustache.params; + + this.pushParams(params); + + if (mustache.hash) { + this.hash(mustache.hash); + } + + this.ID(mustache.id); + + return params; + } + }; + + JavaScriptCompiler.prototype = { + // PUBLIC API: You can override these methods in a subclass to provide + // alternative compiled forms for name lookup and buffering semantics + nameLookup: function (parent, name, type) { + if (/^[0-9]+$/.test(name)) { + return parent + '[' + name + ']'; + } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { + return parent + '.' + name; + } else { + return parent + "['" + name + "']"; + } + }, + + appendToBuffer: function (string) { + if (this.environment.isSimple) { + return 'return ' + string + ';'; + } else { + return 'buffer += ' + string + ';'; + } + }, + + initializeBuffer: function () { + return this.quotedString(''); + }, + + namespace: 'Handlebars', + // END PUBLIC API + + compile: function (environment, options, context, asObject) { + this.environment = environment; + this.options = options || {}; + + this.name = this.environment.name; + this.isChild = !!context; + this.context = context || { + programs: [], + aliases: { self: 'this' }, + registers: { list: [] } + }; + + this.preamble(); + + this.stackSlot = 0; + this.stackVars = []; + + this.compileChildren(environment, options); + + var opcodes = environment.opcodes, + opcode; + + this.i = 0; + + for (l = opcodes.length; this.i < l; this.i++) { + opcode = this.nextOpcode(0); + + if (opcode[0] === 'DECLARE') { + this.i = this.i + 2; + this[opcode[1]] = opcode[2]; + } else { + this.i = this.i + opcode[1].length; + this[opcode[0]].apply(this, opcode[1]); + } + } + + return this.createFunctionContext(asObject); + }, + + nextOpcode: function (n) { + var opcodes = this.environment.opcodes, + opcode = opcodes[this.i + n], + name, + val; + var extraParams, codes; + + if (opcode === 'DECLARE') { + name = opcodes[this.i + 1]; + val = opcodes[this.i + 2]; + return ['DECLARE', name, val]; + } else { + name = Compiler.DISASSEMBLE_MAP[opcode]; + + extraParams = Compiler.multiParamSize(opcode); + codes = []; + + for (var j = 0; j < extraParams; j++) { + codes.push(opcodes[this.i + j + 1 + n]); + } + + return [name, codes]; + } + }, + + eat: function (opcode) { + this.i = this.i + opcode.length; + }, + + preamble: function () { + var out = []; + + // this register will disambiguate helper lookup from finding a function in + // a context. This is necessary for mustache compatibility, which requires + // that context functions in blocks are evaluated by blockHelperMissing, and + // then proceed as if the resulting value was provided to blockHelperMissing. + this.useRegister('foundHelper'); + + if (!this.isChild) { + var namespace = this.namespace; + var copies = 'helpers = helpers || ' + namespace + '.helpers;'; + if (this.environment.usePartial) { + copies = + copies + ' partials = partials || ' + namespace + '.partials;'; + } + out.push(copies); + } else { + out.push(''); + } + + if (!this.environment.isSimple) { + out.push(', buffer = ' + this.initializeBuffer()); + } else { + out.push(''); + } + + // track the last context pushed into place to allow skipping the + // getContext opcode when it would be a noop + this.lastContext = 0; + this.source = out; + }, + + createFunctionContext: function (asObject) { + var locals = this.stackVars; + if (!this.isChild) { + locals = locals.concat(this.context.registers.list); + } + + if (locals.length > 0) { + this.source[1] = this.source[1] + ', ' + locals.join(', '); + } + + // Generate minimizer alias mappings + if (!this.isChild) { + var aliases = []; + for (var alias in this.context.aliases) { + this.source[1] = + this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; + } + } + + if (this.source[1]) { + this.source[1] = 'var ' + this.source[1].substring(2) + ';'; + } + + // Merge children + if (!this.isChild) { + this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; + } + + if (!this.environment.isSimple) { + this.source.push('return buffer;'); + } + + var params = this.isChild + ? ['depth0', 'data'] + : ['Handlebars', 'depth0', 'helpers', 'partials', 'data']; + + for (var i = 0, l = this.environment.depths.list.length; i < l; i++) { + params.push('depth' + this.environment.depths.list[i]); + } + + if (asObject) { + params.push(this.source.join('\n ')); + + return Function.apply(this, params); + } else { + var functionSource = + 'function ' + + (this.name || '') + + '(' + + params.join(',') + + ') {\n ' + + this.source.join('\n ') + + '}'; + Handlebars.log(Handlebars.logger.DEBUG, functionSource + '\n\n'); + return functionSource; + } + }, + + appendContent: function (content) { + this.source.push(this.appendToBuffer(this.quotedString(content))); + }, + + append: function () { + var local = this.popStack(); + this.source.push( + 'if(' + + local + + ' || ' + + local + + ' === 0) { ' + + this.appendToBuffer(local) + + ' }' + ); + if (this.environment.isSimple) { + this.source.push('else { ' + this.appendToBuffer("''") + ' }'); + } + }, + + appendEscaped: function () { + var opcode = this.nextOpcode(1), + extra = ''; + this.context.aliases.escapeExpression = 'this.escapeExpression'; + + if (opcode[0] === 'appendContent') { + extra = ' + ' + this.quotedString(opcode[1][0]); + this.eat(opcode); + } + + this.source.push( + this.appendToBuffer('escapeExpression(' + this.popStack() + ')' + extra) + ); + }, + + getContext: function (depth) { + if (this.lastContext !== depth) { + this.lastContext = depth; + } + }, + + lookupWithHelpers: function (name, isScoped) { + if (name) { + var topStack = this.nextStack(); + + this.usingKnownHelper = false; + + var toPush; + if (!isScoped && this.options.knownHelpers[name]) { + toPush = + topStack + ' = ' + this.nameLookup('helpers', name, 'helper'); + this.usingKnownHelper = true; + } else if (isScoped || this.options.knownHelpersOnly) { + toPush = + topStack + + ' = ' + + this.nameLookup('depth' + this.lastContext, name, 'context'); + } else { + this.register( + 'foundHelper', + this.nameLookup('helpers', name, 'helper') + ); + toPush = + topStack + + ' = foundHelper || ' + + this.nameLookup('depth' + this.lastContext, name, 'context'); + } + + toPush += ';'; + this.source.push(toPush); + } else { + this.pushStack('depth' + this.lastContext); + } + }, + + lookup: function (name) { + var topStack = this.topStack(); + this.source.push( + topStack + + ' = (' + + topStack + + ' === null || ' + + topStack + + ' === undefined || ' + + topStack + + ' === false ? ' + + topStack + + ' : ' + + this.nameLookup(topStack, name, 'context') + + ');' + ); + }, + + pushStringParam: function (string) { + this.pushStack('depth' + this.lastContext); + this.pushString(string); + }, + + pushString: function (string) { + this.pushStack(this.quotedString(string)); + }, + + push: function (name) { + this.pushStack(name); + }, + + invokeMustache: function (paramSize, original, hasHash) { + this.populateParams( + paramSize, + this.quotedString(original), + '{}', + null, + hasHash, + function (nextStack, helperMissingString, id) { + if (!this.usingKnownHelper) { + this.context.aliases.helperMissing = 'helpers.helperMissing'; + this.context.aliases.undef = 'void 0'; + this.source.push( + 'else if(' + + id + + '=== undef) { ' + + nextStack + + ' = helperMissing.call(' + + helperMissingString + + '); }' + ); + if (nextStack !== id) { + this.source.push('else { ' + nextStack + ' = ' + id + '; }'); + } + } + } + ); + }, + + invokeProgram: function (guid, paramSize, hasHash) { + var inverse = this.programExpression(this.inverse); + var mainProgram = this.programExpression(guid); + + this.populateParams( + paramSize, + null, + mainProgram, + inverse, + hasHash, + function (nextStack, helperMissingString, id) { + if (!this.usingKnownHelper) { + this.context.aliases.blockHelperMissing = + 'helpers.blockHelperMissing'; + this.source.push( + 'else { ' + + nextStack + + ' = blockHelperMissing.call(' + + helperMissingString + + '); }' + ); + } + } + ); + }, + + populateParams: function ( + paramSize, + helperId, + program, + inverse, + hasHash, + fn + ) { + var needsRegister = + hasHash || this.options.stringParams || inverse || this.options.data; + var id = this.popStack(), + nextStack; + var params = [], + param, + stringParam, + stringOptions; + + if (needsRegister) { + this.register('tmp1', program); + stringOptions = 'tmp1'; + } else { + stringOptions = '{ hash: {} }'; + } + + if (needsRegister) { + var hash = hasHash ? this.popStack() : '{}'; + this.source.push('tmp1.hash = ' + hash + ';'); + } + + if (this.options.stringParams) { + this.source.push('tmp1.contexts = [];'); + } + + for (var i = 0; i < paramSize; i++) { + param = this.popStack(); + params.push(param); + + if (this.options.stringParams) { + this.source.push('tmp1.contexts.push(' + this.popStack() + ');'); + } + } + + if (inverse) { + this.source.push('tmp1.fn = tmp1;'); + this.source.push('tmp1.inverse = ' + inverse + ';'); + } + + if (this.options.data) { + this.source.push('tmp1.data = data;'); + } + + params.push(stringOptions); + + this.populateCall(params, id, helperId || id, fn, program !== '{}'); + }, + + populateCall: function (params, id, helperId, fn, program) { + var paramString = ['depth0'].concat(params).join(', '); + var helperMissingString = ['depth0'] + .concat(helperId) + .concat(params) + .join(', '); + + var nextStack = this.nextStack(); + + if (this.usingKnownHelper) { + this.source.push( + nextStack + ' = ' + id + '.call(' + paramString + ');' + ); + } else { + this.context.aliases.functionType = '"function"'; + var condition = program ? 'foundHelper && ' : ''; + this.source.push( + 'if(' + + condition + + 'typeof ' + + id + + ' === functionType) { ' + + nextStack + + ' = ' + + id + + '.call(' + + paramString + + '); }' + ); + } + fn.call(this, nextStack, helperMissingString, id); + this.usingKnownHelper = false; + }, + + invokePartial: function (context) { + params = [ + this.nameLookup('partials', context, 'partial'), + "'" + context + "'", + this.popStack(), + 'helpers', + 'partials' + ]; + + if (this.options.data) { + params.push('data'); + } + + this.pushStack('self.invokePartial(' + params.join(', ') + ');'); + }, + + assignToHash: function (key) { + var value = this.popStack(); + var hash = this.topStack(); + + this.source.push(hash + "['" + key + "'] = " + value + ';'); + }, + + // HELPERS + + compiler: JavaScriptCompiler, + + compileChildren: function (environment, options) { + var children = environment.children, + child, + compiler; + + for (var i = 0, l = children.length; i < l; i++) { + child = children[i]; + compiler = new this.compiler(); + + this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children + var index = this.context.programs.length; + child.index = index; + child.name = 'program' + index; + this.context.programs[index] = compiler.compile( + child, + options, + this.context + ); + } + }, + + programExpression: function (guid) { + if (guid == null) { + return 'self.noop'; + } + + var child = this.environment.children[guid], + depths = child.depths.list; + var programParams = [child.index, child.name, 'data']; + + for (var i = 0, l = depths.length; i < l; i++) { + depth = depths[i]; + + if (depth === 1) { + programParams.push('depth0'); + } else { + programParams.push('depth' + (depth - 1)); + } + } + + if (depths.length === 0) { + return 'self.program(' + programParams.join(', ') + ')'; + } else { + programParams.shift(); + return 'self.programWithDepth(' + programParams.join(', ') + ')'; + } + }, + + register: function (name, val) { + this.useRegister(name); + this.source.push(name + ' = ' + val + ';'); + }, + + useRegister: function (name) { + if (!this.context.registers[name]) { + this.context.registers[name] = true; + this.context.registers.list.push(name); + } + }, + + pushStack: function (item) { + this.source.push(this.nextStack() + ' = ' + item + ';'); + return 'stack' + this.stackSlot; + }, + + nextStack: function () { + this.stackSlot++; + if (this.stackSlot > this.stackVars.length) { + this.stackVars.push('stack' + this.stackSlot); + } + return 'stack' + this.stackSlot; + }, + + popStack: function () { + return 'stack' + this.stackSlot--; + }, + + topStack: function () { + return 'stack' + this.stackSlot; + }, + + quotedString: function (str) { + return ( + '"' + + str + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + + '"' + ); + } + }; + + var reservedWords = ( + 'break else new var' + + ' case finally return void' + + ' catch for switch while' + + ' continue function this with' + + ' default if throw' + + ' delete in try' + + ' do instanceof typeof' + + ' abstract enum int short' + + ' boolean export interface static' + + ' byte extends long super' + + ' char final native synchronized' + + ' class float package throws' + + ' const goto private transient' + + ' debugger implements protected volatile' + + ' double import public let yield' + ).split(' '); + + var compilerWords = (JavaScriptCompiler.RESERVED_WORDS = {}); + + for (var i = 0, l = reservedWords.length; i < l; i++) { + compilerWords[reservedWords[i]] = true; + } + + JavaScriptCompiler.isValidJavaScriptVariableName = function (name) { + if ( + !JavaScriptCompiler.RESERVED_WORDS[name] && + /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name) + ) { + return true; + } + return false; + }; +})(Handlebars.Compiler, Handlebars.JavaScriptCompiler); + +Handlebars.precompile = function (string, options) { + options = options || {}; + + var ast = Handlebars.parse(string); + var environment = new Handlebars.Compiler().compile(ast, options); + return new Handlebars.JavaScriptCompiler().compile(environment, options); +}; + +Handlebars.compile = function (string, options) { + options = options || {}; + + var compiled; + function compile() { + var ast = Handlebars.parse(string); + var environment = new Handlebars.Compiler().compile(ast, options); + var templateSpec = new Handlebars.JavaScriptCompiler().compile( + environment, + options, + undefined, + true + ); + return Handlebars.template(templateSpec); + } + + // Template is only compiled on first use and cached after that point. + return function (context, options) { + if (!compiled) { + compiled = compile(); + } + return compiled.call(this, context, options); + }; +}; +// lib/handlebars/runtime.js +Handlebars.VM = { + template: function (templateSpec) { + // Just add water + var container = { + escapeExpression: Handlebars.Utils.escapeExpression, + invokePartial: Handlebars.VM.invokePartial, + programs: [], + program: function (i, fn, data) { + var programWrapper = this.programs[i]; + if (data) { + return Handlebars.VM.program(fn, data); + } else if (programWrapper) { + return programWrapper; + } else { + programWrapper = this.programs[i] = Handlebars.VM.program(fn); + return programWrapper; + } + }, + programWithDepth: Handlebars.VM.programWithDepth, + noop: Handlebars.VM.noop + }; + + return function (context, options) { + options = options || {}; + return templateSpec.call( + container, + Handlebars, + context, + options.helpers, + options.partials, + options.data + ); + }; + }, + + programWithDepth: function (fn, data, $depth) { + var args = Array.prototype.slice.call(arguments, 2); + + return function (context, options) { + options = options || {}; + + return fn.apply(this, [context, options.data || data].concat(args)); + }; + }, + program: function (fn, data) { + return function (context, options) { + options = options || {}; + + return fn(context, options.data || data); + }; + }, + noop: function () { + return ''; + }, + invokePartial: function (partial, name, context, helpers, partials, data) { + options = { helpers: helpers, partials: partials, data: data }; + + if (partial === undefined) { + throw new Handlebars.Exception( + 'The partial ' + name + ' could not be found' + ); + } else if (partial instanceof Function) { + return partial(context, options); + } else if (!Handlebars.compile) { + throw new Handlebars.Exception( + 'The partial ' + + name + + ' could not be compiled when running in runtime-only mode' + ); + } else { + partials[name] = Handlebars.compile(partial); + return partials[name](context, options); + } + } +}; + +Handlebars.template = Handlebars.VM.template; diff --git a/api-samples/sandbox/sandbox/icon_128.png b/api-samples/sandbox/sandbox/icon_128.png new file mode 100644 index 00000000..8d8a3e1e Binary files /dev/null and b/api-samples/sandbox/sandbox/icon_128.png differ diff --git a/api-samples/sandbox/sandbox/mainpage.html b/api-samples/sandbox/sandbox/mainpage.html new file mode 100644 index 00000000..487bbfe8 --- /dev/null +++ b/api-samples/sandbox/sandbox/mainpage.html @@ -0,0 +1,32 @@ + + + + + + + + +
+ + +
+ +
+ + + + diff --git a/api-samples/sandbox/sandbox/mainpage.js b/api-samples/sandbox/sandbox/mainpage.js new file mode 100644 index 00000000..b042816a --- /dev/null +++ b/api-samples/sandbox/sandbox/mainpage.js @@ -0,0 +1,37 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +let counter = 0; +document.addEventListener('DOMContentLoaded', () => { + document.getElementById('reset').addEventListener('click', function () { + counter = 0; + document.querySelector('#result').innerHTML = ''; + }); + + document.getElementById('sendMessage').addEventListener('click', function () { + counter++; + let message = { + command: 'render', + templateName: 'sample-template-' + counter, + context: { counter: counter } + }; + document.getElementById('theFrame').contentWindow.postMessage(message, '*'); + }); + + // on result from sandboxed frame: + window.addEventListener('message', function () { + document.querySelector('#result').innerHTML = + event.data.result || 'invalid result'; + }); +}); diff --git a/api-samples/sandbox/sandbox/manifest.json b/api-samples/sandbox/sandbox/manifest.json new file mode 100644 index 00000000..2c3b469b --- /dev/null +++ b/api-samples/sandbox/sandbox/manifest.json @@ -0,0 +1,14 @@ +{ + "name": "Sandboxed Frame Sample", + "version": "1.0.3", + "manifest_version": 3, + "background": { + "service_worker": "service-worker.js" + }, + "icons": { + "128": "icon_128.png" + }, + "sandbox": { + "pages": ["sandbox.html"] + } +} diff --git a/api-samples/sandbox/sandbox/sandbox.html b/api-samples/sandbox/sandbox/sandbox.html new file mode 100644 index 00000000..8dcc76c1 --- /dev/null +++ b/api-samples/sandbox/sandbox/sandbox.html @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + diff --git a/api-samples/sandbox/sandbox/sandbox.md b/api-samples/sandbox/sandbox/sandbox.md new file mode 100644 index 00000000..9da04b8a --- /dev/null +++ b/api-samples/sandbox/sandbox/sandbox.md @@ -0,0 +1,10 @@ +# Sandbox + +This sample creates a tab with a sandboxed iframe (`sandbox.html`) to which the main page (`mainpage.html`) +passes a counter variable. The sandboxed page uses the +Handlebars template library to evaluate and compose a message +using the counter variable which is then passed back to the main page for rendering. + +## Overview + +The default Content Security Policy (CSP) settings of the extension disallows the use of `eval()` so using a sandbox is necessary to use external resources for this extension. diff --git a/api-samples/sandbox/sandbox/service-worker.js b/api-samples/sandbox/sandbox/service-worker.js new file mode 100644 index 00000000..1ed24462 --- /dev/null +++ b/api-samples/sandbox/sandbox/service-worker.js @@ -0,0 +1,26 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * Listens for the extension launching then creates a new tab + * + * @see http://developer.chrome.com/docs/extensions/reference/runtime + * @see http://developer.chrome.com/docs/extensions/reference/tabs + */ +chrome.runtime.onInstalled.addListener(() => { + chrome.tabs.create({ + url: 'mainpage.html' + }); + console.log('Opened a tab with a sandboxed page!'); +}); diff --git a/api-samples/sandbox/sandbox/styles/main.css b/api-samples/sandbox/sandbox/styles/main.css new file mode 100644 index 00000000..5badba0f --- /dev/null +++ b/api-samples/sandbox/sandbox/styles/main.css @@ -0,0 +1,42 @@ +html, +body { + font-family: Helvetica, Arial, sans-serif; +} + +button { + width: 150px; + line-height: 26px; + font-size: 14px; + border-radius:4px; + border: 1px solid #666; + background: -webkit-linear-gradient(top, #ffffff 0%, #f2f2f2 99%); + box-shadow: 0 1px 1px rgba(0,0,0,0.3); + color: #555; +} + +button:hover { + color: #333; +} + +button:active { + color: #000; +} + +#buttons { + text-align: center; + padding: 15px; + background: #fcfcfc; + border-bottom: 1px solid #f2f2f2; +} + +#result { + padding: 20px; +} + +#result h1 { + margin: 0 0 0.2em 0; +} + +#result p { + line-height: 140% +} diff --git a/api-samples/sandbox/sandboxed-content/assets/screenshot_1280_800.png b/api-samples/sandbox/sandboxed-content/assets/screenshot_1280_800.png new file mode 100644 index 00000000..e6bbab66 Binary files /dev/null and b/api-samples/sandbox/sandboxed-content/assets/screenshot_1280_800.png differ diff --git a/api-samples/sandbox/sandboxed-content/icon_128.png b/api-samples/sandbox/sandboxed-content/icon_128.png new file mode 100644 index 00000000..3f66f029 Binary files /dev/null and b/api-samples/sandbox/sandboxed-content/icon_128.png differ diff --git a/api-samples/sandbox/sandboxed-content/main.html b/api-samples/sandbox/sandboxed-content/main.html new file mode 100644 index 00000000..01b87e0c --- /dev/null +++ b/api-samples/sandbox/sandboxed-content/main.html @@ -0,0 +1,13 @@ + + + + + Sandboxed Content + + + +

Main Window

+

I am the main window. I am not sandboxed.

+ + + diff --git a/api-samples/sandbox/sandboxed-content/manifest.json b/api-samples/sandbox/sandboxed-content/manifest.json new file mode 100644 index 00000000..8a6e3296 --- /dev/null +++ b/api-samples/sandbox/sandboxed-content/manifest.json @@ -0,0 +1,14 @@ +{ + "name": "Sandboxed Content Sample", + "version": "1.0.3", + "manifest_version": 3, + "background": { + "service_worker": "service-worker.js" + }, + "icons": { + "128": "icon_128.png" + }, + "sandbox": { + "pages": ["sandboxed.html"] + } +} diff --git a/api-samples/sandbox/sandboxed-content/sandboxed-content.md b/api-samples/sandbox/sandboxed-content/sandboxed-content.md new file mode 100644 index 00000000..977f008f --- /dev/null +++ b/api-samples/sandbox/sandboxed-content/sandboxed-content.md @@ -0,0 +1,8 @@ +# Sandboxed Content + +This sample creates a tab containing a sandboxed iframe (`sandbox.html`). +The sandbox calls `eval()` to write some HTML to its own document. + +## Overview + +The default Content Security Policy (CSP) settings of the extension disallows the use of `eval()` so using a sandbox is necessary to use external resources for this extension. diff --git a/api-samples/sandbox/sandboxed-content/sandboxed.html b/api-samples/sandbox/sandboxed-content/sandboxed.html new file mode 100644 index 00000000..fd0329ce --- /dev/null +++ b/api-samples/sandbox/sandboxed-content/sandboxed.html @@ -0,0 +1,19 @@ + + + + + Sandboxed Content + + + +

Sandboxed Content

+

I am the sandboxed iframe.

+
+ + + diff --git a/api-samples/sandbox/sandboxed-content/service-worker.js b/api-samples/sandbox/sandboxed-content/service-worker.js new file mode 100644 index 00000000..bbcae6d1 --- /dev/null +++ b/api-samples/sandbox/sandboxed-content/service-worker.js @@ -0,0 +1,25 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * Listens for the extension launching then creates the window + * + * @see http://developer.chrome.com/docs/extensions/reference/runtime.html + * @see http://developer.chrome.com/docs/extensions/reference/tab.html + */ +chrome.runtime.onInstalled.addListener(() => { + chrome.tabs.create({ + url: 'main.html' + }); +}); diff --git a/api-samples/sandbox/sandboxed-content/styles/main.css b/api-samples/sandbox/sandboxed-content/styles/main.css new file mode 100644 index 00000000..c6451aa4 --- /dev/null +++ b/api-samples/sandbox/sandboxed-content/styles/main.css @@ -0,0 +1,18 @@ +html, body { + font-family: Helvetica, Arial; + color: #444; +} + +h1 { + margin: 0 0 0.3em 0; +} + +p { + color: #888; + margin: 0 0 1em 0; +} + +iframe { + border: 1px solid #CCC; + margin-top: 20px; +}