twitst4tz

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit b5c0d463c3cdda9b154db19325aad9c57a8e5be4
parent d1f815b66794c6e2e8a54f8e9375df1f6317cbb5
Author: underd0g1 <hide4@comcast.net>
Date:   Mon, 23 Mar 2020 00:12:57 -0400

added ejs templating engine

Diffstat:
Mconfig.js | 35++++++++++++++++++-----------------
Dindex.html | 93-------------------------------------------------------------------------------
Mindex.js | 64+++++++++++++++++-----------------------------------------------
Anode_modules/ejs/LICENSE | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anode_modules/ejs/README.md | 273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anode_modules/ejs/ejs.js | 1532+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anode_modules/ejs/ejs.min.js | 1+
Anode_modules/ejs/jakefile.js | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anode_modules/ejs/lib/ejs.js | 904+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anode_modules/ejs/lib/utils.js | 167+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anode_modules/ejs/package.json | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anode_modules/ejs/postinstall.js | 17+++++++++++++++++
Mnode_modules/express/package.json | 26+++++++++++++-------------
Mnode_modules/node-fetch/package.json | 14+++++++-------
Mnode_modules/twit/package.json | 18+++++++++---------
Mpublic/js/fetch.js | 2+-
Aviews/index.ejs | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
17 files changed, 3427 insertions(+), 187 deletions(-)

