1'use strict';
2
3var lib = require('./lib');
4var arrayFrom = Array.from;
5var supportsIterators = typeof Symbol === 'function' && Symbol.iterator && typeof arrayFrom === 'function';
6
7// Frames keep track of scoping both at compile-time and run-time so
8// we know how to access variables. Block tags can introduce special
9// variables, for example.
10var Frame = /*#__PURE__*/function () {
11  function Frame(parent, isolateWrites) {
12    this.variables = Object.create(null);
13    this.parent = parent;
14    this.topLevel = false;
15    // if this is true, writes (set) should never propagate upwards past
16    // this frame to its parent (though reads may).
17    this.isolateWrites = isolateWrites;
18  }
19  var _proto = Frame.prototype;
20  _proto.set = function set(name, val, resolveUp) {
21    // Allow variables with dots by automatically creating the
22    // nested structure
23    var parts = name.split('.');
24    var obj = this.variables;
25    var frame = this;
26    if (resolveUp) {
27      if (frame = this.resolve(parts[0], true)) {
28        frame.set(name, val);
29        return;
30      }
31    }
32    for (var i = 0; i < parts.length - 1; i++) {
33      var id = parts[i];
34      if (!obj[id]) {
35        obj[id] = {};
36      }
37      obj = obj[id];
38    }
39    obj[parts[parts.length - 1]] = val;
40  };
41  _proto.get = function get(name) {
42    var val = this.variables[name];
43    if (val !== undefined) {
44      return val;
45    }
46    return null;
47  };
48  _proto.lookup = function lookup(name) {
49    var p = this.parent;
50    var val = this.variables[name];
51    if (val !== undefined) {
52      return val;
53    }
54    return p && p.lookup(name);
55  };
56  _proto.resolve = function resolve(name, forWrite) {
57    var p = forWrite && this.isolateWrites ? undefined : this.parent;
58    var val = this.variables[name];
59    if (val !== undefined) {
60      return this;
61    }
62    return p && p.resolve(name);
63  };
64  _proto.push = function push(isolateWrites) {
65    return new Frame(this, isolateWrites);
66  };
67  _proto.pop = function pop() {
68    return this.parent;
69  };
70  return Frame;
71}();
72function makeMacro(argNames, kwargNames, func) {
73  return function macro() {
74    for (var _len = arguments.length, macroArgs = new Array(_len), _key = 0; _key < _len; _key++) {
75      macroArgs[_key] = arguments[_key];
76    }
77    var argCount = numArgs(macroArgs);
78    var args;
79    var kwargs = getKeywordArgs(macroArgs);
80    if (argCount > argNames.length) {
81      args = macroArgs.slice(0, argNames.length);
82
83      // Positional arguments that should be passed in as
84      // keyword arguments (essentially default values)
85      macroArgs.slice(args.length, argCount).forEach(function (val, i) {
86        if (i < kwargNames.length) {
87          kwargs[kwargNames[i]] = val;
88        }
89      });
90      args.push(kwargs);
91    } else if (argCount < argNames.length) {
92      args = macroArgs.slice(0, argCount);
93      for (var i = argCount; i < argNames.length; i++) {
94        var arg = argNames[i];
95
96        // Keyword arguments that should be passed as
97        // positional arguments, i.e. the caller explicitly
98        // used the name of a positional arg
99        args.push(kwargs[arg]);
100        delete kwargs[arg];
101      }
102      args.push(kwargs);
103    } else {
104      args = macroArgs;
105    }
106    return func.apply(this, args);
107  };
108}
109function makeKeywordArgs(obj) {
110  obj.__keywords = true;
111  return obj;
112}
113function isKeywordArgs(obj) {
114  return obj && Object.prototype.hasOwnProperty.call(obj, '__keywords');
115}
116function getKeywordArgs(args) {
117  var len = args.length;
118  if (len) {
119    var lastArg = args[len - 1];
120    if (isKeywordArgs(lastArg)) {
121      return lastArg;
122    }
123  }
124  return {};
125}
126function numArgs(args) {
127  var len = args.length;
128  if (len === 0) {
129    return 0;
130  }
131  var lastArg = args[len - 1];
132  if (isKeywordArgs(lastArg)) {
133    return len - 1;
134  } else {
135    return len;
136  }
137}
138
139// A SafeString object indicates that the string should not be
140// autoescaped. This happens magically because autoescaping only
141// occurs on primitive string objects.
142function SafeString(val) {
143  if (typeof val !== 'string') {
144    return val;
145  }
146  this.val = val;
147  this.length = val.length;
148}
149SafeString.prototype = Object.create(String.prototype, {
150  length: {
151    writable: true,
152    configurable: true,
153    value: 0
154  }
155});
156SafeString.prototype.valueOf = function valueOf() {
157  return this.val;
158};
159SafeString.prototype.toString = function toString() {
160  return this.val;
161};
162function copySafeness(dest, target) {
163  if (dest instanceof SafeString) {
164    return new SafeString(target);
165  }
166  return target.toString();
167}
168function markSafe(val) {
169  var type = typeof val;
170  if (type === 'string') {
171    return new SafeString(val);
172  } else if (type !== 'function') {
173    return val;
174  } else {
175    return function wrapSafe(args) {
176      var ret = val.apply(this, arguments);
177      if (typeof ret === 'string') {
178        return new SafeString(ret);
179      }
180      return ret;
181    };
182  }
183}
184function suppressValue(val, autoescape) {
185  val = val !== undefined && val !== null ? val : '';
186  if (autoescape && !(val instanceof SafeString)) {
187    val = lib.escape(val.toString());
188  }
189  return val;
190}
191function ensureDefined(val, lineno, colno) {
192  if (val === null || val === undefined) {
193    throw new lib.TemplateError('attempted to output null or undefined value', lineno + 1, colno + 1);
194  }
195  return val;
196}
197function memberLookup(obj, val) {
198  if (obj === undefined || obj === null) {
199    return undefined;
200  }
201  if (typeof obj[val] === 'function') {
202    return function () {
203      for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
204        args[_key2] = arguments[_key2];
205      }
206      return obj[val].apply(obj, args);
207    };
208  }
209  return obj[val];
210}
211function callWrap(obj, name, context, args) {
212  if (!obj) {
213    throw new Error('Unable to call `' + name + '`, which is undefined or falsey');
214  } else if (typeof obj !== 'function') {
215    throw new Error('Unable to call `' + name + '`, which is not a function');
216  }
217  return obj.apply(context, args);
218}
219function contextOrFrameLookup(context, frame, name) {
220  var val = frame.lookup(name);
221  return val !== undefined ? val : context.lookup(name);
222}
223function handleError(error, lineno, colno) {
224  if (error.lineno) {
225    return error;
226  } else {
227    return new lib.TemplateError(error, lineno, colno);
228  }
229}
230function asyncEach(arr, dimen, iter, cb) {
231  if (lib.isArray(arr)) {
232    var len = arr.length;
233    lib.asyncIter(arr, function iterCallback(item, i, next) {
234      switch (dimen) {
235        case 1:
236          iter(item, i, len, next);
237          break;
238        case 2:
239          iter(item[0], item[1], i, len, next);
240          break;
241        case 3:
242          iter(item[0], item[1], item[2], i, len, next);
243          break;
244        default:
245          item.push(i, len, next);
246          iter.apply(this, item);
247      }
248    }, cb);
249  } else {
250    lib.asyncFor(arr, function iterCallback(key, val, i, len, next) {
251      iter(key, val, i, len, next);
252    }, cb);
253  }
254}
255function asyncAll(arr, dimen, func, cb) {
256  var finished = 0;
257  var len;
258  var outputArr;
259  function done(i, output) {
260    finished++;
261    outputArr[i] = output;
262    if (finished === len) {
263      cb(null, outputArr.join(''));
264    }
265  }
266  if (lib.isArray(arr)) {
267    len = arr.length;
268    outputArr = new Array(len);
269    if (len === 0) {
270      cb(null, '');
271    } else {
272      for (var i = 0; i < arr.length; i++) {
273        var item = arr[i];
274        switch (dimen) {
275          case 1:
276            func(item, i, len, done);
277            break;
278          case 2:
279            func(item[0], item[1], i, len, done);
280            break;
281          case 3:
282            func(item[0], item[1], item[2], i, len, done);
283            break;
284          default:
285            item.push(i, len, done);
286            func.apply(this, item);
287        }
288      }
289    }
290  } else {
291    var keys = lib.keys(arr || {});
292    len = keys.length;
293    outputArr = new Array(len);
294    if (len === 0) {
295      cb(null, '');
296    } else {
297      for (var _i = 0; _i < keys.length; _i++) {
298        var k = keys[_i];
299        func(k, arr[k], _i, len, done);
300      }
301    }
302  }
303}
304function fromIterator(arr) {
305  if (typeof arr !== 'object' || arr === null || lib.isArray(arr)) {
306    return arr;
307  } else if (supportsIterators && Symbol.iterator in arr) {
308    return arrayFrom(arr);
309  } else {
310    return arr;
311  }
312}
313module.exports = {
314  Frame: Frame,
315  makeMacro: makeMacro,
316  makeKeywordArgs: makeKeywordArgs,
317  numArgs: numArgs,
318  suppressValue: suppressValue,
319  ensureDefined: ensureDefined,
320  memberLookup: memberLookup,
321  contextOrFrameLookup: contextOrFrameLookup,
322  callWrap: callWrap,
323  handleError: handleError,
324  isArray: lib.isArray,
325  keys: lib.keys,
326  SafeString: SafeString,
327  copySafeness: copySafeness,
328  markSafe: markSafe,
329  asyncEach: asyncEach,
330  asyncAll: asyncAll,
331  inOperator: lib.inOperator,
332  fromIterator: fromIterator
333};