1'use strict';
2
3var lib = require('./lib');
4var r = require('./runtime');
5var _exports = module.exports = {};
6function normalize(value, defaultValue) {
7  if (value === null || value === undefined || value === false) {
8    return defaultValue;
9  }
10  return value;
11}
12_exports.abs = Math.abs;
13function isNaN(num) {
14  return num !== num; // eslint-disable-line no-self-compare
15}
16
17function batch(arr, linecount, fillWith) {
18  var i;
19  var res = [];
20  var tmp = [];
21  for (i = 0; i < arr.length; i++) {
22    if (i % linecount === 0 && tmp.length) {
23      res.push(tmp);
24      tmp = [];
25    }
26    tmp.push(arr[i]);
27  }
28  if (tmp.length) {
29    if (fillWith) {
30      for (i = tmp.length; i < linecount; i++) {
31        tmp.push(fillWith);
32      }
33    }
34    res.push(tmp);
35  }
36  return res;
37}
38_exports.batch = batch;
39function capitalize(str) {
40  str = normalize(str, '');
41  var ret = str.toLowerCase();
42  return r.copySafeness(str, ret.charAt(0).toUpperCase() + ret.slice(1));
43}
44_exports.capitalize = capitalize;
45function center(str, width) {
46  str = normalize(str, '');
47  width = width || 80;
48  if (str.length >= width) {
49    return str;
50  }
51  var spaces = width - str.length;
52  var pre = lib.repeat(' ', spaces / 2 - spaces % 2);
53  var post = lib.repeat(' ', spaces / 2);
54  return r.copySafeness(str, pre + str + post);
55}
56_exports.center = center;
57function default_(val, def, bool) {
58  if (bool) {
59    return val || def;
60  } else {
61    return val !== undefined ? val : def;
62  }
63}
64
65// TODO: it is confusing to export something called 'default'
66_exports['default'] = default_; // eslint-disable-line dot-notation
67
68function dictsort(val, caseSensitive, by) {
69  if (!lib.isObject(val)) {
70    throw new lib.TemplateError('dictsort filter: val must be an object');
71  }
72  var array = [];
73  // deliberately include properties from the object's prototype
74  for (var k in val) {
75    // eslint-disable-line guard-for-in, no-restricted-syntax
76    array.push([k, val[k]]);
77  }
78  var si;
79  if (by === undefined || by === 'key') {
80    si = 0;
81  } else if (by === 'value') {
82    si = 1;
83  } else {
84    throw new lib.TemplateError('dictsort filter: You can only sort by either key or value');
85  }
86  array.sort(function (t1, t2) {
87    var a = t1[si];
88    var b = t2[si];
89    if (!caseSensitive) {
90      if (lib.isString(a)) {
91        a = a.toUpperCase();
92      }
93      if (lib.isString(b)) {
94        b = b.toUpperCase();
95      }
96    }
97    return a > b ? 1 : a === b ? 0 : -1; // eslint-disable-line no-nested-ternary
98  });
99
100  return array;
101}
102_exports.dictsort = dictsort;
103function dump(obj, spaces) {
104  return JSON.stringify(obj, null, spaces);
105}
106_exports.dump = dump;
107function escape(str) {
108  if (str instanceof r.SafeString) {
109    return str;
110  }
111  str = str === null || str === undefined ? '' : str;
112  return r.markSafe(lib.escape(str.toString()));
113}
114_exports.escape = escape;
115function safe(str) {
116  if (str instanceof r.SafeString) {
117    return str;
118  }
119  str = str === null || str === undefined ? '' : str;
120  return r.markSafe(str.toString());
121}
122_exports.safe = safe;
123function first(arr) {
124  return arr[0];
125}
126_exports.first = first;
127function forceescape(str) {
128  str = str === null || str === undefined ? '' : str;
129  return r.markSafe(lib.escape(str.toString()));
130}
131_exports.forceescape = forceescape;
132function groupby(arr, attr) {
133  return lib.groupBy(arr, attr, this.env.opts.throwOnUndefined);
134}
135_exports.groupby = groupby;
136function indent(str, width, indentfirst) {
137  str = normalize(str, '');
138  if (str === '') {
139    return '';
140  }
141  width = width || 4;
142  // let res = '';
143  var lines = str.split('\n');
144  var sp = lib.repeat(' ', width);
145  var res = lines.map(function (l, i) {
146    return i === 0 && !indentfirst ? l : "" + sp + l;
147  }).join('\n');
148  return r.copySafeness(str, res);
149}
150_exports.indent = indent;
151function join(arr, del, attr) {
152  del = del || '';
153  if (attr) {
154    arr = lib.map(arr, function (v) {
155      return v[attr];
156    });
157  }
158  return arr.join(del);
159}
160_exports.join = join;
161function last(arr) {
162  return arr[arr.length - 1];
163}
164_exports.last = last;
165function lengthFilter(val) {
166  var value = normalize(val, '');
167  if (value !== undefined) {
168    if (typeof Map === 'function' && value instanceof Map || typeof Set === 'function' && value instanceof Set) {
169      // ECMAScript 2015 Maps and Sets
170      return value.size;
171    }
172    if (lib.isObject(value) && !(value instanceof r.SafeString)) {
173      // Objects (besides SafeStrings), non-primative Arrays
174      return lib.keys(value).length;
175    }
176    return value.length;
177  }
178  return 0;
179}
180_exports.length = lengthFilter;
181function list(val) {
182  if (lib.isString(val)) {
183    return val.split('');
184  } else if (lib.isObject(val)) {
185    return lib._entries(val || {}).map(function (_ref) {
186      var key = _ref[0],
187        value = _ref[1];
188      return {
189        key: key,
190        value: value
191      };
192    });
193  } else if (lib.isArray(val)) {
194    return val;
195  } else {
196    throw new lib.TemplateError('list filter: type not iterable');
197  }
198}
199_exports.list = list;
200function lower(str) {
201  str = normalize(str, '');
202  return str.toLowerCase();
203}
204_exports.lower = lower;
205function nl2br(str) {
206  if (str === null || str === undefined) {
207    return '';
208  }
209  return r.copySafeness(str, str.replace(/\r\n|\n/g, '<br />\n'));
210}
211_exports.nl2br = nl2br;
212function random(arr) {
213  return arr[Math.floor(Math.random() * arr.length)];
214}
215_exports.random = random;
216
217/**
218 * Construct select or reject filter
219 *
220 * @param {boolean} expectedTestResult
221 * @returns {function(array, string, *): array}
222 */
223function getSelectOrReject(expectedTestResult) {
224  function filter(arr, testName, secondArg) {
225    if (testName === void 0) {
226      testName = 'truthy';
227    }
228    var context = this;
229    var test = context.env.getTest(testName);
230    return lib.toArray(arr).filter(function examineTestResult(item) {
231      return test.call(context, item, secondArg) === expectedTestResult;
232    });
233  }
234  return filter;
235}
236_exports.reject = getSelectOrReject(false);
237function rejectattr(arr, attr) {
238  return arr.filter(function (item) {
239    return !item[attr];
240  });
241}
242_exports.rejectattr = rejectattr;
243_exports.select = getSelectOrReject(true);
244function selectattr(arr, attr) {
245  return arr.filter(function (item) {
246    return !!item[attr];
247  });
248}
249_exports.selectattr = selectattr;
250function replace(str, old, new_, maxCount) {
251  var originalStr = str;
252  if (old instanceof RegExp) {
253    return str.replace(old, new_);
254  }
255  if (typeof maxCount === 'undefined') {
256    maxCount = -1;
257  }
258  var res = ''; // Output
259
260  // Cast Numbers in the search term to string
261  if (typeof old === 'number') {
262    old = '' + old;
263  } else if (typeof old !== 'string') {
264    // If it is something other than number or string,
265    // return the original string
266    return str;
267  }
268
269  // Cast numbers in the replacement to string
270  if (typeof str === 'number') {
271    str = '' + str;
272  }
273
274  // If by now, we don't have a string, throw it back
275  if (typeof str !== 'string' && !(str instanceof r.SafeString)) {
276    return str;
277  }
278
279  // ShortCircuits
280  if (old === '') {
281    // Mimic the python behaviour: empty string is replaced
282    // by replacement e.g. "abc"|replace("", ".") -> .a.b.c.
283    res = new_ + str.split('').join(new_) + new_;
284    return r.copySafeness(str, res);
285  }
286  var nextIndex = str.indexOf(old);
287  // if # of replacements to perform is 0, or the string to does
288  // not contain the old value, return the string
289  if (maxCount === 0 || nextIndex === -1) {
290    return str;
291  }
292  var pos = 0;
293  var count = 0; // # of replacements made
294
295  while (nextIndex > -1 && (maxCount === -1 || count < maxCount)) {
296    // Grab the next chunk of src string and add it with the
297    // replacement, to the result
298    res += str.substring(pos, nextIndex) + new_;
299    // Increment our pointer in the src string
300    pos = nextIndex + old.length;
301    count++;
302    // See if there are any more replacements to be made
303    nextIndex = str.indexOf(old, pos);
304  }
305
306  // We've either reached the end, or done the max # of
307  // replacements, tack on any remaining string
308  if (pos < str.length) {
309    res += str.substring(pos);
310  }
311  return r.copySafeness(originalStr, res);
312}
313_exports.replace = replace;
314function reverse(val) {
315  var arr;
316  if (lib.isString(val)) {
317    arr = list(val);
318  } else {
319    // Copy it
320    arr = lib.map(val, function (v) {
321      return v;
322    });
323  }
324  arr.reverse();
325  if (lib.isString(val)) {
326    return r.copySafeness(val, arr.join(''));
327  }
328  return arr;
329}
330_exports.reverse = reverse;
331function round(val, precision, method) {
332  precision = precision || 0;
333  var factor = Math.pow(10, precision);
334  var rounder;
335  if (method === 'ceil') {
336    rounder = Math.ceil;
337  } else if (method === 'floor') {
338    rounder = Math.floor;
339  } else {
340    rounder = Math.round;
341  }
342  return rounder(val * factor) / factor;
343}
344_exports.round = round;
345function slice(arr, slices, fillWith) {
346  var sliceLength = Math.floor(arr.length / slices);
347  var extra = arr.length % slices;
348  var res = [];
349  var offset = 0;
350  for (var i = 0; i < slices; i++) {
351    var start = offset + i * sliceLength;
352    if (i < extra) {
353      offset++;
354    }
355    var end = offset + (i + 1) * sliceLength;
356    var currSlice = arr.slice(start, end);
357    if (fillWith && i >= extra) {
358      currSlice.push(fillWith);
359    }
360    res.push(currSlice);
361  }
362  return res;
363}
364_exports.slice = slice;
365function sum(arr, attr, start) {
366  if (start === void 0) {
367    start = 0;
368  }
369  if (attr) {
370    arr = lib.map(arr, function (v) {
371      return v[attr];
372    });
373  }
374  return start + arr.reduce(function (a, b) {
375    return a + b;
376  }, 0);
377}
378_exports.sum = sum;
379_exports.sort = r.makeMacro(['value', 'reverse', 'case_sensitive', 'attribute'], [], function sortFilter(arr, reversed, caseSens, attr) {
380  var _this = this;
381  // Copy it
382  var array = lib.map(arr, function (v) {
383    return v;
384  });
385  var getAttribute = lib.getAttrGetter(attr);
386  array.sort(function (a, b) {
387    var x = attr ? getAttribute(a) : a;
388    var y = attr ? getAttribute(b) : b;
389    if (_this.env.opts.throwOnUndefined && attr && (x === undefined || y === undefined)) {
390      throw new TypeError("sort: attribute \"" + attr + "\" resolved to undefined");
391    }
392    if (!caseSens && lib.isString(x) && lib.isString(y)) {
393      x = x.toLowerCase();
394      y = y.toLowerCase();
395    }
396    if (x < y) {
397      return reversed ? 1 : -1;
398    } else if (x > y) {
399      return reversed ? -1 : 1;
400    } else {
401      return 0;
402    }
403  });
404  return array;
405});
406function string(obj) {
407  return r.copySafeness(obj, obj);
408}
409_exports.string = string;
410function striptags(input, preserveLinebreaks) {
411  input = normalize(input, '');
412  var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>|<!--[\s\S]*?-->/gi;
413  var trimmedInput = trim(input.replace(tags, ''));
414  var res = '';
415  if (preserveLinebreaks) {
416    res = trimmedInput.replace(/^ +| +$/gm, '') // remove leading and trailing spaces
417    .replace(/ +/g, ' ') // squash adjacent spaces
418    .replace(/(\r\n)/g, '\n') // normalize linebreaks (CRLF -> LF)
419    .replace(/\n\n\n+/g, '\n\n'); // squash abnormal adjacent linebreaks
420  } else {
421    res = trimmedInput.replace(/\s+/gi, ' ');
422  }
423  return r.copySafeness(input, res);
424}
425_exports.striptags = striptags;
426function title(str) {
427  str = normalize(str, '');
428  var words = str.split(' ').map(function (word) {
429    return capitalize(word);
430  });
431  return r.copySafeness(str, words.join(' '));
432}
433_exports.title = title;
434function trim(str) {
435  return r.copySafeness(str, str.replace(/^\s*|\s*$/g, ''));
436}
437_exports.trim = trim;
438function truncate(input, length, killwords, end) {
439  var orig = input;
440  input = normalize(input, '');
441  length = length || 255;
442  if (input.length <= length) {
443    return input;
444  }
445  if (killwords) {
446    input = input.substring(0, length);
447  } else {
448    var idx = input.lastIndexOf(' ', length);
449    if (idx === -1) {
450      idx = length;
451    }
452    input = input.substring(0, idx);
453  }
454  input += end !== undefined && end !== null ? end : '...';
455  return r.copySafeness(orig, input);
456}
457_exports.truncate = truncate;
458function upper(str) {
459  str = normalize(str, '');
460  return str.toUpperCase();
461}
462_exports.upper = upper;
463function urlencode(obj) {
464  var enc = encodeURIComponent;
465  if (lib.isString(obj)) {
466    return enc(obj);
467  } else {
468    var keyvals = lib.isArray(obj) ? obj : lib._entries(obj);
469    return keyvals.map(function (_ref2) {
470      var k = _ref2[0],
471        v = _ref2[1];
472      return enc(k) + "=" + enc(v);
473    }).join('&');
474  }
475}
476_exports.urlencode = urlencode;
477
478// For the jinja regexp, see
479// https://github.com/mitsuhiko/jinja2/blob/f15b814dcba6aa12bc74d1f7d0c881d55f7126be/jinja2/utils.py#L20-L23
480var puncRe = /^(?:\(|<|&lt;)?(.*?)(?:\.|,|\)|\n|&gt;)?$/;
481// from http://blog.gerv.net/2011/05/html5_email_address_regexp/
482var emailRe = /^[\w.!#$%&'*+\-\/=?\^`{|}~]+@[a-z\d\-]+(\.[a-z\d\-]+)+$/i;
483var httpHttpsRe = /^https?:\/\/.*$/;
484var wwwRe = /^www\./;
485var tldRe = /\.(?:org|net|com)(?:\:|\/|$)/;
486function urlize(str, length, nofollow) {
487  if (isNaN(length)) {
488    length = Infinity;
489  }
490  var noFollowAttr = nofollow === true ? ' rel="nofollow"' : '';
491  var words = str.split(/(\s+)/).filter(function (word) {
492    // If the word has no length, bail. This can happen for str with
493    // trailing whitespace.
494    return word && word.length;
495  }).map(function (word) {
496    var matches = word.match(puncRe);
497    var possibleUrl = matches ? matches[1] : word;
498    var shortUrl = possibleUrl.substr(0, length);
499
500    // url that starts with http or https
501    if (httpHttpsRe.test(possibleUrl)) {
502      return "<a href=\"" + possibleUrl + "\"" + noFollowAttr + ">" + shortUrl + "</a>";
503    }
504
505    // url that starts with www.
506    if (wwwRe.test(possibleUrl)) {
507      return "<a href=\"http://" + possibleUrl + "\"" + noFollowAttr + ">" + shortUrl + "</a>";
508    }
509
510    // an email address of the form username@domain.tld
511    if (emailRe.test(possibleUrl)) {
512      return "<a href=\"mailto:" + possibleUrl + "\">" + possibleUrl + "</a>";
513    }
514
515    // url that ends in .com, .org or .net that is not an email address
516    if (tldRe.test(possibleUrl)) {
517      return "<a href=\"http://" + possibleUrl + "\"" + noFollowAttr + ">" + shortUrl + "</a>";
518    }
519    return word;
520  });
521  return words.join('');
522}
523_exports.urlize = urlize;
524function wordcount(str) {
525  str = normalize(str, '');
526  var words = str ? str.match(/\w+/g) : null;
527  return words ? words.length : null;
528}
529_exports.wordcount = wordcount;
530function float(val, def) {
531  var res = parseFloat(val);
532  return isNaN(res) ? def : res;
533}
534_exports.float = float;
535var intFilter = r.makeMacro(['value', 'default', 'base'], [], function doInt(value, defaultValue, base) {
536  if (base === void 0) {
537    base = 10;
538  }
539  var res = parseInt(value, base);
540  return isNaN(res) ? defaultValue : res;
541});
542_exports.int = intFilter;
543
544// Aliases
545_exports.d = _exports.default;
546_exports.e = _exports.escape;