1"use strict";
2
3function installCompat() {
4  'use strict';
5
6  /* eslint-disable camelcase */
7
8  // This must be called like `nunjucks.installCompat` so that `this`
9  // references the nunjucks instance
10  var runtime = this.runtime;
11  var lib = this.lib;
12  // Handle slim case where these 'modules' are excluded from the built source
13  var Compiler = this.compiler.Compiler;
14  var Parser = this.parser.Parser;
15  var nodes = this.nodes;
16  var lexer = this.lexer;
17  var orig_contextOrFrameLookup = runtime.contextOrFrameLookup;
18  var orig_memberLookup = runtime.memberLookup;
19  var orig_Compiler_assertType;
20  var orig_Parser_parseAggregate;
21  if (Compiler) {
22    orig_Compiler_assertType = Compiler.prototype.assertType;
23  }
24  if (Parser) {
25    orig_Parser_parseAggregate = Parser.prototype.parseAggregate;
26  }
27  function uninstall() {
28    runtime.contextOrFrameLookup = orig_contextOrFrameLookup;
29    runtime.memberLookup = orig_memberLookup;
30    if (Compiler) {
31      Compiler.prototype.assertType = orig_Compiler_assertType;
32    }
33    if (Parser) {
34      Parser.prototype.parseAggregate = orig_Parser_parseAggregate;
35    }
36  }
37  runtime.contextOrFrameLookup = function contextOrFrameLookup(context, frame, key) {
38    var val = orig_contextOrFrameLookup.apply(this, arguments);
39    if (val !== undefined) {
40      return val;
41    }
42    switch (key) {
43      case 'True':
44        return true;
45      case 'False':
46        return false;
47      case 'None':
48        return null;
49      default:
50        return undefined;
51    }
52  };
53  function getTokensState(tokens) {
54    return {
55      index: tokens.index,
56      lineno: tokens.lineno,
57      colno: tokens.colno
58    };
59  }
60  if (process.env.BUILD_TYPE !== 'SLIM' && nodes && Compiler && Parser) {
61    // i.e., not slim mode
62    var Slice = nodes.Node.extend('Slice', {
63      fields: ['start', 'stop', 'step'],
64      init: function init(lineno, colno, start, stop, step) {
65        start = start || new nodes.Literal(lineno, colno, null);
66        stop = stop || new nodes.Literal(lineno, colno, null);
67        step = step || new nodes.Literal(lineno, colno, 1);
68        this.parent(lineno, colno, start, stop, step);
69      }
70    });
71    Compiler.prototype.assertType = function assertType(node) {
72      if (node instanceof Slice) {
73        return;
74      }
75      orig_Compiler_assertType.apply(this, arguments);
76    };
77    Compiler.prototype.compileSlice = function compileSlice(node, frame) {
78      this._emit('(');
79      this._compileExpression(node.start, frame);
80      this._emit('),(');
81      this._compileExpression(node.stop, frame);
82      this._emit('),(');
83      this._compileExpression(node.step, frame);
84      this._emit(')');
85    };
86    Parser.prototype.parseAggregate = function parseAggregate() {
87      var _this = this;
88      var origState = getTokensState(this.tokens);
89      // Set back one accounting for opening bracket/parens
90      origState.colno--;
91      origState.index--;
92      try {
93        return orig_Parser_parseAggregate.apply(this);
94      } catch (e) {
95        var errState = getTokensState(this.tokens);
96        var rethrow = function rethrow() {
97          lib._assign(_this.tokens, errState);
98          return e;
99        };
100
101        // Reset to state before original parseAggregate called
102        lib._assign(this.tokens, origState);
103        this.peeked = false;
104        var tok = this.peekToken();
105        if (tok.type !== lexer.TOKEN_LEFT_BRACKET) {
106          throw rethrow();
107        } else {
108          this.nextToken();
109        }
110        var node = new Slice(tok.lineno, tok.colno);
111
112        // If we don't encounter a colon while parsing, this is not a slice,
113        // so re-raise the original exception.
114        var isSlice = false;
115        for (var i = 0; i <= node.fields.length; i++) {
116          if (this.skip(lexer.TOKEN_RIGHT_BRACKET)) {
117            break;
118          }
119          if (i === node.fields.length) {
120            if (isSlice) {
121              this.fail('parseSlice: too many slice components', tok.lineno, tok.colno);
122            } else {
123              break;
124            }
125          }
126          if (this.skip(lexer.TOKEN_COLON)) {
127            isSlice = true;
128          } else {
129            var field = node.fields[i];
130            node[field] = this.parseExpression();
131            isSlice = this.skip(lexer.TOKEN_COLON) || isSlice;
132          }
133        }
134        if (!isSlice) {
135          throw rethrow();
136        }
137        return new nodes.Array(tok.lineno, tok.colno, [node]);
138      }
139    };
140  }
141  function sliceLookup(obj, start, stop, step) {
142    obj = obj || [];
143    if (start === null) {
144      start = step < 0 ? obj.length - 1 : 0;
145    }
146    if (stop === null) {
147      stop = step < 0 ? -1 : obj.length;
148    } else if (stop < 0) {
149      stop += obj.length;
150    }
151    if (start < 0) {
152      start += obj.length;
153    }
154    var results = [];
155    for (var i = start;; i += step) {
156      if (i < 0 || i > obj.length) {
157        break;
158      }
159      if (step > 0 && i >= stop) {
160        break;
161      }
162      if (step < 0 && i <= stop) {
163        break;
164      }
165      results.push(runtime.memberLookup(obj, i));
166    }
167    return results;
168  }
169  function hasOwnProp(obj, key) {
170    return Object.prototype.hasOwnProperty.call(obj, key);
171  }
172  var ARRAY_MEMBERS = {
173    pop: function pop(index) {
174      if (index === undefined) {
175        return this.pop();
176      }
177      if (index >= this.length || index < 0) {
178        throw new Error('KeyError');
179      }
180      return this.splice(index, 1);
181    },
182    append: function append(element) {
183      return this.push(element);
184    },
185    remove: function remove(element) {
186      for (var i = 0; i < this.length; i++) {
187        if (this[i] === element) {
188          return this.splice(i, 1);
189        }
190      }
191      throw new Error('ValueError');
192    },
193    count: function count(element) {
194      var count = 0;
195      for (var i = 0; i < this.length; i++) {
196        if (this[i] === element) {
197          count++;
198        }
199      }
200      return count;
201    },
202    index: function index(element) {
203      var i;
204      if ((i = this.indexOf(element)) === -1) {
205        throw new Error('ValueError');
206      }
207      return i;
208    },
209    find: function find(element) {
210      return this.indexOf(element);
211    },
212    insert: function insert(index, elem) {
213      return this.splice(index, 0, elem);
214    }
215  };
216  var OBJECT_MEMBERS = {
217    items: function items() {
218      return lib._entries(this);
219    },
220    values: function values() {
221      return lib._values(this);
222    },
223    keys: function keys() {
224      return lib.keys(this);
225    },
226    get: function get(key, def) {
227      var output = this[key];
228      if (output === undefined) {
229        output = def;
230      }
231      return output;
232    },
233    has_key: function has_key(key) {
234      return hasOwnProp(this, key);
235    },
236    pop: function pop(key, def) {
237      var output = this[key];
238      if (output === undefined && def !== undefined) {
239        output = def;
240      } else if (output === undefined) {
241        throw new Error('KeyError');
242      } else {
243        delete this[key];
244      }
245      return output;
246    },
247    popitem: function popitem() {
248      var keys = lib.keys(this);
249      if (!keys.length) {
250        throw new Error('KeyError');
251      }
252      var k = keys[0];
253      var val = this[k];
254      delete this[k];
255      return [k, val];
256    },
257    setdefault: function setdefault(key, def) {
258      if (def === void 0) {
259        def = null;
260      }
261      if (!(key in this)) {
262        this[key] = def;
263      }
264      return this[key];
265    },
266    update: function update(kwargs) {
267      lib._assign(this, kwargs);
268      return null; // Always returns None
269    }
270  };
271
272  OBJECT_MEMBERS.iteritems = OBJECT_MEMBERS.items;
273  OBJECT_MEMBERS.itervalues = OBJECT_MEMBERS.values;
274  OBJECT_MEMBERS.iterkeys = OBJECT_MEMBERS.keys;
275  runtime.memberLookup = function memberLookup(obj, val, autoescape) {
276    if (arguments.length === 4) {
277      return sliceLookup.apply(this, arguments);
278    }
279    obj = obj || {};
280
281    // If the object is an object, return any of the methods that Python would
282    // otherwise provide.
283    if (lib.isArray(obj) && hasOwnProp(ARRAY_MEMBERS, val)) {
284      return ARRAY_MEMBERS[val].bind(obj);
285    }
286    if (lib.isObject(obj) && hasOwnProp(OBJECT_MEMBERS, val)) {
287      return OBJECT_MEMBERS[val].bind(obj);
288    }
289    return orig_memberLookup.apply(this, arguments);
290  };
291  return uninstall;
292}
293module.exports = installCompat;