Ajout exercices supplémentaires formation JS

This commit is contained in:
2015-04-01 22:05:05 +02:00
committed by Benjamin Bohard
parent ee0ae6d1be
commit b1cbcaefd3
3012 changed files with 363655 additions and 126 deletions

View File

@ -0,0 +1,628 @@
/*global setTimeout: false, console: false */
(function () {
var async = {};
// global on the server, window in the browser
var root = this,
previous_async = root.async;
if (typeof module !== 'undefined' && module.exports) {
module.exports = async;
}
else {
root.async = async;
}
async.noConflict = function () {
root.async = previous_async;
return async;
};
//// cross-browser compatiblity functions ////
var _forEach = function (arr, iterator) {
if (arr.forEach) {
return arr.forEach(iterator);
}
for (var i = 0; i < arr.length; i += 1) {
iterator(arr[i], i, arr);
}
};
var _map = function (arr, iterator) {
if (arr.map) {
return arr.map(iterator);
}
var results = [];
_forEach(arr, function (x, i, a) {
results.push(iterator(x, i, a));
});
return results;
};
var _reduce = function (arr, iterator, memo) {
if (arr.reduce) {
return arr.reduce(iterator, memo);
}
_forEach(arr, function (x, i, a) {
memo = iterator(memo, x, i, a);
});
return memo;
};
var _keys = function (obj) {
if (Object.keys) {
return Object.keys(obj);
}
var keys = [];
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
keys.push(k);
}
}
return keys;
};
var _indexOf = function (arr, item) {
if (arr.indexOf) {
return arr.indexOf(item);
}
for (var i = 0; i < arr.length; i += 1) {
if (arr[i] === item) {
return i;
}
}
return -1;
};
//// exported async module functions ////
//// nextTick implementation with browser-compatible fallback ////
if (typeof setImmediate === 'function') {
async.nextTick = function (fn) {
setImmediate(fn);
};
}
else if (typeof process !== 'undefined' && process.nextTick) {
async.nextTick = process.nextTick;
}
else {
async.nextTick = function (fn) {
setTimeout(fn, 0);
};
}
async.forEach = function (arr, iterator, callback) {
if (!arr.length) {
return callback();
}
var completed = 0;
_forEach(arr, function (x) {
iterator(x, function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
if (completed === arr.length) {
callback();
}
}
});
});
};
async.forEachSeries = function (arr, iterator, callback) {
if (!arr.length) {
return callback();
}
var completed = 0;
var iterate = function () {
iterator(arr[completed], function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
if (completed === arr.length) {
callback();
}
else {
iterate();
}
}
});
};
iterate();
};
var doParallel = function (fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
return fn.apply(null, [async.forEach].concat(args));
};
};
var doSeries = function (fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
return fn.apply(null, [async.forEachSeries].concat(args));
};
};
var _asyncMap = function (eachfn, arr, iterator, callback) {
var results = [];
arr = _map(arr, function (x, i) {
return {index: i, value: x};
});
eachfn(arr, function (x, callback) {
iterator(x.value, function (err, v) {
results[x.index] = v;
callback(err);
});
}, function (err) {
callback(err, results);
});
};
async.map = doParallel(_asyncMap);
async.mapSeries = doSeries(_asyncMap);
// reduce only has a series version, as doing reduce in parallel won't
// work in many situations.
async.reduce = function (arr, memo, iterator, callback) {
async.forEachSeries(arr, function (x, callback) {
iterator(memo, x, function (err, v) {
memo = v;
callback(err);
});
}, function (err) {
callback(err, memo);
});
};
// inject alias
async.inject = async.reduce;
// foldl alias
async.foldl = async.reduce;
async.reduceRight = function (arr, memo, iterator, callback) {
var reversed = _map(arr, function (x) {
return x;
}).reverse();
async.reduce(reversed, memo, iterator, callback);
};
// foldr alias
async.foldr = async.reduceRight;
var _filter = function (eachfn, arr, iterator, callback) {
var results = [];
arr = _map(arr, function (x, i) {
return {index: i, value: x};
});
eachfn(arr, function (x, callback) {
iterator(x.value, function (v) {
if (v) {
results.push(x);
}
callback();
});
}, function (err) {
callback(_map(results.sort(function (a, b) {
return a.index - b.index;
}), function (x) {
return x.value;
}));
});
};
async.filter = doParallel(_filter);
async.filterSeries = doSeries(_filter);
// select alias
async.select = async.filter;
async.selectSeries = async.filterSeries;
var _reject = function (eachfn, arr, iterator, callback) {
var results = [];
arr = _map(arr, function (x, i) {
return {index: i, value: x};
});
eachfn(arr, function (x, callback) {
iterator(x.value, function (v) {
if (!v) {
results.push(x);
}
callback();
});
}, function (err) {
callback(_map(results.sort(function (a, b) {
return a.index - b.index;
}), function (x) {
return x.value;
}));
});
};
async.reject = doParallel(_reject);
async.rejectSeries = doSeries(_reject);
var _detect = function (eachfn, arr, iterator, main_callback) {
eachfn(arr, function (x, callback) {
iterator(x, function (result) {
if (result) {
main_callback(x);
}
else {
callback();
}
});
}, function (err) {
main_callback();
});
};
async.detect = doParallel(_detect);
async.detectSeries = doSeries(_detect);
async.some = function (arr, iterator, main_callback) {
async.forEach(arr, function (x, callback) {
iterator(x, function (v) {
if (v) {
main_callback(true);
main_callback = function () {};
}
callback();
});
}, function (err) {
main_callback(false);
});
};
// any alias
async.any = async.some;
async.every = function (arr, iterator, main_callback) {
async.forEach(arr, function (x, callback) {
iterator(x, function (v) {
if (!v) {
main_callback(false);
main_callback = function () {};
}
callback();
});
}, function (err) {
main_callback(true);
});
};
// all alias
async.all = async.every;
async.sortBy = function (arr, iterator, callback) {
async.map(arr, function (x, callback) {
iterator(x, function (err, criteria) {
if (err) {
callback(err);
}
else {
callback(null, {value: x, criteria: criteria});
}
});
}, function (err, results) {
if (err) {
return callback(err);
}
else {
var fn = function (left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
};
callback(null, _map(results.sort(fn), function (x) {
return x.value;
}));
}
});
};
async.auto = function (tasks, callback) {
callback = callback || function () {};
var keys = _keys(tasks);
if (!keys.length) {
return callback(null);
}
var completed = [];
var listeners = [];
var addListener = function (fn) {
listeners.unshift(fn);
};
var removeListener = function (fn) {
for (var i = 0; i < listeners.length; i += 1) {
if (listeners[i] === fn) {
listeners.splice(i, 1);
return;
}
}
};
var taskComplete = function () {
_forEach(listeners, function (fn) {
fn();
});
};
addListener(function () {
if (completed.length === keys.length) {
callback(null);
}
});
_forEach(keys, function (k) {
var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
var taskCallback = function (err) {
if (err) {
callback(err);
// stop subsequent errors hitting callback multiple times
callback = function () {};
}
else {
completed.push(k);
taskComplete();
}
};
var requires = task.slice(0, Math.abs(task.length - 1)) || [];
var ready = function () {
return _reduce(requires, function (a, x) {
return (a && _indexOf(completed, x) !== -1);
}, true);
};
if (ready()) {
task[task.length - 1](taskCallback);
}
else {
var listener = function () {
if (ready()) {
removeListener(listener);
task[task.length - 1](taskCallback);
}
};
addListener(listener);
}
});
};
async.waterfall = function (tasks, callback) {
if (!tasks.length) {
return callback();
}
callback = callback || function () {};
var wrapIterator = function (iterator) {
return function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
var args = Array.prototype.slice.call(arguments, 1);
var next = iterator.next();
if (next) {
args.push(wrapIterator(next));
}
else {
args.push(callback);
}
async.nextTick(function () {
iterator.apply(null, args);
});
}
};
};
wrapIterator(async.iterator(tasks))();
};
async.parallel = function (tasks, callback) {
callback = callback || function () {};
if (tasks.constructor === Array) {
async.map(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
callback.call(null, err, args || null);
});
}
}, callback);
}
else {
var results = {};
async.forEach(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
results[k] = args;
callback(err);
});
}, function (err) {
callback(err, results);
});
}
};
async.series = function (tasks, callback) {
callback = callback || function () {};
if (tasks.constructor === Array) {
async.mapSeries(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
callback.call(null, err, args || null);
});
}
}, callback);
}
else {
var results = {};
async.forEachSeries(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
results[k] = args;
callback(err);
});
}, function (err) {
callback(err, results);
});
}
};
async.iterator = function (tasks) {
var makeCallback = function (index) {
var fn = function () {
if (tasks.length) {
tasks[index].apply(null, arguments);
}
return fn.next();
};
fn.next = function () {
return (index < tasks.length - 1) ? makeCallback(index + 1): null;
};
return fn;
};
return makeCallback(0);
};
async.apply = function (fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function () {
return fn.apply(
null, args.concat(Array.prototype.slice.call(arguments))
);
};
};
var _concat = function (eachfn, arr, fn, callback) {
var r = [];
eachfn(arr, function (x, cb) {
fn(x, function (err, y) {
r = r.concat(y || []);
cb(err);
});
}, function (err) {
callback(err, r);
});
};
async.concat = doParallel(_concat);
async.concatSeries = doSeries(_concat);
async.whilst = function (test, iterator, callback) {
if (test()) {
iterator(function (err) {
if (err) {
return callback(err);
}
async.whilst(test, iterator, callback);
});
}
else {
callback();
}
};
async.until = function (test, iterator, callback) {
if (!test()) {
iterator(function (err) {
if (err) {
return callback(err);
}
async.until(test, iterator, callback);
});
}
else {
callback();
}
};
async.queue = function (worker, concurrency) {
var workers = 0;
var tasks = [];
var q = {
concurrency: concurrency,
push: function (data, callback) {
tasks.push({data: data, callback: callback});
async.nextTick(q.process);
},
process: function () {
if (workers < q.concurrency && tasks.length) {
var task = tasks.splice(0, 1)[0];
workers += 1;
worker(task.data, function () {
workers -= 1;
if (task.callback) {
task.callback.apply(task, arguments);
}
q.process();
});
}
},
length: function () {
return tasks.length;
}
};
return q;
};
var _console_fn = function (name) {
return function (fn) {
var args = Array.prototype.slice.call(arguments, 1);
fn.apply(null, args.concat([function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (typeof console !== 'undefined') {
if (err) {
if (console.error) {
console.error(err);
}
}
else if (console[name]) {
_forEach(args, function (x) {
console[name](x);
});
}
}
}]));
};
};
async.log = _console_fn('log');
async.dir = _console_fn('dir');
/*async.info = _console_fn('info');
async.warn = _console_fn('warn');
async.error = _console_fn('error');*/
async.memoize = function (fn, hasher) {
var memo = {};
hasher = hasher || function (x) {
return x;
};
return function () {
var args = Array.prototype.slice.call(arguments);
var callback = args.pop();
var key = hasher.apply(null, args);
if (key in memo) {
callback.apply(null, memo[key]);
}
else {
fn.apply(null, args.concat([function () {
memo[key] = arguments;
callback.apply(null, arguments);
}]));
}
};
};
}());

