PageRenderTime 44ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/PhoneGap02/node_modules/bower/node_modules/handlebars/lib/handlebars/compiler/compiler.js

https://gitlab.com/hemantr/NetBeansProjects
JavaScript | 451 lines | 346 code | 86 blank | 19 comment | 90 complexity | c8fa16cefcb35e77473aac44f80a371d MD5 | raw file
  1. import Exception from "../exception";
  2. import {isArray} from "../utils";
  3. var slice = [].slice;
  4. export function Compiler() {}
  5. // the foundHelper register will disambiguate helper lookup from finding a
  6. // function in a context. This is necessary for mustache compatibility, which
  7. // requires that context functions in blocks are evaluated by blockHelperMissing,
  8. // and then proceed as if the resulting value was provided to blockHelperMissing.
  9. Compiler.prototype = {
  10. compiler: Compiler,
  11. equals: function(other) {
  12. var len = this.opcodes.length;
  13. if (other.opcodes.length !== len) {
  14. return false;
  15. }
  16. for (var i = 0; i < len; i++) {
  17. var opcode = this.opcodes[i],
  18. otherOpcode = other.opcodes[i];
  19. if (opcode.opcode !== otherOpcode.opcode || !argEquals(opcode.args, otherOpcode.args)) {
  20. return false;
  21. }
  22. }
  23. // We know that length is the same between the two arrays because they are directly tied
  24. // to the opcode behavior above.
  25. len = this.children.length;
  26. for (i = 0; i < len; i++) {
  27. if (!this.children[i].equals(other.children[i])) {
  28. return false;
  29. }
  30. }
  31. return true;
  32. },
  33. guid: 0,
  34. compile: function(program, options) {
  35. this.opcodes = [];
  36. this.children = [];
  37. this.depths = {list: []};
  38. this.options = options;
  39. this.stringParams = options.stringParams;
  40. this.trackIds = options.trackIds;
  41. // These changes will propagate to the other compiler components
  42. var knownHelpers = this.options.knownHelpers;
  43. this.options.knownHelpers = {
  44. 'helperMissing': true,
  45. 'blockHelperMissing': true,
  46. 'each': true,
  47. 'if': true,
  48. 'unless': true,
  49. 'with': true,
  50. 'log': true,
  51. 'lookup': true
  52. };
  53. if (knownHelpers) {
  54. for (var name in knownHelpers) {
  55. this.options.knownHelpers[name] = knownHelpers[name];
  56. }
  57. }
  58. return this.accept(program);
  59. },
  60. accept: function(node) {
  61. return this[node.type](node);
  62. },
  63. program: function(program) {
  64. var statements = program.statements;
  65. for(var i=0, l=statements.length; i<l; i++) {
  66. this.accept(statements[i]);
  67. }
  68. this.isSimple = l === 1;
  69. this.depths.list = this.depths.list.sort(function(a, b) {
  70. return a - b;
  71. });
  72. return this;
  73. },
  74. compileProgram: function(program) {
  75. var result = new this.compiler().compile(program, this.options);
  76. var guid = this.guid++, depth;
  77. this.usePartial = this.usePartial || result.usePartial;
  78. this.children[guid] = result;
  79. for(var i=0, l=result.depths.list.length; i<l; i++) {
  80. depth = result.depths.list[i];
  81. if(depth < 2) { continue; }
  82. else { this.addDepth(depth - 1); }
  83. }
  84. return guid;
  85. },
  86. block: function(block) {
  87. var mustache = block.mustache,
  88. program = block.program,
  89. inverse = block.inverse;
  90. if (program) {
  91. program = this.compileProgram(program);
  92. }
  93. if (inverse) {
  94. inverse = this.compileProgram(inverse);
  95. }
  96. var sexpr = mustache.sexpr;
  97. var type = this.classifySexpr(sexpr);
  98. if (type === "helper") {
  99. this.helperSexpr(sexpr, program, inverse);
  100. } else if (type === "simple") {
  101. this.simpleSexpr(sexpr);
  102. // now that the simple mustache is resolved, we need to
  103. // evaluate it by executing `blockHelperMissing`
  104. this.opcode('pushProgram', program);
  105. this.opcode('pushProgram', inverse);
  106. this.opcode('emptyHash');
  107. this.opcode('blockValue', sexpr.id.original);
  108. } else {
  109. this.ambiguousSexpr(sexpr, program, inverse);
  110. // now that the simple mustache is resolved, we need to
  111. // evaluate it by executing `blockHelperMissing`
  112. this.opcode('pushProgram', program);
  113. this.opcode('pushProgram', inverse);
  114. this.opcode('emptyHash');
  115. this.opcode('ambiguousBlockValue');
  116. }
  117. this.opcode('append');
  118. },
  119. hash: function(hash) {
  120. var pairs = hash.pairs, i, l;
  121. this.opcode('pushHash');
  122. for(i=0, l=pairs.length; i<l; i++) {
  123. this.pushParam(pairs[i][1]);
  124. }
  125. while(i--) {
  126. this.opcode('assignToHash', pairs[i][0]);
  127. }
  128. this.opcode('popHash');
  129. },
  130. partial: function(partial) {
  131. var partialName = partial.partialName;
  132. this.usePartial = true;
  133. if (partial.hash) {
  134. this.accept(partial.hash);
  135. } else {
  136. this.opcode('push', 'undefined');
  137. }
  138. if (partial.context) {
  139. this.accept(partial.context);
  140. } else {
  141. this.opcode('getContext', 0);
  142. this.opcode('pushContext');
  143. }
  144. this.opcode('invokePartial', partialName.name, partial.indent || '');
  145. this.opcode('append');
  146. },
  147. content: function(content) {
  148. if (content.string) {
  149. this.opcode('appendContent', content.string);
  150. }
  151. },
  152. mustache: function(mustache) {
  153. this.sexpr(mustache.sexpr);
  154. if(mustache.escaped && !this.options.noEscape) {
  155. this.opcode('appendEscaped');
  156. } else {
  157. this.opcode('append');
  158. }
  159. },
  160. ambiguousSexpr: function(sexpr, program, inverse) {
  161. var id = sexpr.id,
  162. name = id.parts[0],
  163. isBlock = program != null || inverse != null;
  164. this.opcode('getContext', id.depth);
  165. this.opcode('pushProgram', program);
  166. this.opcode('pushProgram', inverse);
  167. this.ID(id);
  168. this.opcode('invokeAmbiguous', name, isBlock);
  169. },
  170. simpleSexpr: function(sexpr) {
  171. var id = sexpr.id;
  172. if (id.type === 'DATA') {
  173. this.DATA(id);
  174. } else if (id.parts.length) {
  175. this.ID(id);
  176. } else {
  177. // Simplified ID for `this`
  178. this.addDepth(id.depth);
  179. this.opcode('getContext', id.depth);
  180. this.opcode('pushContext');
  181. }
  182. this.opcode('resolvePossibleLambda');
  183. },
  184. helperSexpr: function(sexpr, program, inverse) {
  185. var params = this.setupFullMustacheParams(sexpr, program, inverse),
  186. id = sexpr.id,
  187. name = id.parts[0];
  188. if (this.options.knownHelpers[name]) {
  189. this.opcode('invokeKnownHelper', params.length, name);
  190. } else if (this.options.knownHelpersOnly) {
  191. throw new Exception("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);
  192. } else {
  193. id.falsy = true;
  194. this.ID(id);
  195. this.opcode('invokeHelper', params.length, id.original, id.isSimple);
  196. }
  197. },
  198. sexpr: function(sexpr) {
  199. var type = this.classifySexpr(sexpr);
  200. if (type === "simple") {
  201. this.simpleSexpr(sexpr);
  202. } else if (type === "helper") {
  203. this.helperSexpr(sexpr);
  204. } else {
  205. this.ambiguousSexpr(sexpr);
  206. }
  207. },
  208. ID: function(id) {
  209. this.addDepth(id.depth);
  210. this.opcode('getContext', id.depth);
  211. var name = id.parts[0];
  212. if (!name) {
  213. // Context reference, i.e. `{{foo .}}` or `{{foo ..}}`
  214. this.opcode('pushContext');
  215. } else {
  216. this.opcode('lookupOnContext', id.parts, id.falsy, id.isScoped);
  217. }
  218. },
  219. DATA: function(data) {
  220. this.options.data = true;
  221. this.opcode('lookupData', data.id.depth, data.id.parts);
  222. },
  223. STRING: function(string) {
  224. this.opcode('pushString', string.string);
  225. },
  226. NUMBER: function(number) {
  227. this.opcode('pushLiteral', number.number);
  228. },
  229. BOOLEAN: function(bool) {
  230. this.opcode('pushLiteral', bool.bool);
  231. },
  232. comment: function() {},
  233. // HELPERS
  234. opcode: function(name) {
  235. this.opcodes.push({ opcode: name, args: slice.call(arguments, 1) });
  236. },
  237. addDepth: function(depth) {
  238. if(depth === 0) { return; }
  239. if(!this.depths[depth]) {
  240. this.depths[depth] = true;
  241. this.depths.list.push(depth);
  242. }
  243. },
  244. classifySexpr: function(sexpr) {
  245. var isHelper = sexpr.isHelper;
  246. var isEligible = sexpr.eligibleHelper;
  247. var options = this.options;
  248. // if ambiguous, we can possibly resolve the ambiguity now
  249. // An eligible helper is one that does not have a complex path, i.e. `this.foo`, `../foo` etc.
  250. if (isEligible && !isHelper) {
  251. var name = sexpr.id.parts[0];
  252. if (options.knownHelpers[name]) {
  253. isHelper = true;
  254. } else if (options.knownHelpersOnly) {
  255. isEligible = false;
  256. }
  257. }
  258. if (isHelper) { return "helper"; }
  259. else if (isEligible) { return "ambiguous"; }
  260. else { return "simple"; }
  261. },
  262. pushParams: function(params) {
  263. for(var i=0, l=params.length; i<l; i++) {
  264. this.pushParam(params[i]);
  265. }
  266. },
  267. pushParam: function(val) {
  268. if (this.stringParams) {
  269. if(val.depth) {
  270. this.addDepth(val.depth);
  271. }
  272. this.opcode('getContext', val.depth || 0);
  273. this.opcode('pushStringParam', val.stringModeValue, val.type);
  274. if (val.type === 'sexpr') {
  275. // Subexpressions get evaluated and passed in
  276. // in string params mode.
  277. this.sexpr(val);
  278. }
  279. } else {
  280. if (this.trackIds) {
  281. this.opcode('pushId', val.type, val.idName || val.stringModeValue);
  282. }
  283. this.accept(val);
  284. }
  285. },
  286. setupFullMustacheParams: function(sexpr, program, inverse) {
  287. var params = sexpr.params;
  288. this.pushParams(params);
  289. this.opcode('pushProgram', program);
  290. this.opcode('pushProgram', inverse);
  291. if (sexpr.hash) {
  292. this.hash(sexpr.hash);
  293. } else {
  294. this.opcode('emptyHash');
  295. }
  296. return params;
  297. }
  298. };
  299. export function precompile(input, options, env) {
  300. if (input == null || (typeof input !== 'string' && input.constructor !== env.AST.ProgramNode)) {
  301. throw new Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input);
  302. }
  303. options = options || {};
  304. if (!('data' in options)) {
  305. options.data = true;
  306. }
  307. if (options.compat) {
  308. options.useDepths = true;
  309. }
  310. var ast = env.parse(input);
  311. var environment = new env.Compiler().compile(ast, options);
  312. return new env.JavaScriptCompiler().compile(environment, options);
  313. }
  314. export function compile(input, options, env) {
  315. if (input == null || (typeof input !== 'string' && input.constructor !== env.AST.ProgramNode)) {
  316. throw new Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
  317. }
  318. options = options || {};
  319. if (!('data' in options)) {
  320. options.data = true;
  321. }
  322. if (options.compat) {
  323. options.useDepths = true;
  324. }
  325. var compiled;
  326. function compileInput() {
  327. var ast = env.parse(input);
  328. var environment = new env.Compiler().compile(ast, options);
  329. var templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true);
  330. return env.template(templateSpec);
  331. }
  332. // Template is only compiled on first use and cached after that point.
  333. var ret = function(context, options) {
  334. if (!compiled) {
  335. compiled = compileInput();
  336. }
  337. return compiled.call(this, context, options);
  338. };
  339. ret._setup = function(options) {
  340. if (!compiled) {
  341. compiled = compileInput();
  342. }
  343. return compiled._setup(options);
  344. };
  345. ret._child = function(i, data, depths) {
  346. if (!compiled) {
  347. compiled = compileInput();
  348. }
  349. return compiled._child(i, data, depths);
  350. };
  351. return ret;
  352. }
  353. function argEquals(a, b) {
  354. if (a === b) {
  355. return true;
  356. }
  357. if (isArray(a) && isArray(b) && a.length === b.length) {
  358. for (var i = 0; i < a.length; i++) {
  359. if (!argEquals(a[i], b[i])) {
  360. return false;
  361. }
  362. }
  363. return true;
  364. }
  365. }