유희정

change jade to ejs

...@@ -9,9 +9,9 @@ var usersRouter = require('./routes/users'); ...@@ -9,9 +9,9 @@ var usersRouter = require('./routes/users');
9 9
10 var app = express(); 10 var app = express();
11 11
12 -// view engine setup 12 +// view engine setup (뷰 폴더 경로 설정)
13 app.set('views', path.join(__dirname, 'views')); 13 app.set('views', path.join(__dirname, 'views'));
14 -app.set('view engine', 'jade'); 14 +app.set('view engine', 'ejs');
15 15
16 app.use(logger('dev')); 16 app.use(logger('dev'));
17 app.use(express.json()); 17 app.use(express.json());
......
1 +
2 + Apache License
3 + Version 2.0, January 2004
4 + http://www.apache.org/licenses/
5 +
6 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 +
8 + 1. Definitions.
9 +
10 + "License" shall mean the terms and conditions for use, reproduction,
11 + and distribution as defined by Sections 1 through 9 of this document.
12 +
13 + "Licensor" shall mean the copyright owner or entity authorized by
14 + the copyright owner that is granting the License.
15 +
16 + "Legal Entity" shall mean the union of the acting entity and all
17 + other entities that control, are controlled by, or are under common
18 + control with that entity. For the purposes of this definition,
19 + "control" means (i) the power, direct or indirect, to cause the
20 + direction or management of such entity, whether by contract or
21 + otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 + outstanding shares, or (iii) beneficial ownership of such entity.
23 +
24 + "You" (or "Your") shall mean an individual or Legal Entity
25 + exercising permissions granted by this License.
26 +
27 + "Source" form shall mean the preferred form for making modifications,
28 + including but not limited to software source code, documentation
29 + source, and configuration files.
30 +
31 + "Object" form shall mean any form resulting from mechanical
32 + transformation or translation of a Source form, including but
33 + not limited to compiled object code, generated documentation,
34 + and conversions to other media types.
35 +
36 + "Work" shall mean the work of authorship, whether in Source or
37 + Object form, made available under the License, as indicated by a
38 + copyright notice that is included in or attached to the work
39 + (an example is provided in the Appendix below).
40 +
41 + "Derivative Works" shall mean any work, whether in Source or Object
42 + form, that is based on (or derived from) the Work and for which the
43 + editorial revisions, annotations, elaborations, or other modifications
44 + represent, as a whole, an original work of authorship. For the purposes
45 + of this License, Derivative Works shall not include works that remain
46 + separable from, or merely link (or bind by name) to the interfaces of,
47 + the Work and Derivative Works thereof.
48 +
49 + "Contribution" shall mean any work of authorship, including
50 + the original version of the Work and any modifications or additions
51 + to that Work or Derivative Works thereof, that is intentionally
52 + submitted to Licensor for inclusion in the Work by the copyright owner
53 + or by an individual or Legal Entity authorized to submit on behalf of
54 + the copyright owner. For the purposes of this definition, "submitted"
55 + means any form of electronic, verbal, or written communication sent
56 + to the Licensor or its representatives, including but not limited to
57 + communication on electronic mailing lists, source code control systems,
58 + and issue tracking systems that are managed by, or on behalf of, the
59 + Licensor for the purpose of discussing and improving the Work, but
60 + excluding communication that is conspicuously marked or otherwise
61 + designated in writing by the copyright owner as "Not a Contribution."
62 +
63 + "Contributor" shall mean Licensor and any individual or Legal Entity
64 + on behalf of whom a Contribution has been received by Licensor and
65 + subsequently incorporated within the Work.
66 +
67 + 2. Grant of Copyright License. Subject to the terms and conditions of
68 + this License, each Contributor hereby grants to You a perpetual,
69 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 + copyright license to reproduce, prepare Derivative Works of,
71 + publicly display, publicly perform, sublicense, and distribute the
72 + Work and such Derivative Works in Source or Object form.
73 +
74 + 3. Grant of Patent License. Subject to the terms and conditions of
75 + this License, each Contributor hereby grants to You a perpetual,
76 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 + (except as stated in this section) patent license to make, have made,
78 + use, offer to sell, sell, import, and otherwise transfer the Work,
79 + where such license applies only to those patent claims licensable
80 + by such Contributor that are necessarily infringed by their
81 + Contribution(s) alone or by combination of their Contribution(s)
82 + with the Work to which such Contribution(s) was submitted. If You
83 + institute patent litigation against any entity (including a
84 + cross-claim or counterclaim in a lawsuit) alleging that the Work
85 + or a Contribution incorporated within the Work constitutes direct
86 + or contributory patent infringement, then any patent licenses
87 + granted to You under this License for that Work shall terminate
88 + as of the date such litigation is filed.
89 +
90 + 4. Redistribution. You may reproduce and distribute copies of the
91 + Work or Derivative Works thereof in any medium, with or without
92 + modifications, and in Source or Object form, provided that You
93 + meet the following conditions:
94 +
95 + (a) You must give any other recipients of the Work or
96 + Derivative Works a copy of this License; and
97 +
98 + (b) You must cause any modified files to carry prominent notices
99 + stating that You changed the files; and
100 +
101 + (c) You must retain, in the Source form of any Derivative Works
102 + that You distribute, all copyright, patent, trademark, and
103 + attribution notices from the Source form of the Work,
104 + excluding those notices that do not pertain to any part of
105 + the Derivative Works; and
106 +
107 + (d) If the Work includes a "NOTICE" text file as part of its
108 + distribution, then any Derivative Works that You distribute must
109 + include a readable copy of the attribution notices contained
110 + within such NOTICE file, excluding those notices that do not
111 + pertain to any part of the Derivative Works, in at least one
112 + of the following places: within a NOTICE text file distributed
113 + as part of the Derivative Works; within the Source form or
114 + documentation, if provided along with the Derivative Works; or,
115 + within a display generated by the Derivative Works, if and
116 + wherever such third-party notices normally appear. The contents
117 + of the NOTICE file are for informational purposes only and
118 + do not modify the License. You may add Your own attribution
119 + notices within Derivative Works that You distribute, alongside
120 + or as an addendum to the NOTICE text from the Work, provided
121 + that such additional attribution notices cannot be construed
122 + as modifying the License.
123 +
124 + You may add Your own copyright statement to Your modifications and
125 + may provide additional or different license terms and conditions
126 + for use, reproduction, or distribution of Your modifications, or
127 + for any such Derivative Works as a whole, provided Your use,
128 + reproduction, and distribution of the Work otherwise complies with
129 + the conditions stated in this License.
130 +
131 + 5. Submission of Contributions. Unless You explicitly state otherwise,
132 + any Contribution intentionally submitted for inclusion in the Work
133 + by You to the Licensor shall be under the terms and conditions of
134 + this License, without any additional terms or conditions.
135 + Notwithstanding the above, nothing herein shall supersede or modify
136 + the terms of any separate license agreement you may have executed
137 + with Licensor regarding such Contributions.
138 +
139 + 6. Trademarks. This License does not grant permission to use the trade
140 + names, trademarks, service marks, or product names of the Licensor,
141 + except as required for reasonable and customary use in describing the
142 + origin of the Work and reproducing the content of the NOTICE file.
143 +
144 + 7. Disclaimer of Warranty. Unless required by applicable law or
145 + agreed to in writing, Licensor provides the Work (and each
146 + Contributor provides its Contributions) on an "AS IS" BASIS,
147 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 + implied, including, without limitation, any warranties or conditions
149 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 + PARTICULAR PURPOSE. You are solely responsible for determining the
151 + appropriateness of using or redistributing the Work and assume any
152 + risks associated with Your exercise of permissions under this License.
153 +
154 + 8. Limitation of Liability. In no event and under no legal theory,
155 + whether in tort (including negligence), contract, or otherwise,
156 + unless required by applicable law (such as deliberate and grossly
157 + negligent acts) or agreed to in writing, shall any Contributor be
158 + liable to You for damages, including any direct, indirect, special,
159 + incidental, or consequential damages of any character arising as a
160 + result of this License or out of the use or inability to use the
161 + Work (including but not limited to damages for loss of goodwill,
162 + work stoppage, computer failure or malfunction, or any and all
163 + other commercial damages or losses), even if such Contributor
164 + has been advised of the possibility of such damages.
165 +
166 + 9. Accepting Warranty or Additional Liability. While redistributing
167 + the Work or Derivative Works thereof, You may choose to offer,
168 + and charge a fee for, acceptance of support, warranty, indemnity,
169 + or other liability obligations and/or rights consistent with this
170 + License. However, in accepting such obligations, You may act only
171 + on Your own behalf and on Your sole responsibility, not on behalf
172 + of any other Contributor, and only if You agree to indemnify,
173 + defend, and hold each Contributor harmless for any liability
174 + incurred by, or claims asserted against, such Contributor by reason
175 + of your accepting any such warranty or additional liability.
176 +
177 + END OF TERMS AND CONDITIONS
178 +
179 + APPENDIX: How to apply the Apache License to your work.
180 +
181 + To apply the Apache License to your work, attach the following
182 + boilerplate notice, with the fields enclosed by brackets "[]"
183 + replaced with your own identifying information. (Don't include
184 + the brackets!) The text should be enclosed in the appropriate
185 + comment syntax for the file format. We also recommend that a
186 + file or class name and description of purpose be included on the
187 + same "printed page" as the copyright notice for easier
188 + identification within third-party archives.
189 +
190 + Copyright [yyyy] [name of copyright owner]
191 +
192 + Licensed under the Apache License, Version 2.0 (the "License");
193 + you may not use this file except in compliance with the License.
194 + You may obtain a copy of the License at
195 +
196 + http://www.apache.org/licenses/LICENSE-2.0
197 +
198 + Unless required by applicable law or agreed to in writing, software
199 + distributed under the License is distributed on an "AS IS" BASIS,
200 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 + See the License for the specific language governing permissions and
202 + limitations under the License.
1 +# EJS
2 +
3 +Embedded JavaScript templates
4 +
5 +[![Build Status](https://img.shields.io/travis/mde/ejs/master.svg?style=flat)](https://travis-ci.org/mde/ejs)
6 +[![Developing Dependencies](https://img.shields.io/david/dev/mde/ejs.svg?style=flat)](https://david-dm.org/mde/ejs?type=dev)
7 +[![Known Vulnerabilities](https://snyk.io/test/npm/ejs/badge.svg?style=flat)](https://snyk.io/test/npm/ejs)
8 +
9 +## Installation
10 +
11 +```bash
12 +$ npm install ejs
13 +```
14 +
15 +## Features
16 +
17 + * Control flow with `<% %>`
18 + * Escaped output with `<%= %>` (escape function configurable)
19 + * Unescaped raw output with `<%- %>`
20 + * Newline-trim mode ('newline slurping') with `-%>` ending tag
21 + * Whitespace-trim mode (slurp all whitespace) for control flow with `<%_ _%>`
22 + * Custom delimiters (e.g., use `<? ?>` instead of `<% %>`)
23 + * Includes
24 + * Client-side support
25 + * Static caching of intermediate JavaScript
26 + * Static caching of templates
27 + * Complies with the [Express](http://expressjs.com) view system
28 +
29 +## Example
30 +
31 +```ejs
32 +<% if (user) { %>
33 + <h2><%= user.name %></h2>
34 +<% } %>
35 +```
36 +
37 +Try EJS online at: https://ionicabizau.github.io/ejs-playground/.
38 +
39 +## Usage
40 +
41 +```javascript
42 +let template = ejs.compile(str, options);
43 +template(data);
44 +// => Rendered HTML string
45 +
46 +ejs.render(str, data, options);
47 +// => Rendered HTML string
48 +
49 +ejs.renderFile(filename, data, options, function(err, str){
50 + // str => Rendered HTML string
51 +});
52 +```
53 +
54 +It is also possible to use `ejs.render(dataAndOptions);` where you pass
55 +everything in a single object. In that case, you'll end up with local variables
56 +for all the passed options. However, be aware that your code could break if we
57 +add an option with the same name as one of your data object's properties.
58 +Therefore, we do not recommend using this shortcut.
59 +
60 +## Options
61 +
62 + - `cache` Compiled functions are cached, requires `filename`
63 + - `filename` The name of the file being rendered. Not required if you
64 + are using `renderFile()`. Used by `cache` to key caches, and for includes.
65 + - `root` Set project root for includes with an absolute path (/file.ejs).
66 + - `context` Function execution context
67 + - `compileDebug` When `false` no debug instrumentation is compiled
68 + - `client` When `true`, compiles a function that can be rendered
69 + in the browser without needing to load the EJS Runtime
70 + ([ejs.min.js](https://github.com/mde/ejs/releases/latest)).
71 + - `delimiter` Character to use with angle brackets for open/close
72 + - `debug` Output generated function body
73 + - `strict` When set to `true`, generated function is in strict mode
74 + - `_with` Whether or not to use `with() {}` constructs. If `false`
75 + then the locals will be stored in the `locals` object. Set to `false` in strict mode.
76 + - `destructuredLocals` An array of local variables that are always destructured from
77 + the locals object, available even in strict mode.
78 + - `localsName` Name to use for the object storing local variables when not using
79 + `with` Defaults to `locals`
80 + - `rmWhitespace` Remove all safe-to-remove whitespace, including leading
81 + and trailing whitespace. It also enables a safer version of `-%>` line
82 + slurping for all scriptlet tags (it does not strip new lines of tags in
83 + the middle of a line).
84 + - `escape` The escaping function used with `<%=` construct. It is
85 + used in rendering and is `.toString()`ed in the generation of client functions.
86 + (By default escapes XML).
87 + - `outputFunctionName` Set to a string (e.g., 'echo' or 'print') for a function to print
88 + output inside scriptlet tags.
89 + - `async` When `true`, EJS will use an async function for rendering. (Depends
90 + on async/await support in the JS runtime.
91 +
92 +This project uses [JSDoc](http://usejsdoc.org/). For the full public API
93 +documentation, clone the repository and run `npm run doc`. This will run JSDoc
94 +with the proper options and output the documentation to `out/`. If you want
95 +the both the public & private API docs, run `npm run devdoc` instead.
96 +
97 +## Tags
98 +
99 + - `<%` 'Scriptlet' tag, for control-flow, no output
100 + - `<%_` 'Whitespace Slurping' Scriptlet tag, strips all whitespace before it
101 + - `<%=` Outputs the value into the template (escaped)
102 + - `<%-` Outputs the unescaped value into the template
103 + - `<%#` Comment tag, no execution, no output
104 + - `<%%` Outputs a literal '<%'
105 + - `%%>` Outputs a literal '%>'
106 + - `%>` Plain ending tag
107 + - `-%>` Trim-mode ('newline slurp') tag, trims following newline
108 + - `_%>` 'Whitespace Slurping' ending tag, removes all whitespace after it
109 +
110 +For the full syntax documentation, please see [docs/syntax.md](https://github.com/mde/ejs/blob/master/docs/syntax.md).
111 +
112 +## Includes
113 +
114 +Includes either have to be an absolute path, or, if not, are assumed as
115 +relative to the template with the `include` call. For example if you are
116 +including `./views/user/show.ejs` from `./views/users.ejs` you would
117 +use `<%- include('user/show') %>`.
118 +
119 +You must specify the `filename` option for the template with the `include`
120 +call unless you are using `renderFile()`.
121 +
122 +You'll likely want to use the raw output tag (`<%-`) with your include to avoid
123 +double-escaping the HTML output.
124 +
125 +```ejs
126 +<ul>
127 + <% users.forEach(function(user){ %>
128 + <%- include('user/show', {user: user}) %>
129 + <% }); %>
130 +</ul>
131 +```
132 +
133 +Includes are inserted at runtime, so you can use variables for the path in the
134 +`include` call (for example `<%- include(somePath) %>`). Variables in your
135 +top-level data object are available to all your includes, but local variables
136 +need to be passed down.
137 +
138 +NOTE: Include preprocessor directives (`<% include user/show %>`) are
139 +still supported.
140 +
141 +## Custom delimiters
142 +
143 +Custom delimiters can be applied on a per-template basis, or globally:
144 +
145 +```javascript
146 +let ejs = require('ejs'),
147 + users = ['geddy', 'neil', 'alex'];
148 +
149 +// Just one template
150 +ejs.render('<?= users.join(" | "); ?>', {users: users}, {delimiter: '?'});
151 +// => 'geddy | neil | alex'
152 +
153 +// Or globally
154 +ejs.delimiter = '$';
155 +ejs.render('<$= users.join(" | "); $>', {users: users});
156 +// => 'geddy | neil | alex'
157 +```
158 +
159 +## Caching
160 +
161 +EJS ships with a basic in-process cache for caching the intermediate JavaScript
162 +functions used to render templates. It's easy to plug in LRU caching using
163 +Node's `lru-cache` library:
164 +
165 +```javascript
166 +let ejs = require('ejs'),
167 + LRU = require('lru-cache');
168 +ejs.cache = LRU(100); // LRU cache with 100-item limit
169 +```
170 +
171 +If you want to clear the EJS cache, call `ejs.clearCache`. If you're using the
172 +LRU cache and need a different limit, simple reset `ejs.cache` to a new instance
173 +of the LRU.
174 +
175 +## Custom file loader
176 +
177 +The default file loader is `fs.readFileSync`, if you want to customize it, you can set ejs.fileLoader.
178 +
179 +```javascript
180 +let ejs = require('ejs');
181 +let myFileLoad = function (filePath) {
182 + return 'myFileLoad: ' + fs.readFileSync(filePath);
183 +};
184 +
185 +ejs.fileLoader = myFileLoad;
186 +```
187 +
188 +With this feature, you can preprocess the template before reading it.
189 +
190 +## Layouts
191 +
192 +EJS does not specifically support blocks, but layouts can be implemented by
193 +including headers and footers, like so:
194 +
195 +
196 +```ejs
197 +<%- include('header') -%>
198 +<h1>
199 + Title
200 +</h1>
201 +<p>
202 + My page
203 +</p>
204 +<%- include('footer') -%>
205 +```
206 +
207 +## Client-side support
208 +
209 +Go to the [Latest Release](https://github.com/mde/ejs/releases/latest), download
210 +`./ejs.js` or `./ejs.min.js`. Alternately, you can compile it yourself by cloning
211 +the repository and running `jake build` (or `$(npm bin)/jake build` if jake is
212 +not installed globally).
213 +
214 +Include one of these files on your page, and `ejs` should be available globally.
215 +
216 +### Example
217 +
218 +```html
219 +<div id="output"></div>
220 +<script src="ejs.min.js"></script>
221 +<script>
222 + let people = ['geddy', 'neil', 'alex'],
223 + html = ejs.render('<%= people.join(", "); %>', {people: people});
224 + // With jQuery:
225 + $('#output').html(html);
226 + // Vanilla JS:
227 + document.getElementById('output').innerHTML = html;
228 +</script>
229 +```
230 +
231 +### Caveats
232 +
233 +Most of EJS will work as expected; however, there are a few things to note:
234 +
235 +1. Obviously, since you do not have access to the filesystem, `ejs.renderFile()` won't work.
236 +2. For the same reason, `include`s do not work unless you use an `include callback`. Here is an example:
237 + ```javascript
238 + let str = "Hello <%= include('file', {person: 'John'}); %>",
239 + fn = ejs.compile(str, {client: true});
240 +
241 + fn(data, null, function(path, d){ // include callback
242 + // path -> 'file'
243 + // d -> {person: 'John'}
244 + // Put your code here
245 + // Return the contents of file as a string
246 + }); // returns rendered string
247 + ```
248 +
249 +See the [examples folder](https://github.com/mde/ejs/tree/master/examples) for more details.
250 +
251 +### IDE Integration with Syntax Highlighting
252 +
253 +VSCode:Javascript EJS by *DigitalBrainstem*
254 +
255 +## Related projects
256 +
257 +There are a number of implementations of EJS:
258 +
259 + * TJ's implementation, the v1 of this library: https://github.com/tj/ejs
260 + * Jupiter Consulting's EJS: http://www.embeddedjs.com/
261 + * EJS Embedded JavaScript Framework on Google Code: https://code.google.com/p/embeddedjavascript/
262 + * Sam Stephenson's Ruby implementation: https://rubygems.org/gems/ejs
263 + * Erubis, an ERB implementation which also runs JavaScript: http://www.kuwata-lab.com/erubis/users-guide.04.html#lang-javascript
264 + * DigitalBrainstem EJS Language support: https://github.com/Digitalbrainstem/ejs-grammar
265 +
266 +## License
267 +
268 +Licensed under the Apache License, Version 2.0
269 +(<http://www.apache.org/licenses/LICENSE-2.0>)
270 +
271 +- - -
272 +EJS Embedded JavaScript templates copyright 2112
273 +mde@fleegix.org.
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(){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}return e})()({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. {@link http://ejs.co}
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 +
48 +var fs = require('fs');
49 +var path = require('path');
50 +var utils = require('./utils');
51 +
52 +var scopeOptionWarned = false;
53 +var _VERSION_STRING = require('../package.json').version;
54 +var _DEFAULT_OPEN_DELIMITER = '<';
55 +var _DEFAULT_CLOSE_DELIMITER = '>';
56 +var _DEFAULT_DELIMITER = '%';
57 +var _DEFAULT_LOCALS_NAME = 'locals';
58 +var _NAME = 'ejs';
59 +var _REGEX_STRING = '(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)';
60 +var _OPTS_PASSABLE_WITH_DATA = ['delimiter', 'scope', 'context', 'debug', 'compileDebug',
61 + 'client', '_with', 'rmWhitespace', 'strict', 'filename', 'async'];
62 +// We don't allow 'cache' option to be passed in the data obj for
63 +// the normal `render` call, but this is where Express 2 & 3 put it
64 +// so we make an exception for `renderFile`
65 +var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat('cache');
66 +var _BOM = /^\uFEFF/;
67 +
68 +/**
69 + * EJS template function cache. This can be a LRU object from lru-cache NPM
70 + * module. By default, it is {@link module:utils.cache}, a simple in-process
71 + * cache that grows continuously.
72 + *
73 + * @type {Cache}
74 + */
75 +
76 +exports.cache = utils.cache;
77 +
78 +/**
79 + * Custom file loader. Useful for template preprocessing or restricting access
80 + * to a certain part of the filesystem.
81 + *
82 + * @type {fileLoader}
83 + */
84 +
85 +exports.fileLoader = fs.readFileSync;
86 +
87 +/**
88 + * Name of the object containing the locals.
89 + *
90 + * This variable is overridden by {@link Options}`.localsName` if it is not
91 + * `undefined`.
92 + *
93 + * @type {String}
94 + * @public
95 + */
96 +
97 +exports.localsName = _DEFAULT_LOCALS_NAME;
98 +
99 +/**
100 + * Promise implementation -- defaults to the native implementation if available
101 + * This is mostly just for testability
102 + *
103 + * @type {Function}
104 + * @public
105 + */
106 +
107 +exports.promiseImpl = (new Function('return this;'))().Promise;
108 +
109 +/**
110 + * Get the path to the included file from the parent file path and the
111 + * specified path.
112 + *
113 + * @param {String} name specified path
114 + * @param {String} filename parent file path
115 + * @param {Boolean} isDir parent file path whether is directory
116 + * @return {String}
117 + */
118 +exports.resolveInclude = function(name, filename, isDir) {
119 + var dirname = path.dirname;
120 + var extname = path.extname;
121 + var resolve = path.resolve;
122 + var includePath = resolve(isDir ? filename : dirname(filename), name);
123 + var ext = extname(name);
124 + if (!ext) {
125 + includePath += '.ejs';
126 + }
127 + return includePath;
128 +};
129 +
130 +/**
131 + * Get the path to the included file by Options
132 + *
133 + * @param {String} path specified path
134 + * @param {Options} options compilation options
135 + * @return {String}
136 + */
137 +function getIncludePath(path, options) {
138 + var includePath;
139 + var filePath;
140 + var views = options.views;
141 + var match = /^[A-Za-z]+:\\|^\//.exec(path);
142 +
143 + // Abs path
144 + if (match && match.length) {
145 + includePath = exports.resolveInclude(path.replace(/^\/*/,''), options.root || '/', true);
146 + }
147 + // Relative paths
148 + else {
149 + // Look relative to a passed filename first
150 + if (options.filename) {
151 + filePath = exports.resolveInclude(path, options.filename);
152 + if (fs.existsSync(filePath)) {
153 + includePath = filePath;
154 + }
155 + }
156 + // Then look in any views directories
157 + if (!includePath) {
158 + if (Array.isArray(views) && views.some(function (v) {
159 + filePath = exports.resolveInclude(path, v, true);
160 + return fs.existsSync(filePath);
161 + })) {
162 + includePath = filePath;
163 + }
164 + }
165 + if (!includePath) {
166 + throw new Error('Could not find the include file "' +
167 + options.escapeFunction(path) + '"');
168 + }
169 + }
170 + return includePath;
171 +}
172 +
173 +/**
174 + * Get the template from a string or a file, either compiled on-the-fly or
175 + * read from cache (if enabled), and cache the template if needed.
176 + *
177 + * If `template` is not set, the file specified in `options.filename` will be
178 + * read.
179 + *
180 + * If `options.cache` is true, this function reads the file from
181 + * `options.filename` so it must be set prior to calling this function.
182 + *
183 + * @memberof module:ejs-internal
184 + * @param {Options} options compilation options
185 + * @param {String} [template] template source
186 + * @return {(TemplateFunction|ClientFunction)}
187 + * Depending on the value of `options.client`, either type might be returned.
188 + * @static
189 + */
190 +
191 +function handleCache(options, template) {
192 + var func;
193 + var filename = options.filename;
194 + var hasTemplate = arguments.length > 1;
195 +
196 + if (options.cache) {
197 + if (!filename) {
198 + throw new Error('cache option requires a filename');
199 + }
200 + func = exports.cache.get(filename);
201 + if (func) {
202 + return func;
203 + }
204 + if (!hasTemplate) {
205 + template = fileLoader(filename).toString().replace(_BOM, '');
206 + }
207 + }
208 + else if (!hasTemplate) {
209 + // istanbul ignore if: should not happen at all
210 + if (!filename) {
211 + throw new Error('Internal EJS error: no file name or template '
212 + + 'provided');
213 + }
214 + template = fileLoader(filename).toString().replace(_BOM, '');
215 + }
216 + func = exports.compile(template, options);
217 + if (options.cache) {
218 + exports.cache.set(filename, func);
219 + }
220 + return func;
221 +}
222 +
223 +/**
224 + * Try calling handleCache with the given options and data and call the
225 + * callback with the result. If an error occurs, call the callback with
226 + * the error. Used by renderFile().
227 + *
228 + * @memberof module:ejs-internal
229 + * @param {Options} options compilation options
230 + * @param {Object} data template data
231 + * @param {RenderFileCallback} cb callback
232 + * @static
233 + */
234 +
235 +function tryHandleCache(options, data, cb) {
236 + var result;
237 + if (!cb) {
238 + if (typeof exports.promiseImpl == 'function') {
239 + return new exports.promiseImpl(function (resolve, reject) {
240 + try {
241 + result = handleCache(options)(data);
242 + resolve(result);
243 + }
244 + catch (err) {
245 + reject(err);
246 + }
247 + });
248 + }
249 + else {
250 + throw new Error('Please provide a callback function');
251 + }
252 + }
253 + else {
254 + try {
255 + result = handleCache(options)(data);
256 + }
257 + catch (err) {
258 + return cb(err);
259 + }
260 +
261 + cb(null, result);
262 + }
263 +}
264 +
265 +/**
266 + * fileLoader is independent
267 + *
268 + * @param {String} filePath ejs file path.
269 + * @return {String} The contents of the specified file.
270 + * @static
271 + */
272 +
273 +function fileLoader(filePath){
274 + return exports.fileLoader(filePath);
275 +}
276 +
277 +/**
278 + * Get the template function.
279 + *
280 + * If `options.cache` is `true`, then the template is cached.
281 + *
282 + * @memberof module:ejs-internal
283 + * @param {String} path path for the specified file
284 + * @param {Options} options compilation options
285 + * @return {(TemplateFunction|ClientFunction)}
286 + * Depending on the value of `options.client`, either type might be returned
287 + * @static
288 + */
289 +
290 +function includeFile(path, options) {
291 + var opts = utils.shallowCopy({}, options);
292 + opts.filename = getIncludePath(path, opts);
293 + return handleCache(opts);
294 +}
295 +
296 +/**
297 + * Re-throw the given `err` in context to the `str` of ejs, `filename`, and
298 + * `lineno`.
299 + *
300 + * @implements RethrowCallback
301 + * @memberof module:ejs-internal
302 + * @param {Error} err Error object
303 + * @param {String} str EJS source
304 + * @param {String} filename file name of the EJS file
305 + * @param {String} lineno line number of the error
306 + * @static
307 + */
308 +
309 +function rethrow(err, str, flnm, lineno, esc){
310 + var lines = str.split('\n');
311 + var start = Math.max(lineno - 3, 0);
312 + var end = Math.min(lines.length, lineno + 3);
313 + var filename = esc(flnm); // eslint-disable-line
314 + // Error context
315 + var context = lines.slice(start, end).map(function (line, i){
316 + var curr = i + start + 1;
317 + return (curr == lineno ? ' >> ' : ' ')
318 + + curr
319 + + '| '
320 + + line;
321 + }).join('\n');
322 +
323 + // Alter exception message
324 + err.path = filename;
325 + err.message = (filename || 'ejs') + ':'
326 + + lineno + '\n'
327 + + context + '\n\n'
328 + + err.message;
329 +
330 + throw err;
331 +}
332 +
333 +function stripSemi(str){
334 + return str.replace(/;(\s*$)/, '$1');
335 +}
336 +
337 +/**
338 + * Compile the given `str` of ejs into a template function.
339 + *
340 + * @param {String} template EJS template
341 + *
342 + * @param {Options} opts compilation options
343 + *
344 + * @return {(TemplateFunction|ClientFunction)}
345 + * Depending on the value of `opts.client`, either type might be returned.
346 + * Note that the return type of the function also depends on the value of `opts.async`.
347 + * @public
348 + */
349 +
350 +exports.compile = function compile(template, opts) {
351 + var templ;
352 +
353 + // v1 compat
354 + // 'scope' is 'context'
355 + // FIXME: Remove this in a future version
356 + if (opts && opts.scope) {
357 + if (!scopeOptionWarned){
358 + console.warn('`scope` option is deprecated and will be removed in EJS 3');
359 + scopeOptionWarned = true;
360 + }
361 + if (!opts.context) {
362 + opts.context = opts.scope;
363 + }
364 + delete opts.scope;
365 + }
366 + templ = new Template(template, opts);
367 + return templ.compile();
368 +};
369 +
370 +/**
371 + * Render the given `template` of ejs.
372 + *
373 + * If you would like to include options but not data, you need to explicitly
374 + * call this function with `data` being an empty object or `null`.
375 + *
376 + * @param {String} template EJS template
377 + * @param {Object} [data={}] template data
378 + * @param {Options} [opts={}] compilation and rendering options
379 + * @return {(String|Promise<String>)}
380 + * Return value type depends on `opts.async`.
381 + * @public
382 + */
383 +
384 +exports.render = function (template, d, o) {
385 + var data = d || {};
386 + var opts = o || {};
387 +
388 + // No options object -- if there are optiony names
389 + // in the data, copy them to options
390 + if (arguments.length == 2) {
391 + utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA);
392 + }
393 +
394 + return handleCache(opts, template)(data);
395 +};
396 +
397 +/**
398 + * Render an EJS file at the given `path` and callback `cb(err, str)`.
399 + *
400 + * If you would like to include options but not data, you need to explicitly
401 + * call this function with `data` being an empty object or `null`.
402 + *
403 + * @param {String} path path to the EJS file
404 + * @param {Object} [data={}] template data
405 + * @param {Options} [opts={}] compilation and rendering options
406 + * @param {RenderFileCallback} cb callback
407 + * @public
408 + */
409 +
410 +exports.renderFile = function () {
411 + var args = Array.prototype.slice.call(arguments);
412 + var filename = args.shift();
413 + var cb;
414 + var opts = {filename: filename};
415 + var data;
416 + var viewOpts;
417 +
418 + // Do we have a callback?
419 + if (typeof arguments[arguments.length - 1] == 'function') {
420 + cb = args.pop();
421 + }
422 + // Do we have data/opts?
423 + if (args.length) {
424 + // Should always have data obj
425 + data = args.shift();
426 + // Normal passed opts (data obj + opts obj)
427 + if (args.length) {
428 + // Use shallowCopy so we don't pollute passed in opts obj with new vals
429 + utils.shallowCopy(opts, args.pop());
430 + }
431 + // Special casing for Express (settings + opts-in-data)
432 + else {
433 + // Express 3 and 4
434 + if (data.settings) {
435 + // Pull a few things from known locations
436 + if (data.settings.views) {
437 + opts.views = data.settings.views;
438 + }
439 + if (data.settings['view cache']) {
440 + opts.cache = true;
441 + }
442 + // Undocumented after Express 2, but still usable, esp. for
443 + // items that are unsafe to be passed along with data, like `root`
444 + viewOpts = data.settings['view options'];
445 + if (viewOpts) {
446 + utils.shallowCopy(opts, viewOpts);
447 + }
448 + }
449 + // Express 2 and lower, values set in app.locals, or people who just
450 + // want to pass options in their data. NOTE: These values will override
451 + // anything previously set in settings or settings['view options']
452 + utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA_EXPRESS);
453 + }
454 + opts.filename = filename;
455 + }
456 + else {
457 + data = {};
458 + }
459 +
460 + return tryHandleCache(opts, data, cb);
461 +};
462 +
463 +/**
464 + * Clear intermediate JavaScript cache. Calls {@link Cache#reset}.
465 + * @public
466 + */
467 +
468 +/**
469 + * EJS template class
470 + * @public
471 + */
472 +exports.Template = Template;
473 +
474 +exports.clearCache = function () {
475 + exports.cache.reset();
476 +};
477 +
478 +function Template(text, opts) {
479 + opts = opts || {};
480 + var options = {};
481 + this.templateText = text;
482 + this.mode = null;
483 + this.truncate = false;
484 + this.currentLine = 1;
485 + this.source = '';
486 + this.dependencies = [];
487 + options.client = opts.client || false;
488 + options.escapeFunction = opts.escape || opts.escapeFunction || utils.escapeXML;
489 + options.compileDebug = opts.compileDebug !== false;
490 + options.debug = !!opts.debug;
491 + options.filename = opts.filename;
492 + options.openDelimiter = opts.openDelimiter || exports.openDelimiter || _DEFAULT_OPEN_DELIMITER;
493 + options.closeDelimiter = opts.closeDelimiter || exports.closeDelimiter || _DEFAULT_CLOSE_DELIMITER;
494 + options.delimiter = opts.delimiter || exports.delimiter || _DEFAULT_DELIMITER;
495 + options.strict = opts.strict || false;
496 + options.context = opts.context;
497 + options.cache = opts.cache || false;
498 + options.rmWhitespace = opts.rmWhitespace;
499 + options.root = opts.root;
500 + options.outputFunctionName = opts.outputFunctionName;
501 + options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME;
502 + options.views = opts.views;
503 + options.async = opts.async;
504 + options.destructuredLocals = opts.destructuredLocals;
505 + options.legacyInclude = typeof opts.legacyInclude != 'undefined' ? !!opts.legacyInclude : true;
506 +
507 + if (options.strict) {
508 + options._with = false;
509 + }
510 + else {
511 + options._with = typeof opts._with != 'undefined' ? opts._with : true;
512 + }
513 +
514 + this.opts = options;
515 +
516 + this.regex = this.createRegex();
517 +}
518 +
519 +Template.modes = {
520 + EVAL: 'eval',
521 + ESCAPED: 'escaped',
522 + RAW: 'raw',
523 + COMMENT: 'comment',
524 + LITERAL: 'literal'
525 +};
526 +
527 +Template.prototype = {
528 + createRegex: function () {
529 + var str = _REGEX_STRING;
530 + var delim = utils.escapeRegExpChars(this.opts.delimiter);
531 + var open = utils.escapeRegExpChars(this.opts.openDelimiter);
532 + var close = utils.escapeRegExpChars(this.opts.closeDelimiter);
533 + str = str.replace(/%/g, delim)
534 + .replace(/</g, open)
535 + .replace(/>/g, close);
536 + return new RegExp(str);
537 + },
538 +
539 + compile: function () {
540 + var src;
541 + var fn;
542 + var opts = this.opts;
543 + var prepended = '';
544 + var appended = '';
545 + var escapeFn = opts.escapeFunction;
546 + var ctor;
547 +
548 + if (!this.source) {
549 + this.generateSource();
550 + prepended +=
551 + ' var __output = "";\n' +
552 + ' function __append(s) { if (s !== undefined && s !== null) __output += s }\n';
553 + if (opts.outputFunctionName) {
554 + prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n';
555 + }
556 + if (opts.destructuredLocals && opts.destructuredLocals.length) {
557 + var destructuring = ' var __locals = (' + opts.localsName + ' || {}),\n';
558 + for (var i = 0; i < opts.destructuredLocals.length; i++) {
559 + var name = opts.destructuredLocals[i];
560 + if (i > 0) {
561 + destructuring += ',\n ';
562 + }
563 + destructuring += name + ' = __locals.' + name;
564 + }
565 + prepended += destructuring + ';\n';
566 + }
567 + if (opts._with !== false) {
568 + prepended += ' with (' + opts.localsName + ' || {}) {' + '\n';
569 + appended += ' }' + '\n';
570 + }
571 + appended += ' return __output;' + '\n';
572 + this.source = prepended + this.source + appended;
573 + }
574 +
575 + if (opts.compileDebug) {
576 + src = 'var __line = 1' + '\n'
577 + + ' , __lines = ' + JSON.stringify(this.templateText) + '\n'
578 + + ' , __filename = ' + (opts.filename ?
579 + JSON.stringify(opts.filename) : 'undefined') + ';' + '\n'
580 + + 'try {' + '\n'
581 + + this.source
582 + + '} catch (e) {' + '\n'
583 + + ' rethrow(e, __lines, __filename, __line, escapeFn);' + '\n'
584 + + '}' + '\n';
585 + }
586 + else {
587 + src = this.source;
588 + }
589 +
590 + if (opts.client) {
591 + src = 'escapeFn = escapeFn || ' + escapeFn.toString() + ';' + '\n' + src;
592 + if (opts.compileDebug) {
593 + src = 'rethrow = rethrow || ' + rethrow.toString() + ';' + '\n' + src;
594 + }
595 + }
596 +
597 + if (opts.strict) {
598 + src = '"use strict";\n' + src;
599 + }
600 + if (opts.debug) {
601 + console.log(src);
602 + }
603 + if (opts.compileDebug && opts.filename) {
604 + src = src + '\n'
605 + + '//# sourceURL=' + opts.filename + '\n';
606 + }
607 +
608 + try {
609 + if (opts.async) {
610 + // Have to use generated function for this, since in envs without support,
611 + // it breaks in parsing
612 + try {
613 + ctor = (new Function('return (async function(){}).constructor;'))();
614 + }
615 + catch(e) {
616 + if (e instanceof SyntaxError) {
617 + throw new Error('This environment does not support async/await');
618 + }
619 + else {
620 + throw e;
621 + }
622 + }
623 + }
624 + else {
625 + ctor = Function;
626 + }
627 + fn = new ctor(opts.localsName + ', escapeFn, include, rethrow', src);
628 + }
629 + catch(e) {
630 + // istanbul ignore else
631 + if (e instanceof SyntaxError) {
632 + if (opts.filename) {
633 + e.message += ' in ' + opts.filename;
634 + }
635 + e.message += ' while compiling ejs\n\n';
636 + e.message += 'If the above error is not helpful, you may want to try EJS-Lint:\n';
637 + e.message += 'https://github.com/RyanZim/EJS-Lint';
638 + if (!opts.async) {
639 + e.message += '\n';
640 + e.message += 'Or, if you meant to create an async function, pass `async: true` as an option.';
641 + }
642 + }
643 + throw e;
644 + }
645 +
646 + // Return a callable function which will execute the function
647 + // created by the source-code, with the passed data as locals
648 + // Adds a local `include` function which allows full recursive include
649 + var returnedFn = opts.client ? fn : function anonymous(data) {
650 + var include = function (path, includeData) {
651 + var d = utils.shallowCopy({}, data);
652 + if (includeData) {
653 + d = utils.shallowCopy(d, includeData);
654 + }
655 + return includeFile(path, opts)(d);
656 + };
657 + return fn.apply(opts.context, [data || {}, escapeFn, include, rethrow]);
658 + };
659 + returnedFn.dependencies = this.dependencies;
660 + if (opts.filename && typeof Object.defineProperty === 'function') {
661 + var filename = opts.filename;
662 + var basename = path.basename(filename, path.extname(filename));
663 + try {
664 + Object.defineProperty(returnedFn, 'name', {
665 + value: basename,
666 + writable: false,
667 + enumerable: false,
668 + configurable: true
669 + });
670 + } catch (e) {/* ignore */}
671 + }
672 + return returnedFn;
673 + },
674 +
675 + generateSource: function () {
676 + var opts = this.opts;
677 +
678 + if (opts.rmWhitespace) {
679 + // Have to use two separate replace here as `^` and `$` operators don't
680 + // work well with `\r` and empty lines don't work well with the `m` flag.
681 + this.templateText =
682 + this.templateText.replace(/[\r\n]+/g, '\n').replace(/^\s+|\s+$/gm, '');
683 + }
684 +
685 + // Slurp spaces and tabs before <%_ and after _%>
686 + this.templateText =
687 + this.templateText.replace(/[ \t]*<%_/gm, '<%_').replace(/_%>[ \t]*/gm, '_%>');
688 +
689 + var self = this;
690 + var matches = this.parseTemplateText();
691 + var d = this.opts.delimiter;
692 + var o = this.opts.openDelimiter;
693 + var c = this.opts.closeDelimiter;
694 +
695 + if (matches && matches.length) {
696 + matches.forEach(function (line, index) {
697 + var closing;
698 + // If this is an opening tag, check for closing tags
699 + // FIXME: May end up with some false positives here
700 + // Better to store modes as k/v with openDelimiter + delimiter as key
701 + // Then this can simply check against the map
702 + if ( line.indexOf(o + d) === 0 // If it is a tag
703 + && line.indexOf(o + d + d) !== 0) { // and is not escaped
704 + closing = matches[index + 2];
705 + if (!(closing == d + c || closing == '-' + d + c || closing == '_' + d + c)) {
706 + throw new Error('Could not find matching close tag for "' + line + '".');
707 + }
708 + }
709 + self.scanLine(line);
710 + });
711 + }
712 +
713 + },
714 +
715 + parseTemplateText: function () {
716 + var str = this.templateText;
717 + var pat = this.regex;
718 + var result = pat.exec(str);
719 + var arr = [];
720 + var firstPos;
721 +
722 + while (result) {
723 + firstPos = result.index;
724 +
725 + if (firstPos !== 0) {
726 + arr.push(str.substring(0, firstPos));
727 + str = str.slice(firstPos);
728 + }
729 +
730 + arr.push(result[0]);
731 + str = str.slice(result[0].length);
732 + result = pat.exec(str);
733 + }
734 +
735 + if (str) {
736 + arr.push(str);
737 + }
738 +
739 + return arr;
740 + },
741 +
742 + _addOutput: function (line) {
743 + if (this.truncate) {
744 + // Only replace single leading linebreak in the line after
745 + // -%> tag -- this is the single, trailing linebreak
746 + // after the tag that the truncation mode replaces
747 + // Handle Win / Unix / old Mac linebreaks -- do the \r\n
748 + // combo first in the regex-or
749 + line = line.replace(/^(?:\r\n|\r|\n)/, '');
750 + this.truncate = false;
751 + }
752 + if (!line) {
753 + return line;
754 + }
755 +
756 + // Preserve literal slashes
757 + line = line.replace(/\\/g, '\\\\');
758 +
759 + // Convert linebreaks
760 + line = line.replace(/\n/g, '\\n');
761 + line = line.replace(/\r/g, '\\r');
762 +
763 + // Escape double-quotes
764 + // - this will be the delimiter during execution
765 + line = line.replace(/"/g, '\\"');
766 + this.source += ' ; __append("' + line + '")' + '\n';
767 + },
768 +
769 + scanLine: function (line) {
770 + var self = this;
771 + var d = this.opts.delimiter;
772 + var o = this.opts.openDelimiter;
773 + var c = this.opts.closeDelimiter;
774 + var newLineCount = 0;
775 +
776 + newLineCount = (line.split('\n').length - 1);
777 +
778 + switch (line) {
779 + case o + d:
780 + case o + d + '_':
781 + this.mode = Template.modes.EVAL;
782 + break;
783 + case o + d + '=':
784 + this.mode = Template.modes.ESCAPED;
785 + break;
786 + case o + d + '-':
787 + this.mode = Template.modes.RAW;
788 + break;
789 + case o + d + '#':
790 + this.mode = Template.modes.COMMENT;
791 + break;
792 + case o + d + d:
793 + this.mode = Template.modes.LITERAL;
794 + this.source += ' ; __append("' + line.replace(o + d + d, o + d) + '")' + '\n';
795 + break;
796 + case d + d + c:
797 + this.mode = Template.modes.LITERAL;
798 + this.source += ' ; __append("' + line.replace(d + d + c, d + c) + '")' + '\n';
799 + break;
800 + case d + c:
801 + case '-' + d + c:
802 + case '_' + d + c:
803 + if (this.mode == Template.modes.LITERAL) {
804 + this._addOutput(line);
805 + }
806 +
807 + this.mode = null;
808 + this.truncate = line.indexOf('-') === 0 || line.indexOf('_') === 0;
809 + break;
810 + default:
811 + // In script mode, depends on type of tag
812 + if (this.mode) {
813 + // If '//' is found without a line break, add a line break.
814 + switch (this.mode) {
815 + case Template.modes.EVAL:
816 + case Template.modes.ESCAPED:
817 + case Template.modes.RAW:
818 + if (line.lastIndexOf('//') > line.lastIndexOf('\n')) {
819 + line += '\n';
820 + }
821 + }
822 + switch (this.mode) {
823 + // Just executing code
824 + case Template.modes.EVAL:
825 + this.source += ' ; ' + line + '\n';
826 + break;
827 + // Exec, esc, and output
828 + case Template.modes.ESCAPED:
829 + this.source += ' ; __append(escapeFn(' + stripSemi(line) + '))' + '\n';
830 + break;
831 + // Exec and output
832 + case Template.modes.RAW:
833 + this.source += ' ; __append(' + stripSemi(line) + ')' + '\n';
834 + break;
835 + case Template.modes.COMMENT:
836 + // Do nothing
837 + break;
838 + // Literal <%% mode, append as raw output
839 + case Template.modes.LITERAL:
840 + this._addOutput(line);
841 + break;
842 + }
843 + }
844 + // In string mode, just add the output
845 + else {
846 + this._addOutput(line);
847 + }
848 + }
849 +
850 + if (self.opts.compileDebug && newLineCount) {
851 + this.currentLine += newLineCount;
852 + this.source += ' ; __line = ' + this.currentLine + '\n';
853 + }
854 + }
855 +};
856 +
857 +/**
858 + * Escape characters reserved in XML.
859 + *
860 + * This is simply an export of {@link module:utils.escapeXML}.
861 + *
862 + * If `markup` is `undefined` or `null`, the empty string is returned.
863 + *
864 + * @param {String} markup Input string
865 + * @return {String} Escaped string
866 + * @public
867 + * @func
868 + * */
869 +exports.escapeXML = utils.escapeXML;
870 +
871 +/**
872 + * Express.js support.
873 + *
874 + * This is an alias for {@link module:ejs.renderFile}, in order to support
875 + * Express.js out-of-the-box.
876 + *
877 + * @func
878 + */
879 +
880 +exports.__express = exports.renderFile;
881 +
882 +/**
883 + * Version of EJS.
884 + *
885 + * @readonly
886 + * @type {String}
887 + * @public
888 + */
889 +
890 +exports.VERSION = _VERSION_STRING;
891 +
892 +/**
893 + * Name for detection of EJS.
894 + *
895 + * @readonly
896 + * @type {String}
897 + * @public
898 + */
899 +
900 +exports.name = _NAME;
901 +
902 +/* istanbul ignore if */
903 +if (typeof window != 'undefined') {
904 + window.ejs = exports;
905 +}
906 +
907 +},{"../package.json":6,"./utils":2,"fs":3,"path":4}],2:[function(require,module,exports){
908 +/*
909 + * EJS Embedded JavaScript templates
910 + * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
911 + *
912 + * Licensed under the Apache License, Version 2.0 (the "License");
913 + * you may not use this file except in compliance with the License.
914 + * You may obtain a copy of the License at
915 + *
916 + * http://www.apache.org/licenses/LICENSE-2.0
917 + *
918 + * Unless required by applicable law or agreed to in writing, software
919 + * distributed under the License is distributed on an "AS IS" BASIS,
920 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
921 + * See the License for the specific language governing permissions and
922 + * limitations under the License.
923 + *
924 +*/
925 +
926 +/**
927 + * Private utility functions
928 + * @module utils
929 + * @private
930 + */
931 +
932 +'use strict';
933 +
934 +var regExpChars = /[|\\{}()[\]^$+*?.]/g;
935 +
936 +/**
937 + * Escape characters reserved in regular expressions.
938 + *
939 + * If `string` is `undefined` or `null`, the empty string is returned.
940 + *
941 + * @param {String} string Input string
942 + * @return {String} Escaped string
943 + * @static
944 + * @private
945 + */
946 +exports.escapeRegExpChars = function (string) {
947 + // istanbul ignore if
948 + if (!string) {
949 + return '';
950 + }
951 + return String(string).replace(regExpChars, '\\$&');
952 +};
953 +
954 +var _ENCODE_HTML_RULES = {
955 + '&': '&amp;',
956 + '<': '&lt;',
957 + '>': '&gt;',
958 + '"': '&#34;',
959 + "'": '&#39;'
960 +};
961 +var _MATCH_HTML = /[&<>'"]/g;
962 +
963 +function encode_char(c) {
964 + return _ENCODE_HTML_RULES[c] || c;
965 +}
966 +
967 +/**
968 + * Stringified version of constants used by {@link module:utils.escapeXML}.
969 + *
970 + * It is used in the process of generating {@link ClientFunction}s.
971 + *
972 + * @readonly
973 + * @type {String}
974 + */
975 +
976 +var escapeFuncStr =
977 + 'var _ENCODE_HTML_RULES = {\n'
978 ++ ' "&": "&amp;"\n'
979 ++ ' , "<": "&lt;"\n'
980 ++ ' , ">": "&gt;"\n'
981 ++ ' , \'"\': "&#34;"\n'
982 ++ ' , "\'": "&#39;"\n'
983 ++ ' }\n'
984 ++ ' , _MATCH_HTML = /[&<>\'"]/g;\n'
985 ++ 'function encode_char(c) {\n'
986 ++ ' return _ENCODE_HTML_RULES[c] || c;\n'
987 ++ '};\n';
988 +
989 +/**
990 + * Escape characters reserved in XML.
991 + *
992 + * If `markup` is `undefined` or `null`, the empty string is returned.
993 + *
994 + * @implements {EscapeCallback}
995 + * @param {String} markup Input string
996 + * @return {String} Escaped string
997 + * @static
998 + * @private
999 + */
1000 +
1001 +exports.escapeXML = function (markup) {
1002 + return markup == undefined
1003 + ? ''
1004 + : String(markup)
1005 + .replace(_MATCH_HTML, encode_char);
1006 +};
1007 +exports.escapeXML.toString = function () {
1008 + return Function.prototype.toString.call(this) + ';\n' + escapeFuncStr;
1009 +};
1010 +
1011 +/**
1012 + * Naive copy of properties from one object to another.
1013 + * Does not recurse into non-scalar properties
1014 + * Does not check to see if the property has a value before copying
1015 + *
1016 + * @param {Object} to Destination object
1017 + * @param {Object} from Source object
1018 + * @return {Object} Destination object
1019 + * @static
1020 + * @private
1021 + */
1022 +exports.shallowCopy = function (to, from) {
1023 + from = from || {};
1024 + for (var p in from) {
1025 + to[p] = from[p];
1026 + }
1027 + return to;
1028 +};
1029 +
1030 +/**
1031 + * Naive copy of a list of key names, from one object to another.
1032 + * Only copies property if it is actually defined
1033 + * Does not recurse into non-scalar properties
1034 + *
1035 + * @param {Object} to Destination object
1036 + * @param {Object} from Source object
1037 + * @param {Array} list List of properties to copy
1038 + * @return {Object} Destination object
1039 + * @static
1040 + * @private
1041 + */
1042 +exports.shallowCopyFromList = function (to, from, list) {
1043 + for (var i = 0; i < list.length; i++) {
1044 + var p = list[i];
1045 + if (typeof from[p] != 'undefined') {
1046 + to[p] = from[p];
1047 + }
1048 + }
1049 + return to;
1050 +};
1051 +
1052 +/**
1053 + * Simple in-process cache implementation. Does not implement limits of any
1054 + * sort.
1055 + *
1056 + * @implements Cache
1057 + * @static
1058 + * @private
1059 + */
1060 +exports.cache = {
1061 + _data: {},
1062 + set: function (key, val) {
1063 + this._data[key] = val;
1064 + },
1065 + get: function (key) {
1066 + return this._data[key];
1067 + },
1068 + remove: function (key) {
1069 + delete this._data[key];
1070 + },
1071 + reset: function () {
1072 + this._data = {};
1073 + }
1074 +};
1075 +
1076 +},{}],3:[function(require,module,exports){
1077 +
1078 +},{}],4:[function(require,module,exports){
1079 +(function (process){
1080 +// Copyright Joyent, Inc. and other Node contributors.
1081 +//
1082 +// Permission is hereby granted, free of charge, to any person obtaining a
1083 +// copy of this software and associated documentation files (the
1084 +// "Software"), to deal in the Software without restriction, including
1085 +// without limitation the rights to use, copy, modify, merge, publish,
1086 +// distribute, sublicense, and/or sell copies of the Software, and to permit
1087 +// persons to whom the Software is furnished to do so, subject to the
1088 +// following conditions:
1089 +//
1090 +// The above copyright notice and this permission notice shall be included
1091 +// in all copies or substantial portions of the Software.
1092 +//
1093 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1094 +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1095 +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
1096 +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
1097 +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
1098 +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
1099 +// USE OR OTHER DEALINGS IN THE SOFTWARE.
1100 +
1101 +// resolves . and .. elements in a path array with directory names there
1102 +// must be no slashes, empty elements, or device names (c:\) in the array
1103 +// (so also no leading and trailing slashes - it does not distinguish
1104 +// relative and absolute paths)
1105 +function normalizeArray(parts, allowAboveRoot) {
1106 + // if the path tries to go above the root, `up` ends up > 0
1107 + var up = 0;
1108 + for (var i = parts.length - 1; i >= 0; i--) {
1109 + var last = parts[i];
1110 + if (last === '.') {
1111 + parts.splice(i, 1);
1112 + } else if (last === '..') {
1113 + parts.splice(i, 1);
1114 + up++;
1115 + } else if (up) {
1116 + parts.splice(i, 1);
1117 + up--;
1118 + }
1119 + }
1120 +
1121 + // if the path is allowed to go above the root, restore leading ..s
1122 + if (allowAboveRoot) {
1123 + for (; up--; up) {
1124 + parts.unshift('..');
1125 + }
1126 + }
1127 +
1128 + return parts;
1129 +}
1130 +
1131 +// Split a filename into [root, dir, basename, ext], unix version
1132 +// 'root' is just a slash, or nothing.
1133 +var splitPathRe =
1134 + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
1135 +var splitPath = function(filename) {
1136 + return splitPathRe.exec(filename).slice(1);
1137 +};
1138 +
1139 +// path.resolve([from ...], to)
1140 +// posix version
1141 +exports.resolve = function() {
1142 + var resolvedPath = '',
1143 + resolvedAbsolute = false;
1144 +
1145 + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
1146 + var path = (i >= 0) ? arguments[i] : process.cwd();
1147 +
1148 + // Skip empty and invalid entries
1149 + if (typeof path !== 'string') {
1150 + throw new TypeError('Arguments to path.resolve must be strings');
1151 + } else if (!path) {
1152 + continue;
1153 + }
1154 +
1155 + resolvedPath = path + '/' + resolvedPath;
1156 + resolvedAbsolute = path.charAt(0) === '/';
1157 + }
1158 +
1159 + // At this point the path should be resolved to a full absolute path, but
1160 + // handle relative paths to be safe (might happen when process.cwd() fails)
1161 +
1162 + // Normalize the path
1163 + resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
1164 + return !!p;
1165 + }), !resolvedAbsolute).join('/');
1166 +
1167 + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
1168 +};
1169 +
1170 +// path.normalize(path)
1171 +// posix version
1172 +exports.normalize = function(path) {
1173 + var isAbsolute = exports.isAbsolute(path),
1174 + trailingSlash = substr(path, -1) === '/';
1175 +
1176 + // Normalize the path
1177 + path = normalizeArray(filter(path.split('/'), function(p) {
1178 + return !!p;
1179 + }), !isAbsolute).join('/');
1180 +
1181 + if (!path && !isAbsolute) {
1182 + path = '.';
1183 + }
1184 + if (path && trailingSlash) {
1185 + path += '/';
1186 + }
1187 +
1188 + return (isAbsolute ? '/' : '') + path;
1189 +};
1190 +
1191 +// posix version
1192 +exports.isAbsolute = function(path) {
1193 + return path.charAt(0) === '/';
1194 +};
1195 +
1196 +// posix version
1197 +exports.join = function() {
1198 + var paths = Array.prototype.slice.call(arguments, 0);
1199 + return exports.normalize(filter(paths, function(p, index) {
1200 + if (typeof p !== 'string') {
1201 + throw new TypeError('Arguments to path.join must be strings');
1202 + }
1203 + return p;
1204 + }).join('/'));
1205 +};
1206 +
1207 +
1208 +// path.relative(from, to)
1209 +// posix version
1210 +exports.relative = function(from, to) {
1211 + from = exports.resolve(from).substr(1);
1212 + to = exports.resolve(to).substr(1);
1213 +
1214 + function trim(arr) {
1215 + var start = 0;
1216 + for (; start < arr.length; start++) {
1217 + if (arr[start] !== '') break;
1218 + }
1219 +
1220 + var end = arr.length - 1;
1221 + for (; end >= 0; end--) {
1222 + if (arr[end] !== '') break;
1223 + }
1224 +
1225 + if (start > end) return [];
1226 + return arr.slice(start, end - start + 1);
1227 + }
1228 +
1229 + var fromParts = trim(from.split('/'));
1230 + var toParts = trim(to.split('/'));
1231 +
1232 + var length = Math.min(fromParts.length, toParts.length);
1233 + var samePartsLength = length;
1234 + for (var i = 0; i < length; i++) {
1235 + if (fromParts[i] !== toParts[i]) {
1236 + samePartsLength = i;
1237 + break;
1238 + }
1239 + }
1240 +
1241 + var outputParts = [];
1242 + for (var i = samePartsLength; i < fromParts.length; i++) {
1243 + outputParts.push('..');
1244 + }
1245 +
1246 + outputParts = outputParts.concat(toParts.slice(samePartsLength));
1247 +
1248 + return outputParts.join('/');
1249 +};
1250 +
1251 +exports.sep = '/';
1252 +exports.delimiter = ':';
1253 +
1254 +exports.dirname = function(path) {
1255 + var result = splitPath(path),
1256 + root = result[0],
1257 + dir = result[1];
1258 +
1259 + if (!root && !dir) {
1260 + // No dirname whatsoever
1261 + return '.';
1262 + }
1263 +
1264 + if (dir) {
1265 + // It has a dirname, strip trailing slash
1266 + dir = dir.substr(0, dir.length - 1);
1267 + }
1268 +
1269 + return root + dir;
1270 +};
1271 +
1272 +
1273 +exports.basename = function(path, ext) {
1274 + var f = splitPath(path)[2];
1275 + // TODO: make this comparison case-insensitive on windows?
1276 + if (ext && f.substr(-1 * ext.length) === ext) {
1277 + f = f.substr(0, f.length - ext.length);
1278 + }
1279 + return f;
1280 +};
1281 +
1282 +
1283 +exports.extname = function(path) {
1284 + return splitPath(path)[3];
1285 +};
1286 +
1287 +function filter (xs, f) {
1288 + if (xs.filter) return xs.filter(f);
1289 + var res = [];
1290 + for (var i = 0; i < xs.length; i++) {
1291 + if (f(xs[i], i, xs)) res.push(xs[i]);
1292 + }
1293 + return res;
1294 +}
1295 +
1296 +// String.prototype.substr - negative index don't work in IE8
1297 +var substr = 'ab'.substr(-1) === 'b'
1298 + ? function (str, start, len) { return str.substr(start, len) }
1299 + : function (str, start, len) {
1300 + if (start < 0) start = str.length + start;
1301 + return str.substr(start, len);
1302 + }
1303 +;
1304 +
1305 +}).call(this,require('_process'))
1306 +},{"_process":5}],5:[function(require,module,exports){
1307 +// shim for using process in browser
1308 +var process = module.exports = {};
1309 +
1310 +// cached from whatever global is present so that test runners that stub it
1311 +// don't break things. But we need to wrap it in a try catch in case it is
1312 +// wrapped in strict mode code which doesn't define any globals. It's inside a
1313 +// function because try/catches deoptimize in certain engines.
1314 +
1315 +var cachedSetTimeout;
1316 +var cachedClearTimeout;
1317 +
1318 +function defaultSetTimout() {
1319 + throw new Error('setTimeout has not been defined');
1320 +}
1321 +function defaultClearTimeout () {
1322 + throw new Error('clearTimeout has not been defined');
1323 +}
1324 +(function () {
1325 + try {
1326 + if (typeof setTimeout === 'function') {
1327 + cachedSetTimeout = setTimeout;
1328 + } else {
1329 + cachedSetTimeout = defaultSetTimout;
1330 + }
1331 + } catch (e) {
1332 + cachedSetTimeout = defaultSetTimout;
1333 + }
1334 + try {
1335 + if (typeof clearTimeout === 'function') {
1336 + cachedClearTimeout = clearTimeout;
1337 + } else {
1338 + cachedClearTimeout = defaultClearTimeout;
1339 + }
1340 + } catch (e) {
1341 + cachedClearTimeout = defaultClearTimeout;
1342 + }
1343 +} ())
1344 +function runTimeout(fun) {
1345 + if (cachedSetTimeout === setTimeout) {
1346 + //normal enviroments in sane situations
1347 + return setTimeout(fun, 0);
1348 + }
1349 + // if setTimeout wasn't available but was latter defined
1350 + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
1351 + cachedSetTimeout = setTimeout;
1352 + return setTimeout(fun, 0);
1353 + }
1354 + try {
1355 + // when when somebody has screwed with setTimeout but no I.E. maddness
1356 + return cachedSetTimeout(fun, 0);
1357 + } catch(e){
1358 + try {
1359 + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
1360 + return cachedSetTimeout.call(null, fun, 0);
1361 + } catch(e){
1362 + // 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
1363 + return cachedSetTimeout.call(this, fun, 0);
1364 + }
1365 + }
1366 +
1367 +
1368 +}
1369 +function runClearTimeout(marker) {
1370 + if (cachedClearTimeout === clearTimeout) {
1371 + //normal enviroments in sane situations
1372 + return clearTimeout(marker);
1373 + }
1374 + // if clearTimeout wasn't available but was latter defined
1375 + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
1376 + cachedClearTimeout = clearTimeout;
1377 + return clearTimeout(marker);
1378 + }
1379 + try {
1380 + // when when somebody has screwed with setTimeout but no I.E. maddness
1381 + return cachedClearTimeout(marker);
1382 + } catch (e){
1383 + try {
1384 + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
1385 + return cachedClearTimeout.call(null, marker);
1386 + } catch (e){
1387 + // 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.
1388 + // Some versions of I.E. have different rules for clearTimeout vs setTimeout
1389 + return cachedClearTimeout.call(this, marker);
1390 + }
1391 + }
1392 +
1393 +
1394 +
1395 +}
1396 +var queue = [];
1397 +var draining = false;
1398 +var currentQueue;
1399 +var queueIndex = -1;
1400 +
1401 +function cleanUpNextTick() {
1402 + if (!draining || !currentQueue) {
1403 + return;
1404 + }
1405 + draining = false;
1406 + if (currentQueue.length) {
1407 + queue = currentQueue.concat(queue);
1408 + } else {
1409 + queueIndex = -1;
1410 + }
1411 + if (queue.length) {
1412 + drainQueue();
1413 + }
1414 +}
1415 +
1416 +function drainQueue() {
1417 + if (draining) {
1418 + return;
1419 + }
1420 + var timeout = runTimeout(cleanUpNextTick);
1421 + draining = true;
1422 +
1423 + var len = queue.length;
1424 + while(len) {
1425 + currentQueue = queue;
1426 + queue = [];
1427 + while (++queueIndex < len) {
1428 + if (currentQueue) {
1429 + currentQueue[queueIndex].run();
1430 + }
1431 + }
1432 + queueIndex = -1;
1433 + len = queue.length;
1434 + }
1435 + currentQueue = null;
1436 + draining = false;
1437 + runClearTimeout(timeout);
1438 +}
1439 +
1440 +process.nextTick = function (fun) {
1441 + var args = new Array(arguments.length - 1);
1442 + if (arguments.length > 1) {
1443 + for (var i = 1; i < arguments.length; i++) {
1444 + args[i - 1] = arguments[i];
1445 + }
1446 + }
1447 + queue.push(new Item(fun, args));
1448 + if (queue.length === 1 && !draining) {
1449 + runTimeout(drainQueue);
1450 + }
1451 +};
1452 +
1453 +// v8 likes predictible objects
1454 +function Item(fun, array) {
1455 + this.fun = fun;
1456 + this.array = array;
1457 +}
1458 +Item.prototype.run = function () {
1459 + this.fun.apply(null, this.array);
1460 +};
1461 +process.title = 'browser';
1462 +process.browser = true;
1463 +process.env = {};
1464 +process.argv = [];
1465 +process.version = ''; // empty string to avoid regexp issues
1466 +process.versions = {};
1467 +
1468 +function noop() {}
1469 +
1470 +process.on = noop;
1471 +process.addListener = noop;
1472 +process.once = noop;
1473 +process.off = noop;
1474 +process.removeListener = noop;
1475 +process.removeAllListeners = noop;
1476 +process.emit = noop;
1477 +process.prependListener = noop;
1478 +process.prependOnceListener = noop;
1479 +
1480 +process.listeners = function (name) { return [] }
1481 +
1482 +process.binding = function (name) {
1483 + throw new Error('process.binding is not supported');
1484 +};
1485 +
1486 +process.cwd = function () { return '/' };
1487 +process.chdir = function (dir) {
1488 + throw new Error('process.chdir is not supported');
1489 +};
1490 +process.umask = function() { return 0; };
1491 +
1492 +},{}],6:[function(require,module,exports){
1493 +module.exports={
1494 + "name": "ejs",
1495 + "description": "Embedded JavaScript templates",
1496 + "keywords": [
1497 + "template",
1498 + "engine",
1499 + "ejs"
1500 + ],
1501 + "version": "3.0.1",
1502 + "author": "Matthew Eernisse <mde@fleegix.org> (http://fleegix.org)",
1503 + "license": "Apache-2.0",
1504 + "main": "./lib/ejs.js",
1505 + "repository": {
1506 + "type": "git",
1507 + "url": "git://github.com/mde/ejs.git"
1508 + },
1509 + "bugs": "https://github.com/mde/ejs/issues",
1510 + "homepage": "https://github.com/mde/ejs",
1511 + "dependencies": {},
1512 + "devDependencies": {
1513 + "browserify": "^13.1.1",
1514 + "eslint": "^4.14.0",
1515 + "git-directory-deploy": "^1.5.1",
1516 + "jake": "^10.3.1",
1517 + "jsdoc": "^3.4.0",
1518 + "lru-cache": "^4.0.1",
1519 + "mocha": "^5.0.5",
1520 + "uglify-js": "^3.3.16"
1521 + },
1522 + "engines": {
1523 + "node": ">=0.10.0"
1524 + },
1525 + "scripts": {
1526 + "test": "mocha",
1527 + "postinstall": "node ./postinstall.js"
1528 + }
1529 +}
1530 +
1531 +},{}]},{},[1])(1)
1532 +});
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(){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}return e}()({1:[function(require,module,exports){"use strict";var fs=require("fs");var path=require("path");var utils=require("./utils");var scopeOptionWarned=false;var _VERSION_STRING=require("../package.json").version;var _DEFAULT_OPEN_DELIMITER="<";var _DEFAULT_CLOSE_DELIMITER=">";var _DEFAULT_DELIMITER="%";var _DEFAULT_LOCALS_NAME="locals";var _NAME="ejs";var _REGEX_STRING="(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)";var _OPTS_PASSABLE_WITH_DATA=["delimiter","scope","context","debug","compileDebug","client","_with","rmWhitespace","strict","filename","async"];var _OPTS_PASSABLE_WITH_DATA_EXPRESS=_OPTS_PASSABLE_WITH_DATA.concat("cache");var _BOM=/^\uFEFF/;exports.cache=utils.cache;exports.fileLoader=fs.readFileSync;exports.localsName=_DEFAULT_LOCALS_NAME;exports.promiseImpl=new Function("return this;")().Promise;exports.resolveInclude=function(name,filename,isDir){var dirname=path.dirname;var extname=path.extname;var resolve=path.resolve;var includePath=resolve(isDir?filename:dirname(filename),name);var ext=extname(name);if(!ext){includePath+=".ejs"}return includePath};function getIncludePath(path,options){var includePath;var filePath;var views=options.views;var match=/^[A-Za-z]+:\\|^\//.exec(path);if(match&&match.length){includePath=exports.resolveInclude(path.replace(/^\/*/,""),options.root||"/",true)}else{if(options.filename){filePath=exports.resolveInclude(path,options.filename);if(fs.existsSync(filePath)){includePath=filePath}}if(!includePath){if(Array.isArray(views)&&views.some(function(v){filePath=exports.resolveInclude(path,v,true);return fs.existsSync(filePath)})){includePath=filePath}}if(!includePath){throw new Error('Could not find the include file "'+options.escapeFunction(path)+'"')}}return includePath}function handleCache(options,template){var func;var filename=options.filename;var hasTemplate=arguments.length>1;if(options.cache){if(!filename){throw new Error("cache option requires a filename")}func=exports.cache.get(filename);if(func){return func}if(!hasTemplate){template=fileLoader(filename).toString().replace(_BOM,"")}}else if(!hasTemplate){if(!filename){throw new Error("Internal EJS error: no file name or template "+"provided")}template=fileLoader(filename).toString().replace(_BOM,"")}func=exports.compile(template,options);if(options.cache){exports.cache.set(filename,func)}return func}function tryHandleCache(options,data,cb){var result;if(!cb){if(typeof exports.promiseImpl=="function"){return new exports.promiseImpl(function(resolve,reject){try{result=handleCache(options)(data);resolve(result)}catch(err){reject(err)}})}else{throw new Error("Please provide a callback function")}}else{try{result=handleCache(options)(data)}catch(err){return cb(err)}cb(null,result)}}function fileLoader(filePath){return exports.fileLoader(filePath)}function includeFile(path,options){var opts=utils.shallowCopy({},options);opts.filename=getIncludePath(path,opts);return handleCache(opts)}function rethrow(err,str,flnm,lineno,esc){var lines=str.split("\n");var start=Math.max(lineno-3,0);var end=Math.min(lines.length,lineno+3);var filename=esc(flnm);var context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" >> ":" ")+curr+"| "+line}).join("\n");err.path=filename;err.message=(filename||"ejs")+":"+lineno+"\n"+context+"\n\n"+err.message;throw err}function stripSemi(str){return str.replace(/;(\s*$)/,"$1")}exports.compile=function compile(template,opts){var templ;if(opts&&opts.scope){if(!scopeOptionWarned){console.warn("`scope` option is deprecated and will be removed in EJS 3");scopeOptionWarned=true}if(!opts.context){opts.context=opts.scope}delete opts.scope}templ=new Template(template,opts);return templ.compile()};exports.render=function(template,d,o){var data=d||{};var opts=o||{};if(arguments.length==2){utils.shallowCopyFromList(opts,data,_OPTS_PASSABLE_WITH_DATA)}return handleCache(opts,template)(data)};exports.renderFile=function(){var args=Array.prototype.slice.call(arguments);var filename=args.shift();var cb;var opts={filename:filename};var data;var viewOpts;if(typeof arguments[arguments.length-1]=="function"){cb=args.pop()}if(args.length){data=args.shift();if(args.length){utils.shallowCopy(opts,args.pop())}else{if(data.settings){if(data.settings.views){opts.views=data.settings.views}if(data.settings["view cache"]){opts.cache=true}viewOpts=data.settings["view options"];if(viewOpts){utils.shallowCopy(opts,viewOpts)}}utils.shallowCopyFromList(opts,data,_OPTS_PASSABLE_WITH_DATA_EXPRESS)}opts.filename=filename}else{data={}}return tryHandleCache(opts,data,cb)};exports.Template=Template;exports.clearCache=function(){exports.cache.reset()};function Template(text,opts){opts=opts||{};var options={};this.templateText=text;this.mode=null;this.truncate=false;this.currentLine=1;this.source="";this.dependencies=[];options.client=opts.client||false;options.escapeFunction=opts.escape||opts.escapeFunction||utils.escapeXML;options.compileDebug=opts.compileDebug!==false;options.debug=!!opts.debug;options.filename=opts.filename;options.openDelimiter=opts.openDelimiter||exports.openDelimiter||_DEFAULT_OPEN_DELIMITER;options.closeDelimiter=opts.closeDelimiter||exports.closeDelimiter||_DEFAULT_CLOSE_DELIMITER;options.delimiter=opts.delimiter||exports.delimiter||_DEFAULT_DELIMITER;options.strict=opts.strict||false;options.context=opts.context;options.cache=opts.cache||false;options.rmWhitespace=opts.rmWhitespace;options.root=opts.root;options.outputFunctionName=opts.outputFunctionName;options.localsName=opts.localsName||exports.localsName||_DEFAULT_LOCALS_NAME;options.views=opts.views;options.async=opts.async;options.destructuredLocals=opts.destructuredLocals;options.legacyInclude=typeof opts.legacyInclude!="undefined"?!!opts.legacyInclude:true;if(options.strict){options._with=false}else{options._with=typeof opts._with!="undefined"?opts._with:true}this.opts=options;this.regex=this.createRegex()}Template.modes={EVAL:"eval",ESCAPED:"escaped",RAW:"raw",COMMENT:"comment",LITERAL:"literal"};Template.prototype={createRegex:function(){var str=_REGEX_STRING;var delim=utils.escapeRegExpChars(this.opts.delimiter);var open=utils.escapeRegExpChars(this.opts.openDelimiter);var close=utils.escapeRegExpChars(this.opts.closeDelimiter);str=str.replace(/%/g,delim).replace(/</g,open).replace(/>/g,close);return new RegExp(str)},compile:function(){var src;var fn;var opts=this.opts;var prepended="";var appended="";var escapeFn=opts.escapeFunction;var ctor;if(!this.source){this.generateSource();prepended+=' var __output = "";\n'+" function __append(s) { if (s !== undefined && s !== null) __output += s }\n";if(opts.outputFunctionName){prepended+=" var "+opts.outputFunctionName+" = __append;"+"\n"}if(opts.destructuredLocals&&opts.destructuredLocals.length){var destructuring=" var __locals = ("+opts.localsName+" || {}),\n";for(var i=0;i<opts.destructuredLocals.length;i++){var name=opts.destructuredLocals[i];if(i>0){destructuring+=",\n "}destructuring+=name+" = __locals."+name}prepended+=destructuring+";\n"}if(opts._with!==false){prepended+=" with ("+opts.localsName+" || {}) {"+"\n";appended+=" }"+"\n"}appended+=" return __output;"+"\n";this.source=prepended+this.source+appended}if(opts.compileDebug){src="var __line = 1"+"\n"+" , __lines = "+JSON.stringify(this.templateText)+"\n"+" , __filename = "+(opts.filename?JSON.stringify(opts.filename):"undefined")+";"+"\n"+"try {"+"\n"+this.source+"} catch (e) {"+"\n"+" rethrow(e, __lines, __filename, __line, escapeFn);"+"\n"+"}"+"\n"}else{src=this.source}if(opts.client){src="escapeFn = escapeFn || "+escapeFn.toString()+";"+"\n"+src;if(opts.compileDebug){src="rethrow = rethrow || "+rethrow.toString()+";"+"\n"+src}}if(opts.strict){src='"use strict";\n'+src}if(opts.debug){console.log(src)}if(opts.compileDebug&&opts.filename){src=src+"\n"+"//# sourceURL="+opts.filename+"\n"}try{if(opts.async){try{ctor=new Function("return (async function(){}).constructor;")()}catch(e){if(e instanceof SyntaxError){throw new Error("This environment does not support async/await")}else{throw e}}}else{ctor=Function}fn=new ctor(opts.localsName+", escapeFn, include, rethrow",src)}catch(e){if(e instanceof SyntaxError){if(opts.filename){e.message+=" in "+opts.filename}e.message+=" while compiling ejs\n\n";e.message+="If the above error is not helpful, you may want to try EJS-Lint:\n";e.message+="https://github.com/RyanZim/EJS-Lint";if(!opts.async){e.message+="\n";e.message+="Or, if you meant to create an async function, pass `async: true` as an option."}}throw e}var returnedFn=opts.client?fn:function anonymous(data){var include=function(path,includeData){var d=utils.shallowCopy({},data);if(includeData){d=utils.shallowCopy(d,includeData)}return includeFile(path,opts)(d)};return fn.apply(opts.context,[data||{},escapeFn,include,rethrow])};returnedFn.dependencies=this.dependencies;if(opts.filename&&typeof Object.defineProperty==="function"){var filename=opts.filename;var basename=path.basename(filename,path.extname(filename));try{Object.defineProperty(returnedFn,"name",{value:basename,writable:false,enumerable:false,configurable:true})}catch(e){}}return returnedFn},generateSource:function(){var opts=this.opts;if(opts.rmWhitespace){this.templateText=this.templateText.replace(/[\r\n]+/g,"\n").replace(/^\s+|\s+$/gm,"")}this.templateText=this.templateText.replace(/[ \t]*<%_/gm,"<%_").replace(/_%>[ \t]*/gm,"_%>");var self=this;var matches=this.parseTemplateText();var d=this.opts.delimiter;var o=this.opts.openDelimiter;var c=this.opts.closeDelimiter;if(matches&&matches.length){matches.forEach(function(line,index){var closing;if(line.indexOf(o+d)===0&&line.indexOf(o+d+d)!==0){closing=matches[index+2];if(!(closing==d+c||closing=="-"+d+c||closing=="_"+d+c)){throw new Error('Could not find matching close tag for "'+line+'".')}}self.scanLine(line)})}},parseTemplateText:function(){var str=this.templateText;var pat=this.regex;var result=pat.exec(str);var arr=[];var firstPos;while(result){firstPos=result.index;if(firstPos!==0){arr.push(str.substring(0,firstPos));str=str.slice(firstPos)}arr.push(result[0]);str=str.slice(result[0].length);result=pat.exec(str)}if(str){arr.push(str)}return arr},_addOutput:function(line){if(this.truncate){line=line.replace(/^(?:\r\n|\r|\n)/,"");this.truncate=false}if(!line){return line}line=line.replace(/\\/g,"\\\\");line=line.replace(/\n/g,"\\n");line=line.replace(/\r/g,"\\r");line=line.replace(/"/g,'\\"');this.source+=' ; __append("'+line+'")'+"\n"},scanLine:function(line){var self=this;var d=this.opts.delimiter;var o=this.opts.openDelimiter;var c=this.opts.closeDelimiter;var newLineCount=0;newLineCount=line.split("\n").length-1;switch(line){case o+d:case o+d+"_":this.mode=Template.modes.EVAL;break;case o+d+"=":this.mode=Template.modes.ESCAPED;break;case o+d+"-":this.mode=Template.modes.RAW;break;case o+d+"#":this.mode=Template.modes.COMMENT;break;case o+d+d:this.mode=Template.modes.LITERAL;this.source+=' ; __append("'+line.replace(o+d+d,o+d)+'")'+"\n";break;case d+d+c:this.mode=Template.modes.LITERAL;this.source+=' ; __append("'+line.replace(d+d+c,d+c)+'")'+"\n";break;case d+c:case"-"+d+c:case"_"+d+c:if(this.mode==Template.modes.LITERAL){this._addOutput(line)}this.mode=null;this.truncate=line.indexOf("-")===0||line.indexOf("_")===0;break;default:if(this.mode){switch(this.mode){case Template.modes.EVAL:case Template.modes.ESCAPED:case Template.modes.RAW:if(line.lastIndexOf("//")>line.lastIndexOf("\n")){line+="\n"}}switch(this.mode){case Template.modes.EVAL:this.source+=" ; "+line+"\n";break;case Template.modes.ESCAPED:this.source+=" ; __append(escapeFn("+stripSemi(line)+"))"+"\n";break;case Template.modes.RAW:this.source+=" ; __append("+stripSemi(line)+")"+"\n";break;case Template.modes.COMMENT:break;case Template.modes.LITERAL:this._addOutput(line);break}}else{this._addOutput(line)}}if(self.opts.compileDebug&&newLineCount){this.currentLine+=newLineCount;this.source+=" ; __line = "+this.currentLine+"\n"}}};exports.escapeXML=utils.escapeXML;exports.__express=exports.renderFile;exports.VERSION=_VERSION_STRING;exports.name=_NAME;if(typeof window!="undefined"){window.ejs=exports}},{"../package.json":6,"./utils":2,fs:3,path:4}],2:[function(require,module,exports){"use strict";var regExpChars=/[|\\{}()[\]^$+*?.]/g;exports.escapeRegExpChars=function(string){if(!string){return""}return String(string).replace(regExpChars,"\\$&")};var _ENCODE_HTML_RULES={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&#34;","'":"&#39;"};var _MATCH_HTML=/[&<>'"]/g;function encode_char(c){return _ENCODE_HTML_RULES[c]||c}var escapeFuncStr="var _ENCODE_HTML_RULES = {\n"+' "&": "&amp;"\n'+' , "<": "&lt;"\n'+' , ">": "&gt;"\n'+' , \'"\': "&#34;"\n'+' , "\'": "&#39;"\n'+" }\n"+" , _MATCH_HTML = /[&<>'\"]/g;\n"+"function encode_char(c) {\n"+" return _ENCODE_HTML_RULES[c] || c;\n"+"};\n";exports.escapeXML=function(markup){return markup==undefined?"":String(markup).replace(_MATCH_HTML,encode_char)};exports.escapeXML.toString=function(){return Function.prototype.toString.call(this)+";\n"+escapeFuncStr};exports.shallowCopy=function(to,from){from=from||{};for(var p in from){to[p]=from[p]}return to};exports.shallowCopyFromList=function(to,from,list){for(var i=0;i<list.length;i++){var p=list[i];if(typeof from[p]!="undefined"){to[p]=from[p]}}return to};exports.cache={_data:{},set:function(key,val){this._data[key]=val},get:function(key){return this._data[key]},remove:function(key){delete this._data[key]},reset:function(){this._data={}}}},{}],3:[function(require,module,exports){},{}],4:[function(require,module,exports){(function(process){function normalizeArray(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up--;up){parts.unshift("..")}}return parts}var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;var splitPath=function(filename){return splitPathRe.exec(filename).slice(1)};exports.resolve=function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:process.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){continue}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=normalizeArray(filter(resolvedPath.split("/"),function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."};exports.normalize=function(path){var isAbsolute=exports.isAbsolute(path),trailingSlash=substr(path,-1)==="/";path=normalizeArray(filter(path.split("/"),function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path};exports.isAbsolute=function(path){return path.charAt(0)==="/"};exports.join=function(){var paths=Array.prototype.slice.call(arguments,0);return exports.normalize(filter(paths,function(p,index){if(typeof p!=="string"){throw new TypeError("Arguments to path.join must be strings")}return p}).join("/"))};exports.relative=function(from,to){from=exports.resolve(from).substr(1);to=exports.resolve(to).substr(1);function trim(arr){var start=0;for(;start<arr.length;start++){if(arr[start]!=="")break}var end=arr.length-1;for(;end>=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i<length;i++){if(fromParts[i]!==toParts[i]){samePartsLength=i;break}}var outputParts=[];for(var i=samePartsLength;i<fromParts.length;i++){outputParts.push("..")}outputParts=outputParts.concat(toParts.slice(samePartsLength));return outputParts.join("/")};exports.sep="/";exports.delimiter=":";exports.dirname=function(path){var result=splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir};exports.basename=function(path,ext){var f=splitPath(path)[2];if(ext&&f.substr(-1*ext.length)===ext){f=f.substr(0,f.length-ext.length)}return f};exports.extname=function(path){return splitPath(path)[3]};function filter(xs,f){if(xs.filter)return xs.filter(f);var res=[];for(var i=0;i<xs.length;i++){if(f(xs[i],i,xs))res.push(xs[i])}return res}var substr="ab".substr(-1)==="b"?function(str,start,len){return str.substr(start,len)}:function(str,start,len){if(start<0)start=str.length+start;return str.substr(start,len)}}).call(this,require("_process"))},{_process:5}],5:[function(require,module,exports){var process=module.exports={};var cachedSetTimeout;var cachedClearTimeout;function defaultSetTimout(){throw new Error("setTimeout has not been defined")}function defaultClearTimeout(){throw new Error("clearTimeout has not been defined")}(function(){try{if(typeof setTimeout==="function"){cachedSetTimeout=setTimeout}else{cachedSetTimeout=defaultSetTimout}}catch(e){cachedSetTimeout=defaultSetTimout}try{if(typeof clearTimeout==="function"){cachedClearTimeout=clearTimeout}else{cachedClearTimeout=defaultClearTimeout}}catch(e){cachedClearTimeout=defaultClearTimeout}})();function runTimeout(fun){if(cachedSetTimeout===setTimeout){return setTimeout(fun,0)}if((cachedSetTimeout===defaultSetTimout||!cachedSetTimeout)&&setTimeout){cachedSetTimeout=setTimeout;return setTimeout(fun,0)}try{return cachedSetTimeout(fun,0)}catch(e){try{return cachedSetTimeout.call(null,fun,0)}catch(e){return cachedSetTimeout.call(this,fun,0)}}}function runClearTimeout(marker){if(cachedClearTimeout===clearTimeout){return clearTimeout(marker)}if((cachedClearTimeout===defaultClearTimeout||!cachedClearTimeout)&&clearTimeout){cachedClearTimeout=clearTimeout;return clearTimeout(marker)}try{return cachedClearTimeout(marker)}catch(e){try{return cachedClearTimeout.call(null,marker)}catch(e){return cachedClearTimeout.call(this,marker)}}}var queue=[];var draining=false;var currentQueue;var queueIndex=-1;function cleanUpNextTick(){if(!draining||!currentQueue){return}draining=false;if(currentQueue.length){queue=currentQueue.concat(queue)}else{queueIndex=-1}if(queue.length){drainQueue()}}function drainQueue(){if(draining){return}var timeout=runTimeout(cleanUpNextTick);draining=true;var len=queue.length;while(len){currentQueue=queue;queue=[];while(++queueIndex<len){if(currentQueue){currentQueue[queueIndex].run()}}queueIndex=-1;len=queue.length}currentQueue=null;draining=false;runClearTimeout(timeout)}process.nextTick=function(fun){var args=new Array(arguments.length-1);if(arguments.length>1){for(var i=1;i<arguments.length;i++){args[i-1]=arguments[i]}}queue.push(new Item(fun,args));if(queue.length===1&&!draining){runTimeout(drainQueue)}};function Item(fun,array){this.fun=fun;this.array=array}Item.prototype.run=function(){this.fun.apply(null,this.array)};process.title="browser";process.browser=true;process.env={};process.argv=[];process.version="";process.versions={};function noop(){}process.on=noop;process.addListener=noop;process.once=noop;process.off=noop;process.removeListener=noop;process.removeAllListeners=noop;process.emit=noop;process.prependListener=noop;process.prependOnceListener=noop;process.listeners=function(name){return[]};process.binding=function(name){throw new Error("process.binding is not supported")};process.cwd=function(){return"/"};process.chdir=function(dir){throw new Error("process.chdir is not supported")};process.umask=function(){return 0}},{}],6:[function(require,module,exports){module.exports={name:"ejs",description:"Embedded JavaScript templates",keywords:["template","engine","ejs"],version:"3.0.1",author:"Matthew Eernisse <mde@fleegix.org> (http://fleegix.org)",license:"Apache-2.0",main:"./lib/ejs.js",repository:{type:"git",url:"git://github.com/mde/ejs.git"},bugs:"https://github.com/mde/ejs/issues",homepage:"https://github.com/mde/ejs",dependencies:{},devDependencies:{browserify:"^13.1.1",eslint:"^4.14.0","git-directory-deploy":"^1.5.1",jake:"^10.3.1",jsdoc:"^3.4.0","lru-cache":"^4.0.1",mocha:"^5.0.5","uglify-js":"^3.3.16"},engines:{node:">=0.10.0"},scripts:{test:"mocha",postinstall:"node ./postinstall.js"}}},{}]},{},[1])(1)});
1 +var fs = require('fs');
2 +var execSync = require('child_process').execSync;
3 +var exec = function (cmd) {
4 + execSync(cmd, {stdio: 'inherit'});
5 +};
6 +
7 +/* global jake, task, desc, publishTask */
8 +
9 +task('build', ['lint', 'clean', 'browserify', 'minify'], function () {
10 + console.log('Build completed.');
11 +});
12 +
13 +desc('Cleans browerified/minified files and package files');
14 +task('clean', ['clobber'], function () {
15 + jake.rmRf('./ejs.js');
16 + jake.rmRf('./ejs.min.js');
17 + console.log('Cleaned up compiled files.');
18 +});
19 +
20 +desc('Lints the source code');
21 +task('lint', ['clean'], function () {
22 + exec('./node_modules/.bin/eslint "**/*.js"');
23 + console.log('Linting completed.');
24 +});
25 +
26 +task('browserify', function () {
27 + exec('./node_modules/browserify/bin/cmd.js --standalone ejs lib/ejs.js > ejs.js');
28 + console.log('Browserification completed.');
29 +});
30 +
31 +task('minify', function () {
32 + exec('./node_modules/uglify-js/bin/uglifyjs ejs.js > ejs.min.js');
33 + console.log('Minification completed.');
34 +});
35 +
36 +desc('Generates the EJS API docs');
37 +task('doc', function (dev) {
38 + jake.rmRf('out');
39 + var p = dev ? '-p' : '';
40 + exec('./node_modules/.bin/jsdoc ' + p + ' -c jsdoc.json lib/* docs/jsdoc/*');
41 + console.log('Documentation generated.');
42 +});
43 +
44 +desc('Publishes the EJS API docs');
45 +task('docPublish', ['doc'], function () {
46 + fs.writeFileSync('out/CNAME', 'api.ejs.co');
47 + console.log('Pushing docs to gh-pages...');
48 + exec('./node_modules/.bin/git-directory-deploy --directory out/');
49 + console.log('Docs published to gh-pages.');
50 +});
51 +
52 +desc('Runs the EJS test suite');
53 +task('test', ['lint'], function () {
54 + exec('./node_modules/.bin/mocha');
55 +});
56 +
57 +publishTask('ejs', ['build'], function () {
58 + this.packageFiles.include([
59 + 'jakefile.js',
60 + 'README.md',
61 + 'LICENSE',
62 + 'package.json',
63 + 'postinstall.js',
64 + 'ejs.js',
65 + 'ejs.min.js',
66 + 'lib/**'
67 + ]);
68 +});
69 +
70 +jake.Task.publish.on('complete', function () {
71 + console.log('Updating hosted docs...');
72 + console.log('If this fails, run jake docPublish to re-try.');
73 + jake.Task.docPublish.invoke();
74 +});
1 +/*
2 + * EJS Embedded JavaScript templates
3 + * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
4 + *
5 + * Licensed under the Apache License, Version 2.0 (the "License");
6 + * you may not use this file except in compliance with the License.
7 + * You may obtain a copy of the License at
8 + *
9 + * http://www.apache.org/licenses/LICENSE-2.0
10 + *
11 + * Unless required by applicable law or agreed to in writing, software
12 + * distributed under the License is distributed on an "AS IS" BASIS,
13 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + * See the License for the specific language governing permissions and
15 + * limitations under the License.
16 + *
17 +*/
18 +
19 +'use strict';
20 +
21 +/**
22 + * @file Embedded JavaScript templating engine. {@link http://ejs.co}
23 + * @author Matthew Eernisse <mde@fleegix.org>
24 + * @author Tiancheng "Timothy" Gu <timothygu99@gmail.com>
25 + * @project EJS
26 + * @license {@link http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0}
27 + */
28 +
29 +/**
30 + * EJS internal functions.
31 + *
32 + * Technically this "module" lies in the same file as {@link module:ejs}, for
33 + * the sake of organization all the private functions re grouped into this
34 + * module.
35 + *
36 + * @module ejs-internal
37 + * @private
38 + */
39 +
40 +/**
41 + * Embedded JavaScript templating engine.
42 + *
43 + * @module ejs
44 + * @public
45 + */
46 +
47 +var fs = require('fs');
48 +var path = require('path');
49 +var utils = require('./utils');
50 +
51 +var scopeOptionWarned = false;
52 +var _VERSION_STRING = require('../package.json').version;
53 +var _DEFAULT_OPEN_DELIMITER = '<';
54 +var _DEFAULT_CLOSE_DELIMITER = '>';
55 +var _DEFAULT_DELIMITER = '%';
56 +var _DEFAULT_LOCALS_NAME = 'locals';
57 +var _NAME = 'ejs';
58 +var _REGEX_STRING = '(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)';
59 +var _OPTS_PASSABLE_WITH_DATA = ['delimiter', 'scope', 'context', 'debug', 'compileDebug',
60 + 'client', '_with', 'rmWhitespace', 'strict', 'filename', 'async'];
61 +// We don't allow 'cache' option to be passed in the data obj for
62 +// the normal `render` call, but this is where Express 2 & 3 put it
63 +// so we make an exception for `renderFile`
64 +var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat('cache');
65 +var _BOM = /^\uFEFF/;
66 +
67 +/**
68 + * EJS template function cache. This can be a LRU object from lru-cache NPM
69 + * module. By default, it is {@link module:utils.cache}, a simple in-process
70 + * cache that grows continuously.
71 + *
72 + * @type {Cache}
73 + */
74 +
75 +exports.cache = utils.cache;
76 +
77 +/**
78 + * Custom file loader. Useful for template preprocessing or restricting access
79 + * to a certain part of the filesystem.
80 + *
81 + * @type {fileLoader}
82 + */
83 +
84 +exports.fileLoader = fs.readFileSync;
85 +
86 +/**
87 + * Name of the object containing the locals.
88 + *
89 + * This variable is overridden by {@link Options}`.localsName` if it is not
90 + * `undefined`.
91 + *
92 + * @type {String}
93 + * @public
94 + */
95 +
96 +exports.localsName = _DEFAULT_LOCALS_NAME;
97 +
98 +/**
99 + * Promise implementation -- defaults to the native implementation if available
100 + * This is mostly just for testability
101 + *
102 + * @type {Function}
103 + * @public
104 + */
105 +
106 +exports.promiseImpl = (new Function('return this;'))().Promise;
107 +
108 +/**
109 + * Get the path to the included file from the parent file path and the
110 + * specified path.
111 + *
112 + * @param {String} name specified path
113 + * @param {String} filename parent file path
114 + * @param {Boolean} isDir parent file path whether is directory
115 + * @return {String}
116 + */
117 +exports.resolveInclude = function(name, filename, isDir) {
118 + var dirname = path.dirname;
119 + var extname = path.extname;
120 + var resolve = path.resolve;
121 + var includePath = resolve(isDir ? filename : dirname(filename), name);
122 + var ext = extname(name);
123 + if (!ext) {
124 + includePath += '.ejs';
125 + }
126 + return includePath;
127 +};
128 +
129 +/**
130 + * Get the path to the included file by Options
131 + *
132 + * @param {String} path specified path
133 + * @param {Options} options compilation options
134 + * @return {String}
135 + */
136 +function getIncludePath(path, options) {
137 + var includePath;
138 + var filePath;
139 + var views = options.views;
140 + var match = /^[A-Za-z]+:\\|^\//.exec(path);
141 +
142 + // Abs path
143 + if (match && match.length) {
144 + includePath = exports.resolveInclude(path.replace(/^\/*/,''), options.root || '/', true);
145 + }
146 + // Relative paths
147 + else {
148 + // Look relative to a passed filename first
149 + if (options.filename) {
150 + filePath = exports.resolveInclude(path, options.filename);
151 + if (fs.existsSync(filePath)) {
152 + includePath = filePath;
153 + }
154 + }
155 + // Then look in any views directories
156 + if (!includePath) {
157 + if (Array.isArray(views) && views.some(function (v) {
158 + filePath = exports.resolveInclude(path, v, true);
159 + return fs.existsSync(filePath);
160 + })) {
161 + includePath = filePath;
162 + }
163 + }
164 + if (!includePath) {
165 + throw new Error('Could not find the include file "' +
166 + options.escapeFunction(path) + '"');
167 + }
168 + }
169 + return includePath;
170 +}
171 +
172 +/**
173 + * Get the template from a string or a file, either compiled on-the-fly or
174 + * read from cache (if enabled), and cache the template if needed.
175 + *
176 + * If `template` is not set, the file specified in `options.filename` will be
177 + * read.
178 + *
179 + * If `options.cache` is true, this function reads the file from
180 + * `options.filename` so it must be set prior to calling this function.
181 + *
182 + * @memberof module:ejs-internal
183 + * @param {Options} options compilation options
184 + * @param {String} [template] template source
185 + * @return {(TemplateFunction|ClientFunction)}
186 + * Depending on the value of `options.client`, either type might be returned.
187 + * @static
188 + */
189 +
190 +function handleCache(options, template) {
191 + var func;
192 + var filename = options.filename;
193 + var hasTemplate = arguments.length > 1;
194 +
195 + if (options.cache) {
196 + if (!filename) {
197 + throw new Error('cache option requires a filename');
198 + }
199 + func = exports.cache.get(filename);
200 + if (func) {
201 + return func;
202 + }
203 + if (!hasTemplate) {
204 + template = fileLoader(filename).toString().replace(_BOM, '');
205 + }
206 + }
207 + else if (!hasTemplate) {
208 + // istanbul ignore if: should not happen at all
209 + if (!filename) {
210 + throw new Error('Internal EJS error: no file name or template '
211 + + 'provided');
212 + }
213 + template = fileLoader(filename).toString().replace(_BOM, '');
214 + }
215 + func = exports.compile(template, options);
216 + if (options.cache) {
217 + exports.cache.set(filename, func);
218 + }
219 + return func;
220 +}
221 +
222 +/**
223 + * Try calling handleCache with the given options and data and call the
224 + * callback with the result. If an error occurs, call the callback with
225 + * the error. Used by renderFile().
226 + *
227 + * @memberof module:ejs-internal
228 + * @param {Options} options compilation options
229 + * @param {Object} data template data
230 + * @param {RenderFileCallback} cb callback
231 + * @static
232 + */
233 +
234 +function tryHandleCache(options, data, cb) {
235 + var result;
236 + if (!cb) {
237 + if (typeof exports.promiseImpl == 'function') {
238 + return new exports.promiseImpl(function (resolve, reject) {
239 + try {
240 + result = handleCache(options)(data);
241 + resolve(result);
242 + }
243 + catch (err) {
244 + reject(err);
245 + }
246 + });
247 + }
248 + else {
249 + throw new Error('Please provide a callback function');
250 + }
251 + }
252 + else {
253 + try {
254 + result = handleCache(options)(data);
255 + }
256 + catch (err) {
257 + return cb(err);
258 + }
259 +
260 + cb(null, result);
261 + }
262 +}
263 +
264 +/**
265 + * fileLoader is independent
266 + *
267 + * @param {String} filePath ejs file path.
268 + * @return {String} The contents of the specified file.
269 + * @static
270 + */
271 +
272 +function fileLoader(filePath){
273 + return exports.fileLoader(filePath);
274 +}
275 +
276 +/**
277 + * Get the template function.
278 + *
279 + * If `options.cache` is `true`, then the template is cached.
280 + *
281 + * @memberof module:ejs-internal
282 + * @param {String} path path for the specified file
283 + * @param {Options} options compilation options
284 + * @return {(TemplateFunction|ClientFunction)}
285 + * Depending on the value of `options.client`, either type might be returned
286 + * @static
287 + */
288 +
289 +function includeFile(path, options) {
290 + var opts = utils.shallowCopy({}, options);
291 + opts.filename = getIncludePath(path, opts);
292 + return handleCache(opts);
293 +}
294 +
295 +/**
296 + * Re-throw the given `err` in context to the `str` of ejs, `filename`, and
297 + * `lineno`.
298 + *
299 + * @implements RethrowCallback
300 + * @memberof module:ejs-internal
301 + * @param {Error} err Error object
302 + * @param {String} str EJS source
303 + * @param {String} filename file name of the EJS file
304 + * @param {String} lineno line number of the error
305 + * @static
306 + */
307 +
308 +function rethrow(err, str, flnm, lineno, esc){
309 + var lines = str.split('\n');
310 + var start = Math.max(lineno - 3, 0);
311 + var end = Math.min(lines.length, lineno + 3);
312 + var filename = esc(flnm); // eslint-disable-line
313 + // Error context
314 + var context = lines.slice(start, end).map(function (line, i){
315 + var curr = i + start + 1;
316 + return (curr == lineno ? ' >> ' : ' ')
317 + + curr
318 + + '| '
319 + + line;
320 + }).join('\n');
321 +
322 + // Alter exception message
323 + err.path = filename;
324 + err.message = (filename || 'ejs') + ':'
325 + + lineno + '\n'
326 + + context + '\n\n'
327 + + err.message;
328 +
329 + throw err;
330 +}
331 +
332 +function stripSemi(str){
333 + return str.replace(/;(\s*$)/, '$1');
334 +}
335 +
336 +/**
337 + * Compile the given `str` of ejs into a template function.
338 + *
339 + * @param {String} template EJS template
340 + *
341 + * @param {Options} opts compilation options
342 + *
343 + * @return {(TemplateFunction|ClientFunction)}
344 + * Depending on the value of `opts.client`, either type might be returned.
345 + * Note that the return type of the function also depends on the value of `opts.async`.
346 + * @public
347 + */
348 +
349 +exports.compile = function compile(template, opts) {
350 + var templ;
351 +
352 + // v1 compat
353 + // 'scope' is 'context'
354 + // FIXME: Remove this in a future version
355 + if (opts && opts.scope) {
356 + if (!scopeOptionWarned){
357 + console.warn('`scope` option is deprecated and will be removed in EJS 3');
358 + scopeOptionWarned = true;
359 + }
360 + if (!opts.context) {
361 + opts.context = opts.scope;
362 + }
363 + delete opts.scope;
364 + }
365 + templ = new Template(template, opts);
366 + return templ.compile();
367 +};
368 +
369 +/**
370 + * Render the given `template` of ejs.
371 + *
372 + * If you would like to include options but not data, you need to explicitly
373 + * call this function with `data` being an empty object or `null`.
374 + *
375 + * @param {String} template EJS template
376 + * @param {Object} [data={}] template data
377 + * @param {Options} [opts={}] compilation and rendering options
378 + * @return {(String|Promise<String>)}
379 + * Return value type depends on `opts.async`.
380 + * @public
381 + */
382 +
383 +exports.render = function (template, d, o) {
384 + var data = d || {};
385 + var opts = o || {};
386 +
387 + // No options object -- if there are optiony names
388 + // in the data, copy them to options
389 + if (arguments.length == 2) {
390 + utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA);
391 + }
392 +
393 + return handleCache(opts, template)(data);
394 +};
395 +
396 +/**
397 + * Render an EJS file at the given `path` and callback `cb(err, str)`.
398 + *
399 + * If you would like to include options but not data, you need to explicitly
400 + * call this function with `data` being an empty object or `null`.
401 + *
402 + * @param {String} path path to the EJS file
403 + * @param {Object} [data={}] template data
404 + * @param {Options} [opts={}] compilation and rendering options
405 + * @param {RenderFileCallback} cb callback
406 + * @public
407 + */
408 +
409 +exports.renderFile = function () {
410 + var args = Array.prototype.slice.call(arguments);
411 + var filename = args.shift();
412 + var cb;
413 + var opts = {filename: filename};
414 + var data;
415 + var viewOpts;
416 +
417 + // Do we have a callback?
418 + if (typeof arguments[arguments.length - 1] == 'function') {
419 + cb = args.pop();
420 + }
421 + // Do we have data/opts?
422 + if (args.length) {
423 + // Should always have data obj
424 + data = args.shift();
425 + // Normal passed opts (data obj + opts obj)
426 + if (args.length) {
427 + // Use shallowCopy so we don't pollute passed in opts obj with new vals
428 + utils.shallowCopy(opts, args.pop());
429 + }
430 + // Special casing for Express (settings + opts-in-data)
431 + else {
432 + // Express 3 and 4
433 + if (data.settings) {
434 + // Pull a few things from known locations
435 + if (data.settings.views) {
436 + opts.views = data.settings.views;
437 + }
438 + if (data.settings['view cache']) {
439 + opts.cache = true;
440 + }
441 + // Undocumented after Express 2, but still usable, esp. for
442 + // items that are unsafe to be passed along with data, like `root`
443 + viewOpts = data.settings['view options'];
444 + if (viewOpts) {
445 + utils.shallowCopy(opts, viewOpts);
446 + }
447 + }
448 + // Express 2 and lower, values set in app.locals, or people who just
449 + // want to pass options in their data. NOTE: These values will override
450 + // anything previously set in settings or settings['view options']
451 + utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA_EXPRESS);
452 + }
453 + opts.filename = filename;
454 + }
455 + else {
456 + data = {};
457 + }
458 +
459 + return tryHandleCache(opts, data, cb);
460 +};
461 +
462 +/**
463 + * Clear intermediate JavaScript cache. Calls {@link Cache#reset}.
464 + * @public
465 + */
466 +
467 +/**
468 + * EJS template class
469 + * @public
470 + */
471 +exports.Template = Template;
472 +
473 +exports.clearCache = function () {
474 + exports.cache.reset();
475 +};
476 +
477 +function Template(text, opts) {
478 + opts = opts || {};
479 + var options = {};
480 + this.templateText = text;
481 + this.mode = null;
482 + this.truncate = false;
483 + this.currentLine = 1;
484 + this.source = '';
485 + this.dependencies = [];
486 + options.client = opts.client || false;
487 + options.escapeFunction = opts.escape || opts.escapeFunction || utils.escapeXML;
488 + options.compileDebug = opts.compileDebug !== false;
489 + options.debug = !!opts.debug;
490 + options.filename = opts.filename;
491 + options.openDelimiter = opts.openDelimiter || exports.openDelimiter || _DEFAULT_OPEN_DELIMITER;
492 + options.closeDelimiter = opts.closeDelimiter || exports.closeDelimiter || _DEFAULT_CLOSE_DELIMITER;
493 + options.delimiter = opts.delimiter || exports.delimiter || _DEFAULT_DELIMITER;
494 + options.strict = opts.strict || false;
495 + options.context = opts.context;
496 + options.cache = opts.cache || false;
497 + options.rmWhitespace = opts.rmWhitespace;
498 + options.root = opts.root;
499 + options.outputFunctionName = opts.outputFunctionName;
500 + options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME;
501 + options.views = opts.views;
502 + options.async = opts.async;
503 + options.destructuredLocals = opts.destructuredLocals;
504 + options.legacyInclude = typeof opts.legacyInclude != 'undefined' ? !!opts.legacyInclude : true;
505 +
506 + if (options.strict) {
507 + options._with = false;
508 + }
509 + else {
510 + options._with = typeof opts._with != 'undefined' ? opts._with : true;
511 + }
512 +
513 + this.opts = options;
514 +
515 + this.regex = this.createRegex();
516 +}
517 +
518 +Template.modes = {
519 + EVAL: 'eval',
520 + ESCAPED: 'escaped',
521 + RAW: 'raw',
522 + COMMENT: 'comment',
523 + LITERAL: 'literal'
524 +};
525 +
526 +Template.prototype = {
527 + createRegex: function () {
528 + var str = _REGEX_STRING;
529 + var delim = utils.escapeRegExpChars(this.opts.delimiter);
530 + var open = utils.escapeRegExpChars(this.opts.openDelimiter);
531 + var close = utils.escapeRegExpChars(this.opts.closeDelimiter);
532 + str = str.replace(/%/g, delim)
533 + .replace(/</g, open)
534 + .replace(/>/g, close);
535 + return new RegExp(str);
536 + },
537 +
538 + compile: function () {
539 + var src;
540 + var fn;
541 + var opts = this.opts;
542 + var prepended = '';
543 + var appended = '';
544 + var escapeFn = opts.escapeFunction;
545 + var ctor;
546 +
547 + if (!this.source) {
548 + this.generateSource();
549 + prepended +=
550 + ' var __output = "";\n' +
551 + ' function __append(s) { if (s !== undefined && s !== null) __output += s }\n';
552 + if (opts.outputFunctionName) {
553 + prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n';
554 + }
555 + if (opts.destructuredLocals && opts.destructuredLocals.length) {
556 + var destructuring = ' var __locals = (' + opts.localsName + ' || {}),\n';
557 + for (var i = 0; i < opts.destructuredLocals.length; i++) {
558 + var name = opts.destructuredLocals[i];
559 + if (i > 0) {
560 + destructuring += ',\n ';
561 + }
562 + destructuring += name + ' = __locals.' + name;
563 + }
564 + prepended += destructuring + ';\n';
565 + }
566 + if (opts._with !== false) {
567 + prepended += ' with (' + opts.localsName + ' || {}) {' + '\n';
568 + appended += ' }' + '\n';
569 + }
570 + appended += ' return __output;' + '\n';
571 + this.source = prepended + this.source + appended;
572 + }
573 +
574 + if (opts.compileDebug) {
575 + src = 'var __line = 1' + '\n'
576 + + ' , __lines = ' + JSON.stringify(this.templateText) + '\n'
577 + + ' , __filename = ' + (opts.filename ?
578 + JSON.stringify(opts.filename) : 'undefined') + ';' + '\n'
579 + + 'try {' + '\n'
580 + + this.source
581 + + '} catch (e) {' + '\n'
582 + + ' rethrow(e, __lines, __filename, __line, escapeFn);' + '\n'
583 + + '}' + '\n';
584 + }
585 + else {
586 + src = this.source;
587 + }
588 +
589 + if (opts.client) {
590 + src = 'escapeFn = escapeFn || ' + escapeFn.toString() + ';' + '\n' + src;
591 + if (opts.compileDebug) {
592 + src = 'rethrow = rethrow || ' + rethrow.toString() + ';' + '\n' + src;
593 + }
594 + }
595 +
596 + if (opts.strict) {
597 + src = '"use strict";\n' + src;
598 + }
599 + if (opts.debug) {
600 + console.log(src);
601 + }
602 + if (opts.compileDebug && opts.filename) {
603 + src = src + '\n'
604 + + '//# sourceURL=' + opts.filename + '\n';
605 + }
606 +
607 + try {
608 + if (opts.async) {
609 + // Have to use generated function for this, since in envs without support,
610 + // it breaks in parsing
611 + try {
612 + ctor = (new Function('return (async function(){}).constructor;'))();
613 + }
614 + catch(e) {
615 + if (e instanceof SyntaxError) {
616 + throw new Error('This environment does not support async/await');
617 + }
618 + else {
619 + throw e;
620 + }
621 + }
622 + }
623 + else {
624 + ctor = Function;
625 + }
626 + fn = new ctor(opts.localsName + ', escapeFn, include, rethrow', src);
627 + }
628 + catch(e) {
629 + // istanbul ignore else
630 + if (e instanceof SyntaxError) {
631 + if (opts.filename) {
632 + e.message += ' in ' + opts.filename;
633 + }
634 + e.message += ' while compiling ejs\n\n';
635 + e.message += 'If the above error is not helpful, you may want to try EJS-Lint:\n';
636 + e.message += 'https://github.com/RyanZim/EJS-Lint';
637 + if (!opts.async) {
638 + e.message += '\n';
639 + e.message += 'Or, if you meant to create an async function, pass `async: true` as an option.';
640 + }
641 + }
642 + throw e;
643 + }
644 +
645 + // Return a callable function which will execute the function
646 + // created by the source-code, with the passed data as locals
647 + // Adds a local `include` function which allows full recursive include
648 + var returnedFn = opts.client ? fn : function anonymous(data) {
649 + var include = function (path, includeData) {
650 + var d = utils.shallowCopy({}, data);
651 + if (includeData) {
652 + d = utils.shallowCopy(d, includeData);
653 + }
654 + return includeFile(path, opts)(d);
655 + };
656 + return fn.apply(opts.context, [data || {}, escapeFn, include, rethrow]);
657 + };
658 + returnedFn.dependencies = this.dependencies;
659 + if (opts.filename && typeof Object.defineProperty === 'function') {
660 + var filename = opts.filename;
661 + var basename = path.basename(filename, path.extname(filename));
662 + try {
663 + Object.defineProperty(returnedFn, 'name', {
664 + value: basename,
665 + writable: false,
666 + enumerable: false,
667 + configurable: true
668 + });
669 + } catch (e) {/* ignore */}
670 + }
671 + return returnedFn;
672 + },
673 +
674 + generateSource: function () {
675 + var opts = this.opts;
676 +
677 + if (opts.rmWhitespace) {
678 + // Have to use two separate replace here as `^` and `$` operators don't
679 + // work well with `\r` and empty lines don't work well with the `m` flag.
680 + this.templateText =
681 + this.templateText.replace(/[\r\n]+/g, '\n').replace(/^\s+|\s+$/gm, '');
682 + }
683 +
684 + // Slurp spaces and tabs before <%_ and after _%>
685 + this.templateText =
686 + this.templateText.replace(/[ \t]*<%_/gm, '<%_').replace(/_%>[ \t]*/gm, '_%>');
687 +
688 + var self = this;
689 + var matches = this.parseTemplateText();
690 + var d = this.opts.delimiter;
691 + var o = this.opts.openDelimiter;
692 + var c = this.opts.closeDelimiter;
693 +
694 + if (matches && matches.length) {
695 + matches.forEach(function (line, index) {
696 + var closing;
697 + // If this is an opening tag, check for closing tags
698 + // FIXME: May end up with some false positives here
699 + // Better to store modes as k/v with openDelimiter + delimiter as key
700 + // Then this can simply check against the map
701 + if ( line.indexOf(o + d) === 0 // If it is a tag
702 + && line.indexOf(o + d + d) !== 0) { // and is not escaped
703 + closing = matches[index + 2];
704 + if (!(closing == d + c || closing == '-' + d + c || closing == '_' + d + c)) {
705 + throw new Error('Could not find matching close tag for "' + line + '".');
706 + }
707 + }
708 + self.scanLine(line);
709 + });
710 + }
711 +
712 + },
713 +
714 + parseTemplateText: function () {
715 + var str = this.templateText;
716 + var pat = this.regex;
717 + var result = pat.exec(str);
718 + var arr = [];
719 + var firstPos;
720 +
721 + while (result) {
722 + firstPos = result.index;
723 +
724 + if (firstPos !== 0) {
725 + arr.push(str.substring(0, firstPos));
726 + str = str.slice(firstPos);
727 + }
728 +
729 + arr.push(result[0]);
730 + str = str.slice(result[0].length);
731 + result = pat.exec(str);
732 + }
733 +
734 + if (str) {
735 + arr.push(str);
736 + }
737 +
738 + return arr;
739 + },
740 +
741 + _addOutput: function (line) {
742 + if (this.truncate) {
743 + // Only replace single leading linebreak in the line after
744 + // -%> tag -- this is the single, trailing linebreak
745 + // after the tag that the truncation mode replaces
746 + // Handle Win / Unix / old Mac linebreaks -- do the \r\n
747 + // combo first in the regex-or
748 + line = line.replace(/^(?:\r\n|\r|\n)/, '');
749 + this.truncate = false;
750 + }
751 + if (!line) {
752 + return line;
753 + }
754 +
755 + // Preserve literal slashes
756 + line = line.replace(/\\/g, '\\\\');
757 +
758 + // Convert linebreaks
759 + line = line.replace(/\n/g, '\\n');
760 + line = line.replace(/\r/g, '\\r');
761 +
762 + // Escape double-quotes
763 + // - this will be the delimiter during execution
764 + line = line.replace(/"/g, '\\"');
765 + this.source += ' ; __append("' + line + '")' + '\n';
766 + },
767 +
768 + scanLine: function (line) {
769 + var self = this;
770 + var d = this.opts.delimiter;
771 + var o = this.opts.openDelimiter;
772 + var c = this.opts.closeDelimiter;
773 + var newLineCount = 0;
774 +
775 + newLineCount = (line.split('\n').length - 1);
776 +
777 + switch (line) {
778 + case o + d:
779 + case o + d + '_':
780 + this.mode = Template.modes.EVAL;
781 + break;
782 + case o + d + '=':
783 + this.mode = Template.modes.ESCAPED;
784 + break;
785 + case o + d + '-':
786 + this.mode = Template.modes.RAW;
787 + break;
788 + case o + d + '#':
789 + this.mode = Template.modes.COMMENT;
790 + break;
791 + case o + d + d:
792 + this.mode = Template.modes.LITERAL;
793 + this.source += ' ; __append("' + line.replace(o + d + d, o + d) + '")' + '\n';
794 + break;
795 + case d + d + c:
796 + this.mode = Template.modes.LITERAL;
797 + this.source += ' ; __append("' + line.replace(d + d + c, d + c) + '")' + '\n';
798 + break;
799 + case d + c:
800 + case '-' + d + c:
801 + case '_' + d + c:
802 + if (this.mode == Template.modes.LITERAL) {
803 + this._addOutput(line);
804 + }
805 +
806 + this.mode = null;
807 + this.truncate = line.indexOf('-') === 0 || line.indexOf('_') === 0;
808 + break;
809 + default:
810 + // In script mode, depends on type of tag
811 + if (this.mode) {
812 + // If '//' is found without a line break, add a line break.
813 + switch (this.mode) {
814 + case Template.modes.EVAL:
815 + case Template.modes.ESCAPED:
816 + case Template.modes.RAW:
817 + if (line.lastIndexOf('//') > line.lastIndexOf('\n')) {
818 + line += '\n';
819 + }
820 + }
821 + switch (this.mode) {
822 + // Just executing code
823 + case Template.modes.EVAL:
824 + this.source += ' ; ' + line + '\n';
825 + break;
826 + // Exec, esc, and output
827 + case Template.modes.ESCAPED:
828 + this.source += ' ; __append(escapeFn(' + stripSemi(line) + '))' + '\n';
829 + break;
830 + // Exec and output
831 + case Template.modes.RAW:
832 + this.source += ' ; __append(' + stripSemi(line) + ')' + '\n';
833 + break;
834 + case Template.modes.COMMENT:
835 + // Do nothing
836 + break;
837 + // Literal <%% mode, append as raw output
838 + case Template.modes.LITERAL:
839 + this._addOutput(line);
840 + break;
841 + }
842 + }
843 + // In string mode, just add the output
844 + else {
845 + this._addOutput(line);
846 + }
847 + }
848 +
849 + if (self.opts.compileDebug && newLineCount) {
850 + this.currentLine += newLineCount;
851 + this.source += ' ; __line = ' + this.currentLine + '\n';
852 + }
853 + }
854 +};
855 +
856 +/**
857 + * Escape characters reserved in XML.
858 + *
859 + * This is simply an export of {@link module:utils.escapeXML}.
860 + *
861 + * If `markup` is `undefined` or `null`, the empty string is returned.
862 + *
863 + * @param {String} markup Input string
864 + * @return {String} Escaped string
865 + * @public
866 + * @func
867 + * */
868 +exports.escapeXML = utils.escapeXML;
869 +
870 +/**
871 + * Express.js support.
872 + *
873 + * This is an alias for {@link module:ejs.renderFile}, in order to support
874 + * Express.js out-of-the-box.
875 + *
876 + * @func
877 + */
878 +
879 +exports.__express = exports.renderFile;
880 +
881 +/**
882 + * Version of EJS.
883 + *
884 + * @readonly
885 + * @type {String}
886 + * @public
887 + */
888 +
889 +exports.VERSION = _VERSION_STRING;
890 +
891 +/**
892 + * Name for detection of EJS.
893 + *
894 + * @readonly
895 + * @type {String}
896 + * @public
897 + */
898 +
899 +exports.name = _NAME;
900 +
901 +/* istanbul ignore if */
902 +if (typeof window != 'undefined') {
903 + window.ejs = exports;
904 +}
1 +/*
2 + * EJS Embedded JavaScript templates
3 + * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
4 + *
5 + * Licensed under the Apache License, Version 2.0 (the "License");
6 + * you may not use this file except in compliance with the License.
7 + * You may obtain a copy of the License at
8 + *
9 + * http://www.apache.org/licenses/LICENSE-2.0
10 + *
11 + * Unless required by applicable law or agreed to in writing, software
12 + * distributed under the License is distributed on an "AS IS" BASIS,
13 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + * See the License for the specific language governing permissions and
15 + * limitations under the License.
16 + *
17 +*/
18 +
19 +/**
20 + * Private utility functions
21 + * @module utils
22 + * @private
23 + */
24 +
25 +'use strict';
26 +
27 +var regExpChars = /[|\\{}()[\]^$+*?.]/g;
28 +
29 +/**
30 + * Escape characters reserved in regular expressions.
31 + *
32 + * If `string` is `undefined` or `null`, the empty string is returned.
33 + *
34 + * @param {String} string Input string
35 + * @return {String} Escaped string
36 + * @static
37 + * @private
38 + */
39 +exports.escapeRegExpChars = function (string) {
40 + // istanbul ignore if
41 + if (!string) {
42 + return '';
43 + }
44 + return String(string).replace(regExpChars, '\\$&');
45 +};
46 +
47 +var _ENCODE_HTML_RULES = {
48 + '&': '&amp;',
49 + '<': '&lt;',
50 + '>': '&gt;',
51 + '"': '&#34;',
52 + "'": '&#39;'
53 +};
54 +var _MATCH_HTML = /[&<>'"]/g;
55 +
56 +function encode_char(c) {
57 + return _ENCODE_HTML_RULES[c] || c;
58 +}
59 +
60 +/**
61 + * Stringified version of constants used by {@link module:utils.escapeXML}.
62 + *
63 + * It is used in the process of generating {@link ClientFunction}s.
64 + *
65 + * @readonly
66 + * @type {String}
67 + */
68 +
69 +var escapeFuncStr =
70 + 'var _ENCODE_HTML_RULES = {\n'
71 ++ ' "&": "&amp;"\n'
72 ++ ' , "<": "&lt;"\n'
73 ++ ' , ">": "&gt;"\n'
74 ++ ' , \'"\': "&#34;"\n'
75 ++ ' , "\'": "&#39;"\n'
76 ++ ' }\n'
77 ++ ' , _MATCH_HTML = /[&<>\'"]/g;\n'
78 ++ 'function encode_char(c) {\n'
79 ++ ' return _ENCODE_HTML_RULES[c] || c;\n'
80 ++ '};\n';
81 +
82 +/**
83 + * Escape characters reserved in XML.
84 + *
85 + * If `markup` is `undefined` or `null`, the empty string is returned.
86 + *
87 + * @implements {EscapeCallback}
88 + * @param {String} markup Input string
89 + * @return {String} Escaped string
90 + * @static
91 + * @private
92 + */
93 +
94 +exports.escapeXML = function (markup) {
95 + return markup == undefined
96 + ? ''
97 + : String(markup)
98 + .replace(_MATCH_HTML, encode_char);
99 +};
100 +exports.escapeXML.toString = function () {
101 + return Function.prototype.toString.call(this) + ';\n' + escapeFuncStr;
102 +};
103 +
104 +/**
105 + * Naive copy of properties from one object to another.
106 + * Does not recurse into non-scalar properties
107 + * Does not check to see if the property has a value before copying
108 + *
109 + * @param {Object} to Destination object
110 + * @param {Object} from Source object
111 + * @return {Object} Destination object
112 + * @static
113 + * @private
114 + */
115 +exports.shallowCopy = function (to, from) {
116 + from = from || {};
117 + for (var p in from) {
118 + to[p] = from[p];
119 + }
120 + return to;
121 +};
122 +
123 +/**
124 + * Naive copy of a list of key names, from one object to another.
125 + * Only copies property if it is actually defined
126 + * Does not recurse into non-scalar properties
127 + *
128 + * @param {Object} to Destination object
129 + * @param {Object} from Source object
130 + * @param {Array} list List of properties to copy
131 + * @return {Object} Destination object
132 + * @static
133 + * @private
134 + */
135 +exports.shallowCopyFromList = function (to, from, list) {
136 + for (var i = 0; i < list.length; i++) {
137 + var p = list[i];
138 + if (typeof from[p] != 'undefined') {
139 + to[p] = from[p];
140 + }
141 + }
142 + return to;
143 +};
144 +
145 +/**
146 + * Simple in-process cache implementation. Does not implement limits of any
147 + * sort.
148 + *
149 + * @implements Cache
150 + * @static
151 + * @private
152 + */
153 +exports.cache = {
154 + _data: {},
155 + set: function (key, val) {
156 + this._data[key] = val;
157 + },
158 + get: function (key) {
159 + return this._data[key];
160 + },
161 + remove: function (key) {
162 + delete this._data[key];
163 + },
164 + reset: function () {
165 + this._data = {};
166 + }
167 +};
1 +{
2 + "_from": "ejs",
3 + "_id": "ejs@3.0.1",
4 + "_inBundle": false,
5 + "_integrity": "sha512-cuIMtJwxvzumSAkqaaoGY/L6Fc/t6YvoP9/VIaK0V/CyqKLEQ8sqODmYfy/cjXEdZ9+OOL8TecbJu+1RsofGDw==",
6 + "_location": "/ejs",
7 + "_phantomChildren": {},
8 + "_requested": {
9 + "type": "tag",
10 + "registry": true,
11 + "raw": "ejs",
12 + "name": "ejs",
13 + "escapedName": "ejs",
14 + "rawSpec": "",
15 + "saveSpec": null,
16 + "fetchSpec": "latest"
17 + },
18 + "_requiredBy": [
19 + "#USER",
20 + "/"
21 + ],
22 + "_resolved": "https://registry.npmjs.org/ejs/-/ejs-3.0.1.tgz",
23 + "_shasum": "30c8f6ee9948502cc32e85c37a3f8b39b5a614a5",
24 + "_spec": "ejs",
25 + "_where": "C:\\Users\\LG\\Desktop\\4-1\\Reminder-Talk",
26 + "author": {
27 + "name": "Matthew Eernisse",
28 + "email": "mde@fleegix.org",
29 + "url": "http://fleegix.org"
30 + },
31 + "bugs": {
32 + "url": "https://github.com/mde/ejs/issues"
33 + },
34 + "bundleDependencies": false,
35 + "dependencies": {},
36 + "deprecated": false,
37 + "description": "Embedded JavaScript templates",
38 + "devDependencies": {
39 + "browserify": "^13.1.1",
40 + "eslint": "^4.14.0",
41 + "git-directory-deploy": "^1.5.1",
42 + "jake": "^10.3.1",
43 + "jsdoc": "^3.4.0",
44 + "lru-cache": "^4.0.1",
45 + "mocha": "^5.0.5",
46 + "uglify-js": "^3.3.16"
47 + },
48 + "engines": {
49 + "node": ">=0.10.0"
50 + },
51 + "homepage": "https://github.com/mde/ejs",
52 + "keywords": [
53 + "template",
54 + "engine",
55 + "ejs"
56 + ],
57 + "license": "Apache-2.0",
58 + "main": "./lib/ejs.js",
59 + "name": "ejs",
60 + "repository": {
61 + "type": "git",
62 + "url": "git://github.com/mde/ejs.git"
63 + },
64 + "scripts": {
65 + "postinstall": "node ./postinstall.js",
66 + "test": "mocha"
67 + },
68 + "version": "3.0.1"
69 +}
1 +#!/usr/bin/env node
2 +
3 +'use strict';
4 +
5 +function isTrue(value) {
6 + return !!value && value !== '0' && value !== 'false';
7 +}
8 +
9 +let envDisable = isTrue(process.env.DISABLE_OPENCOLLECTIVE) || isTrue(process.env.CI);
10 +let logLevel = process.env.npm_config_loglevel;
11 +let logLevelDisplay = ['silent', 'error', 'warn'].indexOf(logLevel) > -1;
12 +
13 +if (!envDisable && !logLevelDisplay) {
14 + console.log('Thank you for installing \u001b[35mEJS\u001b[0m: built with the \u001b[32mJake\u001b[0m JavaScript build tool (\u001b[32mhttps://jakejs.com/\u001b[0m)\n');
15 +}
16 +
17 +
...@@ -225,6 +225,11 @@ ...@@ -225,6 +225,11 @@
225 "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 225 "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
226 "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 226 "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
227 }, 227 },
228 + "ejs": {
229 + "version": "3.0.1",
230 + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.0.1.tgz",
231 + "integrity": "sha512-cuIMtJwxvzumSAkqaaoGY/L6Fc/t6YvoP9/VIaK0V/CyqKLEQ8sqODmYfy/cjXEdZ9+OOL8TecbJu+1RsofGDw=="
232 + },
228 "encodeurl": { 233 "encodeurl": {
229 "version": "1.0.2", 234 "version": "1.0.2",
230 "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 235 "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
8 "dependencies": { 8 "dependencies": {
9 "cookie-parser": "~1.4.4", 9 "cookie-parser": "~1.4.4",
10 "debug": "~2.6.9", 10 "debug": "~2.6.9",
11 + "ejs": "^3.0.1",
11 "express": "~4.16.1", 12 "express": "~4.16.1",
12 "http-errors": "~1.6.3", 13 "http-errors": "~1.6.3",
13 "jade": "~1.11.0", 14 "jade": "~1.11.0",
......
1 +<h1><%= message %></h1>
2 +<h2><%= error.status %></h2>
3 +<pre><%= error.stack %></pre>
...\ No newline at end of file ...\ No newline at end of file
1 -extends layout
2 -
3 -block content
4 - h1= message
5 - h2= error.status
6 - pre #{error.stack}
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <title><%= title %></title>
5 + <link rel='stylesheet' href='/stylesheets/style.css' />
6 + </head>
7 + <body>
8 + <h1><%= title %></h1>
9 + <p>Welcome to <%= title %></p>
10 + </body>
11 +</html>
...\ No newline at end of file ...\ No newline at end of file
1 -extends layout
2 -
3 -block content
4 - h1= title
5 - p Welcome to #{title}
1 -doctype html
2 -html
3 - head
4 - title= title
5 - link(rel='stylesheet', href='/stylesheets/style.css')
6 - body
7 - block content