View File

@ -0,0 +1,55 @@
/*
A console.log that won't leave you hanging when node exits
90% of this file was ripped from node.js
License: see: https://github.com/joyent/node/blob/master/lib/console.js
*/
// console object
var formatRegExp = /%[sdj]/g;
function format(f) {
var util = require('util');
if (typeof f !== 'string') {
var objects = [];
for (var i = 0; i < arguments.length; i++) {
objects.push(util.inspect(arguments[i]));
}
return objects.join(' ');
}
var i = 1;
var args = arguments;
var str = String(f).replace(formatRegExp, function(x) {
switch (x) {
case '%s': return String(args[i++]);
case '%d': return Number(args[i++]);
case '%j': return JSON.stringify(args[i++]);
default:
return x;
}
});
for (var len = args.length, x = args[i]; i < len; x = args[++i]) {
if (x === null || typeof x !== 'object') {
str += ' ' + x;
} else {
str += ' ' + util.inspect(x);
}
}
return str;
}
console.log = function() {
var res = process.stdout.write(format.apply(this, arguments) + '\n');
// this is the first time stdout got backed up
if (!res && !process.stdout.pendingWrite) {
process.stdout.pendingWrite = true;
// magic sauce: keep node alive until stdout has flushed
process.stdout.once('drain', function () {
process.stdout.draining = false;
});
}
};

