'use strict';
|
|
var lib = require('./lib');
|
|
var r = require('./runtime');
|
|
var _exports = module.exports = {};
|
|
function normalize(value, defaultValue) {
|
if (value === null || value === undefined || value === false) {
|
return defaultValue;
|
}
|
|
return value;
|
}
|
|
_exports.abs = Math.abs;
|
|
function isNaN(num) {
|
return num !== num; // eslint-disable-line no-self-compare
|
}
|
|
function batch(arr, linecount, fillWith) {
|
var i;
|
var res = [];
|
var tmp = [];
|
|
for (i = 0; i < arr.length; i++) {
|
if (i % linecount === 0 && tmp.length) {
|
res.push(tmp);
|
tmp = [];
|
}
|
|
tmp.push(arr[i]);
|
}
|
|
if (tmp.length) {
|
if (fillWith) {
|
for (i = tmp.length; i < linecount; i++) {
|
tmp.push(fillWith);
|
}
|
}
|
|
res.push(tmp);
|
}
|
|
return res;
|
}
|
|
_exports.batch = batch;
|
|
function capitalize(str) {
|
str = normalize(str, '');
|
var ret = str.toLowerCase();
|
return r.copySafeness(str, ret.charAt(0).toUpperCase() + ret.slice(1));
|
}
|
|
_exports.capitalize = capitalize;
|
|
function center(str, width) {
|
str = normalize(str, '');
|
width = width || 80;
|
|
if (str.length >= width) {
|
return str;
|
}
|
|
var spaces = width - str.length;
|
var pre = lib.repeat(' ', spaces / 2 - spaces % 2);
|
var post = lib.repeat(' ', spaces / 2);
|
return r.copySafeness(str, pre + str + post);
|
}
|
|
_exports.center = center;
|
|
function default_(val, def, bool) {
|
if (bool) {
|
return val || def;
|
} else {
|
return val !== undefined ? val : def;
|
}
|
} // TODO: it is confusing to export something called 'default'
|
|
|
_exports['default'] = default_; // eslint-disable-line dot-notation
|
|
function dictsort(val, caseSensitive, by) {
|
if (!lib.isObject(val)) {
|
throw new lib.TemplateError('dictsort filter: val must be an object');
|
}
|
|
var array = []; // deliberately include properties from the object's prototype
|
|
for (var k in val) {
|
// eslint-disable-line guard-for-in, no-restricted-syntax
|
array.push([k, val[k]]);
|
}
|
|
var si;
|
|
if (by === undefined || by === 'key') {
|
si = 0;
|
} else if (by === 'value') {
|
si = 1;
|
} else {
|
throw new lib.TemplateError('dictsort filter: You can only sort by either key or value');
|
}
|
|
array.sort(function (t1, t2) {
|
var a = t1[si];
|
var b = t2[si];
|
|
if (!caseSensitive) {
|
if (lib.isString(a)) {
|
a = a.toUpperCase();
|
}
|
|
if (lib.isString(b)) {
|
b = b.toUpperCase();
|
}
|
}
|
|
return a > b ? 1 : a === b ? 0 : -1; // eslint-disable-line no-nested-ternary
|
});
|
return array;
|
}
|
|
_exports.dictsort = dictsort;
|
|
function dump(obj, spaces) {
|
return JSON.stringify(obj, null, spaces);
|
}
|
|
_exports.dump = dump;
|
|
function escape(str) {
|
if (str instanceof r.SafeString) {
|
return str;
|
}
|
|
str = str === null || str === undefined ? '' : str;
|
return r.markSafe(lib.escape(str.toString()));
|
}
|
|
_exports.escape = escape;
|
|
function safe(str) {
|
if (str instanceof r.SafeString) {
|
return str;
|
}
|
|
str = str === null || str === undefined ? '' : str;
|
return r.markSafe(str.toString());
|
}
|
|
_exports.safe = safe;
|
|
function first(arr) {
|
return arr[0];
|
}
|
|
_exports.first = first;
|
|
function forceescape(str) {
|
str = str === null || str === undefined ? '' : str;
|
return r.markSafe(lib.escape(str.toString()));
|
}
|
|
_exports.forceescape = forceescape;
|
|
function groupby(arr, attr) {
|
return lib.groupBy(arr, attr);
|
}
|
|
_exports.groupby = groupby;
|
|
function indent(str, width, indentfirst) {
|
str = normalize(str, '');
|
|
if (str === '') {
|
return '';
|
}
|
|
width = width || 4; // let res = '';
|
|
var lines = str.split('\n');
|
var sp = lib.repeat(' ', width);
|
var res = lines.map(function (l, i) {
|
return i === 0 && !indentfirst ? l + "\n" : "" + sp + l + "\n";
|
}).join('');
|
return r.copySafeness(str, res);
|
}
|
|
_exports.indent = indent;
|
|
function join(arr, del, attr) {
|
del = del || '';
|
|
if (attr) {
|
arr = lib.map(arr, function (v) {
|
return v[attr];
|
});
|
}
|
|
return arr.join(del);
|
}
|
|
_exports.join = join;
|
|
function last(arr) {
|
return arr[arr.length - 1];
|
}
|
|
_exports.last = last;
|
|
function lengthFilter(val) {
|
var value = normalize(val, '');
|
|
if (value !== undefined) {
|
if (typeof Map === 'function' && value instanceof Map || typeof Set === 'function' && value instanceof Set) {
|
// ECMAScript 2015 Maps and Sets
|
return value.size;
|
}
|
|
if (lib.isObject(value) && !(value instanceof r.SafeString)) {
|
// Objects (besides SafeStrings), non-primative Arrays
|
return lib.keys(value).length;
|
}
|
|
return value.length;
|
}
|
|
return 0;
|
}
|
|
_exports.length = lengthFilter;
|
|
function list(val) {
|
if (lib.isString(val)) {
|
return val.split('');
|
} else if (lib.isObject(val)) {
|
return lib._entries(val || {}).map(function (_ref) {
|
var key = _ref[0],
|
value = _ref[1];
|
return {
|
key: key,
|
value: value
|
};
|
});
|
} else if (lib.isArray(val)) {
|
return val;
|
} else {
|
throw new lib.TemplateError('list filter: type not iterable');
|
}
|
}
|
|
_exports.list = list;
|
|
function lower(str) {
|
str = normalize(str, '');
|
return str.toLowerCase();
|
}
|
|
_exports.lower = lower;
|
|
function nl2br(str) {
|
if (str === null || str === undefined) {
|
return '';
|
}
|
|
return r.copySafeness(str, str.replace(/\r\n|\n/g, '<br />\n'));
|
}
|
|
_exports.nl2br = nl2br;
|
|
function random(arr) {
|
return arr[Math.floor(Math.random() * arr.length)];
|
}
|
|
_exports.random = random;
|
|
function rejectattr(arr, attr) {
|
return arr.filter(function (item) {
|
return !item[attr];
|
});
|
}
|
|
_exports.rejectattr = rejectattr;
|
|
function selectattr(arr, attr) {
|
return arr.filter(function (item) {
|
return !!item[attr];
|
});
|
}
|
|
_exports.selectattr = selectattr;
|
|
function replace(str, old, new_, maxCount) {
|
var originalStr = str;
|
|
if (old instanceof RegExp) {
|
return str.replace(old, new_);
|
}
|
|
if (typeof maxCount === 'undefined') {
|
maxCount = -1;
|
}
|
|
var res = ''; // Output
|
// Cast Numbers in the search term to string
|
|
if (typeof old === 'number') {
|
old = '' + old;
|
} else if (typeof old !== 'string') {
|
// If it is something other than number or string,
|
// return the original string
|
return str;
|
} // Cast numbers in the replacement to string
|
|
|
if (typeof str === 'number') {
|
str = '' + str;
|
} // If by now, we don't have a string, throw it back
|
|
|
if (typeof str !== 'string' && !(str instanceof r.SafeString)) {
|
return str;
|
} // ShortCircuits
|
|
|
if (old === '') {
|
// Mimic the python behaviour: empty string is replaced
|
// by replacement e.g. "abc"|replace("", ".") -> .a.b.c.
|
res = new_ + str.split('').join(new_) + new_;
|
return r.copySafeness(str, res);
|
}
|
|
var nextIndex = str.indexOf(old); // if # of replacements to perform is 0, or the string to does
|
// not contain the old value, return the string
|
|
if (maxCount === 0 || nextIndex === -1) {
|
return str;
|
}
|
|
var pos = 0;
|
var count = 0; // # of replacements made
|
|
while (nextIndex > -1 && (maxCount === -1 || count < maxCount)) {
|
// Grab the next chunk of src string and add it with the
|
// replacement, to the result
|
res += str.substring(pos, nextIndex) + new_; // Increment our pointer in the src string
|
|
pos = nextIndex + old.length;
|
count++; // See if there are any more replacements to be made
|
|
nextIndex = str.indexOf(old, pos);
|
} // We've either reached the end, or done the max # of
|
// replacements, tack on any remaining string
|
|
|
if (pos < str.length) {
|
res += str.substring(pos);
|
}
|
|
return r.copySafeness(originalStr, res);
|
}
|
|
_exports.replace = replace;
|
|
function reverse(val) {
|
var arr;
|
|
if (lib.isString(val)) {
|
arr = list(val);
|
} else {
|
// Copy it
|
arr = lib.map(val, function (v) {
|
return v;
|
});
|
}
|
|
arr.reverse();
|
|
if (lib.isString(val)) {
|
return r.copySafeness(val, arr.join(''));
|
}
|
|
return arr;
|
}
|
|
_exports.reverse = reverse;
|
|
function round(val, precision, method) {
|
precision = precision || 0;
|
var factor = Math.pow(10, precision);
|
var rounder;
|
|
if (method === 'ceil') {
|
rounder = Math.ceil;
|
} else if (method === 'floor') {
|
rounder = Math.floor;
|
} else {
|
rounder = Math.round;
|
}
|
|
return rounder(val * factor) / factor;
|
}
|
|
_exports.round = round;
|
|
function slice(arr, slices, fillWith) {
|
var sliceLength = Math.floor(arr.length / slices);
|
var extra = arr.length % slices;
|
var res = [];
|
var offset = 0;
|
|
for (var i = 0; i < slices; i++) {
|
var start = offset + i * sliceLength;
|
|
if (i < extra) {
|
offset++;
|
}
|
|
var end = offset + (i + 1) * sliceLength;
|
var currSlice = arr.slice(start, end);
|
|
if (fillWith && i >= extra) {
|
currSlice.push(fillWith);
|
}
|
|
res.push(currSlice);
|
}
|
|
return res;
|
}
|
|
_exports.slice = slice;
|
|
function sum(arr, attr, start) {
|
if (start === void 0) {
|
start = 0;
|
}
|
|
if (attr) {
|
arr = lib.map(arr, function (v) {
|
return v[attr];
|
});
|
}
|
|
return start + arr.reduce(function (a, b) {
|
return a + b;
|
}, 0);
|
}
|
|
_exports.sum = sum;
|
_exports.sort = r.makeMacro(['value', 'reverse', 'case_sensitive', 'attribute'], [], function (arr, reversed, caseSens, attr) {
|
// Copy it
|
var array = lib.map(arr, function (v) {
|
return v;
|
});
|
array.sort(function (a, b) {
|
var x = attr ? a[attr] : a;
|
var y = attr ? b[attr] : b;
|
|
if (!caseSens && lib.isString(x) && lib.isString(y)) {
|
x = x.toLowerCase();
|
y = y.toLowerCase();
|
}
|
|
if (x < y) {
|
return reversed ? 1 : -1;
|
} else if (x > y) {
|
return reversed ? -1 : 1;
|
} else {
|
return 0;
|
}
|
});
|
return array;
|
});
|
|
function string(obj) {
|
return r.copySafeness(obj, obj);
|
}
|
|
_exports.string = string;
|
|
function striptags(input, preserveLinebreaks) {
|
input = normalize(input, '');
|
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>|<!--[\s\S]*?-->/gi;
|
var trimmedInput = trim(input.replace(tags, ''));
|
var res = '';
|
|
if (preserveLinebreaks) {
|
res = trimmedInput.replace(/^ +| +$/gm, '') // remove leading and trailing spaces
|
.replace(/ +/g, ' ') // squash adjacent spaces
|
.replace(/(\r\n)/g, '\n') // normalize linebreaks (CRLF -> LF)
|
.replace(/\n\n\n+/g, '\n\n'); // squash abnormal adjacent linebreaks
|
} else {
|
res = trimmedInput.replace(/\s+/gi, ' ');
|
}
|
|
return r.copySafeness(input, res);
|
}
|
|
_exports.striptags = striptags;
|
|
function title(str) {
|
str = normalize(str, '');
|
var words = str.split(' ').map(function (word) {
|
return capitalize(word);
|
});
|
return r.copySafeness(str, words.join(' '));
|
}
|
|
_exports.title = title;
|
|
function trim(str) {
|
return r.copySafeness(str, str.replace(/^\s*|\s*$/g, ''));
|
}
|
|
_exports.trim = trim;
|
|
function truncate(input, length, killwords, end) {
|
var orig = input;
|
input = normalize(input, '');
|
length = length || 255;
|
|
if (input.length <= length) {
|
return input;
|
}
|
|
if (killwords) {
|
input = input.substring(0, length);
|
} else {
|
var idx = input.lastIndexOf(' ', length);
|
|
if (idx === -1) {
|
idx = length;
|
}
|
|
input = input.substring(0, idx);
|
}
|
|
input += end !== undefined && end !== null ? end : '...';
|
return r.copySafeness(orig, input);
|
}
|
|
_exports.truncate = truncate;
|
|
function upper(str) {
|
str = normalize(str, '');
|
return str.toUpperCase();
|
}
|
|
_exports.upper = upper;
|
|
function urlencode(obj) {
|
var enc = encodeURIComponent;
|
|
if (lib.isString(obj)) {
|
return enc(obj);
|
} else {
|
var keyvals = lib.isArray(obj) ? obj : lib._entries(obj);
|
return keyvals.map(function (_ref2) {
|
var k = _ref2[0],
|
v = _ref2[1];
|
return enc(k) + "=" + enc(v);
|
}).join('&');
|
}
|
}
|
|
_exports.urlencode = urlencode; // For the jinja regexp, see
|
// https://github.com/mitsuhiko/jinja2/blob/f15b814dcba6aa12bc74d1f7d0c881d55f7126be/jinja2/utils.py#L20-L23
|
|
var puncRe = /^(?:\(|<|<)?(.*?)(?:\.|,|\)|\n|>)?$/; // from http://blog.gerv.net/2011/05/html5_email_address_regexp/
|
|
var emailRe = /^[\w.!#$%&'*+\-\/=?\^`{|}~]+@[a-z\d\-]+(\.[a-z\d\-]+)+$/i;
|
var httpHttpsRe = /^https?:\/\/.*$/;
|
var wwwRe = /^www\./;
|
var tldRe = /\.(?:org|net|com)(?:\:|\/|$)/;
|
|
function urlize(str, length, nofollow) {
|
if (isNaN(length)) {
|
length = Infinity;
|
}
|
|
var noFollowAttr = nofollow === true ? ' rel="nofollow"' : '';
|
var words = str.split(/(\s+)/).filter(function (word) {
|
// If the word has no length, bail. This can happen for str with
|
// trailing whitespace.
|
return word && word.length;
|
}).map(function (word) {
|
var matches = word.match(puncRe);
|
var possibleUrl = matches ? matches[1] : word;
|
var shortUrl = possibleUrl.substr(0, length); // url that starts with http or https
|
|
if (httpHttpsRe.test(possibleUrl)) {
|
return "<a href=\"" + possibleUrl + "\"" + noFollowAttr + ">" + shortUrl + "</a>";
|
} // url that starts with www.
|
|
|
if (wwwRe.test(possibleUrl)) {
|
return "<a href=\"http://" + possibleUrl + "\"" + noFollowAttr + ">" + shortUrl + "</a>";
|
} // an email address of the form username@domain.tld
|
|
|
if (emailRe.test(possibleUrl)) {
|
return "<a href=\"mailto:" + possibleUrl + "\">" + possibleUrl + "</a>";
|
} // url that ends in .com, .org or .net that is not an email address
|
|
|
if (tldRe.test(possibleUrl)) {
|
return "<a href=\"http://" + possibleUrl + "\"" + noFollowAttr + ">" + shortUrl + "</a>";
|
}
|
|
return word;
|
});
|
return words.join('');
|
}
|
|
_exports.urlize = urlize;
|
|
function wordcount(str) {
|
str = normalize(str, '');
|
var words = str ? str.match(/\w+/g) : null;
|
return words ? words.length : null;
|
}
|
|
_exports.wordcount = wordcount;
|
|
function float(val, def) {
|
var res = parseFloat(val);
|
return isNaN(res) ? def : res;
|
}
|
|
_exports.float = float;
|
|
function int(val, def) {
|
var res = parseInt(val, 10);
|
return isNaN(res) ? def : res;
|
}
|
|
_exports.int = int; // Aliases
|
|
_exports.d = _exports.default;
|
_exports.e = _exports.escape;
|