UNPKG

39.8 kBJavaScriptView Raw
1(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ejs = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2/*
3 * EJS Embedded JavaScript templates
4 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18*/
19
20'use strict';
21
22/**
23 * @file Embedded JavaScript templating engine.
24 * @author Matthew Eernisse <mde@fleegix.org>
25 * @author Tiancheng "Timothy" Gu <timothygu99@gmail.com>
26 * @project EJS
27 * @license {@link http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0}
28 */
29
30/**
31 * EJS internal functions.
32 *
33 * Technically this "module" lies in the same file as {@link module:ejs}, for
34 * the sake of organization all the private functions re grouped into this
35 * module.
36 *
37 * @module ejs-internal
38 * @private
39 */
40
41/**
42 * Embedded JavaScript templating engine.
43 *
44 * @module ejs
45 * @public
46 */
47
48var fs = require('fs');
49var path = require('path');
50var utils = require('./utils');
51
52var scopeOptionWarned = false;
53var _VERSION_STRING = require('../package.json').version;
54var _DEFAULT_DELIMITER = '%';
55var _DEFAULT_LOCALS_NAME = 'locals';
56var _REGEX_STRING = '(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)';
57var _OPTS = [ 'cache', 'filename', 'delimiter', 'scope', 'context',
58 'debug', 'compileDebug', 'client', '_with', 'root', 'rmWhitespace',
59 'strict', 'localsName'];
60var _TRAILING_SEMCOL = /;\s*$/;
61var _BOM = /^\uFEFF/;
62
63/**
64 * EJS template function cache. This can be a LRU object from lru-cache NPM
65 * module. By default, it is {@link module:utils.cache}, a simple in-process
66 * cache that grows continuously.
67 *
68 * @type {Cache}
69 */
70
71exports.cache = utils.cache;
72
73/**
74 * Name of the object containing the locals.
75 *
76 * This variable is overridden by {@link Options}`.localsName` if it is not
77 * `undefined`.
78 *
79 * @type {String}
80 * @public
81 */
82
83exports.localsName = _DEFAULT_LOCALS_NAME;
84
85/**
86 * Get the path to the included file from the parent file path and the
87 * specified path.
88 *
89 * @param {String} name specified path
90 * @param {String} filename parent file path
91 * @param {Boolean} isDir parent file path whether is directory
92 * @return {String}
93 */
94exports.resolveInclude = function(name, filename, isDir) {
95 var dirname = path.dirname;
96 var extname = path.extname;
97 var resolve = path.resolve;
98 var includePath = resolve(isDir ? filename : dirname(filename), name);
99 var ext = extname(name);
100 if (!ext) {
101 includePath += '.ejs';
102 }
103 return includePath;
104};
105
106/**
107 * Get the path to the included file by Options
108 *
109 * @param {String} path specified path
110 * @param {Options} options compilation options
111 * @return {String}
112 */
113function getIncludePath(path, options){
114 var includePath;
115 if (path.charAt(0) == '/') {
116 includePath = exports.resolveInclude(path.replace(/^\/*/,''), options.root || '/', true);
117 }
118 else {
119 if (!options.filename) {
120 throw new Error('`include` use relative path requires the \'filename\' option.');
121 }
122 includePath = exports.resolveInclude(path, options.filename);
123 }
124 return includePath;
125}
126
127/**
128 * Get the template from a string or a file, either compiled on-the-fly or
129 * read from cache (if enabled), and cache the template if needed.
130 *
131 * If `template` is not set, the file specified in `options.filename` will be
132 * read.
133 *
134 * If `options.cache` is true, this function reads the file from
135 * `options.filename` so it must be set prior to calling this function.
136 *
137 * @memberof module:ejs-internal
138 * @param {Options} options compilation options
139 * @param {String} [template] template source
140 * @return {(TemplateFunction|ClientFunction)}
141 * Depending on the value of `options.client`, either type might be returned.
142 * @static
143 */
144
145function handleCache(options, template) {
146 var func;
147 var filename = options.filename;
148 var hasTemplate = arguments.length > 1;
149
150 if (options.cache) {
151 if (!filename) {
152 throw new Error('cache option requires a filename');
153 }
154 func = exports.cache.get(filename);
155 if (func) {
156 return func;
157 }
158 if (!hasTemplate) {
159 template = fs.readFileSync(filename).toString().replace(_BOM, '');
160 }
161 }
162 else if (!hasTemplate) {
163 // istanbul ignore if: should not happen at all
164 if (!filename) {
165 throw new Error('Internal EJS error: no file name or template '
166 + 'provided');
167 }
168 template = fs.readFileSync(filename).toString().replace(_BOM, '');
169 }
170 func = exports.compile(template, options);
171 if (options.cache) {
172 exports.cache.set(filename, func);
173 }
174 return func;
175}
176
177/**
178 * Get the template function.
179 *
180 * If `options.cache` is `true`, then the template is cached.
181 *
182 * @memberof module:ejs-internal
183 * @param {String} path path for the specified file
184 * @param {Options} options compilation options
185 * @return {(TemplateFunction|ClientFunction)}
186 * Depending on the value of `options.client`, either type might be returned
187 * @static
188 */
189
190function includeFile(path, options) {
191 var opts = utils.shallowCopy({}, options);
192 opts.filename = getIncludePath(path, opts);
193 return handleCache(opts);
194}
195
196/**
197 * Get the JavaScript source of an included file.
198 *
199 * @memberof module:ejs-internal
200 * @param {String} path path for the specified file
201 * @param {Options} options compilation options
202 * @return {Object}
203 * @static
204 */
205
206function includeSource(path, options) {
207 var opts = utils.shallowCopy({}, options);
208 var includePath;
209 var template;
210 includePath = getIncludePath(path,opts);
211 template = fs.readFileSync(includePath).toString().replace(_BOM, '');
212 opts.filename = includePath;
213 var templ = new Template(template, opts);
214 templ.generateSource();
215 return {
216 source: templ.source,
217 filename: includePath,
218 template: template
219 };
220}
221
222/**
223 * Re-throw the given `err` in context to the `str` of ejs, `filename`, and
224 * `lineno`.
225 *
226 * @implements RethrowCallback
227 * @memberof module:ejs-internal
228 * @param {Error} err Error object
229 * @param {String} str EJS source
230 * @param {String} filename file name of the EJS file
231 * @param {String} lineno line number of the error
232 * @static
233 */
234
235function rethrow(err, str, filename, lineno){
236 var lines = str.split('\n');
237 var start = Math.max(lineno - 3, 0);
238 var end = Math.min(lines.length, lineno + 3);
239 // Error context
240 var context = lines.slice(start, end).map(function (line, i){
241 var curr = i + start + 1;
242 return (curr == lineno ? ' >> ' : ' ')
243 + curr
244 + '| '
245 + line;
246 }).join('\n');
247
248 // Alter exception message
249 err.path = filename;
250 err.message = (filename || 'ejs') + ':'
251 + lineno + '\n'
252 + context + '\n\n'
253 + err.message;
254
255 throw err;
256}
257
258/**
259 * Copy properties in data object that are recognized as options to an
260 * options object.
261 *
262 * This is used for compatibility with earlier versions of EJS and Express.js.
263 *
264 * @memberof module:ejs-internal
265 * @param {Object} data data object
266 * @param {Options} opts options object
267 * @static
268 */
269
270function cpOptsInData(data, opts) {
271 _OPTS.forEach(function (p) {
272 if (typeof data[p] != 'undefined') {
273 // Disallow setting the root opt for includes via a passed data obj
274 // Unsanitized, parameterized use of `render` could allow the
275 // include directory to be reset, opening up the possibility of
276 // remote code execution
277 if (p == 'root') {
278 return;
279 }
280 opts[p] = data[p];
281 }
282 });
283}
284
285/**
286 * Compile the given `str` of ejs into a template function.
287 *
288 * @param {String} template EJS template
289 *
290 * @param {Options} opts compilation options
291 *
292 * @return {(TemplateFunction|ClientFunction)}
293 * Depending on the value of `opts.client`, either type might be returned.
294 * @public
295 */
296
297exports.compile = function compile(template, opts) {
298 var templ;
299
300 // v1 compat
301 // 'scope' is 'context'
302 // FIXME: Remove this in a future version
303 if (opts && opts.scope) {
304 if (!scopeOptionWarned){
305 console.warn('`scope` option is deprecated and will be removed in EJS 3');
306 scopeOptionWarned = true;
307 }
308 if (!opts.context) {
309 opts.context = opts.scope;
310 }
311 delete opts.scope;
312 }
313 templ = new Template(template, opts);
314 return templ.compile();
315};
316
317/**
318 * Render the given `template` of ejs.
319 *
320 * If you would like to include options but not data, you need to explicitly
321 * call this function with `data` being an empty object or `null`.
322 *
323 * @param {String} template EJS template
324 * @param {Object} [data={}] template data
325 * @param {Options} [opts={}] compilation and rendering options
326 * @return {String}
327 * @public
328 */
329
330exports.render = function (template, d, o) {
331 var data = d || {};
332 var opts = o || {};
333
334 // No options object -- if there are optiony names
335 // in the data, copy them to options
336 if (arguments.length == 2) {
337 cpOptsInData(data, opts);
338 }
339
340 return handleCache(opts, template)(data);
341};
342
343/**
344 * Render an EJS file at the given `path` and callback `cb(err, str)`.
345 *
346 * If you would like to include options but not data, you need to explicitly
347 * call this function with `data` being an empty object or `null`.
348 *
349 * @param {String} path path to the EJS file
350 * @param {Object} [data={}] template data
351 * @param {Options} [opts={}] compilation and rendering options
352 * @param {RenderFileCallback} cb callback
353 * @public
354 */
355
356exports.renderFile = function () {
357 var args = Array.prototype.slice.call(arguments);
358 var filename = args.shift();
359 var cb = args.pop();
360 var data = args.shift() || {};
361 var opts = args.pop() || {};
362 var result;
363
364 // Don't pollute passed in opts obj with new vals
365 opts = utils.shallowCopy({}, opts);
366
367 // No options object -- if there are optiony names
368 // in the data, copy them to options
369 if (arguments.length == 3) {
370 // Express 4
371 if (data.settings && data.settings['view options']) {
372 cpOptsInData(data.settings['view options'], opts);
373 }
374 // Express 3 and lower
375 else {
376 cpOptsInData(data, opts);
377 }
378 }
379 opts.filename = filename;
380
381 try {
382 result = handleCache(opts)(data);
383 }
384 catch(err) {
385 return cb(err);
386 }
387 return cb(null, result);
388};
389
390/**
391 * Clear intermediate JavaScript cache. Calls {@link Cache#reset}.
392 * @public
393 */
394
395exports.clearCache = function () {
396 exports.cache.reset();
397};
398
399function Template(text, opts) {
400 opts = opts || {};
401 var options = {};
402 this.templateText = text;
403 this.mode = null;
404 this.truncate = false;
405 this.currentLine = 1;
406 this.source = '';
407 this.dependencies = [];
408 options.client = opts.client || false;
409 options.escapeFunction = opts.escape || utils.escapeXML;
410 options.compileDebug = opts.compileDebug !== false;
411 options.debug = !!opts.debug;
412 options.filename = opts.filename;
413 options.delimiter = opts.delimiter || exports.delimiter || _DEFAULT_DELIMITER;
414 options.strict = opts.strict || false;
415 options.context = opts.context;
416 options.cache = opts.cache || false;
417 options.rmWhitespace = opts.rmWhitespace;
418 options.root = opts.root;
419 options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME;
420
421 if (options.strict) {
422 options._with = false;
423 }
424 else {
425 options._with = typeof opts._with != 'undefined' ? opts._with : true;
426 }
427
428 this.opts = options;
429
430 this.regex = this.createRegex();
431}
432
433Template.modes = {
434 EVAL: 'eval',
435 ESCAPED: 'escaped',
436 RAW: 'raw',
437 COMMENT: 'comment',
438 LITERAL: 'literal'
439};
440
441Template.prototype = {
442 createRegex: function () {
443 var str = _REGEX_STRING;
444 var delim = utils.escapeRegExpChars(this.opts.delimiter);
445 str = str.replace(/%/g, delim);
446 return new RegExp(str);
447 },
448
449 compile: function () {
450 var src;
451 var fn;
452 var opts = this.opts;
453 var prepended = '';
454 var appended = '';
455 var escape = opts.escapeFunction;
456
457 if (!this.source) {
458 this.generateSource();
459 prepended += ' var __output = [], __append = __output.push.bind(__output);' + '\n';
460 if (opts._with !== false) {
461 prepended += ' with (' + opts.localsName + ' || {}) {' + '\n';
462 appended += ' }' + '\n';
463 }
464 appended += ' return __output.join("");' + '\n';
465 this.source = prepended + this.source + appended;
466 }
467
468 if (opts.compileDebug) {
469 src = 'var __line = 1' + '\n'
470 + ' , __lines = ' + JSON.stringify(this.templateText) + '\n'
471 + ' , __filename = ' + (opts.filename ?
472 JSON.stringify(opts.filename) : 'undefined') + ';' + '\n'
473 + 'try {' + '\n'
474 + this.source
475 + '} catch (e) {' + '\n'
476 + ' rethrow(e, __lines, __filename, __line);' + '\n'
477 + '}' + '\n';
478 }
479 else {
480 src = this.source;
481 }
482
483 if (opts.debug) {
484 console.log(src);
485 }
486
487 if (opts.client) {
488 src = 'escape = escape || ' + escape.toString() + ';' + '\n' + src;
489 if (opts.compileDebug) {
490 src = 'rethrow = rethrow || ' + rethrow.toString() + ';' + '\n' + src;
491 }
492 }
493
494 if (opts.strict) {
495 src = '"use strict";\n' + src;
496 }
497
498 try {
499 fn = new Function(opts.localsName + ', escape, include, rethrow', src);
500 }
501 catch(e) {
502 // istanbul ignore else
503 if (e instanceof SyntaxError) {
504 if (opts.filename) {
505 e.message += ' in ' + opts.filename;
506 }
507 e.message += ' while compiling ejs\n\n';
508 e.message += 'If the above error is not helpful, you may want to try EJS-Lint:\n';
509 e.message += 'https://github.com/RyanZim/EJS-Lint';
510 }
511 throw e;
512 }
513
514 if (opts.client) {
515 fn.dependencies = this.dependencies;
516 return fn;
517 }
518
519 // Return a callable function which will execute the function
520 // created by the source-code, with the passed data as locals
521 // Adds a local `include` function which allows full recursive include
522 var returnedFn = function (data) {
523 var include = function (path, includeData) {
524 var d = utils.shallowCopy({}, data);
525 if (includeData) {
526 d = utils.shallowCopy(d, includeData);
527 }
528 return includeFile(path, opts)(d);
529 };
530 return fn.apply(opts.context, [data || {}, escape, include, rethrow]);
531 };
532 returnedFn.dependencies = this.dependencies;
533 return returnedFn;
534 },
535
536 generateSource: function () {
537 var opts = this.opts;
538
539 if (opts.rmWhitespace) {
540 // Have to use two separate replace here as `^` and `$` operators don't
541 // work well with `\r`.
542 this.templateText =
543 this.templateText.replace(/\r/g, '').replace(/^\s+|\s+$/gm, '');
544 }
545
546 // Slurp spaces and tabs before <%_ and after _%>
547 this.templateText =
548 this.templateText.replace(/[ \t]*<%_/gm, '<%_').replace(/_%>[ \t]*/gm, '_%>');
549
550 var self = this;
551 var matches = this.parseTemplateText();
552 var d = this.opts.delimiter;
553
554 if (matches && matches.length) {
555 matches.forEach(function (line, index) {
556 var opening;
557 var closing;
558 var include;
559 var includeOpts;
560 var includeObj;
561 var includeSrc;
562 // If this is an opening tag, check for closing tags
563 // FIXME: May end up with some false positives here
564 // Better to store modes as k/v with '<' + delimiter as key
565 // Then this can simply check against the map
566 if ( line.indexOf('<' + d) === 0 // If it is a tag
567 && line.indexOf('<' + d + d) !== 0) { // and is not escaped
568 closing = matches[index + 2];
569 if (!(closing == d + '>' || closing == '-' + d + '>' || closing == '_' + d + '>')) {
570 throw new Error('Could not find matching close tag for "' + line + '".');
571 }
572 }
573 // HACK: backward-compat `include` preprocessor directives
574 if ((include = line.match(/^\s*include\s+(\S+)/))) {
575 opening = matches[index - 1];
576 // Must be in EVAL or RAW mode
577 if (opening && (opening == '<' + d || opening == '<' + d + '-' || opening == '<' + d + '_')) {
578 includeOpts = utils.shallowCopy({}, self.opts);
579 includeObj = includeSource(include[1], includeOpts);
580 if (self.opts.compileDebug) {
581 includeSrc =
582 ' ; (function(){' + '\n'
583 + ' var __line = 1' + '\n'
584 + ' , __lines = ' + JSON.stringify(includeObj.template) + '\n'
585 + ' , __filename = ' + JSON.stringify(includeObj.filename) + ';' + '\n'
586 + ' try {' + '\n'
587 + includeObj.source
588 + ' } catch (e) {' + '\n'
589 + ' rethrow(e, __lines, __filename, __line);' + '\n'
590 + ' }' + '\n'
591 + ' ; }).call(this)' + '\n';
592 }else{
593 includeSrc = ' ; (function(){' + '\n' + includeObj.source +
594 ' ; }).call(this)' + '\n';
595 }
596 self.source += includeSrc;
597 self.dependencies.push(exports.resolveInclude(include[1],
598 includeOpts.filename));
599 return;
600 }
601 }
602 self.scanLine(line);
603 });
604 }
605
606 },
607
608 parseTemplateText: function () {
609 var str = this.templateText;
610 var pat = this.regex;
611 var result = pat.exec(str);
612 var arr = [];
613 var firstPos;
614
615 while (result) {
616 firstPos = result.index;
617
618 if (firstPos !== 0) {
619 arr.push(str.substring(0, firstPos));
620 str = str.slice(firstPos);
621 }
622
623 arr.push(result[0]);
624 str = str.slice(result[0].length);
625 result = pat.exec(str);
626 }
627
628 if (str) {
629 arr.push(str);
630 }
631
632 return arr;
633 },
634
635 scanLine: function (line) {
636 var self = this;
637 var d = this.opts.delimiter;
638 var newLineCount = 0;
639
640 function _addOutput() {
641 if (self.truncate) {
642 // Only replace single leading linebreak in the line after
643 // -%> tag -- this is the single, trailing linebreak
644 // after the tag that the truncation mode replaces
645 // Handle Win / Unix / old Mac linebreaks -- do the \r\n
646 // combo first in the regex-or
647 line = line.replace(/^(?:\r\n|\r|\n)/, '');
648 self.truncate = false;
649 }
650 else if (self.opts.rmWhitespace) {
651 // Gotta be more careful here.
652 // .replace(/^(\s*)\n/, '$1') might be more appropriate here but as
653 // rmWhitespace already removes trailing spaces anyway so meh.
654 line = line.replace(/^\n/, '');
655 }
656 if (!line) {
657 return;
658 }
659
660 // Preserve literal slashes
661 line = line.replace(/\\/g, '\\\\');
662
663 // Convert linebreaks
664 line = line.replace(/\n/g, '\\n');
665 line = line.replace(/\r/g, '\\r');
666
667 // Escape double-quotes
668 // - this will be the delimiter during execution
669 line = line.replace(/"/g, '\\"');
670 self.source += ' ; __append("' + line + '")' + '\n';
671 }
672
673 newLineCount = (line.split('\n').length - 1);
674
675 switch (line) {
676 case '<' + d:
677 case '<' + d + '_':
678 this.mode = Template.modes.EVAL;
679 break;
680 case '<' + d + '=':
681 this.mode = Template.modes.ESCAPED;
682 break;
683 case '<' + d + '-':
684 this.mode = Template.modes.RAW;
685 break;
686 case '<' + d + '#':
687 this.mode = Template.modes.COMMENT;
688 break;
689 case '<' + d + d:
690 this.mode = Template.modes.LITERAL;
691 this.source += ' ; __append("' + line.replace('<' + d + d, '<' + d) + '")' + '\n';
692 break;
693 case d + d + '>':
694 this.mode = Template.modes.LITERAL;
695 this.source += ' ; __append("' + line.replace(d + d + '>', d + '>') + '")' + '\n';
696 break;
697 case d + '>':
698 case '-' + d + '>':
699 case '_' + d + '>':
700 if (this.mode == Template.modes.LITERAL) {
701 _addOutput();
702 }
703
704 this.mode = null;
705 this.truncate = line.indexOf('-') === 0 || line.indexOf('_') === 0;
706 break;
707 default:
708 // In script mode, depends on type of tag
709 if (this.mode) {
710 // If '//' is found without a line break, add a line break.
711 switch (this.mode) {
712 case Template.modes.EVAL:
713 case Template.modes.ESCAPED:
714 case Template.modes.RAW:
715 if (line.lastIndexOf('//') > line.lastIndexOf('\n')) {
716 line += '\n';
717 }
718 }
719 switch (this.mode) {
720 // Just executing code
721 case Template.modes.EVAL:
722 this.source += ' ; ' + line + '\n';
723 break;
724 // Exec, esc, and output
725 case Template.modes.ESCAPED:
726 this.source += ' ; __append(escape(' +
727 line.replace(_TRAILING_SEMCOL, '').trim() + '))' + '\n';
728 break;
729 // Exec and output
730 case Template.modes.RAW:
731 this.source += ' ; __append(' +
732 line.replace(_TRAILING_SEMCOL, '').trim() + ')' + '\n';
733 break;
734 case Template.modes.COMMENT:
735 // Do nothing
736 break;
737 // Literal <%% mode, append as raw output
738 case Template.modes.LITERAL:
739 _addOutput();
740 break;
741 }
742 }
743 // In string mode, just add the output
744 else {
745 _addOutput();
746 }
747 }
748
749 if (self.opts.compileDebug && newLineCount) {
750 this.currentLine += newLineCount;
751 this.source += ' ; __line = ' + this.currentLine + '\n';
752 }
753 }
754};
755
756/**
757 * Escape characters reserved in XML.
758 *
759 * This is simply an export of {@link module:utils.escapeXML}.
760 *
761 * If `markup` is `undefined` or `null`, the empty string is returned.
762 *
763 * @param {String} markup Input string
764 * @return {String} Escaped string
765 * @public
766 * @func
767 * */
768exports.escapeXML = utils.escapeXML;
769
770/**
771 * Express.js support.
772 *
773 * This is an alias for {@link module:ejs.renderFile}, in order to support
774 * Express.js out-of-the-box.
775 *
776 * @func
777 */
778
779exports.__express = exports.renderFile;
780
781// Add require support
782/* istanbul ignore else */
783if (require.extensions) {
784 require.extensions['.ejs'] = function (module, flnm) {
785 var filename = flnm || /* istanbul ignore next */ module.filename;
786 var options = {
787 filename: filename,
788 client: true
789 };
790 var template = fs.readFileSync(filename).toString();
791 var fn = exports.compile(template, options);
792 module._compile('module.exports = ' + fn.toString() + ';', filename);
793 };
794}
795
796/**
797 * Version of EJS.
798 *
799 * @readonly
800 * @type {String}
801 * @public
802 */
803
804exports.VERSION = _VERSION_STRING;
805
806/* istanbul ignore if */
807if (typeof window != 'undefined') {
808 window.ejs = exports;
809}
810
811},{"../package.json":6,"./utils":2,"fs":3,"path":4}],2:[function(require,module,exports){
812/*
813 * EJS Embedded JavaScript templates
814 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
815 *
816 * Licensed under the Apache License, Version 2.0 (the "License");
817 * you may not use this file except in compliance with the License.
818 * You may obtain a copy of the License at
819 *
820 * http://www.apache.org/licenses/LICENSE-2.0
821 *
822 * Unless required by applicable law or agreed to in writing, software
823 * distributed under the License is distributed on an "AS IS" BASIS,
824 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
825 * See the License for the specific language governing permissions and
826 * limitations under the License.
827 *
828*/
829
830/**
831 * Private utility functions
832 * @module utils
833 * @private
834 */
835
836'use strict';
837
838var regExpChars = /[|\\{}()[\]^$+*?.]/g;
839
840/**
841 * Escape characters reserved in regular expressions.
842 *
843 * If `string` is `undefined` or `null`, the empty string is returned.
844 *
845 * @param {String} string Input string
846 * @return {String} Escaped string
847 * @static
848 * @private
849 */
850exports.escapeRegExpChars = function (string) {
851 // istanbul ignore if
852 if (!string) {
853 return '';
854 }
855 return String(string).replace(regExpChars, '\\$&');
856};
857
858var _ENCODE_HTML_RULES = {
859 '&': '&amp;',
860 '<': '&lt;',
861 '>': '&gt;',
862 '"': '&#34;',
863 "'": '&#39;'
864};
865var _MATCH_HTML = /[&<>\'"]/g;
866
867function encode_char(c) {
868 return _ENCODE_HTML_RULES[c] || c;
869}
870
871/**
872 * Stringified version of constants used by {@link module:utils.escapeXML}.
873 *
874 * It is used in the process of generating {@link ClientFunction}s.
875 *
876 * @readonly
877 * @type {String}
878 */
879
880var escapeFuncStr =
881 'var _ENCODE_HTML_RULES = {\n'
882+ ' "&": "&amp;"\n'
883+ ' , "<": "&lt;"\n'
884+ ' , ">": "&gt;"\n'
885+ ' , \'"\': "&#34;"\n'
886+ ' , "\'": "&#39;"\n'
887+ ' }\n'
888+ ' , _MATCH_HTML = /[&<>\'"]/g;\n'
889+ 'function encode_char(c) {\n'
890+ ' return _ENCODE_HTML_RULES[c] || c;\n'
891+ '};\n';
892
893/**
894 * Escape characters reserved in XML.
895 *
896 * If `markup` is `undefined` or `null`, the empty string is returned.
897 *
898 * @implements {EscapeCallback}
899 * @param {String} markup Input string
900 * @return {String} Escaped string
901 * @static
902 * @private
903 */
904
905exports.escapeXML = function (markup) {
906 return markup == undefined
907 ? ''
908 : String(markup)
909 .replace(_MATCH_HTML, encode_char);
910};
911exports.escapeXML.toString = function () {
912 return Function.prototype.toString.call(this) + ';\n' + escapeFuncStr;
913};
914
915/**
916 * Copy all properties from one object to another, in a shallow fashion.
917 *
918 * @param {Object} to Destination object
919 * @param {Object} from Source object
920 * @return {Object} Destination object
921 * @static
922 * @private
923 */
924exports.shallowCopy = function (to, from) {
925 from = from || {};
926 for (var p in from) {
927 to[p] = from[p];
928 }
929 return to;
930};
931
932/**
933 * Simple in-process cache implementation. Does not implement limits of any
934 * sort.
935 *
936 * @implements Cache
937 * @static
938 * @private
939 */
940exports.cache = {
941 _data: {},
942 set: function (key, val) {
943 this._data[key] = val;
944 },
945 get: function (key) {
946 return this._data[key];
947 },
948 reset: function () {
949 this._data = {};
950 }
951};
952
953},{}],3:[function(require,module,exports){
954
955},{}],4:[function(require,module,exports){
956(function (process){
957// Copyright Joyent, Inc. and other Node contributors.
958//
959// Permission is hereby granted, free of charge, to any person obtaining a
960// copy of this software and associated documentation files (the
961// "Software"), to deal in the Software without restriction, including
962// without limitation the rights to use, copy, modify, merge, publish,
963// distribute, sublicense, and/or sell copies of the Software, and to permit
964// persons to whom the Software is furnished to do so, subject to the
965// following conditions:
966//
967// The above copyright notice and this permission notice shall be included
968// in all copies or substantial portions of the Software.
969//
970// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
971// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
972// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
973// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
974// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
975// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
976// USE OR OTHER DEALINGS IN THE SOFTWARE.
977
978// resolves . and .. elements in a path array with directory names there
979// must be no slashes, empty elements, or device names (c:\) in the array
980// (so also no leading and trailing slashes - it does not distinguish
981// relative and absolute paths)
982function normalizeArray(parts, allowAboveRoot) {
983 // if the path tries to go above the root, `up` ends up > 0
984 var up = 0;
985 for (var i = parts.length - 1; i >= 0; i--) {
986 var last = parts[i];
987 if (last === '.') {
988 parts.splice(i, 1);
989 } else if (last === '..') {
990 parts.splice(i, 1);
991 up++;
992 } else if (up) {
993 parts.splice(i, 1);
994 up--;
995 }
996 }
997
998 // if the path is allowed to go above the root, restore leading ..s
999 if (allowAboveRoot) {
1000 for (; up--; up) {
1001 parts.unshift('..');
1002 }
1003 }
1004
1005 return parts;
1006}
1007
1008// Split a filename into [root, dir, basename, ext], unix version
1009// 'root' is just a slash, or nothing.
1010var splitPathRe =
1011 /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
1012var splitPath = function(filename) {
1013 return splitPathRe.exec(filename).slice(1);
1014};
1015
1016// path.resolve([from ...], to)
1017// posix version
1018exports.resolve = function() {
1019 var resolvedPath = '',
1020 resolvedAbsolute = false;
1021
1022 for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
1023 var path = (i >= 0) ? arguments[i] : process.cwd();
1024
1025 // Skip empty and invalid entries
1026 if (typeof path !== 'string') {
1027 throw new TypeError('Arguments to path.resolve must be strings');
1028 } else if (!path) {
1029 continue;
1030 }
1031
1032 resolvedPath = path + '/' + resolvedPath;
1033 resolvedAbsolute = path.charAt(0) === '/';
1034 }
1035
1036 // At this point the path should be resolved to a full absolute path, but
1037 // handle relative paths to be safe (might happen when process.cwd() fails)
1038
1039 // Normalize the path
1040 resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
1041 return !!p;
1042 }), !resolvedAbsolute).join('/');
1043
1044 return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
1045};
1046
1047// path.normalize(path)
1048// posix version
1049exports.normalize = function(path) {
1050 var isAbsolute = exports.isAbsolute(path),
1051 trailingSlash = substr(path, -1) === '/';
1052
1053 // Normalize the path
1054 path = normalizeArray(filter(path.split('/'), function(p) {
1055 return !!p;
1056 }), !isAbsolute).join('/');
1057
1058 if (!path && !isAbsolute) {
1059 path = '.';
1060 }
1061 if (path && trailingSlash) {
1062 path += '/';
1063 }
1064
1065 return (isAbsolute ? '/' : '') + path;
1066};
1067
1068// posix version
1069exports.isAbsolute = function(path) {
1070 return path.charAt(0) === '/';
1071};
1072
1073// posix version
1074exports.join = function() {
1075 var paths = Array.prototype.slice.call(arguments, 0);
1076 return exports.normalize(filter(paths, function(p, index) {
1077 if (typeof p !== 'string') {
1078 throw new TypeError('Arguments to path.join must be strings');
1079 }
1080 return p;
1081 }).join('/'));
1082};
1083
1084
1085// path.relative(from, to)
1086// posix version
1087exports.relative = function(from, to) {
1088 from = exports.resolve(from).substr(1);
1089 to = exports.resolve(to).substr(1);
1090
1091 function trim(arr) {
1092 var start = 0;
1093 for (; start < arr.length; start++) {
1094 if (arr[start] !== '') break;
1095 }
1096
1097 var end = arr.length - 1;
1098 for (; end >= 0; end--) {
1099 if (arr[end] !== '') break;
1100 }
1101
1102 if (start > end) return [];
1103 return arr.slice(start, end - start + 1);
1104 }
1105
1106 var fromParts = trim(from.split('/'));
1107 var toParts = trim(to.split('/'));
1108
1109 var length = Math.min(fromParts.length, toParts.length);
1110 var samePartsLength = length;
1111 for (var i = 0; i < length; i++) {
1112 if (fromParts[i] !== toParts[i]) {
1113 samePartsLength = i;
1114 break;
1115 }
1116 }
1117
1118 var outputParts = [];
1119 for (var i = samePartsLength; i < fromParts.length; i++) {
1120 outputParts.push('..');
1121 }
1122
1123 outputParts = outputParts.concat(toParts.slice(samePartsLength));
1124
1125 return outputParts.join('/');
1126};
1127
1128exports.sep = '/';
1129exports.delimiter = ':';
1130
1131exports.dirname = function(path) {
1132 var result = splitPath(path),
1133 root = result[0],
1134 dir = result[1];
1135
1136 if (!root && !dir) {
1137 // No dirname whatsoever
1138 return '.';
1139 }
1140
1141 if (dir) {
1142 // It has a dirname, strip trailing slash
1143 dir = dir.substr(0, dir.length - 1);
1144 }
1145
1146 return root + dir;
1147};
1148
1149
1150exports.basename = function(path, ext) {
1151 var f = splitPath(path)[2];
1152 // TODO: make this comparison case-insensitive on windows?
1153 if (ext && f.substr(-1 * ext.length) === ext) {
1154 f = f.substr(0, f.length - ext.length);
1155 }
1156 return f;
1157};
1158
1159
1160exports.extname = function(path) {
1161 return splitPath(path)[3];
1162};
1163
1164function filter (xs, f) {
1165 if (xs.filter) return xs.filter(f);
1166 var res = [];
1167 for (var i = 0; i < xs.length; i++) {
1168 if (f(xs[i], i, xs)) res.push(xs[i]);
1169 }
1170 return res;
1171}
1172
1173// String.prototype.substr - negative index don't work in IE8
1174var substr = 'ab'.substr(-1) === 'b'
1175 ? function (str, start, len) { return str.substr(start, len) }
1176 : function (str, start, len) {
1177 if (start < 0) start = str.length + start;
1178 return str.substr(start, len);
1179 }
1180;
1181
1182}).call(this,require('_process'))
1183},{"_process":5}],5:[function(require,module,exports){
1184// shim for using process in browser
1185var process = module.exports = {};
1186
1187// cached from whatever global is present so that test runners that stub it
1188// don't break things. But we need to wrap it in a try catch in case it is
1189// wrapped in strict mode code which doesn't define any globals. It's inside a
1190// function because try/catches deoptimize in certain engines.
1191
1192var cachedSetTimeout;
1193var cachedClearTimeout;
1194
1195function defaultSetTimout() {
1196 throw new Error('setTimeout has not been defined');
1197}
1198function defaultClearTimeout () {
1199 throw new Error('clearTimeout has not been defined');
1200}
1201(function () {
1202 try {
1203 if (typeof setTimeout === 'function') {
1204 cachedSetTimeout = setTimeout;
1205 } else {
1206 cachedSetTimeout = defaultSetTimout;
1207 }
1208 } catch (e) {
1209 cachedSetTimeout = defaultSetTimout;
1210 }
1211 try {
1212 if (typeof clearTimeout === 'function') {
1213 cachedClearTimeout = clearTimeout;
1214 } else {
1215 cachedClearTimeout = defaultClearTimeout;
1216 }
1217 } catch (e) {
1218 cachedClearTimeout = defaultClearTimeout;
1219 }
1220} ())
1221function runTimeout(fun) {
1222 if (cachedSetTimeout === setTimeout) {
1223 //normal enviroments in sane situations
1224 return setTimeout(fun, 0);
1225 }
1226 // if setTimeout wasn't available but was latter defined
1227 if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
1228 cachedSetTimeout = setTimeout;
1229 return setTimeout(fun, 0);
1230 }
1231 try {
1232 // when when somebody has screwed with setTimeout but no I.E. maddness
1233 return cachedSetTimeout(fun, 0);
1234 } catch(e){
1235 try {
1236 // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
1237 return cachedSetTimeout.call(null, fun, 0);
1238 } catch(e){
1239 // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
1240 return cachedSetTimeout.call(this, fun, 0);
1241 }
1242 }
1243
1244
1245}
1246function runClearTimeout(marker) {
1247 if (cachedClearTimeout === clearTimeout) {
1248 //normal enviroments in sane situations
1249 return clearTimeout(marker);
1250 }
1251 // if clearTimeout wasn't available but was latter defined
1252 if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
1253 cachedClearTimeout = clearTimeout;
1254 return clearTimeout(marker);
1255 }
1256 try {
1257 // when when somebody has screwed with setTimeout but no I.E. maddness
1258 return cachedClearTimeout(marker);
1259 } catch (e){
1260 try {
1261 // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
1262 return cachedClearTimeout.call(null, marker);
1263 } catch (e){
1264 // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
1265 // Some versions of I.E. have different rules for clearTimeout vs setTimeout
1266 return cachedClearTimeout.call(this, marker);
1267 }
1268 }
1269
1270
1271
1272}
1273var queue = [];
1274var draining = false;
1275var currentQueue;
1276var queueIndex = -1;
1277
1278function cleanUpNextTick() {
1279 if (!draining || !currentQueue) {
1280 return;
1281 }
1282 draining = false;
1283 if (currentQueue.length) {
1284 queue = currentQueue.concat(queue);
1285 } else {
1286 queueIndex = -1;
1287 }
1288 if (queue.length) {
1289 drainQueue();
1290 }
1291}
1292
1293function drainQueue() {
1294 if (draining) {
1295 return;
1296 }
1297 var timeout = runTimeout(cleanUpNextTick);
1298 draining = true;
1299
1300 var len = queue.length;
1301 while(len) {
1302 currentQueue = queue;
1303 queue = [];
1304 while (++queueIndex < len) {
1305 if (currentQueue) {
1306 currentQueue[queueIndex].run();
1307 }
1308 }
1309 queueIndex = -1;
1310 len = queue.length;
1311 }
1312 currentQueue = null;
1313 draining = false;
1314 runClearTimeout(timeout);
1315}
1316
1317process.nextTick = function (fun) {
1318 var args = new Array(arguments.length - 1);
1319 if (arguments.length > 1) {
1320 for (var i = 1; i < arguments.length; i++) {
1321 args[i - 1] = arguments[i];
1322 }
1323 }
1324 queue.push(new Item(fun, args));
1325 if (queue.length === 1 && !draining) {
1326 runTimeout(drainQueue);
1327 }
1328};
1329
1330// v8 likes predictible objects
1331function Item(fun, array) {
1332 this.fun = fun;
1333 this.array = array;
1334}
1335Item.prototype.run = function () {
1336 this.fun.apply(null, this.array);
1337};
1338process.title = 'browser';
1339process.browser = true;
1340process.env = {};
1341process.argv = [];
1342process.version = ''; // empty string to avoid regexp issues
1343process.versions = {};
1344
1345function noop() {}
1346
1347process.on = noop;
1348process.addListener = noop;
1349process.once = noop;
1350process.off = noop;
1351process.removeListener = noop;
1352process.removeAllListeners = noop;
1353process.emit = noop;
1354
1355process.binding = function (name) {
1356 throw new Error('process.binding is not supported');
1357};
1358
1359process.cwd = function () { return '/' };
1360process.chdir = function (dir) {
1361 throw new Error('process.chdir is not supported');
1362};
1363process.umask = function() { return 0; };
1364
1365},{}],6:[function(require,module,exports){
1366module.exports={
1367 "name": "ejs",
1368 "description": "Embedded JavaScript templates",
1369 "keywords": [
1370 "template",
1371 "engine",
1372 "ejs"
1373 ],
1374 "version": "2.5.2",
1375 "author": "Matthew Eernisse <mde@fleegix.org> (http://fleegix.org)",
1376 "contributors": [
1377 "Timothy Gu <timothygu99@gmail.com> (https://timothygu.github.io)"
1378 ],
1379 "license": "Apache-2.0",
1380 "main": "./lib/ejs.js",
1381 "repository": {
1382 "type": "git",
1383 "url": "git://github.com/mde/ejs.git"
1384 },
1385 "bugs": "https://github.com/mde/ejs/issues",
1386 "homepage": "https://github.com/mde/ejs",
1387 "dependencies": {},
1388 "devDependencies": {
1389 "browserify": "^13.0.1",
1390 "eslint": "^3.0.0",
1391 "git-directory-deploy": "^1.5.1",
1392 "istanbul": "~0.4.3",
1393 "jake": "^8.0.0",
1394 "jsdoc": "^3.4.0",
1395 "lru-cache": "^4.0.1",
1396 "mocha": "^3.0.2",
1397 "uglify-js": "^2.6.2"
1398 },
1399 "engines": {
1400 "node": ">=0.10.0"
1401 },
1402 "scripts": {
1403 "test": "mocha",
1404 "lint": "eslint \"**/*.js\" Jakefile",
1405 "coverage": "istanbul cover node_modules/mocha/bin/_mocha",
1406 "doc": "jake doc",
1407 "devdoc": "jake doc[dev]"
1408 }
1409}
1410
1411},{}]},{},[1])(1)
1412});
\No newline at end of file