View File

@ -0,0 +1,70 @@
0.4.3 / 2011-06-20
==================
* Fixed stacktraces line number when used multiline js expressions [Octave]
0.4.2 / 2011-05-11
==================
* Added client side support
0.4.1 / 2011-04-21
==================
* Fixed error context
0.4.0 / 2011-04-21
==================
* Added; ported jade's error reporting to ejs. [slaskis]
0.3.1 / 2011-02-23
==================
* Fixed optional `compile()` options
0.3.0 / 2011-02-14
==================
* Added 'json' filter [Yuriy Bogdanov]
* Use exported version of parse function to allow monkey-patching [Anatoliy Chakkaev]
0.2.1 / 2010-10-07
==================
* Added filter support
* Fixed _cache_ option. ~4x performance increase
0.2.0 / 2010-08-05
==================
* Added support for global tag config
* Added custom tag support. Closes #5
* Fixed whitespace bug. Closes #4
0.1.0 / 2010-08-04
==================
* Faster implementation [ashleydev]
0.0.4 / 2010-08-02
==================
* Fixed single quotes for content outside of template tags. [aniero]
* Changed; `exports.compile()` now expects only "locals"
0.0.3 / 2010-07-15
==================
* Fixed single quotes
0.0.2 / 2010-07-09
==================
* Fixed newline preservation
0.0.1 / 2010-07-09
==================
* Initial release

View File