diff --git a/config.js b/config.js @@ -1,27 +1,28 @@ // main sheet to call the commands and interact with web calls -var Twit = require('twit') +module.exports = { -var T = new Twit({ - consumer_key: 'w6ON79TdOclk5BKZ2zgSMJLrZ', - consumer_secret: 'LNHOlUsYcUWyX4YDSR88WWPoD6NWeajoubfIYvcCZmuBNaqz0b', - access_token: '2577728548-KVxUKWqyr29nsocxvH5Em0oLWUx4n30VXfABORS', - access_token_secret: 'CVdF3NKtoNtObTFZ8zI0VLheKDpxhvsv5SdxDX67ko5Kt', + consumer_key: 'iPKcerCW1JmKUfQ880ERn8POP', + consumer_secret: 'TaPHpZS6bhD1P5VEj8p5lVZ95GiLOccwySDbXRDzgrfcbOT88P', + access_token: '1212777559819079680-4nYVYbkIj8t6RUwwmQFIUGO4210ujQ', + access_token_secret: 'JtIVh8XD7a8P4FqK3oh35K1bjPgjMb5np2ZVsnfdWAc1j', + timeout_ms: 60*1000, // optional HTTP request timeout to apply to all requests. + strictSSL: false, // optional - requires SSL certificates to be valid -}) +}; // search twitter for all tweets containing the word 'banana' since July 11, 2011 -T.get('search/tweets', { q: '350z since:2011-07-11', count: 100 }, function(err, data, response) { - console.log(data) -}) - - - - -T.get('followers/ids', { screen_name: 'Greatt_DaNe1' }, function (err, data, response) { - console.log(data.ids) -}) +// T.get('search/tweets', { q: '350z since:2011-07-11', count: 3 }, function(err, data, response) { +// console.log(data) +// }) +// +// +// +// +// T.get('followers/ids', { screen_name: 'Greatt_DaNe1' }, function (err, data, response) { +// console.log(data.ids) +// }) // fetch to tranlate the ids diff --git a/index.html b/index.html @@ -1,93 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - - <title>TweetWebGraph</title> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <link rel = 'stylesheet' href = '/css/stylesheet.css'> - <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"> - <link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet"> - <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> - <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script> - <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular.min.js"></script> - - -</head> -<body> - -<!-- Navbar --> -<nav class="navbar navbar-default"> - <div class="container"> - <div class="navbar-header"> - <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar"> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - </button> - <a class="navbar-brand" href="#">< TweetWebGraph /> </a> - </div> - <div class="collapse navbar-collapse" id="myNavbar"> - <ul class="nav navbar-nav navbar-right"> - <li><a href="#">WHOIS</a></li> - <li><a href="#">./run</a></li> - <li><a href="#">HOWTO</a></li> - </ul> - </div> - </div> -</nav> - -<!-- First Container --> -<div class="container-fluid bg-1 text-center"> - <h3 class="margin">Whois?</h3> - <img src="/images/dead-smiley-face-icon_17015.png" class="img-responsive" style="display:inline" alt="smile" width="350" height="350"> - <h3>Just Another WebApp To Show Your Twitter Stats...</h3> -</div> - -<!-- Second Container --> -<!-- Inject some JS to add the api connection and user functionality --> -<div class="container-fluid bg-2 text-center"> - - <h3 class="margin">./Run</h3> - <p>Enter in the username you would like to get a list of followers for. (example: twitterdevteam)</p> - - -<input type="text" id="usernametxt" required> -<button onclick ='getinfo()'>GET</button> -<<script src="fetch.js" charset="utf-8"></script> -</div> - -<!-- Third Container (Grid) --> -<div class="container-fluid bg-3 text-center"> - <h3 class="margin">HOWTO</h3><br> - <div class="row"> - <div class="col-sm-4"> - <p>Check out the project on Github!</p> - <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a> - </div> - <div class="col-sm-4"> - <p>References</p> - <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> - <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> - <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> - <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> - </div> - <div class="col-sm-4"> - <p>Friends</p> - <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> - <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> - <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> - <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> - <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> - - </div> - </div> -</div> - -<!-- Footer --> -<footer class="container-fluid bg-4 text-center"> - <p>Made with <span class="glyphicon glyphicon-heart"></span><br><a href="http://www.underd0g.co"><span class="glyphicon glyphicon-barcode"></span></a></p> -</footer> - -</body> -</html> diff --git a/index.js b/index.js @@ -2,66 +2,36 @@ const fetch = require('node-fetch'); const http = require('http'); var express = require("express"); +var twit = require('twit'); +var creds = require('./config.js'); var app = express(); var router = express.Router(); -router.use(function (req,res,next) { - console.log("/" + req.method); - next(); -}); - -router.get("/",function(req,res){ - res.sendFile("/home/hide4/Documents/Javascript/TwitWebGraph/index.html"); +var t = new twit(creds); -}); +app.set('view engine', 'ejs'); +app.use(express.static(__dirname + '/public')); -app.use("/",router); -app.use(express.static("public")); +app.get("/",function(req,res){ -app.listen(3000,function(){ - console.log("Live at Port 3000"); + res.render('index',{ + name: name + }); }); -//set up a stream to send and recieve the data from node server to the actual web server +t.get('search/tweets', { q: '350z since:2011-07-11', count: 3 }, function(err, data, response) { + console.log(data) + }) +app.listen(3000,function(){ + console.log("Live at Port 3000"); +}); -// const hostname = '127.0.0.1'; -// const port = 3000; -// -// const server = http.createServer((req, res) => { -// res.statusCode = 200; -// res.setHeader('Content-Type', 'text/plain'); -// res.write('Have no fear, underd0g is here!\n'); -// -// res.end('Hello World\n'); -// -// }); -// -// server.listen(port, hostname, () => { -// console.log(`Server running at http://${hostname}:${port}/`); -// }); -// -// async function test(){ -// var req = await fetch("https://api.twitter.com/1.1/followers/list.json?cursor=-1&screen_name=Greatt_DaNe1&skip_status=true&include_user_entities=false", { -// headers: { -// Authorization: "Bearer enter bearer token here", -// //Access-Control-Allow-Origin: experimental use* -// } -// }); -// const data = await req.json(); -// -// -// //start the loop to display all the screen names assosiated with the user. -// for(i=0;i<100;i++){ -// console.log(data.users[i].screen_name); -// } -// } -// -// //call the function -// test(); + +//set up a stream to send and recieve the data from node server to the actual web server diff --git a/node_modules/ejs/LICENSE b/node_modules/ejs/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/node_modules/ejs/README.md b/node_modules/ejs/README.md @@ -0,0 +1,273 @@ +# EJS + +Embedded JavaScript templates + +[![Build Status](https://img.shields.io/travis/mde/ejs/master.svg?style=flat)](https://travis-ci.org/mde/ejs) +[![Developing Dependencies](https://img.shields.io/david/dev/mde/ejs.svg?style=flat)](https://david-dm.org/mde/ejs?type=dev) +[![Known Vulnerabilities](https://snyk.io/test/npm/ejs/badge.svg?style=flat)](https://snyk.io/test/npm/ejs) + +## Installation + +```bash +$ npm install ejs +``` + +## Features + + * Control flow with `<% %>` + * Escaped output with `<%= %>` (escape function configurable) + * Unescaped raw output with `<%- %>` + * Newline-trim mode ('newline slurping') with `-%>` ending tag + * Whitespace-trim mode (slurp all whitespace) for control flow with `<%_ _%>` + * Custom delimiters (e.g., use `<? ?>` instead of `<% %>`) + * Includes + * Client-side support + * Static caching of intermediate JavaScript + * Static caching of templates + * Complies with the [Express](http://expressjs.com) view system + +## Example + +```ejs +<% if (user) { %> + <h2><%= user.name %></h2> +<% } %> +``` + +Try EJS online at: https://ionicabizau.github.io/ejs-playground/. + +## Usage + +```javascript +let template = ejs.compile(str, options); +template(data); +// => Rendered HTML string + +ejs.render(str, data, options); +// => Rendered HTML string + +ejs.renderFile(filename, data, options, function(err, str){ + // str => Rendered HTML string +}); +``` + +It is also possible to use `ejs.render(dataAndOptions);` where you pass +everything in a single object. In that case, you'll end up with local variables +for all the passed options. However, be aware that your code could break if we +add an option with the same name as one of your data object's properties. +Therefore, we do not recommend using this shortcut. + +## Options + + - `cache` Compiled functions are cached, requires `filename` + - `filename` The name of the file being rendered. Not required if you + are using `renderFile()`. Used by `cache` to key caches, and for includes. + - `root` Set project root for includes with an absolute path (/file.ejs). + - `context` Function execution context + - `compileDebug` When `false` no debug instrumentation is compiled + - `client` When `true`, compiles a function that can be rendered + in the browser without needing to load the EJS Runtime + ([ejs.min.js](https://github.com/mde/ejs/releases/latest)). + - `delimiter` Character to use with angle brackets for open/close + - `debug` Output generated function body + - `strict` When set to `true`, generated function is in strict mode + - `_with` Whether or not to use `with() {}` constructs. If `false` + then the locals will be stored in the `locals` object. Set to `false` in strict mode. + - `destructuredLocals` An array of local variables that are always destructured from + the locals object, available even in strict mode. + - `localsName` Name to use for the object storing local variables when not using + `with` Defaults to `locals` + - `rmWhitespace` Remove all safe-to-remove whitespace, including leading + and trailing whitespace. It also enables a safer version of `-%>` line + slurping for all scriptlet tags (it does not strip new lines of tags in + the middle of a line). + - `escape` The escaping function used with `<%=` construct. It is + used in rendering and is `.toString()`ed in the generation of client functions. + (By default escapes XML). + - `outputFunctionName` Set to a string (e.g., 'echo' or 'print') for a function to print + output inside scriptlet tags. + - `async` When `true`, EJS will use an async function for rendering. (Depends + on async/await support in the JS runtime. + +This project uses [JSDoc](http://usejsdoc.org/). For the full public API +documentation, clone the repository and run `npm run doc`. This will run JSDoc +with the proper options and output the documentation to `out/`. If you want +the both the public & private API docs, run `npm run devdoc` instead. + +## Tags + + - `<%` 'Scriptlet' tag, for control-flow, no output + - `<%_` 'Whitespace Slurping' Scriptlet tag, strips all whitespace before it + - `<%=` Outputs the value into the template (escaped) + - `<%-` Outputs the unescaped value into the template + - `<%#` Comment tag, no execution, no output + - `<%%` Outputs a literal '<%' + - `%%>` Outputs a literal '%>' + - `%>` Plain ending tag + - `-%>` Trim-mode ('newline slurp') tag, trims following newline + - `_%>` 'Whitespace Slurping' ending tag, removes all whitespace after it + +For the full syntax documentation, please see [docs/syntax.md](https://github.com/mde/ejs/blob/master/docs/syntax.md). + +## Includes + +Includes either have to be an absolute path, or, if not, are assumed as +relative to the template with the `include` call. For example if you are +including `./views/user/show.ejs` from `./views/users.ejs` you would +use `<%- include('user/show') %>`. + +You must specify the `filename` option for the template with the `include` +call unless you are using `renderFile()`. + +You'll likely want to use the raw output tag (`<%-`) with your include to avoid +double-escaping the HTML output. + +```ejs +<ul> + <% users.forEach(function(user){ %> + <%- include('user/show', {user: user}) %> + <% }); %> +</ul> +``` + +Includes are inserted at runtime, so you can use variables for the path in the +`include` call (for example `<%- include(somePath) %>`). Variables in your +top-level data object are available to all your includes, but local variables +need to be passed down. + +NOTE: Include preprocessor directives (`<% include user/show %>`) are +still supported. + +## Custom delimiters + +Custom delimiters can be applied on a per-template basis, or globally: + +```javascript +let ejs = require('ejs'), + users = ['geddy', 'neil', 'alex']; + +// Just one template +ejs.render('<?= users.join(" | "); ?>', {users: users}, {delimiter: '?'}); +// => 'geddy | neil | alex' + +// Or globally +ejs.delimiter = '$'; +ejs.render('<$= users.join(" | "); $>', {users: users}); +// => 'geddy | neil | alex' +``` + +## Caching + +EJS ships with a basic in-process cache for caching the intermediate JavaScript +functions used to render templates. It's easy to plug in LRU caching using +Node's `lru-cache` library: + +```javascript +let ejs = require('ejs'), + LRU = require('lru-cache'); +ejs.cache = LRU(100); // LRU cache with 100-item limit +``` + +If you want to clear the EJS cache, call `ejs.clearCache`. If you're using the +LRU cache and need a different limit, simple reset `ejs.cache` to a new instance +of the LRU. + +## Custom file loader + +The default file loader is `fs.readFileSync`, if you want to customize it, you can set ejs.fileLoader. + +```javascript +let ejs = require('ejs'); +let myFileLoad = function (filePath) { + return 'myFileLoad: ' + fs.readFileSync(filePath); +}; + +ejs.fileLoader = myFileLoad; +``` + +With this feature, you can preprocess the template before reading it. + +## Layouts + +EJS does not specifically support blocks, but layouts can be implemented by +including headers and footers, like so: + + +```ejs +<%- include('header') -%> +<h1> + Title +</h1> +<p> + My page +</p> +<%- include('footer') -%> +``` + +## Client-side support + +Go to the [Latest Release](https://github.com/mde/ejs/releases/latest), download +`./ejs.js` or `./ejs.min.js`. Alternately, you can compile it yourself by cloning +the repository and running `jake build` (or `$(npm bin)/jake build` if jake is +not installed globally). + +Include one of these files on your page, and `ejs` should be available globally. + +### Example + +```html +<div id="output"></div> +<script src="ejs.min.js"></script> +<script> + let people = ['geddy', 'neil', 'alex'], + html = ejs.render('<%= people.join(", "); %>', {people: people}); + // With jQuery: + $('#output').html(html); + // Vanilla JS: + document.getElementById('output').innerHTML = html; +</script> +``` + +### Caveats + +Most of EJS will work as expected; however, there are a few things to note: + +1. Obviously, since you do not have access to the filesystem, `ejs.renderFile()` won't work. +2. For the same reason, `include`s do not work unless you use an `include callback`. Here is an example: + ```javascript + let str = "Hello <%= include('file', {person: 'John'}); %>", + fn = ejs.compile(str, {client: true}); + + fn(data, null, function(path, d){ // include callback + // path -> 'file' + // d -> {person: 'John'} + // Put your code here + // Return the contents of file as a string + }); // returns rendered string + ``` + +See the [examples folder](https://github.com/mde/ejs/tree/master/examples) for more details. + +### IDE Integration with Syntax Highlighting + +VSCode:Javascript EJS by *DigitalBrainstem* + +## Related projects + +There are a number of implementations of EJS: + + * TJ's implementation, the v1 of this library: https://github.com/tj/ejs + * Jupiter Consulting's EJS: http://www.embeddedjs.com/ + * EJS Embedded JavaScript Framework on Google Code: https://code.google.com/p/embeddedjavascript/ + * Sam Stephenson's Ruby implementation: https://rubygems.org/gems/ejs + * Erubis, an ERB implementation which also runs JavaScript: http://www.kuwata-lab.com/erubis/users-guide.04.html#lang-javascript + * DigitalBrainstem EJS Language support: https://github.com/Digitalbrainstem/ejs-grammar + +## License + +Licensed under the Apache License, Version 2.0 +(<http://www.apache.org/licenses/LICENSE-2.0>) + +- - - +EJS Embedded JavaScript templates copyright 2112 +mde@fleegix.org. diff --git a/node_modules/ejs/ejs.js b/node_modules/ejs/ejs.js @@ -0,0 +1,1532 @@ +(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){ +/* + * EJS Embedded JavaScript templates + * Copyright 2112 Matthew Eernisse (mde@fleegix.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +'use strict'; + +/** + * @file Embedded JavaScript templating engine. {@link http://ejs.co} + * @author Matthew Eernisse <mde@fleegix.org> + * @author Tiancheng "Timothy" Gu <timothygu99@gmail.com> + * @project EJS + * @license {@link http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0} + */ + +/** + * EJS internal functions. + * + * Technically this "module" lies in the same file as {@link module:ejs}, for + * the sake of organization all the private functions re grouped into this + * module. + * + * @module ejs-internal + * @private + */ + +/** + * Embedded JavaScript templating engine. + * + * @module ejs + * @public + */ + +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']; +// We don't allow 'cache' option to be passed in the data obj for +// the normal `render` call, but this is where Express 2 & 3 put it +// so we make an exception for `renderFile` +var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat('cache'); +var _BOM = /^\uFEFF/; + +/** + * EJS template function cache. This can be a LRU object from lru-cache NPM + * module. By default, it is {@link module:utils.cache}, a simple in-process + * cache that grows continuously. + * + * @type {Cache} + */ + +exports.cache = utils.cache; + +/** + * Custom file loader. Useful for template preprocessing or restricting access + * to a certain part of the filesystem. + * + * @type {fileLoader} + */ + +exports.fileLoader = fs.readFileSync; + +/** + * Name of the object containing the locals. + * + * This variable is overridden by {@link Options}`.localsName` if it is not + * `undefined`. + * + * @type {String} + * @public + */ + +exports.localsName = _DEFAULT_LOCALS_NAME; + +/** + * Promise implementation -- defaults to the native implementation if available + * This is mostly just for testability + * + * @type {Function} + * @public + */ + +exports.promiseImpl = (new Function('return this;'))().Promise; + +/** + * Get the path to the included file from the parent file path and the + * specified path. + * + * @param {String} name specified path + * @param {String} filename parent file path + * @param {Boolean} isDir parent file path whether is directory + * @return {String} + */ +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; +}; + +/** + * Get the path to the included file by Options + * + * @param {String} path specified path + * @param {Options} options compilation options + * @return {String} + */ +function getIncludePath(path, options) { + var includePath; + var filePath; + var views = options.views; + var match = /^[A-Za-z]+:\\|^\//.exec(path); + + // Abs path + if (match && match.length) { + includePath = exports.resolveInclude(path.replace(/^\/*/,''), options.root || '/', true); + } + // Relative paths + else { + // Look relative to a passed filename first + if (options.filename) { + filePath = exports.resolveInclude(path, options.filename); + if (fs.existsSync(filePath)) { + includePath = filePath; + } + } + // Then look in any views directories + 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; +} + +/** + * Get the template from a string or a file, either compiled on-the-fly or + * read from cache (if enabled), and cache the template if needed. + * + * If `template` is not set, the file specified in `options.filename` will be + * read. + * + * If `options.cache` is true, this function reads the file from + * `options.filename` so it must be set prior to calling this function. + * + * @memberof module:ejs-internal + * @param {Options} options compilation options + * @param {String} [template] template source + * @return {(TemplateFunction|ClientFunction)} + * Depending on the value of `options.client`, either type might be returned. + * @static + */ + +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) { + // istanbul ignore if: should not happen at all + 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; +} + +/** + * Try calling handleCache with the given options and data and call the + * callback with the result. If an error occurs, call the callback with + * the error. Used by renderFile(). + * + * @memberof module:ejs-internal + * @param {Options} options compilation options + * @param {Object} data template data + * @param {RenderFileCallback} cb callback + * @static + */ + +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); + } +} + +/** + * fileLoader is independent + * + * @param {String} filePath ejs file path. + * @return {String} The contents of the specified file. + * @static + */ + +function fileLoader(filePath){ + return exports.fileLoader(filePath); +} + +/** + * Get the template function. + * + * If `options.cache` is `true`, then the template is cached. + * + * @memberof module:ejs-internal + * @param {String} path path for the specified file + * @param {Options} options compilation options + * @return {(TemplateFunction|ClientFunction)} + * Depending on the value of `options.client`, either type might be returned + * @static + */ + +function includeFile(path, options) { + var opts = utils.shallowCopy({}, options); + opts.filename = getIncludePath(path, opts); + return handleCache(opts); +} + +/** + * Re-throw the given `err` in context to the `str` of ejs, `filename`, and + * `lineno`. + * + * @implements RethrowCallback + * @memberof module:ejs-internal + * @param {Error} err Error object + * @param {String} str EJS source + * @param {String} filename file name of the EJS file + * @param {String} lineno line number of the error + * @static + */ + +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); // eslint-disable-line + // Error context + var context = lines.slice(start, end).map(function (line, i){ + var curr = i + start + 1; + return (curr == lineno ? ' >> ' : ' ') + + curr + + '| ' + + line; + }).join('\n'); + + // Alter exception message + err.path = filename; + err.message = (filename || 'ejs') + ':' + + lineno + '\n' + + context + '\n\n' + + err.message; + + throw err; +} + +function stripSemi(str){ + return str.replace(/;(\s*$)/, '$1'); +} + +/** + * Compile the given `str` of ejs into a template function. + * + * @param {String} template EJS template + * + * @param {Options} opts compilation options + * + * @return {(TemplateFunction|ClientFunction)} + * Depending on the value of `opts.client`, either type might be returned. + * Note that the return type of the function also depends on the value of `opts.async`. + * @public + */ + +exports.compile = function compile(template, opts) { + var templ; + + // v1 compat + // 'scope' is 'context' + // FIXME: Remove this in a future version + 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(); +}; + +/** + * Render the given `template` of ejs. + * + * If you would like to include options but not data, you need to explicitly + * call this function with `data` being an empty object or `null`. + * + * @param {String} template EJS template + * @param {Object} [data={}] template data + * @param {Options} [opts={}] compilation and rendering options + * @return {(String|Promise<String>)} + * Return value type depends on `opts.async`. + * @public + */ + +exports.render = function (template, d, o) { + var data = d || {}; + var opts = o || {}; + + // No options object -- if there are optiony names + // in the data, copy them to options + if (arguments.length == 2) { + utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA); + } + + return handleCache(opts, template)(data); +}; + +/** + * Render an EJS file at the given `path` and callback `cb(err, str)`. + * + * If you would like to include options but not data, you need to explicitly + * call this function with `data` being an empty object or `null`. + * + * @param {String} path path to the EJS file + * @param {Object} [data={}] template data + * @param {Options} [opts={}] compilation and rendering options + * @param {RenderFileCallback} cb callback + * @public + */ + +exports.renderFile = function () { + var args = Array.prototype.slice.call(arguments); + var filename = args.shift(); + var cb; + var opts = {filename: filename}; + var data; + var viewOpts; + + // Do we have a callback? + if (typeof arguments[arguments.length - 1] == 'function') { + cb = args.pop(); + } + // Do we have data/opts? + if (args.length) { + // Should always have data obj + data = args.shift(); + // Normal passed opts (data obj + opts obj) + if (args.length) { + // Use shallowCopy so we don't pollute passed in opts obj with new vals + utils.shallowCopy(opts, args.pop()); + } + // Special casing for Express (settings + opts-in-data) + else { + // Express 3 and 4 + if (data.settings) { + // Pull a few things from known locations + if (data.settings.views) { + opts.views = data.settings.views; + } + if (data.settings['view cache']) { + opts.cache = true; + } + // Undocumented after Express 2, but still usable, esp. for + // items that are unsafe to be passed along with data, like `root` + viewOpts = data.settings['view options']; + if (viewOpts) { + utils.shallowCopy(opts, viewOpts); + } + } + // Express 2 and lower, values set in app.locals, or people who just + // want to pass options in their data. NOTE: These values will override + // anything previously set in settings or settings['view options'] + utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA_EXPRESS); + } + opts.filename = filename; + } + else { + data = {}; + } + + return tryHandleCache(opts, data, cb); +}; + +/** + * Clear intermediate JavaScript cache. Calls {@link Cache#reset}. + * @public + */ + +/** + * EJS template class + * @public + */ +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) { + // Have to use generated function for this, since in envs without support, + // it breaks in parsing + 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) { + // istanbul ignore else + 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; + } + + // Return a callable function which will execute the function + // created by the source-code, with the passed data as locals + // Adds a local `include` function which allows full recursive include + 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) {/* ignore */} + } + return returnedFn; + }, + + generateSource: function () { + var opts = this.opts; + + if (opts.rmWhitespace) { + // Have to use two separate replace here as `^` and `$` operators don't + // work well with `\r` and empty lines don't work well with the `m` flag. + this.templateText = + this.templateText.replace(/[\r\n]+/g, '\n').replace(/^\s+|\s+$/gm, ''); + } + + // Slurp spaces and tabs before <%_ and after _%> + 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 this is an opening tag, check for closing tags + // FIXME: May end up with some false positives here + // Better to store modes as k/v with openDelimiter + delimiter as key + // Then this can simply check against the map + if ( line.indexOf(o + d) === 0 // If it is a tag + && line.indexOf(o + d + d) !== 0) { // and is not escaped + 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) { + // Only replace single leading linebreak in the line after + // -%> tag -- this is the single, trailing linebreak + // after the tag that the truncation mode replaces + // Handle Win / Unix / old Mac linebreaks -- do the \r\n + // combo first in the regex-or + line = line.replace(/^(?:\r\n|\r|\n)/, ''); + this.truncate = false; + } + if (!line) { + return line; + } + + // Preserve literal slashes + line = line.replace(/\\/g, '\\\\'); + + // Convert linebreaks + line = line.replace(/\n/g, '\\n'); + line = line.replace(/\r/g, '\\r'); + + // Escape double-quotes + // - this will be the delimiter during execution + 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: + // In script mode, depends on type of tag + if (this.mode) { + // If '//' is found without a line break, add a line break. + 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) { + // Just executing code + case Template.modes.EVAL: + this.source += ' ; ' + line + '\n'; + break; + // Exec, esc, and output + case Template.modes.ESCAPED: + this.source += ' ; __append(escapeFn(' + stripSemi(line) + '))' + '\n'; + break; + // Exec and output + case Template.modes.RAW: + this.source += ' ; __append(' + stripSemi(line) + ')' + '\n'; + break; + case Template.modes.COMMENT: + // Do nothing + break; + // Literal <%% mode, append as raw output + case Template.modes.LITERAL: + this._addOutput(line); + break; + } + } + // In string mode, just add the output + else { + this._addOutput(line); + } + } + + if (self.opts.compileDebug && newLineCount) { + this.currentLine += newLineCount; + this.source += ' ; __line = ' + this.currentLine + '\n'; + } + } +}; + +/** + * Escape characters reserved in XML. + * + * This is simply an export of {@link module:utils.escapeXML}. + * + * If `markup` is `undefined` or `null`, the empty string is returned. + * + * @param {String} markup Input string + * @return {String} Escaped string + * @public + * @func + * */ +exports.escapeXML = utils.escapeXML; + +/** + * Express.js support. + * + * This is an alias for {@link module:ejs.renderFile}, in order to support + * Express.js out-of-the-box. + * + * @func + */ + +exports.__express = exports.renderFile; + +/** + * Version of EJS. + * + * @readonly + * @type {String} + * @public + */ + +exports.VERSION = _VERSION_STRING; + +/** + * Name for detection of EJS. + * + * @readonly + * @type {String} + * @public + */ + +exports.name = _NAME; + +/* istanbul ignore if */ +if (typeof window != 'undefined') { + window.ejs = exports; +} + +},{"../package.json":6,"./utils":2,"fs":3,"path":4}],2:[function(require,module,exports){ +/* + * EJS Embedded JavaScript templates + * Copyright 2112 Matthew Eernisse (mde@fleegix.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +/** + * Private utility functions + * @module utils + * @private + */ + +'use strict'; + +var regExpChars = /[|\\{}()[\]^$+*?.]/g; + +/** + * Escape characters reserved in regular expressions. + * + * If `string` is `undefined` or `null`, the empty string is returned. + * + * @param {String} string Input string + * @return {String} Escaped string + * @static + * @private + */ +exports.escapeRegExpChars = function (string) { + // istanbul ignore if + 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; +} + +/** + * Stringified version of constants used by {@link module:utils.escapeXML}. + * + * It is used in the process of generating {@link ClientFunction}s. + * + * @readonly + * @type {String} + */ + +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'; + +/** + * Escape characters reserved in XML. + * + * If `markup` is `undefined` or `null`, the empty string is returned. + * + * @implements {EscapeCallback} + * @param {String} markup Input string + * @return {String} Escaped string + * @static + * @private + */ + +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; +}; + +/** + * Naive copy of properties from one object to another. + * Does not recurse into non-scalar properties + * Does not check to see if the property has a value before copying + * + * @param {Object} to Destination object + * @param {Object} from Source object + * @return {Object} Destination object + * @static + * @private + */ +exports.shallowCopy = function (to, from) { + from = from || {}; + for (var p in from) { + to[p] = from[p]; + } + return to; +}; + +/** + * Naive copy of a list of key names, from one object to another. + * Only copies property if it is actually defined + * Does not recurse into non-scalar properties + * + * @param {Object} to Destination object + * @param {Object} from Source object + * @param {Array} list List of properties to copy + * @return {Object} Destination object + * @static + * @private + */ +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; +}; + +/** + * Simple in-process cache implementation. Does not implement limits of any + * sort. + * + * @implements Cache + * @static + * @private + */ +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){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + 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 the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + +// Split a filename into [root, dir, basename, ext], unix version +// 'root' is just a slash, or nothing. +var splitPathRe = + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; +var splitPath = function(filename) { + return splitPathRe.exec(filename).slice(1); +}; + +// path.resolve([from ...], to) +// posix version +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(); + + // Skip empty and invalid entries + 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) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; +}; + +// path.normalize(path) +// posix version +exports.normalize = function(path) { + var isAbsolute = exports.isAbsolute(path), + trailingSlash = substr(path, -1) === '/'; + + // Normalize the path + path = normalizeArray(filter(path.split('/'), function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; +}; + +// posix version +exports.isAbsolute = function(path) { + return path.charAt(0) === '/'; +}; + +// posix version +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('/')); +}; + + +// path.relative(from, to) +// posix version +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) { + // No dirname whatsoever + return '.'; + } + + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + + return root + dir; +}; + + +exports.basename = function(path, ext) { + var f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + 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; +} + +// String.prototype.substr - negative index don't work in IE8 +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){ +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +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) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // 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 + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // 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. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + 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); + } +}; + +// v8 likes predictible objects +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 = ''; // empty string to avoid regexp issues +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) +}); diff --git a/node_modules/ejs/ejs.min.js b/node_modules/ejs/ejs.min.js @@ -0,0 +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)}); diff --git a/node_modules/ejs/jakefile.js b/node_modules/ejs/jakefile.js @@ -0,0 +1,74 @@ +var fs = require('fs'); +var execSync = require('child_process').execSync; +var exec = function (cmd) { + execSync(cmd, {stdio: 'inherit'}); +}; + +/* global jake, task, desc, publishTask */ + +task('build', ['lint', 'clean', 'browserify', 'minify'], function () { + console.log('Build completed.'); +}); + +desc('Cleans browerified/minified files and package files'); +task('clean', ['clobber'], function () { + jake.rmRf('./ejs.js'); + jake.rmRf('./ejs.min.js'); + console.log('Cleaned up compiled files.'); +}); + +desc('Lints the source code'); +task('lint', ['clean'], function () { + exec('./node_modules/.bin/eslint "**/*.js"'); + console.log('Linting completed.'); +}); + +task('browserify', function () { + exec('./node_modules/browserify/bin/cmd.js --standalone ejs lib/ejs.js > ejs.js'); + console.log('Browserification completed.'); +}); + +task('minify', function () { + exec('./node_modules/uglify-js/bin/uglifyjs ejs.js > ejs.min.js'); + console.log('Minification completed.'); +}); + +desc('Generates the EJS API docs'); +task('doc', function (dev) { + jake.rmRf('out'); + var p = dev ? '-p' : ''; + exec('./node_modules/.bin/jsdoc ' + p + ' -c jsdoc.json lib/* docs/jsdoc/*'); + console.log('Documentation generated.'); +}); + +desc('Publishes the EJS API docs'); +task('docPublish', ['doc'], function () { + fs.writeFileSync('out/CNAME', 'api.ejs.co'); + console.log('Pushing docs to gh-pages...'); + exec('./node_modules/.bin/git-directory-deploy --directory out/'); + console.log('Docs published to gh-pages.'); +}); + +desc('Runs the EJS test suite'); +task('test', ['lint'], function () { + exec('./node_modules/.bin/mocha'); +}); + +publishTask('ejs', ['build'], function () { + this.packageFiles.include([ + 'jakefile.js', + 'README.md', + 'LICENSE', + 'package.json', + 'postinstall.js', + 'ejs.js', + 'ejs.min.js', + 'lib/**' + ]); +}); + +jake.Task.publish.on('complete', function () { + console.log('Updating hosted docs...'); + console.log('If this fails, run jake docPublish to re-try.'); + jake.Task.docPublish.invoke(); +}); diff --git a/node_modules/ejs/lib/ejs.js b/node_modules/ejs/lib/ejs.js @@ -0,0 +1,904 @@ +/* + * EJS Embedded JavaScript templates + * Copyright 2112 Matthew Eernisse (mde@fleegix.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +'use strict'; + +/** + * @file Embedded JavaScript templating engine. {@link http://ejs.co} + * @author Matthew Eernisse <mde@fleegix.org> + * @author Tiancheng "Timothy" Gu <timothygu99@gmail.com> + * @project EJS + * @license {@link http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0} + */ + +/** + * EJS internal functions. + * + * Technically this "module" lies in the same file as {@link module:ejs}, for + * the sake of organization all the private functions re grouped into this + * module. + * + * @module ejs-internal + * @private + */ + +/** + * Embedded JavaScript templating engine. + * + * @module ejs + * @public + */ + +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']; +// We don't allow 'cache' option to be passed in the data obj for +// the normal `render` call, but this is where Express 2 & 3 put it +// so we make an exception for `renderFile` +var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat('cache'); +var _BOM = /^\uFEFF/; + +/** + * EJS template function cache. This can be a LRU object from lru-cache NPM + * module. By default, it is {@link module:utils.cache}, a simple in-process + * cache that grows continuously. + * + * @type {Cache} + */ + +exports.cache = utils.cache; + +/** + * Custom file loader. Useful for template preprocessing or restricting access + * to a certain part of the filesystem. + * + * @type {fileLoader} + */ + +exports.fileLoader = fs.readFileSync; + +/** + * Name of the object containing the locals. + * + * This variable is overridden by {@link Options}`.localsName` if it is not + * `undefined`. + * + * @type {String} + * @public + */ + +exports.localsName = _DEFAULT_LOCALS_NAME; + +/** + * Promise implementation -- defaults to the native implementation if available + * This is mostly just for testability + * + * @type {Function} + * @public + */ + +exports.promiseImpl = (new Function('return this;'))().Promise; + +/** + * Get the path to the included file from the parent file path and the + * specified path. + * + * @param {String} name specified path + * @param {String} filename parent file path + * @param {Boolean} isDir parent file path whether is directory + * @return {String} + */ +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; +}; + +/** + * Get the path to the included file by Options + * + * @param {String} path specified path + * @param {Options} options compilation options + * @return {String} + */ +function getIncludePath(path, options) { + var includePath; + var filePath; + var views = options.views; + var match = /^[A-Za-z]+:\\|^\//.exec(path); + + // Abs path + if (match && match.length) { + includePath = exports.resolveInclude(path.replace(/^\/*/,''), options.root || '/', true); + } + // Relative paths + else { + // Look relative to a passed filename first + if (options.filename) { + filePath = exports.resolveInclude(path, options.filename); + if (fs.existsSync(filePath)) { + includePath = filePath; + } + } + // Then look in any views directories + 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; +} + +/** + * Get the template from a string or a file, either compiled on-the-fly or + * read from cache (if enabled), and cache the template if needed. + * + * If `template` is not set, the file specified in `options.filename` will be + * read. + * + * If `options.cache` is true, this function reads the file from + * `options.filename` so it must be set prior to calling this function. + * + * @memberof module:ejs-internal + * @param {Options} options compilation options + * @param {String} [template] template source + * @return {(TemplateFunction|ClientFunction)} + * Depending on the value of `options.client`, either type might be returned. + * @static + */ + +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) { + // istanbul ignore if: should not happen at all + 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; +} + +/** + * Try calling handleCache with the given options and data and call the + * callback with the result. If an error occurs, call the callback with + * the error. Used by renderFile(). + * + * @memberof module:ejs-internal + * @param {Options} options compilation options + * @param {Object} data template data + * @param {RenderFileCallback} cb callback + * @static + */ + +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); + } +} + +/** + * fileLoader is independent + * + * @param {String} filePath ejs file path. + * @return {String} The contents of the specified file. + * @static + */ + +function fileLoader(filePath){ + return exports.fileLoader(filePath); +} + +/** + * Get the template function. + * + * If `options.cache` is `true`, then the template is cached. + * + * @memberof module:ejs-internal + * @param {String} path path for the specified file + * @param {Options} options compilation options + * @return {(TemplateFunction|ClientFunction)} + * Depending on the value of `options.client`, either type might be returned + * @static + */ + +function includeFile(path, options) { + var opts = utils.shallowCopy({}, options); + opts.filename = getIncludePath(path, opts); + return handleCache(opts); +} + +/** + * Re-throw the given `err` in context to the `str` of ejs, `filename`, and + * `lineno`. + * + * @implements RethrowCallback + * @memberof module:ejs-internal + * @param {Error} err Error object + * @param {String} str EJS source + * @param {String} filename file name of the EJS file + * @param {String} lineno line number of the error + * @static + */ + +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); // eslint-disable-line + // Error context + var context = lines.slice(start, end).map(function (line, i){ + var curr = i + start + 1; + return (curr == lineno ? ' >> ' : ' ') + + curr + + '| ' + + line; + }).join('\n'); + + // Alter exception message + err.path = filename; + err.message = (filename || 'ejs') + ':' + + lineno + '\n' + + context + '\n\n' + + err.message; + + throw err; +} + +function stripSemi(str){ + return str.replace(/;(\s*$)/, '$1'); +} + +/** + * Compile the given `str` of ejs into a template function. + * + * @param {String} template EJS template + * + * @param {Options} opts compilation options + * + * @return {(TemplateFunction|ClientFunction)} + * Depending on the value of `opts.client`, either type might be returned. + * Note that the return type of the function also depends on the value of `opts.async`. + * @public + */ + +exports.compile = function compile(template, opts) { + var templ; + + // v1 compat + // 'scope' is 'context' + // FIXME: Remove this in a future version + 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(); +}; + +/** + * Render the given `template` of ejs. + * + * If you would like to include options but not data, you need to explicitly + * call this function with `data` being an empty object or `null`. + * + * @param {String} template EJS template + * @param {Object} [data={}] template data + * @param {Options} [opts={}] compilation and rendering options + * @return {(String|Promise<String>)} + * Return value type depends on `opts.async`. + * @public + */ + +exports.render = function (template, d, o) { + var data = d || {}; + var opts = o || {}; + + // No options object -- if there are optiony names + // in the data, copy them to options + if (arguments.length == 2) { + utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA); + } + + return handleCache(opts, template)(data); +}; + +/** + * Render an EJS file at the given `path` and callback `cb(err, str)`. + * + * If you would like to include options but not data, you need to explicitly + * call this function with `data` being an empty object or `null`. + * + * @param {String} path path to the EJS file + * @param {Object} [data={}] template data + * @param {Options} [opts={}] compilation and rendering options + * @param {RenderFileCallback} cb callback + * @public + */ + +exports.renderFile = function () { + var args = Array.prototype.slice.call(arguments); + var filename = args.shift(); + var cb; + var opts = {filename: filename}; + var data; + var viewOpts; + + // Do we have a callback? + if (typeof arguments[arguments.length - 1] == 'function') { + cb = args.pop(); + } + // Do we have data/opts? + if (args.length) { + // Should always have data obj + data = args.shift(); + // Normal passed opts (data obj + opts obj) + if (args.length) { + // Use shallowCopy so we don't pollute passed in opts obj with new vals + utils.shallowCopy(opts, args.pop()); + } + // Special casing for Express (settings + opts-in-data) + else { + // Express 3 and 4 + if (data.settings) { + // Pull a few things from known locations + if (data.settings.views) { + opts.views = data.settings.views; + } + if (data.settings['view cache']) { + opts.cache = true; + } + // Undocumented after Express 2, but still usable, esp. for + // items that are unsafe to be passed along with data, like `root` + viewOpts = data.settings['view options']; + if (viewOpts) { + utils.shallowCopy(opts, viewOpts); + } + } + // Express 2 and lower, values set in app.locals, or people who just + // want to pass options in their data. NOTE: These values will override + // anything previously set in settings or settings['view options'] + utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA_EXPRESS); + } + opts.filename = filename; + } + else { + data = {}; + } + + return tryHandleCache(opts, data, cb); +}; + +/** + * Clear intermediate JavaScript cache. Calls {@link Cache#reset}. + * @public + */ + +/** + * EJS template class + * @public + */ +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) { + // Have to use generated function for this, since in envs without support, + // it breaks in parsing + 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) { + // istanbul ignore else + 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; + } + + // Return a callable function which will execute the function + // created by the source-code, with the passed data as locals + // Adds a local `include` function which allows full recursive include + 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) {/* ignore */} + } + return returnedFn; + }, + + generateSource: function () { + var opts = this.opts; + + if (opts.rmWhitespace) { + // Have to use two separate replace here as `^` and `$` operators don't + // work well with `\r` and empty lines don't work well with the `m` flag. + this.templateText = + this.templateText.replace(/[\r\n]+/g, '\n').replace(/^\s+|\s+$/gm, ''); + } + + // Slurp spaces and tabs before <%_ and after _%> + 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 this is an opening tag, check for closing tags + // FIXME: May end up with some false positives here + // Better to store modes as k/v with openDelimiter + delimiter as key + // Then this can simply check against the map + if ( line.indexOf(o + d) === 0 // If it is a tag + && line.indexOf(o + d + d) !== 0) { // and is not escaped + 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) { + // Only replace single leading linebreak in the line after + // -%> tag -- this is the single, trailing linebreak + // after the tag that the truncation mode replaces + // Handle Win / Unix / old Mac linebreaks -- do the \r\n + // combo first in the regex-or + line = line.replace(/^(?:\r\n|\r|\n)/, ''); + this.truncate = false; + } + if (!line) { + return line; + } + + // Preserve literal slashes + line = line.replace(/\\/g, '\\\\'); + + // Convert linebreaks + line = line.replace(/\n/g, '\\n'); + line = line.replace(/\r/g, '\\r'); + + // Escape double-quotes + // - this will be the delimiter during execution + 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: + // In script mode, depends on type of tag + if (this.mode) { + // If '//' is found without a line break, add a line break. + 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) { + // Just executing code + case Template.modes.EVAL: + this.source += ' ; ' + line + '\n'; + break; + // Exec, esc, and output + case Template.modes.ESCAPED: + this.source += ' ; __append(escapeFn(' + stripSemi(line) + '))' + '\n'; + break; + // Exec and output + case Template.modes.RAW: + this.source += ' ; __append(' + stripSemi(line) + ')' + '\n'; + break; + case Template.modes.COMMENT: + // Do nothing + break; + // Literal <%% mode, append as raw output + case Template.modes.LITERAL: + this._addOutput(line); + break; + } + } + // In string mode, just add the output + else { + this._addOutput(line); + } + } + + if (self.opts.compileDebug && newLineCount) { + this.currentLine += newLineCount; + this.source += ' ; __line = ' + this.currentLine + '\n'; + } + } +}; + +/** + * Escape characters reserved in XML. + * + * This is simply an export of {@link module:utils.escapeXML}. + * + * If `markup` is `undefined` or `null`, the empty string is returned. + * + * @param {String} markup Input string + * @return {String} Escaped string + * @public + * @func + * */ +exports.escapeXML = utils.escapeXML; + +/** + * Express.js support. + * + * This is an alias for {@link module:ejs.renderFile}, in order to support + * Express.js out-of-the-box. + * + * @func + */ + +exports.__express = exports.renderFile; + +/** + * Version of EJS. + * + * @readonly + * @type {String} + * @public + */ + +exports.VERSION = _VERSION_STRING; + +/** + * Name for detection of EJS. + * + * @readonly + * @type {String} + * @public + */ + +exports.name = _NAME; + +/* istanbul ignore if */ +if (typeof window != 'undefined') { + window.ejs = exports; +} diff --git a/node_modules/ejs/lib/utils.js b/node_modules/ejs/lib/utils.js @@ -0,0 +1,167 @@ +/* + * EJS Embedded JavaScript templates + * Copyright 2112 Matthew Eernisse (mde@fleegix.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +/** + * Private utility functions + * @module utils + * @private + */ + +'use strict'; + +var regExpChars = /[|\\{}()[\]^$+*?.]/g; + +/** + * Escape characters reserved in regular expressions. + * + * If `string` is `undefined` or `null`, the empty string is returned. + * + * @param {String} string Input string + * @return {String} Escaped string + * @static + * @private + */ +exports.escapeRegExpChars = function (string) { + // istanbul ignore if + 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; +} + +/** + * Stringified version of constants used by {@link module:utils.escapeXML}. + * + * It is used in the process of generating {@link ClientFunction}s. + * + * @readonly + * @type {String} + */ + +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'; + +/** + * Escape characters reserved in XML. + * + * If `markup` is `undefined` or `null`, the empty string is returned. + * + * @implements {EscapeCallback} + * @param {String} markup Input string + * @return {String} Escaped string + * @static + * @private + */ + +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; +}; + +/** + * Naive copy of properties from one object to another. + * Does not recurse into non-scalar properties + * Does not check to see if the property has a value before copying + * + * @param {Object} to Destination object + * @param {Object} from Source object + * @return {Object} Destination object + * @static + * @private + */ +exports.shallowCopy = function (to, from) { + from = from || {}; + for (var p in from) { + to[p] = from[p]; + } + return to; +}; + +/** + * Naive copy of a list of key names, from one object to another. + * Only copies property if it is actually defined + * Does not recurse into non-scalar properties + * + * @param {Object} to Destination object + * @param {Object} from Source object + * @param {Array} list List of properties to copy + * @return {Object} Destination object + * @static + * @private + */ +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; +}; + +/** + * Simple in-process cache implementation. Does not implement limits of any + * sort. + * + * @implements Cache + * @static + * @private + */ +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 = {}; + } +}; diff --git a/node_modules/ejs/package.json b/node_modules/ejs/package.json @@ -0,0 +1,100 @@ +{ + "_args": [ + [ + "ejs", + "/home/hide4/Documents/Javascript/TwitWebGraph" + ] + ], + "_from": "ejs@latest", + "_hasShrinkwrap": false, + "_id": "ejs@3.0.1", + "_inCache": true, + "_installable": true, + "_integrity": "", + "_location": "/ejs", + "_nodeVersion": "10.13.0", + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages", + "tmp": "tmp/ejs_3.0.1_1574559796681_0.46243095998800254" + }, + "_npmUser": { + "email": "mde@fleegix.org", + "name": "mde" + }, + "_npmVersion": "6.4.1", + "_phantomChildren": {}, + "_requested": { + "name": "ejs", + "raw": "ejs", + "rawSpec": "", + "scope": null, + "spec": "latest", + "type": "tag" + }, + "_requiredBy": [ + "#USER" + ], + "_resolved": "https://registry.npmjs.org/ejs/-/ejs-3.0.1.tgz", + "_shasum": "30c8f6ee9948502cc32e85c37a3f8b39b5a614a5", + "_shrinkwrap": null, + "_spec": "ejs", + "_where": "/home/hide4/Documents/Javascript/TwitWebGraph", + "author": { + "email": "mde@fleegix.org", + "name": "Matthew Eernisse", + "url": "http://fleegix.org" + }, + "bugs": { + "url": "https://github.com/mde/ejs/issues" + }, + "dependencies": {}, + "description": "Embedded JavaScript templates", + "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" + }, + "directories": {}, + "dist": { + "fileCount": 11, + "integrity": "sha512-cuIMtJwxvzumSAkqaaoGY/L6Fc/t6YvoP9/VIaK0V/CyqKLEQ8sqODmYfy/cjXEdZ9+OOL8TecbJu+1RsofGDw==", + "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd2eA1CRA9TVsSAnZWagAA/1wQAJ9SNyz7P4SeeobpVsTQ\nk8KbX+AF7F7VY7HXwcTXg0pFGLkzQpEREuGoEF2V8J0nxFi6aUKC77lWQ5ld\n2kID5Xx55wZ9OR998NYwkCqQXnrXGtXS6NrKjRZtfIQZ7onI4JCcHWXX42SY\nXOLJUcOEk89xlONrDWpdtS9RQieKubc5IOFEqiEhqBNoJT3mY50IEk0jdzTh\nikrCrkXsEyop+zLbfB9YW5DBavqBgCVkgBeikhVrYo5SxZo7apCbnv7t13ER\nA8llnogbzoTxHzDGDy4N2V1yTwOb9tomzQGs/GuELClTGoyjT5NaxMUk/Bj4\nK2Xn8KToiEEdjjcqjK5x0hnK9agFilXF6+Ts9JD2aO2f+rONdP5Xr82Wj6TG\nq5f8sWV44biBvDeCfS/0BuySWIdim388seVaSJ9DpfAY3rns+p8vzj1GIa1P\nAk2L664Ix2hU3GtphwX9lTGKizVSa68qVVFQA703Uobn9bh+4yiRo94Q5tLG\nVrT0JHctR+ud2tE5EP5Gy5KYMxhACD1siOFEy+8ky0fgbsF8KhphzAGhHMls\nD+0xGvGKfxAzYOSlnez2At+qT+l8rQLH0od9rNHrOejvm0NT4dQLlnhFj0v5\nktlcvjsbPsMFAJmyk9QplCF/yURqnw/8i6BGW2xBr/aBdRcAxNTfrCV3LpNS\nhyMJ\r\n=Ei0O\r\n-----END PGP SIGNATURE-----\r\n", + "shasum": "30c8f6ee9948502cc32e85c37a3f8b39b5a614a5", + "tarball": "https://registry.npmjs.org/ejs/-/ejs-3.0.1.tgz", + "unpackedSize": 118237 + }, + "engines": { + "node": ">=0.10.0" + }, + "homepage": "https://github.com/mde/ejs", + "keywords": [ + "ejs", + "engine", + "template" + ], + "license": "Apache-2.0", + "main": "./lib/ejs.js", + "maintainers": [ + { + "name": "mde", + "email": "mde@fleegix.org" + } + ], + "name": "ejs", + "optionalDependencies": {}, + "readme": "ERROR: No README data found!", + "repository": { + "type": "git", + "url": "git://github.com/mde/ejs.git" + }, + "scripts": { + "postinstall": "node ./postinstall.js", + "test": "mocha" + }, + "version": "3.0.1" +} diff --git a/node_modules/ejs/postinstall.js b/node_modules/ejs/postinstall.js @@ -0,0 +1,17 @@ +#!/usr/bin/env node + +'use strict'; + +function isTrue(value) { + return !!value && value !== '0' && value !== 'false'; +} + +let envDisable = isTrue(process.env.DISABLE_OPENCOLLECTIVE) || isTrue(process.env.CI); +let logLevel = process.env.npm_config_loglevel; +let logLevelDisplay = ['silent', 'error', 'warn'].indexOf(logLevel) > -1; + +if (!envDisable && !logLevelDisplay) { + 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'); +} + + diff --git a/node_modules/express/package.json b/node_modules/express/package.json @@ -24,17 +24,17 @@ "tmp": "tmp/express_4.17.1_1558844734329_0.21547943776855627" }, "_npmUser": { - "name": "dougwilson", - "email": "doug@somethingdoug.com" + "email": "doug@somethingdoug.com", + "name": "dougwilson" }, "_npmVersion": "6.4.1", "_phantomChildren": {}, "_requested": { - "raw": "express@^4.17.1", - "scope": null, "escapedName": "express", "name": "express", + "raw": "express@^4.17.1", "rawSpec": "^4.17.1", + "scope": null, "spec": ">=4.17.1 <5.0.0", "type": "range" }, @@ -48,8 +48,8 @@ "_spec": "express@^4.17.1", "_where": "/home/hide4/Documents/Javascript/TwitWebGraph", "author": { - "name": "TJ Holowaychuk", - "email": "tj@vision-media.ca" + "email": "tj@vision-media.ca", + "name": "TJ Holowaychuk" }, "bugs": { "url": "https://github.com/expressjs/express/issues" @@ -139,12 +139,12 @@ }, "directories": {}, "dist": { + "fileCount": 16, "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc6hU/CRA9TVsSAnZWagAAFc4QAJzqxI1sgdfreUHk+NIa\n38jYea65Xg8N9JgZVF67j7aXqPT6VXhGu2j54oveGIkr+RL2Xm58RrRWn+Sg\nVWOOSZzotLKtx1qCYS4ozPRYvujKMLYDeiLxePDCSrrLYt48+IJwkHF04Un1\nJ0ZUmtlEqgLL85gvaCrKa9qF8TfwbQhhIzQ914vum11tJ506ePpffN2xFY0M\nsHf0CiuV1OFOD7Wne/RR7DVsxQwZ/FXomkxLJm8+T+T9ZYm3WQxWVD7BRQpA\nN08+zkPd1XMEZiVZkR9Ie4+7ydZomJE8PNCOt5SzvEW6ekDW10QuuF0521Wj\n5lHp4AflVFq1LTJB0WDR6VIPJRp0H5aYTh1tBRxWHUx/EP2LfFS/XEz1bUvm\nBDVj2e1iA4ZWz8aeu9p/2N8Zp05WGINF3/E4YG9smxxs5EDJZGA9k1DAj6US\nzKWTOemaqypRshFWThvfA70a1Rcwdj+0XGboscg/S20XTT0FvG2GLkEY0OO/\niHBy5fKYplUQsths48V8I9P9Gx6U534iaFJlxlzzVEsDleBkH+NBSP8OB7dx\n8N/0ZQDBY6JWL5ZSW9yVY2FzrTEmUOPC1Rts5Uj4m7SBmu8yK154ylnPQ4T6\nMr0jG8XQPYhTLc5pYNTFZNV1Ydu4d01xIrLhGy/3dc7kRlwy3FN5ceNVsB88\njyN+\r\n=QYw2\r\n-----END PGP SIGNATURE-----\r\n", "shasum": "4491fc38605cf51f8629d39c2b5d026f98a4c134", "tarball": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "fileCount": 16, - "unpackedSize": 208133, - "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc6hU/CRA9TVsSAnZWagAAFc4QAJzqxI1sgdfreUHk+NIa\n38jYea65Xg8N9JgZVF67j7aXqPT6VXhGu2j54oveGIkr+RL2Xm58RrRWn+Sg\nVWOOSZzotLKtx1qCYS4ozPRYvujKMLYDeiLxePDCSrrLYt48+IJwkHF04Un1\nJ0ZUmtlEqgLL85gvaCrKa9qF8TfwbQhhIzQ914vum11tJ506ePpffN2xFY0M\nsHf0CiuV1OFOD7Wne/RR7DVsxQwZ/FXomkxLJm8+T+T9ZYm3WQxWVD7BRQpA\nN08+zkPd1XMEZiVZkR9Ie4+7ydZomJE8PNCOt5SzvEW6ekDW10QuuF0521Wj\n5lHp4AflVFq1LTJB0WDR6VIPJRp0H5aYTh1tBRxWHUx/EP2LfFS/XEz1bUvm\nBDVj2e1iA4ZWz8aeu9p/2N8Zp05WGINF3/E4YG9smxxs5EDJZGA9k1DAj6US\nzKWTOemaqypRshFWThvfA70a1Rcwdj+0XGboscg/S20XTT0FvG2GLkEY0OO/\niHBy5fKYplUQsths48V8I9P9Gx6U534iaFJlxlzzVEsDleBkH+NBSP8OB7dx\n8N/0ZQDBY6JWL5ZSW9yVY2FzrTEmUOPC1Rts5Uj4m7SBmu8yK154ylnPQ4T6\nMr0jG8XQPYhTLc5pYNTFZNV1Ydu4d01xIrLhGy/3dc7kRlwy3FN5ceNVsB88\njyN+\r\n=QYw2\r\n-----END PGP SIGNATURE-----\r\n" + "unpackedSize": 208133 }, "engines": { "node": ">= 0.10.0" @@ -152,15 +152,15 @@ "gitHead": "e1b45ebd050b6f06aa38cda5aaf0c21708b0c71e", "homepage": "http://expressjs.com/", "keywords": [ + "api", + "app", "express", "framework", - "sinatra", - "web", "rest", "restful", "router", - "app", - "api" + "sinatra", + "web" ], "license": "MIT", "maintainers": [ diff --git a/node_modules/node-fetch/package.json b/node_modules/node-fetch/package.json @@ -24,17 +24,17 @@ "tmp": "tmp/node-fetch_2.6.0_1557988780767_0.1012615293164354" }, "_npmUser": { - "name": "bitinn", - "email": "bitinn@gmail.com" + "email": "bitinn@gmail.com", + "name": "bitinn" }, "_npmVersion": "6.9.0", "_phantomChildren": {}, "_requested": { - "raw": "node-fetch", - "scope": null, "escapedName": "node-fetch", "name": "node-fetch", + "raw": "node-fetch", "rawSpec": "", + "scope": null, "spec": "latest", "type": "tag" }, @@ -83,12 +83,12 @@ }, "directories": {}, "dist": { + "fileCount": 8, "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc3QWtCRA9TVsSAnZWagAAIT4P/3aM/33tMjOx9vllJ/6G\nZOweEzEiRN9ZurrhD9phAVj0QV0iC5BLo+IvZMvLylIzwHQGFZT5P4ZCMucC\nUxNRR/Ckt55de+nG3A0wQ5JNApxHjuVYTBkg70+LbkRKYlFnsAHNH+b62kIG\nWETJV79q/lqryuqw0RjkOhjYm9p2162ThE9kp61YsGopD1a6WuWZD0iDyJTz\nlvK2bJztclYoyXNr4WkNu8QMm6XZVV++eX1xOLbQHeFBnqlstHDYaK8dgzPb\nGOmXjSRs1GvArlemBMHrVr4JlsSoz6lfNoiZ2j2kkkpAKwBvMkx7rikcDIle\n215JLJGySxdrehbTPdxi+A7tVAVK3F2X5n8PENYl7JVTHoTz86LMv0reRlvr\ndCBghHaEpenfR/FhV1z8r59D44hzPRODhaDtOVuDMEpIYIMHmhcGZivP9VHB\ny+uwy4YYYEzaOw0x4N7tPPMnUT9dKx/kGMEpS4XiwVCHrGGMeq+ntHEm03fD\nZ42i5rMjEwlBgRjnTwe4gb/8yGmT8y1hlyeZYpB2jZw+By5WL3vm5TBbaVHJ\n2TgPFJye9/YU/iaDuSAmEi1Fci+tNuOWOoqPWBWywD5paXoe87rQyjVrag4A\nEBdw4cbUFu3U6kWRcNxTmHT6Ba+T8LTQ68UvbI1jfXjVPepHbSSLtPetxvOQ\nEhAm\r\n=275P\r\n-----END PGP SIGNATURE-----\r\n", "shasum": "e633456386d4aa55863f676a7ab0daa8fdecb0fd", "tarball": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "fileCount": 8, - "unpackedSize": 156222, - "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc3QWtCRA9TVsSAnZWagAAIT4P/3aM/33tMjOx9vllJ/6G\nZOweEzEiRN9ZurrhD9phAVj0QV0iC5BLo+IvZMvLylIzwHQGFZT5P4ZCMucC\nUxNRR/Ckt55de+nG3A0wQ5JNApxHjuVYTBkg70+LbkRKYlFnsAHNH+b62kIG\nWETJV79q/lqryuqw0RjkOhjYm9p2162ThE9kp61YsGopD1a6WuWZD0iDyJTz\nlvK2bJztclYoyXNr4WkNu8QMm6XZVV++eX1xOLbQHeFBnqlstHDYaK8dgzPb\nGOmXjSRs1GvArlemBMHrVr4JlsSoz6lfNoiZ2j2kkkpAKwBvMkx7rikcDIle\n215JLJGySxdrehbTPdxi+A7tVAVK3F2X5n8PENYl7JVTHoTz86LMv0reRlvr\ndCBghHaEpenfR/FhV1z8r59D44hzPRODhaDtOVuDMEpIYIMHmhcGZivP9VHB\ny+uwy4YYYEzaOw0x4N7tPPMnUT9dKx/kGMEpS4XiwVCHrGGMeq+ntHEm03fD\nZ42i5rMjEwlBgRjnTwe4gb/8yGmT8y1hlyeZYpB2jZw+By5WL3vm5TBbaVHJ\n2TgPFJye9/YU/iaDuSAmEi1Fci+tNuOWOoqPWBWywD5paXoe87rQyjVrag4A\nEBdw4cbUFu3U6kWRcNxTmHT6Ba+T8LTQ68UvbI1jfXjVPepHbSSLtPetxvOQ\nEhAm\r\n=275P\r\n-----END PGP SIGNATURE-----\r\n" + "unpackedSize": 156222 }, "engines": { "node": "4.x || >=6.0.0" diff --git a/node_modules/twit/package.json b/node_modules/twit/package.json @@ -23,17 +23,17 @@ "tmp": "tmp/twit_2.2.11_1530333399450_0.2265699554320495" }, "_npmUser": { - "name": "ttezel", - "email": "tolgatezel11@gmail.com" + "email": "tolgatezel11@gmail.com", + "name": "ttezel" }, "_npmVersion": "5.6.0", "_phantomChildren": {}, "_requested": { - "raw": "twit", - "scope": null, "escapedName": "twit", "name": "twit", + "raw": "twit", "rawSpec": "", + "scope": null, "spec": "latest", "type": "tag" }, @@ -67,12 +67,12 @@ }, "directories": {}, "dist": { + "fileCount": 25, "integrity": "sha512-BkdwvZGRVoUTcEBp0zuocuqfih4LB+kEFUWkWJOVBg6pAE9Ebv9vmsYTTrfXleZGf45Bj5H3A1/O9YhF2uSYNg==", + "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNwjXCRA9TVsSAnZWagAAGFUP/A7dV7XHqDrNh4Z3bn4V\njaYfPZwOw1lDcawn78XbGF8pbvfndLUldZL/UEM7S6bvIx5dqHwpgfHjdnhX\nmp32ecw2NZa9ZLGHmrsn4pMKELw0DK2RAXvPPQXUQKCtMCo1/XkDUlIZ+jEv\nTov3eVzQyQbg4NVcI/4rUE19umxagnbsgkNvVtKFQz7ysnaqXAPA+nvb/Dgd\n4DqLKPjveRHpjz5EchsokkUSeve3BPDF3kpg9SGfQFBMJENBH13MM8vxftV4\njudikLESw6jncMWp0UfpYCjNMlR510sNDA7EzgkujhRVnfP4srpGHj/6vS6G\nwRLy0fvu9YJotIvIKQqkWacV2WuYb18SUCy8VkdwODdKZBqU/JXmPdiLQNMe\nXYPzvCXg6lGbix+ZnyR7FyJMjGg1Mh9RjvbbdJWW6HANX3cZEvK4h2GKkU+F\nP5xkqIS00sWvLqbU1H8kMRB4sYYTzNGScCRMU6c1KMaR5sPWlkMCuV7QxZvf\nqzlE7Yt40GcHMlZAQ3IEC55NQRnt7iUxXzpCFQaOCsm5QdGEjrwg+FPk5+bt\n5EnMrYHlOtTvvVD5yRCOVmj1HmRSEHIkrEgtEmsfgh/kvEmzB/YUquO2jXG1\nAIawHPB4viea4FwN67nBg5Q8tvAIiH1i0q3zxVsnjqCakHCIpvD22bNlc48e\naCg2\r\n=K+5A\r\n-----END PGP SIGNATURE-----\r\n", "shasum": "554343d1cf343ddf503280db821f61be5ab407c3", "tarball": "https://registry.npmjs.org/twit/-/twit-2.2.11.tgz", - "fileCount": 25, - "unpackedSize": 641073, - "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNwjXCRA9TVsSAnZWagAAGFUP/A7dV7XHqDrNh4Z3bn4V\njaYfPZwOw1lDcawn78XbGF8pbvfndLUldZL/UEM7S6bvIx5dqHwpgfHjdnhX\nmp32ecw2NZa9ZLGHmrsn4pMKELw0DK2RAXvPPQXUQKCtMCo1/XkDUlIZ+jEv\nTov3eVzQyQbg4NVcI/4rUE19umxagnbsgkNvVtKFQz7ysnaqXAPA+nvb/Dgd\n4DqLKPjveRHpjz5EchsokkUSeve3BPDF3kpg9SGfQFBMJENBH13MM8vxftV4\njudikLESw6jncMWp0UfpYCjNMlR510sNDA7EzgkujhRVnfP4srpGHj/6vS6G\nwRLy0fvu9YJotIvIKQqkWacV2WuYb18SUCy8VkdwODdKZBqU/JXmPdiLQNMe\nXYPzvCXg6lGbix+ZnyR7FyJMjGg1Mh9RjvbbdJWW6HANX3cZEvK4h2GKkU+F\nP5xkqIS00sWvLqbU1H8kMRB4sYYTzNGScCRMU6c1KMaR5sPWlkMCuV7QxZvf\nqzlE7Yt40GcHMlZAQ3IEC55NQRnt7iUxXzpCFQaOCsm5QdGEjrwg+FPk5+bt\n5EnMrYHlOtTvvVD5yRCOVmj1HmRSEHIkrEgtEmsfgh/kvEmzB/YUquO2jXG1\nAIawHPB4viea4FwN67nBg5Q8tvAIiH1i0q3zxVsnjqCakHCIpvD22bNlc48e\naCg2\r\n=K+5A\r\n-----END PGP SIGNATURE-----\r\n" + "unpackedSize": 641073 }, "engines": { "node": ">=0.10.0" @@ -80,12 +80,12 @@ "gitHead": "8eec3c672bba825c736ba93b74a445accd40f982", "homepage": "https://github.com/ttezel/twit#readme", "keywords": [ - "twitter", "api", + "oauth", "rest", "stream", "streaming", - "oauth" + "twitter" ], "license": "MIT", "main": "./lib/twitter", diff --git a/public/js/fetch.js b/public/js/fetch.js @@ -5,7 +5,7 @@ var fetch = require('node-fetch'); async function test(){ var req = await fetch("https://api.twitter.com/1.1/followers/list.json?cursor=-1&screen_name=Greatt_DaNe1&skip_status=true&include_user_entities=false", { headers: { - Authorization: "Bearer AAAAAAAAAAAAAAAAAAAAAGLfBQEAAAAAVeDxlZsqNtyYsN4qMmfrrKBDbUM%3DisXeBus50iY7LzBBJBiNXJpyllGkEaVnRNMdLJTvtdHX7MLxQ0", + Authorization: "Bearer ", //Access-Control-Allow-Origin: http://www.underd0g.co } }); diff --git a/views/index.ejs b/views/index.ejs @@ -0,0 +1,92 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + + <title>TweetWebGraph</title> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel = 'stylesheet' href = '/css/stylesheet.css'> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"> + <link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet"> + <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> + <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script> + <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular.min.js"></script> + + +</head> +<body> + +<!-- Navbar --> +<nav class="navbar navbar-default"> + <div class="container"> + <div class="navbar-header"> + <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar"> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="navbar-brand" href="#">< TweetWebGraph /> </a> + </div> + <div class="collapse navbar-collapse" id="myNavbar"> + <ul class="nav navbar-nav navbar-right"> + <li><a href="#">WHOIS</a></li> + <li><a href="#">./run</a></li> + <li><a href="#">HOWTO</a></li> + </ul> + </div> + </div> +</nav> + +<!-- First Container --> +<div class="container-fluid bg-1 text-center"> + <h3 class="margin">Whois?</h3> + <img src="/images/dead-smiley-face-icon_17015.png" class="img-responsive" style="display:inline" alt="smile" width="350" height="350"> + <h3>Just Another WebApp To Show Your Twitter Stats...</h3><p><%= name%></p> +</div> + +<!-- Second Container --> +<!-- Inject some JS to add the api connection and user functionality --> +<div class="container-fluid bg-2 text-center"> + + <h3 class="margin">./Run</h3> + <p>Enter in the username you would like to get a list of followers for. (example: twitterdevteam)</p> + + +<input type="text" id="usernametxt" required> +<button>fetch</button> +</div> + +<!-- Third Container (Grid) --> +<div class="container-fluid bg-3 text-center"> + <h3 class="margin">HOWTO</h3><br> + <div class="row"> + <div class="col-sm-4"> + <p>Check out the project on Github!</p> + <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a> + </div> + <div class="col-sm-4"> + <p>References</p> + <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> + <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> + <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> + <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> + </div> + <div class="col-sm-4"> + <p>Friends</p> + <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> + <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> + <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> + <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> + <a href="https://www.github.com/underd0g1/tweetwebgraph">Project</a><br> + + </div> + </div> +</div> + +<!-- Footer --> +<footer class="container-fluid bg-4 text-center"> + <p>Made with <span class="glyphicon glyphicon-heart"></span><br><a href="http://www.underd0g.co"><span class="glyphicon glyphicon-barcode"></span></a></p> +</footer> + +</body> +</html>