@ -0,0 +1,20 @@
SRC = $(shell find lib -name "*.js" -type f)
UGLIFY_FLAGS = --no-mangle
test:
@./node_modules/.bin/expresso test/*.test.js
ejs.js: $(SRC)
@node support/compile.js $^
ejs.min.js: ejs.js
@uglifyjs $(UGLIFY_FLAGS) $< > $@ \
&& du ejs.min.js \
&& du ejs.js
clean:
rm -f ejs.js
rm -f ejs.min.js
.PHONY: test

View File

@ -0,0 +1,152 @@
# EJS
Embedded JavaScript templates.
## Installation
$ npm install ejs
## Features
* Complies with the [Express](http://expressjs.com) view system
* Static caching of intermediate JavaScript
* Unbuffered code for conditionals etc `<% code %>`
* Escapes html by default with `<%= code %>`
* Unescaped buffering with `<%- code %>`
* Supports tag customization
* Filter support for designer-friendly templates
* Client-side support
## Example
<% if (user) { %>
<h2><%= user.name %></h2>
<% } %>
## Usage
ejs.compile(str, options);
// => Function
ejs.render(str, options);
// => str
## Options
- `locals` Local variables object
- `cache` Compiled functions are cached, requires `filename`
- `filename` Used by `cache` to key caches
- `scope` Function execution context
- `debug` Output generated function body
- `open` Open tag, defaulting to "<%"
- `close` Closing tag, defaulting to "%>"
## Custom tags
Custom tags can also be applied globally:
var ejs = require('ejs');
ejs.open = '{{';
ejs.close = '}}';
Which would make the following a valid template:
<h1>{{= title }}</h1>
## Filters
EJS conditionally supports the concept of "filters". A "filter chain"
is a designer friendly api for manipulating data, without writing JavaScript.
Filters can be applied by supplying the _:_ modifier, so for example if we wish to take the array `[{ name: 'tj' }, { name: 'mape' }, { name: 'guillermo' }]` and output a list of names we can do this simply with filters:
Template:
<p><%=: users | map:'name' | join %></p>
Output:
<p>Tj, Mape, Guillermo</p>
Render call:
ejs.render(str, {
locals: {
users: [
{ name: 'tj' },
{ name: 'mape' },
{ name: 'guillermo' }
]
}
});
Or perhaps capitalize the first user's name for display:
<p><%=: users | first | capitalize %></p>
## Filter list
Currently these filters are available:
- first
- last
- capitalize
- downcase
- upcase
- sort
- sort_by:'prop'
- size
- length
- plus:n
- minus:n
- times:n
- divided_by:n
- join:'val'
- truncate:n
- truncate_words:n
- replace:pattern,substitution
- prepend:val
- append:val
- map:'prop'
- reverse
- get:'prop'
## Adding filters
To add a filter simply add a method to the `.filters` object:
```js
ejs.filters.last = function(obj) {
return obj[obj.length - 1];
};
```
## client-side support
include `./ejs.js` or `./ejs.min.js` and `require("ejs").compile(str)`.
## License
(The MIT License)
Copyright (c) 2009-2010 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
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.

View File

@ -0,0 +1,14 @@
var ejs = require('./lib/ejs'),
str = '<% if (foo) { %><p><%= foo %></p><% } %>',
times = 50000;
console.log('rendering ' + times + ' times');
var start = new Date;
while (times--) {
ejs.render(str, { cache: true, filename: 'test', locals: { foo: 'bar' }});
}
console.log('took ' + (new Date - start) + 'ms');

View File

@ -0,0 +1,531 @@
// CommonJS require()
function require(p){
var path = require.resolve(p)
, mod = require.modules[path];
if (!mod) throw new Error('failed to require "' + p + '"');
if (!mod.exports) {
mod.exports = {};
mod.call(mod.exports, mod, mod.exports, require.relative(path));
}
return mod.exports;
}
require.modules = {};
require.resolve = function (path){
var orig = path
, reg = path + '.js'
, index = path + '/index.js';
return require.modules[reg] && reg
|| require.modules[index] && index
|| orig;
};
require.register = function (path, fn){
require.modules[path] = fn;
};
require.relative = function (parent) {
return function(p){
if ('.' != p[0]) return require(p);
var path = parent.split('/')
, segs = p.split('/');
path.pop();
for (var i = 0; i < segs.length; i++) {
var seg = segs[i];
if ('..' == seg) path.pop();
else if ('.' != seg) path.push(seg);
}
return require(path.join('/'));
};
};
require.register("ejs.js", function(module, exports, require){
/*!
* EJS
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('./utils');
/**
* Library version.
*/
exports.version = '0.4.2';
/**
* Filters.
*
* @type Object
*/
var filters = exports.filters = require('./filters');
/**
* Intermediate js cache.
*
* @type Object
*/
var cache = {};
/**
* Clear intermediate js cache.
*
* @api public
*/
exports.clearCache = function(){
cache = {};
};
/**
* Translate filtered code into function calls.
*
* @param {String} js
* @return {String}
* @api private
*/
function filtered(js) {
return js.substr(1).split('|').reduce(function(js, filter){
var parts = filter.split(':')
, name = parts.shift()
, args = parts.shift() || '';
if (args) args = ', ' + args;
return 'filters.' + name + '(' + js + args + ')';
});
};
/**
* Re-throw the given `err` in context to the
* `str` of ejs, `filename`, and `lineno`.
*
* @param {Error} err
* @param {String} str
* @param {String} filename
* @param {String} lineno
* @api private
*/
function rethrow(err, str, filename, lineno){
var lines = str.split('\n')
, start = Math.max(lineno - 3, 0)
, end = Math.min(lines.length, lineno + 3);
// 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;
}
/**
* Parse the given `str` of ejs, returning the function body.
*
* @param {String} str
* @return {String}
* @api public
*/
var parse = exports.parse = function(str, options){
var options = options || {}
, open = options.open || exports.open || '<%'
, close = options.close || exports.close || '%>';
var buf = [
"var buf = [];"
, "\nwith (locals) {"
, "\n buf.push('"
];
var lineno = 1;
for (var i = 0, len = str.length; i < len; ++i) {
if (str.slice(i, open.length + i) == open) {
i += open.length
var prefix, postfix, line = '__stack.lineno=' + lineno;
switch (str[i]) {
case '=':
prefix = "', escape((" + line + ', ';
postfix = ")), '";
++i;
break;
case '-':
prefix = "', (" + line + ', ';
postfix = "), '";
++i;
break;
default:
prefix = "');" + line + ';';
postfix = "; buf.push('";
}
var start = i;
var end = str.indexOf(close, i);
var js = str.substring(i, end);
var n = 0;
while ((n = js.indexOf("\n", n)) > -1) {
n++;
lineno++;
}
if (js[0] == ':') js = filtered(js);
buf.push(prefix, js, postfix);
i += end - start + close.length - 1;
} else if (str[i] == "\\") {
buf.push("\\\\");
} else if (str[i] == "'") {
buf.push("\\'");
} else if (str[i] == "\r") {
buf.push(" ");
} else if (str[i] == "\n") {
buf.push("\\n");
lineno++;
} else {
buf.push(str[i]);
}
}
buf.push("');\n}\nreturn buf.join('');");
return buf.join('');
};
/**
* Compile the given `str` of ejs into a `Function`.
*
* @param {String} str
* @param {Object} options
* @return {Function}
* @api public
*/
var compile = exports.compile = function(str, options){
options = options || {};
var input = JSON.stringify(str)
, filename = options.filename
? JSON.stringify(options.filename)
: 'undefined';
// Adds the fancy stack trace meta info
str = [
'var __stack = { lineno: 1, input: ' + input + ', filename: ' + filename + ' };',
rethrow.toString(),
'try {',
exports.parse(str, options),
'} catch (err) {',
' rethrow(err, __stack.input, __stack.filename, __stack.lineno);',
'}'
].join("\n");
if (options.debug) console.log(str);
var fn = new Function('locals, filters, escape', str);
return function(locals){
return fn.call(this, locals, filters, utils.escape);
}
};
/**
* Render the given `str` of ejs.
*
* Options:
*
* - `locals` Local variables object
* - `cache` Compiled functions are cached, requires `filename`
* - `filename` Used by `cache` to key caches
* - `scope` Function execution context
* - `debug` Output generated function body
* - `open` Open tag, defaulting to "<%"
* - `close` Closing tag, defaulting to "%>"
*
* @param {String} str
* @param {Object} options
* @return {String}
* @api public
*/
exports.render = function(str, options){
var fn
, options = options || {};
if (options.cache) {
if (options.filename) {
fn = cache[options.filename] || (cache[options.filename] = compile(str, options));
} else {
throw new Error('"cache" option requires "filename".');
}
} else {
fn = compile(str, options);
}
return fn.call(options.scope, options.locals || {});
};
/**
* Expose to require().
*/
if (require.extensions) {
require.extensions['.ejs'] = function(module, filename) {
source = require('fs').readFileSync(filename, 'utf-8');
module._compile(compile(source, {}), filename);
};
} else if (require.registerExtension) {
require.registerExtension('.ejs', function(src) {
return compile(src, {});
});
}
}); // module: ejs.js
require.register("filters.js", function(module, exports, require){
/*!
* EJS - Filters
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
/**
* First element of the target `obj`.
*/
exports.first = function(obj) {
return obj[0];
};
/**
* Last element of the target `obj`.
*/
exports.last = function(obj) {
return obj[obj.length - 1];
};
/**
* Capitalize the first letter of the target `str`.
*/
exports.capitalize = function(str){
str = String(str);
return str[0].toUpperCase() + str.substr(1, str.length);
};
/**
* Downcase the target `str`.
*/
exports.downcase = function(str){
return String(str).toLowerCase();
};
/**
* Uppercase the target `str`.
*/
exports.upcase = function(str){
return String(str).toUpperCase();
};
/**
* Sort the target `obj`.
*/
exports.sort = function(obj){
return Object.create(obj).sort();
};
/**
* Sort the target `obj` by the given `prop` ascending.
*/
exports.sort_by = function(obj, prop){
return Object.create(obj).sort(function(a, b){
a = a[prop], b = b[prop];
if (a > b) return 1;
if (a < b) return -1;
return 0;
});
};
/**
* Size or length of the target `obj`.
*/
exports.size = exports.length = function(obj) {
return obj.length;
};
/**
* Add `a` and `b`.
*/
exports.plus = function(a, b){
return Number(a) + Number(b);
};
/**
* Subtract `b` from `a`.
*/
exports.minus = function(a, b){
return Number(a) - Number(b);
};
/**
* Multiply `a` by `b`.
*/
exports.times = function(a, b){
return Number(a) * Number(b);
};
/**
* Divide `a` by `b`.
*/
exports.divided_by = function(a, b){
return Number(a) / Number(b);
};
/**
* Join `obj` with the given `str`.
*/
exports.join = function(obj, str){
return obj.join(str || ', ');
};
/**
* Truncate `str` to `len`.
*/
exports.truncate = function(str, len){
str = String(str);
return str.substr(0, len);
};
/**
* Truncate `str` to `n` words.
*/
exports.truncate_words = function(str, n){
var str = String(str)
, words = str.split(/ +/);
return words.slice(0, n).join(' ');
};
/**
* Replace `pattern` with `substitution` in `str`.
*/
exports.replace = function(str, pattern, substitution){
return String(str).replace(pattern, substitution || '');
};
/**
* Prepend `val` to `obj`.
*/
exports.prepend = function(obj, val){
return Array.isArray(obj)
? [val].concat(obj)
: val + obj;
};
/**
* Append `val` to `obj`.
*/
exports.append = function(obj, val){
return Array.isArray(obj)
? obj.concat(val)
: obj + val;
};
/**
* Map the given `prop`.
*/
exports.map = function(arr, prop){
return arr.map(function(obj){
return obj[prop];
});
};
/**
* Reverse the given `obj`.
*/
exports.reverse = function(obj){
return Array.isArray(obj)
? obj.reverse()
: String(obj).split('').reverse().join('');
};
/**
* Get `prop` of the given `obj`.
*/
exports.get = function(obj, prop){
return obj[prop];
};
/**
* Packs the given `obj` into json string
*/
exports.json = function(obj){
return JSON.stringify(obj);
};
}); // module: filters.js
require.register("utils.js", function(module, exports, require){
/*!
* EJS
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
/**
* Escape the given string of `html`.
*
* @param {String} html
* @return {String}
* @api private
*/
exports.escape = function(html){
return String(html)
.replace(/&(?!\w+;)/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};
}); // module: utils.js

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
<html>
<head>
<script src="../ejs.js"></script>
</head>
</html>

View File

@ -0,0 +1,7 @@
<% if (names.length) { %>
<ul>
<% names.forEach(function(name){ %>
<li><%= name %></li>
<% }) %>
</ul>
<% } %>

View File

@ -0,0 +1,16 @@
/**
* Module dependencies.
*/
var ejs = require('../')
, fs = require('fs')
, str = fs.readFileSync(__dirname + '/list.ejs', 'utf8');
var ret = ejs.render(str, {
locals: {
names: ['foo', 'bar', 'baz']
}
});
console.log(ret);

View File

@ -0,0 +1,2 @@
module.exports = require('./lib/ejs');

View File

@ -0,0 +1,251 @@
/*!
* EJS
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('./utils');
/**
* Library version.
*/
exports.version = '0.4.3';
/**
* Filters.
*
* @type Object
*/
var filters = exports.filters = require('./filters');
/**
* Intermediate js cache.
*
* @type Object
*/
var cache = {};
/**
* Clear intermediate js cache.
*
* @api public
*/
exports.clearCache = function(){
cache = {};
};
/**
* Translate filtered code into function calls.
*
* @param {String} js
* @return {String}
* @api private
*/
function filtered(js) {
return js.substr(1).split('|').reduce(function(js, filter){
var parts = filter.split(':')
, name = parts.shift()
, args = parts.shift() || '';
if (args) args = ', ' + args;
return 'filters.' + name + '(' + js + args + ')';
});
};
/**
* Re-throw the given `err` in context to the
* `str` of ejs, `filename`, and `lineno`.
*
* @param {Error} err
* @param {String} str
* @param {String} filename
* @param {String} lineno
* @api private
*/
function rethrow(err, str, filename, lineno){
var lines = str.split('\n')
, start = Math.max(lineno - 3, 0)
, end = Math.min(lines.length, lineno + 3);
// 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;
}
/**
* Parse the given `str` of ejs, returning the function body.
*
* @param {String} str
* @return {String}
* @api public
*/
var parse = exports.parse = function(str, options){
var options = options || {}
, open = options.open || exports.open || '<%'
, close = options.close || exports.close || '%>';
var buf = [
"var buf = [];"
, "\nwith (locals) {"
, "\n buf.push('"
];
var lineno = 1;
for (var i = 0, len = str.length; i < len; ++i) {
if (str.slice(i, open.length + i) == open) {
i += open.length
var prefix, postfix, line = '__stack.lineno=' + lineno;
switch (str.substr(i, 1)) {
case '=':
prefix = "', escape((" + line + ', ';
postfix = ")), '";
++i;
break;
case '-':
prefix = "', (" + line + ', ';
postfix = "), '";
++i;
break;
default:
prefix = "');" + line + ';';
postfix = "; buf.push('";
}
var end = str.indexOf(close, i)
, js = str.substring(i, end)
, start = i
, n = 0;
while (~(n = js.indexOf("\n", n))) n++, lineno++;
if (js.substr(0, 1) == ':') js = filtered(js);
buf.push(prefix, js, postfix);
i += end - start + close.length - 1;
} else if (str.substr(i, 1) == "\\") {
buf.push("\\\\");
} else if (str.substr(i, 1) == "'") {
buf.push("\\'");
} else if (str.substr(i, 1) == "\r") {
buf.push(" ");
} else if (str.substr(i, 1) == "\n") {
buf.push("\\n");
lineno++;
} else {
buf.push(str.substr(i, 1));
}
}
buf.push("');\n}\nreturn buf.join('');");
return buf.join('');
};
/**
* Compile the given `str` of ejs into a `Function`.
*
* @param {String} str
* @param {Object} options
* @return {Function}
* @api public
*/
var compile = exports.compile = function(str, options){
options = options || {};
var input = JSON.stringify(str)
, filename = options.filename
? JSON.stringify(options.filename)
: 'undefined';
// Adds the fancy stack trace meta info
str = [
'var __stack = { lineno: 1, input: ' + input + ', filename: ' + filename + ' };',
rethrow.toString(),
'try {',
exports.parse(str, options),
'} catch (err) {',
' rethrow(err, __stack.input, __stack.filename, __stack.lineno);',
'}'
].join("\n");
if (options.debug) console.log(str);
var fn = new Function('locals, filters, escape', str);
return function(locals){
return fn.call(this, locals, filters, utils.escape);
}
};
/**
* Render the given `str` of ejs.
*
* Options:
*
* - `locals` Local variables object
* - `cache` Compiled functions are cached, requires `filename`
* - `filename` Used by `cache` to key caches
* - `scope` Function execution context
* - `debug` Output generated function body
* - `open` Open tag, defaulting to "<%"
* - `close` Closing tag, defaulting to "%>"
*
* @param {String} str
* @param {Object} options
* @return {String}
* @api public
*/
exports.render = function(str, options){
var fn
, options = options || {};
if (options.cache) {
if (options.filename) {
fn = cache[options.filename] || (cache[options.filename] = compile(str, options));
} else {
throw new Error('"cache" option requires "filename".');
}
} else {
fn = compile(str, options);
}
return fn.call(options.scope, options.locals || {});
};
/**
* Expose to require().
*/
if (require.extensions) {
require.extensions['.ejs'] = function(module, filename) {
source = require('fs').readFileSync(filename, 'utf-8');
module._compile(compile(source, {}), filename);
};
} else if (require.registerExtension) {
require.registerExtension('.ejs', function(src) {
return compile(src, {});
});
}

View File

@ -0,0 +1,198 @@
/*!
* EJS - Filters
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
/**
* First element of the target `obj`.
*/
exports.first = function(obj) {
return obj[0];
};
/**
* Last element of the target `obj`.
*/
exports.last = function(obj) {
return obj[obj.length - 1];
};
/**
* Capitalize the first letter of the target `str`.
*/
exports.capitalize = function(str){
str = String(str);
return str[0].toUpperCase() + str.substr(1, str.length);
};
/**
* Downcase the target `str`.
*/
exports.downcase = function(str){
return String(str).toLowerCase();
};
/**
* Uppercase the target `str`.
*/
exports.upcase = function(str){
return String(str).toUpperCase();
};
/**
* Sort the target `obj`.
*/
exports.sort = function(obj){
return Object.create(obj).sort();
};
/**
* Sort the target `obj` by the given `prop` ascending.
*/
exports.sort_by = function(obj, prop){
return Object.create(obj).sort(function(a, b){
a = a[prop], b = b[prop];
if (a > b) return 1;
if (a < b) return -1;
return 0;
});
};
/**
* Size or length of the target `obj`.
*/
exports.size = exports.length = function(obj) {
return obj.length;
};
/**
* Add `a` and `b`.
*/
exports.plus = function(a, b){
return Number(a) + Number(b);
};
/**
* Subtract `b` from `a`.
*/
exports.minus = function(a, b){
return Number(a) - Number(b);
};
/**
* Multiply `a` by `b`.
*/
exports.times = function(a, b){
return Number(a) * Number(b);
};
/**
* Divide `a` by `b`.
*/
exports.divided_by = function(a, b){
return Number(a) / Number(b);
};
/**
* Join `obj` with the given `str`.
*/
exports.join = function(obj, str){
return obj.join(str || ', ');
};
/**
* Truncate `str` to `len`.
*/
exports.truncate = function(str, len){
str = String(str);
return str.substr(0, len);
};
/**
* Truncate `str` to `n` words.
*/
exports.truncate_words = function(str, n){
var str = String(str)
, words = str.split(/ +/);
return words.slice(0, n).join(' ');
};
/**
* Replace `pattern` with `substitution` in `str`.
*/
exports.replace = function(str, pattern, substitution){
return String(str).replace(pattern, substitution || '');
};
/**
* Prepend `val` to `obj`.
*/
exports.prepend = function(obj, val){
return Array.isArray(obj)
? [val].concat(obj)
: val + obj;
};
/**
* Append `val` to `obj`.
*/
exports.append = function(obj, val){
return Array.isArray(obj)
? obj.concat(val)
: obj + val;
};
/**
* Map the given `prop`.
*/
exports.map = function(arr, prop){
return arr.map(function(obj){
return obj[prop];
});
};
/**
* Reverse the given `obj`.
*/
exports.reverse = function(obj){
return Array.isArray(obj)
? obj.reverse()
: String(obj).split('').reverse().join('');
};
/**
* Get `prop` of the given `obj`.
*/
exports.get = function(obj, prop){
return obj[prop];
};
/**
* Packs the given `obj` into json string
*/
exports.json = function(obj){
return JSON.stringify(obj);
};

View File

@ -0,0 +1,23 @@
/*!
* EJS
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
/**
* Escape the given string of `html`.
*
* @param {String} html
* @return {String}
* @api private
*/
exports.escape = function(html){
return String(html)
.replace(/&(?!\w+;)/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};

View File

@ -0,0 +1,11 @@
{
"name": "ejs",
"description": "Embedded JavaScript templates",
"version": "0.4.3",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"keywords": ["template", "engine", "ejs"],
"devDependencies": {
"expresso": "0.9.2"
},
"main": "./lib/ejs.js"
}

View File

@ -0,0 +1,173 @@
/**
* Module dependencies.
*/
var fs = require('fs');
/**
* Arguments.
*/
var args = process.argv.slice(2)
, pending = args.length
, files = {};
console.log('');
// parse arguments
args.forEach(function(file){
var mod = file.replace('lib/', '');
fs.readFile(file, 'utf8', function(err, js){
if (err) throw err;
console.log(' \033[90mcompile : \033[0m\033[36m%s\033[0m', file);
files[file] = parse(js);
--pending || compile();
});
});
/**
* Parse the given `js`.
*/
function parse(js) {
return parseInheritance(parseConditionals(js));
}
/**
* Parse __proto__.
*/
function parseInheritance(js) {
return js
.replace(/^ *(\w+)\.prototype\.__proto__ * = *(\w+)\.prototype *;?/gm, function(_, child, parent){
return child + '.prototype = new ' + parent + ';\n'
+ child + '.prototype.constructor = '+ child + ';\n';
});
}
/**
* Parse the given `js`, currently supporting:
*
* 'if' ['node' | 'browser']
* 'end'
*
*/
function parseConditionals(js) {
var lines = js.split('\n')
, len = lines.length
, buffer = true
, browser = false
, buf = []
, line
, cond;
for (var i = 0; i < len; ++i) {
line = lines[i];
if (/^ *\/\/ *if *(node|browser)/gm.exec(line)) {
cond = RegExp.$1;
buffer = browser = 'browser' == cond;
} else if (/^ *\/\/ *end/.test(line)) {
buffer = true;
browser = false;
} else if (browser) {
buf.push(line.replace(/^( *)\/\//, '$1'));
} else if (buffer) {
buf.push(line);
}
}
return buf.join('\n');
}
/**
* Compile the files.
*/
function compile() {
var buf = '';
buf += '\n// CommonJS require()\n\n';
buf += browser.require + '\n\n';
buf += 'require.modules = {};\n\n';
buf += 'require.resolve = ' + browser.resolve + ';\n\n';
buf += 'require.register = ' + browser.register + ';\n\n';
buf += 'require.relative = ' + browser.relative + ';\n\n';
args.forEach(function(file){
var js = files[file];
file = file.replace('lib/', '');
buf += '\nrequire.register("' + file + '", function(module, exports, require){\n';
buf += js;
buf += '\n}); // module: ' + file + '\n';
});
fs.writeFile('ejs.js', buf, function(err){
if (err) throw err;
console.log(' \033[90m create : \033[0m\033[36m%s\033[0m', 'ejs.js');
console.log();
});
}
// refactored version of weepy's
// https://github.com/weepy/brequire/blob/master/browser/brequire.js
var browser = {
/**
* Require a module.
*/
require: function require(p){
var path = require.resolve(p)
, mod = require.modules[path];
if (!mod) throw new Error('failed to require "' + p + '"');
if (!mod.exports) {
mod.exports = {};
mod.call(mod.exports, mod, mod.exports, require.relative(path));
}
return mod.exports;
},
/**
* Resolve module path.
*/
resolve: function(path){
var orig = path
, reg = path + '.js'
, index = path + '/index.js';
return require.modules[reg] && reg
|| require.modules[index] && index
|| orig;
},
/**
* Return relative require().
*/
relative: function(parent) {
return function(p){
if ('.' != p.substr(0, 1)) return require(p);
var path = parent.split('/')
, segs = p.split('/');
path.pop();
for (var i = 0; i < segs.length; i++) {
var seg = segs[i];
if ('..' == seg) path.pop();
else if ('.' != seg) path.push(seg);
}
return require(path.join('/'));
};
},
/**
* Register a module.
*/
register: function(path, fn){
require.modules[path] = fn;
}
};

View File

@ -0,0 +1,269 @@
/**
* Module dependencies.
*/
var ejs = require('../')
, assert = require('assert');
module.exports = {
'test .version': function(){
assert.ok(/^\d+\.\d+\.\d+$/.test(ejs.version), 'Test .version format');
},
'test html': function(){
assert.equal('<p>yay</p>', ejs.render('<p>yay</p>'));
},
'test buffered code': function(){
var html = '<p>tj</p>',
str = '<p><%= name %></p>',
locals = { name: 'tj' };
assert.equal(html, ejs.render(str, { locals: locals }));
},
'test unbuffered code': function(){
var html = '<p>tj</p>',
str = '<% if (name) { %><p><%= name %></p><% } %>',
locals = { name: 'tj' };
assert.equal(html, ejs.render(str, { locals: locals }));
},
'test `scope` option': function(){
var html = '<p>tj</p>',
str = '<p><%= this %></p>';
assert.equal(html, ejs.render(str, { scope: 'tj' }));
},
'test escaping': function(){
assert.equal('&lt;script&gt;', ejs.render('<%= "<script>" %>'));
assert.equal('<script>', ejs.render('<%- "<script>" %>'));
},
'test newlines': function(){
var html = '\n<p>tj</p>\n<p>tj@sencha.com</p>',
str = '<% if (name) { %>\n<p><%= name %></p>\n<p><%= email %></p><% } %>',
locals = { name: 'tj', email: 'tj@sencha.com' };
assert.equal(html, ejs.render(str, { locals: locals }));
},
'test single quotes': function(){
var html = '<p>WAHOO</p>',
str = "<p><%= up('wahoo') %></p>",
locals = { up: function(str){ return str.toUpperCase(); }};
assert.equal(html, ejs.render(str, { locals: locals }));
},
'test single quotes in the html': function(){
var html = '<p>WAHOO that\'s cool</p>',
str = '<p><%= up(\'wahoo\') %> that\'s cool</p>',
locals = { up: function(str){ return str.toUpperCase(); }};
assert.equal(html, ejs.render(str, { locals: locals }));
},
'test multiple single quotes': function() {
var html = "<p>couldn't shouldn't can't</p>",
str = "<p>couldn't shouldn't can't</p>";
assert.equal(html, ejs.render(str));
},
'test single quotes inside tags': function() {
var html = '<p>string</p>',
str = "<p><%= 'string' %></p>";
assert.equal(html, ejs.render(str));
},
'test back-slashes in the document': function() {
var html = "<p>backslash: '\\'</p>",
str = "<p>backslash: '\\'</p>";
assert.equal(html, ejs.render(str));
},
'test double quotes': function(){
var html = '<p>WAHOO</p>',
str = '<p><%= up("wahoo") %></p>',
locals = { up: function(str){ return str.toUpperCase(); }};
assert.equal(html, ejs.render(str, { locals: locals }));
},
'test multiple double quotes': function() {
var html = '<p>just a "test" wahoo</p>',
str = '<p>just a "test" wahoo</p>';
assert.equal(html, ejs.render(str));
},
'test whitespace': function(){
var html = '<p>foo</p>',
str = '<p><%="foo"%></p>';
assert.equal(html, ejs.render(str));
var html = '<p>foo</p>',
str = '<p><%=bar%></p>';
assert.equal(html, ejs.render(str, { locals: { bar: 'foo' }}));
},
'test custom tags': function(){
var html = '<p>foo</p>',
str = '<p>{{= "foo" }}</p>';
assert.equal(html, ejs.render(str, {
open: '{{',
close: '}}'
}));
var html = '<p>foo</p>',
str = '<p><?= "foo" ?></p>';
assert.equal(html, ejs.render(str, {
open: '<?',
close: '?>'
}));
},
'test custom tags over 2 chars': function(){
var html = '<p>foo</p>',
str = '<p>{{{{= "foo" }>>}</p>';
assert.equal(html, ejs.render(str, {
open: '{{{{',
close: '}>>}'
}));
var html = '<p>foo</p>',
str = '<p><??= "foo" ??></p>';
assert.equal(html, ejs.render(str, {
open: '<??',
close: '??>'
}));
},
'test global custom tags': function(){
var html = '<p>foo</p>',
str = '<p>{{= "foo" }}</p>';
ejs.open = '{{';
ejs.close = '}}';
assert.equal(html, ejs.render(str));
delete ejs.open;
delete ejs.close;
},
'test iteration': function(){
var html = '<p>foo</p>',
str = '<% for (var key in items) { %>'
+ '<p><%= items[key] %></p>'
+ '<% } %>';
assert.equal(html, ejs.render(str, {
locals: {
items: ['foo']
}
}));
var html = '<p>foo</p>',
str = '<% items.forEach(function(item){ %>'
+ '<p><%= item %></p>'
+ '<% }) %>';
assert.equal(html, ejs.render(str, {
locals: {
items: ['foo']
}
}));
},
'test filter support': function(){
var html = 'Zab',
str = '<%=: items | reverse | first | reverse | capitalize %>';
assert.equal(html, ejs.render(str, {
locals: {
items: ['foo', 'bar', 'baz']
}
}));
},
'test filter argument support': function(){
var html = 'tj, guillermo',
str = '<%=: users | map:"name" | join:", " %>';
assert.equal(html, ejs.render(str, {
locals: {
users: [
{ name: 'tj' },
{ name: 'guillermo' }
]
}
}));
},
'test sort_by filter': function(){
var html = 'tj',
str = '<%=: users | sort_by:"name" | last | get:"name" %>';
assert.equal(html, ejs.render(str, {
locals: {
users: [
{ name: 'guillermo' },
{ name: 'tj' },
{ name: 'mape' }
]
}
}));
},
'test custom filters': function(){
var html = 'Welcome Tj Holowaychuk',
str = '<%=: users | first | greeting %>';
ejs.filters.greeting = function(user){
return 'Welcome ' + user.first + ' ' + user.last + '';
};
assert.equal(html, ejs.render(str, {
locals: {
users: [
{ first: 'Tj', last: 'Holowaychuk' }
]
}
}));
},
'test useful stack traces': function(){
var str = [
"A little somethin'",
"somethin'",
"<% if (name) { %>", // Failing line
" <p><%= name %></p>",
" <p><%= email %></p>",
"<% } %>"
].join("\n");
try {
ejs.render(str)
} catch( err ){
assert.includes(err.message,"name is not defined");
assert.eql(err.name,"ReferenceError");
var lineno = parseInt(err.toString().match(/ejs:(\d+)\n/)[1]);
assert.eql(lineno,3,"Error should been thrown on line 3, was thrown on line "+lineno);
}
},
'test useful stack traces multiline': function(){
var str = [
"A little somethin'",
"somethin'",
"<% var some = 'pretty';",
" var multiline = 'javascript';",
"%>",
"<% if (name) { %>", // Failing line
" <p><%= name %></p>",
" <p><%= email %></p>",
"<% } %>"
].join("\n");
try {
ejs.render(str)
} catch( err ){
assert.includes(err.message,"name is not defined");
assert.eql(err.name,"ReferenceError");
var lineno = parseInt(err.toString().match(/ejs:(\d+)\n/)[1]);
assert.eql(lineno,6,"Error should been thrown on line 3, was thrown on line "+lineno);
}
}
};

View File

@ -0,0 +1,481 @@
/*
http://www.JSON.org/json2.js
2010-11-17
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See http://www.JSON.org/js.html
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
This file creates a global JSON object containing two methods: stringify
and parse.
JSON.stringify(value, replacer, space)
value any JavaScript value, usually an object or array.
replacer an optional parameter that determines how object
values are stringified for objects. It can be a
function or an array of strings.
space an optional parameter that specifies the indentation
of nested structures. If it is omitted, the text will
be packed without extra whitespace. If it is a number,
it will specify the number of spaces to indent at each
level. If it is a string (such as '\t' or '&nbsp;'),
it contains the characters used to indent at each level.
This method produces a JSON text from a JavaScript value.
When an object value is found, if the object contains a toJSON
method, its toJSON method will be called and the result will be
stringified. A toJSON method does not serialize: it returns the
value represented by the name/value pair that should be serialized,
or undefined if nothing should be serialized. The toJSON method
will be passed the key associated with the value, and this will be
bound to the value
For example, this would serialize Dates as ISO strings.
Date.prototype.toJSON = function (key) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
You can provide an optional replacer method. It will be passed the
key and value of each member, with this bound to the containing
object. The value that is returned from your method will be
serialized. If your method returns undefined, then the member will
be excluded from the serialization.
If the replacer parameter is an array of strings, then it will be
used to select the members to be serialized. It filters the results
such that only members with keys listed in the replacer array are
stringified.
Values that do not have JSON representations, such as undefined or
functions, will not be serialized. Such values in objects will be
dropped; in arrays they will be replaced with null. You can use
a replacer function to replace those with JSON values.
JSON.stringify(undefined) returns undefined.
The optional space parameter produces a stringification of the
value that is filled with line breaks and indentation to make it
easier to read.
If the space parameter is a non-empty string, then that string will
be used for indentation. If the space parameter is a number, then
the indentation will be that many spaces.
Example:
text = JSON.stringify(['e', {pluribus: 'unum'}]);
// text is '["e",{"pluribus":"unum"}]'
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
text = JSON.stringify([new Date()], function (key, value) {
return this[key] instanceof Date ?
'Date(' + this[key] + ')' : value;
});
// text is '["Date(---current time---)"]'
JSON.parse(text, reviver)
This method parses a JSON text to produce an object or array.
It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
Example:
// Parse the text. Values that look like ISO date strings will
// be converted to Date objects.
myData = JSON.parse(text, function (key, value) {
var a;
if (typeof value === 'string') {
a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+a[5], +a[6]));
}
}
return value;
});
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
var d;
if (typeof value === 'string' &&
value.slice(0, 5) === 'Date(' &&
value.slice(-1) === ')') {
d = new Date(value.slice(5, -1));
if (d) {
return d;
}
}
return value;
});
This is a reference implementation. You are free to copy, modify, or
redistribute.
*/
/*jslint evil: true, strict: false, regexp: false */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf
*/
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
var JSON = {};
(function () {
"use strict";
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function (key) {
return isFinite(this.valueOf()) ?
this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z' : null;
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function (key) {
return this.valueOf();
};
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ?
'"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' :
'"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0 ? '[]' :
gap ? '[\n' + gap +
partial.join(',\n' + gap) + '\n' +
mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
k = rep[i];
if (typeof k === 'string') {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' :
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
mind + '}' : '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
};
}
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
text = String(text);
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' ?
walk({'': j}, '') : j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
};
}
}());