(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
	typeof define === 'function' && define.amd ? define('acorn/dist/acorn',['exports'], factory) :
	(factory((global.acorn = global.acorn || {})));
}(this, (function (exports) { 'use strict';

// Reserved word lists for various dialects of the language

var reservedWords = {
  3: "abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile",
  5: "class enum extends super const export import",
  6: "enum",
  strict: "implements interface let package private protected public static yield",
  strictBind: "eval arguments"
};

// And the keywords

var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this";

var keywords = {
  5: ecma5AndLessKeywords,
  6: ecma5AndLessKeywords + " const class extends export import super"
};

// ## Character categories

// Big ugly regular expressions that match characters in the
// whitespace, identifier, and identifier-start categories. These
// are only applied when a character is found to actually have a
// code point above 128.
// Generated by `bin/generate-identifier-regex.js`.

var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0-\u08b4\u08b6-\u08bd\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fd5\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7ae\ua7b0-\ua7b7\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab65\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc";
var nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d4-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d01-\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1cf8\u1cf9\u1dc0-\u1df5\u1dfb-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f";

var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");

nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;

// These are a run-length and offset encoded representation of the
// >0xffff code points that are a valid part of identifiers. The
// offset starts at 0x10000, and each pair of numbers represents an
// offset to the next range, and then a size of the range. They were
// generated by bin/generate-identifier-regex.js

// eslint-disable-next-line comma-spacing
var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,19,35,5,35,5,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,66,18,2,1,11,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,56,50,14,50,785,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,2,0,36,17,2,24,85,6,2,0,2,3,2,14,2,9,8,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,19,0,13,4,159,52,19,3,54,47,21,1,2,0,185,46,42,3,37,47,21,0,60,42,86,25,391,63,32,0,449,56,264,8,2,36,18,0,50,29,881,921,103,110,18,195,2749,1070,4050,582,8634,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,65,0,32,6124,20,754,9486,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,60,67,1213,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,3,5761,10591,541];

// eslint-disable-next-line comma-spacing
var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,10,2,4,9,83,11,7,0,161,11,6,9,7,3,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,193,17,10,9,87,19,13,9,214,6,3,8,28,1,83,16,16,9,82,12,9,9,84,14,5,9,423,9,838,7,2,7,17,9,57,21,2,13,19882,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,513,54,5,49,9,0,15,0,23,4,2,14,1361,6,2,16,3,6,2,1,2,4,2214,6,110,6,6,9,792487,239];

// This has a complexity linear to the value of the code. The
// assumption is that looking up astral identifier characters is
// rare.
function isInAstralSet(code, set) {
  var pos = 0x10000;
  for (var i = 0; i < set.length; i += 2) {
    pos += set[i];
    if (pos > code) { return false }
    pos += set[i + 1];
    if (pos >= code) { return true }
  }
}

// Test whether a given character code starts an identifier.

function isIdentifierStart(code, astral) {
  if (code < 65) { return code === 36 }
  if (code < 91) { return true }
  if (code < 97) { return code === 95 }
  if (code < 123) { return true }
  if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)) }
  if (astral === false) { return false }
  return isInAstralSet(code, astralIdentifierStartCodes)
}

// Test whether a given character is part of an identifier.

function isIdentifierChar(code, astral) {
  if (code < 48) { return code === 36 }
  if (code < 58) { return true }
  if (code < 65) { return false }
  if (code < 91) { return true }
  if (code < 97) { return code === 95 }
  if (code < 123) { return true }
  if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)) }
  if (astral === false) { return false }
  return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes)
}

// ## Token types

// The assignment of fine-grained, information-carrying type objects
// allows the tokenizer to store the information it has about a
// token in a way that is very cheap for the parser to look up.

// All token type variables start with an underscore, to make them
// easy to recognize.

// The `beforeExpr` property is used to disambiguate between regular
// expressions and divisions. It is set on all token types that can
// be followed by an expression (thus, a slash after them would be a
// regular expression).
//
// The `startsExpr` property is used to check if the token ends a
// `yield` expression. It is set on all token types that either can
// directly start an expression (like a quotation mark) or can
// continue an expression (like the body of a string).
//
// `isLoop` marks a keyword as starting a loop, which is important
// to know when parsing a label, in order to allow or disallow
// continue jumps to that label.

var TokenType = function TokenType(label, conf) {
  if ( conf === void 0 ) conf = {};

  this.label = label;
  this.keyword = conf.keyword;
  this.beforeExpr = !!conf.beforeExpr;
  this.startsExpr = !!conf.startsExpr;
  this.isLoop = !!conf.isLoop;
  this.isAssign = !!conf.isAssign;
  this.prefix = !!conf.prefix;
  this.postfix = !!conf.postfix;
  this.binop = conf.binop || null;
  this.updateContext = null;
};

function binop(name, prec) {
  return new TokenType(name, {beforeExpr: true, binop: prec})
}
var beforeExpr = {beforeExpr: true};
var startsExpr = {startsExpr: true};

// Map keyword names to token types.

var keywords$1 = {};

// Succinct definitions of keyword token types
function kw(name, options) {
  if ( options === void 0 ) options = {};

  options.keyword = name;
  return keywords$1[name] = new TokenType(name, options)
}

var types = {
  num: new TokenType("num", startsExpr),
  regexp: new TokenType("regexp", startsExpr),
  string: new TokenType("string", startsExpr),
  name: new TokenType("name", startsExpr),
  eof: new TokenType("eof"),

  // Punctuation token types.
  bracketL: new TokenType("[", {beforeExpr: true, startsExpr: true}),
  bracketR: new TokenType("]"),
  braceL: new TokenType("{", {beforeExpr: true, startsExpr: true}),
  braceR: new TokenType("}"),
  parenL: new TokenType("(", {beforeExpr: true, startsExpr: true}),
  parenR: new TokenType(")"),
  comma: new TokenType(",", beforeExpr),
  semi: new TokenType(";", beforeExpr),
  colon: new TokenType(":", beforeExpr),
  dot: new TokenType("."),
  question: new TokenType("?", beforeExpr),
  arrow: new TokenType("=>", beforeExpr),
  template: new TokenType("template"),
  invalidTemplate: new TokenType("invalidTemplate"),
  ellipsis: new TokenType("...", beforeExpr),
  backQuote: new TokenType("`", startsExpr),
  dollarBraceL: new TokenType("${", {beforeExpr: true, startsExpr: true}),

  // Operators. These carry several kinds of properties to help the
  // parser use them properly (the presence of these properties is
  // what categorizes them as operators).
  //
  // `binop`, when present, specifies that this operator is a binary
  // operator, and will refer to its precedence.
  //
  // `prefix` and `postfix` mark the operator as a prefix or postfix
  // unary operator.
  //
  // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
  // binary operators with a very low precedence, that should result
  // in AssignmentExpression nodes.

  eq: new TokenType("=", {beforeExpr: true, isAssign: true}),
  assign: new TokenType("_=", {beforeExpr: true, isAssign: true}),
  incDec: new TokenType("++/--", {prefix: true, postfix: true, startsExpr: true}),
  prefix: new TokenType("prefix", {beforeExpr: true, prefix: true, startsExpr: true}),
  logicalOR: binop("||", 1),
  logicalAND: binop("&&", 2),
  bitwiseOR: binop("|", 3),
  bitwiseXOR: binop("^", 4),
  bitwiseAND: binop("&", 5),
  equality: binop("==/!=", 6),
  relational: binop("</>", 7),
  bitShift: binop("<</>>", 8),
  plusMin: new TokenType("+/-", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}),
  modulo: binop("%", 10),
  star: binop("*", 10),
  slash: binop("/", 10),
  starstar: new TokenType("**", {beforeExpr: true}),

  // Keyword token types.
  _break: kw("break"),
  _case: kw("case", beforeExpr),
  _catch: kw("catch"),
  _continue: kw("continue"),
  _debugger: kw("debugger"),
  _default: kw("default", beforeExpr),
  _do: kw("do", {isLoop: true, beforeExpr: true}),
  _else: kw("else", beforeExpr),
  _finally: kw("finally"),
  _for: kw("for", {isLoop: true}),
  _function: kw("function", startsExpr),
  _if: kw("if"),
  _return: kw("return", beforeExpr),
  _switch: kw("switch"),
  _throw: kw("throw", beforeExpr),
  _try: kw("try"),
  _var: kw("var"),
  _const: kw("const"),
  _while: kw("while", {isLoop: true}),
  _with: kw("with"),
  _new: kw("new", {beforeExpr: true, startsExpr: true}),
  _this: kw("this", startsExpr),
  _super: kw("super", startsExpr),
  _class: kw("class", startsExpr),
  _extends: kw("extends", beforeExpr),
  _export: kw("export"),
  _import: kw("import"),
  _null: kw("null", startsExpr),
  _true: kw("true", startsExpr),
  _false: kw("false", startsExpr),
  _in: kw("in", {beforeExpr: true, binop: 7}),
  _instanceof: kw("instanceof", {beforeExpr: true, binop: 7}),
  _typeof: kw("typeof", {beforeExpr: true, prefix: true, startsExpr: true}),
  _void: kw("void", {beforeExpr: true, prefix: true, startsExpr: true}),
  _delete: kw("delete", {beforeExpr: true, prefix: true, startsExpr: true})
};

// Matches a whole line break (where CRLF is considered a single
// line break). Used to count lines.

var lineBreak = /\r\n?|\n|\u2028|\u2029/;
var lineBreakG = new RegExp(lineBreak.source, "g");

function isNewLine(code) {
  return code === 10 || code === 13 || code === 0x2028 || code === 0x2029
}

var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;

var skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g;

var ref = Object.prototype;
var hasOwnProperty = ref.hasOwnProperty;
var toString = ref.toString;

// Checks if an object has a property.

function has(obj, propName) {
  return hasOwnProperty.call(obj, propName)
}

var isArray = Array.isArray || (function (obj) { return (
  toString.call(obj) === "[object Array]"
); });

// These are used when `options.locations` is on, for the
// `startLoc` and `endLoc` properties.

var Position = function Position(line, col) {
  this.line = line;
  this.column = col;
};

Position.prototype.offset = function offset (n) {
  return new Position(this.line, this.column + n)
};

var SourceLocation = function SourceLocation(p, start, end) {
  this.start = start;
  this.end = end;
  if (p.sourceFile !== null) { this.source = p.sourceFile; }
};

// The `getLineInfo` function is mostly useful when the
// `locations` option is off (for performance reasons) and you
// want to find the line/column position for a given character
// offset. `input` should be the code string that the offset refers
// into.

function getLineInfo(input, offset) {
  for (var line = 1, cur = 0;;) {
    lineBreakG.lastIndex = cur;
    var match = lineBreakG.exec(input);
    if (match && match.index < offset) {
      ++line;
      cur = match.index + match[0].length;
    } else {
      return new Position(line, offset - cur)
    }
  }
}

// A second optional argument can be given to further configure
// the parser process. These options are recognized:

var defaultOptions = {
  // `ecmaVersion` indicates the ECMAScript version to parse. Must
  // be either 3, 5, 6 (2015), 7 (2016), or 8 (2017). This influences support
  // for strict mode, the set of reserved words, and support for
  // new syntax features. The default is 7.
  ecmaVersion: 7,
  // `sourceType` indicates the mode the code should be parsed in.
  // Can be either `"script"` or `"module"`. This influences global
  // strict mode and parsing of `import` and `export` declarations.
  sourceType: "script",
  // `onInsertedSemicolon` can be a callback that will be called
  // when a semicolon is automatically inserted. It will be passed
  // th position of the comma as an offset, and if `locations` is
  // enabled, it is given the location as a `{line, column}` object
  // as second argument.
  onInsertedSemicolon: null,
  // `onTrailingComma` is similar to `onInsertedSemicolon`, but for
  // trailing commas.
  onTrailingComma: null,
  // By default, reserved words are only enforced if ecmaVersion >= 5.
  // Set `allowReserved` to a boolean value to explicitly turn this on
  // an off. When this option has the value "never", reserved words
  // and keywords can also not be used as property names.
  allowReserved: null,
  // When enabled, a return at the top level is not considered an
  // error.
  allowReturnOutsideFunction: false,
  // When enabled, import/export statements are not constrained to
  // appearing at the top of the program.
  allowImportExportEverywhere: false,
  // When enabled, hashbang directive in the beginning of file
  // is allowed and treated as a line comment.
  allowHashBang: false,
  // When `locations` is on, `loc` properties holding objects with
  // `start` and `end` properties in `{line, column}` form (with
  // line being 1-based and column 0-based) will be attached to the
  // nodes.
  locations: false,
  // A function can be passed as `onToken` option, which will
  // cause Acorn to call that function with object in the same
  // format as tokens returned from `tokenizer().getToken()`. Note
  // that you are not allowed to call the parser from the
  // callback—that will corrupt its internal state.
  onToken: null,
  // A function can be passed as `onComment` option, which will
  // cause Acorn to call that function with `(block, text, start,
  // end)` parameters whenever a comment is skipped. `block` is a
  // boolean indicating whether this is a block (`/* */`) comment,
  // `text` is the content of the comment, and `start` and `end` are
  // character offsets that denote the start and end of the comment.
  // When the `locations` option is on, two more parameters are
  // passed, the full `{line, column}` locations of the start and
  // end of the comments. Note that you are not allowed to call the
  // parser from the callback—that will corrupt its internal state.
  onComment: null,
  // Nodes have their start and end characters offsets recorded in
  // `start` and `end` properties (directly on the node, rather than
  // the `loc` object, which holds line/column data. To also add a
  // [semi-standardized][range] `range` property holding a `[start,
  // end]` array with the same numbers, set the `ranges` option to
  // `true`.
  //
  // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
  ranges: false,
  // It is possible to parse multiple files into a single AST by
  // passing the tree produced by parsing the first file as
  // `program` option in subsequent parses. This will add the
  // toplevel forms of the parsed file to the `Program` (top) node
  // of an existing parse tree.
  program: null,
  // When `locations` is on, you can pass this to record the source
  // file in every node's `loc` object.
  sourceFile: null,
  // This value, if given, is stored in every node, whether
  // `locations` is on or off.
  directSourceFile: null,
  // When enabled, parenthesized expressions are represented by
  // (non-standard) ParenthesizedExpression nodes
  preserveParens: false,
  plugins: {}
};

// Interpret and default an options object

function getOptions(opts) {
  var options = {};

  for (var opt in defaultOptions)
    { options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]; }

  if (options.ecmaVersion >= 2015)
    { options.ecmaVersion -= 2009; }

  if (options.allowReserved == null)
    { options.allowReserved = options.ecmaVersion < 5; }

  if (isArray(options.onToken)) {
    var tokens = options.onToken;
    options.onToken = function (token) { return tokens.push(token); };
  }
  if (isArray(options.onComment))
    { options.onComment = pushComment(options, options.onComment); }

  return options
}

function pushComment(options, array) {
  return function(block, text, start, end, startLoc, endLoc) {
    var comment = {
      type: block ? "Block" : "Line",
      value: text,
      start: start,
      end: end
    };
    if (options.locations)
      { comment.loc = new SourceLocation(this, startLoc, endLoc); }
    if (options.ranges)
      { comment.range = [start, end]; }
    array.push(comment);
  }
}

// Registered plugins
var plugins = {};

function keywordRegexp(words) {
  return new RegExp("^(?:" + words.replace(/ /g, "|") + ")$")
}

var Parser = function Parser(options, input, startPos) {
  this.options = options = getOptions(options);
  this.sourceFile = options.sourceFile;
  this.keywords = keywordRegexp(keywords[options.ecmaVersion >= 6 ? 6 : 5]);
  var reserved = "";
  if (!options.allowReserved) {
    for (var v = options.ecmaVersion;; v--)
      { if (reserved = reservedWords[v]) { break } }
    if (options.sourceType == "module") { reserved += " await"; }
  }
  this.reservedWords = keywordRegexp(reserved);
  var reservedStrict = (reserved ? reserved + " " : "") + reservedWords.strict;
  this.reservedWordsStrict = keywordRegexp(reservedStrict);
  this.reservedWordsStrictBind = keywordRegexp(reservedStrict + " " + reservedWords.strictBind);
  this.input = String(input);

  // Used to signal to callers of `readWord1` whether the word
  // contained any escape sequences. This is needed because words with
  // escape sequences must not be interpreted as keywords.
  this.containsEsc = false;

  // Load plugins
  this.loadPlugins(options.plugins);

  // Set up token state

  // The current position of the tokenizer in the input.
  if (startPos) {
    this.pos = startPos;
    this.lineStart = this.input.lastIndexOf("\n", startPos - 1) + 1;
    this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length;
  } else {
    this.pos = this.lineStart = 0;
    this.curLine = 1;
  }

  // Properties of the current token:
  // Its type
  this.type = types.eof;
  // For tokens that include more information than their type, the value
  this.value = null;
  // Its start and end offset
  this.start = this.end = this.pos;
  // And, if locations are used, the {line, column} object
  // corresponding to those offsets
  this.startLoc = this.endLoc = this.curPosition();

  // Position information for the previous token
  this.lastTokEndLoc = this.lastTokStartLoc = null;
  this.lastTokStart = this.lastTokEnd = this.pos;

  // The context stack is used to superficially track syntactic
  // context to predict whether a regular expression is allowed in a
  // given position.
  this.context = this.initialContext();
  this.exprAllowed = true;

  // Figure out if it's a module code.
  this.inModule = options.sourceType === "module";
  this.strict = this.inModule || this.strictDirective(this.pos);

  // Used to signify the start of a potential arrow function
  this.potentialArrowAt = -1;

  // Flags to track whether we are in a function, a generator, an async function.
  this.inFunction = this.inGenerator = this.inAsync = false;
  // Positions to delayed-check that yield/await does not exist in default parameters.
  this.yieldPos = this.awaitPos = 0;
  // Labels in scope.
  this.labels = [];

  // If enabled, skip leading hashbang line.
  if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === "#!")
    { this.skipLineComment(2); }

  // Scope tracking for duplicate variable names (see scope.js)
  this.scopeStack = [];
  this.enterFunctionScope();
};

// DEPRECATED Kept for backwards compatibility until 3.0 in case a plugin uses them
Parser.prototype.isKeyword = function isKeyword (word) { return this.keywords.test(word) };
Parser.prototype.isReservedWord = function isReservedWord (word) { return this.reservedWords.test(word) };

Parser.prototype.extend = function extend (name, f) {
  this[name] = f(this[name]);
};

Parser.prototype.loadPlugins = function loadPlugins (pluginConfigs) {
    var this$1 = this;

  for (var name in pluginConfigs) {
    var plugin = plugins[name];
    if (!plugin) { throw new Error("Plugin '" + name + "' not found") }
    plugin(this$1, pluginConfigs[name]);
  }
};

Parser.prototype.parse = function parse () {
  var node = this.options.program || this.startNode();
  this.nextToken();
  return this.parseTopLevel(node)
};

var pp = Parser.prototype;

// ## Parser utilities

var literal = /^(?:'((?:[^']|\.)*)'|"((?:[^"]|\.)*)"|;)/;
pp.strictDirective = function(start) {
  var this$1 = this;

  for (;;) {
    skipWhiteSpace.lastIndex = start;
    start += skipWhiteSpace.exec(this$1.input)[0].length;
    var match = literal.exec(this$1.input.slice(start));
    if (!match) { return false }
    if ((match[1] || match[2]) == "use strict") { return true }
    start += match[0].length;
  }
};

// Predicate that tests whether the next token is of the given
// type, and if yes, consumes it as a side effect.

pp.eat = function(type) {
  if (this.type === type) {
    this.next();
    return true
  } else {
    return false
  }
};

// Tests whether parsed token is a contextual keyword.

pp.isContextual = function(name) {
  return this.type === types.name && this.value === name
};

// Consumes contextual keyword if possible.

pp.eatContextual = function(name) {
  return this.value === name && this.eat(types.name)
};

// Asserts that following token is given contextual keyword.

pp.expectContextual = function(name) {
  if (!this.eatContextual(name)) { this.unexpected(); }
};

// Test whether a semicolon can be inserted at the current position.

pp.canInsertSemicolon = function() {
  return this.type === types.eof ||
    this.type === types.braceR ||
    lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
};

pp.insertSemicolon = function() {
  if (this.canInsertSemicolon()) {
    if (this.options.onInsertedSemicolon)
      { this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc); }
    return true
  }
};

// Consume a semicolon, or, failing that, see if we are allowed to
// pretend that there is a semicolon at this position.

pp.semicolon = function() {
  if (!this.eat(types.semi) && !this.insertSemicolon()) { this.unexpected(); }
};

pp.afterTrailingComma = function(tokType, notNext) {
  if (this.type == tokType) {
    if (this.options.onTrailingComma)
      { this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc); }
    if (!notNext)
      { this.next(); }
    return true
  }
};

// Expect a token of a given type. If found, consume it, otherwise,
// raise an unexpected token error.

pp.expect = function(type) {
  this.eat(type) || this.unexpected();
};

// Raise an unexpected token error.

pp.unexpected = function(pos) {
  this.raise(pos != null ? pos : this.start, "Unexpected token");
};

function DestructuringErrors() {
  this.shorthandAssign =
  this.trailingComma =
  this.parenthesizedAssign =
  this.parenthesizedBind =
    -1;
}

pp.checkPatternErrors = function(refDestructuringErrors, isAssign) {
  if (!refDestructuringErrors) { return }
  if (refDestructuringErrors.trailingComma > -1)
    { this.raiseRecoverable(refDestructuringErrors.trailingComma, "Comma is not permitted after the rest element"); }
  var parens = isAssign ? refDestructuringErrors.parenthesizedAssign : refDestructuringErrors.parenthesizedBind;
  if (parens > -1) { this.raiseRecoverable(parens, "Parenthesized pattern"); }
};

pp.checkExpressionErrors = function(refDestructuringErrors, andThrow) {
  var pos = refDestructuringErrors ? refDestructuringErrors.shorthandAssign : -1;
  if (!andThrow) { return pos >= 0 }
  if (pos > -1) { this.raise(pos, "Shorthand property assignments are valid only in destructuring patterns"); }
};

pp.checkYieldAwaitInDefaultParams = function() {
  if (this.yieldPos && (!this.awaitPos || this.yieldPos < this.awaitPos))
    { this.raise(this.yieldPos, "Yield expression cannot be a default value"); }
  if (this.awaitPos)
    { this.raise(this.awaitPos, "Await expression cannot be a default value"); }
};

pp.isSimpleAssignTarget = function(expr) {
  if (expr.type === "ParenthesizedExpression")
    { return this.isSimpleAssignTarget(expr.expression) }
  return expr.type === "Identifier" || expr.type === "MemberExpression"
};

var pp$1 = Parser.prototype;

// ### Statement parsing

// Parse a program. Initializes the parser, reads any number of
// statements, and wraps them in a Program node.  Optionally takes a
// `program` argument.  If present, the statements will be appended
// to its body instead of creating a new node.

pp$1.parseTopLevel = function(node) {
  var this$1 = this;

  var exports = {};
  if (!node.body) { node.body = []; }
  while (this.type !== types.eof) {
    var stmt = this$1.parseStatement(true, true, exports);
    node.body.push(stmt);
  }
  this.next();
  if (this.options.ecmaVersion >= 6) {
    node.sourceType = this.options.sourceType;
  }
  return this.finishNode(node, "Program")
};

var loopLabel = {kind: "loop"};
var switchLabel = {kind: "switch"};

pp$1.isLet = function() {
  if (this.type !== types.name || this.options.ecmaVersion < 6 || this.value != "let") { return false }
  skipWhiteSpace.lastIndex = this.pos;
  var skip = skipWhiteSpace.exec(this.input);
  var next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next);
  if (nextCh === 91 || nextCh == 123) { return true } // '{' and '['
  if (isIdentifierStart(nextCh, true)) {
    var pos = next + 1;
    while (isIdentifierChar(this.input.charCodeAt(pos), true)) { ++pos; }
    var ident = this.input.slice(next, pos);
    if (!this.isKeyword(ident)) { return true }
  }
  return false
};

// check 'async [no LineTerminator here] function'
// - 'async /*foo*/ function' is OK.
// - 'async /*\n*/ function' is invalid.
pp$1.isAsyncFunction = function() {
  if (this.type !== types.name || this.options.ecmaVersion < 8 || this.value != "async")
    { return false }

  skipWhiteSpace.lastIndex = this.pos;
  var skip = skipWhiteSpace.exec(this.input);
  var next = this.pos + skip[0].length;
  return !lineBreak.test(this.input.slice(this.pos, next)) &&
    this.input.slice(next, next + 8) === "function" &&
    (next + 8 == this.input.length || !isIdentifierChar(this.input.charAt(next + 8)))
};

// Parse a single statement.
//
// If expecting a statement and finding a slash operator, parse a
// regular expression literal. This is to handle cases like
// `if (foo) /blah/.exec(foo)`, where looking at the previous token
// does not help.

pp$1.parseStatement = function(declaration, topLevel, exports) {
  var starttype = this.type, node = this.startNode(), kind;

  if (this.isLet()) {
    starttype = types._var;
    kind = "let";
  }

  // Most types of statements are recognized by the keyword they
  // start with. Many are trivial to parse, some require a bit of
  // complexity.

  switch (starttype) {
  case types._break: case types._continue: return this.parseBreakContinueStatement(node, starttype.keyword)
  case types._debugger: return this.parseDebuggerStatement(node)
  case types._do: return this.parseDoStatement(node)
  case types._for: return this.parseForStatement(node)
  case types._function:
    if (!declaration && this.options.ecmaVersion >= 6) { this.unexpected(); }
    return this.parseFunctionStatement(node, false)
  case types._class:
    if (!declaration) { this.unexpected(); }
    return this.parseClass(node, true)
  case types._if: return this.parseIfStatement(node)
  case types._return: return this.parseReturnStatement(node)
  case types._switch: return this.parseSwitchStatement(node)
  case types._throw: return this.parseThrowStatement(node)
  case types._try: return this.parseTryStatement(node)
  case types._const: case types._var:
    kind = kind || this.value;
    if (!declaration && kind != "var") { this.unexpected(); }
    return this.parseVarStatement(node, kind)
  case types._while: return this.parseWhileStatement(node)
  case types._with: return this.parseWithStatement(node)
  case types.braceL: return this.parseBlock()
  case types.semi: return this.parseEmptyStatement(node)
  case types._export:
  case types._import:
    if (!this.options.allowImportExportEverywhere) {
      if (!topLevel)
        { this.raise(this.start, "'import' and 'export' may only appear at the top level"); }
      if (!this.inModule)
        { this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'"); }
    }
    return starttype === types._import ? this.parseImport(node) : this.parseExport(node, exports)

    // If the statement does not start with a statement keyword or a
    // brace, it's an ExpressionStatement or LabeledStatement. We
    // simply start parsing an expression, and afterwards, if the
    // next token is a colon and the expression was a simple
    // Identifier node, we switch to interpreting it as a label.
  default:
    if (this.isAsyncFunction() && declaration) {
      this.next();
      return this.parseFunctionStatement(node, true)
    }

    var maybeName = this.value, expr = this.parseExpression();
    if (starttype === types.name && expr.type === "Identifier" && this.eat(types.colon))
      { return this.parseLabeledStatement(node, maybeName, expr) }
    else { return this.parseExpressionStatement(node, expr) }
  }
};

pp$1.parseBreakContinueStatement = function(node, keyword) {
  var this$1 = this;

  var isBreak = keyword == "break";
  this.next();
  if (this.eat(types.semi) || this.insertSemicolon()) { node.label = null; }
  else if (this.type !== types.name) { this.unexpected(); }
  else {
    node.label = this.parseIdent();
    this.semicolon();
  }

  // Verify that there is an actual destination to break or
  // continue to.
  var i = 0;
  for (; i < this.labels.length; ++i) {
    var lab = this$1.labels[i];
    if (node.label == null || lab.name === node.label.name) {
      if (lab.kind != null && (isBreak || lab.kind === "loop")) { break }
      if (node.label && isBreak) { break }
    }
  }
  if (i === this.labels.length) { this.raise(node.start, "Unsyntactic " + keyword); }
  return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
};

pp$1.parseDebuggerStatement = function(node) {
  this.next();
  this.semicolon();
  return this.finishNode(node, "DebuggerStatement")
};

pp$1.parseDoStatement = function(node) {
  this.next();
  this.labels.push(loopLabel);
  node.body = this.parseStatement(false);
  this.labels.pop();
  this.expect(types._while);
  node.test = this.parseParenExpression();
  if (this.options.ecmaVersion >= 6)
    { this.eat(types.semi); }
  else
    { this.semicolon(); }
  return this.finishNode(node, "DoWhileStatement")
};

// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
// loop is non-trivial. Basically, we have to parse the init `var`
// statement or expression, disallowing the `in` operator (see
// the second parameter to `parseExpression`), and then check
// whether the next token is `in` or `of`. When there is no init
// part (semicolon immediately after the opening parenthesis), it
// is a regular `for` loop.

pp$1.parseForStatement = function(node) {
  this.next();
  this.labels.push(loopLabel);
  this.enterLexicalScope();
  this.expect(types.parenL);
  if (this.type === types.semi) { return this.parseFor(node, null) }
  var isLet = this.isLet();
  if (this.type === types._var || this.type === types._const || isLet) {
    var init$1 = this.startNode(), kind = isLet ? "let" : this.value;
    this.next();
    this.parseVar(init$1, true, kind);
    this.finishNode(init$1, "VariableDeclaration");
    if ((this.type === types._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init$1.declarations.length === 1 &&
        !(kind !== "var" && init$1.declarations[0].init))
      { return this.parseForIn(node, init$1) }
    return this.parseFor(node, init$1)
  }
  var refDestructuringErrors = new DestructuringErrors;
  var init = this.parseExpression(true, refDestructuringErrors);
  if (this.type === types._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
    this.toAssignable(init);
    this.checkLVal(init);
    this.checkPatternErrors(refDestructuringErrors, true);
    return this.parseForIn(node, init)
  } else {
    this.checkExpressionErrors(refDestructuringErrors, true);
  }
  return this.parseFor(node, init)
};

pp$1.parseFunctionStatement = function(node, isAsync) {
  this.next();
  return this.parseFunction(node, true, false, isAsync)
};

pp$1.isFunction = function() {
  return this.type === types._function || this.isAsyncFunction()
};

pp$1.parseIfStatement = function(node) {
  this.next();
  node.test = this.parseParenExpression();
  // allow function declarations in branches, but only in non-strict mode
  node.consequent = this.parseStatement(!this.strict && this.isFunction());
  node.alternate = this.eat(types._else) ? this.parseStatement(!this.strict && this.isFunction()) : null;
  return this.finishNode(node, "IfStatement")
};

pp$1.parseReturnStatement = function(node) {
  if (!this.inFunction && !this.options.allowReturnOutsideFunction)
    { this.raise(this.start, "'return' outside of function"); }
  this.next();

  // In `return` (and `break`/`continue`), the keywords with
  // optional arguments, we eagerly look for a semicolon or the
  // possibility to insert one.

  if (this.eat(types.semi) || this.insertSemicolon()) { node.argument = null; }
  else { node.argument = this.parseExpression(); this.semicolon(); }
  return this.finishNode(node, "ReturnStatement")
};

pp$1.parseSwitchStatement = function(node) {
  var this$1 = this;

  this.next();
  node.discriminant = this.parseParenExpression();
  node.cases = [];
  this.expect(types.braceL);
  this.labels.push(switchLabel);
  this.enterLexicalScope();

  // Statements under must be grouped (by label) in SwitchCase
  // nodes. `cur` is used to keep the node that we are currently
  // adding statements to.

  var cur;
  for (var sawDefault = false; this.type != types.braceR;) {
    if (this$1.type === types._case || this$1.type === types._default) {
      var isCase = this$1.type === types._case;
      if (cur) { this$1.finishNode(cur, "SwitchCase"); }
      node.cases.push(cur = this$1.startNode());
      cur.consequent = [];
      this$1.next();
      if (isCase) {
        cur.test = this$1.parseExpression();
      } else {
        if (sawDefault) { this$1.raiseRecoverable(this$1.lastTokStart, "Multiple default clauses"); }
        sawDefault = true;
        cur.test = null;
      }
      this$1.expect(types.colon);
    } else {
      if (!cur) { this$1.unexpected(); }
      cur.consequent.push(this$1.parseStatement(true));
    }
  }
  this.exitLexicalScope();
  if (cur) { this.finishNode(cur, "SwitchCase"); }
  this.next(); // Closing brace
  this.labels.pop();
  return this.finishNode(node, "SwitchStatement")
};

pp$1.parseThrowStatement = function(node) {
  this.next();
  if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))
    { this.raise(this.lastTokEnd, "Illegal newline after throw"); }
  node.argument = this.parseExpression();
  this.semicolon();
  return this.finishNode(node, "ThrowStatement")
};

// Reused empty array added for node fields that are always empty.

var empty = [];

pp$1.parseTryStatement = function(node) {
  this.next();
  node.block = this.parseBlock();
  node.handler = null;
  if (this.type === types._catch) {
    var clause = this.startNode();
    this.next();
    this.expect(types.parenL);
    clause.param = this.parseBindingAtom();
    this.enterLexicalScope();
    this.checkLVal(clause.param, "let");
    this.expect(types.parenR);
    clause.body = this.parseBlock(false);
    this.exitLexicalScope();
    node.handler = this.finishNode(clause, "CatchClause");
  }
  node.finalizer = this.eat(types._finally) ? this.parseBlock() : null;
  if (!node.handler && !node.finalizer)
    { this.raise(node.start, "Missing catch or finally clause"); }
  return this.finishNode(node, "TryStatement")
};

pp$1.parseVarStatement = function(node, kind) {
  this.next();
  this.parseVar(node, false, kind);
  this.semicolon();
  return this.finishNode(node, "VariableDeclaration")
};

pp$1.parseWhileStatement = function(node) {
  this.next();
  node.test = this.parseParenExpression();
  this.labels.push(loopLabel);
  node.body = this.parseStatement(false);
  this.labels.pop();
  return this.finishNode(node, "WhileStatement")
};

pp$1.parseWithStatement = function(node) {
  if (this.strict) { this.raise(this.start, "'with' in strict mode"); }
  this.next();
  node.object = this.parseParenExpression();
  node.body = this.parseStatement(false);
  return this.finishNode(node, "WithStatement")
};

pp$1.parseEmptyStatement = function(node) {
  this.next();
  return this.finishNode(node, "EmptyStatement")
};

pp$1.parseLabeledStatement = function(node, maybeName, expr) {
  var this$1 = this;

  for (var i$1 = 0, list = this$1.labels; i$1 < list.length; i$1 += 1)
    {
    var label = list[i$1];

    if (label.name === maybeName)
      { this$1.raise(expr.start, "Label '" + maybeName + "' is already declared");
  } }
  var kind = this.type.isLoop ? "loop" : this.type === types._switch ? "switch" : null;
  for (var i = this.labels.length - 1; i >= 0; i--) {
    var label$1 = this$1.labels[i];
    if (label$1.statementStart == node.start) {
      label$1.statementStart = this$1.start;
      label$1.kind = kind;
    } else { break }
  }
  this.labels.push({name: maybeName, kind: kind, statementStart: this.start});
  node.body = this.parseStatement(true);
  if (node.body.type == "ClassDeclaration" ||
      node.body.type == "VariableDeclaration" && node.body.kind != "var" ||
      node.body.type == "FunctionDeclaration" && (this.strict || node.body.generator))
    { this.raiseRecoverable(node.body.start, "Invalid labeled declaration"); }
  this.labels.pop();
  node.label = expr;
  return this.finishNode(node, "LabeledStatement")
};

pp$1.parseExpressionStatement = function(node, expr) {
  node.expression = expr;
  this.semicolon();
  return this.finishNode(node, "ExpressionStatement")
};

// Parse a semicolon-enclosed block of statements, handling `"use
// strict"` declarations when `allowStrict` is true (used for
// function bodies).

pp$1.parseBlock = function(createNewLexicalScope) {
  var this$1 = this;
  if ( createNewLexicalScope === void 0 ) createNewLexicalScope = true;

  var node = this.startNode();
  node.body = [];
  this.expect(types.braceL);
  if (createNewLexicalScope) {
    this.enterLexicalScope();
  }
  while (!this.eat(types.braceR)) {
    var stmt = this$1.parseStatement(true);
    node.body.push(stmt);
  }
  if (createNewLexicalScope) {
    this.exitLexicalScope();
  }
  return this.finishNode(node, "BlockStatement")
};

// Parse a regular `for` loop. The disambiguation code in
// `parseStatement` will already have parsed the init statement or
// expression.

pp$1.parseFor = function(node, init) {
  node.init = init;
  this.expect(types.semi);
  node.test = this.type === types.semi ? null : this.parseExpression();
  this.expect(types.semi);
  node.update = this.type === types.parenR ? null : this.parseExpression();
  this.expect(types.parenR);
  this.exitLexicalScope();
  node.body = this.parseStatement(false);
  this.labels.pop();
  return this.finishNode(node, "ForStatement")
};

// Parse a `for`/`in` and `for`/`of` loop, which are almost
// same from parser's perspective.

pp$1.parseForIn = function(node, init) {
  var type = this.type === types._in ? "ForInStatement" : "ForOfStatement";
  this.next();
  node.left = init;
  node.right = this.parseExpression();
  this.expect(types.parenR);
  this.exitLexicalScope();
  node.body = this.parseStatement(false);
  this.labels.pop();
  return this.finishNode(node, type)
};

// Parse a list of variable declarations.

pp$1.parseVar = function(node, isFor, kind) {
  var this$1 = this;

  node.declarations = [];
  node.kind = kind;
  for (;;) {
    var decl = this$1.startNode();
    this$1.parseVarId(decl, kind);
    if (this$1.eat(types.eq)) {
      decl.init = this$1.parseMaybeAssign(isFor);
    } else if (kind === "const" && !(this$1.type === types._in || (this$1.options.ecmaVersion >= 6 && this$1.isContextual("of")))) {
      this$1.unexpected();
    } else if (decl.id.type != "Identifier" && !(isFor && (this$1.type === types._in || this$1.isContextual("of")))) {
      this$1.raise(this$1.lastTokEnd, "Complex binding patterns require an initialization value");
    } else {
      decl.init = null;
    }
    node.declarations.push(this$1.finishNode(decl, "VariableDeclarator"));
    if (!this$1.eat(types.comma)) { break }
  }
  return node
};

pp$1.parseVarId = function(decl, kind) {
  decl.id = this.parseBindingAtom(kind);
  this.checkLVal(decl.id, kind, false);
};

// Parse a function declaration or literal (depending on the
// `isStatement` parameter).

pp$1.parseFunction = function(node, isStatement, allowExpressionBody, isAsync) {
  this.initFunction(node);
  if (this.options.ecmaVersion >= 6 && !isAsync)
    { node.generator = this.eat(types.star); }
  if (this.options.ecmaVersion >= 8)
    { node.async = !!isAsync; }

  if (isStatement) {
    node.id = isStatement === "nullableID" && this.type != types.name ? null : this.parseIdent();
    if (node.id) {
      this.checkLVal(node.id, "var");
    }
  }

  var oldInGen = this.inGenerator, oldInAsync = this.inAsync,
      oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldInFunc = this.inFunction;
  this.inGenerator = node.generator;
  this.inAsync = node.async;
  this.yieldPos = 0;
  this.awaitPos = 0;
  this.inFunction = true;
  this.enterFunctionScope();

  if (!isStatement)
    { node.id = this.type == types.name ? this.parseIdent() : null; }

  this.parseFunctionParams(node);
  this.parseFunctionBody(node, allowExpressionBody);

  this.inGenerator = oldInGen;
  this.inAsync = oldInAsync;
  this.yieldPos = oldYieldPos;
  this.awaitPos = oldAwaitPos;
  this.inFunction = oldInFunc;
  return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
};

pp$1.parseFunctionParams = function(node) {
  this.expect(types.parenL);
  node.params = this.parseBindingList(types.parenR, false, this.options.ecmaVersion >= 8);
  this.checkYieldAwaitInDefaultParams();
};

// Parse a class declaration or literal (depending on the
// `isStatement` parameter).

pp$1.parseClass = function(node, isStatement) {
  var this$1 = this;

  this.next();

  this.parseClassId(node, isStatement);
  this.parseClassSuper(node);
  var classBody = this.startNode();
  var hadConstructor = false;
  classBody.body = [];
  this.expect(types.braceL);
  while (!this.eat(types.braceR)) {
    if (this$1.eat(types.semi)) { continue }
    var method = this$1.startNode();
    var isGenerator = this$1.eat(types.star);
    var isAsync = false;
    var isMaybeStatic = this$1.type === types.name && this$1.value === "static";
    this$1.parsePropertyName(method);
    method.static = isMaybeStatic && this$1.type !== types.parenL;
    if (method.static) {
      if (isGenerator) { this$1.unexpected(); }
      isGenerator = this$1.eat(types.star);
      this$1.parsePropertyName(method);
    }
    if (this$1.options.ecmaVersion >= 8 && !isGenerator && !method.computed &&
        method.key.type === "Identifier" && method.key.name === "async" && this$1.type !== types.parenL &&
        !this$1.canInsertSemicolon()) {
      isAsync = true;
      this$1.parsePropertyName(method);
    }
    method.kind = "method";
    var isGetSet = false;
    if (!method.computed) {
      var key = method.key;
      if (!isGenerator && !isAsync && key.type === "Identifier" && this$1.type !== types.parenL && (key.name === "get" || key.name === "set")) {
        isGetSet = true;
        method.kind = key.name;
        key = this$1.parsePropertyName(method);
      }
      if (!method.static && (key.type === "Identifier" && key.name === "constructor" ||
          key.type === "Literal" && key.value === "constructor")) {
        if (hadConstructor) { this$1.raise(key.start, "Duplicate constructor in the same class"); }
        if (isGetSet) { this$1.raise(key.start, "Constructor can't have get/set modifier"); }
        if (isGenerator) { this$1.raise(key.start, "Constructor can't be a generator"); }
        if (isAsync) { this$1.raise(key.start, "Constructor can't be an async method"); }
        method.kind = "constructor";
        hadConstructor = true;
      }
    }
    this$1.parseClassMethod(classBody, method, isGenerator, isAsync);
    if (isGetSet) {
      var paramCount = method.kind === "get" ? 0 : 1;
      if (method.value.params.length !== paramCount) {
        var start = method.value.start;
        if (method.kind === "get")
          { this$1.raiseRecoverable(start, "getter should have no params"); }
        else
          { this$1.raiseRecoverable(start, "setter should have exactly one param"); }
      } else {
        if (method.kind === "set" && method.value.params[0].type === "RestElement")
          { this$1.raiseRecoverable(method.value.params[0].start, "Setter cannot use rest params"); }
      }
    }
  }
  node.body = this.finishNode(classBody, "ClassBody");
  return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
};

pp$1.parseClassMethod = function(classBody, method, isGenerator, isAsync) {
  method.value = this.parseMethod(isGenerator, isAsync);
  classBody.body.push(this.finishNode(method, "MethodDefinition"));
};

pp$1.parseClassId = function(node, isStatement) {
  node.id = this.type === types.name ? this.parseIdent() : isStatement === true ? this.unexpected() : null;
};

pp$1.parseClassSuper = function(node) {
  node.superClass = this.eat(types._extends) ? this.parseExprSubscripts() : null;
};

// Parses module export declaration.

pp$1.parseExport = function(node, exports) {
  var this$1 = this;

  this.next();
  // export * from '...'
  if (this.eat(types.star)) {
    this.expectContextual("from");
    node.source = this.type === types.string ? this.parseExprAtom() : this.unexpected();
    this.semicolon();
    return this.finishNode(node, "ExportAllDeclaration")
  }
  if (this.eat(types._default)) { // export default ...
    this.checkExport(exports, "default", this.lastTokStart);
    var isAsync;
    if (this.type === types._function || (isAsync = this.isAsyncFunction())) {
      var fNode = this.startNode();
      this.next();
      if (isAsync) { this.next(); }
      node.declaration = this.parseFunction(fNode, "nullableID", false, isAsync);
    } else if (this.type === types._class) {
      var cNode = this.startNode();
      node.declaration = this.parseClass(cNode, "nullableID");
    } else {
      node.declaration = this.parseMaybeAssign();
      this.semicolon();
    }
    return this.finishNode(node, "ExportDefaultDeclaration")
  }
  // export var|const|let|function|class ...
  if (this.shouldParseExportStatement()) {
    node.declaration = this.parseStatement(true);
    if (node.declaration.type === "VariableDeclaration")
      { this.checkVariableExport(exports, node.declaration.declarations); }
    else
      { this.checkExport(exports, node.declaration.id.name, node.declaration.id.start); }
    node.specifiers = [];
    node.source = null;
  } else { // export { x, y as z } [from '...']
    node.declaration = null;
    node.specifiers = this.parseExportSpecifiers(exports);
    if (this.eatContextual("from")) {
      node.source = this.type === types.string ? this.parseExprAtom() : this.unexpected();
    } else {
      // check for keywords used as local names
      for (var i = 0, list = node.specifiers; i < list.length; i += 1) {
        var spec = list[i];

        this$1.checkUnreserved(spec.local);
      }

      node.source = null;
    }
    this.semicolon();
  }
  return this.finishNode(node, "ExportNamedDeclaration")
};

pp$1.checkExport = function(exports, name, pos) {
  if (!exports) { return }
  if (has(exports, name))
    { this.raiseRecoverable(pos, "Duplicate export '" + name + "'"); }
  exports[name] = true;
};

pp$1.checkPatternExport = function(exports, pat) {
  var this$1 = this;

  var type = pat.type;
  if (type == "Identifier")
    { this.checkExport(exports, pat.name, pat.start); }
  else if (type == "ObjectPattern")
    { for (var i = 0, list = pat.properties; i < list.length; i += 1)
      {
        var prop = list[i];

        this$1.checkPatternExport(exports, prop.value);
      } }
  else if (type == "ArrayPattern")
    { for (var i$1 = 0, list$1 = pat.elements; i$1 < list$1.length; i$1 += 1) {
      var elt = list$1[i$1];

        if (elt) { this$1.checkPatternExport(exports, elt); }
    } }
  else if (type == "AssignmentPattern")
    { this.checkPatternExport(exports, pat.left); }
  else if (type == "ParenthesizedExpression")
    { this.checkPatternExport(exports, pat.expression); }
};

pp$1.checkVariableExport = function(exports, decls) {
  var this$1 = this;

  if (!exports) { return }
  for (var i = 0, list = decls; i < list.length; i += 1)
    {
    var decl = list[i];

    this$1.checkPatternExport(exports, decl.id);
  }
};

pp$1.shouldParseExportStatement = function() {
  return this.type.keyword === "var" ||
    this.type.keyword === "const" ||
    this.type.keyword === "class" ||
    this.type.keyword === "function" ||
    this.isLet() ||
    this.isAsyncFunction()
};

// Parses a comma-separated list of module exports.

pp$1.parseExportSpecifiers = function(exports) {
  var this$1 = this;

  var nodes = [], first = true;
  // export { x, y as z } [from '...']
  this.expect(types.braceL);
  while (!this.eat(types.braceR)) {
    if (!first) {
      this$1.expect(types.comma);
      if (this$1.afterTrailingComma(types.braceR)) { break }
    } else { first = false; }

    var node = this$1.startNode();
    node.local = this$1.parseIdent(true);
    node.exported = this$1.eatContextual("as") ? this$1.parseIdent(true) : node.local;
    this$1.checkExport(exports, node.exported.name, node.exported.start);
    nodes.push(this$1.finishNode(node, "ExportSpecifier"));
  }
  return nodes
};

// Parses import declaration.

pp$1.parseImport = function(node) {
  this.next();
  // import '...'
  if (this.type === types.string) {
    node.specifiers = empty;
    node.source = this.parseExprAtom();
  } else {
    node.specifiers = this.parseImportSpecifiers();
    this.expectContextual("from");
    node.source = this.type === types.string ? this.parseExprAtom() : this.unexpected();
  }
  this.semicolon();
  return this.finishNode(node, "ImportDeclaration")
};

// Parses a comma-separated list of module imports.

pp$1.parseImportSpecifiers = function() {
  var this$1 = this;

  var nodes = [], first = true;
  if (this.type === types.name) {
    // import defaultObj, { x, y as z } from '...'
    var node = this.startNode();
    node.local = this.parseIdent();
    this.checkLVal(node.local, "let");
    nodes.push(this.finishNode(node, "ImportDefaultSpecifier"));
    if (!this.eat(types.comma)) { return nodes }
  }
  if (this.type === types.star) {
    var node$1 = this.startNode();
    this.next();
    this.expectContextual("as");
    node$1.local = this.parseIdent();
    this.checkLVal(node$1.local, "let");
    nodes.push(this.finishNode(node$1, "ImportNamespaceSpecifier"));
    return nodes
  }
  this.expect(types.braceL);
  while (!this.eat(types.braceR)) {
    if (!first) {
      this$1.expect(types.comma);
      if (this$1.afterTrailingComma(types.braceR)) { break }
    } else { first = false; }

    var node$2 = this$1.startNode();
    node$2.imported = this$1.parseIdent(true);
    if (this$1.eatContextual("as")) {
      node$2.local = this$1.parseIdent();
    } else {
      this$1.checkUnreserved(node$2.imported);
      node$2.local = node$2.imported;
    }
    this$1.checkLVal(node$2.local, "let");
    nodes.push(this$1.finishNode(node$2, "ImportSpecifier"));
  }
  return nodes
};

var pp$2 = Parser.prototype;

// Convert existing expression atom to assignable pattern
// if possible.

pp$2.toAssignable = function(node, isBinding) {
  var this$1 = this;

  if (this.options.ecmaVersion >= 6 && node) {
    switch (node.type) {
    case "Identifier":
      if (this.inAsync && node.name === "await")
        { this.raise(node.start, "Can not use 'await' as identifier inside an async function"); }
      break

    case "ObjectPattern":
    case "ArrayPattern":
      break

    case "ObjectExpression":
      node.type = "ObjectPattern";
      for (var i = 0, list = node.properties; i < list.length; i += 1) {
        var prop = list[i];

      if (prop.kind !== "init") { this$1.raise(prop.key.start, "Object pattern can't contain getter or setter"); }
        this$1.toAssignable(prop.value, isBinding);
      }
      break

    case "ArrayExpression":
      node.type = "ArrayPattern";
      this.toAssignableList(node.elements, isBinding);
      break

    case "AssignmentExpression":
      if (node.operator === "=") {
        node.type = "AssignmentPattern";
        delete node.operator;
        this.toAssignable(node.left, isBinding);
        // falls through to AssignmentPattern
      } else {
        this.raise(node.left.end, "Only '=' operator can be used for specifying default value.");
        break
      }

    case "AssignmentPattern":
      break

    case "ParenthesizedExpression":
      this.toAssignable(node.expression, isBinding);
      break

    case "MemberExpression":
      if (!isBinding) { break }

    default:
      this.raise(node.start, "Assigning to rvalue");
    }
  }
  return node
};

// Convert list of expression atoms to binding list.

pp$2.toAssignableList = function(exprList, isBinding) {
  var this$1 = this;

  var end = exprList.length;
  if (end) {
    var last = exprList[end - 1];
    if (last && last.type == "RestElement") {
      --end;
    } else if (last && last.type == "SpreadElement") {
      last.type = "RestElement";
      var arg = last.argument;
      this.toAssignable(arg, isBinding);
      --end;
    }

    if (this.options.ecmaVersion === 6 && isBinding && last && last.type === "RestElement" && last.argument.type !== "Identifier")
      { this.unexpected(last.argument.start); }
  }
  for (var i = 0; i < end; i++) {
    var elt = exprList[i];
    if (elt) { this$1.toAssignable(elt, isBinding); }
  }
  return exprList
};

// Parses spread element.

pp$2.parseSpread = function(refDestructuringErrors) {
  var node = this.startNode();
  this.next();
  node.argument = this.parseMaybeAssign(false, refDestructuringErrors);
  return this.finishNode(node, "SpreadElement")
};

pp$2.parseRestBinding = function() {
  var node = this.startNode();
  this.next();

  // RestElement inside of a function parameter must be an identifier
  if (this.options.ecmaVersion === 6 && this.type !== types.name)
    { this.unexpected(); }

  node.argument = this.parseBindingAtom();

  return this.finishNode(node, "RestElement")
};

// Parses lvalue (assignable) atom.

pp$2.parseBindingAtom = function() {
  if (this.options.ecmaVersion < 6) { return this.parseIdent() }
  switch (this.type) {
  case types.name:
    return this.parseIdent()

  case types.bracketL:
    var node = this.startNode();
    this.next();
    node.elements = this.parseBindingList(types.bracketR, true, true);
    return this.finishNode(node, "ArrayPattern")

  case types.braceL:
    return this.parseObj(true)

  default:
    this.unexpected();
  }
};

pp$2.parseBindingList = function(close, allowEmpty, allowTrailingComma) {
  var this$1 = this;

  var elts = [], first = true;
  while (!this.eat(close)) {
    if (first) { first = false; }
    else { this$1.expect(types.comma); }
    if (allowEmpty && this$1.type === types.comma) {
      elts.push(null);
    } else if (allowTrailingComma && this$1.afterTrailingComma(close)) {
      break
    } else if (this$1.type === types.ellipsis) {
      var rest = this$1.parseRestBinding();
      this$1.parseBindingListItem(rest);
      elts.push(rest);
      if (this$1.type === types.comma) { this$1.raise(this$1.start, "Comma is not permitted after the rest element"); }
      this$1.expect(close);
      break
    } else {
      var elem = this$1.parseMaybeDefault(this$1.start, this$1.startLoc);
      this$1.parseBindingListItem(elem);
      elts.push(elem);
    }
  }
  return elts
};

pp$2.parseBindingListItem = function(param) {
  return param
};

// Parses assignment pattern around given atom if possible.

pp$2.parseMaybeDefault = function(startPos, startLoc, left) {
  left = left || this.parseBindingAtom();
  if (this.options.ecmaVersion < 6 || !this.eat(types.eq)) { return left }
  var node = this.startNodeAt(startPos, startLoc);
  node.left = left;
  node.right = this.parseMaybeAssign();
  return this.finishNode(node, "AssignmentPattern")
};

// Verify that a node is an lval — something that can be assigned
// to.
// bindingType can be either:
// 'var' indicating that the lval creates a 'var' binding
// 'let' indicating that the lval creates a lexical ('let' or 'const') binding
// 'none' indicating that the binding should be checked for illegal identifiers, but not for duplicate references

pp$2.checkLVal = function(expr, bindingType, checkClashes) {
  var this$1 = this;

  switch (expr.type) {
  case "Identifier":
    if (this.strict && this.reservedWordsStrictBind.test(expr.name))
      { this.raiseRecoverable(expr.start, (bindingType ? "Binding " : "Assigning to ") + expr.name + " in strict mode"); }
    if (checkClashes) {
      if (has(checkClashes, expr.name))
        { this.raiseRecoverable(expr.start, "Argument name clash"); }
      checkClashes[expr.name] = true;
    }
    if (bindingType && bindingType !== "none") {
      if (
        bindingType === "var" && !this.canDeclareVarName(expr.name) ||
        bindingType !== "var" && !this.canDeclareLexicalName(expr.name)
      ) {
        this.raiseRecoverable(expr.start, ("Identifier '" + (expr.name) + "' has already been declared"));
      }
      if (bindingType === "var") {
        this.declareVarName(expr.name);
      } else {
        this.declareLexicalName(expr.name);
      }
    }
    break

  case "MemberExpression":
    if (bindingType) { this.raiseRecoverable(expr.start, (bindingType ? "Binding" : "Assigning to") + " member expression"); }
    break

  case "ObjectPattern":
    for (var i = 0, list = expr.properties; i < list.length; i += 1)
      {
    var prop = list[i];

    this$1.checkLVal(prop.value, bindingType, checkClashes);
  }
    break

  case "ArrayPattern":
    for (var i$1 = 0, list$1 = expr.elements; i$1 < list$1.length; i$1 += 1) {
      var elem = list$1[i$1];

    if (elem) { this$1.checkLVal(elem, bindingType, checkClashes); }
    }
    break

  case "AssignmentPattern":
    this.checkLVal(expr.left, bindingType, checkClashes);
    break

  case "RestElement":
    this.checkLVal(expr.argument, bindingType, checkClashes);
    break

  case "ParenthesizedExpression":
    this.checkLVal(expr.expression, bindingType, checkClashes);
    break

  default:
    this.raise(expr.start, (bindingType ? "Binding" : "Assigning to") + " rvalue");
  }
};

// A recursive descent parser operates by defining functions for all
// syntactic elements, and recursively calling those, each function
// advancing the input stream and returning an AST node. Precedence
// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
// instead of `(!x)[1]` is handled by the fact that the parser
// function that parses unary prefix operators is called first, and
// in turn calls the function that parses `[]` subscripts — that
// way, it'll receive the node for `x[1]` already parsed, and wraps
// *that* in the unary operator node.
//
// Acorn uses an [operator precedence parser][opp] to handle binary
// operator precedence, because it is much more compact than using
// the technique outlined above, which uses different, nesting
// functions to specify precedence, for all of the ten binary
// precedence levels that JavaScript defines.
//
// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser

var pp$3 = Parser.prototype;

// Check if property name clashes with already added.
// Object/class getters and setters are not allowed to clash —
// either with each other or with an init property — and in
// strict mode, init properties are also not allowed to be repeated.

pp$3.checkPropClash = function(prop, propHash) {
  if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand))
    { return }
  var key = prop.key;
  var name;
  switch (key.type) {
  case "Identifier": name = key.name; break
  case "Literal": name = String(key.value); break
  default: return
  }
  var kind = prop.kind;
  if (this.options.ecmaVersion >= 6) {
    if (name === "__proto__" && kind === "init") {
      if (propHash.proto) { this.raiseRecoverable(key.start, "Redefinition of __proto__ property"); }
      propHash.proto = true;
    }
    return
  }
  name = "$" + name;
  var other = propHash[name];
  if (other) {
    var redefinition;
    if (kind === "init") {
      redefinition = this.strict && other.init || other.get || other.set;
    } else {
      redefinition = other.init || other[kind];
    }
    if (redefinition)
      { this.raiseRecoverable(key.start, "Redefinition of property"); }
  } else {
    other = propHash[name] = {
      init: false,
      get: false,
      set: false
    };
  }
  other[kind] = true;
};

// ### Expression parsing

// These nest, from the most general expression type at the top to
// 'atomic', nondivisible expression types at the bottom. Most of
// the functions will simply let the function(s) below them parse,
// and, *if* the syntactic construct they handle is present, wrap
// the AST node that the inner parser gave them in another node.

// Parse a full expression. The optional arguments are used to
// forbid the `in` operator (in for loops initalization expressions)
// and provide reference for storing '=' operator inside shorthand
// property assignment in contexts where both object expression
// and object pattern might appear (so it's possible to raise
// delayed syntax error at correct position).

pp$3.parseExpression = function(noIn, refDestructuringErrors) {
  var this$1 = this;

  var startPos = this.start, startLoc = this.startLoc;
  var expr = this.parseMaybeAssign(noIn, refDestructuringErrors);
  if (this.type === types.comma) {
    var node = this.startNodeAt(startPos, startLoc);
    node.expressions = [expr];
    while (this.eat(types.comma)) { node.expressions.push(this$1.parseMaybeAssign(noIn, refDestructuringErrors)); }
    return this.finishNode(node, "SequenceExpression")
  }
  return expr
};

// Parse an assignment expression. This includes applications of
// operators like `+=`.

pp$3.parseMaybeAssign = function(noIn, refDestructuringErrors, afterLeftParse) {
  if (this.inGenerator && this.isContextual("yield")) { return this.parseYield() }

  var ownDestructuringErrors = false, oldParenAssign = -1, oldTrailingComma = -1;
  if (refDestructuringErrors) {
    oldParenAssign = refDestructuringErrors.parenthesizedAssign;
    oldTrailingComma = refDestructuringErrors.trailingComma;
    refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = -1;
  } else {
    refDestructuringErrors = new DestructuringErrors;
    ownDestructuringErrors = true;
  }

  var startPos = this.start, startLoc = this.startLoc;
  if (this.type == types.parenL || this.type == types.name)
    { this.potentialArrowAt = this.start; }
  var left = this.parseMaybeConditional(noIn, refDestructuringErrors);
  if (afterLeftParse) { left = afterLeftParse.call(this, left, startPos, startLoc); }
  if (this.type.isAssign) {
    this.checkPatternErrors(refDestructuringErrors, true);
    if (!ownDestructuringErrors) { DestructuringErrors.call(refDestructuringErrors); }
    var node = this.startNodeAt(startPos, startLoc);
    node.operator = this.value;
    node.left = this.type === types.eq ? this.toAssignable(left) : left;
    refDestructuringErrors.shorthandAssign = -1; // reset because shorthand default was used correctly
    this.checkLVal(left);
    this.next();
    node.right = this.parseMaybeAssign(noIn);
    return this.finishNode(node, "AssignmentExpression")
  } else {
    if (ownDestructuringErrors) { this.checkExpressionErrors(refDestructuringErrors, true); }
  }
  if (oldParenAssign > -1) { refDestructuringErrors.parenthesizedAssign = oldParenAssign; }
  if (oldTrailingComma > -1) { refDestructuringErrors.trailingComma = oldTrailingComma; }
  return left
};

// Parse a ternary conditional (`?:`) operator.

pp$3.parseMaybeConditional = function(noIn, refDestructuringErrors) {
  var startPos = this.start, startLoc = this.startLoc;
  var expr = this.parseExprOps(noIn, refDestructuringErrors);
  if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }
  if (this.eat(types.question)) {
    var node = this.startNodeAt(startPos, startLoc);
    node.test = expr;
    node.consequent = this.parseMaybeAssign();
    this.expect(types.colon);
    node.alternate = this.parseMaybeAssign(noIn);
    return this.finishNode(node, "ConditionalExpression")
  }
  return expr
};

// Start the precedence parser.

pp$3.parseExprOps = function(noIn, refDestructuringErrors) {
  var startPos = this.start, startLoc = this.startLoc;
  var expr = this.parseMaybeUnary(refDestructuringErrors, false);
  if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }
  return expr.start == startPos && expr.type === "ArrowFunctionExpression" ? expr : this.parseExprOp(expr, startPos, startLoc, -1, noIn)
};

// Parse binary operators with the operator precedence parsing
// algorithm. `left` is the left-hand side of the operator.
// `minPrec` provides context that allows the function to stop and
// defer further parser to one of its callers when it encounters an
// operator that has a lower precedence than the set it is parsing.

pp$3.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
  var prec = this.type.binop;
  if (prec != null && (!noIn || this.type !== types._in)) {
    if (prec > minPrec) {
      var logical = this.type === types.logicalOR || this.type === types.logicalAND;
      var op = this.value;
      this.next();
      var startPos = this.start, startLoc = this.startLoc;
      var right = this.parseExprOp(this.parseMaybeUnary(null, false), startPos, startLoc, prec, noIn);
      var node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical);
      return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn)
    }
  }
  return left
};

pp$3.buildBinary = function(startPos, startLoc, left, right, op, logical) {
  var node = this.startNodeAt(startPos, startLoc);
  node.left = left;
  node.operator = op;
  node.right = right;
  return this.finishNode(node, logical ? "LogicalExpression" : "BinaryExpression")
};

// Parse unary operators, both prefix and postfix.

pp$3.parseMaybeUnary = function(refDestructuringErrors, sawUnary) {
  var this$1 = this;

  var startPos = this.start, startLoc = this.startLoc, expr;
  if (this.inAsync && this.isContextual("await")) {
    expr = this.parseAwait(refDestructuringErrors);
    sawUnary = true;
  } else if (this.type.prefix) {
    var node = this.startNode(), update = this.type === types.incDec;
    node.operator = this.value;
    node.prefix = true;
    this.next();
    node.argument = this.parseMaybeUnary(null, true);
    this.checkExpressionErrors(refDestructuringErrors, true);
    if (update) { this.checkLVal(node.argument); }
    else if (this.strict && node.operator === "delete" &&
             node.argument.type === "Identifier")
      { this.raiseRecoverable(node.start, "Deleting local variable in strict mode"); }
    else { sawUnary = true; }
    expr = this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
  } else {
    expr = this.parseExprSubscripts(refDestructuringErrors);
    if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }
    while (this.type.postfix && !this.canInsertSemicolon()) {
      var node$1 = this$1.startNodeAt(startPos, startLoc);
      node$1.operator = this$1.value;
      node$1.prefix = false;
      node$1.argument = expr;
      this$1.checkLVal(expr);
      this$1.next();
      expr = this$1.finishNode(node$1, "UpdateExpression");
    }
  }

  if (!sawUnary && this.eat(types.starstar))
    { return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false), "**", false) }
  else
    { return expr }
};

// Parse call, dot, and `[]`-subscript expressions.

pp$3.parseExprSubscripts = function(refDestructuringErrors) {
  var startPos = this.start, startLoc = this.startLoc;
  var expr = this.parseExprAtom(refDestructuringErrors);
  var skipArrowSubscripts = expr.type === "ArrowFunctionExpression" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== ")";
  if (this.checkExpressionErrors(refDestructuringErrors) || skipArrowSubscripts) { return expr }
  var result = this.parseSubscripts(expr, startPos, startLoc);
  if (refDestructuringErrors && result.type === "MemberExpression") {
    if (refDestructuringErrors.parenthesizedAssign >= result.start) { refDestructuringErrors.parenthesizedAssign = -1; }
    if (refDestructuringErrors.parenthesizedBind >= result.start) { refDestructuringErrors.parenthesizedBind = -1; }
  }
  return result
};

pp$3.parseSubscripts = function(base, startPos, startLoc, noCalls) {
  var this$1 = this;

  var maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === "Identifier" && base.name === "async" &&
      this.lastTokEnd == base.end && !this.canInsertSemicolon();
  for (var computed = (void 0);;) {
    if ((computed = this$1.eat(types.bracketL)) || this$1.eat(types.dot)) {
      var node = this$1.startNodeAt(startPos, startLoc);
      node.object = base;
      node.property = computed ? this$1.parseExpression() : this$1.parseIdent(true);
      node.computed = !!computed;
      if (computed) { this$1.expect(types.bracketR); }
      base = this$1.finishNode(node, "MemberExpression");
    } else if (!noCalls && this$1.eat(types.parenL)) {
      var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this$1.yieldPos, oldAwaitPos = this$1.awaitPos;
      this$1.yieldPos = 0;
      this$1.awaitPos = 0;
      var exprList = this$1.parseExprList(types.parenR, this$1.options.ecmaVersion >= 8, false, refDestructuringErrors);
      if (maybeAsyncArrow && !this$1.canInsertSemicolon() && this$1.eat(types.arrow)) {
        this$1.checkPatternErrors(refDestructuringErrors, false);
        this$1.checkYieldAwaitInDefaultParams();
        this$1.yieldPos = oldYieldPos;
        this$1.awaitPos = oldAwaitPos;
        return this$1.parseArrowExpression(this$1.startNodeAt(startPos, startLoc), exprList, true)
      }
      this$1.checkExpressionErrors(refDestructuringErrors, true);
      this$1.yieldPos = oldYieldPos || this$1.yieldPos;
      this$1.awaitPos = oldAwaitPos || this$1.awaitPos;
      var node$1 = this$1.startNodeAt(startPos, startLoc);
      node$1.callee = base;
      node$1.arguments = exprList;
      base = this$1.finishNode(node$1, "CallExpression");
    } else if (this$1.type === types.backQuote) {
      var node$2 = this$1.startNodeAt(startPos, startLoc);
      node$2.tag = base;
      node$2.quasi = this$1.parseTemplate({isTagged: true});
      base = this$1.finishNode(node$2, "TaggedTemplateExpression");
    } else {
      return base
    }
  }
};

// Parse an atomic expression — either a single token that is an
// expression, an expression started by a keyword like `function` or
// `new`, or an expression wrapped in punctuation like `()`, `[]`,
// or `{}`.

pp$3.parseExprAtom = function(refDestructuringErrors) {
  var node, canBeArrow = this.potentialArrowAt == this.start;
  switch (this.type) {
  case types._super:
    if (!this.inFunction)
      { this.raise(this.start, "'super' outside of function or class"); }

  case types._this:
    var type = this.type === types._this ? "ThisExpression" : "Super";
    node = this.startNode();
    this.next();
    return this.finishNode(node, type)

  case types.name:
    var startPos = this.start, startLoc = this.startLoc;
    var id = this.parseIdent(this.type !== types.name);
    if (this.options.ecmaVersion >= 8 && id.name === "async" && !this.canInsertSemicolon() && this.eat(types._function))
      { return this.parseFunction(this.startNodeAt(startPos, startLoc), false, false, true) }
    if (canBeArrow && !this.canInsertSemicolon()) {
      if (this.eat(types.arrow))
        { return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], false) }
      if (this.options.ecmaVersion >= 8 && id.name === "async" && this.type === types.name) {
        id = this.parseIdent();
        if (this.canInsertSemicolon() || !this.eat(types.arrow))
          { this.unexpected(); }
        return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], true)
      }
    }
    return id

  case types.regexp:
    var value = this.value;
    node = this.parseLiteral(value.value);
    node.regex = {pattern: value.pattern, flags: value.flags};
    return node

  case types.num: case types.string:
    return this.parseLiteral(this.value)

  case types._null: case types._true: case types._false:
    node = this.startNode();
    node.value = this.type === types._null ? null : this.type === types._true;
    node.raw = this.type.keyword;
    this.next();
    return this.finishNode(node, "Literal")

  case types.parenL:
    var start = this.start, expr = this.parseParenAndDistinguishExpression(canBeArrow);
    if (refDestructuringErrors) {
      if (refDestructuringErrors.parenthesizedAssign < 0 && !this.isSimpleAssignTarget(expr))
        { refDestructuringErrors.parenthesizedAssign = start; }
      if (refDestructuringErrors.parenthesizedBind < 0)
        { refDestructuringErrors.parenthesizedBind = start; }
    }
    return expr

  case types.bracketL:
    node = this.startNode();
    this.next();
    node.elements = this.parseExprList(types.bracketR, true, true, refDestructuringErrors);
    return this.finishNode(node, "ArrayExpression")

  case types.braceL:
    return this.parseObj(false, refDestructuringErrors)

  case types._function:
    node = this.startNode();
    this.next();
    return this.parseFunction(node, false)

  case types._class:
    return this.parseClass(this.startNode(), false)

  case types._new:
    return this.parseNew()

  case types.backQuote:
    return this.parseTemplate()

  default:
    this.unexpected();
  }
};

pp$3.parseLiteral = function(value) {
  var node = this.startNode();
  node.value = value;
  node.raw = this.input.slice(this.start, this.end);
  this.next();
  return this.finishNode(node, "Literal")
};

pp$3.parseParenExpression = function() {
  this.expect(types.parenL);
  var val = this.parseExpression();
  this.expect(types.parenR);
  return val
};

pp$3.parseParenAndDistinguishExpression = function(canBeArrow) {
  var this$1 = this;

  var startPos = this.start, startLoc = this.startLoc, val, allowTrailingComma = this.options.ecmaVersion >= 8;
  if (this.options.ecmaVersion >= 6) {
    this.next();

    var innerStartPos = this.start, innerStartLoc = this.startLoc;
    var exprList = [], first = true, lastIsComma = false;
    var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, spreadStart, innerParenStart;
    this.yieldPos = 0;
    this.awaitPos = 0;
    while (this.type !== types.parenR) {
      first ? first = false : this$1.expect(types.comma);
      if (allowTrailingComma && this$1.afterTrailingComma(types.parenR, true)) {
        lastIsComma = true;
        break
      } else if (this$1.type === types.ellipsis) {
        spreadStart = this$1.start;
        exprList.push(this$1.parseParenItem(this$1.parseRestBinding()));
        if (this$1.type === types.comma) { this$1.raise(this$1.start, "Comma is not permitted after the rest element"); }
        break
      } else {
        if (this$1.type === types.parenL && !innerParenStart) {
          innerParenStart = this$1.start;
        }
        exprList.push(this$1.parseMaybeAssign(false, refDestructuringErrors, this$1.parseParenItem));
      }
    }
    var innerEndPos = this.start, innerEndLoc = this.startLoc;
    this.expect(types.parenR);

    if (canBeArrow && !this.canInsertSemicolon() && this.eat(types.arrow)) {
      this.checkPatternErrors(refDestructuringErrors, false);
      this.checkYieldAwaitInDefaultParams();
      if (innerParenStart) { this.unexpected(innerParenStart); }
      this.yieldPos = oldYieldPos;
      this.awaitPos = oldAwaitPos;
      return this.parseParenArrowList(startPos, startLoc, exprList)
    }

    if (!exprList.length || lastIsComma) { this.unexpected(this.lastTokStart); }
    if (spreadStart) { this.unexpected(spreadStart); }
    this.checkExpressionErrors(refDestructuringErrors, true);
    this.yieldPos = oldYieldPos || this.yieldPos;
    this.awaitPos = oldAwaitPos || this.awaitPos;

    if (exprList.length > 1) {
      val = this.startNodeAt(innerStartPos, innerStartLoc);
      val.expressions = exprList;
      this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc);
    } else {
      val = exprList[0];
    }
  } else {
    val = this.parseParenExpression();
  }

  if (this.options.preserveParens) {
    var par = this.startNodeAt(startPos, startLoc);
    par.expression = val;
    return this.finishNode(par, "ParenthesizedExpression")
  } else {
    return val
  }
};

pp$3.parseParenItem = function(item) {
  return item
};

pp$3.parseParenArrowList = function(startPos, startLoc, exprList) {
  return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList)
};

// New's precedence is slightly tricky. It must allow its argument to
// be a `[]` or dot subscript expression, but not a call — at least,
// not without wrapping it in parentheses. Thus, it uses the noCalls
// argument to parseSubscripts to prevent it from consuming the
// argument list.

var empty$1 = [];

pp$3.parseNew = function() {
  var node = this.startNode();
  var meta = this.parseIdent(true);
  if (this.options.ecmaVersion >= 6 && this.eat(types.dot)) {
    node.meta = meta;
    node.property = this.parseIdent(true);
    if (node.property.name !== "target")
      { this.raiseRecoverable(node.property.start, "The only valid meta property for new is new.target"); }
    if (!this.inFunction)
      { this.raiseRecoverable(node.start, "new.target can only be used in functions"); }
    return this.finishNode(node, "MetaProperty")
  }
  var startPos = this.start, startLoc = this.startLoc;
  node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true);
  if (this.eat(types.parenL)) { node.arguments = this.parseExprList(types.parenR, this.options.ecmaVersion >= 8, false); }
  else { node.arguments = empty$1; }
  return this.finishNode(node, "NewExpression")
};

// Parse template expression.

pp$3.parseTemplateElement = function(ref) {
  var isTagged = ref.isTagged;

  var elem = this.startNode();
  if (this.type === types.invalidTemplate) {
    if (!isTagged) {
      this.raiseRecoverable(this.start, "Bad escape sequence in untagged template literal");
    }
    elem.value = {
      raw: this.value,
      cooked: null
    };
  } else {
    elem.value = {
      raw: this.input.slice(this.start, this.end).replace(/\r\n?/g, "\n"),
      cooked: this.value
    };
  }
  this.next();
  elem.tail = this.type === types.backQuote;
  return this.finishNode(elem, "TemplateElement")
};

pp$3.parseTemplate = function(ref) {
  var this$1 = this;
  if ( ref === void 0 ) ref = {};
  var isTagged = ref.isTagged; if ( isTagged === void 0 ) isTagged = false;

  var node = this.startNode();
  this.next();
  node.expressions = [];
  var curElt = this.parseTemplateElement({isTagged: isTagged});
  node.quasis = [curElt];
  while (!curElt.tail) {
    this$1.expect(types.dollarBraceL);
    node.expressions.push(this$1.parseExpression());
    this$1.expect(types.braceR);
    node.quasis.push(curElt = this$1.parseTemplateElement({isTagged: isTagged}));
  }
  this.next();
  return this.finishNode(node, "TemplateLiteral")
};

// Parse an object literal or binding pattern.

pp$3.isAsyncProp = function(prop) {
  return !prop.computed && prop.key.type === "Identifier" && prop.key.name === "async" &&
    (this.type === types.name || this.type === types.num || this.type === types.string || this.type === types.bracketL) &&
    !lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
};

pp$3.parseObj = function(isPattern, refDestructuringErrors) {
  var this$1 = this;

  var node = this.startNode(), first = true, propHash = {};
  node.properties = [];
  this.next();
  while (!this.eat(types.braceR)) {
    if (!first) {
      this$1.expect(types.comma);
      if (this$1.afterTrailingComma(types.braceR)) { break }
    } else { first = false; }

    var prop = this$1.startNode(), isGenerator = (void 0), isAsync = (void 0), startPos = (void 0), startLoc = (void 0);
    if (this$1.options.ecmaVersion >= 6) {
      prop.method = false;
      prop.shorthand = false;
      if (isPattern || refDestructuringErrors) {
        startPos = this$1.start;
        startLoc = this$1.startLoc;
      }
      if (!isPattern)
        { isGenerator = this$1.eat(types.star); }
    }
    this$1.parsePropertyName(prop);
    if (!isPattern && this$1.options.ecmaVersion >= 8 && !isGenerator && this$1.isAsyncProp(prop)) {
      isAsync = true;
      this$1.parsePropertyName(prop, refDestructuringErrors);
    } else {
      isAsync = false;
    }
    this$1.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors);
    this$1.checkPropClash(prop, propHash);
    node.properties.push(this$1.finishNode(prop, "Property"));
  }
  return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression")
};

pp$3.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors) {
  if ((isGenerator || isAsync) && this.type === types.colon)
    { this.unexpected(); }

  if (this.eat(types.colon)) {
    prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refDestructuringErrors);
    prop.kind = "init";
  } else if (this.options.ecmaVersion >= 6 && this.type === types.parenL) {
    if (isPattern) { this.unexpected(); }
    prop.kind = "init";
    prop.method = true;
    prop.value = this.parseMethod(isGenerator, isAsync);
  } else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
             (prop.key.name === "get" || prop.key.name === "set") &&
             (this.type != types.comma && this.type != types.braceR)) {
    if (isGenerator || isAsync || isPattern) { this.unexpected(); }
    prop.kind = prop.key.name;
    this.parsePropertyName(prop);
    prop.value = this.parseMethod(false);
    var paramCount = prop.kind === "get" ? 0 : 1;
    if (prop.value.params.length !== paramCount) {
      var start = prop.value.start;
      if (prop.kind === "get")
        { this.raiseRecoverable(start, "getter should have no params"); }
      else
        { this.raiseRecoverable(start, "setter should have exactly one param"); }
    } else {
      if (prop.kind === "set" && prop.value.params[0].type === "RestElement")
        { this.raiseRecoverable(prop.value.params[0].start, "Setter cannot use rest params"); }
    }
  } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
    this.checkUnreserved(prop.key);
    prop.kind = "init";
    if (isPattern) {
      prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
    } else if (this.type === types.eq && refDestructuringErrors) {
      if (refDestructuringErrors.shorthandAssign < 0)
        { refDestructuringErrors.shorthandAssign = this.start; }
      prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
    } else {
      prop.value = prop.key;
    }
    prop.shorthand = true;
  } else { this.unexpected(); }
};

pp$3.parsePropertyName = function(prop) {
  if (this.options.ecmaVersion >= 6) {
    if (this.eat(types.bracketL)) {
      prop.computed = true;
      prop.key = this.parseMaybeAssign();
      this.expect(types.bracketR);
      return prop.key
    } else {
      prop.computed = false;
    }
  }
  return prop.key = this.type === types.num || this.type === types.string ? this.parseExprAtom() : this.parseIdent(true)
};

// Initialize empty function node.

pp$3.initFunction = function(node) {
  node.id = null;
  if (this.options.ecmaVersion >= 6) {
    node.generator = false;
    node.expression = false;
  }
  if (this.options.ecmaVersion >= 8)
    { node.async = false; }
};

// Parse object or class method.

pp$3.parseMethod = function(isGenerator, isAsync) {
  var node = this.startNode(), oldInGen = this.inGenerator, oldInAsync = this.inAsync,
      oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldInFunc = this.inFunction;

  this.initFunction(node);
  if (this.options.ecmaVersion >= 6)
    { node.generator = isGenerator; }
  if (this.options.ecmaVersion >= 8)
    { node.async = !!isAsync; }

  this.inGenerator = node.generator;
  this.inAsync = node.async;
  this.yieldPos = 0;
  this.awaitPos = 0;
  this.inFunction = true;
  this.enterFunctionScope();

  this.expect(types.parenL);
  node.params = this.parseBindingList(types.parenR, false, this.options.ecmaVersion >= 8);
  this.checkYieldAwaitInDefaultParams();
  this.parseFunctionBody(node, false);

  this.inGenerator = oldInGen;
  this.inAsync = oldInAsync;
  this.yieldPos = oldYieldPos;
  this.awaitPos = oldAwaitPos;
  this.inFunction = oldInFunc;
  return this.finishNode(node, "FunctionExpression")
};

// Parse arrow function expression with given parameters.

pp$3.parseArrowExpression = function(node, params, isAsync) {
  var oldInGen = this.inGenerator, oldInAsync = this.inAsync,
      oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldInFunc = this.inFunction;

  this.enterFunctionScope();
  this.initFunction(node);
  if (this.options.ecmaVersion >= 8)
    { node.async = !!isAsync; }

  this.inGenerator = false;
  this.inAsync = node.async;
  this.yieldPos = 0;
  this.awaitPos = 0;
  this.inFunction = true;

  node.params = this.toAssignableList(params, true);
  this.parseFunctionBody(node, true);

  this.inGenerator = oldInGen;
  this.inAsync = oldInAsync;
  this.yieldPos = oldYieldPos;
  this.awaitPos = oldAwaitPos;
  this.inFunction = oldInFunc;
  return this.finishNode(node, "ArrowFunctionExpression")
};

// Parse function body and check parameters.

pp$3.parseFunctionBody = function(node, isArrowFunction) {
  var isExpression = isArrowFunction && this.type !== types.braceL;
  var oldStrict = this.strict, useStrict = false;

  if (isExpression) {
    node.body = this.parseMaybeAssign();
    node.expression = true;
    this.checkParams(node, false);
  } else {
    var nonSimple = this.options.ecmaVersion >= 7 && !this.isSimpleParamList(node.params);
    if (!oldStrict || nonSimple) {
      useStrict = this.strictDirective(this.end);
      // If this is a strict mode function, verify that argument names
      // are not repeated, and it does not try to bind the words `eval`
      // or `arguments`.
      if (useStrict && nonSimple)
        { this.raiseRecoverable(node.start, "Illegal 'use strict' directive in function with non-simple parameter list"); }
    }
    // Start a new scope with regard to labels and the `inFunction`
    // flag (restore them to their old value afterwards).
    var oldLabels = this.labels;
    this.labels = [];
    if (useStrict) { this.strict = true; }

    // Add the params to varDeclaredNames to ensure that an error is thrown
    // if a let/const declaration in the function clashes with one of the params.
    this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && this.isSimpleParamList(node.params));
    node.body = this.parseBlock(false);
    node.expression = false;
    this.labels = oldLabels;
  }
  this.exitFunctionScope();

  if (this.strict && node.id) {
    // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval'
    this.checkLVal(node.id, "none");
  }
  this.strict = oldStrict;
};

pp$3.isSimpleParamList = function(params) {
  for (var i = 0, list = params; i < list.length; i += 1)
    {
    var param = list[i];

    if (param.type !== "Identifier") { return false
  } }
  return true
};

// Checks function params for various disallowed patterns such as using "eval"
// or "arguments" and duplicate parameters.

pp$3.checkParams = function(node, allowDuplicates) {
  var this$1 = this;

  var nameHash = {};
  for (var i = 0, list = node.params; i < list.length; i += 1)
    {
    var param = list[i];

    this$1.checkLVal(param, "var", allowDuplicates ? null : nameHash);
  }
};

// Parses a comma-separated list of expressions, and returns them as
// an array. `close` is the token type that ends the list, and
// `allowEmpty` can be turned on to allow subsequent commas with
// nothing in between them to be parsed as `null` (which is needed
// for array literals).

pp$3.parseExprList = function(close, allowTrailingComma, allowEmpty, refDestructuringErrors) {
  var this$1 = this;

  var elts = [], first = true;
  while (!this.eat(close)) {
    if (!first) {
      this$1.expect(types.comma);
      if (allowTrailingComma && this$1.afterTrailingComma(close)) { break }
    } else { first = false; }

    var elt = (void 0);
    if (allowEmpty && this$1.type === types.comma)
      { elt = null; }
    else if (this$1.type === types.ellipsis) {
      elt = this$1.parseSpread(refDestructuringErrors);
      if (refDestructuringErrors && this$1.type === types.comma && refDestructuringErrors.trailingComma < 0)
        { refDestructuringErrors.trailingComma = this$1.start; }
    } else {
      elt = this$1.parseMaybeAssign(false, refDestructuringErrors);
    }
    elts.push(elt);
  }
  return elts
};

// Parse the next token as an identifier. If `liberal` is true (used
// when parsing properties), it will also convert keywords into
// identifiers.

pp$3.checkUnreserved = function(ref) {
  var start = ref.start;
  var end = ref.end;
  var name = ref.name;

  if (this.inGenerator && name === "yield")
    { this.raiseRecoverable(start, "Can not use 'yield' as identifier inside a generator"); }
  if (this.inAsync && name === "await")
    { this.raiseRecoverable(start, "Can not use 'await' as identifier inside an async function"); }
  if (this.isKeyword(name))
    { this.raise(start, ("Unexpected keyword '" + name + "'")); }
  if (this.options.ecmaVersion < 6 &&
    this.input.slice(start, end).indexOf("\\") != -1) { return }
  var re = this.strict ? this.reservedWordsStrict : this.reservedWords;
  if (re.test(name))
    { this.raiseRecoverable(start, ("The keyword '" + name + "' is reserved")); }
};

pp$3.parseIdent = function(liberal, isBinding) {
  var node = this.startNode();
  if (liberal && this.options.allowReserved == "never") { liberal = false; }
  if (this.type === types.name) {
    node.name = this.value;
  } else if (this.type.keyword) {
    node.name = this.type.keyword;
  } else {
    this.unexpected();
  }
  this.next();
  this.finishNode(node, "Identifier");
  if (!liberal) { this.checkUnreserved(node); }
  return node
};

// Parses yield expression inside generator.

pp$3.parseYield = function() {
  if (!this.yieldPos) { this.yieldPos = this.start; }

  var node = this.startNode();
  this.next();
  if (this.type == types.semi || this.canInsertSemicolon() || (this.type != types.star && !this.type.startsExpr)) {
    node.delegate = false;
    node.argument = null;
  } else {
    node.delegate = this.eat(types.star);
    node.argument = this.parseMaybeAssign();
  }
  return this.finishNode(node, "YieldExpression")
};

pp$3.parseAwait = function() {
  if (!this.awaitPos) { this.awaitPos = this.start; }

  var node = this.startNode();
  this.next();
  node.argument = this.parseMaybeUnary(null, true);
  return this.finishNode(node, "AwaitExpression")
};

var pp$4 = Parser.prototype;

// This function is used to raise exceptions on parse errors. It
// takes an offset integer (into the current `input`) to indicate
// the location of the error, attaches the position to the end
// of the error message, and then raises a `SyntaxError` with that
// message.

pp$4.raise = function(pos, message) {
  var loc = getLineInfo(this.input, pos);
  message += " (" + loc.line + ":" + loc.column + ")";
  var err = new SyntaxError(message);
  err.pos = pos; err.loc = loc; err.raisedAt = this.pos;
  throw err
};

pp$4.raiseRecoverable = pp$4.raise;

pp$4.curPosition = function() {
  if (this.options.locations) {
    return new Position(this.curLine, this.pos - this.lineStart)
  }
};

var pp$5 = Parser.prototype;

// Object.assign polyfill
var assign = Object.assign || function(target) {
  var sources = [], len = arguments.length - 1;
  while ( len-- > 0 ) sources[ len ] = arguments[ len + 1 ];

  for (var i = 0, list = sources; i < list.length; i += 1) {
    var source = list[i];

    for (var key in source) {
      if (has(source, key)) {
        target[key] = source[key];
      }
    }
  }
  return target
};

// The functions in this module keep track of declared variables in the current scope in order to detect duplicate variable names.

pp$5.enterFunctionScope = function() {
  // var: a hash of var-declared names in the current lexical scope
  // lexical: a hash of lexically-declared names in the current lexical scope
  // childVar: a hash of var-declared names in all child lexical scopes of the current lexical scope (within the current function scope)
  // parentLexical: a hash of lexically-declared names in all parent lexical scopes of the current lexical scope (within the current function scope)
  this.scopeStack.push({var: {}, lexical: {}, childVar: {}, parentLexical: {}});
};

pp$5.exitFunctionScope = function() {
  this.scopeStack.pop();
};

pp$5.enterLexicalScope = function() {
  var parentScope = this.scopeStack[this.scopeStack.length - 1];
  var childScope = {var: {}, lexical: {}, childVar: {}, parentLexical: {}};

  this.scopeStack.push(childScope);
  assign(childScope.parentLexical, parentScope.lexical, parentScope.parentLexical);
};

pp$5.exitLexicalScope = function() {
  var childScope = this.scopeStack.pop();
  var parentScope = this.scopeStack[this.scopeStack.length - 1];

  assign(parentScope.childVar, childScope.var, childScope.childVar);
};

/**
 * A name can be declared with `var` if there are no variables with the same name declared with `let`/`const`
 * in the current lexical scope or any of the parent lexical scopes in this function.
 */
pp$5.canDeclareVarName = function(name) {
  var currentScope = this.scopeStack[this.scopeStack.length - 1];

  return !has(currentScope.lexical, name) && !has(currentScope.parentLexical, name)
};

/**
 * A name can be declared with `let`/`const` if there are no variables with the same name declared with `let`/`const`
 * in the current scope, and there are no variables with the same name declared with `var` in the current scope or in
 * any child lexical scopes in this function.
 */
pp$5.canDeclareLexicalName = function(name) {
  var currentScope = this.scopeStack[this.scopeStack.length - 1];

  return !has(currentScope.lexical, name) && !has(currentScope.var, name) && !has(currentScope.childVar, name)
};

pp$5.declareVarName = function(name) {
  this.scopeStack[this.scopeStack.length - 1].var[name] = true;
};

pp$5.declareLexicalName = function(name) {
  this.scopeStack[this.scopeStack.length - 1].lexical[name] = true;
};

var Node = function Node(parser, pos, loc) {
  this.type = "";
  this.start = pos;
  this.end = 0;
  if (parser.options.locations)
    { this.loc = new SourceLocation(parser, loc); }
  if (parser.options.directSourceFile)
    { this.sourceFile = parser.options.directSourceFile; }
  if (parser.options.ranges)
    { this.range = [pos, 0]; }
};

// Start an AST node, attaching a start offset.

var pp$6 = Parser.prototype;

pp$6.startNode = function() {
  return new Node(this, this.start, this.startLoc)
};

pp$6.startNodeAt = function(pos, loc) {
  return new Node(this, pos, loc)
};

// Finish an AST node, adding `type` and `end` properties.

function finishNodeAt(node, type, pos, loc) {
  node.type = type;
  node.end = pos;
  if (this.options.locations)
    { node.loc.end = loc; }
  if (this.options.ranges)
    { node.range[1] = pos; }
  return node
}

pp$6.finishNode = function(node, type) {
  return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc)
};

// Finish node at given position

pp$6.finishNodeAt = function(node, type, pos, loc) {
  return finishNodeAt.call(this, node, type, pos, loc)
};

// The algorithm used to determine whether a regexp can appear at a
// given point in the program is loosely based on sweet.js' approach.
// See https://github.com/mozilla/sweet.js/wiki/design

var TokContext = function TokContext(token, isExpr, preserveSpace, override, generator) {
  this.token = token;
  this.isExpr = !!isExpr;
  this.preserveSpace = !!preserveSpace;
  this.override = override;
  this.generator = !!generator;
};

var types$1 = {
  b_stat: new TokContext("{", false),
  b_expr: new TokContext("{", true),
  b_tmpl: new TokContext("${", false),
  p_stat: new TokContext("(", false),
  p_expr: new TokContext("(", true),
  q_tmpl: new TokContext("`", true, true, function (p) { return p.tryReadTemplateToken(); }),
  f_stat: new TokContext("function", false),
  f_expr: new TokContext("function", true),
  f_expr_gen: new TokContext("function", true, false, null, true),
  f_gen: new TokContext("function", false, false, null, true)
};

var pp$7 = Parser.prototype;

pp$7.initialContext = function() {
  return [types$1.b_stat]
};

pp$7.braceIsBlock = function(prevType) {
  var parent = this.curContext();
  if (parent === types$1.f_expr || parent === types$1.f_stat)
    { return true }
  if (prevType === types.colon && (parent === types$1.b_stat || parent === types$1.b_expr))
    { return !parent.isExpr }

  // The check for `tt.name && exprAllowed` detects whether we are
  // after a `yield` or `of` construct. See the `updateContext` for
  // `tt.name`.
  if (prevType === types._return || prevType == types.name && this.exprAllowed)
    { return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) }
  if (prevType === types._else || prevType === types.semi || prevType === types.eof || prevType === types.parenR || prevType == types.arrow)
    { return true }
  if (prevType == types.braceL)
    { return parent === types$1.b_stat }
  if (prevType == types._var || prevType == types.name)
    { return false }
  return !this.exprAllowed
};

pp$7.inGeneratorContext = function() {
  var this$1 = this;

  for (var i = this.context.length - 1; i >= 1; i--) {
    var context = this$1.context[i];
    if (context.token === "function")
      { return context.generator }
  }
  return false
};

pp$7.updateContext = function(prevType) {
  var update, type = this.type;
  if (type.keyword && prevType == types.dot)
    { this.exprAllowed = false; }
  else if (update = type.updateContext)
    { update.call(this, prevType); }
  else
    { this.exprAllowed = type.beforeExpr; }
};

// Token-specific context update code

types.parenR.updateContext = types.braceR.updateContext = function() {
  if (this.context.length == 1) {
    this.exprAllowed = true;
    return
  }
  var out = this.context.pop();
  if (out === types$1.b_stat && this.curContext().token === "function") {
    out = this.context.pop();
  }
  this.exprAllowed = !out.isExpr;
};

types.braceL.updateContext = function(prevType) {
  this.context.push(this.braceIsBlock(prevType) ? types$1.b_stat : types$1.b_expr);
  this.exprAllowed = true;
};

types.dollarBraceL.updateContext = function() {
  this.context.push(types$1.b_tmpl);
  this.exprAllowed = true;
};

types.parenL.updateContext = function(prevType) {
  var statementParens = prevType === types._if || prevType === types._for || prevType === types._with || prevType === types._while;
  this.context.push(statementParens ? types$1.p_stat : types$1.p_expr);
  this.exprAllowed = true;
};

types.incDec.updateContext = function() {
  // tokExprAllowed stays unchanged
};

types._function.updateContext = types._class.updateContext = function(prevType) {
  if (prevType.beforeExpr && prevType !== types.semi && prevType !== types._else &&
      !((prevType === types.colon || prevType === types.braceL) && this.curContext() === types$1.b_stat))
    { this.context.push(types$1.f_expr); }
  else
    { this.context.push(types$1.f_stat); }
  this.exprAllowed = false;
};

types.backQuote.updateContext = function() {
  if (this.curContext() === types$1.q_tmpl)
    { this.context.pop(); }
  else
    { this.context.push(types$1.q_tmpl); }
  this.exprAllowed = false;
};

types.star.updateContext = function(prevType) {
  if (prevType == types._function) {
    var index = this.context.length - 1;
    if (this.context[index] === types$1.f_expr)
      { this.context[index] = types$1.f_expr_gen; }
    else
      { this.context[index] = types$1.f_gen; }
  }
  this.exprAllowed = true;
};

types.name.updateContext = function(prevType) {
  var allowed = false;
  if (this.options.ecmaVersion >= 6) {
    if (this.value == "of" && !this.exprAllowed ||
        this.value == "yield" && this.inGeneratorContext())
      { allowed = true; }
  }
  this.exprAllowed = allowed;
};

// Object type used to represent tokens. Note that normally, tokens
// simply exist as properties on the parser object. This is only
// used for the onToken callback and the external tokenizer.

var Token = function Token(p) {
  this.type = p.type;
  this.value = p.value;
  this.start = p.start;
  this.end = p.end;
  if (p.options.locations)
    { this.loc = new SourceLocation(p, p.startLoc, p.endLoc); }
  if (p.options.ranges)
    { this.range = [p.start, p.end]; }
};

// ## Tokenizer

var pp$8 = Parser.prototype;

// Are we running under Rhino?
var isRhino = typeof Packages == "object" && Object.prototype.toString.call(Packages) == "[object JavaPackage]";

// Move to the next token

pp$8.next = function() {
  if (this.options.onToken)
    { this.options.onToken(new Token(this)); }

  this.lastTokEnd = this.end;
  this.lastTokStart = this.start;
  this.lastTokEndLoc = this.endLoc;
  this.lastTokStartLoc = this.startLoc;
  this.nextToken();
};

pp$8.getToken = function() {
  this.next();
  return new Token(this)
};

// If we're in an ES6 environment, make parsers iterable
if (typeof Symbol !== "undefined")
  { pp$8[Symbol.iterator] = function() {
    var this$1 = this;

    return {
      next: function () {
        var token = this$1.getToken();
        return {
          done: token.type === types.eof,
          value: token
        }
      }
    }
  }; }

// Toggle strict mode. Re-reads the next number or string to please
// pedantic tests (`"use strict"; 010;` should fail).

pp$8.curContext = function() {
  return this.context[this.context.length - 1]
};

// Read a single token, updating the parser object's token-related
// properties.

pp$8.nextToken = function() {
  var curContext = this.curContext();
  if (!curContext || !curContext.preserveSpace) { this.skipSpace(); }

  this.start = this.pos;
  if (this.options.locations) { this.startLoc = this.curPosition(); }
  if (this.pos >= this.input.length) { return this.finishToken(types.eof) }

  if (curContext.override) { return curContext.override(this) }
  else { this.readToken(this.fullCharCodeAtPos()); }
};

pp$8.readToken = function(code) {
  // Identifier or keyword. '\uXXXX' sequences are allowed in
  // identifiers, so '\' also dispatches to that.
  if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */)
    { return this.readWord() }

  return this.getTokenFromCode(code)
};

pp$8.fullCharCodeAtPos = function() {
  var code = this.input.charCodeAt(this.pos);
  if (code <= 0xd7ff || code >= 0xe000) { return code }
  var next = this.input.charCodeAt(this.pos + 1);
  return (code << 10) + next - 0x35fdc00
};

pp$8.skipBlockComment = function() {
  var this$1 = this;

  var startLoc = this.options.onComment && this.curPosition();
  var start = this.pos, end = this.input.indexOf("*/", this.pos += 2);
  if (end === -1) { this.raise(this.pos - 2, "Unterminated comment"); }
  this.pos = end + 2;
  if (this.options.locations) {
    lineBreakG.lastIndex = start;
    var match;
    while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) {
      ++this$1.curLine;
      this$1.lineStart = match.index + match[0].length;
    }
  }
  if (this.options.onComment)
    { this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos,
                           startLoc, this.curPosition()); }
};

pp$8.skipLineComment = function(startSkip) {
  var this$1 = this;

  var start = this.pos;
  var startLoc = this.options.onComment && this.curPosition();
  var ch = this.input.charCodeAt(this.pos += startSkip);
  while (this.pos < this.input.length && !isNewLine(ch)) {
    ch = this$1.input.charCodeAt(++this$1.pos);
  }
  if (this.options.onComment)
    { this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos,
                           startLoc, this.curPosition()); }
};

// Called at the start of the parse and after every token. Skips
// whitespace and comments, and.

pp$8.skipSpace = function() {
  var this$1 = this;

  loop: while (this.pos < this.input.length) {
    var ch = this$1.input.charCodeAt(this$1.pos);
    switch (ch) {
    case 32: case 160: // ' '
      ++this$1.pos;
      break
    case 13:
      if (this$1.input.charCodeAt(this$1.pos + 1) === 10) {
        ++this$1.pos;
      }
    case 10: case 8232: case 8233:
      ++this$1.pos;
      if (this$1.options.locations) {
        ++this$1.curLine;
        this$1.lineStart = this$1.pos;
      }
      break
    case 47: // '/'
      switch (this$1.input.charCodeAt(this$1.pos + 1)) {
      case 42: // '*'
        this$1.skipBlockComment();
        break
      case 47:
        this$1.skipLineComment(2);
        break
      default:
        break loop
      }
      break
    default:
      if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {
        ++this$1.pos;
      } else {
        break loop
      }
    }
  }
};

// Called at the end of every token. Sets `end`, `val`, and
// maintains `context` and `exprAllowed`, and skips the space after
// the token, so that the next one's `start` will point at the
// right position.

pp$8.finishToken = function(type, val) {
  this.end = this.pos;
  if (this.options.locations) { this.endLoc = this.curPosition(); }
  var prevType = this.type;
  this.type = type;
  this.value = val;

  this.updateContext(prevType);
};

// ### Token reading

// This is the function that is called to fetch the next token. It
// is somewhat obscure, because it works in character codes rather
// than characters, and because operator parsing has been inlined
// into it.
//
// All in the name of speed.
//
pp$8.readToken_dot = function() {
  var next = this.input.charCodeAt(this.pos + 1);
  if (next >= 48 && next <= 57) { return this.readNumber(true) }
  var next2 = this.input.charCodeAt(this.pos + 2);
  if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.'
    this.pos += 3;
    return this.finishToken(types.ellipsis)
  } else {
    ++this.pos;
    return this.finishToken(types.dot)
  }
};

pp$8.readToken_slash = function() { // '/'
  var next = this.input.charCodeAt(this.pos + 1);
  if (this.exprAllowed) { ++this.pos; return this.readRegexp() }
  if (next === 61) { return this.finishOp(types.assign, 2) }
  return this.finishOp(types.slash, 1)
};

pp$8.readToken_mult_modulo_exp = function(code) { // '%*'
  var next = this.input.charCodeAt(this.pos + 1);
  var size = 1;
  var tokentype = code === 42 ? types.star : types.modulo;

  // exponentiation operator ** and **=
  if (this.options.ecmaVersion >= 7 && next === 42) {
    ++size;
    tokentype = types.starstar;
    next = this.input.charCodeAt(this.pos + 2);
  }

  if (next === 61) { return this.finishOp(types.assign, size + 1) }
  return this.finishOp(tokentype, size)
};

pp$8.readToken_pipe_amp = function(code) { // '|&'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === code) { return this.finishOp(code === 124 ? types.logicalOR : types.logicalAND, 2) }
  if (next === 61) { return this.finishOp(types.assign, 2) }
  return this.finishOp(code === 124 ? types.bitwiseOR : types.bitwiseAND, 1)
};

pp$8.readToken_caret = function() { // '^'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === 61) { return this.finishOp(types.assign, 2) }
  return this.finishOp(types.bitwiseXOR, 1)
};

pp$8.readToken_plus_min = function(code) { // '+-'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === code) {
    if (next == 45 && this.input.charCodeAt(this.pos + 2) == 62 &&
        (this.lastTokEnd === 0 || lineBreak.test(this.input.slice(this.lastTokEnd, this.pos)))) {
      // A `-->` line comment
      this.skipLineComment(3);
      this.skipSpace();
      return this.nextToken()
    }
    return this.finishOp(types.incDec, 2)
  }
  if (next === 61) { return this.finishOp(types.assign, 2) }
  return this.finishOp(types.plusMin, 1)
};

pp$8.readToken_lt_gt = function(code) { // '<>'
  var next = this.input.charCodeAt(this.pos + 1);
  var size = 1;
  if (next === code) {
    size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;
    if (this.input.charCodeAt(this.pos + size) === 61) { return this.finishOp(types.assign, size + 1) }
    return this.finishOp(types.bitShift, size)
  }
  if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 &&
      this.input.charCodeAt(this.pos + 3) == 45) {
    if (this.inModule) { this.unexpected(); }
    // `<!--`, an XML-style comment that should be interpreted as a line comment
    this.skipLineComment(4);
    this.skipSpace();
    return this.nextToken()
  }
  if (next === 61) { size = 2; }
  return this.finishOp(types.relational, size)
};

pp$8.readToken_eq_excl = function(code) { // '=!'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === 61) { return this.finishOp(types.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2) }
  if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) { // '=>'
    this.pos += 2;
    return this.finishToken(types.arrow)
  }
  return this.finishOp(code === 61 ? types.eq : types.prefix, 1)
};

pp$8.getTokenFromCode = function(code) {
  switch (code) {
    // The interpretation of a dot depends on whether it is followed
    // by a digit or another two dots.
  case 46: // '.'
    return this.readToken_dot()

    // Punctuation tokens.
  case 40: ++this.pos; return this.finishToken(types.parenL)
  case 41: ++this.pos; return this.finishToken(types.parenR)
  case 59: ++this.pos; return this.finishToken(types.semi)
  case 44: ++this.pos; return this.finishToken(types.comma)
  case 91: ++this.pos; return this.finishToken(types.bracketL)
  case 93: ++this.pos; return this.finishToken(types.bracketR)
  case 123: ++this.pos; return this.finishToken(types.braceL)
  case 125: ++this.pos; return this.finishToken(types.braceR)
  case 58: ++this.pos; return this.finishToken(types.colon)
  case 63: ++this.pos; return this.finishToken(types.question)

  case 96: // '`'
    if (this.options.ecmaVersion < 6) { break }
    ++this.pos;
    return this.finishToken(types.backQuote)

  case 48: // '0'
    var next = this.input.charCodeAt(this.pos + 1);
    if (next === 120 || next === 88) { return this.readRadixNumber(16) } // '0x', '0X' - hex number
    if (this.options.ecmaVersion >= 6) {
      if (next === 111 || next === 79) { return this.readRadixNumber(8) } // '0o', '0O' - octal number
      if (next === 98 || next === 66) { return this.readRadixNumber(2) } // '0b', '0B' - binary number
    }
    // Anything else beginning with a digit is an integer, octal
    // number, or float.
  case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9
    return this.readNumber(false)

    // Quotes produce strings.
  case 34: case 39: // '"', "'"
    return this.readString(code)

    // Operators are parsed inline in tiny state machines. '=' (61) is
    // often referred to. `finishOp` simply skips the amount of
    // characters it is given as second argument, and returns a token
    // of the type given by its first argument.

  case 47: // '/'
    return this.readToken_slash()

  case 37: case 42: // '%*'
    return this.readToken_mult_modulo_exp(code)

  case 124: case 38: // '|&'
    return this.readToken_pipe_amp(code)

  case 94: // '^'
    return this.readToken_caret()

  case 43: case 45: // '+-'
    return this.readToken_plus_min(code)

  case 60: case 62: // '<>'
    return this.readToken_lt_gt(code)

  case 61: case 33: // '=!'
    return this.readToken_eq_excl(code)

  case 126: // '~'
    return this.finishOp(types.prefix, 1)
  }

  this.raise(this.pos, "Unexpected character '" + codePointToString(code) + "'");
};

pp$8.finishOp = function(type, size) {
  var str = this.input.slice(this.pos, this.pos + size);
  this.pos += size;
  return this.finishToken(type, str)
};

// Parse a regular expression. Some context-awareness is necessary,
// since a '/' inside a '[]' set does not end the expression.

function tryCreateRegexp(src, flags, throwErrorAt, parser) {
  try {
    return new RegExp(src, flags)
  } catch (e) {
    if (throwErrorAt !== undefined) {
      if (e instanceof SyntaxError) { parser.raise(throwErrorAt, "Error parsing regular expression: " + e.message); }
      throw e
    }
  }
}

var regexpUnicodeSupport = !!tryCreateRegexp("\uffff", "u");

pp$8.readRegexp = function() {
  var this$1 = this;

  var escaped, inClass, start = this.pos;
  for (;;) {
    if (this$1.pos >= this$1.input.length) { this$1.raise(start, "Unterminated regular expression"); }
    var ch = this$1.input.charAt(this$1.pos);
    if (lineBreak.test(ch)) { this$1.raise(start, "Unterminated regular expression"); }
    if (!escaped) {
      if (ch === "[") { inClass = true; }
      else if (ch === "]" && inClass) { inClass = false; }
      else if (ch === "/" && !inClass) { break }
      escaped = ch === "\\";
    } else { escaped = false; }
    ++this$1.pos;
  }
  var content = this.input.slice(start, this.pos);
  ++this.pos;
  // Need to use `readWord1` because '\uXXXX' sequences are allowed
  // here (don't ask).
  var mods = this.readWord1();
  var tmp = content, tmpFlags = "";
  if (mods) {
    var validFlags = /^[gim]*$/;
    if (this.options.ecmaVersion >= 6) { validFlags = /^[gimuy]*$/; }
    if (!validFlags.test(mods)) { this.raise(start, "Invalid regular expression flag"); }
    if (mods.indexOf("u") >= 0) {
      if (regexpUnicodeSupport) {
        tmpFlags = "u";
      } else {
        // Replace each astral symbol and every Unicode escape sequence that
        // possibly represents an astral symbol or a paired surrogate with a
        // single ASCII symbol to avoid throwing on regular expressions that
        // are only valid in combination with the `/u` flag.
        // Note: replacing with the ASCII symbol `x` might cause false
        // negatives in unlikely scenarios. For example, `[\u{61}-b]` is a
        // perfectly valid pattern that is equivalent to `[a-b]`, but it would
        // be replaced by `[x-b]` which throws an error.
        tmp = tmp.replace(/\\u\{([0-9a-fA-F]+)\}/g, function (_match, code, offset) {
          code = Number("0x" + code);
          if (code > 0x10FFFF) { this$1.raise(start + offset + 3, "Code point out of bounds"); }
          return "x"
        });
        tmp = tmp.replace(/\\u([a-fA-F0-9]{4})|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x");
        tmpFlags = tmpFlags.replace("u", "");
      }
    }
  }
  // Detect invalid regular expressions.
  var value = null;
  // Rhino's regular expression parser is flaky and throws uncatchable exceptions,
  // so don't do detection if we are running under Rhino
  if (!isRhino) {
    tryCreateRegexp(tmp, tmpFlags, start, this);
    // Get a regular expression object for this pattern-flag pair, or `null` in
    // case the current environment doesn't support the flags it uses.
    value = tryCreateRegexp(content, mods);
  }
  return this.finishToken(types.regexp, {pattern: content, flags: mods, value: value})
};

// Read an integer in the given radix. Return null if zero digits
// were read, the integer value otherwise. When `len` is given, this
// will return `null` unless the integer has exactly `len` digits.

pp$8.readInt = function(radix, len) {
  var this$1 = this;

  var start = this.pos, total = 0;
  for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) {
    var code = this$1.input.charCodeAt(this$1.pos), val = (void 0);
    if (code >= 97) { val = code - 97 + 10; } // a
    else if (code >= 65) { val = code - 65 + 10; } // A
    else if (code >= 48 && code <= 57) { val = code - 48; } // 0-9
    else { val = Infinity; }
    if (val >= radix) { break }
    ++this$1.pos;
    total = total * radix + val;
  }
  if (this.pos === start || len != null && this.pos - start !== len) { return null }

  return total
};

pp$8.readRadixNumber = function(radix) {
  this.pos += 2; // 0x
  var val = this.readInt(radix);
  if (val == null) { this.raise(this.start + 2, "Expected number in radix " + radix); }
  if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, "Identifier directly after number"); }
  return this.finishToken(types.num, val)
};

// Read an integer, octal integer, or floating-point number.

pp$8.readNumber = function(startsWithDot) {
  var start = this.pos, isFloat = false, octal = this.input.charCodeAt(this.pos) === 48;
  if (!startsWithDot && this.readInt(10) === null) { this.raise(start, "Invalid number"); }
  if (octal && this.pos == start + 1) { octal = false; }
  var next = this.input.charCodeAt(this.pos);
  if (next === 46 && !octal) { // '.'
    ++this.pos;
    this.readInt(10);
    isFloat = true;
    next = this.input.charCodeAt(this.pos);
  }
  if ((next === 69 || next === 101) && !octal) { // 'eE'
    next = this.input.charCodeAt(++this.pos);
    if (next === 43 || next === 45) { ++this.pos; } // '+-'
    if (this.readInt(10) === null) { this.raise(start, "Invalid number"); }
    isFloat = true;
  }
  if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, "Identifier directly after number"); }

  var str = this.input.slice(start, this.pos), val;
  if (isFloat) { val = parseFloat(str); }
  else if (!octal || str.length === 1) { val = parseInt(str, 10); }
  else if (this.strict) { this.raise(start, "Invalid number"); }
  else if (/[89]/.test(str)) { val = parseInt(str, 10); }
  else { val = parseInt(str, 8); }
  return this.finishToken(types.num, val)
};

// Read a string value, interpreting backslash-escapes.

pp$8.readCodePoint = function() {
  var ch = this.input.charCodeAt(this.pos), code;

  if (ch === 123) { // '{'
    if (this.options.ecmaVersion < 6) { this.unexpected(); }
    var codePos = ++this.pos;
    code = this.readHexChar(this.input.indexOf("}", this.pos) - this.pos);
    ++this.pos;
    if (code > 0x10FFFF) { this.invalidStringToken(codePos, "Code point out of bounds"); }
  } else {
    code = this.readHexChar(4);
  }
  return code
};

function codePointToString(code) {
  // UTF-16 Decoding
  if (code <= 0xFFFF) { return String.fromCharCode(code) }
  code -= 0x10000;
  return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00)
}

pp$8.readString = function(quote) {
  var this$1 = this;

  var out = "", chunkStart = ++this.pos;
  for (;;) {
    if (this$1.pos >= this$1.input.length) { this$1.raise(this$1.start, "Unterminated string constant"); }
    var ch = this$1.input.charCodeAt(this$1.pos);
    if (ch === quote) { break }
    if (ch === 92) { // '\'
      out += this$1.input.slice(chunkStart, this$1.pos);
      out += this$1.readEscapedChar(false);
      chunkStart = this$1.pos;
    } else {
      if (isNewLine(ch)) { this$1.raise(this$1.start, "Unterminated string constant"); }
      ++this$1.pos;
    }
  }
  out += this.input.slice(chunkStart, this.pos++);
  return this.finishToken(types.string, out)
};

// Reads template string tokens.

var INVALID_TEMPLATE_ESCAPE_ERROR = {};

pp$8.tryReadTemplateToken = function() {
  this.inTemplateElement = true;
  try {
    this.readTmplToken();
  } catch (err) {
    if (err === INVALID_TEMPLATE_ESCAPE_ERROR) {
      this.readInvalidTemplateToken();
    } else {
      throw err
    }
  }

  this.inTemplateElement = false;
};

pp$8.invalidStringToken = function(position, message) {
  if (this.inTemplateElement && this.options.ecmaVersion >= 9) {
    throw INVALID_TEMPLATE_ESCAPE_ERROR
  } else {
    this.raise(position, message);
  }
};

pp$8.readTmplToken = function() {
  var this$1 = this;

  var out = "", chunkStart = this.pos;
  for (;;) {
    if (this$1.pos >= this$1.input.length) { this$1.raise(this$1.start, "Unterminated template"); }
    var ch = this$1.input.charCodeAt(this$1.pos);
    if (ch === 96 || ch === 36 && this$1.input.charCodeAt(this$1.pos + 1) === 123) { // '`', '${'
      if (this$1.pos === this$1.start && (this$1.type === types.template || this$1.type === types.invalidTemplate)) {
        if (ch === 36) {
          this$1.pos += 2;
          return this$1.finishToken(types.dollarBraceL)
        } else {
          ++this$1.pos;
          return this$1.finishToken(types.backQuote)
        }
      }
      out += this$1.input.slice(chunkStart, this$1.pos);
      return this$1.finishToken(types.template, out)
    }
    if (ch === 92) { // '\'
      out += this$1.input.slice(chunkStart, this$1.pos);
      out += this$1.readEscapedChar(true);
      chunkStart = this$1.pos;
    } else if (isNewLine(ch)) {
      out += this$1.input.slice(chunkStart, this$1.pos);
      ++this$1.pos;
      switch (ch) {
      case 13:
        if (this$1.input.charCodeAt(this$1.pos) === 10) { ++this$1.pos; }
      case 10:
        out += "\n";
        break
      default:
        out += String.fromCharCode(ch);
        break
      }
      if (this$1.options.locations) {
        ++this$1.curLine;
        this$1.lineStart = this$1.pos;
      }
      chunkStart = this$1.pos;
    } else {
      ++this$1.pos;
    }
  }
};

// Reads a template token to search for the end, without validating any escape sequences
pp$8.readInvalidTemplateToken = function() {
  var this$1 = this;

  for (; this.pos < this.input.length; this.pos++) {
    switch (this$1.input[this$1.pos]) {
    case "\\":
      ++this$1.pos;
      break

    case "$":
      if (this$1.input[this$1.pos + 1] !== "{") {
        break
      }
    // falls through

    case "`":
      return this$1.finishToken(types.invalidTemplate, this$1.input.slice(this$1.start, this$1.pos))

    // no default
    }
  }
  this.raise(this.start, "Unterminated template");
};

// Used to read escaped characters

pp$8.readEscapedChar = function(inTemplate) {
  var ch = this.input.charCodeAt(++this.pos);
  ++this.pos;
  switch (ch) {
  case 110: return "\n" // 'n' -> '\n'
  case 114: return "\r" // 'r' -> '\r'
  case 120: return String.fromCharCode(this.readHexChar(2)) // 'x'
  case 117: return codePointToString(this.readCodePoint()) // 'u'
  case 116: return "\t" // 't' -> '\t'
  case 98: return "\b" // 'b' -> '\b'
  case 118: return "\u000b" // 'v' -> '\u000b'
  case 102: return "\f" // 'f' -> '\f'
  case 13: if (this.input.charCodeAt(this.pos) === 10) { ++this.pos; } // '\r\n'
  case 10: // ' \n'
    if (this.options.locations) { this.lineStart = this.pos; ++this.curLine; }
    return ""
  default:
    if (ch >= 48 && ch <= 55) {
      var octalStr = this.input.substr(this.pos - 1, 3).match(/^[0-7]+/)[0];
      var octal = parseInt(octalStr, 8);
      if (octal > 255) {
        octalStr = octalStr.slice(0, -1);
        octal = parseInt(octalStr, 8);
      }
      if (octalStr !== "0" && (this.strict || inTemplate)) {
        this.invalidStringToken(this.pos - 2, "Octal literal in strict mode");
      }
      this.pos += octalStr.length - 1;
      return String.fromCharCode(octal)
    }
    return String.fromCharCode(ch)
  }
};

// Used to read character escape sequences ('\x', '\u', '\U').

pp$8.readHexChar = function(len) {
  var codePos = this.pos;
  var n = this.readInt(16, len);
  if (n === null) { this.invalidStringToken(codePos, "Bad character escape sequence"); }
  return n
};

// Read an identifier, and return it as a string. Sets `this.containsEsc`
// to whether the word contained a '\u' escape.
//
// Incrementally adds only escaped chars, adding other chunks as-is
// as a micro-optimization.

pp$8.readWord1 = function() {
  var this$1 = this;

  this.containsEsc = false;
  var word = "", first = true, chunkStart = this.pos;
  var astral = this.options.ecmaVersion >= 6;
  while (this.pos < this.input.length) {
    var ch = this$1.fullCharCodeAtPos();
    if (isIdentifierChar(ch, astral)) {
      this$1.pos += ch <= 0xffff ? 1 : 2;
    } else if (ch === 92) { // "\"
      this$1.containsEsc = true;
      word += this$1.input.slice(chunkStart, this$1.pos);
      var escStart = this$1.pos;
      if (this$1.input.charCodeAt(++this$1.pos) != 117) // "u"
        { this$1.invalidStringToken(this$1.pos, "Expecting Unicode escape sequence \\uXXXX"); }
      ++this$1.pos;
      var esc = this$1.readCodePoint();
      if (!(first ? isIdentifierStart : isIdentifierChar)(esc, astral))
        { this$1.invalidStringToken(escStart, "Invalid Unicode escape"); }
      word += codePointToString(esc);
      chunkStart = this$1.pos;
    } else {
      break
    }
    first = false;
  }
  return word + this.input.slice(chunkStart, this.pos)
};

// Read an identifier or keyword token. Will check for reserved
// words when necessary.

pp$8.readWord = function() {
  var word = this.readWord1();
  var type = types.name;
  if (this.keywords.test(word)) {
    if (this.containsEsc) { this.raiseRecoverable(this.start, "Escape sequence in keyword " + word); }
    type = keywords$1[word];
  }
  return this.finishToken(type, word)
};

// Acorn is a tiny, fast JavaScript parser written in JavaScript.
//
// Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and
// various contributors and released under an MIT license.
//
// Git repositories for Acorn are available at
//
//     http://marijnhaverbeke.nl/git/acorn
//     https://github.com/ternjs/acorn.git
//
// Please use the [github bug tracker][ghbt] to report issues.
//
// [ghbt]: https://github.com/ternjs/acorn/issues
//
// This file defines the main parser interface. The library also comes
// with a [error-tolerant parser][dammit] and an
// [abstract syntax tree walker][walk], defined in other files.
//
// [dammit]: acorn_loose.js
// [walk]: util/walk.js

var version = "5.1.1";

// The main exported interface (under `self.acorn` when in the
// browser) is a `parse` function that takes a code string and
// returns an abstract syntax tree as specified by [Mozilla parser
// API][api].
//
// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API

function parse(input, options) {
  return new Parser(options, input).parse()
}

// This function tries to parse a single expression at a given
// offset in a string. Useful for parsing mixed-language formats
// that embed JavaScript expressions.

function parseExpressionAt(input, pos, options) {
  var p = new Parser(options, input, pos);
  p.nextToken();
  return p.parseExpression()
}

// Acorn is organized as a tokenizer and a recursive-descent parser.
// The `tokenizer` export provides an interface to the tokenizer.

function tokenizer(input, options) {
  return new Parser(options, input)
}

// This is a terrible kludge to support the existing, pre-ES6
// interface where the loose parser module retroactively adds exports
// to this module.
 // eslint-disable-line camelcase
function addLooseExports(parse, Parser$$1, plugins$$1) {
  exports.parse_dammit = parse; // eslint-disable-line camelcase
  exports.LooseParser = Parser$$1;
  exports.pluginsLoose = plugins$$1;
}

exports.version = version;
exports.parse = parse;
exports.parseExpressionAt = parseExpressionAt;
exports.tokenizer = tokenizer;
exports.addLooseExports = addLooseExports;
exports.Parser = Parser;
exports.plugins = plugins;
exports.defaultOptions = defaultOptions;
exports.Position = Position;
exports.SourceLocation = SourceLocation;
exports.getLineInfo = getLineInfo;
exports.Node = Node;
exports.TokenType = TokenType;
exports.tokTypes = types;
exports.keywordTypes = keywords$1;
exports.TokContext = TokContext;
exports.tokContexts = types$1;
exports.isIdentifierChar = isIdentifierChar;
exports.isIdentifierStart = isIdentifierStart;
exports.Token = Token;
exports.isNewLine = isNewLine;
exports.lineBreak = lineBreak;
exports.lineBreakG = lineBreakG;
exports.nonASCIIwhitespace = nonASCIIwhitespace;

Object.defineProperty(exports, '__esModule', { value: true });

})));

(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./acorn')) :
	typeof define === 'function' && define.amd ? define('acorn/dist/acorn_loose',['exports', './acorn'], factory) :
	(factory((global.acorn = global.acorn || {}, global.acorn.loose = global.acorn.loose || {}),global.acorn));
}(this, (function (exports,__acorn) { 'use strict';

// Registered plugins
var pluginsLoose = {};

var LooseParser = function LooseParser(input, options) {
  if ( options === void 0 ) options = {};

  this.toks = __acorn.tokenizer(input, options);
  this.options = this.toks.options;
  this.input = this.toks.input;
  this.tok = this.last = {type: __acorn.tokTypes.eof, start: 0, end: 0};
  if (this.options.locations) {
    var here = this.toks.curPosition();
    this.tok.loc = new __acorn.SourceLocation(this.toks, here, here);
  }
  this.ahead = []; // Tokens ahead
  this.context = []; // Indentation contexted
  this.curIndent = 0;
  this.curLineStart = 0;
  this.nextLineStart = this.lineEnd(this.curLineStart) + 1;
  this.inAsync = false;
  // Load plugins
  this.options.pluginsLoose = options.pluginsLoose || {};
  this.loadPlugins(this.options.pluginsLoose);
};

LooseParser.prototype.startNode = function startNode () {
  return new __acorn.Node(this.toks, this.tok.start, this.options.locations ? this.tok.loc.start : null)
};

LooseParser.prototype.storeCurrentPos = function storeCurrentPos () {
  return this.options.locations ? [this.tok.start, this.tok.loc.start] : this.tok.start
};

LooseParser.prototype.startNodeAt = function startNodeAt (pos) {
  if (this.options.locations) {
    return new __acorn.Node(this.toks, pos[0], pos[1])
  } else {
    return new __acorn.Node(this.toks, pos)
  }
};

LooseParser.prototype.finishNode = function finishNode (node, type) {
  node.type = type;
  node.end = this.last.end;
  if (this.options.locations)
    { node.loc.end = this.last.loc.end; }
  if (this.options.ranges)
    { node.range[1] = this.last.end; }
  return node
};

LooseParser.prototype.dummyNode = function dummyNode (type) {
  var dummy = this.startNode();
  dummy.type = type;
  dummy.end = dummy.start;
  if (this.options.locations)
    { dummy.loc.end = dummy.loc.start; }
  if (this.options.ranges)
    { dummy.range[1] = dummy.start; }
  this.last = {type: __acorn.tokTypes.name, start: dummy.start, end: dummy.start, loc: dummy.loc};
  return dummy
};

LooseParser.prototype.dummyIdent = function dummyIdent () {
  var dummy = this.dummyNode("Identifier");
  dummy.name = "✖";
  return dummy
};

LooseParser.prototype.dummyString = function dummyString () {
  var dummy = this.dummyNode("Literal");
  dummy.value = dummy.raw = "✖";
  return dummy
};

LooseParser.prototype.eat = function eat (type) {
  if (this.tok.type === type) {
    this.next();
    return true
  } else {
    return false
  }
};

LooseParser.prototype.isContextual = function isContextual (name) {
  return this.tok.type === __acorn.tokTypes.name && this.tok.value === name
};

LooseParser.prototype.eatContextual = function eatContextual (name) {
  return this.tok.value === name && this.eat(__acorn.tokTypes.name)
};

LooseParser.prototype.canInsertSemicolon = function canInsertSemicolon () {
  return this.tok.type === __acorn.tokTypes.eof || this.tok.type === __acorn.tokTypes.braceR ||
    __acorn.lineBreak.test(this.input.slice(this.last.end, this.tok.start))
};

LooseParser.prototype.semicolon = function semicolon () {
  return this.eat(__acorn.tokTypes.semi)
};

LooseParser.prototype.expect = function expect (type) {
    var this$1 = this;

  if (this.eat(type)) { return true }
  for (var i = 1; i <= 2; i++) {
    if (this$1.lookAhead(i).type == type) {
      for (var j = 0; j < i; j++) { this$1.next(); }
      return true
    }
  }
};

LooseParser.prototype.pushCx = function pushCx () {
  this.context.push(this.curIndent);
};

LooseParser.prototype.popCx = function popCx () {
  this.curIndent = this.context.pop();
};

LooseParser.prototype.lineEnd = function lineEnd (pos) {
  while (pos < this.input.length && !__acorn.isNewLine(this.input.charCodeAt(pos))) { ++pos; }
  return pos
};

LooseParser.prototype.indentationAfter = function indentationAfter (pos) {
    var this$1 = this;

  for (var count = 0;; ++pos) {
    var ch = this$1.input.charCodeAt(pos);
    if (ch === 32) { ++count; }
    else if (ch === 9) { count += this$1.options.tabSize; }
    else { return count }
  }
};

LooseParser.prototype.closes = function closes (closeTok, indent, line, blockHeuristic) {
  if (this.tok.type === closeTok || this.tok.type === __acorn.tokTypes.eof) { return true }
  return line != this.curLineStart && this.curIndent < indent && this.tokenStartsLine() &&
    (!blockHeuristic || this.nextLineStart >= this.input.length ||
     this.indentationAfter(this.nextLineStart) < indent)
};

LooseParser.prototype.tokenStartsLine = function tokenStartsLine () {
    var this$1 = this;

  for (var p = this.tok.start - 1; p >= this.curLineStart; --p) {
    var ch = this$1.input.charCodeAt(p);
    if (ch !== 9 && ch !== 32) { return false }
  }
  return true
};

LooseParser.prototype.extend = function extend (name, f) {
  this[name] = f(this[name]);
};

LooseParser.prototype.loadPlugins = function loadPlugins (pluginConfigs) {
    var this$1 = this;

  for (var name in pluginConfigs) {
    var plugin = pluginsLoose[name];
    if (!plugin) { throw new Error("Plugin '" + name + "' not found") }
    plugin(this$1, pluginConfigs[name]);
  }
};

LooseParser.prototype.parse = function parse () {
  this.next();
  return this.parseTopLevel()
};

var lp = LooseParser.prototype;

function isSpace(ch) {
  return (ch < 14 && ch > 8) || ch === 32 || ch === 160 || __acorn.isNewLine(ch)
}

lp.next = function() {
  var this$1 = this;

  this.last = this.tok;
  if (this.ahead.length)
    { this.tok = this.ahead.shift(); }
  else
    { this.tok = this.readToken(); }

  if (this.tok.start >= this.nextLineStart) {
    while (this.tok.start >= this.nextLineStart) {
      this$1.curLineStart = this$1.nextLineStart;
      this$1.nextLineStart = this$1.lineEnd(this$1.curLineStart) + 1;
    }
    this.curIndent = this.indentationAfter(this.curLineStart);
  }
};

lp.readToken = function() {
  var this$1 = this;

  for (;;) {
    try {
      this$1.toks.next();
      if (this$1.toks.type === __acorn.tokTypes.dot &&
          this$1.input.substr(this$1.toks.end, 1) === "." &&
          this$1.options.ecmaVersion >= 6) {
        this$1.toks.end++;
        this$1.toks.type = __acorn.tokTypes.ellipsis;
      }
      return new __acorn.Token(this$1.toks)
    } catch (e) {
      if (!(e instanceof SyntaxError)) { throw e }

      // Try to skip some text, based on the error message, and then continue
      var msg = e.message, pos = e.raisedAt, replace = true;
      if (/unterminated/i.test(msg)) {
        pos = this$1.lineEnd(e.pos + 1);
        if (/string/.test(msg)) {
          replace = {start: e.pos, end: pos, type: __acorn.tokTypes.string, value: this$1.input.slice(e.pos + 1, pos)};
        } else if (/regular expr/i.test(msg)) {
          var re = this$1.input.slice(e.pos, pos);
          try { re = new RegExp(re); } catch (e) { /* ignore compilation error due to new syntax */ }
          replace = {start: e.pos, end: pos, type: __acorn.tokTypes.regexp, value: re};
        } else if (/template/.test(msg)) {
          replace = {
            start: e.pos,
            end: pos,
            type: __acorn.tokTypes.template,
            value: this$1.input.slice(e.pos, pos)
          };
        } else {
          replace = false;
        }
      } else if (/invalid (unicode|regexp|number)|expecting unicode|octal literal|is reserved|directly after number|expected number in radix/i.test(msg)) {
        while (pos < this.input.length && !isSpace(this.input.charCodeAt(pos))) { ++pos; }
      } else if (/character escape|expected hexadecimal/i.test(msg)) {
        while (pos < this.input.length) {
          var ch = this$1.input.charCodeAt(pos++);
          if (ch === 34 || ch === 39 || __acorn.isNewLine(ch)) { break }
        }
      } else if (/unexpected character/i.test(msg)) {
        pos++;
        replace = false;
      } else if (/regular expression/i.test(msg)) {
        replace = true;
      } else {
        throw e
      }
      this$1.resetTo(pos);
      if (replace === true) { replace = {start: pos, end: pos, type: __acorn.tokTypes.name, value: "✖"}; }
      if (replace) {
        if (this$1.options.locations)
          { replace.loc = new __acorn.SourceLocation(
            this$1.toks,
            __acorn.getLineInfo(this$1.input, replace.start),
            __acorn.getLineInfo(this$1.input, replace.end)); }
        return replace
      }
    }
  }
};

lp.resetTo = function(pos) {
  var this$1 = this;

  this.toks.pos = pos;
  var ch = this.input.charAt(pos - 1);
  this.toks.exprAllowed = !ch || /[[{(,;:?/*=+\-~!|&%^<>]/.test(ch) ||
    /[enwfd]/.test(ch) &&
    /\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(this.input.slice(pos - 10, pos));

  if (this.options.locations) {
    this.toks.curLine = 1;
    this.toks.lineStart = __acorn.lineBreakG.lastIndex = 0;
    var match;
    while ((match = __acorn.lineBreakG.exec(this.input)) && match.index < pos) {
      ++this$1.toks.curLine;
      this$1.toks.lineStart = match.index + match[0].length;
    }
  }
};

lp.lookAhead = function(n) {
  var this$1 = this;

  while (n > this.ahead.length)
    { this$1.ahead.push(this$1.readToken()); }
  return this.ahead[n - 1]
};

function isDummy(node) { return node.name == "✖" }

var lp$1 = LooseParser.prototype;

lp$1.parseTopLevel = function() {
  var this$1 = this;

  var node = this.startNodeAt(this.options.locations ? [0, __acorn.getLineInfo(this.input, 0)] : 0);
  node.body = [];
  while (this.tok.type !== __acorn.tokTypes.eof) { node.body.push(this$1.parseStatement()); }
  this.last = this.tok;
  if (this.options.ecmaVersion >= 6) {
    node.sourceType = this.options.sourceType;
  }
  return this.finishNode(node, "Program")
};

lp$1.parseStatement = function() {
  var this$1 = this;

  var starttype = this.tok.type, node = this.startNode(), kind;

  if (this.toks.isLet()) {
    starttype = __acorn.tokTypes._var;
    kind = "let";
  }

  switch (starttype) {
  case __acorn.tokTypes._break: case __acorn.tokTypes._continue:
    this.next();
    var isBreak = starttype === __acorn.tokTypes._break;
    if (this.semicolon() || this.canInsertSemicolon()) {
      node.label = null;
    } else {
      node.label = this.tok.type === __acorn.tokTypes.name ? this.parseIdent() : null;
      this.semicolon();
    }
    return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")

  case __acorn.tokTypes._debugger:
    this.next();
    this.semicolon();
    return this.finishNode(node, "DebuggerStatement")

  case __acorn.tokTypes._do:
    this.next();
    node.body = this.parseStatement();
    node.test = this.eat(__acorn.tokTypes._while) ? this.parseParenExpression() : this.dummyIdent();
    this.semicolon();
    return this.finishNode(node, "DoWhileStatement")

  case __acorn.tokTypes._for:
    this.next();
    this.pushCx();
    this.expect(__acorn.tokTypes.parenL);
    if (this.tok.type === __acorn.tokTypes.semi) { return this.parseFor(node, null) }
    var isLet = this.toks.isLet();
    if (isLet || this.tok.type === __acorn.tokTypes._var || this.tok.type === __acorn.tokTypes._const) {
      var init$1 = this.parseVar(true, isLet ? "let" : this.tok.value);
      if (init$1.declarations.length === 1 && (this.tok.type === __acorn.tokTypes._in || this.isContextual("of"))) {
        return this.parseForIn(node, init$1)
      }
      return this.parseFor(node, init$1)
    }
    var init = this.parseExpression(true);
    if (this.tok.type === __acorn.tokTypes._in || this.isContextual("of"))
      { return this.parseForIn(node, this.toAssignable(init)) }
    return this.parseFor(node, init)

  case __acorn.tokTypes._function:
    this.next();
    return this.parseFunction(node, true)

  case __acorn.tokTypes._if:
    this.next();
    node.test = this.parseParenExpression();
    node.consequent = this.parseStatement();
    node.alternate = this.eat(__acorn.tokTypes._else) ? this.parseStatement() : null;
    return this.finishNode(node, "IfStatement")

  case __acorn.tokTypes._return:
    this.next();
    if (this.eat(__acorn.tokTypes.semi) || this.canInsertSemicolon()) { node.argument = null; }
    else { node.argument = this.parseExpression(); this.semicolon(); }
    return this.finishNode(node, "ReturnStatement")

  case __acorn.tokTypes._switch:
    var blockIndent = this.curIndent, line = this.curLineStart;
    this.next();
    node.discriminant = this.parseParenExpression();
    node.cases = [];
    this.pushCx();
    this.expect(__acorn.tokTypes.braceL);

    var cur;
    while (!this.closes(__acorn.tokTypes.braceR, blockIndent, line, true)) {
      if (this$1.tok.type === __acorn.tokTypes._case || this$1.tok.type === __acorn.tokTypes._default) {
        var isCase = this$1.tok.type === __acorn.tokTypes._case;
        if (cur) { this$1.finishNode(cur, "SwitchCase"); }
        node.cases.push(cur = this$1.startNode());
        cur.consequent = [];
        this$1.next();
        if (isCase) { cur.test = this$1.parseExpression(); }
        else { cur.test = null; }
        this$1.expect(__acorn.tokTypes.colon);
      } else {
        if (!cur) {
          node.cases.push(cur = this$1.startNode());
          cur.consequent = [];
          cur.test = null;
        }
        cur.consequent.push(this$1.parseStatement());
      }
    }
    if (cur) { this.finishNode(cur, "SwitchCase"); }
    this.popCx();
    this.eat(__acorn.tokTypes.braceR);
    return this.finishNode(node, "SwitchStatement")

  case __acorn.tokTypes._throw:
    this.next();
    node.argument = this.parseExpression();
    this.semicolon();
    return this.finishNode(node, "ThrowStatement")

  case __acorn.tokTypes._try:
    this.next();
    node.block = this.parseBlock();
    node.handler = null;
    if (this.tok.type === __acorn.tokTypes._catch) {
      var clause = this.startNode();
      this.next();
      this.expect(__acorn.tokTypes.parenL);
      clause.param = this.toAssignable(this.parseExprAtom(), true);
      this.expect(__acorn.tokTypes.parenR);
      clause.body = this.parseBlock();
      node.handler = this.finishNode(clause, "CatchClause");
    }
    node.finalizer = this.eat(__acorn.tokTypes._finally) ? this.parseBlock() : null;
    if (!node.handler && !node.finalizer) { return node.block }
    return this.finishNode(node, "TryStatement")

  case __acorn.tokTypes._var:
  case __acorn.tokTypes._const:
    return this.parseVar(false, kind || this.tok.value)

  case __acorn.tokTypes._while:
    this.next();
    node.test = this.parseParenExpression();
    node.body = this.parseStatement();
    return this.finishNode(node, "WhileStatement")

  case __acorn.tokTypes._with:
    this.next();
    node.object = this.parseParenExpression();
    node.body = this.parseStatement();
    return this.finishNode(node, "WithStatement")

  case __acorn.tokTypes.braceL:
    return this.parseBlock()

  case __acorn.tokTypes.semi:
    this.next();
    return this.finishNode(node, "EmptyStatement")

  case __acorn.tokTypes._class:
    return this.parseClass(true)

  case __acorn.tokTypes._import:
    return this.parseImport()

  case __acorn.tokTypes._export:
    return this.parseExport()

  default:
    if (this.toks.isAsyncFunction()) {
      this.next();
      this.next();
      return this.parseFunction(node, true, true)
    }
    var expr = this.parseExpression();
    if (isDummy(expr)) {
      this.next();
      if (this.tok.type === __acorn.tokTypes.eof) { return this.finishNode(node, "EmptyStatement") }
      return this.parseStatement()
    } else if (starttype === __acorn.tokTypes.name && expr.type === "Identifier" && this.eat(__acorn.tokTypes.colon)) {
      node.body = this.parseStatement();
      node.label = expr;
      return this.finishNode(node, "LabeledStatement")
    } else {
      node.expression = expr;
      this.semicolon();
      return this.finishNode(node, "ExpressionStatement")
    }
  }
};

lp$1.parseBlock = function() {
  var this$1 = this;

  var node = this.startNode();
  this.pushCx();
  this.expect(__acorn.tokTypes.braceL);
  var blockIndent = this.curIndent, line = this.curLineStart;
  node.body = [];
  while (!this.closes(__acorn.tokTypes.braceR, blockIndent, line, true))
    { node.body.push(this$1.parseStatement()); }
  this.popCx();
  this.eat(__acorn.tokTypes.braceR);
  return this.finishNode(node, "BlockStatement")
};

lp$1.parseFor = function(node, init) {
  node.init = init;
  node.test = node.update = null;
  if (this.eat(__acorn.tokTypes.semi) && this.tok.type !== __acorn.tokTypes.semi) { node.test = this.parseExpression(); }
  if (this.eat(__acorn.tokTypes.semi) && this.tok.type !== __acorn.tokTypes.parenR) { node.update = this.parseExpression(); }
  this.popCx();
  this.expect(__acorn.tokTypes.parenR);
  node.body = this.parseStatement();
  return this.finishNode(node, "ForStatement")
};

lp$1.parseForIn = function(node, init) {
  var type = this.tok.type === __acorn.tokTypes._in ? "ForInStatement" : "ForOfStatement";
  this.next();
  node.left = init;
  node.right = this.parseExpression();
  this.popCx();
  this.expect(__acorn.tokTypes.parenR);
  node.body = this.parseStatement();
  return this.finishNode(node, type)
};

lp$1.parseVar = function(noIn, kind) {
  var this$1 = this;

  var node = this.startNode();
  node.kind = kind;
  this.next();
  node.declarations = [];
  do {
    var decl = this$1.startNode();
    decl.id = this$1.options.ecmaVersion >= 6 ? this$1.toAssignable(this$1.parseExprAtom(), true) : this$1.parseIdent();
    decl.init = this$1.eat(__acorn.tokTypes.eq) ? this$1.parseMaybeAssign(noIn) : null;
    node.declarations.push(this$1.finishNode(decl, "VariableDeclarator"));
  } while (this.eat(__acorn.tokTypes.comma))
  if (!node.declarations.length) {
    var decl$1 = this.startNode();
    decl$1.id = this.dummyIdent();
    node.declarations.push(this.finishNode(decl$1, "VariableDeclarator"));
  }
  if (!noIn) { this.semicolon(); }
  return this.finishNode(node, "VariableDeclaration")
};

lp$1.parseClass = function(isStatement) {
  var this$1 = this;

  var node = this.startNode();
  this.next();
  if (this.tok.type === __acorn.tokTypes.name) { node.id = this.parseIdent(); }
  else if (isStatement === true) { node.id = this.dummyIdent(); }
  else { node.id = null; }
  node.superClass = this.eat(__acorn.tokTypes._extends) ? this.parseExpression() : null;
  node.body = this.startNode();
  node.body.body = [];
  this.pushCx();
  var indent = this.curIndent + 1, line = this.curLineStart;
  this.eat(__acorn.tokTypes.braceL);
  if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart; }
  while (!this.closes(__acorn.tokTypes.braceR, indent, line)) {
    if (this$1.semicolon()) { continue }
    var method = this$1.startNode(), isGenerator = (void 0), isAsync = (void 0);
    if (this$1.options.ecmaVersion >= 6) {
      method.static = false;
      isGenerator = this$1.eat(__acorn.tokTypes.star);
    }
    this$1.parsePropertyName(method);
    if (isDummy(method.key)) { if (isDummy(this$1.parseMaybeAssign())) { this$1.next(); } this$1.eat(__acorn.tokTypes.comma); continue }
    if (method.key.type === "Identifier" && !method.computed && method.key.name === "static" &&
        (this$1.tok.type != __acorn.tokTypes.parenL && this$1.tok.type != __acorn.tokTypes.braceL)) {
      method.static = true;
      isGenerator = this$1.eat(__acorn.tokTypes.star);
      this$1.parsePropertyName(method);
    } else {
      method.static = false;
    }
    if (!method.computed &&
        method.key.type === "Identifier" && method.key.name === "async" && this$1.tok.type !== __acorn.tokTypes.parenL &&
        !this$1.canInsertSemicolon()) {
      this$1.parsePropertyName(method);
      isAsync = true;
    } else {
      isAsync = false;
    }
    if (this$1.options.ecmaVersion >= 5 && method.key.type === "Identifier" &&
        !method.computed && (method.key.name === "get" || method.key.name === "set") &&
        this$1.tok.type !== __acorn.tokTypes.parenL && this$1.tok.type !== __acorn.tokTypes.braceL) {
      method.kind = method.key.name;
      this$1.parsePropertyName(method);
      method.value = this$1.parseMethod(false);
    } else {
      if (!method.computed && !method.static && !isGenerator && !isAsync && (
        method.key.type === "Identifier" && method.key.name === "constructor" ||
          method.key.type === "Literal" && method.key.value === "constructor")) {
        method.kind = "constructor";
      } else {
        method.kind = "method";
      }
      method.value = this$1.parseMethod(isGenerator, isAsync);
    }
    node.body.body.push(this$1.finishNode(method, "MethodDefinition"));
  }
  this.popCx();
  if (!this.eat(__acorn.tokTypes.braceR)) {
    // If there is no closing brace, make the node span to the start
    // of the next token (this is useful for Tern)
    this.last.end = this.tok.start;
    if (this.options.locations) { this.last.loc.end = this.tok.loc.start; }
  }
  this.semicolon();
  this.finishNode(node.body, "ClassBody");
  return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
};

lp$1.parseFunction = function(node, isStatement, isAsync) {
  var oldInAsync = this.inAsync;
  this.initFunction(node);
  if (this.options.ecmaVersion >= 6) {
    node.generator = this.eat(__acorn.tokTypes.star);
  }
  if (this.options.ecmaVersion >= 8) {
    node.async = !!isAsync;
  }
  if (this.tok.type === __acorn.tokTypes.name) { node.id = this.parseIdent(); }
  else if (isStatement === true) { node.id = this.dummyIdent(); }
  this.inAsync = node.async;
  node.params = this.parseFunctionParams();
  node.body = this.parseBlock();
  this.inAsync = oldInAsync;
  return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
};

lp$1.parseExport = function() {
  var node = this.startNode();
  this.next();
  if (this.eat(__acorn.tokTypes.star)) {
    node.source = this.eatContextual("from") ? this.parseExprAtom() : this.dummyString();
    return this.finishNode(node, "ExportAllDeclaration")
  }
  if (this.eat(__acorn.tokTypes._default)) {
    // export default (function foo() {}) // This is FunctionExpression.
    var isAsync;
    if (this.tok.type === __acorn.tokTypes._function || (isAsync = this.toks.isAsyncFunction())) {
      var fNode = this.startNode();
      this.next();
      if (isAsync) { this.next(); }
      node.declaration = this.parseFunction(fNode, "nullableID", isAsync);
    } else if (this.tok.type === __acorn.tokTypes._class) {
      node.declaration = this.parseClass("nullableID");
    } else {
      node.declaration = this.parseMaybeAssign();
      this.semicolon();
    }
    return this.finishNode(node, "ExportDefaultDeclaration")
  }
  if (this.tok.type.keyword || this.toks.isLet() || this.toks.isAsyncFunction()) {
    node.declaration = this.parseStatement();
    node.specifiers = [];
    node.source = null;
  } else {
    node.declaration = null;
    node.specifiers = this.parseExportSpecifierList();
    node.source = this.eatContextual("from") ? this.parseExprAtom() : null;
    this.semicolon();
  }
  return this.finishNode(node, "ExportNamedDeclaration")
};

lp$1.parseImport = function() {
  var node = this.startNode();
  this.next();
  if (this.tok.type === __acorn.tokTypes.string) {
    node.specifiers = [];
    node.source = this.parseExprAtom();
    node.kind = "";
  } else {
    var elt;
    if (this.tok.type === __acorn.tokTypes.name && this.tok.value !== "from") {
      elt = this.startNode();
      elt.local = this.parseIdent();
      this.finishNode(elt, "ImportDefaultSpecifier");
      this.eat(__acorn.tokTypes.comma);
    }
    node.specifiers = this.parseImportSpecifierList();
    node.source = this.eatContextual("from") && this.tok.type == __acorn.tokTypes.string ? this.parseExprAtom() : this.dummyString();
    if (elt) { node.specifiers.unshift(elt); }
  }
  this.semicolon();
  return this.finishNode(node, "ImportDeclaration")
};

lp$1.parseImportSpecifierList = function() {
  var this$1 = this;

  var elts = [];
  if (this.tok.type === __acorn.tokTypes.star) {
    var elt = this.startNode();
    this.next();
    elt.local = this.eatContextual("as") ? this.parseIdent() : this.dummyIdent();
    elts.push(this.finishNode(elt, "ImportNamespaceSpecifier"));
  } else {
    var indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart;
    this.pushCx();
    this.eat(__acorn.tokTypes.braceL);
    if (this.curLineStart > continuedLine) { continuedLine = this.curLineStart; }
    while (!this.closes(__acorn.tokTypes.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
      var elt$1 = this$1.startNode();
      if (this$1.eat(__acorn.tokTypes.star)) {
        elt$1.local = this$1.eatContextual("as") ? this$1.parseIdent() : this$1.dummyIdent();
        this$1.finishNode(elt$1, "ImportNamespaceSpecifier");
      } else {
        if (this$1.isContextual("from")) { break }
        elt$1.imported = this$1.parseIdent();
        if (isDummy(elt$1.imported)) { break }
        elt$1.local = this$1.eatContextual("as") ? this$1.parseIdent() : elt$1.imported;
        this$1.finishNode(elt$1, "ImportSpecifier");
      }
      elts.push(elt$1);
      this$1.eat(__acorn.tokTypes.comma);
    }
    this.eat(__acorn.tokTypes.braceR);
    this.popCx();
  }
  return elts
};

lp$1.parseExportSpecifierList = function() {
  var this$1 = this;

  var elts = [];
  var indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart;
  this.pushCx();
  this.eat(__acorn.tokTypes.braceL);
  if (this.curLineStart > continuedLine) { continuedLine = this.curLineStart; }
  while (!this.closes(__acorn.tokTypes.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
    if (this$1.isContextual("from")) { break }
    var elt = this$1.startNode();
    elt.local = this$1.parseIdent();
    if (isDummy(elt.local)) { break }
    elt.exported = this$1.eatContextual("as") ? this$1.parseIdent() : elt.local;
    this$1.finishNode(elt, "ExportSpecifier");
    elts.push(elt);
    this$1.eat(__acorn.tokTypes.comma);
  }
  this.eat(__acorn.tokTypes.braceR);
  this.popCx();
  return elts
};

var lp$2 = LooseParser.prototype;

lp$2.checkLVal = function(expr) {
  if (!expr) { return expr }
  switch (expr.type) {
  case "Identifier":
  case "MemberExpression":
    return expr

  case "ParenthesizedExpression":
    expr.expression = this.checkLVal(expr.expression);
    return expr

  default:
    return this.dummyIdent()
  }
};

lp$2.parseExpression = function(noIn) {
  var this$1 = this;

  var start = this.storeCurrentPos();
  var expr = this.parseMaybeAssign(noIn);
  if (this.tok.type === __acorn.tokTypes.comma) {
    var node = this.startNodeAt(start);
    node.expressions = [expr];
    while (this.eat(__acorn.tokTypes.comma)) { node.expressions.push(this$1.parseMaybeAssign(noIn)); }
    return this.finishNode(node, "SequenceExpression")
  }
  return expr
};

lp$2.parseParenExpression = function() {
  this.pushCx();
  this.expect(__acorn.tokTypes.parenL);
  var val = this.parseExpression();
  this.popCx();
  this.expect(__acorn.tokTypes.parenR);
  return val
};

lp$2.parseMaybeAssign = function(noIn) {
  if (this.toks.isContextual("yield")) {
    var node = this.startNode();
    this.next();
    if (this.semicolon() || this.canInsertSemicolon() || (this.tok.type != __acorn.tokTypes.star && !this.tok.type.startsExpr)) {
      node.delegate = false;
      node.argument = null;
    } else {
      node.delegate = this.eat(__acorn.tokTypes.star);
      node.argument = this.parseMaybeAssign();
    }
    return this.finishNode(node, "YieldExpression")
  }

  var start = this.storeCurrentPos();
  var left = this.parseMaybeConditional(noIn);
  if (this.tok.type.isAssign) {
    var node$1 = this.startNodeAt(start);
    node$1.operator = this.tok.value;
    node$1.left = this.tok.type === __acorn.tokTypes.eq ? this.toAssignable(left) : this.checkLVal(left);
    this.next();
    node$1.right = this.parseMaybeAssign(noIn);
    return this.finishNode(node$1, "AssignmentExpression")
  }
  return left
};

lp$2.parseMaybeConditional = function(noIn) {
  var start = this.storeCurrentPos();
  var expr = this.parseExprOps(noIn);
  if (this.eat(__acorn.tokTypes.question)) {
    var node = this.startNodeAt(start);
    node.test = expr;
    node.consequent = this.parseMaybeAssign();
    node.alternate = this.expect(__acorn.tokTypes.colon) ? this.parseMaybeAssign(noIn) : this.dummyIdent();
    return this.finishNode(node, "ConditionalExpression")
  }
  return expr
};

lp$2.parseExprOps = function(noIn) {
  var start = this.storeCurrentPos();
  var indent = this.curIndent, line = this.curLineStart;
  return this.parseExprOp(this.parseMaybeUnary(false), start, -1, noIn, indent, line)
};

lp$2.parseExprOp = function(left, start, minPrec, noIn, indent, line) {
  if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) { return left }
  var prec = this.tok.type.binop;
  if (prec != null && (!noIn || this.tok.type !== __acorn.tokTypes._in)) {
    if (prec > minPrec) {
      var node = this.startNodeAt(start);
      node.left = left;
      node.operator = this.tok.value;
      this.next();
      if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) {
        node.right = this.dummyIdent();
      } else {
        var rightStart = this.storeCurrentPos();
        node.right = this.parseExprOp(this.parseMaybeUnary(false), rightStart, prec, noIn, indent, line);
      }
      this.finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression");
      return this.parseExprOp(node, start, minPrec, noIn, indent, line)
    }
  }
  return left
};

lp$2.parseMaybeUnary = function(sawUnary) {
  var this$1 = this;

  var start = this.storeCurrentPos(), expr;
  if (this.options.ecmaVersion >= 8 && this.inAsync && this.toks.isContextual("await")) {
    expr = this.parseAwait();
    sawUnary = true;
  } else if (this.tok.type.prefix) {
    var node = this.startNode(), update = this.tok.type === __acorn.tokTypes.incDec;
    if (!update) { sawUnary = true; }
    node.operator = this.tok.value;
    node.prefix = true;
    this.next();
    node.argument = this.parseMaybeUnary(true);
    if (update) { node.argument = this.checkLVal(node.argument); }
    expr = this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
  } else if (this.tok.type === __acorn.tokTypes.ellipsis) {
    var node$1 = this.startNode();
    this.next();
    node$1.argument = this.parseMaybeUnary(sawUnary);
    expr = this.finishNode(node$1, "SpreadElement");
  } else {
    expr = this.parseExprSubscripts();
    while (this.tok.type.postfix && !this.canInsertSemicolon()) {
      var node$2 = this$1.startNodeAt(start);
      node$2.operator = this$1.tok.value;
      node$2.prefix = false;
      node$2.argument = this$1.checkLVal(expr);
      this$1.next();
      expr = this$1.finishNode(node$2, "UpdateExpression");
    }
  }

  if (!sawUnary && this.eat(__acorn.tokTypes.starstar)) {
    var node$3 = this.startNodeAt(start);
    node$3.operator = "**";
    node$3.left = expr;
    node$3.right = this.parseMaybeUnary(false);
    return this.finishNode(node$3, "BinaryExpression")
  }

  return expr
};

lp$2.parseExprSubscripts = function() {
  var start = this.storeCurrentPos();
  return this.parseSubscripts(this.parseExprAtom(), start, false, this.curIndent, this.curLineStart)
};

lp$2.parseSubscripts = function(base, start, noCalls, startIndent, line) {
  var this$1 = this;

  for (;;) {
    if (this$1.curLineStart != line && this$1.curIndent <= startIndent && this$1.tokenStartsLine()) {
      if (this$1.tok.type == __acorn.tokTypes.dot && this$1.curIndent == startIndent)
        { --startIndent; }
      else
        { return base }
    }

    var maybeAsyncArrow = base.type === "Identifier" && base.name === "async" && !this$1.canInsertSemicolon();

    if (this$1.eat(__acorn.tokTypes.dot)) {
      var node = this$1.startNodeAt(start);
      node.object = base;
      if (this$1.curLineStart != line && this$1.curIndent <= startIndent && this$1.tokenStartsLine())
        { node.property = this$1.dummyIdent(); }
      else
        { node.property = this$1.parsePropertyAccessor() || this$1.dummyIdent(); }
      node.computed = false;
      base = this$1.finishNode(node, "MemberExpression");
    } else if (this$1.tok.type == __acorn.tokTypes.bracketL) {
      this$1.pushCx();
      this$1.next();
      var node$1 = this$1.startNodeAt(start);
      node$1.object = base;
      node$1.property = this$1.parseExpression();
      node$1.computed = true;
      this$1.popCx();
      this$1.expect(__acorn.tokTypes.bracketR);
      base = this$1.finishNode(node$1, "MemberExpression");
    } else if (!noCalls && this$1.tok.type == __acorn.tokTypes.parenL) {
      var exprList = this$1.parseExprList(__acorn.tokTypes.parenR);
      if (maybeAsyncArrow && this$1.eat(__acorn.tokTypes.arrow))
        { return this$1.parseArrowExpression(this$1.startNodeAt(start), exprList, true) }
      var node$2 = this$1.startNodeAt(start);
      node$2.callee = base;
      node$2.arguments = exprList;
      base = this$1.finishNode(node$2, "CallExpression");
    } else if (this$1.tok.type == __acorn.tokTypes.backQuote) {
      var node$3 = this$1.startNodeAt(start);
      node$3.tag = base;
      node$3.quasi = this$1.parseTemplate();
      base = this$1.finishNode(node$3, "TaggedTemplateExpression");
    } else {
      return base
    }
  }
};

lp$2.parseExprAtom = function() {
  var node;
  switch (this.tok.type) {
  case __acorn.tokTypes._this:
  case __acorn.tokTypes._super:
    var type = this.tok.type === __acorn.tokTypes._this ? "ThisExpression" : "Super";
    node = this.startNode();
    this.next();
    return this.finishNode(node, type)

  case __acorn.tokTypes.name:
    var start = this.storeCurrentPos();
    var id = this.parseIdent();
    var isAsync = false;
    if (id.name === "async" && !this.canInsertSemicolon()) {
      if (this.eat(__acorn.tokTypes._function))
        { return this.parseFunction(this.startNodeAt(start), false, true) }
      if (this.tok.type === __acorn.tokTypes.name) {
        id = this.parseIdent();
        isAsync = true;
      }
    }
    return this.eat(__acorn.tokTypes.arrow) ? this.parseArrowExpression(this.startNodeAt(start), [id], isAsync) : id

  case __acorn.tokTypes.regexp:
    node = this.startNode();
    var val = this.tok.value;
    node.regex = {pattern: val.pattern, flags: val.flags};
    node.value = val.value;
    node.raw = this.input.slice(this.tok.start, this.tok.end);
    this.next();
    return this.finishNode(node, "Literal")

  case __acorn.tokTypes.num: case __acorn.tokTypes.string:
    node = this.startNode();
    node.value = this.tok.value;
    node.raw = this.input.slice(this.tok.start, this.tok.end);
    this.next();
    return this.finishNode(node, "Literal")

  case __acorn.tokTypes._null: case __acorn.tokTypes._true: case __acorn.tokTypes._false:
    node = this.startNode();
    node.value = this.tok.type === __acorn.tokTypes._null ? null : this.tok.type === __acorn.tokTypes._true;
    node.raw = this.tok.type.keyword;
    this.next();
    return this.finishNode(node, "Literal")

  case __acorn.tokTypes.parenL:
    var parenStart = this.storeCurrentPos();
    this.next();
    var inner = this.parseExpression();
    this.expect(__acorn.tokTypes.parenR);
    if (this.eat(__acorn.tokTypes.arrow)) {
      // (a,)=>a // SequenceExpression makes dummy in the last hole. Drop the dummy.
      var params = inner.expressions || [inner];
      if (params.length && isDummy(params[params.length - 1]))
        { params.pop(); }
      return this.parseArrowExpression(this.startNodeAt(parenStart), params)
    }
    if (this.options.preserveParens) {
      var par = this.startNodeAt(parenStart);
      par.expression = inner;
      inner = this.finishNode(par, "ParenthesizedExpression");
    }
    return inner

  case __acorn.tokTypes.bracketL:
    node = this.startNode();
    node.elements = this.parseExprList(__acorn.tokTypes.bracketR, true);
    return this.finishNode(node, "ArrayExpression")

  case __acorn.tokTypes.braceL:
    return this.parseObj()

  case __acorn.tokTypes._class:
    return this.parseClass(false)

  case __acorn.tokTypes._function:
    node = this.startNode();
    this.next();
    return this.parseFunction(node, false)

  case __acorn.tokTypes._new:
    return this.parseNew()

  case __acorn.tokTypes.backQuote:
    return this.parseTemplate()

  default:
    return this.dummyIdent()
  }
};

lp$2.parseNew = function() {
  var node = this.startNode(), startIndent = this.curIndent, line = this.curLineStart;
  var meta = this.parseIdent(true);
  if (this.options.ecmaVersion >= 6 && this.eat(__acorn.tokTypes.dot)) {
    node.meta = meta;
    node.property = this.parseIdent(true);
    return this.finishNode(node, "MetaProperty")
  }
  var start = this.storeCurrentPos();
  node.callee = this.parseSubscripts(this.parseExprAtom(), start, true, startIndent, line);
  if (this.tok.type == __acorn.tokTypes.parenL) {
    node.arguments = this.parseExprList(__acorn.tokTypes.parenR);
  } else {
    node.arguments = [];
  }
  return this.finishNode(node, "NewExpression")
};

lp$2.parseTemplateElement = function() {
  var elem = this.startNode();

  // The loose parser accepts invalid unicode escapes even in untagged templates.
  if (this.tok.type === __acorn.tokTypes.invalidTemplate) {
    elem.value = {
      raw: this.tok.value,
      cooked: null
    };
  } else {
    elem.value = {
      raw: this.input.slice(this.tok.start, this.tok.end).replace(/\r\n?/g, "\n"),
      cooked: this.tok.value
    };
  }
  this.next();
  elem.tail = this.tok.type === __acorn.tokTypes.backQuote;
  return this.finishNode(elem, "TemplateElement")
};

lp$2.parseTemplate = function() {
  var this$1 = this;

  var node = this.startNode();
  this.next();
  node.expressions = [];
  var curElt = this.parseTemplateElement();
  node.quasis = [curElt];
  while (!curElt.tail) {
    this$1.next();
    node.expressions.push(this$1.parseExpression());
    if (this$1.expect(__acorn.tokTypes.braceR)) {
      curElt = this$1.parseTemplateElement();
    } else {
      curElt = this$1.startNode();
      curElt.value = {cooked: "", raw: ""};
      curElt.tail = true;
      this$1.finishNode(curElt, "TemplateElement");
    }
    node.quasis.push(curElt);
  }
  this.expect(__acorn.tokTypes.backQuote);
  return this.finishNode(node, "TemplateLiteral")
};

lp$2.parseObj = function() {
  var this$1 = this;

  var node = this.startNode();
  node.properties = [];
  this.pushCx();
  var indent = this.curIndent + 1, line = this.curLineStart;
  this.eat(__acorn.tokTypes.braceL);
  if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart; }
  while (!this.closes(__acorn.tokTypes.braceR, indent, line)) {
    var prop = this$1.startNode(), isGenerator = (void 0), isAsync = (void 0), start = (void 0);
    if (this$1.options.ecmaVersion >= 6) {
      start = this$1.storeCurrentPos();
      prop.method = false;
      prop.shorthand = false;
      isGenerator = this$1.eat(__acorn.tokTypes.star);
    }
    this$1.parsePropertyName(prop);
    if (this$1.toks.isAsyncProp(prop)) {
      this$1.parsePropertyName(prop);
      isAsync = true;
    } else {
      isAsync = false;
    }
    if (isDummy(prop.key)) { if (isDummy(this$1.parseMaybeAssign())) { this$1.next(); } this$1.eat(__acorn.tokTypes.comma); continue }
    if (this$1.eat(__acorn.tokTypes.colon)) {
      prop.kind = "init";
      prop.value = this$1.parseMaybeAssign();
    } else if (this$1.options.ecmaVersion >= 6 && (this$1.tok.type === __acorn.tokTypes.parenL || this$1.tok.type === __acorn.tokTypes.braceL)) {
      prop.kind = "init";
      prop.method = true;
      prop.value = this$1.parseMethod(isGenerator, isAsync);
    } else if (this$1.options.ecmaVersion >= 5 && prop.key.type === "Identifier" &&
               !prop.computed && (prop.key.name === "get" || prop.key.name === "set") &&
               (this$1.tok.type != __acorn.tokTypes.comma && this$1.tok.type != __acorn.tokTypes.braceR)) {
      prop.kind = prop.key.name;
      this$1.parsePropertyName(prop);
      prop.value = this$1.parseMethod(false);
    } else {
      prop.kind = "init";
      if (this$1.options.ecmaVersion >= 6) {
        if (this$1.eat(__acorn.tokTypes.eq)) {
          var assign = this$1.startNodeAt(start);
          assign.operator = "=";
          assign.left = prop.key;
          assign.right = this$1.parseMaybeAssign();
          prop.value = this$1.finishNode(assign, "AssignmentExpression");
        } else {
          prop.value = prop.key;
        }
      } else {
        prop.value = this$1.dummyIdent();
      }
      prop.shorthand = true;
    }
    node.properties.push(this$1.finishNode(prop, "Property"));
    this$1.eat(__acorn.tokTypes.comma);
  }
  this.popCx();
  if (!this.eat(__acorn.tokTypes.braceR)) {
    // If there is no closing brace, make the node span to the start
    // of the next token (this is useful for Tern)
    this.last.end = this.tok.start;
    if (this.options.locations) { this.last.loc.end = this.tok.loc.start; }
  }
  return this.finishNode(node, "ObjectExpression")
};

lp$2.parsePropertyName = function(prop) {
  if (this.options.ecmaVersion >= 6) {
    if (this.eat(__acorn.tokTypes.bracketL)) {
      prop.computed = true;
      prop.key = this.parseExpression();
      this.expect(__acorn.tokTypes.bracketR);
      return
    } else {
      prop.computed = false;
    }
  }
  var key = (this.tok.type === __acorn.tokTypes.num || this.tok.type === __acorn.tokTypes.string) ? this.parseExprAtom() : this.parseIdent();
  prop.key = key || this.dummyIdent();
};

lp$2.parsePropertyAccessor = function() {
  if (this.tok.type === __acorn.tokTypes.name || this.tok.type.keyword) { return this.parseIdent() }
};

lp$2.parseIdent = function() {
  var name = this.tok.type === __acorn.tokTypes.name ? this.tok.value : this.tok.type.keyword;
  if (!name) { return this.dummyIdent() }
  var node = this.startNode();
  this.next();
  node.name = name;
  return this.finishNode(node, "Identifier")
};

lp$2.initFunction = function(node) {
  node.id = null;
  node.params = [];
  if (this.options.ecmaVersion >= 6) {
    node.generator = false;
    node.expression = false;
  }
  if (this.options.ecmaVersion >= 8)
    { node.async = false; }
};

// Convert existing expression atom to assignable pattern
// if possible.

lp$2.toAssignable = function(node, binding) {
  var this$1 = this;

  if (!node || node.type == "Identifier" || (node.type == "MemberExpression" && !binding)) {
    // Okay
  } else if (node.type == "ParenthesizedExpression") {
    this.toAssignable(node.expression, binding);
  } else if (this.options.ecmaVersion < 6) {
    return this.dummyIdent()
  } else if (node.type == "ObjectExpression") {
    node.type = "ObjectPattern";
    var props = node.properties;
    for (var i = 0, list = props; i < list.length; i += 1)
      {
      var prop = list[i];

      this$1.toAssignable(prop.value, binding);
    }
  } else if (node.type == "ArrayExpression") {
    node.type = "ArrayPattern";
    this.toAssignableList(node.elements, binding);
  } else if (node.type == "SpreadElement") {
    node.type = "RestElement";
    this.toAssignable(node.argument, binding);
  } else if (node.type == "AssignmentExpression") {
    node.type = "AssignmentPattern";
    delete node.operator;
  } else {
    return this.dummyIdent()
  }
  return node
};

lp$2.toAssignableList = function(exprList, binding) {
  var this$1 = this;

  for (var i = 0, list = exprList; i < list.length; i += 1)
    {
    var expr = list[i];

    this$1.toAssignable(expr, binding);
  }
  return exprList
};

lp$2.parseFunctionParams = function(params) {
  params = this.parseExprList(__acorn.tokTypes.parenR);
  return this.toAssignableList(params, true)
};

lp$2.parseMethod = function(isGenerator, isAsync) {
  var node = this.startNode(), oldInAsync = this.inAsync;
  this.initFunction(node);
  if (this.options.ecmaVersion >= 6)
    { node.generator = !!isGenerator; }
  if (this.options.ecmaVersion >= 8)
    { node.async = !!isAsync; }
  this.inAsync = node.async;
  node.params = this.parseFunctionParams();
  node.expression = this.options.ecmaVersion >= 6 && this.tok.type !== __acorn.tokTypes.braceL;
  node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
  this.inAsync = oldInAsync;
  return this.finishNode(node, "FunctionExpression")
};

lp$2.parseArrowExpression = function(node, params, isAsync) {
  var oldInAsync = this.inAsync;
  this.initFunction(node);
  if (this.options.ecmaVersion >= 8)
    { node.async = !!isAsync; }
  this.inAsync = node.async;
  node.params = this.toAssignableList(params, true);
  node.expression = this.tok.type !== __acorn.tokTypes.braceL;
  node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
  this.inAsync = oldInAsync;
  return this.finishNode(node, "ArrowFunctionExpression")
};

lp$2.parseExprList = function(close, allowEmpty) {
  var this$1 = this;

  this.pushCx();
  var indent = this.curIndent, line = this.curLineStart, elts = [];
  this.next(); // Opening bracket
  while (!this.closes(close, indent + 1, line)) {
    if (this$1.eat(__acorn.tokTypes.comma)) {
      elts.push(allowEmpty ? null : this$1.dummyIdent());
      continue
    }
    var elt = this$1.parseMaybeAssign();
    if (isDummy(elt)) {
      if (this$1.closes(close, indent, line)) { break }
      this$1.next();
    } else {
      elts.push(elt);
    }
    this$1.eat(__acorn.tokTypes.comma);
  }
  this.popCx();
  if (!this.eat(close)) {
    // If there is no closing brace, make the node span to the start
    // of the next token (this is useful for Tern)
    this.last.end = this.tok.start;
    if (this.options.locations) { this.last.loc.end = this.tok.loc.start; }
  }
  return elts
};

lp$2.parseAwait = function() {
  var node = this.startNode();
  this.next();
  node.argument = this.parseMaybeUnary();
  return this.finishNode(node, "AwaitExpression")
};

// Acorn: Loose parser
//
// This module provides an alternative parser (`parse_dammit`) that
// exposes that same interface as `parse`, but will try to parse
// anything as JavaScript, repairing syntax error the best it can.
// There are circumstances in which it will raise an error and give
// up, but they are very rare. The resulting AST will be a mostly
// valid JavaScript AST (as per the [Mozilla parser API][api], except
// that:
//
// - Return outside functions is allowed
//
// - Label consistency (no conflicts, break only to existing labels)
//   is not enforced.
//
// - Bogus Identifier nodes with a name of `"✖"` are inserted whenever
//   the parser got too confused to return anything meaningful.
//
// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
//
// The expected use for this is to *first* try `acorn.parse`, and only
// if that fails switch to `parse_dammit`. The loose parser might
// parse badly indented code incorrectly, so **don't** use it as
// your default parser.
//
// Quite a lot of acorn.js is duplicated here. The alternative was to
// add a *lot* of extra cruft to that file, making it less readable
// and slower. Copying and editing the code allowed me to make
// invasive changes and simplifications without creating a complicated
// tangle.

__acorn.defaultOptions.tabSize = 4;

// eslint-disable-next-line camelcase
function parse_dammit(input, options) {
  return new LooseParser(input, options).parse()
}

__acorn.addLooseExports(parse_dammit, LooseParser, pluginsLoose);

exports.parse_dammit = parse_dammit;
exports.LooseParser = LooseParser;
exports.pluginsLoose = pluginsLoose;

Object.defineProperty(exports, '__esModule', { value: true });

})));

(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
	typeof define === 'function' && define.amd ? define('acorn/dist/walk',['exports'], factory) :
	(factory((global.acorn = global.acorn || {}, global.acorn.walk = global.acorn.walk || {})));
}(this, (function (exports) { 'use strict';

// AST walker module for Mozilla Parser API compatible trees

// A simple walk is one where you simply specify callbacks to be
// called on specific nodes. The last two arguments are optional. A
// simple use would be
//
//     walk.simple(myTree, {
//         Expression: function(node) { ... }
//     });
//
// to do something with all expressions. All Parser API node types
// can be used to identify node types, as well as Expression,
// Statement, and ScopeBody, which denote categories of nodes.
//
// The base argument can be used to pass a custom (recursive)
// walker, and state can be used to give this walked an initial
// state.

function simple(node, visitors, base, state, override) {
  if (!base) { base = exports.base
  ; }(function c(node, st, override) {
    var type = override || node.type, found = visitors[type];
    base[type](node, st, c);
    if (found) { found(node, st); }
  })(node, state, override);
}

// An ancestor walk keeps an array of ancestor nodes (including the
// current node) and passes them to the callback as third parameter
// (and also as state parameter when no other state is present).
function ancestor(node, visitors, base, state) {
  if (!base) { base = exports.base; }
  var ancestors = [];(function c(node, st, override) {
    var type = override || node.type, found = visitors[type];
    var isNew = node != ancestors[ancestors.length - 1];
    if (isNew) { ancestors.push(node); }
    base[type](node, st, c);
    if (found) { found(node, st || ancestors, ancestors); }
    if (isNew) { ancestors.pop(); }
  })(node, state);
}

// A recursive walk is one where your functions override the default
// walkers. They can modify and replace the state parameter that's
// threaded through the walk, and can opt how and whether to walk
// their child nodes (by calling their third argument on these
// nodes).
function recursive(node, state, funcs, base, override) {
  var visitor = funcs ? exports.make(funcs, base) : base;(function c(node, st, override) {
    visitor[override || node.type](node, st, c);
  })(node, state, override);
}

function makeTest(test) {
  if (typeof test == "string")
    { return function (type) { return type == test; } }
  else if (!test)
    { return function () { return true; } }
  else
    { return test }
}

var Found = function Found(node, state) { this.node = node; this.state = state; };

// A full walk triggers the callback on each node
function full(node, callback, base, state, override) {
  if (!base) { base = exports.base
  ; }(function c(node, st, override) {
    var type = override || node.type;
    base[type](node, st, c);
    callback(node, st, type);
  })(node, state, override);
}

// An fullAncestor walk is like an ancestor walk, but triggers
// the callback on each node
function fullAncestor(node, callback, base, state) {
  if (!base) { base = exports.base; }
  var ancestors = [];(function c(node, st, override) {
    var type = override || node.type;
    var isNew = node != ancestors[ancestors.length - 1];
    if (isNew) { ancestors.push(node); }
    base[type](node, st, c);
    callback(node, st || ancestors, ancestors, type);
    if (isNew) { ancestors.pop(); }
  })(node, state);
}

// Find a node with a given start, end, and type (all are optional,
// null can be used as wildcard). Returns a {node, state} object, or
// undefined when it doesn't find a matching node.
function findNodeAt(node, start, end, test, base, state) {
  test = makeTest(test);
  if (!base) { base = exports.base; }
  try {
    (function c(node, st, override) {
      var type = override || node.type;
      if ((start == null || node.start <= start) &&
          (end == null || node.end >= end))
        { base[type](node, st, c); }
      if ((start == null || node.start == start) &&
          (end == null || node.end == end) &&
          test(type, node))
        { throw new Found(node, st) }
    })(node, state);
  } catch (e) {
    if (e instanceof Found) { return e }
    throw e
  }
}

// Find the innermost node of a given type that contains the given
// position. Interface similar to findNodeAt.
function findNodeAround(node, pos, test, base, state) {
  test = makeTest(test);
  if (!base) { base = exports.base; }
  try {
    (function c(node, st, override) {
      var type = override || node.type;
      if (node.start > pos || node.end < pos) { return }
      base[type](node, st, c);
      if (test(type, node)) { throw new Found(node, st) }
    })(node, state);
  } catch (e) {
    if (e instanceof Found) { return e }
    throw e
  }
}

// Find the outermost matching node after a given position.
function findNodeAfter(node, pos, test, base, state) {
  test = makeTest(test);
  if (!base) { base = exports.base; }
  try {
    (function c(node, st, override) {
      if (node.end < pos) { return }
      var type = override || node.type;
      if (node.start >= pos && test(type, node)) { throw new Found(node, st) }
      base[type](node, st, c);
    })(node, state);
  } catch (e) {
    if (e instanceof Found) { return e }
    throw e
  }
}

// Find the outermost matching node before a given position.
function findNodeBefore(node, pos, test, base, state) {
  test = makeTest(test);
  if (!base) { base = exports.base; }
  var max;(function c(node, st, override) {
    if (node.start > pos) { return }
    var type = override || node.type;
    if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))
      { max = new Found(node, st); }
    base[type](node, st, c);
  })(node, state);
  return max
}

// Fallback to an Object.create polyfill for older environments.
var create = Object.create || function(proto) {
  function Ctor() {}
  Ctor.prototype = proto;
  return new Ctor
};

// Used to create a custom walker. Will fill in all missing node
// type properties with the defaults.
function make(funcs, base) {
  if (!base) { base = exports.base; }
  var visitor = create(base);
  for (var type in funcs) { visitor[type] = funcs[type]; }
  return visitor
}

function skipThrough(node, st, c) { c(node, st); }
function ignore(_node, _st, _c) {}

// Node walkers.

var base = {};

base.Program = base.BlockStatement = function (node, st, c) {
  for (var i = 0, list = node.body; i < list.length; i += 1)
    {
    var stmt = list[i];

    c(stmt, st, "Statement");
  }
};
base.Statement = skipThrough;
base.EmptyStatement = ignore;
base.ExpressionStatement = base.ParenthesizedExpression =
  function (node, st, c) { return c(node.expression, st, "Expression"); };
base.IfStatement = function (node, st, c) {
  c(node.test, st, "Expression");
  c(node.consequent, st, "Statement");
  if (node.alternate) { c(node.alternate, st, "Statement"); }
};
base.LabeledStatement = function (node, st, c) { return c(node.body, st, "Statement"); };
base.BreakStatement = base.ContinueStatement = ignore;
base.WithStatement = function (node, st, c) {
  c(node.object, st, "Expression");
  c(node.body, st, "Statement");
};
base.SwitchStatement = function (node, st, c) {
  c(node.discriminant, st, "Expression");
  for (var i = 0, list = node.cases; i < list.length; i += 1) {
    var cs = list[i];

    if (cs.test) { c(cs.test, st, "Expression"); }
    for (var i$1 = 0, list$1 = cs.consequent; i$1 < list$1.length; i$1 += 1)
      {
      var cons = list$1[i$1];

      c(cons, st, "Statement");
    }
  }
};
base.ReturnStatement = base.YieldExpression = base.AwaitExpression = function (node, st, c) {
  if (node.argument) { c(node.argument, st, "Expression"); }
};
base.ThrowStatement = base.SpreadElement =
  function (node, st, c) { return c(node.argument, st, "Expression"); };
base.TryStatement = function (node, st, c) {
  c(node.block, st, "Statement");
  if (node.handler) { c(node.handler, st); }
  if (node.finalizer) { c(node.finalizer, st, "Statement"); }
};
base.CatchClause = function (node, st, c) {
  c(node.param, st, "Pattern");
  c(node.body, st, "ScopeBody");
};
base.WhileStatement = base.DoWhileStatement = function (node, st, c) {
  c(node.test, st, "Expression");
  c(node.body, st, "Statement");
};
base.ForStatement = function (node, st, c) {
  if (node.init) { c(node.init, st, "ForInit"); }
  if (node.test) { c(node.test, st, "Expression"); }
  if (node.update) { c(node.update, st, "Expression"); }
  c(node.body, st, "Statement");
};
base.ForInStatement = base.ForOfStatement = function (node, st, c) {
  c(node.left, st, "ForInit");
  c(node.right, st, "Expression");
  c(node.body, st, "Statement");
};
base.ForInit = function (node, st, c) {
  if (node.type == "VariableDeclaration") { c(node, st); }
  else { c(node, st, "Expression"); }
};
base.DebuggerStatement = ignore;

base.FunctionDeclaration = function (node, st, c) { return c(node, st, "Function"); };
base.VariableDeclaration = function (node, st, c) {
  for (var i = 0, list = node.declarations; i < list.length; i += 1)
    {
    var decl = list[i];

    c(decl, st);
  }
};
base.VariableDeclarator = function (node, st, c) {
  c(node.id, st, "Pattern");
  if (node.init) { c(node.init, st, "Expression"); }
};

base.Function = function (node, st, c) {
  if (node.id) { c(node.id, st, "Pattern"); }
  for (var i = 0, list = node.params; i < list.length; i += 1)
    {
    var param = list[i];

    c(param, st, "Pattern");
  }
  c(node.body, st, node.expression ? "ScopeExpression" : "ScopeBody");
};
// FIXME drop these node types in next major version
// (They are awkward, and in ES6 every block can be a scope.)
base.ScopeBody = function (node, st, c) { return c(node, st, "Statement"); };
base.ScopeExpression = function (node, st, c) { return c(node, st, "Expression"); };

base.Pattern = function (node, st, c) {
  if (node.type == "Identifier")
    { c(node, st, "VariablePattern"); }
  else if (node.type == "MemberExpression")
    { c(node, st, "MemberPattern"); }
  else
    { c(node, st); }
};
base.VariablePattern = ignore;
base.MemberPattern = skipThrough;
base.RestElement = function (node, st, c) { return c(node.argument, st, "Pattern"); };
base.ArrayPattern = function (node, st, c) {
  for (var i = 0, list = node.elements; i < list.length; i += 1) {
    var elt = list[i];

    if (elt) { c(elt, st, "Pattern"); }
  }
};
base.ObjectPattern = function (node, st, c) {
  for (var i = 0, list = node.properties; i < list.length; i += 1)
    {
    var prop = list[i];

    c(prop.value, st, "Pattern");
  }
};

base.Expression = skipThrough;
base.ThisExpression = base.Super = base.MetaProperty = ignore;
base.ArrayExpression = function (node, st, c) {
  for (var i = 0, list = node.elements; i < list.length; i += 1) {
    var elt = list[i];

    if (elt) { c(elt, st, "Expression"); }
  }
};
base.ObjectExpression = function (node, st, c) {
  for (var i = 0, list = node.properties; i < list.length; i += 1)
    {
    var prop = list[i];

    c(prop, st);
  }
};
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
base.SequenceExpression = base.TemplateLiteral = function (node, st, c) {
  for (var i = 0, list = node.expressions; i < list.length; i += 1)
    {
    var expr = list[i];

    c(expr, st, "Expression");
  }
};
base.UnaryExpression = base.UpdateExpression = function (node, st, c) {
  c(node.argument, st, "Expression");
};
base.BinaryExpression = base.LogicalExpression = function (node, st, c) {
  c(node.left, st, "Expression");
  c(node.right, st, "Expression");
};
base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) {
  c(node.left, st, "Pattern");
  c(node.right, st, "Expression");
};
base.ConditionalExpression = function (node, st, c) {
  c(node.test, st, "Expression");
  c(node.consequent, st, "Expression");
  c(node.alternate, st, "Expression");
};
base.NewExpression = base.CallExpression = function (node, st, c) {
  c(node.callee, st, "Expression");
  if (node.arguments)
    { for (var i = 0, list = node.arguments; i < list.length; i += 1)
      {
        var arg = list[i];

        c(arg, st, "Expression");
      } }
};
base.MemberExpression = function (node, st, c) {
  c(node.object, st, "Expression");
  if (node.computed) { c(node.property, st, "Expression"); }
};
base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {
  if (node.declaration)
    { c(node.declaration, st, node.type == "ExportNamedDeclaration" || node.declaration.id ? "Statement" : "Expression"); }
  if (node.source) { c(node.source, st, "Expression"); }
};
base.ExportAllDeclaration = function (node, st, c) {
  c(node.source, st, "Expression");
};
base.ImportDeclaration = function (node, st, c) {
  for (var i = 0, list = node.specifiers; i < list.length; i += 1)
    {
    var spec = list[i];

    c(spec, st);
  }
  c(node.source, st, "Expression");
};
base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.Literal = ignore;

base.TaggedTemplateExpression = function (node, st, c) {
  c(node.tag, st, "Expression");
  c(node.quasi, st);
};
base.ClassDeclaration = base.ClassExpression = function (node, st, c) { return c(node, st, "Class"); };
base.Class = function (node, st, c) {
  if (node.id) { c(node.id, st, "Pattern"); }
  if (node.superClass) { c(node.superClass, st, "Expression"); }
  for (var i = 0, list = node.body.body; i < list.length; i += 1)
    {
    var item = list[i];

    c(item, st);
  }
};
base.MethodDefinition = base.Property = function (node, st, c) {
  if (node.computed) { c(node.key, st, "Expression"); }
  c(node.value, st, "Expression");
};

exports.simple = simple;
exports.ancestor = ancestor;
exports.recursive = recursive;
exports.full = full;
exports.fullAncestor = fullAncestor;
exports.findNodeAt = findNodeAt;
exports.findNodeAround = findNodeAround;
exports.findNodeAfter = findNodeAfter;
exports.findNodeBefore = findNodeBefore;
exports.make = make;
exports.base = base;

Object.defineProperty(exports, '__esModule', { value: true });

})));

// Type description parser
//
// Type description JSON files (such as ecma5.json and browser.json)
// are used to
//
// A) describe types that come from native code
//
// B) to cheaply load the types for big libraries, or libraries that
//    can't be inferred well

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return exports.init = mod;
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/lib/def',{init: mod});
  tern.def = {init: mod};
})(function(exports, infer) {
  "use strict";

  function hop(obj, prop) {
    return Object.prototype.hasOwnProperty.call(obj, prop);
  }

  var TypeParser = exports.TypeParser = function(spec, start, base, forceNew) {
    this.pos = start || 0;
    this.spec = spec;
    this.base = base;
    this.forceNew = forceNew;
  };

  function unwrapType(type, self, args) {
    return type.call ? type(self, args) : type;
  }

  function extractProp(type, prop) {
    if (prop == "!ret") {
      if (type.retval) return type.retval;
      var rv = new infer.AVal;
      type.propagate(new infer.IsCallee(infer.ANull, [], null, rv));
      return rv;
    } else {
      return type.getProp(prop);
    }
  }

  function computedFunc(name, args, retType, generator) {
    return function(self, cArgs) {
      var realArgs = [];
      for (var i = 0; i < args.length; i++) realArgs.push(unwrapType(args[i], self, cArgs));
      return new infer.Fn(name, infer.ANull, realArgs, unwrapType(retType, self, cArgs), generator);
    };
  }
  function computedUnion(types) {
    return function(self, args) {
      var union = new infer.AVal;
      for (var i = 0; i < types.length; i++) unwrapType(types[i], self, args).propagate(union);
      union.maxWeight = 1e5;
      return union;
    };
  }
  function computedArray(inner) {
    return function(self, args) {
      return new infer.Arr(inner(self, args));
    };
  }
  function computedTuple(types) {
    return function(self, args) {
      return new infer.Arr(types.map(function(tp) { return unwrapType(tp, self, args) }))
    }
  }

  TypeParser.prototype = {
    eat: function(str) {
      if (str.length == 1 ? this.spec.charAt(this.pos) == str : this.spec.indexOf(str, this.pos) == this.pos) {
        this.pos += str.length;
        return true;
      }
    },
    word: function(re) {
      var word = "", ch, re = re || /[\w$]/;
      while ((ch = this.spec.charAt(this.pos)) && re.test(ch)) { word += ch; ++this.pos; }
      return word;
    },
    error: function() {
      throw new Error("Unrecognized type spec: " + this.spec + " (at " + this.pos + ")");
    },
    parseFnType: function(comp, name, top, generator) {
      var args = [], names = [], computed = false;
      if (!this.eat(")")) for (var i = 0; ; ++i) {
        var colon = this.spec.indexOf(": ", this.pos), argname;
        if (colon != -1) {
          argname = this.spec.slice(this.pos, colon);
          if (/^[$\w?]+$/.test(argname))
            this.pos = colon + 2;
          else
            argname = null;
        }
        names.push(argname);
        var argType = this.parseType(comp);
        if (argType.call) computed = true;
        args.push(argType);
        if (!this.eat(", ")) {
          this.eat(")") || this.error();
          break;
        }
      }
      var retType, computeRet, computeRetStart, fn;
      if (this.eat(" -> ")) {
        var retStart = this.pos;
        retType = this.parseType(true);
        if (retType.call && !computed) {
          computeRet = retType;
          retType = infer.ANull;
          computeRetStart = retStart;
        }
      } else {
        retType = infer.ANull;
      }
      if (computed) return computedFunc(name, args, retType, generator);

      if (top && (fn = this.base))
        infer.Fn.call(this.base, name, infer.ANull, args, names, retType, generator);
      else
        fn = new infer.Fn(name, infer.ANull, args, names, retType, generator);
      if (computeRet) fn.computeRet = computeRet;
      if (computeRetStart != null) fn.computeRetSource = this.spec.slice(computeRetStart, this.pos);
      return fn;
    },
    parseType: function(comp, name, top) {
      var main = this.parseTypeMaybeProp(comp, name, top);
      if (!this.eat("|")) return main;
      var types = [main], computed = main.call;
      for (;;) {
        var next = this.parseTypeMaybeProp(comp, name, top);
        types.push(next);
        if (next.call) computed = true;
        if (!this.eat("|")) break;
      }
      if (computed) return computedUnion(types);
      var union = new infer.AVal;
      for (var i = 0; i < types.length; i++) types[i].propagate(union);
      union.maxWeight = 1e5;
      return union;
    },
    parseTypeMaybeProp: function(comp, name, top) {
      var result = this.parseTypeInner(comp, name, top);
      while (comp && this.eat(".")) result = this.extendWithProp(result);
      return result;
    },
    extendWithProp: function(base) {
      var propName = this.word(/[\w<>$!:]/) || this.error();
      if (base.apply) return function(self, args) {
        return extractProp(base(self, args), propName);
      };
      return extractProp(base, propName);
    },
    parseTypeInner: function(comp, name, top) {
      var gen
      if (this.eat("fn(") || (gen = this.eat("fn*("))) {
        return this.parseFnType(comp, name, top, gen);
      } else if (this.eat("[")) {
        var inner = this.parseType(comp), types, computed = inner.call
        while (this.eat(", ")) {
          if (!types) types = [inner]
          var next = this.parseType(comp)
          types.push(next)
          computed = computed || next.call
        }
        this.eat("]") || this.error()
        if (computed) return types ? computedTuple(types) : computedArray(inner)
        if (top && this.base) {
          infer.Arr.call(this.base, types || inner)
          return this.base
        }
        return new infer.Arr(types || inner)
      } else if (this.eat("+")) {
        var path = this.word(/[\w$<>\.:!]/)
        var base = infer.cx().localDefs[path + ".prototype"]
        if (!base) {
          var base = parsePath(path);
          if (!(base instanceof infer.Obj)) return base;
          var proto = descendProps(base, ["prototype"])
          if (proto && (proto = proto.getObjType()))
            base = proto
        }
        if (comp && this.eat("[")) return this.parsePoly(base);
        if (top && this.base) {
          this.base.proto = base
          var name = base.hasCtor && base.hasCtor.name || base.name
          if (name) this.base.name = name
          return this.base
        }
        if (top && this.forceNew) return new infer.Obj(base);
        return infer.getInstance(base);
      } else if (this.eat(":")) {
        var name = this.word(/[\w$\.]/)
        return infer.getSymbol(name)
      } else if (comp && this.eat("!")) {
        var arg = this.word(/\d/);
        if (arg) {
          arg = Number(arg);
          return function(_self, args) {return args[arg] || infer.ANull;};
        } else if (this.eat("this")) {
          return function(self) {return self;};
        } else if (this.eat("custom:")) {
          var fname = this.word(/[\w$]/);
          return customFunctions[fname] || function() { return infer.ANull; };
        } else {
          return this.fromWord("!" + this.word(/[\w$<>\.!:]/));
        }
      } else if (this.eat("?")) {
        return infer.ANull;
      } else {
        return this.fromWord(this.word(/[\w$<>\.!:`]/));
      }
    },
    fromWord: function(spec) {
      var cx = infer.cx();
      switch (spec) {
      case "number": return cx.num;
      case "string": return cx.str;
      case "bool": return cx.bool;
      case "<top>": return cx.topScope;
      }
      if (cx.localDefs && spec in cx.localDefs) return cx.localDefs[spec];
      return parsePath(spec);
    },
    parsePoly: function(base) {
      var propName = "<i>", match;
      if (match = this.spec.slice(this.pos).match(/^\s*([\w$:]+)\s*=\s*/)) {
        propName = match[1];
        this.pos += match[0].length;
      }
      var value = this.parseType(true);
      if (!this.eat("]")) this.error();
      if (value.call) return function(self, args) {
        var instance = new infer.Obj(base);
        value(self, args).propagate(instance.defProp(propName));
        return instance;
      };
      var instance = new infer.Obj(base);
      value.propagate(instance.defProp(propName));
      return instance;
    }
  };

  function parseType(spec, name, base, forceNew) {
    var type = new TypeParser(spec, null, base, forceNew).parseType(false, name, true);
    if (/^fn\(/.test(spec)) for (var i = 0; i < type.args.length; ++i) (function(i) {
      var arg = type.args[i];
      if (arg instanceof infer.Fn && arg.args && arg.args.length) addEffect(type, function(_self, fArgs) {
        var fArg = fArgs[i];
        if (fArg) fArg.propagate(new infer.IsCallee(infer.cx().topScope, arg.args, null, infer.ANull));
      });
    })(i);
    return type;
  }

  function addEffect(fn, handler, replaceRet) {
    var oldCmp = fn.computeRet, rv = fn.retval;
    fn.computeRet = function(self, args, argNodes) {
      var handled = handler(self, args, argNodes);
      var old = oldCmp ? oldCmp(self, args, argNodes) : rv;
      return replaceRet ? handled : old;
    };
  }

  var parseEffect = exports.parseEffect = function(effect, fn) {
    var m;
    if (effect.indexOf("propagate ") == 0) {
      var p = new TypeParser(effect, 10);
      var origin = p.parseType(true);
      if (!p.eat(" ")) p.error();
      var target = p.parseType(true);
      addEffect(fn, function(self, args) {
        unwrapType(origin, self, args).propagate(unwrapType(target, self, args));
      });
    } else if (effect.indexOf("call ") == 0) {
      var andRet = effect.indexOf("and return ", 5) == 5;
      var p = new TypeParser(effect, andRet ? 16 : 5);
      var getCallee = p.parseType(true), getSelf = null, getArgs = [];
      if (p.eat(" this=")) getSelf = p.parseType(true);
      while (p.eat(" ")) getArgs.push(p.parseType(true));
      addEffect(fn, function(self, args) {
        var callee = unwrapType(getCallee, self, args);
        var slf = getSelf ? unwrapType(getSelf, self, args) : infer.ANull, as = [];
        for (var i = 0; i < getArgs.length; ++i) as.push(unwrapType(getArgs[i], self, args));
        var result = andRet ? new infer.AVal : infer.ANull;
        callee.propagate(new infer.IsCallee(slf, as, null, result));
        return result;
      }, andRet);
    } else if (m = effect.match(/^custom (\S+)\s*(.*)/)) {
      var customFunc = customFunctions[m[1]];
      if (customFunc) addEffect(fn, m[2] ? customFunc(m[2]) : customFunc);
    } else if (effect.indexOf("copy ") == 0) {
      var p = new TypeParser(effect, 5);
      var getFrom = p.parseType(true);
      p.eat(" ");
      var getTo = p.parseType(true);
      addEffect(fn, function(self, args) {
        var from = unwrapType(getFrom, self, args), to = unwrapType(getTo, self, args);
        from.forAllProps(function(prop, val, local) {
          if (local && prop != "<i>")
            to.propagate(new infer.DefProp(prop, val));
        });
      });
    } else {
      throw new Error("Unknown effect type: " + effect);
    }
  };

  var currentTopScope;

  var parsePath = exports.parsePath = function(path, scope) {
    var cx = infer.cx(), cached = cx.paths[path], origPath = path;
    if (cached != null) return cached;
    cx.paths[path] = infer.ANull;

    var base = scope || currentTopScope || cx.topScope;

    if (cx.localDefs) for (var name in cx.localDefs) {
      if (path.indexOf(name) == 0) {
        if (path == name) return cx.paths[path] = cx.localDefs[path];
        if (path.charAt(name.length) == ".") {
          base = cx.localDefs[name];
          path = path.slice(name.length + 1);
          break;
        }
      }
    }

    var result = descendProps(base, path.split("."))
    // Uncomment this to get feedback on your poorly written .json files
    // if (result == infer.ANull) console.error("bad path: " + origPath + " (" + cx.curOrigin + ")")
    cx.paths[origPath] = result == infer.ANull ? null : result
    return result
  }

  function descendProps(base, parts) {
    for (var i = 0; i < parts.length && base != infer.ANull; ++i) {
      var prop = parts[i];
      if (prop.charAt(0) == "!") {
        if (prop == "!proto") {
          base = (base instanceof infer.Obj && base.proto) || infer.ANull;
        } else {
          var fn = base.getFunctionType();
          if (!fn) {
            base = infer.ANull;
          } else if (prop == "!ret") {
            base = fn.retval && fn.retval.getType(false) || infer.ANull;
          } else {
            var arg = fn.args && fn.args[Number(prop.slice(1))];
            base = (arg && arg.getType(false)) || infer.ANull;
          }
        }
      } else if (base instanceof infer.Obj &&
                 (prop == "prototype" && base instanceof infer.Fn || base.hasProp(prop))) {
        var propVal = base.getProp(prop);
        if (!propVal || propVal.isEmpty())
          base = infer.ANull;
        else
          base = propVal.types[0];
      } else {
        base = infer.ANull;
      }
    }
    return base;
  }

  function emptyObj(ctor) {
    var empty = Object.create(ctor.prototype);
    empty.props = Object.create(null);
    empty.isShell = true;
    return empty;
  }

  function isSimpleAnnotation(spec) {
    if (!spec["!type"] || /^(fn\(|\[|\+)/.test(spec["!type"])) return false;
    for (var prop in spec)
      if (prop != "!type" && prop != "!doc" && prop != "!url" && prop != "!span" && prop != "!data")
        return false;
    return true;
  }

  function passOne(base, spec, path) {
    if (!base) {
      var tp = spec["!type"];
      if (tp) {
        if (/^fn\(/.test(tp)) base = emptyObj(infer.Fn);
        else if (tp.charAt(0) == "[") base = emptyObj(infer.Arr);
        else if (tp.charAt(0) == "+") base = emptyObj(infer.Obj)
        else throw new Error("Invalid !type spec: " + tp);
      } else if (spec["!stdProto"]) {
        base = infer.cx().protos[spec["!stdProto"]];
      } else {
        base = emptyObj(infer.Obj);
      }
      base.name = path;
    }

    for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) {
      var inner = spec[name];
      if (typeof inner == "string" || isSimpleAnnotation(inner)) continue;
      var prop = base.defProp(name);
      passOne(prop.getObjType(), inner, path ? path + "." + name : name).propagate(prop);
    }
    return base;
  }

  function passTwo(base, spec, path) {
    if (base.isShell) {
      delete base.isShell;
      var tp = spec["!type"];
      if (tp) {
        parseType(tp, path, base);
      } else {
        var proto = spec["!proto"] && parseType(spec["!proto"]);
        infer.Obj.call(base, proto instanceof infer.Obj ? proto : true, path);
      }
    }

    var effects = spec["!effects"];
    if (effects && base instanceof infer.Fn) for (var i = 0; i < effects.length; ++i)
      parseEffect(effects[i], base);
    copyInfo(spec, base);

    for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) {
      var inner = spec[name], known = base.defProp(name), innerPath = path ? path + "." + name : name;
      if (typeof inner == "string") {
        if (known.isEmpty()) parseType(inner, innerPath).propagate(known);
      } else {
        if (!isSimpleAnnotation(inner))
          passTwo(known.getObjType(), inner, innerPath);
        else if (known.isEmpty())
          parseType(inner["!type"], innerPath, null, true).propagate(known);
        else
          continue;
        if (inner["!doc"]) known.doc = inner["!doc"];
        if (inner["!url"]) known.url = inner["!url"];
        if (inner["!span"]) known.span = inner["!span"];
      }
    }
    return base;
  }

  function copyInfo(spec, type) {
    if (spec["!doc"]) type.doc = spec["!doc"];
    if (spec["!url"]) type.url = spec["!url"];
    if (spec["!span"]) type.span = spec["!span"];
    if (spec["!data"]) type.metaData = spec["!data"];
  }

  function doLoadEnvironment(data, scope) {
    var cx = infer.cx(), server = cx.parent

    infer.addOrigin(cx.curOrigin = data["!name"] || "env#" + cx.origins.length);
    cx.localDefs = cx.definitions[cx.curOrigin] = Object.create(null);

    if (server) server.signal("preLoadDef", data)

    passOne(scope, data);

    var def = data["!define"];
    if (def) {
      for (var name in def) {
        var spec = def[name];
        cx.localDefs[name] = typeof spec == "string" ? parsePath(spec) : passOne(null, spec, name);
      }
      for (var name in def) {
        var spec = def[name];
        if (typeof spec != "string") passTwo(cx.localDefs[name], def[name], name);
      }
    }

    passTwo(scope, data);

    if (server) server.signal("postLoadDef", data)

    cx.curOrigin = cx.localDefs = null;
  }

  exports.load = function(data, scope) {
    if (!scope) scope = infer.cx().topScope;
    var oldScope = currentTopScope;
    currentTopScope = scope;
    try {
      doLoadEnvironment(data, scope);
    } finally {
      currentTopScope = oldScope;
    }
  };

  exports.parse = function(data, origin, path) {
    var cx = infer.cx();
    if (origin) {
      cx.origin = origin;
      cx.localDefs = cx.definitions[origin];
    }

    try {
      if (typeof data == "string")
        return parseType(data, path);
      else
        return passTwo(passOne(null, data, path), data, path);
    } finally {
      if (origin) cx.origin = cx.localDefs = null;
    }
  };

  // Used to register custom logic for more involved effect or type
  // computation.
  var customFunctions = Object.create(null);
  infer.registerFunction = function(name, f) { customFunctions[name] = f; };

  var IsCreated = infer.constraint({
    construct: function(created, target, spec) {
      this.created = created;
      this.target = target;
      this.spec = spec;
    },
    addType: function(tp) {
      if (tp instanceof infer.Obj && this.created++ < 5) {
        var derived = new infer.Obj(tp), spec = this.spec;
        if (spec instanceof infer.AVal) spec = spec.getObjType(false);
        if (spec instanceof infer.Obj) for (var prop in spec.props) {
          var cur = spec.props[prop].types[0];
          var p = derived.defProp(prop);
          if (cur && cur instanceof infer.Obj && cur.props.value) {
            var vtp = cur.props.value.getType(false);
            if (vtp) p.addType(vtp);
          }
        }
        this.target.addType(derived);
      }
    }
  });

  infer.registerFunction("Object_create", function(_self, args, argNodes) {
    if (argNodes && argNodes.length && argNodes[0].type == "Literal" && argNodes[0].value == null)
      return new infer.Obj();

    var result = new infer.AVal;
    if (args[0]) args[0].propagate(new IsCreated(0, result, args[1]));
    return result;
  });

  var PropSpec = infer.constraint({
    construct: function(target) { this.target = target; },
    addType: function(tp) {
      if (!(tp instanceof infer.Obj)) return;
      if (tp.hasProp("value"))
        tp.getProp("value").propagate(this.target);
      else if (tp.hasProp("get"))
        tp.getProp("get").propagate(new infer.IsCallee(infer.ANull, [], null, this.target));
    }
  });

  infer.registerFunction("Object_defineProperty", function(_self, args, argNodes) {
    if (argNodes && argNodes.length >= 3 && argNodes[1].type == "Literal" &&
        typeof argNodes[1].value == "string") {
      var obj = args[0], connect = new infer.AVal;
      obj.propagate(new infer.DefProp(argNodes[1].value, connect, argNodes[1]));
      args[2].propagate(new PropSpec(connect));
    }
    return infer.ANull;
  });

  infer.registerFunction("Object_defineProperties", function(_self, args, argNodes) {
    if (args.length >= 2) {
      var obj = args[0];
      args[1].forAllProps(function(prop, val, local) {
        if (!local) return;
        var connect = new infer.AVal;
        obj.propagate(new infer.DefProp(prop, connect, argNodes && argNodes[1]));
        val.propagate(new PropSpec(connect));
      });
    }
    return infer.ANull;
  });

  var IsBound = infer.constraint({
    construct: function(self, args, target) {
      this.self = self; this.args = args; this.target = target;
    },
    addType: function(tp) {
      if (!(tp instanceof infer.Fn)) return;
      this.target.addType(new infer.Fn(tp.name, infer.ANull, tp.args.slice(this.args.length),
                                       tp.argNames.slice(this.args.length), tp.retval, tp.generator));
      this.self.propagate(tp.self);
      for (var i = 0; i < Math.min(tp.args.length, this.args.length); ++i)
        this.args[i].propagate(tp.args[i]);
    }
  });

  infer.registerFunction("Function_bind", function(self, args) {
    if (!args.length) return infer.ANull;
    var result = new infer.AVal;
    self.propagate(new IsBound(args[0], args.slice(1), result));
    return result;
  });

  infer.registerFunction("Array_ctor", function(_self, args) {
    var arr = new infer.Arr;
    if (args.length != 1 || !args[0].hasType(infer.cx().num)) {
      var content = arr.getProp("<i>");
      for (var i = 0; i < args.length; ++i) args[i].propagate(content);
    }
    return arr;
  });

  infer.registerFunction("Promise_ctor", function(_self, args, argNodes) {
    var defs6 = infer.cx().definitions.ecma6
    if (!defs6 || args.length < 1) return infer.ANull;
    var self = new infer.Obj(defs6["Promise.prototype"]);
    var valProp = self.defProp(":t", argNodes && argNodes[0]);
    var valArg = new infer.AVal;
    valArg.propagate(valProp);
    var exec = new infer.Fn("execute", infer.ANull, [valArg], ["value"], infer.ANull);
    var reject = defs6.Promise_reject;
    args[0].propagate(new infer.IsCallee(infer.ANull, [exec, reject], null, infer.ANull));
    return self;
  });

  var PromiseResolvesTo = infer.constraint({
    construct: function(output) { this.output = output; },
    addType: function(tp) {
      if (tp.constructor == infer.Obj && tp.name == "Promise" && tp.hasProp(":t"))
        tp.getProp(":t").propagate(this.output);
      else
        tp.propagate(this.output);
    }
  });

  var WG_PROMISE_KEEP_VALUE = 50;

  infer.registerFunction("Promise_then", function(self, args, argNodes) {
    var fn = args.length && args[0].getFunctionType();
    var defs6 = infer.cx().definitions.ecma6
    if (!fn || !defs6) return self;

    var result = new infer.Obj(defs6["Promise.prototype"]);
    var value = result.defProp(":t", argNodes && argNodes[0]), ty;
    if (fn.retval.isEmpty() && (ty = self.getType()) instanceof infer.Obj && ty.hasProp(":t"))
      ty.getProp(":t").propagate(value, WG_PROMISE_KEEP_VALUE);
    fn.retval.propagate(new PromiseResolvesTo(value));
    return result;
  });

  infer.registerFunction("getOwnPropertySymbols", function(_self, args) {
    if (!args.length) return infer.ANull
    var result = new infer.AVal
    args[0].forAllProps(function(prop, _val, local) {
      if (local && prop.charAt(0) == ":") result.addType(infer.getSymbol(prop.slice(1)))
    })
    return result
  })

  infer.registerFunction("getSymbol", function(_self, _args, argNodes) {
    if (argNodes.length && argNodes[0].type == "Literal" && typeof argNodes[0].value == "string")
      return infer.getSymbol(argNodes[0].value)
    else
      return infer.ANull
  })

  return exports;
});

(function(root, mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(exports);
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/lib/signal',["exports"], mod);
  mod((root.tern || (root.tern = {})).signal = {}); // Plain browser env
})(this, function(exports) {

  function on(type, f) {
    var handlers = this._handlers || (this._handlers = Object.create(null));
    (handlers[type] || (handlers[type] = [])).push(f);
  }

  function off(type, f) {
    var arr = this._handlers && this._handlers[type];
    if (arr) for (var i = 0; i < arr.length; ++i)
      if (arr[i] == f) { arr.splice(i, 1); break; }
  }

  var noHandlers = []
  function getHandlers(emitter, type) {
    var arr = emitter._handlers && emitter._handlers[type];
    return arr && arr.length ? arr.slice() : noHandlers
  }

  function signal(type, a1, a2, a3, a4) {
    var arr = getHandlers(this, type)
    for (var i = 0; i < arr.length; ++i) arr[i].call(this, a1, a2, a3, a4)
  }

  function signalReturnFirst(type, a1, a2, a3, a4) {
    var arr = getHandlers(this, type)
    for (var i = 0; i < arr.length; ++i) {
      var result = arr[i].call(this, a1, a2, a3, a4)
      // ORION: Bug 493144 just checking truthiness of result is not accurate
      if (result !== null && result !== undefined) return result
    }
  }

  function hasHandler(type) {
    var arr = this._handlers && this._handlers[type]
    return arr && arr.length > 0 && arr
  }

  exports.mixin = function(obj) {
    obj.on = on; obj.off = off;
    obj.signal = signal;
    obj.signalReturnFirst = signalReturnFirst;
    obj.hasHandler = hasHandler;
    return obj;
  };
});

// Main type inference engine

// Walks an AST, building up a graph of abstract values and constraints
// that cause types to flow from one node to another. Also defines a
// number of utilities for accessing ASTs and scopes.

// Analysis is done in a context, which is tracked by the dynamically
// bound cx variable. Use withContext to set the current context.

// For memory-saving reasons, individual types export an interface
// similar to abstract values (which can hold multiple types), and can
// thus be used in place abstract values that only ever contain a
// single type.
/* eslint-disable */
(function(root, mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(exports, require("acorn/dist/acorn"), require("acorn/dist/acorn_loose"), require("acorn/dist/walk"),
               require("./def"), require("./signal"));
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/lib/infer',["exports", "acorn/dist/acorn", "acorn/dist/acorn_loose", "acorn/dist/walk", "./def", "./signal"], mod);
  mod(root.tern || (root.tern = {}), acorn, acorn, acorn.walk, tern.def, tern.signal); // Plain browser env
})(this, function(exports, acorn, acorn_loose, walk, def, signal) {
  "use strict";

  var toString = exports.toString = function(type, maxDepth, parent) {
    if (!type || type == parent || maxDepth && maxDepth < -3) return "?";
    return type.toString(maxDepth, parent);
  };

  // A variant of AVal used for unknown, dead-end values. Also serves
  // as prototype for AVals, Types, and Constraints because it
  // implements 'empty' versions of all the methods that the code
  // expects.
  var ANull = exports.ANull = signal.mixin({
    addType: function() {},
    propagate: function() {},
    getProp: function() { return ANull; },
    forAllProps: function() {},
    hasType: function() { return false; },
    isEmpty: function() { return true; },
    getFunctionType: function() {},
    getObjType: function() {},
    getSymbolType: function() {},
    getType: function() {},
    gatherProperties: function() {},
    propagatesTo: function() {},
    typeHint: function() {},
    propHint: function() {},
    toString: function() { return "?"; }
  });

  function extend(proto, props) {
    var obj = Object.create(proto);
    if (props) for (var prop in props) obj[prop] = props[prop];
    return obj;
  }

  // ABSTRACT VALUES

  var WG_DEFAULT = 100, WG_NEW_INSTANCE = 90, WG_MADEUP_PROTO = 10,
      WG_MULTI_MEMBER = 6, WG_CATCH_ERROR = 6,
      WG_PHANTOM_OBJ = 1,
      WG_GLOBAL_THIS = 90, WG_SPECULATIVE_THIS = 2, WG_SPECULATIVE_PROTO_THIS = 4;

  var AVal = exports.AVal = function() {
    this.types = [];
    this.forward = null;
    this.maxWeight = 0;
  };
  AVal.prototype = extend(ANull, {
    addType: function(type, weight) {
      weight = weight || WG_DEFAULT;
      if (this.maxWeight < weight) {
        this.maxWeight = weight;
        if (this.types.length == 1 && this.types[0] == type) return;
        this.types.length = 0;
      } else if (this.maxWeight > weight || this.types.indexOf(type) > -1) {
        return;
      }

      this.signal("addType", type);
      this.types.push(type);
      var forward = this.forward;
      if (forward) withWorklist(function(add) {
        for (var i = 0; i < forward.length; ++i) add(type, forward[i], weight);
      });
    },

    propagate: function(target, weight) {
      if (target == ANull || (target instanceof Type && this.forward && this.forward.length > 2)) return;
      if (weight && weight != WG_DEFAULT) target = new Muffle(target, weight);
      (this.forward || (this.forward = [])).push(target);
      var types = this.types;
      if (types.length) withWorklist(function(add) {
        for (var i = 0; i < types.length; ++i) add(types[i], target, weight);
      });
    },

    getProp: function(prop) {
      if (ignoredProp(prop)) return ANull;
      var found = (this.props || (this.props = Object.create(null)))[prop];
      if (!found) {
        found = this.props[prop] = new AVal;
        this.propagate(new GetProp(prop, found));
      }
      return found;
    },

    forAllProps: function(c) {
      this.propagate(new ForAllProps(c));
    },

    hasType: function(type) {
      return this.types.indexOf(type) > -1;
    },
    isEmpty: function() { return this.types.length === 0; },
    getFunctionType: function() {
      for (var i = this.types.length - 1; i >= 0; --i)
        if (this.types[i] instanceof Fn) return this.types[i];
    },
    getObjType: function() {
      var seen = null;
      for (var i = this.types.length - 1; i >= 0; --i) {
        var type = this.types[i];
        if (!(type instanceof Obj)) continue;
        if (type.name) return type;
        if (!seen) seen = type;
      }
      return seen;
    },

    getSymbolType: function() {
      for (var i = this.types.length - 1; i >= 0; --i)
        if (this.types[i] instanceof Sym) return this.types[i]
    },

    getType: function(guess) {
      if (this.types.length === 0 && guess !== false) return this.makeupType();
      if (this.types.length === 1) return this.types[0];
      return canonicalType(this.types);
    },

    toString: function(maxDepth, parent) {
      if (this.types.length == 0) return toString(this.makeupType(), maxDepth, parent);
      if (this.types.length == 1) return toString(this.types[0], maxDepth, parent);
      var simplified = simplifyTypes(this.types);
      if (simplified.length > 2) return "?";
      return simplified.map(function(tp) { return toString(tp, maxDepth, parent); }).join("|");
    },

    makeupPropType: function(obj) {
      var propName = this.propertyName;

      var protoProp = obj.proto && obj.proto.hasProp(propName);
      if (protoProp) {
        var fromProto = protoProp.getType();
        if (fromProto) return fromProto;
      }

      if (propName != "<i>") {
        var computedProp = obj.hasProp("<i>");
        if (computedProp) return computedProp.getType();
      } else if (obj.props["<i>"] != this) {
        for (var prop in obj.props) {
          var val = obj.props[prop];
          if (!val.isEmpty()) return val.getType();
        }
      }
    },

    makeupType: function() {
      var computed = this.propertyOf && this.makeupPropType(this.propertyOf);
      if (computed) return computed;

      if (!this.forward) return null;
      for (var i = this.forward.length - 1; i >= 0; --i) {
        var hint = this.forward[i].typeHint();
        if (hint && !hint.isEmpty()) {guessing = true; return hint;}
      }

      var props = Object.create(null), foundProp = null;
      for (var i = 0; i < this.forward.length; ++i) {
        var prop = this.forward[i].propHint();
        if (prop && prop != "length" && prop != "<i>" && prop != "✖" && prop != cx.completingProperty) {
          props[prop] = true;
          foundProp = prop;
        }
      }
      if (!foundProp) return null;

      var objs = objsWithProp(foundProp);
      if (objs) {
        var matches = [];
        search: for (var i = 0; i < objs.length; ++i) {
          var obj = objs[i];
          for (var prop in props) if (!obj.hasProp(prop)) continue search;
          if (obj.hasCtor) obj = getInstance(obj);
          matches.push(obj);
        }
        var canon = canonicalType(matches);
        if (canon) {
        	guessing = true;
        	
        	// ORION
        	if (matches.length > 0) {
        		canon.potentialMatches = matches;
        	}
        	
        	return canon;
        }
      }
    },

    typeHint: function() { return this.types.length ? this.getType() : null; },
    propagatesTo: function() { return this; },

    gatherProperties: function(f, depth) {
      for (var i = 0; i < this.types.length; ++i)
        this.types[i].gatherProperties(f, depth);
    },

    guessProperties: function(f) {
      if (this.forward) for (var i = 0; i < this.forward.length; ++i) {
        var prop = this.forward[i].propHint();
        if (prop) f(prop, null, 0);
      }
      var guessed = this.makeupType();
      if (guessed) guessed.gatherProperties(f);
    }
  });

  function similarAVal(a, b, depth) {
    var typeA = a.getType(false), typeB = b.getType(false);
    if (!typeA || !typeB) return true;
    return similarType(typeA, typeB, depth);
  }

  function similarType(a, b, depth) {
    if (!a || depth >= 5) return b;
    if (!a || a == b) return a;
    if (!b) return a;
    if (a.constructor != b.constructor) return false;
    if (a.constructor == Arr) {
      var innerA = a.getProp("<i>").getType(false);
      if (!innerA) return b;
      var innerB = b.getProp("<i>").getType(false);
      if (!innerB || similarType(innerA, innerB, depth + 1)) return b;
    } else if (a.constructor == Obj) {
      var propsA = 0, propsB = 0, same = 0;
      for (var prop in a.props) {
        propsA++;
        if (prop in b.props && similarAVal(a.props[prop], b.props[prop], depth + 1))
          same++;
      }
      for (var prop in b.props) propsB++;
      if (propsA && propsB && same < Math.max(propsA, propsB) / 2) return false;
      return propsA > propsB ? a : b;
    } else if (a.constructor == Fn) {
      if (a.args.length != b.args.length ||
          !a.args.every(function(tp, i) { return similarAVal(tp, b.args[i], depth + 1); }) ||
          !similarAVal(a.retval, b.retval, depth + 1) || !similarAVal(a.self, b.self, depth + 1))
        return false;
      return a;
    } else {
      return false;
    }
  }

  var simplifyTypes = exports.simplifyTypes = function(types) {
    var found = [];
    outer: for (var i = 0; i < types.length; ++i) {
      var tp = types[i];
      for (var j = 0; j < found.length; j++) {
        var similar = similarType(tp, found[j], 0);
        if (similar) {
          found[j] = similar;
          continue outer;
        }
      }
      found.push(tp);
    }
    return found;
  };

  function canonicalType(types) {
    var arrays = 0, fns = 0, objs = 0, prim = null;
    for (var i = 0; i < types.length; ++i) {
      var tp = types[i];
      if (tp instanceof Arr) ++arrays;
      else if (tp instanceof Fn) ++fns;
      else if (tp instanceof Obj) ++objs;
      else if (tp instanceof Prim) {
        if (prim && tp.name != prim.name) return null;
        prim = tp;
      }
    }
    var kinds = (arrays && 1) + (fns && 1) + (objs && 1) + (prim && 1);
    if (kinds > 1) return null;
    if (prim) return prim;

    var maxScore = 0, maxTp = null;
    for (var i = 0; i < types.length; ++i) {
      var tp = types[i], score = 0;
      if (arrays) {
        score = tp.getProp("<i>").isEmpty() ? 1 : 2;
      } else if (fns) {
        score = 1;
        for (var j = 0; j < tp.args.length; ++j) if (!tp.args[j].isEmpty()) ++score;
        if (!tp.retval.isEmpty()) ++score;
      } else if (objs) {
        score = tp.name ? 100 : 2;
      }
      if (score >= maxScore) { maxScore = score; maxTp = tp; }
    }
    return maxTp;
  }

  // PROPAGATION STRATEGIES

  var constraint = exports.constraint = function(methods) {
    var ctor = function() {
      this.origin = cx.curOrigin;
      this.construct.apply(this, arguments);
    };
    ctor.prototype = Object.create(ANull);
    for (var m in methods) if (methods.hasOwnProperty(m)) ctor.prototype[m] = methods[m];
    return ctor;
  };

  var GetProp = constraint({
    construct: function(prop, target) {
      this.prop = prop; this.target = target;
    },
    addType: function(type, weight) {
      if (type.getProp)
        type.getProp(this.prop).propagate(this.target, weight);
    },
    propHint: function() { return this.prop; },
    propagatesTo: function() {
      if (this.prop == "<i>" || !/[^\w_]/.test(this.prop))
        return {target: this.target, pathExt: "." + this.prop};
    }
  });

  var DefProp = exports.PropHasSubset = exports.DefProp = constraint({
    construct: function(prop, type, originNode) {
      this.prop = prop; this.type = type; this.originNode = originNode;
    },
    addType: function(type, weight) {
      if (!(type instanceof Obj)) return;
      var prop = type.defProp(this.prop, this.originNode);
      if (!prop.origin) prop.origin = this.origin;
      this.type.propagate(prop, weight);
    },
    propHint: function() { return this.prop; }
  });

  var ForAllProps = constraint({
    construct: function(c) { this.c = c; },
    addType: function(type) {
      if (!(type instanceof Obj)) return;
      type.forAllProps(this.c);
    }
  });

  function withDisabledComputing(fn, body) {
    cx.disabledComputing = {fn: fn, prev: cx.disabledComputing};
    var result = body();
    cx.disabledComputing = cx.disabledComputing.prev;
    return result;
  }
  var IsCallee = exports.IsCallee = constraint({
    construct: function(self, args, argNodes, retval) {
      this.self = self; this.args = args; this.argNodes = argNodes; this.retval = retval;
      this.disabled = cx.disabledComputing;
    },
    addType: function(fn, weight) {
      if (!(fn instanceof Fn)) return;
      for (var i = 0; i < this.args.length; ++i) {
        if (i < fn.args.length) this.args[i].propagate(fn.args[i], weight);
        if (fn.arguments) this.args[i].propagate(fn.arguments, weight);
      }
      if (!fn.isArrowFn())
        this.self.propagate(fn.self, this.self == cx.topScope ? WG_GLOBAL_THIS : weight);
      var compute = fn.computeRet, result = fn.retval
      if (compute) for (var d = this.disabled; d; d = d.prev)
        if (d.fn == fn || fn.originNode && d.fn.originNode == fn.originNode) compute = null;
      if (compute) {
        var old = cx.disabledComputing;
        cx.disabledComputing = this.disabled;
        result = compute(this.self, this.args, this.argNodes)
        cx.disabledComputing = old;
      }
      //ORION - if a dependency fails to resolve, we end up with a null in the worklist, guard against it
      var iter = maybeIterator(fn, result);
      if(iter) {
      	iter.propagate(this.retval, weight);
      }
    },
    typeHint: function() {
      var names = [];
      for (var i = 0; i < this.args.length; ++i) names.push("?");
      return new Fn(null, this.self, this.args, names, ANull);
    },
    propagatesTo: function() {
      return {target: this.retval, pathExt: ".!ret"};
    }
  });

  var HasMethodCall = constraint({
    construct: function(propName, args, argNodes, retval) {
      this.propName = propName; this.args = args; this.argNodes = argNodes; this.retval = retval;
      this.disabled = cx.disabledComputing;
    },
    addType: function(obj, weight) {
      var callee = new IsCallee(obj, this.args, this.argNodes, this.retval);
      callee.disabled = this.disabled;
      obj.getProp(this.propName).propagate(callee, weight);
    },
    propHint: function() { return this.propName; }
  });

  var IsCtor = exports.IsCtor = constraint({
    construct: function(target, noReuse) {
      this.target = target; this.noReuse = noReuse;
    },
    addType: function(f, weight) {
      if (!(f instanceof Fn)) return;
      if (cx.parent && !cx.parent.options.reuseInstances) this.noReuse = true;
      f.getProp("prototype").propagate(new IsProto(this.noReuse ? false : f, this.target), weight);
    }
  });

  var getInstance = exports.getInstance = function(obj, ctor) {
    if (ctor === false) return new Obj(obj);

    if (!ctor) ctor = obj.hasCtor;
    if (!obj.instances) obj.instances = [];
    for (var i = 0; i < obj.instances.length; ++i) {
      var cur = obj.instances[i];
      if (cur.ctor == ctor) return cur.instance;
    }
    var instance = new Obj(obj, ctor && ctor.name);
    instance.origin = obj.origin;
    obj.instances.push({ctor: ctor, instance: instance});
    return instance;
  };

  var IsProto = exports.IsProto = constraint({
    construct: function(ctor, target) {
      this.ctor = ctor; this.target = target;
    },
    addType: function(o, _weight) {
      if (!(o instanceof Obj)) return;
      if ((this.count = (this.count || 0) + 1) > 8) return;
      if (o == cx.protos.Array)
        this.target.addType(new Arr);
      else
        this.target.addType(getInstance(o, this.ctor));
    }
  });

  var FnPrototype = constraint({
    construct: function(fn) { this.fn = fn; },
    addType: function(o, _weight) {
      if (o instanceof Obj && !o.hasCtor) {
        o.hasCtor = this.fn;
        var adder = new SpeculativeThis(o, this.fn);
        adder.addType(this.fn);
        o.forAllProps(function(_prop, val, local) {
          if (local) val.propagate(adder);
        });
      }
    }
  });

  var IsAdded = constraint({
    construct: function(other, target) {
      this.other = other; this.target = target;
    },
    addType: function(type, weight) {
      if (type == cx.str)
        this.target.addType(cx.str, weight);
      else if (type == cx.num && this.other.hasType(cx.num))
        this.target.addType(cx.num, weight);
    },
    typeHint: function() { return this.other; }
  });

  var IfObj = exports.IfObj = constraint({
    construct: function(target) { this.target = target; },
    addType: function(t, weight) {
      if (t instanceof Obj) this.target.addType(t, weight);
    },
    propagatesTo: function() { return this.target; }
  });

  var SpeculativeThis = constraint({
    construct: function(obj, ctor) { this.obj = obj; this.ctor = ctor; },
    addType: function(tp) {
      if (tp instanceof Fn && tp.self)
        tp.self.addType(getInstance(this.obj, this.ctor), WG_SPECULATIVE_PROTO_THIS);
    }
  });

  var HasProto = constraint({
    construct: function(obj) { this.obj = obj },
    addType: function(tp) {
      if (tp instanceof Obj && this.obj.proto == cx.protos.Object)
        this.obj.replaceProto(tp)
    }
  });

  var Muffle = constraint({
    construct: function(inner, weight) {
      this.inner = inner; this.weight = weight;
    },
    addType: function(tp, weight) {
      this.inner.addType(tp, Math.min(weight, this.weight));
    },
    propagatesTo: function() { return this.inner.propagatesTo(); },
    typeHint: function() { return this.inner.typeHint(); },
    propHint: function() { return this.inner.propHint(); }
  });

  // TYPE OBJECTS

  var Type = exports.Type = function() {};
  Type.prototype = extend(ANull, {
    constructor: Type,
    propagate: function(c, w) { c.addType(this, w); },
    hasType: function(other) { return other == this; },
    isEmpty: function() { return false; },
    typeHint: function() { return this; },
    getType: function() { return this; }
  });

  var Prim = exports.Prim = function(proto, name) { this.name = name; this.proto = proto; };
  Prim.prototype = extend(Type.prototype, {
    constructor: Prim,
    toString: function() { return this.name; },
    getProp: function(prop) {return this.proto.hasProp(prop) || ANull;},
    gatherProperties: function(f, depth) {
      if (this.proto) this.proto.gatherProperties(f, depth);
    }
  });

  function isInteger(str) {
    var c0 = str.charCodeAt(0)
    if (c0 >= 48 && c0 <= 57) return !/\D/.test(str)
    else return false
  }

  var Obj = exports.Obj = function(proto, name) {
    if (!this.props) this.props = Object.create(null);
    this.proto = proto === true ? cx.protos.Object : proto;
    if (this.proto && !(this.proto instanceof Obj)) {
      throw new Error("bad " + Object.keys(this.proto).join())
    }
    if (proto && !name && proto.name && !(this instanceof Fn)) {
      var match = /^(.*)\.prototype$/.exec(this.proto.name);
      if (match) name = match[1];
    }
    this.name = name;
    this.maybeProps = null;
    this.origin = cx.curOrigin;
  };
  Obj.prototype = extend(Type.prototype, {
    constructor: Obj,
    toString: function(maxDepth) {
      if (maxDepth == null) maxDepth = 0;
      if (maxDepth <= 0 && this.name) return this.name;
      var props = [], etc = false;
      for (var prop in this.props) if (prop != "<i>") {
        if (props.length > 5) { etc = true; break; }
        if (maxDepth)
          props.push(prop + ": " + toString(this.props[prop], maxDepth - 1, this));
        else
          props.push(prop);
      }
      props.sort();
      if (etc) props.push("...");
      return "{" + props.join(", ") + "}";
    },
    hasProp: function(prop, searchProto) {
      if (isInteger(prop)) prop = this.normalizeIntegerProp(prop)
      var found = this.props[prop];
      if (searchProto !== false)
        for (var p = this.proto; p && !found; p = p.proto) found = p.props[prop];
      return found;
    },
    defProp: function(prop, originNode) {
      var found = this.hasProp(prop, false);
      if (found) {
        if (originNode && !found.originNode) found.originNode = originNode;
        return found;
      }
      if (ignoredProp(prop)) return ANull;
      if (isInteger(prop)) prop = this.normalizeIntegerProp(prop)

      var av = this.maybeProps && this.maybeProps[prop];
      if (av) {
        delete this.maybeProps[prop];
        this.maybeUnregProtoPropHandler();
      } else {
        av = new AVal;
        av.propertyOf = this;
        av.propertyName = prop;
      }

      this.props[prop] = av;
      av.originNode = originNode;
      av.origin = cx.curOrigin;
      this.broadcastProp(prop, av, true);
      return av;
    },
    getProp: function(prop) {
      var found = this.hasProp(prop, true) || (this.maybeProps && this.maybeProps[prop]);
      if (found) return found;
      if (ignoredProp(prop)) return ANull;
      if (isInteger(prop)) prop = this.normalizeIntegerProp(prop)
      var av = this.ensureMaybeProps()[prop] = new AVal;
      av.propertyOf = this;
      av.propertyName = prop;
      return av;
    },
    normalizeIntegerProp: function(_) { return "<i>" },
    broadcastProp: function(prop, val, local) {
      if (local) {
        this.signal("addProp", prop, val);
        // If this is a scope, it shouldn't be registered
        if (!(this instanceof Scope)) registerProp(prop, this);
      }

      if (this.onNewProp) for (var i = 0; i < this.onNewProp.length; ++i) {
        var h = this.onNewProp[i];
        h.onProtoProp ? h.onProtoProp(prop, val, local) : h(prop, val, local);
      }
    },
    onProtoProp: function(prop, val, _local) {
      var maybe = this.maybeProps && this.maybeProps[prop];
      if (maybe) {
        delete this.maybeProps[prop];
        this.maybeUnregProtoPropHandler();
        this.proto.getProp(prop).propagate(maybe);
      }
      this.broadcastProp(prop, val, false);
    },
    replaceProto: function(proto) {
      if (this.proto && this.maybeProps)
        this.proto.unregPropHandler(this)
      this.proto = proto
      if (this.maybeProps)
        this.proto.forAllProps(this)
    },
    ensureMaybeProps: function() {
      if (!this.maybeProps) {
        if (this.proto) this.proto.forAllProps(this);
        this.maybeProps = Object.create(null);
      }
      return this.maybeProps;
    },
    removeProp: function(prop) {
      var av = this.props[prop];
      delete this.props[prop];
      this.ensureMaybeProps()[prop] = av;
      av.types.length = 0;
    },
    forAllProps: function(c) {
      if (!this.onNewProp) {
        this.onNewProp = [];
        if (this.proto) this.proto.forAllProps(this);
      }
      this.onNewProp.push(c);
      for (var o = this; o; o = o.proto) for (var prop in o.props) {
        if (c.onProtoProp)
          c.onProtoProp(prop, o.props[prop], o == this);
        else
          c(prop, o.props[prop], o == this);
      }
    },
    maybeUnregProtoPropHandler: function() {
      if (this.maybeProps) {
        for (var _n in this.maybeProps) return;
        this.maybeProps = null;
      }
      if (!this.proto || this.onNewProp && this.onNewProp.length) return;
      this.proto.unregPropHandler(this);
    },
    unregPropHandler: function(handler) {
      for (var i = 0; i < this.onNewProp.length; ++i)
        if (this.onNewProp[i] == handler) { this.onNewProp.splice(i, 1); break; }
      this.maybeUnregProtoPropHandler();
    },
    gatherProperties: function(f, depth) {
      for (var prop in this.props) if (prop != "<i>" && prop.charAt(0) != ":")
        f(prop, this, depth);
      if (this.proto) this.proto.gatherProperties(f, depth + 1);
    },
    getObjType: function() { return this; }
  });

  var geckoIterators = typeof StopIteration != "undefined";
  function ignoredProp(name) {
    return name == "__proto__" || name == "✖" || geckoIterators && name == "__iterator__";
  }

  var Fn = exports.Fn = function(name, self, args, argNames, retval, generator) {
    Obj.call(this, cx.protos.Function, name);
    this.self = self;
    this.args = args;
    this.argNames = argNames;
    this.retval = retval;
    this.generator = generator
  };
  Fn.prototype = extend(Obj.prototype, {
    constructor: Fn,
    toString: function(maxDepth) {
      if (maxDepth == null) maxDepth = 0;
      var str = this.generator ? "fn*(" : "fn(";
      for (var i = 0; i < this.args.length; ++i) {
        if (i) str += ", ";
        var name = this.argNames[i];
        if (name && name != "?") str += name + ": ";
        str += maxDepth > -3 ? toString(this.args[i], maxDepth - 1, this) : "?";
      }
      str += ")";
      if (!this.retval.isEmpty())
        str += " -> " + (maxDepth > -3 ? toString(this.retval, maxDepth - 1, this) : "?");
      return str;
    },
    getProp: function(prop) {
      if (prop == "prototype") {
        var known = this.hasProp(prop, false);
        if (!known) {
          known = this.defProp(prop);
          var proto = new Obj(true, this.name && this.name + ".prototype");
          proto.origin = this.origin;
          known.addType(proto, WG_MADEUP_PROTO);
        }
        return known;
      }
      return Obj.prototype.getProp.call(this, prop);
    },
    defProp: function(prop, originNode) {
      if (prop == "prototype") {
        var found = this.hasProp(prop, false);
        if (found) return found;
        found = Obj.prototype.defProp.call(this, prop, originNode);
        found.origin = this.origin;
        found.propagate(new FnPrototype(this));
        return found;
      }
      return Obj.prototype.defProp.call(this, prop, originNode);
    },
    getFunctionType: function() { return this; },
    isArrowFn: function() { return this.originNode && this.originNode.type == "ArrowFunctionExpression" }
  });

  var Arr = exports.Arr = function(contentType) {
    Obj.call(this, cx.protos.Array)
    var content = this.defProp("<i>")
    if (Array.isArray(contentType)) {
      this.tuple = contentType.length
      for (var i = 0; i < contentType.length; i++) {
        var prop = this.defProp(String(i))
        contentType[i].propagate(prop)
        prop.propagate(content)
      }
    } else if (contentType) {
      this.tuple = 0
      contentType.propagate(content)
    }
  };
  Arr.prototype = extend(Obj.prototype, {
    constructor: Arr,
    toString: function(maxDepth) {
      if (maxDepth == null) maxDepth = 0
      if (maxDepth <= -3) return "[?]"
      var content = ""
      if (this.tuple) {
        var similar
        for (var i = 0; i in this.props; i++) {
          var type = toString(this.getProp(String(i)), maxDepth - 1, this)
          if (similar == null)
            similar = type
          else if (similar != type)
            similar = false
          else
            similar = type
          content += (content ? ", " : "") + type
        }
        if (similar) content = similar
      } else {
        content = toString(this.getProp("<i>"), maxDepth - 1, this)
      }
      return "[" + content + "]"
    },
    normalizeIntegerProp: function(prop) {
      if (+prop < this.tuple) return prop
      else return "<i>"
    }
  });

  var Sym = exports.Sym = function(name, originNode) {
    Prim.call(this, cx.protos.Symbol, "Symbol")
    this.symName = name
    this.originNode = originNode
  }
  Sym.prototype = extend(Prim.prototype, {
    constructor: Sym,
    asPropName: function() { return ":" + this.symName },
    getSymbolType: function() { return this }
  })

  exports.getSymbol = function(name, originNode) {
    var cleanName = name.replace(/[^\w$\.]/g, "_")
    var known = cx.symbols[cleanName]
    if (known) {
      if (originNode && !known.originNode) known.originNode = originNode
      return known
    }
    return cx.symbols[cleanName] = new Sym(cleanName, originNode)
  }

  // THE PROPERTY REGISTRY

  function registerProp(prop, obj) {
    var data = cx.props[prop] || (cx.props[prop] = []);
    data.push(obj);
  }

  function objsWithProp(prop) {
    return cx.props[prop];
  }

  // INFERENCE CONTEXT

  exports.Context = function(defs, parent) {
    this.parent = parent;
    this.props = Object.create(null);
    this.protos = Object.create(null);
    this.origins = [];
    this.curOrigin = "ecma5";
    this.paths = Object.create(null);
    this.definitions = Object.create(null);
    this.purgeGen = 0;
    this.workList = null;
    this.disabledComputing = null;
    this.curSuperCtor = this.curSuper = null;
    this.symbols = Object.create(null)

    exports.withContext(this, function() {
      cx.protos.Object = new Obj(null, "Object.prototype");
      cx.topScope = new Scope();
      cx.topScope.name = "<top>";
      cx.protos.Array = new Obj(true, "Array.prototype");
      cx.protos.Function = new Fn("Function.prototype", ANull, [], [], ANull);
      cx.protos.Function.proto = cx.protos.Object;
      cx.protos.RegExp = new Obj(true, "RegExp.prototype");
      cx.protos.String = new Obj(true, "String.prototype");
      cx.protos.Number = new Obj(true, "Number.prototype");
      cx.protos.Boolean = new Obj(true, "Boolean.prototype");
      cx.protos.Symbol = new Obj(true, "Symbol.prototype");
      cx.str = new Prim(cx.protos.String, "string");
      cx.bool = new Prim(cx.protos.Boolean, "bool");
      cx.num = new Prim(cx.protos.Number, "number");
      cx.curOrigin = null;

      if (defs) for (var i = 0; i < defs.length; ++i)
        def.load(defs[i]);
    });
  };

  exports.Context.prototype.startAnalysis = function() {
    this.disabledComputing = this.workList = this.curSuperCtor = this.curSuper = null;
  };

  var cx = null;
  exports.cx = function() { return cx; };

  exports.withContext = function(context, f) {
    var old = cx;
    cx = context;
    try { return f(); }
    finally { cx = old; }
  };

  exports.TimedOut = function() {
    this.message = "Timed out";
    this.stack = (new Error()).stack;
  };
  exports.TimedOut.prototype = Object.create(Error.prototype);
  exports.TimedOut.prototype.name = "infer.TimedOut";

  var timeout;
  exports.withTimeout = function(ms, f) {
    var end = +new Date + ms;
    var oldEnd = timeout;
    if (oldEnd && oldEnd < end) return f();
    timeout = end;
    try { return f(); }
    finally { timeout = oldEnd; }
  };

  exports.addOrigin = function(origin) {
    if (cx.origins.indexOf(origin) < 0) cx.origins.push(origin);
  };

  var baseMaxWorkDepth = 20, reduceMaxWorkDepth = 0.0001;
  function withWorklist(f) {
    if (cx.workList) return f(cx.workList);

    var list = [], depth = 0;
    var add = cx.workList = function(type, target, weight) {
      if (depth < baseMaxWorkDepth - reduceMaxWorkDepth * list.length)
        list.push(type, target, weight, depth);
    };
    var ret = f(add);
    for (var i = 0; i < list.length; i += 4) {
      if (timeout && +new Date >= timeout)
        throw new exports.TimedOut();
      depth = list[i + 3] + 1;
      list[i + 1].addType(list[i], list[i + 2]);
    }
    cx.workList = null;
    return ret;
  }

  function withSuper(ctor, obj, f) {
    var oldCtor = cx.curSuperCtor, oldObj = cx.curSuper
    cx.curSuperCtor = ctor; cx.curSuper = obj
    var result = f()
    cx.curSuperCtor = oldCtor; cx.curSuper = oldObj
    return result
  }

  // SCOPES

  var Scope = exports.Scope = function(prev, originNode, isBlock) {
    Obj.call(this, prev || true);
    this.prev = prev;
    this.originNode = originNode
    this.isBlock = !!isBlock
  };
  Scope.prototype = extend(Obj.prototype, {
    constructor: Scope,
    defVar: function(name, originNode) {
      for (var s = this; ; s = s.proto) {
        var found = s.props[name];
        if (found) return found;
        if (!s.prev) return s.defProp(name, originNode);
      }
    }
  });

  function functionScope(scope) {
    while (scope.isBlock) scope = scope.prev
    return scope
  }


  // RETVAL COMPUTATION HEURISTICS

  function maybeInstantiate(scope, score) {
    var fn = functionScope(scope).fnType
    if (fn) fn.instantiateScore = (fn.instantiateScore || 0) + score;
  }

  var NotSmaller = {};
  function nodeSmallerThan(node, n) {
    try {
      walk.simple(node, {Expression: function() { if (--n <= 0) throw NotSmaller; }});
      return true;
    } catch(e) {
      if (e == NotSmaller) return false;
      throw e;
    }
  }

  function maybeTagAsInstantiated(node, fn) {
    var score = fn.instantiateScore;
    if (!cx.disabledComputing && score && fn.args.length && nodeSmallerThan(node, score * 5)) {
      maybeInstantiate(functionScope(fn.originNode.scope.prev), score / 2);
      setFunctionInstantiated(node, fn);
      return true;
    } else {
      fn.instantiateScore = null;
    }
  }

  function setFunctionInstantiated(node, fn) {
    // Disconnect the arg avals, so that we can add info to them without side effects
    for (var i = 0; i < fn.args.length; ++i) fn.args[i] = new AVal;
    fn.self = new AVal;
    fn.computeRet = function(self, args) {
      // Prevent recursion
      return withDisabledComputing(fn, function() {
        var oldOrigin = cx.curOrigin;
        cx.curOrigin = fn.origin;
        var scope = node.scope
        var scopeCopy = new Scope(scope.prev, scope.originNode);
        for (var v in scope.props) {
          var local = scopeCopy.defProp(v, scope.props[v].originNode);
          for (var i = 0; i < args.length; ++i) if (fn.argNames[i] == v && i < args.length)
            args[i].propagate(local);
        }
        var argNames = fn.argNames.length != args.length ? fn.argNames.slice(0, args.length) : fn.argNames;
        while (argNames.length < args.length) argNames.push("?");
        scopeCopy.fnType = new Fn(fn.name, self, args, argNames, ANull, fn.generator);
        scopeCopy.fnType.originNode = fn.originNode;
        if (fn.arguments) {
          var argset = scopeCopy.fnType.arguments = new AVal;
          scopeCopy.defProp("arguments").addType(new Arr(argset));
          for (var i = 0; i < args.length; ++i) args[i].propagate(argset);
        }
        node.scope = scopeCopy;
        walk.recursive(node.body, scopeCopy, null, scopeGatherer);
        walk.recursive(node.body, scopeCopy, null, inferWrapper);
        cx.curOrigin = oldOrigin;
        return scopeCopy.fnType.retval;
      });
    };
  }

  function maybeTagAsGeneric(fn) {
    var target = fn.retval;
    if (target == ANull || fn.isArrowFn()) return;
    var targetInner, asArray;
    if (!target.isEmpty() && (targetInner = target.getType()) instanceof Arr)
      target = asArray = targetInner.getProp("<i>");

    function explore(aval, path, depth) {
      if (depth > 3 || !aval.forward) return;
      for (var i = 0; i < aval.forward.length; ++i) {
        var prop = aval.forward[i].propagatesTo();
        if (!prop) continue;
        var newPath = path, dest;
        if (prop instanceof AVal) {
          dest = prop;
        } else if (prop.target instanceof AVal) {
          newPath += prop.pathExt;
          dest = prop.target;
        } else continue;
        if (dest == target) return newPath;
        var found = explore(dest, newPath, depth + 1);
        if (found) return found;
      }
    }

    var foundPath = explore(fn.self, "!this", 0);
    for (var i = 0; !foundPath && i < fn.args.length; ++i)
      foundPath = explore(fn.args[i], "!" + i, 0);

    if (foundPath) {
      if (asArray) foundPath = "[" + foundPath + "]";
      var p = new def.TypeParser(foundPath);
      var parsed = p.parseType(true);
      fn.computeRet = parsed.apply ? parsed : function() { return parsed; };
      fn.computeRetSource = foundPath;
      return true;
    }
  }

  // SCOPE GATHERING PASS

  function addVar(scope, nameNode) {
    return scope.defProp(nameNode.name, nameNode);
  }
  function patternName(node) {
    if (node.type == "Identifier") return node.name
    if (node.type == "AssignmentPattern") return patternName(node.left)
    if (node.type == "ObjectPattern") return "{" + node.properties.map(function(e) { return patternName(e.value) }).join(", ") + "}"
    if (node.type == "ArrayPattern") return "[" + node.elements.map(patternName).join(", ") + "]"
    if (node.type == "RestElement") return "..." + patternName(node.argument)
    return "_"
  }

  function isBlockScopedDecl(node) {
    return node.type == "VariableDeclaration" && node.kind != "var" ||
      node.type == "FunctionDeclaration" ||
      node.type == "ClassDeclaration";
  }

  function patternScopes(inner, outer) {
    return {inner: inner, outer: outer || inner}
  }

  var scopeGatherer = exports.scopeGatherer = walk.make({
    VariablePattern: function(node, scopes) {
      if (scopes.inner) addVar(scopes.inner, node)
    },
    AssignmentPattern: function(node, scopes, c) {
      c(node.left, scopes, "Pattern")
      c(node.right, scopes.outer, "Expression")
    },
    AssignmentExpression: function(node, scope, c) {
      if (node.left.type == "MemberExpression")
        c(node.left, scope, "Expression")
      else
        c(node.left, patternScopes(false, scope), "Pattern")
      c(node.right, scope, "Expression")
    },
    Function: function(node, scope, c) {
      if (scope.inner) throw new Error("problem at " + node.start + " " + node.type)
      var inner = node.scope = new Scope(scope, node)
      var argVals = [], argNames = []
      for (var i = 0; i < node.params.length; ++i) {
        var param = node.params[i]
        argNames.push(patternName(param))
        if (param.type == "Identifier") {
          argVals.push(addVar(inner, param))
        } else {
          var arg = new AVal
          argVals.push(arg)
          arg.originNode = param
          c(param, patternScopes(inner), "Pattern")
        }
      }
      inner.fnType = new Fn(node.id && node.id.name, new AVal, argVals, argNames, ANull, node.generator)
      inner.fnType.originNode = node;
      if (node.id) {
        var decl = node.type == "FunctionDeclaration";
        addVar(decl ? scope : inner, node.id);
      }
      c(node.body, inner, node.expression ? "Expression" : "Statement");
    },
    BlockStatement: function(node, scope, c) {
      if (!node.scope && node.body.some(isBlockScopedDecl))
        scope = node.scope = new Scope(scope, node, true)
      walk.base.BlockStatement(node, scope, c)
    },
    TryStatement: function(node, scope, c) {
      c(node.block, scope, "Statement");
      if (node.handler) {
        if (node.handler.param.type == "Identifier") {
          var v = addVar(scope, node.handler.param);
          c(node.handler.body, scope, "Statement");
          var e5 = cx.definitions.ecma5;
          if (e5 && v.isEmpty()) getInstance(e5["Error.prototype"]).propagate(v, WG_CATCH_ERROR);
        } else {
          c(node.handler.param, patternScopes(scope), "Pattern")
        }
      }
      if (node.finalizer) c(node.finalizer, scope, "Statement");
    },
    VariableDeclaration: function(node, scope, c) {
      var targetScope = node.kind == "var" ? functionScope(scope) : scope
      for (var i = 0; i < node.declarations.length; ++i) {
        var decl = node.declarations[i];
        c(decl.id, patternScopes(targetScope, scope), "Pattern")
        if (decl.init) c(decl.init, scope, "Expression");
      }
    },
    ClassDeclaration: function(node, scope, c) {
      addVar(scope, node.id)
      if (node.superClass) c(node.superClass, scope, "Expression")
      for (var i = 0; i < node.body.body.length; i++)
        c(node.body.body[i], scope)
    },
    ForInStatement: function(node, scope, c) {
      if (!node.scope && isBlockScopedDecl(node.left))
        scope = node.scope = new Scope(scope, node, true)
      walk.base.ForInStatement(node, scope, c)
    },
    ForStatement: function(node, scope, c) {
      if (!node.scope && node.init && isBlockScopedDecl(node.init))
        scope = node.scope = new Scope(scope, node, true)
      walk.base.ForStatement(node, scope, c)
    },
    ImportDeclaration: function(node, scope) {
      for (var i = 0; i < node.specifiers.length; i++)
        addVar(scope, node.specifiers[i].local)
    }
  });
  scopeGatherer.ForOfStatement = scopeGatherer.ForInStatement

  // CONSTRAINT GATHERING PASS

  var propName = exports.propName = function(node, inferInScope) {
    var key = node.property || node.key;
    if (!node.computed && key.type == "Identifier") return key.name;
    if (key.type == "Literal") {
      if (typeof key.value == "string") return key.value
      if (typeof key.value == "number") return String(key.value)
    }
    if (inferInScope) {
      var symName = symbolName(infer(key, inferInScope))
      if (symName) return node.propName = symName
    } else if (node.propName) {
      return node.propName
    }
    return "<i>";
  }
  function symbolName(val) {
    var sym = val.getSymbolType()
    if (sym) return sym.asPropName()
  }

  function unopResultType(op) {
    switch (op) {
    case "+": case "-": case "~": return cx.num;
    case "!": return cx.bool;
    case "typeof": return cx.str;
    case "void": case "delete": return ANull;
    }
  }
  function binopIsBoolean(op) {
    switch (op) {
    case "==": case "!=": case "===": case "!==": case "<": case ">": case ">=": case "<=":
    case "in": case "instanceof": return true;
    }
  }
  function literalType(node) {
    if (node.regex) return getInstance(cx.protos.RegExp);
    switch (typeof node.value) {
    case "boolean": return cx.bool;
    case "number": return cx.num;
    case "string": return cx.str;
    case "object":
    case "function":
      if (!node.value) return ANull;
      return getInstance(cx.protos.RegExp);
    }
  }

  function join(a, b) {
    if (a == b || b == ANull) return a
    if (a == ANull) return b
    var joined = new AVal
    a.propagate(joined)
    b.propagate(joined)
    return joined
  }

  function connectParams(node, scope) {
    for (var i = 0; i < node.params.length; i++) {
      var param = node.params[i]
      if (param.type == "Identifier") continue
      connectPattern(param, scope, node.scope.fnType.args[i])
    }
  }

  function ensureVar(node, scope) {
    return scope.hasProp(node.name) || cx.topScope.defProp(node.name, node)
  }

  var inferPatternVisitor = exports.inferPatternVisitor = {
    Identifier: function(node, scope, source) {
      source.propagate(ensureVar(node, scope))
    },
    MemberExpression: function(node, scope, source) {
      var obj = infer(node.object, scope)
      var pName = propName(node, scope)
      obj.propagate(new DefProp(pName, source, node.property))
    },
    RestElement: function(node, scope, source) {
      connectPattern(node.argument, scope, new Arr(source))
    },
    ObjectPattern: function(node, scope, source) {
      for (var i = 0; i < node.properties.length; ++i) {
        var prop = node.properties[i]
        connectPattern(prop.value, scope, source.getProp(prop.key.name))
      }
    },
    ArrayPattern: function(node, scope, source) {
      for (var i = 0; i < node.elements.length; i++)
        if (node.elements[i])
          connectPattern(node.elements[i], scope, source.getProp(String(i)))
    },
    AssignmentPattern: function(node, scope, source) {
      connectPattern(node.left, scope, join(source, infer(node.right, scope)))
    }
  }

  function connectPattern(node, scope, source) {
    var connecter = inferPatternVisitor[node.type]
    if (connecter) connecter(node, scope, source)
  }

  function getThis(scope) {
    var fnScope = functionScope(scope)
    return fnScope.fnType ? fnScope.fnType.self : fnScope
  }

  function maybeAddPhantomObj(obj) {
    if (!obj.isEmpty() || !obj.propertyOf) return
    obj.propertyOf.getProp(obj.propertyName).addType(new Obj, WG_PHANTOM_OBJ)
    maybeAddPhantomObj(obj.propertyOf)
  }

  function inferClass(node, scope, name) {
    if (!name && node.id) name = node.id.name

    var sup = cx.protos.Object, supCtor, delayed
    if (node.superClass) {
      if (node.superClass.type == "Literal" && node.superClass.value == null) {
        sup = null
      } else {
        var supVal = infer(node.superClass, scope), supProto
        supCtor = supVal.getFunctionType()
        if (supCtor && (supProto = supCtor.getProp("prototype").getObjType())) {
          sup = supProto
        } else {
          supCtor = supVal
          delayed = supVal.getProp("prototype")
        }
      }
    }
    var proto = new Obj(sup, name && name + ".prototype")
    if (delayed) delayed.propagate(new HasProto(proto))

    return withSuper(supCtor, delayed || sup, function() {
      var ctor, body = node.body.body
      for (var i = 0; i < body.length; i++)
        if (body[i].kind == "constructor") ctor = body[i].value
      var fn = node.objType = ctor ? infer(ctor, scope) : new Fn(name, ANull, [], null, ANull)
      fn.originNode = node.id || ctor || node

      var inst = getInstance(proto, fn)
      fn.self.addType(inst)
      fn.defProp("prototype", node).addType(proto)
      for (var i = 0; i < body.length; i++) {
        var method = body[i], target
        if (method.kind == "constructor") continue
        var pName = propName(method, scope)
        if (pName == "<i>" || method.kind == "set") {
          target = ANull
        } else {
          target = (method.static ? fn : proto).defProp(pName, method.key)
          target.initializer = true
          if (method.kind == "get") target = new IsCallee(inst, [], null, target)
        }
        infer(method.value, scope, target)
        var methodFn = target.getFunctionType()
        if (methodFn) methodFn.self.addType(inst)
      }
      return fn
    })
  }

  function arrayLiteralType(elements, scope, inner) {
    var tuple = elements.length > 1 && elements.length < 6
    if (tuple) {
      var homogenous = true, litType
      for (var i = 0; i < elements.length; i++) {
        var elt = elements[i]
        if (!elt)
          tuple = false
        else if (elt.type != "Literal" || (litType && litType != typeof elt.value))
          homogenous = false
        else
          litType = typeof elt.value
      }
      if (homogenous) tuple = false
    }

    if (tuple) {
      var types = []
      for (var i = 0; i < elements.length; ++i)
        types.push(inner(elements[i], scope))
      return new Arr(types)
    } else if (elements.length < 2) {
      return new Arr(elements[0] && inner(elements[0], scope))
    } else {
      var eltVal = new AVal
      for (var i = 0; i < elements.length; i++)
        if (elements[i]) inner(elements[i], scope).propagate(eltVal)
      return new Arr(eltVal)
    }
  }

  function ret(f) {
    return function(node, scope, out, name) {
      var r = f(node, scope, name);
      if (out) r.propagate(out);
      return r;
    };
  }
  function fill(f) {
    return function(node, scope, out, name) {
      if (!out) out = new AVal;
      f(node, scope, out, name);
      return out;
    };
  }

  var inferExprVisitor = exports.inferExprVisitor = {
    ArrayExpression: ret(function(node, scope) {
      return arrayLiteralType(node.elements, scope, infer)
    }),
    ObjectExpression: ret(function(node, scope, name) {
      // ORION recovery
      if (name === "✖") {
      	return ANull;
      }
      var proto = true, waitForProto
      for (var i = 0; i < node.properties.length; ++i) {
        var prop = node.properties[i]
        if (prop.key.name == "__proto__") {
          if (prop.value.type == "Literal" && prop.value.value == null) {
            proto = null
          } else {
            var protoVal = infer(prop.value, scope), known = protoVal.getObjType()
            if (known) proto = known
            else waitForProto = protoVal
          }
        }
      }

      var obj = node.objType = new Obj(proto, name);
      if (waitForProto) waitForProto.propagate(new HasProto(obj))
      obj.originNode = node;

      withSuper(null, waitForProto || proto, function() {
        for (var i = 0; i < node.properties.length; ++i) {
          var prop = node.properties[i], key = prop.key;
          if (ignoredProp(prop.key.name)) continue;

          var name = propName(prop, scope), target
          if (name == "<i>" || prop.kind == "set") {
            target = ANull;
          } else {
            var val = target = obj.defProp(name, key);
            val.initializer = true;
            if (prop.kind == "get")
              target = new IsCallee(obj, [], null, val);
          }
          infer(prop.value, scope, target, name);
          if (prop.value.type == "FunctionExpression")
            prop.value.scope.fnType.self.addType(obj, WG_SPECULATIVE_THIS);
        }
      })
      return obj;
    }),
    FunctionExpression: ret(function(node, scope, name) {
      var inner = node.scope, fn = inner.fnType;
      if (name && !fn.name) fn.name = name;
      connectParams(node, inner)
      if (node.expression)
        infer(node.body, inner, inner.fnType.retval = new AVal)
      else
        walk.recursive(node.body, inner, null, inferWrapper, "Statement")
      if (node.type == "ArrowFunctionExpression")
        getThis(scope).propagate(fn.self)
      maybeTagAsInstantiated(node, fn) || maybeTagAsGeneric(fn);
      if (node.id) inner.getProp(node.id.name).addType(fn);
      return fn;
    }),
    ClassExpression: ret(inferClass),
    SequenceExpression: ret(function(node, scope) {
      for (var i = 0, l = node.expressions.length - 1; i < l; ++i)
        infer(node.expressions[i], scope, ANull);
      return infer(node.expressions[l], scope);
    }),
    UnaryExpression: ret(function(node, scope) {
      infer(node.argument, scope, ANull);
      return unopResultType(node.operator);
    }),
    UpdateExpression: ret(function(node, scope) {
      infer(node.argument, scope, ANull);
      return cx.num;
    }),
    BinaryExpression: ret(function(node, scope) {
      if (node.operator == "+") {
        var lhs = infer(node.left, scope);
        var rhs = infer(node.right, scope);
        if (lhs.hasType(cx.str) || rhs.hasType(cx.str)) return cx.str;
        if (lhs.hasType(cx.num) && rhs.hasType(cx.num)) return cx.num;
        var result = new AVal;
        lhs.propagate(new IsAdded(rhs, result));
        rhs.propagate(new IsAdded(lhs, result));
        return result;
      } else {
        infer(node.left, scope, ANull);
        infer(node.right, scope, ANull);
        return binopIsBoolean(node.operator) ? cx.bool : cx.num;
      }
    }),
    AssignmentExpression: ret(function(node, scope, name) {
      var rhs, pName;
      if (node.left.type == "MemberExpression") {
        pName = propName(node.left, scope)
        if (!name)
          name = node.left.object.type == "Identifier" ? node.left.object.name + "." + pName : pName
      } else if (!name && node.left.type == "Identifier") {
        name = node.left.name
      }

      if (node.operator && node.operator != "=" && node.operator != "+=") {
        infer(node.right, scope, ANull);
        rhs = cx.num;
      } else {
        rhs = infer(node.right, scope, null, name);
      }

      if (node.left.type == "MemberExpression") {
        var obj = infer(node.left.object, scope);
        if (pName == "prototype") maybeInstantiate(scope, 20);
        if (pName == "<i>") {
          // This is a hack to recognize for/in loops that copy
          // properties, and do the copying ourselves, insofar as we
          // manage, because such loops tend to be relevant for type
          // information.
          var v = node.left.property.name, local = scope.props[v], over = local && local.iteratesOver;
          if (over) {
            maybeInstantiate(scope, 20);
            var fromRight = node.right.type == "MemberExpression" && node.right.computed && node.right.property.name == v;
            over.forAllProps(function(prop, val, local) {
              if (local && prop != "prototype" && prop != "<i>")
                obj.propagate(new DefProp(prop, fromRight ? val : ANull));
            });
            return rhs;
          }
        }

        obj.propagate(new DefProp(pName, rhs, node.left.property));
        maybeAddPhantomObj(obj)
        if (node.right.type == "FunctionExpression")
          obj.propagate(node.right.scope.fnType.self, WG_SPECULATIVE_THIS);
      } else {
        connectPattern(node.left, scope, rhs)
      }
      return rhs;
    }),
    LogicalExpression: fill(function(node, scope, out) {
      infer(node.left, scope, out);
      infer(node.right, scope, out);
    }),
    ConditionalExpression: fill(function(node, scope, out) {
      infer(node.test, scope, ANull);
      infer(node.consequent, scope, out);
      infer(node.alternate, scope, out);
    }),
    NewExpression: fill(function(node, scope, out, name) {
      if (node.callee.type == "Identifier" && node.callee.name in scope.props)
        maybeInstantiate(scope, 20);

      for (var i = 0, args = []; i < node.arguments.length; ++i)
        args.push(infer(node.arguments[i], scope));
      var callee = infer(node.callee, scope);
      var self = new AVal;
      callee.propagate(new IsCtor(self, name && /\.prototype$/.test(name)));
      self.propagate(out, WG_NEW_INSTANCE);
      callee.propagate(new IsCallee(self, args, node.arguments, new IfObj(out)));
    }),
    CallExpression: fill(function(node, scope, out) {
      for (var i = 0, args = []; i < node.arguments.length; ++i)
        args.push(infer(node.arguments[i], scope));
      var outerFn = functionScope(scope).fnType
      if (node.callee.type == "MemberExpression") {
        var self = infer(node.callee.object, scope);
        var pName = propName(node.callee, scope)
        if (outerFn && (pName == "call" || pName == "apply") &&
            outerFn.args.indexOf(self) > -1)
          maybeInstantiate(scope, 30);
        self.propagate(new HasMethodCall(pName, args, node.arguments, out));
      } else if (node.callee.type == "Super" && cx.curSuperCtor) {
        cx.curSuperCtor.propagate(new IsCallee(getThis(scope), args, node.arguments, out))
      } else {
        var callee = infer(node.callee, scope);
        if (outerFn && outerFn.args.indexOf(callee) > -1)
          maybeInstantiate(scope, 30);
        var knownFn = callee.getFunctionType();
        if (knownFn && knownFn.instantiateScore && outerFn)
          maybeInstantiate(scope, knownFn.instantiateScore / 5);
        callee.propagate(new IsCallee(cx.topScope, args, node.arguments, out));
      }
    }),
    MemberExpression: fill(function(node, scope, out) {
      var name = propName(node), wg;
      if (name == "<i>") {
        var propType = infer(node.property, scope)
        var symName = symbolName(propType)
        if (symName)
          name = node.propName = symName
        else if (!propType.hasType(cx.num))
          wg = WG_MULTI_MEMBER
      }
      infer(node.object, scope).getProp(name).propagate(out, wg)
    }),
    Identifier: ret(function(node, scope) {
      if (node.name == "arguments") {
        var fnScope = functionScope(scope)
        if (fnScope.fnType && !(node.name in fnScope.props))
          scope.defProp(node.name, fnScope.fnType.originNode)
            .addType(new Arr(fnScope.fnType.arguments = new AVal));
      }
      return scope.getProp(node.name);
    }),
    ThisExpression: ret(function(_node, scope) {
      return getThis(scope)
    }),
    Super: ret(function(node) {
      return node.superType = cx.curSuper || ANull
    }),
    Literal: ret(function(node) {
      return literalType(node);
    }),
    TemplateLiteral: ret(function(node, scope) {
      for (var i = 0; i < node.expressions.length; ++i)
        infer(node.expressions[i], scope, ANull)
      return cx.str
    }),
    TaggedTemplateExpression: fill(function(node, scope, out) {
      var args = [new Arr(cx.str)]
      for (var i = 0; i < node.quasi.expressions.length; ++i)
        args.push(infer(node.quasi.expressions[i], scope))
      infer(node.tag, scope, new IsCallee(cx.topScope, args, node.quasi.expressions, out))
    }),
    YieldExpression: ret(function(node, scope) {
      var output = ANull, fn = functionScope(scope).fnType
      if (fn) {
        if (fn.retval == ANull) fn.retval = new AVal
        if (!fn.yieldval) fn.yieldval = new AVal
        output = fn.retval
      }
      if (node.argument) {
        if (node.delegate) {
          infer(node.argument, scope, new HasMethodCall("next", [], null,
                                                        new GetProp("value", output)))
        } else {
          infer(node.argument, scope, output)
        }
      }
      return fn ? fn.yieldval : ANull
    })
  };
  inferExprVisitor.ArrowFunctionExpression = inferExprVisitor.FunctionExpression

  function infer(node, scope, out, name) {
    var handler = inferExprVisitor[node.type];
    return handler ? handler(node, scope, out, name) : ANull;
  }

  function loopPattern(init) {
    return init.type == "VariableDeclaration" ? init.declarations[0].id : init
  }

  var inferWrapper = exports.inferWrapper = walk.make({
    Expression: function(node, scope) {
      infer(node, node.scope || scope, ANull);
    },

    FunctionDeclaration: function(node, scope, c) {
      var inner = node.scope, fn = inner.fnType;
      connectParams(node, inner)
      c(node.body, inner, "Statement");
      maybeTagAsInstantiated(node, fn) || maybeTagAsGeneric(fn);
      scope.getProp(node.id.name).addType(fn)
    },

    Statement: function(node, scope, c) {
      c(node, node.scope || scope)
    },

    VariableDeclaration: function(node, scope) {
      for (var i = 0; i < node.declarations.length; ++i) {
        var decl = node.declarations[i];
        if (decl.id.type == "Identifier") {
          var prop = scope.getProp(decl.id.name);
          if (decl.init)
            infer(decl.init, scope, prop, decl.id.name);
        } else if (decl.init) {
          connectPattern(decl.id, scope, infer(decl.init, scope))
        }
      }
    },

    ClassDeclaration: function(node, scope) {
      scope.getProp(node.id.name).addType(inferClass(node, scope, node.id.name))
    },

    ReturnStatement: function(node, scope) {
      if (!node.argument) return;
      var output = ANull, fn = functionScope(scope).fnType
      if (fn) {
        if (fn.retval == ANull) fn.retval = new AVal;
        output = fn.retval;
      }
      infer(node.argument, scope, output);
    },

    ForInStatement: function(node, scope, c) {
      var source = infer(node.right, scope);
      if ((node.right.type == "Identifier" && node.right.name in scope.props) ||
          (node.right.type == "MemberExpression" && node.right.property.name == "prototype")) {
        maybeInstantiate(scope, 5);
        var pattern = loopPattern(node.left)
        if (pattern.type == "Identifier") {
          if (pattern.name in scope.props)
            scope.getProp(pattern.name).iteratesOver = source
          source.getProp("<i>").propagate(ensureVar(pattern, scope))
        } else {
          connectPattern(pattern, scope, source.getProp("<i>"))
        }
      }
      c(node.body, scope, "Statement");
    },

    ForOfStatement: function(node, scope, c) {
      var pattern = loopPattern(node.left), target
      if (pattern.type == "Identifier")
        target = ensureVar(pattern, scope)
      else
        connectPattern(pattern, scope, target = new AVal)
      infer(node.right, scope, new HasMethodCall(":Symbol.iterator", [], null,
                                                 new HasMethodCall("next", [], null,
                                                                   new GetProp("value", target))))
      c(node.body, scope, "Statement")
    }
  });

  // PARSING

  var parse = exports.parse = function(text, options, thirdArg) {
    if (!options || Array.isArray(options)) options = thirdArg
    var ast;
    try { ast = acorn.parse(text, options); }
    catch(e) { ast = acorn_loose.parse_dammit(text, options); }
    return ast;
  };

  // ANALYSIS INTERFACE

  exports.analyze = function(ast, name, scope) {
    if (typeof ast == "string") ast = parse(ast);

    if (!name) name = "file#" + cx.origins.length;
    exports.addOrigin(cx.curOrigin = name);

    if (!scope) scope = cx.topScope;
    cx.startAnalysis();

    walk.recursive(ast, scope, null, scopeGatherer);
    if (cx.parent) cx.parent.signal("preInfer", ast, scope)
    walk.recursive(ast, scope, null, inferWrapper);
    if (cx.parent) cx.parent.signal("postInfer", ast, scope)

    cx.curOrigin = null;
  };

  // PURGING

  exports.purge = function(origins, start, end) {
    var test = makePredicate(origins, start, end);
    ++cx.purgeGen;
    cx.topScope.purge(test);
    for (var prop in cx.props) {
      var list = cx.props[prop];
      for (var i = 0; i < list.length; ++i) {
        var obj = list[i], av = obj.props[prop];
        if (!av || test(av, av.originNode)) list.splice(i--, 1);
      }
      if (!list.length) delete cx.props[prop];
    }
  };

  function makePredicate(origins, start, end) {
    var arr = Array.isArray(origins);
    if (arr && origins.length == 1) { origins = origins[0]; arr = false; }
    if (arr) {
      if (end == null) return function(n) { return origins.indexOf(n.origin) > -1; };
      return function(n, pos) { return pos && pos.start >= start && pos.end <= end && origins.indexOf(n.origin) > -1; };
    } else {
      if (end == null) return function(n) { return n.origin == origins; };
      return function(n, pos) { return pos && pos.start >= start && pos.end <= end && n.origin == origins; };
    }
  }

  AVal.prototype.purge = function(test) {
    if (this.purgeGen == cx.purgeGen) return;
    this.purgeGen = cx.purgeGen;
    for (var i = 0; i < this.types.length; ++i) {
      var type = this.types[i];
      if (test(type, type.originNode))
        this.types.splice(i--, 1);
      else
        type.purge(test);
    }
    if (!this.types.length) this.maxWeight = 0;

    if (this.forward) for (var i = 0; i < this.forward.length; ++i) {
      var f = this.forward[i];
      if (test(f)) {
        this.forward.splice(i--, 1);
        if (this.props) this.props = null;
      } else if (f.purge) {
        f.purge(test);
      }
    }
  };
  ANull.purge = function() {};
  Obj.prototype.purge = function(test) {
    if (this.purgeGen == cx.purgeGen) return true;
    this.purgeGen = cx.purgeGen;
    for (var p in this.props) {
      var av = this.props[p];
      if (test(av, av.originNode))
        this.removeProp(p);
      av.purge(test);
    }
  };
  Fn.prototype.purge = function(test) {
    if (Obj.prototype.purge.call(this, test)) return;
    this.self.purge(test);
    this.retval.purge(test);
    for (var i = 0; i < this.args.length; ++i) this.args[i].purge(test);
  };

  // EXPRESSION TYPE DETERMINATION

  function findByPropertyName(name) {
    guessing = true;
    var found = objsWithProp(name);
    if (found) for (var i = 0; i < found.length; ++i) {
      var val = found[i].getProp(name);
      if (!val.isEmpty()) return val;
    }
    return ANull;
  }

  function generatorResult(input, output) {
    var retObj = new Obj(true)
    retObj.defProp("done").addType(cx.bool)
    output.propagate(retObj.defProp("value"))
    var method = new Fn(null, ANull, input ? [input] : [], input ? ["?"] : [], retObj)
    var result = new Obj(cx.definitions.ecma6 && cx.definitions.ecma6.generator_prototype || true)
    result.defProp("next").addType(method)
    return result
  }

  function maybeIterator(fn, output) {
    if (!fn.generator) return output
    if (!fn.computeRet) { // Reuse iterator objects for non-computed return types
      if (fn.generator === true) fn.generator = generatorResult(fn.yieldval, output)
      return fn.generator
    }
    return generatorResult(fn.yieldval, output)
  }

  function computeReturnType(funcNode, argNodes, scope) {
    var fn = findType(funcNode, scope).getFunctionType()
    if (!fn) return ANull
    var result = fn.retval
    if (fn.computeRet) {
      for (var i = 0, args = []; i < argNodes.length; ++i)
        args.push(findType(argNodes[i], scope))
      var self = ANull
      if (funcNode.type == "MemberExpression")
        self = findType(funcNode.object, scope)
      result = fn.computeRet(self, args, argNodes);
    }
    return maybeIterator(fn, result)
  }

  var typeFinder = exports.typeFinder = {
    ArrayExpression: function(node, scope) {
      return arrayLiteralType(node.elements, scope, findType)
    },
    ObjectExpression: function(node) {
      return node.objType;
    },
    ClassExpression: function(node) {
      return node.objType;
    },
    FunctionExpression: function(node) {
      return node.scope.fnType;
    },
    ArrowFunctionExpression: function(node) {
      return node.scope.fnType;
    },
    SequenceExpression: function(node, scope) {
      return findType(node.expressions[node.expressions.length-1], scope);
    },
    UnaryExpression: function(node) {
      return unopResultType(node.operator);
    },
    UpdateExpression: function() {
      return cx.num;
    },
    BinaryExpression: function(node, scope) {
      if (binopIsBoolean(node.operator)) return cx.bool;
      if (node.operator == "+") {
        var lhs = findType(node.left, scope);
        var rhs = findType(node.right, scope);
        if (lhs.hasType(cx.str) || rhs.hasType(cx.str)) return cx.str;
      }
      return cx.num;
    },
    AssignmentExpression: function(node, scope) {
      return findType(node.right, scope);
    },
    LogicalExpression: function(node, scope) {
      var lhs = findType(node.left, scope);
      return lhs.isEmpty() ? findType(node.right, scope) : lhs;
    },
    ConditionalExpression: function(node, scope) {
      var lhs = findType(node.consequent, scope);
      return lhs.isEmpty() ? findType(node.alternate, scope) : lhs;
    },
    NewExpression: function(node, scope) {
      var f = findType(node.callee, scope).getFunctionType();
      var proto = f && f.getProp("prototype").getObjType();
      if (!proto) return ANull;
      return getInstance(proto, f);
    },
    CallExpression: function(node, scope) {
      return computeReturnType(node.callee, node.arguments, scope)
    },
    MemberExpression: function(node, scope) {
      var propN = propName(node), obj = findType(node.object, scope).getType();
      
      // ORION Collect potential matches
      if (obj) {
			var currentMatch = obj.getProp(propN);
			if (guessing && Array.isArray(obj.potentialMatches)) {
				var potentialMatches = obj.potentialMatches;
				var matchesProp = [];
				for(var i = 0, len = potentialMatches.length; i < len; i++) {
					var match = potentialMatches[i];
					var propMatch = match.getProp(propN);
					if (typeof propMatch !== "undefined") {
						if (typeof propMatch.originNode !== "undefined"
								&& typeof propMatch.origin !== "undefined") {
							if (propMatch.originNode.sourceFile) {
								if (propMatch.originNode.sourceFile.name === propMatch.origin) {
									matchesProp.push(propMatch);
								}
							}
						}
					}
				}
				if (matchesProp.length > 0) {
					currentMatch.potentialMatches = matchesProp;
				}
			}
			return currentMatch;
		}
      
      // Before Orion: if (obj) return obj.getProp(propN);
      
      if (propN == "<i>") return ANull;
      return findByPropertyName(propN);
    },
    MethodDefinition: function(node) {
      var propN = node.key.name, obj = getThis(node.value.scope).getType();
      if (obj) return obj.getProp(propN);
      return ANull;
    },
    Identifier: function(node, scope) {
      return scope.hasProp(node.name) || ANull;
    },
    ThisExpression: function(_node, scope) {
      return getThis(scope)
    },
    Literal: function(node) {
      return literalType(node);
    },
    Super: ret(function(node) {
      return node.superType
    }),
    TemplateLiteral: function() {
      return cx.str
    },
    TaggedTemplateExpression: function(node, scope) {
      return computeReturnType(node.tag, node.quasi.expressions, scope)
    },
    YieldExpression: function(_node, scope) {
      var fn = functionScope(scope).fnType
      return fn ? fn.yieldval : ANull
    }
  };

  function findType(node, scope) {
    var finder = typeFinder[node.type];
    return finder ? finder(node, scope) : ANull;
  }

  var searchVisitor = exports.searchVisitor = walk.make({
    Function: function(node, _st, c) {
      walk.base.Function(node, node.scope, c)
    },
    Property: function(node, st, c) {
      if (node.computed) c(node.key, st, "Expression");
      if (node.key != node.value) c(node.value, st, "Expression");
    },
    Statement: function(node, st, c) {
      c(node, node.scope || st)
    },
    ImportSpecifier: function(node, st, c) {
      c(node.local, st)
    },
    ImportDefaultSpecifier: function(node, st, c) {
      c(node.local, st)
    },
    ImportNamespaceSpecifier: function(node, st, c) {
      c(node.local, st)
    }
  });
  exports.fullVisitor = walk.make({
    MemberExpression: function(node, st, c) {
      c(node.object, st, "Expression");
      c(node.property, st, node.computed ? "Expression" : null);
    },
    ObjectExpression: function(node, st, c) {
      for (var i = 0; i < node.properties.length; ++i) {
        c(node.properties[i].value, st, "Expression");
        c(node.properties[i].key, st);
      }
    }
  }, searchVisitor);

  exports.findExpressionAt = function(ast, start, end, defaultScope, filter) {
    var test = filter || function(_t, node) {
      if (node.type == "Identifier" && node.name == "✖") return false;
      return typeFinder.hasOwnProperty(node.type);
    };
    return walk.findNodeAt(ast, start, end, test, searchVisitor, defaultScope || cx.topScope);
  };

  exports.findExpressionAround = function(ast, start, end, defaultScope, filter) {
    var test = filter || function(_t, node) {
      if (start != null && node.start > start) return false;
      if (node.type == "Identifier" && node.name == "✖") return false;
      return typeFinder.hasOwnProperty(node.type);
    };
    return walk.findNodeAround(ast, end, test, searchVisitor, defaultScope || cx.topScope);
  };

  exports.expressionType = function(found) {
    return findType(found.node, found.state);
  };

  // Finding the expected type of something, from context

  exports.parentNode = function(child, ast) {
    var stack = [];
    function c(node, st, override) {
      if (node.start <= child.start && node.end >= child.end) {
        var top = stack[stack.length - 1];
        if (node == child) throw {found: top};
        if (top != node) stack.push(node);
        walk.base[override || node.type](node, st, c);
        if (top != node) stack.pop();
      }
    }
    try {
      c(ast, null);
    } catch (e) {
      if (e.found) return e.found;
      throw e;
    }
  };

  var findTypeFromContext = exports.findTypeFromContext = {
    ArrayExpression: function(parent, _, get) { return get(parent, true).getProp("<i>"); },
    ObjectExpression: function(parent, node, get) {
      for (var i = 0; i < parent.properties.length; ++i) {
        var prop = node.properties[i];
        if (prop.value == node)
          return get(parent, true).getProp(prop.key.name);
      }
    },
    UnaryExpression: function(parent) { return unopResultType(parent.operator); },
    UpdateExpression: function() { return cx.num; },
    BinaryExpression: function(parent) { return binopIsBoolean(parent.operator) ? cx.bool : cx.num; },
    AssignmentExpression: function(parent, _, get) { return get(parent.left); },
    LogicalExpression: function(parent, _, get) { return get(parent, true); },
    ConditionalExpression: function(parent, node, get) {
      if (parent.consequent == node || parent.alternate == node) return get(parent, true);
    },
    CallExpression: function(parent, node, get) {
      for (var i = 0; i < parent.arguments.length; i++) {
        var arg = parent.arguments[i];
        if (arg == node) {
          var calleeType = get(parent.callee).getFunctionType();
          if (calleeType instanceof Fn)
            return calleeType.args[i];
          break;
        }
      }
    },
    ReturnStatement: function(_parent, node, get) {
      var fnNode = walk.findNodeAround(node.sourceFile.ast, node.start, "Function");
      if (fnNode) {
        var fnType = fnNode.node.type != "FunctionDeclaration"
          ? get(fnNode.node, true).getFunctionType()
          : fnNode.node.scope.fnType;
        if (fnType) return fnType.retval.getType();
      }
    },
    VariableDeclarator: function(parent, node, get) {
      if (parent.init == node) return get(parent.id)
    }
  };
  findTypeFromContext.NewExpression = findTypeFromContext.CallExpression

  exports.typeFromContext = function(ast, found) {
    var parent = exports.parentNode(found.node, ast);
    var type = null;
    if (findTypeFromContext.hasOwnProperty(parent.type)) {
      var finder = findTypeFromContext[parent.type];
      type = finder && finder(parent, found.node, function(node, fromContext) {
        var obj = {node: node, state: found.state};
        var tp = fromContext ? exports.typeFromContext(ast, obj) : exports.expressionType(obj);
        return tp || ANull;
      });
    }
    return type || exports.expressionType(found);
  };

  // Flag used to indicate that some wild guessing was used to produce
  // a type or set of completions.
  var guessing = false;

  exports.resetGuessing = function(val) { guessing = val; };
  exports.didGuess = function() { return guessing; };

  exports.forAllPropertiesOf = function(type, f) {
    type.gatherProperties(f, 0);
  };

  var refFindWalker = walk.make({}, searchVisitor);

  exports.findRefs = function(ast, baseScope, name, refScope, f) {
    refFindWalker.Identifier = refFindWalker.VariablePattern = function(node, scope) {
      if (node.name != name ||
          (node == ast.id && ast.type == "FunctionDeclaration")) return;
      for (var s = scope; s; s = s.prev) {
        if (s == refScope) f(node, scope);
        if (name in s.props) return;
      }
    };
    walk.recursive(ast, baseScope, null, refFindWalker);
  };

  var simpleWalker = walk.make({
    Function: function(node, _scope, c) {
      c(node.body, node.scope, node.expression ? "Expression" : "Statement")
    },
    Statement: function(node, scope, c) {
      c(node, node.scope || scope)
    }
  });

  exports.findPropRefs = function(ast, scope, objType, propName, f) {
    // Find the type which owns the property in hierarchy
    while (objType && !objType.props[propName] && !(objType.maybeProps && objType.maybeProps[propName])) {
      objType = objType.proto;
    }
    if (!objType) throw new Error("Couldn't locate property in the base object type.");

    function isObjTypeProto(type) {
      // Check whether the found type has objType in its hierarchy
      while (type && type != objType) {
        // Ff property is overriden higher in the hierarchy, return false
        if (type.props[propName] || (type.maybeProps && type.maybeProps[propName])) {
          return false;
        }
        type = type.proto;
      }
      return type;
    }

    walk.simple(ast, {
      MemberExpression: function(node, scope) {
        if (node.computed || node.property.name != propName) return;
        if (isObjTypeProto(findType(node.object, scope).getType())) f(node.property, scope);
      },
      ObjectExpression: function(node, scope) {
        if (findType(node, scope).getType() != objType) return;
        for (var i = 0; i < node.properties.length; ++i)
          if (node.properties[i].key.name == propName) f(node.properties[i].key, scope);
      },
      MethodDefinition: function(node) {
        if (node.key.name != propName) return;
        if (node.value && isObjTypeProto(getThis(node.value.scope).getType())) f(node.key, node.value.scope);
      }
    }, simpleWalker, scope);
  };

  // LOCAL-VARIABLE QUERIES

  var scopeAt = exports.scopeAt = function(ast, pos, defaultScope) {
    var found = walk.findNodeAround(ast, pos, function(_, node) {
      return node.scope;
    });
    if (found) return found.node.scope;
    else return defaultScope || cx.topScope;
  };

  exports.forAllLocalsAt = function(ast, pos, defaultScope, f) {
    var scope = scopeAt(ast, pos, defaultScope);
    scope.gatherProperties(f, 0);
  };

  // INIT DEF MODULE

  // Delayed initialization because of cyclic dependencies.
  def = exports.def = def.init({}, exports);
});

// The Tern server object

// A server is a stateful object that manages the analysis for a
// project, and defines an interface for querying the code in the
// project.
/* eslint-disable */
(function(root, mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(exports, require("./infer"), require("./signal"),
               require("acorn"), require("acorn/dist/walk"));
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/lib/tern',["exports", "./infer", "./signal", "acorn/dist/acorn", "acorn/dist/acorn_loose", "acorn/dist/walk"], mod);
  mod(root.tern || (root.tern = {}), tern, tern.signal, acorn, acornloose, acorn.walk); // Plain browser env
})(this, function(exports, infer, signal, acorn, acornloose, walk) {
  "use strict";

  var plugins = Object.create(null);
  exports.registerPlugin = function(name, init) { plugins[name] = init; };

  var defaultOptions = exports.defaultOptions = {
    debug: false,
    async: false,
    getFile: function(_f, c) { if (this.async) c(null, null); },
    normalizeFilename: function(name) { return name },
    defs: [],
    plugins: {},
    fetchTimeout: 1000,
    dependencyBudget: 20000,
    reuseInstances: true,
    stripCRs: false,
    ecmaVersion: 6,
    projectDir: "/",
    parent: null
  };

  var queryTypes = {
    completions: {
      takesFile: true,
      run: findCompletions
    },
    properties: {
      run: findProperties
    },
    type: {
      takesFile: true,
      run: findTypeAt
    },
    documentation: {
      takesFile: true,
      run: findDocs
    },
    definition: {
      takesFile: true,
      run: findDef
    },
    refs: {
      takesFile: true,
      fullFile: true,
      run: findRefs
    },
    rename: {
      takesFile: true,
      fullFile: true,
      run: buildRename
    },
    files: {
      run: listFiles
    }
  };

  exports.defineQueryType = function(name, desc) { queryTypes[name] = desc; };

  function File(name, parent) {
    this.name = name;
    this.parent = parent;
    this.scope = this.text = this.ast = this.lineOffsets = null;
  }
  File.prototype.asLineChar = function(pos) { return asLineChar(this, pos); };

  function parseFile(srv, file) {
    var options = {
      directSourceFile: file,
      allowReturnOutsideFunction: true,
      allowImportExportEverywhere: true,
      ecmaVersion: srv.options.ecmaVersion,
      sourceType: srv.options.sourceType
    }
    // ORION: Checking the truthiness of the text is not accurate
    var text = srv.signalReturnFirst("preParse", file.text, options);
    if(typeof text !== 'string') {
    	text = file.text;
	}
    // ORION: Add parseOptions pass to allow orionAcorn to setup the parser and modify the options
    srv.signal("parseOptions", text, options, acorn, acornloose);
    var ast = infer.parse(text, options)
    srv.signal("postParse", ast, text)
    return ast
  }

  function updateText(file, text, srv) {
    file.text = srv.options.stripCRs ? text.replace(/\r\n/g, "\n") : text;
    infer.withContext(srv.cx, function() {
      file.ast = parseFile(srv, file)
    });
    file.lineOffsets = null;
  }

  var Server = exports.Server = function(options) {
    this.cx = null;
    this.options = options || {};
    for (var o in defaultOptions) if (!options.hasOwnProperty(o))
      options[o] = defaultOptions[o];

    this.projectDir = options.projectDir.replace(/\\/g, "/")
    if (!/\/$/.test(this.projectDir)) this.projectDir += "/"

    this.parent = options.parent;
    this.handlers = Object.create(null);
    this.files = [];
    this.fileMap = Object.create(null);
    this.needsPurge = [];
    this.budgets = Object.create(null);
    this.uses = 0;
    this.pending = 0;
    this.asyncError = null;
    this.mod = {}

    this.defs = options.defs.slice(0)
    this.plugins = Object.create(null);
    //ORION forward exceptions loading plugins
    try {
	    for (var plugin in options.plugins) if (options.plugins.hasOwnProperty(plugin))
	      this.loadPlugin(plugin, options.plugins[plugin])
	} catch(err) {
		throw ternError("Plugin failed to load: "+err.message);
	}
	//ORION forward exceptions during restart
	try {
	    this.reset();
	} catch(err) {
		throw ternError("Tern failed to reset: "+err.message);
	}

  };
  Server.prototype = signal.mixin({
    addFile: function(name, /*optional*/ text, parent) {
      // Don't crash when sloppy plugins pass non-existent parent ids
      if (parent && !(parent in this.fileMap)) parent = null;
      if (!(name in this.fileMap))
        name = this.normalizeFilename(name)
      ensureFile(this, name, parent, text);
    },
    delFile: function(name) {
      var file = this.findFile(name);
      if (file) {
        this.needsPurge.push(file.name);
        this.files.splice(this.files.indexOf(file), 1);
        delete this.fileMap[file.name];
      }
    },
    reset: function() {
      this.signal("reset");
      this.cx = new infer.Context(this.defs, this);
      this.uses = 0;
      this.budgets = Object.create(null);
      for (var i = 0; i < this.files.length; ++i) {
        var file = this.files[i];
        file.scope = null;
      }
      this.signal("postReset");
    },

    request: function(doc, c) {
      var inv = invalidDoc(doc);
      if (inv) return c(inv);

      var self = this;
      doRequest(this, doc, function(err, data) {
        c(err, data);
        if (self.uses > 4000) { //ORION - the default is too low
          self.reset();
          analyzeAll(self, null, function(){});
        }
      });
    },

    findFile: function(name) {
      return this.fileMap[this.normalizeFilename(name)];
    },

    flush: function(c) {
      var cx = this.cx;
      analyzeAll(this, null, function(err) {
        if (err) return c(err);
        infer.withContext(cx, c);
      });
    },

    startAsyncAction: function() {
      ++this.pending;
    },
    finishAsyncAction: function(err) {
      if (err) this.asyncError = err;
      if(this.pending > 0) {
      	this.pending--;
      }
      if (this.pending === 0) this.signal("everythingFetched");
      // Before Orion: if (--this.pending === 0) this.signal("everythingFetched");
    },

    addDefs: function(defs, toFront) {
      if (toFront) this.defs.unshift(defs)
      else this.defs.push(defs)

      if (this.cx) this.reset()
    },

    deleteDefs: function(name) {
      for (var i = 0; i < this.defs.length; i++) if (this.defs[i]["!name"] == name) {
        this.defs.splice(i, 1);
        if (this.cx) this.reset();
        return;
      }
    },

    loadPlugin: function(name, options) {
      if (arguments.length == 1) options = this.options.plugins[name] || true
      if (name in this.plugins || !(name in plugins) || !options) return
      this.plugins[name] = true
      var init = plugins[name](this, options)

      // This is for backwards-compatibilty. Don't rely on it -- use addDef and on directly
      if (!init) return
      if (init.defs) this.addDefs(init.defs, init.loadFirst)
      if (init.passes) for (var type in init.passes) if (init.passes.hasOwnProperty(type))
        this.on(type, init.passes[type])
    },

    normalizeFilename: function(name) {
      var norm = this.options.normalizeFilename(name).replace(/\\/g, "/")
      if (norm.indexOf(this.projectDir) == 0) norm = norm.slice(this.projectDir.length)
      return norm
    }
  });

  function doRequest(srv, doc, c) {
    if (doc.query && !queryTypes.hasOwnProperty(doc.query.type))
      return c("No query type '" + doc.query.type + "' defined");

    var query = doc.query;
    // Respond as soon as possible when this just uploads files
    if (!query) c(null, {});

    var files = doc.files || [];
    if (files.length) ++srv.uses;
    for (var i = 0; i < files.length; ++i) {
      var file = files[i];
      file.name = srv.normalizeFilename(file.name)
      if (file.type == "delete")
        srv.delFile(file.name);
      else
        ensureFile(srv, file.name, null, file.type == "full" ? file.text : null);
    }

    var timeBudget = typeof doc.timeout == "number" ? [doc.timeout] : null;
    if (!query) {
      analyzeAll(srv, timeBudget, function(){});
      return;
    }

    var queryType = queryTypes[query.type];
    if (queryType.takesFile) {
      if (typeof query.file != "string") return c(".query.file must be a string");
      if (!/^#/.test(query.file)) ensureFile(srv, query.file, null);
    }

    analyzeAll(srv, timeBudget, function(err) {
      if (err) return c(err);
      var file = queryType.takesFile && resolveFile(srv, files, query.file);
      if (queryType.fullFile && file.type == "part")
        return c("Can't run a " + query.type + " query on a file fragment");

      infer.resetGuessing()
      infer.withContext(srv.cx, function() {
        var result, run = function() { result = queryType.run(srv, query, file); };
        try {
          if (timeBudget) infer.withTimeout(timeBudget[0], run);
          else run();
        } catch (e) {
          if (srv.options.debug && e.name != "TernError") console.error(e.stack);
          return c(e);
        }
        c(null, result);
      });
    });
  }

  function analyzeFile(srv, file) {
    infer.withContext(srv.cx, function() {
      file.scope = srv.cx.topScope;
      srv.signal("beforeLoad", file);
      infer.analyze(file.ast, file.name, file.scope);
      srv.signal("afterLoad", file);
    });
    return file;
  }

  function ensureFile(srv, name, parent, text) {
    var known = srv.findFile(name);
    if (known) {
      if (text != null) {
        if (known.scope) {
          srv.needsPurge.push(name);
          known.scope = null;
        }
        updateText(known, text, srv);
      }
      if (parentDepth(srv, known.parent) > parentDepth(srv, parent)) {
        known.parent = parent;
        if (known.excluded) known.excluded = null;
        
      } else if (!known.parent) {
      	known.parent = parent; // ORION - Allow a parent to be assigned to an existing file if it had no parent
      }
      return;
    }

    var file = new File(name, parent);
    srv.files.push(file);
    srv.fileMap[name] = file;
    if (text != null) {
      updateText(file, text, srv);
    } else if (srv.options.async) {
      srv.startAsyncAction();
      srv.options.getFile(name, function(err, text) {
        updateText(file, text || "", srv);
        srv.finishAsyncAction(err);
      });
    } else {
      updateText(file, srv.options.getFile(name) || "", srv);
    }
  }

  function fetchAll(srv, c) {
    var done = true, returned = false;
    srv.files.forEach(function(file) {
      if (file.text != null) return;
      if (srv.options.async) {
        done = false;
        srv.options.getFile(file.name, function(err, text) {
          if (err && !returned) { returned = true; return c(err); }
          updateText(file, text || "", srv);
          fetchAll(srv, c);
        });
      } else {
        try {
          updateText(file, srv.options.getFile(file.name) || "", srv);
        } catch (e) { return c(e); }
      }
    });
    if (done) c();
  }

  function waitOnFetch(srv, timeBudget, c) {
    var done = function() {
      srv.off("everythingFetched", done);
      clearTimeout(timeout);
      analyzeAll(srv, timeBudget, c);
    };
    srv.on("everythingFetched", done);
    var timeout = setTimeout(done, srv.options.fetchTimeout);
  }

  function analyzeAll(srv, timeBudget, c) {
    if (srv.pending) return waitOnFetch(srv, timeBudget, c);

    var e = srv.fetchError;
    if (e) { srv.fetchError = null; return c(e); }

    if (srv.needsPurge.length > 0) infer.withContext(srv.cx, function() {
      infer.purge(srv.needsPurge);
      srv.needsPurge.length = 0;
    });

    var done = true;
    // The second inner loop might add new files. The outer loop keeps
    // repeating both inner loops until all files have been looked at.
    for (var i = 0; i < srv.files.length;) {
      var toAnalyze = [];
      for (; i < srv.files.length; ++i) {
        var file = srv.files[i];
        if (file.text == null) done = false;
        else if (file.scope == null && !file.excluded) toAnalyze.push(file);
      }
      toAnalyze.sort(function(a, b) {
        return parentDepth(srv, a.parent) - parentDepth(srv, b.parent);
      });
      for (var j = 0; j < toAnalyze.length; j++) {
        var file = toAnalyze[j];
        if (file.parent && !chargeOnBudget(srv, file)) {
          file.excluded = true;
        } else if (timeBudget) {
          var startTime = +new Date;
          try {
            infer.withTimeout(timeBudget[0], function() { analyzeFile(srv, file); });
          } catch(e) {
            if (e instanceof infer.TimedOut) return c(e)
            else throw e
          }
          timeBudget[0] -= +new Date - startTime;
        } else {
          analyzeFile(srv, file);
        }
      }
    }
    if (done) c();
    else waitOnFetch(srv, timeBudget, c);
  }

  function firstLine(str) {
    var end = str.indexOf("\n");
    if (end < 0) return str;
    return str.slice(0, end);
  }

  function findMatchingPosition(line, file, near) {
    var pos = Math.max(0, near - 500), closest = null;
    if (!/^\s*$/.test(line)) for (;;) {
      var found = file.indexOf(line, pos);
      if (found < 0 || found > near + 500) break;
      if (closest == null || Math.abs(closest - near) > Math.abs(found - near))
        closest = found;
      pos = found + line.length;
    }
    return closest;
  }

  function scopeDepth(s) {
    for (var i = 0; s; ++i, s = s.prev) {}
    return i;
  }

  function ternError(msg) {
    var err = new Error(msg);
    err.name = "TernError";
    return err;
  }

  function resolveFile(srv, localFiles, name) {
    var isRef = name.match(/^#(\d+)$/);
    if (!isRef) return srv.findFile(name);

    var file = localFiles[isRef[1]];
    if (!file || file.type == "delete") throw ternError("Reference to unknown file " + name);
    if (file.type == "full") return srv.fileMap[file.name];

    // This is a partial file

    var realFile = file.backing = srv.fileMap[file.name];
    var offset = file.offset;
    if (file.offsetLines) offset = {line: file.offsetLines, ch: 0};
    file.offset = offset = resolvePos(realFile, file.offsetLines == null ? file.offset : {line: file.offsetLines, ch: 0}, true);
    var line = firstLine(file.text);
    var foundPos = findMatchingPosition(line, realFile.text, offset);
    var pos = foundPos == null ? Math.max(0, realFile.text.lastIndexOf("\n", offset)) : foundPos;
    var inObject, atFunction;

    infer.withContext(srv.cx, function() {
      infer.purge(file.name, pos, pos + file.text.length);

      var text = file.text, m;
      if (m = text.match(/(?:"([^"]*)"|([\w$]+))\s*:\s*function\b/)) {
        var objNode = walk.findNodeAround(file.backing.ast, pos, "ObjectExpression");
        if (objNode && objNode.node.objType)
          inObject = {type: objNode.node.objType, prop: m[2] || m[1]};
      }
      if (foundPos && (m = line.match(/^(.*?)\bfunction\b/))) {
        var cut = m[1].length, white = "";
        for (var i = 0; i < cut; ++i) white += " ";
        file.text = white + text.slice(cut);
        atFunction = true;
      }

      var scopeStart = infer.scopeAt(realFile.ast, pos, realFile.scope);
      var scopeEnd = infer.scopeAt(realFile.ast, pos + text.length, realFile.scope);
      var scope = file.scope = scopeDepth(scopeStart) < scopeDepth(scopeEnd) ? scopeEnd : scopeStart;
      file.ast = parseFile(srv, file)
      infer.analyze(file.ast, file.name, scope);

      // This is a kludge to tie together the function types (if any)
      // outside and inside of the fragment, so that arguments and
      // return values have some information known about them.
      tieTogether: if (inObject || atFunction) {
        var newInner = infer.scopeAt(file.ast, line.length, scopeStart);
        if (!newInner.fnType) break tieTogether;
        if (inObject) {
          var prop = inObject.type.getProp(inObject.prop);
          prop.addType(newInner.fnType);
        } else if (atFunction) {
          var inner = infer.scopeAt(realFile.ast, pos + line.length, realFile.scope);
          if (inner == scopeStart || !inner.fnType) break tieTogether;
          var fOld = inner.fnType, fNew = newInner.fnType;
          if (!fNew || (fNew.name != fOld.name && fOld.name)) break tieTogether;
          for (var i = 0, e = Math.min(fOld.args.length, fNew.args.length); i < e; ++i)
            fOld.args[i].propagate(fNew.args[i]);
          fOld.self.propagate(fNew.self);
          fNew.retval.propagate(fOld.retval);
        }
      }
    });
    return file;
  }

  // Budget management

  function astSize(node) {
    var size = 0;
    walk.simple(node, {Expression: function() { ++size; }});
    return size;
  }

  function parentDepth(srv, parent) {
    var depth = 0;
    while (parent) {
      parent = srv.fileMap[parent].parent;
      ++depth;
    }
    return depth;
  }

  function budgetName(srv, file) {
    for (;;) {
      var parent = srv.fileMap[file.parent];
      if (!parent.parent) break;
      file = parent;
    }
    return file.name;
  }

  function chargeOnBudget(srv, file) {
    var bName = budgetName(srv, file);
    var size = astSize(file.ast);
    var known = srv.budgets[bName];
    if (known == null)
      known = srv.budgets[bName] = srv.options.dependencyBudget;
    if (known < size) return false;
    srv.budgets[bName] = known - size;
    return true;
  }

  // Query helpers

  function isPosition(val) {
    return typeof val == "number" || typeof val == "object" &&
      typeof val.line == "number" && typeof val.ch == "number";
  }

  // Baseline query document validation
  function invalidDoc(doc) {
    if (doc.query) {
      if (typeof doc.query.type != "string") return ".query.type must be a string";
      if (doc.query.start && !isPosition(doc.query.start)) return ".query.start must be a position";
      if (doc.query.end && !isPosition(doc.query.end)) return ".query.end must be a position";
    }
    if (doc.files) {
      if (!Array.isArray(doc.files)) return "Files property must be an array";
      for (var i = 0; i < doc.files.length; ++i) {
        var file = doc.files[i];
        if (typeof file != "object") return ".files[n] must be objects";
        else if (typeof file.name != "string") return ".files[n].name must be a string";
        else if (file.type == "delete") continue;
        else if (typeof file.text != "string") return ".files[n].text must be a string";
        else if (file.type == "part") {
          if (!isPosition(file.offset) && typeof file.offsetLines != "number")
            return ".files[n].offset must be a position";
        } else if (file.type != "full") return ".files[n].type must be \"full\" or \"part\"";
      }
    }
  }

  var offsetSkipLines = 25;

  function findLineStart(file, line) {
    var text = file.text, offsets = file.lineOffsets || (file.lineOffsets = [0]);
    var pos = 0, curLine = 0;
    var storePos = Math.min(Math.floor(line / offsetSkipLines), offsets.length - 1);
    var pos = offsets[storePos], curLine = storePos * offsetSkipLines;

    while (curLine < line) {
      ++curLine;
      pos = text.indexOf("\n", pos) + 1;
      if (pos === 0) return null;
      if (curLine % offsetSkipLines === 0) offsets.push(pos);
    }
    return pos;
  }

  var resolvePos = exports.resolvePos = function(file, pos, tolerant) {
    if (typeof pos != "number") {
      var lineStart = findLineStart(file, pos.line);
      if (lineStart == null) {
        if (tolerant) pos = file.text.length;
        else throw ternError("File doesn't contain a line " + pos.line);
      } else {
        pos = lineStart + pos.ch;
      }
    }
    if (pos > file.text.length) {
      if (tolerant) pos = file.text.length;
      else throw ternError("Position " + pos + " is outside of file.");
    }
    return pos;
  };

  function asLineChar(file, pos) {
    if (!file) return {line: 0, ch: 0};
    var offsets = file.lineOffsets || (file.lineOffsets = [0]);
    var text = file.text, line, lineStart;
    for (var i = offsets.length - 1; i >= 0; --i) if (offsets[i] <= pos) {
      line = i * offsetSkipLines;
      lineStart = offsets[i];
    }
    for (;;) {
      var eol = text.indexOf("\n", lineStart);
      if (eol >= pos || eol < 0) break;
      lineStart = eol + 1;
      ++line;
    }
    return {line: line, ch: pos - lineStart};
  }

  var outputPos = exports.outputPos = function(query, file, pos) {
    if (query.lineCharPositions) {
      var out = asLineChar(file, pos);
      if (file.type == "part")
        out.line += file.offsetLines != null ? file.offsetLines : asLineChar(file.backing, file.offset).line;
      return out;
    } else {
      return pos + (file.type == "part" ? file.offset : 0);
    }
  };

  // Delete empty fields from result objects
  function clean(obj) {
    for (var prop in obj) if (obj[prop] == null) delete obj[prop];
    return obj;
  }
  function maybeSet(obj, prop, val) {
    if (val != null) obj[prop] = val;
  }

  // Built-in query types

  function compareCompletions(a, b) {
    if (typeof a != "string") { a = a.name; b = b.name; }
    var aUp = /^[A-Z]/.test(a), bUp = /^[A-Z]/.test(b);
    if (aUp == bUp) return a < b ? -1 : a == b ? 0 : 1;
    else return aUp ? 1 : -1;
  }

  function isStringAround(node, start, end) {
    return node.type == "Literal" && typeof node.value == "string" &&
      node.start == start - 1 && node.end <= end + 1;
  }

  function pointInProp(objNode, point) {
    for (var i = 0; i < objNode.properties.length; i++) {
      var curProp = objNode.properties[i];
      if (curProp.key.start <= point && curProp.key.end >= point)
        return curProp;
    }
  }

  var jsKeywords = ("break do instanceof typeof case else new var " +
    "catch finally return void continue for switch while debugger " +
    "function this with default if throw delete in try").split(" ");

  var addCompletion = exports.addCompletion = function(query, completions, name, aval, depth) {
    var typeInfo = query.types || query.docs || query.urls || query.origins;
    var wrapAsObjs = typeInfo || query.depths;

	// ORION multiple completions cannot have same name (https://github.com/ternjs/tern/issues/797)
    for (var i = 0, len = completions.length; i < len; ++i) {
      var c = completions[i];
      if ((wrapAsObjs ? c.name : c) == name) {
      	if(query.origins && aval && c.origin !== aval.origin) {
      		continue;
      	}
      	return;
  	  }
    }
    var rec = wrapAsObjs ? {name: name} : name;
    completions.push(rec);

    if (aval && typeInfo) {
      infer.resetGuessing();
      var type = aval.getType();
      rec.guess = infer.didGuess();
      if (query.types)
        rec.type = infer.toString(aval);
      if (query.docs)
        maybeSet(rec, "doc", parseDoc(query, aval.doc || type && type.doc));
      if (query.urls)
        maybeSet(rec, "url", aval.url || type && type.url);
      if (query.origins)
        maybeSet(rec, "origin", aval.origin || type && type.origin);
    }
    if (query.depths) rec.depth = depth || 0;
    return rec;
  };

  function findCompletions(srv, query, file) {
    if (query.end == null) throw ternError("missing .query.end field");
    var fromPlugin = srv.signalReturnFirst("completion", file, query)
    if (fromPlugin) return fromPlugin

    var wordStart = resolvePos(file, query.end), wordEnd = wordStart, text = file.text;
    while (wordStart && acorn.isIdentifierChar(text.charCodeAt(wordStart - 1))) --wordStart;
    if (query.expandWordForward !== false)
      while (wordEnd < text.length && acorn.isIdentifierChar(text.charCodeAt(wordEnd))) ++wordEnd;
    var word = text.slice(wordStart, wordEnd), completions = [], ignoreObj;
    if (query.caseInsensitive) word = word.toLowerCase();

    function gather(prop, obj, depth, addInfo) {
      // 'hasOwnProperty' and such are usually just noise, leave them
      // out when no prefix is provided.
      if ((objLit || query.omitObjectPrototype !== false) && obj == srv.cx.protos.Object && !word) return;
      if (query.filter !== false && word &&
          (query.caseInsensitive ? prop.toLowerCase() : prop).indexOf(word) !== 0) return;
      if (ignoreObj && ignoreObj.props[prop]) return;
      var result = addCompletion(query, completions, prop, obj && obj.props[prop], depth);
      if(result && query.origins) { //ORION set origin to the short name if available
	      if (srv.mod && srv.mod.modules && srv.mod.modules.modules) {
		    var module = srv.mod.modules.modules[result.origin];
	        if (module) {
	            maybeSet(result, "origin", module.modName || result.origin);
	        }
	      }
       }
      if (addInfo && result && typeof result != "string") addInfo(result);
    }
    var hookname, prop, objType, isKey;

    var exprAt = infer.findExpressionAround(file.ast, null, wordStart, file.scope);
    var memberExpr, objLit;
    // Decide whether this is an object property, either in a member
    // expression or an object literal.
    if (exprAt) {
      var exprNode = exprAt.node;
      if(exprNode.type === 'Literal' && typeof exprNode.value === 'string') {
      	//if one of the plugins has not handled in-literal completions, return none
      	return {
      		start: outputPos(query, file, wordStart),
            end: outputPos(query, file, wordEnd),
            isProperty: !!prop,
            isObjectKey: !!isKey,
            completions: []
        };
      }
      if (exprNode.type == "MemberExpression" && exprNode.object.end < wordStart) {
        memberExpr = exprAt;
      } else if (isStringAround(exprNode, wordStart, wordEnd)) {
        var parent = infer.parentNode(exprNode, file.ast);
        if (parent.type == "MemberExpression" && parent.property == exprNode)
          memberExpr = {node: parent, state: exprAt.state};
      } else if (exprNode.type == "ObjectExpression") {
        var objProp = pointInProp(exprNode, wordEnd);
        if (objProp) {
          objLit = exprAt;
          prop = isKey = objProp.key.name;
        } else if (!word && !/:\s*$/.test(file.text.slice(0, wordStart))) {
          objLit = exprAt;
          prop = isKey = true;
        }
      }
    }

    if (objLit) {
      // Since we can't use the type of the literal itself to complete
      // its properties (it doesn't contain the information we need),
      // we have to try asking the surrounding expression for type info.
      objType = infer.typeFromContext(file.ast, objLit);
      ignoreObj = objLit.node.objType;
    } else if (memberExpr) {
      prop = memberExpr.node.property;
      prop = prop.type == "Literal" ? prop.value.slice(1) : prop.name;
      memberExpr.node = memberExpr.node.object;
      objType = infer.expressionType(memberExpr);
    } else if (text.charAt(wordStart - 1) == ".") {
      var pathStart = wordStart - 1;
      while (pathStart && (text.charAt(pathStart - 1) == "." || acorn.isIdentifierChar(text.charCodeAt(pathStart - 1)))) pathStart--;
      var path = text.slice(pathStart, wordStart - 1);
      if (path) {
        objType = infer.def.parsePath(path, file.scope).getObjType();
        prop = word;
      }
    }

    if (prop != null) {
      srv.cx.completingProperty = prop;

      if (objType) infer.forAllPropertiesOf(objType, gather);

      if (!completions.length && query.guess !== false && objType && objType.guessProperties)
        objType.guessProperties(function(p, o, d) {if (p != prop && p != "✖" && p != "<i>") gather(p, o, d);});
      if (!completions.length && word.length >= 2 && query.guess !== false) {
      	Object.keys(srv.cx.props).forEach(function(prop) {
      		srv.cx.props[prop].forEach(function(type) {
      			gather(prop, type, 0);	
      		});
      	});
	  }
      hookname = "memberCompletion";
    } else {
      infer.forAllLocalsAt(file.ast, wordStart, file.scope, gather);
      if (query.includeKeywords) jsKeywords.forEach(function(kw) {
        gather(kw, null, 0, function(rec) { rec.isKeyword = true; });
      });
      hookname = "variableCompletion";
    }
    srv.signal(hookname, file, wordStart, wordEnd, gather)

    if (query.sort !== false) completions.sort(compareCompletions);
    srv.cx.completingProperty = null;

    return {start: outputPos(query, file, wordStart),
            end: outputPos(query, file, wordEnd),
            isProperty: !!prop,
            isObjectKey: !!isKey,
            completions: completions};
  }

  function findProperties(srv, query) {
    var prefix = query.prefix, found = [];
    for (var prop in srv.cx.props)
      if (prop != "<i>" && (!prefix || prop.indexOf(prefix) === 0)) found.push(prop);
    if (query.sort !== false) found.sort(compareCompletions);
    return {completions: found};
  }

  function inBody(node, pos) {
    var body = node.body, start, end
    if (!body) return false
    if (Array.isArray(body)) {
      start = body[0].start
      end = body[body.length - 1].end
    } else {
      start = body.start
      end = body.end
    }
    return start <= pos && end >= pos
  }

  var findExpr = exports.findQueryExpr = function(file, query, wide) {
    if (query.end == null) throw ternError("missing .query.end field");

    if (query.variable) {
      var scope = infer.scopeAt(file.ast, resolvePos(file, query.end), file.scope);
      return {node: {type: "Identifier", name: query.variable, start: query.end, end: query.end + 1},
              state: scope};
    } else {
      var start = query.start && resolvePos(file, query.start), end = resolvePos(file, query.end);
      var expr = infer.findExpressionAt(file.ast, start, end, file.scope);
      if (!expr) {
        var around = infer.findExpressionAround(file.ast, start, end, file.scope);
        if (around && !inBody(around.node, end) &&
            (around.node.type == "ObjectExpression" || wide ||
             (start == null ? end : start) - around.node.start < 20 || around.node.end - end < 20))
          expr = around
      }
      return expr
    }
  };

  function findExprOrThrow(file, query, wide) {
    var expr = findExpr(file, query, wide);
    if (expr) return expr;
    throw ternError("No expression at the given position.");
  }

  function ensureObj(tp) {
    if (!tp || !(tp = tp.getType()) || !(tp instanceof infer.Obj)) return null;
    return tp;
  }

  function findExprType(srv, query, file, expr) {
    var type;
    if (expr) {
      infer.resetGuessing();
      type = infer.expressionType(expr);
    }
    var typeHandlers = srv.hasHandler("typeAt")
    if (typeHandlers) {
      var pos = resolvePos(file, query.end)
      for (var i = 0; i < typeHandlers.length; i++)
        type = typeHandlers[i](file, pos, expr, type)
    }
    if (!type) throw ternError("No type found at the given position.");

    var objProp;
    if (expr.node.type == "ObjectExpression" && query.end != null &&
        (objProp = pointInProp(expr.node, resolvePos(file, query.end)))) {
      var name = objProp.key.name;
      var fromCx = ensureObj(infer.typeFromContext(file.ast, expr));
      if (fromCx && fromCx.hasProp(name)) {
        type = fromCx.hasProp(name);
      } else {
        var fromLocal = ensureObj(type);
        if (fromLocal && fromLocal.hasProp(name))
          type = fromLocal.hasProp(name);
      }
    }
    //ORION
    type.guess = infer.didGuess();
    return type;
  };

  function findTypeAt(srv, query, file) {
    var expr = findExpr(file, query), exprName;
    var type = findExprType(srv, query, file, expr), exprType = type;
    if (query.preferFunction)
      type = type.getFunctionType() || type.getType();
    else
      type = type.getType();

    if (expr) {
      if (expr.node.type == "Identifier")
        exprName = expr.node.name;
      else if (expr.node.type == "MemberExpression" && !expr.node.computed)
        exprName = expr.node.property.name;
      else if (expr.node.type == "MethodDefinition" && !expr.node.computed)
        exprName = expr.node.key.name
    }

    if (query.depth != null && typeof query.depth != "number")
      throw ternError(".query.depth must be a number");

    var result = {guess: infer.didGuess(),
                  type: infer.toString(exprType, query.depth),
                  name: type && type.name,
                  exprName: exprName,
                  doc: exprType.doc,
                  url: exprType.url};
    if (type) storeTypeDocs(query, type, result);

    return clean(result);
  }

  function parseDoc(query, doc) {
    if (!doc) return null;
    if (query.docFormat == "full") return doc;
    var parabreak = /.\n[\s@\n]/.exec(doc);
    if (parabreak) doc = doc.slice(0, parabreak.index + 1);
    doc = doc.replace(/\n\s*/g, " ");
    if (doc.length < 100) return doc;
    var sentenceEnd = /[\.!?] [A-Z]/g;
    sentenceEnd.lastIndex = 80;
    var found = sentenceEnd.exec(doc);
    if (found) doc = doc.slice(0, found.index + 1);
    return doc;
  }

  function findDocs(srv, query, file) {
    var expr = findExpr(file, query);
    var type = findExprType(srv, query, file, expr);
    var result = {url: type.url, doc: parseDoc(query, type.doc), type: infer.toString(type)};
    var inner = type.getType();
    if (inner) storeTypeDocs(query, inner, result);
    return clean(result);
  }

  function storeTypeDocs(query, type, out) {
    if (!out.url) out.url = type.url;
    if (!out.doc) out.doc = parseDoc(query, type.doc);
    if (!out.origin) out.origin = type.origin;
    var ctor, boring = infer.cx().protos;
    if (!out.url && !out.doc && type.proto && (ctor = type.proto.hasCtor) &&
        type.proto != boring.Object && type.proto != boring.Function && type.proto != boring.Array /*Orion*/ && type.proto != boring.Number && type.proto != boring.String && type.proto != boring.Boolean && type.proto != boring.RegExp) {
      out.url = ctor.url;
      out.doc = parseDoc(query, ctor.doc);
    }
  }

  var getSpan = exports.getSpan = function(obj) {
    if (!obj.origin) return;
    if (obj.originNode) {
      var node = obj.originNode;
      if (/^Function/.test(node.type) && node.id) node = node.id;
      return {origin: obj.origin, node: node};
    }
    if (obj.span) return {origin: obj.origin, span: obj.span};
  };

  var storeSpan = exports.storeSpan = function(srv, query, span, target) {
    target.origin = span.origin;
    if (span.span) {
      var m = /^(\d+)\[(\d+):(\d+)\]-(\d+)\[(\d+):(\d+)\]$/.exec(span.span);
      target.start = query.lineCharPositions ? {line: Number(m[2]), ch: Number(m[3])} : Number(m[1]);
      target.end = query.lineCharPositions ? {line: Number(m[5]), ch: Number(m[6])} : Number(m[4]);
    } else {
      var file = srv.fileMap[span.origin];
      target.start = outputPos(query, file, span.node.start);
      target.end = outputPos(query, file, span.node.end);
    }
  };

  function findDef(srv, query, file) {
    var expr = findExpr(file, query);
    var type = findExprType(srv, query, file, expr);
    if (infer.didGuess() && !query.guess) return {};
    // Before Orion: if (infer.didGuess()) return {};
    
    // ORION Move result collection into function and collect al potential matches to display in UI
    var result = getResult(type, srv, query);
    if (infer.didGuess()) {
	   if (type.potentialMatches) {
	      var temp = [];
	      for (var i = 0; i < type.potentialMatches.length; i++) {
			temp.push(getResult(type.potentialMatches[i], srv, query));
	      }
	      result.results = temp;
	   }
   	}
   	if(typeof result.start !== "number" && typeof result.end !== "number") {
   		//ORION, try the type of the found type
   		var inner = type.getType();
   		if(inner && inner.originNode) {
   			result = getResult(inner, srv, query);
   		} else if(expr && expr.node && expr.node.type === 'ThisExpression') {
   			inner = inner.proto;
   			if(inner && inner.hasCtor) {
   				result = getResult(inner.hasCtor, srv, query);
   			}
   		}
   	}
   	return result;
  }
  
  // ORION Moved result creation to a function so it can be called for multiple potential matches
  function getResult(type, srv, query) {
    var span = getSpan(type);
    var result = {url: type.url, doc: parseDoc(query, type.doc), origin: type.origin, guess: infer.didGuess()}; // ORION
	// Before Orion:   var result = {url: type.url, doc: parseDoc(query, type.doc), origin: type.origin};

    if (type.types) for (var i = type.types.length - 1; i >= 0; --i) {
      var tp = type.types[i];
      storeTypeDocs(query, tp, result);
      if (!span) span = getSpan(tp);
    }

    if (span && span.node) { // refers to a loaded file
      var spanFile = span.node.sourceFile || srv.fileMap[span.origin];
      var start = outputPos(query, spanFile, span.node.start), end = outputPos(query, spanFile, span.node.end);
      result.start = start; result.end = end;
      result.file = span.origin;
      var cxStart = Math.max(0, span.node.start - 50);
      result.contextOffset = span.node.start - cxStart;
      result.context = spanFile.text.slice(cxStart, cxStart + 50);
    } else if (span) { // external
      result.file = span.origin;
      storeSpan(srv, query, span, result);
    }
    return clean(result);
  }


  function findRefsToVariable(srv, query, file, expr, checkShadowing) {
    var name = expr.node.name;

    for (var scope = expr.state; scope && !(name in scope.props); scope = scope.prev) {}
    if (!scope) throw ternError("Could not find a definition for " + name);

    var type, refs = [];
    function storeRef(file) {
      return function(node, scopeHere) {
        if (checkShadowing) for (var s = scopeHere; s != scope; s = s.prev) {
          var exists = s.hasProp(checkShadowing);
          if (exists)
            throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would make a variable at line " +
                            (asLineChar(file, node.start).line + 1) + " point to the definition at line " +
                            (asLineChar(file, exists.name.start).line + 1));
        }
        refs.push({file: file.name,
                   start: outputPos(query, file, node.start),
                   end: outputPos(query, file, node.end)});
      };
    }

    if (scope.originNode) {
      type = "local";
      if (checkShadowing) {
        for (var prev = scope.prev; prev; prev = prev.prev)
          if (checkShadowing in prev.props) break;
        if (prev) infer.findRefs(scope.originNode, scope, checkShadowing, prev, function(node) {
          throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would shadow the definition used at line " +
                          (asLineChar(file, node.start).line + 1));
        });
      }
      infer.findRefs(scope.originNode, scope, name, scope, storeRef(file));
    } else {
      type = "global";
      if (query.onlySourceFile) {
        infer.findRefs(file.ast, file.scope, name, scope, storeRef(file));
      } else {
        for (var i = 0; i < srv.files.length; ++i) {
          var cur = srv.files[i];
          infer.findRefs(cur.ast, cur.scope, name, scope, storeRef(cur));
        }
      }
    }

    return {refs: refs, type: type, name: name};
  }

  function findRefsToProperty(srv, query, sourceFile, expr, prop) {
    var exprType = infer.expressionType(expr);
    if (expr.node.type == "MethodDefinition") {
      exprType = exprType.propertyOf;
    }
    var objType = exprType.getObjType();
    if (!objType) throw ternError("Couldn't determine type of base object.");

    var refs = [];
    function storeRef(file) {
      return function(node) {
        refs.push({file: file.name,
                   start: outputPos(query, file, node.start),
                   end: outputPos(query, file, node.end)});
      };
    }

    if (query.onlySourceFile) {
        infer.findPropRefs(sourceFile.ast, sourceFile.scope, objType, prop.name, storeRef(sourceFile));
    } else {
      for (var i = 0; i < srv.files.length; ++i) {
        var cur = srv.files[i];
        infer.findPropRefs(cur.ast, cur.scope, objType, prop.name, storeRef(cur));
      }
    }

    return {refs: refs, name: prop.name};
  }

  function findRefs(srv, query, file) {
    var expr = findExprOrThrow(file, query, true);
    if (expr && expr.node.type == "Identifier") {
      return findRefsToVariable(srv, query, file, expr);
    } else if (expr && expr.node.type == "MemberExpression" && !expr.node.computed) {
      var p = expr.node.property;
      expr.node = expr.node.object;
      return findRefsToProperty(srv, query, file, expr, p);
    } else if (expr && expr.node.type == "ObjectExpression") {
      var pos = resolvePos(file, query.end);
      for (var i = 0; i < expr.node.properties.length; ++i) {
        var k = expr.node.properties[i].key;
        if (k.start <= pos && k.end >= pos)
          return findRefsToProperty(srv, query, file, expr, k);
      }
    } else if (expr && expr.node.type == "MethodDefinition") {
      var p = expr.node.key;
      return findRefsToProperty(srv, query, file, expr, p);
    }
    throw ternError("Not at a variable or property name.");
  }

  function buildRename(srv, query, file) {
    if (typeof query.newName != "string") throw ternError(".query.newName should be a string");
    var expr = findExprOrThrow(file, query);
    if (!expr || expr.node.type != "Identifier") throw ternError("Not at a variable.");

    var data = findRefsToVariable(srv, query, file, expr, query.newName), refs = data.refs;
    delete data.refs;
    data.files = srv.files.map(function(f){return f.name;});

    var changes = data.changes = [];
    for (var i = 0; i < refs.length; ++i) {
      var use = refs[i];
      use.text = query.newName;
      changes.push(use);
    }

    return data;
  }

  function listFiles(srv) {
    return {files: srv.files.map(function(f){return f.name;})};
  }

  exports.version = "0.18.0";
  exports.findDef = findDef; //ORION
  exports.findExprType = findExprType; //ORION
  exports.resolveFile = resolveFile; //ORION
  exports.storeTypeDocs = storeTypeDocs; //ORION
  exports.parseDoc = parseDoc; //ORION
  exports.findRefs = findRefs; // ORION
  exports.findRefsToProperty = findRefsToProperty; // ORION
  exports.findRefsToVariable = findRefsToVariable; // ORION
  exports.ternError = ternError; // ORION
});

/**
 * @license RequireJS i18n 2.0.6 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
 * Available via the MIT or new BSD license.
 * see: http://github.com/requirejs/i18n for details
 */
/*jslint regexp: true */
/*global require: false, navigator: false, define: false */

/**
 * This plugin handles i18n! prefixed modules. It does the following:
 *
 * 1) A regular module can have a dependency on an i18n bundle, but the regular
 * module does not want to specify what locale to load. So it just specifies
 * the top-level bundle, like 'i18n!nls/colors'.
 *
 * This plugin will load the i18n bundle at nls/colors, see that it is a root/master
 * bundle since it does not have a locale in its name. It will then try to find
 * the best match locale available in that master bundle, then request all the
 * locale pieces for that best match locale. For instance, if the locale is 'en-us',
 * then the plugin will ask for the 'en-us', 'en' and 'root' bundles to be loaded
 * (but only if they are specified on the master bundle).
 *
 * Once all the bundles for the locale pieces load, then it mixes in all those
 * locale pieces into each other, then finally sets the context.defined value
 * for the nls/colors bundle to be that mixed in locale.
 *
 * 2) A regular module specifies a specific locale to load. For instance,
 * i18n!nls/fr-fr/colors. In this case, the plugin needs to load the master bundle
 * first, at nls/colors, then figure out what the best match locale is for fr-fr,
 * since maybe only fr or just root is defined for that locale. Once that best
 * fit is found, all of its locale pieces need to have their bundles loaded.
 *
 * Once all the bundles for the locale pieces load, then it mixes in all those
 * locale pieces into each other, then finally sets the context.defined value
 * for the nls/fr-fr/colors bundle to be that mixed in locale.
 */
(function () {
    'use strict';

    //regexp for reconstructing the master bundle name from parts of the regexp match
    //nlsRegExp.exec('foo/bar/baz/nls/en-ca/foo') gives:
    //['foo/bar/baz/nls/en-ca/foo', 'foo/bar/baz/nls/', '/', '/', 'en-ca', 'foo']
    //nlsRegExp.exec('foo/bar/baz/nls/foo') gives:
    //['foo/bar/baz/nls/foo', 'foo/bar/baz/nls/', '/', '/', 'foo', '']
    //so, if match[5] is blank, it means this is the top bundle definition.
    var nlsRegExp = /(^.*(^|\/)nls(\/|$))([^\/]*)\/?([^\/]*)/;

    //Helper function to avoid repeating code. Lots of arguments in the
    //desire to stay functional and support RequireJS contexts without having
    //to know about the RequireJS contexts.
    function addPart(locale, master, needed, toLoad, prefix, suffix) {
        if (master[locale]) {
            needed.push(locale);
            if (master[locale] === true || master[locale] === 1) {
                toLoad.push(prefix + locale + '/' + suffix);
            }
        }
    }

    function addIfExists(req, locale, toLoad, prefix, suffix) {
        var fullName = prefix + locale + '/' + suffix;
        if (require._fileExists(req.toUrl(fullName + '.js'))) {
            toLoad.push(fullName);
        }
    }

    /**
     * Simple function to mix in properties from source into target,
     * but only if target does not already have a property of the same name.
     * This is not robust in IE for transferring methods that match
     * Object.prototype names, but the uses of mixin here seem unlikely to
     * trigger a problem related to that.
     */
    function mixin(target, source, force) {
        var prop;
        for (prop in source) {
            if (source.hasOwnProperty(prop) && (!target.hasOwnProperty(prop) || force)) {
                target[prop] = source[prop];
            } else if (typeof source[prop] === 'object') {
                if (!target[prop] && source[prop]) {
                    target[prop] = {};
                }
                mixin(target[prop], source[prop], force);
            }
        }
    }

    define('i18n',['module'], function (module) {
        var masterConfig = module.config ? module.config() : {};
        masterConfig = masterConfig || {};

        return {
            version: '2.0.6',
            /**
             * Called when a dependency needs to be loaded.
             */
            load: function (name, req, onLoad, config) {
                config = config || {};

                if (config.locale) {
                    masterConfig.locale = config.locale;
                }

                var masterName,
                    match = nlsRegExp.exec(name),
                    prefix = match[1],
                    locale = match[4],
                    suffix = match[5],
                    parts = locale.split('-'),
                    toLoad = [],
                    value = {},
                    i, part, current = '';

                //If match[5] is blank, it means this is the top bundle definition,
                //so it does not have to be handled. Locale-specific requests
                //will have a match[4] value but no match[5]
                if (match[5]) {
                    //locale-specific bundle
                    prefix = match[1];
                    masterName = prefix + suffix;
                } else {
                    //Top-level bundle.
                    masterName = name;
                    suffix = match[4];
                    locale = masterConfig.locale;
                    if (!locale) {
                        locale = masterConfig.locale =
                            typeof navigator === 'undefined' ? 'root' :
                            ((navigator.languages && navigator.languages[0]) ||
                             navigator.language ||
                             navigator.userLanguage || 'root').toLowerCase();
                    }
                    parts = locale.split('-');
                }

                if (config.isBuild) {
                    //Check for existence of all locale possible files and
                    //require them if exist.
                    toLoad.push(masterName);
                    addIfExists(req, 'root', toLoad, prefix, suffix);
                    for (i = 0; i < parts.length; i++) {
                        part = parts[i];
                        current += (current ? '-' : '') + part;
                        addIfExists(req, current, toLoad, prefix, suffix);
                    }

                    if(config.locales) {
                    	var j, k; 
                    	for (j = 0; j < config.locales.length; j++) {
                    		locale = config.locales[j];
                    		parts = locale.split("-");
                    		current = "";
	                    	for (k = 0; k < parts.length; k++) {
		                        part = parts[k];
		                        current += (current ? "-" : "") + part;
		                        addIfExists(req, current, toLoad, prefix, suffix);
	                    	}
                    	}
                    }

                    req(toLoad, function () {
                        onLoad();
                    });
                } else {
                    //First, fetch the master bundle, it knows what locales are available.
                    req([masterName], function (master) {
                        //Figure out the best fit
                        var needed = [],
                            part;

                        //Always allow for root, then do the rest of the locale parts.
                        addPart('root', master, needed, toLoad, prefix, suffix);
                        for (i = 0; i < parts.length; i++) {
                            part = parts[i];
                            current += (current ? '-' : '') + part;
                            addPart(current, master, needed, toLoad, prefix, suffix);
                        }

                        //Load all the parts missing.
                        req(toLoad, function () {
                            var i, partBundle, part;
                            for (i = needed.length - 1; i > -1 && needed[i]; i--) {
                                part = needed[i];
                                partBundle = master[part];
                                if (partBundle === true || partBundle === 1) {
                                    partBundle = req(prefix + part + '/' + suffix);
                                }
                                mixin(value, partBundle);
                            }

                            //All done, notify the loader.
                            onLoad(value);
                        });
                    });
                }
            }
        };
    });
}());

/*******************************************************************************
 * @license
 * Copyright (c) 2014 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 * 
 ******************************************************************************/
/* eslint-env amd */
define('javascript/nls/messages',{
	root:true
});

/*******************************************************************************
 * @license
 * Copyright (c) 2014, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
 *
 ******************************************************************************/
/* eslint-env amd */
define('javascript/nls/root/messages',{
	'pluginName': 'Orion JavaScript Tool Support',
	'pluginDescription': 'This plug-in provides JavaScript tools support for Orion, like editing, search, navigation, validation, and code completion.',
	'error': 'Error',
	'warning' : 'Warning',
	'ignore' : 'Ignore',
	'info': 'Info',
	'unknown': 'Uknown',
	'none': 'None',
	'projectPath': 'Project Path',
	'environment': 'Environment',
	'noEnvironment': 'Could not compute environment',
	'ecmaVersion': 'ECMA Version',
	'devEnv': 'Development Environment',
	'eslintConfig': 'ESLint Configuration',
	'eslintFile': 'eslintrc file',
	'nodeConfig': 'Node Configuration',
	'packagejsonFile': 'package.json file',
	'ternFile': '.tern-project file',
	'ternConfig': 'Tern Configuration',
	'javascript': 'JavaScript',
	'newTernProjectFile': 'New .tern-project file',
	'ternProjectHover': 'Tern project hover',
	'ternContentAssist' : 'Tern JavaScript content assist',
	'ternProjectAssist': 'Tern project file content assist',
	'packageJsonAssist': 'Package.json file content assist',
	'emptyFileTemplateDoc': 'Create new default contents for the .tern-project file',
	'prefCodeStyle':'Code Style',
	'prefBestPractices':'Best Practices',
	'prefPotentialProblems':'Possible Errors',
	'sourceOutline' : 'Source Outline',
	'sourceOutlineTitle': 'JavaScript source outline',
	'astOutline' : 'AST Outline',
	'astOutlineTitle': 'JavaScript AST outline',
	'contentAssist' : 'JavaScript content assist',
	'eslintValidator' : 'JavaScript Validator',
	'missingCurly' : 'Statements not enclosed in braces:',
	'curlyFixName': 'Enclose statement in braces',
	'ignoreInFileFixName': 'Disable ${0}',
	'noCaller' : 'Discouraged \'arguments.caller\' or \'arguments.callee\' use:',
	'noCommaDangle' : 'Trailing commas in object expressions:',
	'noCondAssign' : 'Assignments in conditional expressions:',
	'noConsole' : 'Discouraged console use in browser code:',
	'noConstantCondition' : 'Constant as conditional expression:',
	'noRegexSpaces' : 'Multiple spaces in regular expressions:',
	'noReservedKeys' : 'Reserved words used as property keys:',
	'noReservedKeysFixName': 'Surround key with quotes',
	'noEqeqeq' : 'Discouraged \'==\' use:',
	"unknownRequire": "Unknown required library:",
	'noDebugger' : 'Discouraged \'debugger\' statement use:',
	'noDebuggerFixName': 'Remove statement',
	'noWith': 'Discouraged \'with\' statement use:',
	'noEval' : 'Discouraged \'eval()\' use:',
	'noImpliedEval' : 'Discouraged implied \'eval()\' use:',
	'noDupeKeys' : 'Duplicate object keys:',
	'noDupeKeysFixName': 'Rename key',
	'NoRedeclareFixName' : 'Rename variable',
	'noDuplicateCaseFixName' : 'Rename case',
	'removeDuplicateCaseFixName' : 'Remove case',
	'noIterator': 'Discouraged __iterator__ property use:',
	'noProto': 'Discouraged __proto__ property use:',
	'noUndefInit': 'Explicitly initializing variables to undefined:',
	'noundefinitFixName': 'Remove assignment',
	'useIsNaN' : 'NaN not compared with isNaN():',
	'useIsNanFixName': 'Use isNaN()',
	'missingDoc' : 'Missing JSDoc:',
	'missingDocFixName': 'Generate element JSDoc',
	'noUnreachable' : 'Unreachable code:',
	'noFallthrough' : 'Switch case fall-through:',
	'useBeforeDefine' : 'Member used before definition:',
	'noEmptyBlock' : 'Undocumented empty block:',
	'newParens' : 'Missing parentheses in constructor call:',
	'newparensFixName': 'Add parentheses',
	'noNewArray': 'Discouraged \'new Array()\':',
	'noNewArrayFixName': 'Convert to array literal',
	'noNewFunc': 'Discouraged \'new Function()\':',
	'noNewObject': 'Discouraged \'new Object()\':',
	'noNewWrappers': 'Discouraged wrapper objects:',
	'noNewWrappersLiteralFixName': 'Convert to literal',
	'noNewWrappersFixName': 'Remove \'new\' keyword',
	'noMixedSpacesAndTabs' : 'Mixed spaces and tabs:',
	'missingSemi' : 'Missing semicolons:',
	'missingSemiFineGrained' : 'Kinds:',
	'missingSemiOmitLastInOneLineBlock' : 'Omit the last semicolon in a one-line block:',
	'unusedVars' : 'Unused variables:',
	'varRedecl' : 'Variable re-declarations:',
	'varShadow': 'Variable shadowing:',
	'undefMember' : 'Undeclared global reference:',
	'undefExpression' : 'Undeclared function reference:',
	'unnecessarySemis' : 'Unnecessary semicolons:',
	'unusedParams' : 'Unused parameters:',
	'missingRequirejs': '\'requirejs\' plugin missing in AMD environment:',
	'unsupportedJSLint' : 'Unsupported environment directive:',
	'noThrowLiteral': 'Literal used in \'throw\':',
	'noselfassignFixName': 'Remove assignment',
	'noselfassignRenameFixName': 'Rename right hand variable',
	'missingNls': 'Non-externalized string literals (missing $NON-NLS$ tag):',
	'unnecessaryNls': 'Unnecessary $NON-NLS$ tags:',
	'generateDocName' : 'Generate Element Comment',
	'generateDocTooltip' : 'Generate a JSDoc-like comment for the selected JavaScript element',
	'renameElement' : 'Rename JavaScript Element',
	'renameElementTooltip' : 'Rename the selected JavaScript element',
	'renameFailedTimedOut': 'Could not rename element - operation timed out',
	'openDeclName' : 'Open Declaration',
	'openDeclTooltip' : 'Open the declaration of the selected element',
	'openImplName' : 'Open Implementation',
	'openImplTooltip' : 'Open the implementation of the selected element',
	'noImplFound': 'No implementation was found',
	'implTimedOut': 'Could not compute implementation, the operation timed out',
	'projectRefsTooltip': 'Show all references to the selection in the current project',
	'referencesMenuName': 'References',
	'referencesMenuTooltip': 'Show different kinds of references',
	'noDeclTimedOut': 'No declaration was found - operation timed out',
	'validTypeof': 'Invalid \'typeof\' comparison:',
	'noSparseArrays': 'Sparse array declarations:',
	'javascriptValidation': 'Javascript Validation',
	'jsHover': 'JavaScript Hover Provider',
	'jsHoverEvaluationProvider': 'JavaScript Hover Evaluation Provider',
	'removeExtraParensFixName': 'Remove gratuitous parentheses',
	'removeExtraSemiFixName': 'Remove extra semicolon',
	'addFallthroughCommentFixName': 'Add $FALLTHROUGH$ comment',
	'addEmptyCommentFixName': 'Comment empty block',
	'addESLintEnvFixName': 'Add to eslint-env directive',
	'addESLintGlobalFixName': 'Add to globals directive',
	'openDefinition': 'Go to definition...',
	'removeUnusedParamsFixName': 'Remove parameter',
	'commentCallbackFixName': 'Add @callback to function',
	'eqeqeqFixName': 'Update operator',
	'unreachableFixName': 'Remove unreachable code',
	'sparseArrayFixName': 'Convert to normal array',
	'semiFixName': 'Add missing semicolon',
	'unknownRequirePluginFixName': 'Update project settings',
	'unknownRequirePackagejsonFixName': 'Update package.json',
	'radix': 'Missing radix parameter to parseInt():',
	'radixFixName': 'Add default radix',
	'unusedVarsUnusedFixName': 'Remove unused variable',
	'unreadVarsFixName': 'Remove unread variable',
	'unusedFuncDeclFixName': 'Remove unused function',
	'noCommaDangleFixName': 'Remove extra \',\'',
	'addBBreakFixName': 'Add break statement',
	'noShadowGlobals': 'Global shadowing:',
	'noThrowLiteralFixName': 'Change to Error' ,
	'missingNlsFixName': 'Add missing $NON-NLS$ tag',
	'unnecessaryNlsFixName': 'Remove unnecessary $NON-NLS$ tag',
	'funcProposalDescription': ' - The name of the function',
	'funcParamProposalDescription': ' - Function parameter',
	'eslintRuleProposalDescripton': ' - ESLint rule',
	'eslintEnvProposalDescription': ' - ESLint environment name',
	'onlineDocumentationProposalEntry': '\n\n[Online documentation](${0})',
	'keywordHoverProposal': 'ECMAScript reserved keyword',
	'reloadPluginCmd': 'Reload',
	'reloadPluginCmdTooltip'	: 'Reload plug-in',
	'reloadAllPluginsCmd': 'Reload All',
	'reloadAllPluginsCmdTooltip': 'Reload all plug-ins',
	'templateHoverHeader': 'Template source code:\n\n',
	'templateAssistHeader': 'Templates',
	'keywordAssistHeader': 'Keywords',
	'ternPlugins': 'Tern Plug-ins',
	'noTernPluginsAvailable': 'No Tern plug-ins are currently loaded. This may be because you have not yet activated content assist in a JavaScript file. Tern plug-ins provide type information and code templates for JavaScript.',
	'noDeclFound': 'Could not find declaration',
	'declFoundInIndex': 'Declaration was not found in source, it was found in the \'${0}\' index.',
	'implFoundInIndex': 'Implementation is not in your workspace. Found in \'${0}\' index.',
	'deprecatedHoverTitle': 'Deprecated.', // the in-line title for deprecated memebers. The word is used alone in a sentence with no further punctuation
	'parametersHoverTitle': 'Parameters:', // the in-line title for the parameters section. The word is used alone in a sentence with no further punctuation
	'returnsHoverTitle': 'Returns:', // the in-line title for the returns section. The word is used alone in a sentence with no further punctuation
	'throwsHoverTitle': 'Throws:', // the in-line title for the throws section. The word is used alone in a sentence with no further punctuation
	'callbackHoverTitle': 'Callback:', // the in-line title for the callback section. The word is used alone in a sentence with no further punctuation
	'callbackText': 'This function is used as a callback',
	'sinceHoverTitle': 'Since:', // the in-line title for the since section. The word is used alone in a sentence with no further punctuation
	'seeAlsoHoverTitle': 'See Also:', // the in-line title for the since section. The word is used alone in a sentence with no further punctuation
	'openFileForTitle': 'Open file for', // the in-line title for the 'open file hover'. The sentence is used as-is and is followed only by a OS file path
	'failedToReadFile': 'Failed to read file: ${0}',
	'badInlineRename': 'In-line rename is only available for local variables and declarations.',
	'failedRename': 'In-line rename failed: ${0}',
	'declDisplayName': '${0} (start: ${1}, end: ${2})', // ${0} is the fully qualified file path of the decl, ${1} and ${2} are the numerical start and end offsets of the decl, respectively
	'declPotentialHeader': '**Potential matches:**\n',
	'typeofOptions': 'Typeof Options',
	'NoCondAssignFixName' : 'Surround with parenthesis',
	'no-void' : 'Disallow use of the void operator:',
	'no-extra-bind' : 'Disallow unnecessary calls to \'.bind()\':',
	'NoExtraBindFixName' : 'Remove \'bind\' call',
	'no-implicit-coercion' : 'Disallow the type conversion with shorter notations:',
	'no-implicit-coercion-boolean' : 'Report type conversion to boolean:',
	'no-implicit-coercion-number' : 'Report type conversion to number:',
	'no-implicit-coercion-string' : 'Report type conversion to string:',
	'no-extend-native' : 'Disallow extending of native objects:',
	'no-extend-native-exceptions' : 'Exceptions:',
	'no-lone-blocks' : 'Disallow unnecessary nested blocks:',
	'quotes' : 'Enforce quote style:',
	'quotesKind' : 'Kind of quotes:',
	'quotesAvoidEscape' : 'Avoid escaped quotes:',
	'quotesAllowTemplateLiterals' : 'Allow template literals:',
	'singleQuote' : 'single quote (\')',
	'doubleQuote' : 'double quote (")',
	'backtickQuote' : 'backtick (`)',
	'quoteFixName' : 'Fix quotes',
	'yoda' : 'Require or disallow Yoda conditions:',
	'always' : 'Always',
	'never' : 'Never',
	'yodaKind' : 'Kinds:',
	'yodaExceptRange' : 'Except for range comparisons:',
	'yodaOnlyEquality' : 'Check only for \'==\' and \'===\' operators:',
	'no-param-reassign' : 'Disallow reassignment of function parameters:',
	'no-param-reassign-props' : 'Disallow for parameter properties:',
	'no-native-reassign' : 'Disallow reassignment of native objects',
	'no-native-reassign-exceptions' : 'Exceptions:',
	'no-unused-expressions' : 'Disallow unused expressions:',
	'no-unused-expressions-allowShortCircuit' : 'Allow to use short circuit evaluations:',
	'no-unused-expressions-allowTernary' : 'Allow to use ternary operators:',
	'noUnusedExpressionsFixName' : 'Remove unused expressions',
	'no-invalid-this' : 'Disallow this keywords outside of classes or class-like objects:',
	'noTrailingSpaces' : 'Disallow trailing whitespace:',
	'noTrailingSpacesSkipBlankLines' : 'Skip empty lines:',
	'noTrailingSpacesFixName' : 'Remove trailing spaces',
	'no-irregular-whitespace' : 'Disallow irregular whitespace:',
	'no-irregular-whitespace-skipStrings' : 'Skip strings:',
	'no-irregular-whitespace-skipComments' : 'Skip comments:',
	'no-irregular-whitespace-skipRegexps' : 'Skip regular expressions:',
	'no-irregular-whitespace-skipTemplates' : 'Skip templates:',

	//All refs
	'functionDecls': 'Function Declarations',
	'functionCalls': 'Function Calls',
	'propAccess': 'Property Access',
	'propWrite': 'Property Write',
	'varAccess': 'Variable Access',
	'varWrite': 'Variable Write',
	'varDecls': 'Variable Declarations',
	'regex': 'Regular Expressions',
	'strings': 'Strings',
	'blockComments': 'Block Comments',
	'lineComments': 'Line Comments',
	'partial': 'Partial Matches',
	'uncategorized': 'Uncategorized',
	'parseErrors': 'Parse Errors',
	'noFileContents': 'Could not compute references: failed to compute file text content',
	'noFileMeta': 'Could not compute references: failed to compute file metadata',
	'cannotComputeRefs': 'Cannot compute references: ${0}',
	'notAnIdentifier': 'Cannot compute references at the selected location: Location is not an identifier',
	'notHtmlOffset': 'The selection is not a valid JavaScript offset in HTML',
	'allProjectRefs': 'Finding all project references...',
	'allWorkspaceRefs': 'Finding all workspace references...',
	'refsFoundIn': 'References found in file: \'${0}\' (${1}/${2})',
	'accessor-pairs' : 'Getter and setter accessors not in pairs:',
	'noConstAssign' : 'Disallow modifying variables that are declared using const:',
	'no-control-regex' : 'Disallow control characters in regular expressions:',
	'no-duplicate-case' : 'Duplicate case:',
	'no-empty-character-class' : 'Disallow empty character classes:',
	'no-extra-boolean-cast' : 'Discourage redundant double negation:',
	'no-extra-parens' : 'Discourage redundant parentheses:',
	'no-extra-parens-kinds' : 'Kinds:',
	'no-extra-parens-conditionalAssign' : 'Allows extra parentheses around assignments in conditional test expressions:',
	'no-extra-parens-returnAssign' : 'Allows extra parentheses around assignments in return statements:',
	'no-extra-parens-nestedBinaryExpressions' : 'Allows extra parentheses in nested binary expressions:',
	'no-invalid-regexp' : 'Invalid regular expressions:',
	'no-invalid-regexp-flags' : 'List of allowed flags:',
	'no-negated-in-lhs' : 'Disallow negated left operand of in operator:',
	'no-obj-calls' : 'Disallow global object as function calls:',
	'no-eq-null' : 'Disallow null comparisons:',
	'noeqnullFixName': 'Update operator',
	'no-else-return' : 'Unnecessary else after return:',
	'no-empty-label' : 'No empty labels:',
	'no-self-compare' : 'Disallow self compare:',
	'no-self-assign' : 'Disallow self assignment:',
	'noShadowFixName' : 'Rename in scope',
	'type-checked-consistent-return' : 'Discouraged inconsistent returns:',
	'check-tern-plugin' : 'Missing .tern-project plugins entry for environment directive:',
	'forbiddenExportImportFixName' : 'Set sourceType to module',
	'noElseReturnFixName' : 'Remove unnecessary else',
	'noImplicitCoercionFixName' : 'Remove implicit coercion',
	
	//Tern Plugins
	'versionHoverTitle': 'Version',
	'ecma5': 'The type information index for ECMAScript version 5',
	'ecma6': 'The type information index for ECMAScript version 6',
	'ecma7': 'The type information index for ECMAScript version 7',
	'browserDef': 'The type information index for the browser',
	'chai': 'The type information index for the Chai testing library',
	'dependencyBudget': 'To configure the amount of work Tern is prepared to do to load a single dependency, the dependencyBudget option can be added to a project file. The default value is 20000.\n\nThis property should be used with care.',
	'dontLoad': 'The dontLoad option can be used to prevent Tern from loading certain files. It also takes an array of file names.',
	'ecmaVersionDescription': 'You can specify an ecmaVersion field to configure the version of ECMAScript that Tern parses. The default is 6.',
	'libs': 'The libs property refers to the JSON type descriptions that should be loaded into the environment for this project.',
	'loadEagerly': 'loadEagerly allows you to force some files to always be loaded, it must be an array of filenames.',
	'plugins': 'The plugins field may hold object used to load and configure Tern plugins.',
	'commonjsPluginName': 'CommonJS',
	'commonjsPluginDescription': 'Plug-in that handles CommonJS-style dependency resolution.',
	'asyncAwaitPluginName': 'Early-access async / await support in Tern',
	'asyncAwaitPluginDescription': 'Provides early-access support for async / await in Tern prior to an official implementation.',
	'ternDocPluginName': 'Doc Comments',
	'ternDocPluginDescription': 'Tern plug-in to parse and use JSDoc-like comments for inferencing',
	'ternCompleteStringsPluginName': 'String Completion',
	'ternCompleteStringsPluginDescription': 'Tern plug-in to provide assist for previously seen strings.',
	'orionWebpackPluginName': 'Webpack',
	'orionWebpackPluginDescription': 'Tern plug-in that handles Webpack module resolution.',
	'orionAMQPPluginName': 'Orion AMQP',
	'orionAMQPPluginDescription': 'Plug-in that contributes type information and code templates for AMQP.',
	'orionAngularPluginName': 'AngularJS',
	'orionAngularPluginDescription': 'Plug-in that contributes type information and code templates for AngularJS.',
	'orionComponentPluginName': 'ComponentJS',
	'orionComponentPluginDescription': 'Plug-in that contributes type information and code templates for ComponentJS.',
	'orionExpressPluginName': 'Orion ExpressJS',
	'orionExpressPluginDescription': 'Plug-in that contributes type information and code templates for ExpressJS.',
	'orionESModulesPluginName': 'Orion ES_Modules',
	'orionESModulesPluginDescription': 'Plug-in that contributes type information and code templates for ES Modules.',
	'orionMongoDBPluginName': 'Orion MongoDB',
	'orionMongoDBPluginDescription': 'Plug-in that contributes type information and code templates for MongoDB.',
	'orionMySQLPluginName': 'Orion MySQL',
	'orionMySQLPluginDescription': 'Plug-in that contributes type information and code templates for MySQL.',
	'orionNodePluginName': 'Orion Node.js',
	'orionNodePluginDescription': 'Plug-in that contributes type information and code templates for Node.js.',
	'orionPostgresPluginName': 'Orion PostgreSQL',
	'orionPostgresPluginDescription': 'Plug-in that contributes type information and code templates for PostgreSQL.',
	'orionRequirePluginName': 'Orion RequireJS',
	'orionRequirePluginDescription': 'Plug-in that contributes type information and code templates for RequireJS.',
	'orionRedisPluginName': 'Orion Redis',
	'orionRedisPluginDescription': 'Plug-in that contributes type information and code templates for Redis.',
	'ternPluginsPluginName': 'Orion Tern Plug-in Support',
	'ternPluginsPluginDescription': 'Plug-in that allows Orion to inspect and modify plug-ins running in Tern.',
	'occurrencesPluginName': 'Orion Occurrences Support',
	'occurrencesPluginDescription': 'Plug-in that allows Orion to highlight the occurrenes of an identifier in a file',
	'openImplPluginName': 'Orion Open Implementation Support',
	'openImplPluginDescription': 'Plug-in that allows Orion to try to find implementation locations of elements rather than simple declarations',
	'htmlDepPluginName': 'Orion HTML Dependency Analysis',
	'htmlDepPluginDescription': 'Resolves script block and script tag dependencies',
	'findTypesName': 'Orion References Support',
	'findTypesDescription': 'Plug-in that provides expanded type-finding support in Orion',
	'eslintPluginName': 'ESLint plugin for Tern',
	'eslintPluginDescription': 'Provides ESLint support for Tern',
	'jsdocPluginName': 'JSDoc types and completion support',
	'jsdocPluginDescription': 'Provides auto-complete and type information for JSDoc',
	'outlinerPluginName': 'JavaScript outlining',
	'outlinerPluginDescription': 'Provides JavaScript outlining',
	"fixesPluginName": "JavaScript quick fixes",
	"fixesPluginDescription": "Provides quick fixes for Orion ESLint problems",
	'astPluginName': 'AST (acorn) plugin for Tern',
	'astPluginDescription': 'Provides AST (acorn) for Tern',
	'templatesPlugin': 'Orion code templates',
	'templatesPluginDescription': 'Provides a variety of code templates for JavaScript in Orion.',
	'beautifierPluginName' : 'JSBeautify plugin for Tern',
	'beautifierPluginDescription' : 'Provides JS formatting for Tern',
	'resolverPluginName' : 'Resolver plugin for Tern',
	'resolverPluginDescription' : 'Resolves Orion file paths for other Tern plugins.',
	
	// Tern indexes
	'browser': 'Browser global variables.',
	'node': 'Node.js global variables and Node.js scoping.',
	'commonjs': 'CommonJS global variables and CommonJS scoping (use this for browser-only code that uses Browserify/WebPack).',
	'worker': 'Web workers global variables.',
	'amd': 'Defines require() and define() as global variables as per the amd spec.',
	'mocha': 'Adds all of the Mocha testing global variables.',
	'jasmine': 'Adds all of the Jasmine testing global variables for version 1.3 and 2.0.',
	'jest': 'Jest global variables.',
	'phantomjs': 'PhantomJS global variables.',
	'protractor': 'Protractor global variables.',
	'qunit': 'QUnit global variables.',
	'jquery': 'jQuery global variables.',
	'prototypejs': 'Prototype.js global variables.',
	'shelljs': 'ShellJS global variables.',
	'meteor': 'Meteor global variables.',
	'mongo': 'MongoDB global variables.',
	'applescript': 'AppleScript global variables.',
	'nashorn': 'Java 8 Nashorn global variables.',
	'serviceworker': 'Service Worker global variables.',
	'embertest': 'Ember test helper globals.',
	'webextensions': 'WebExtensions globals.',
	'es6': 'Enable all ECMAScript 6 features except for modules.',
	
	// JSDoc types
	'jsDocEmptyObjDesc': ' - Empty object',
	'jsDocEmptyObjDoc': 'Represents an object wrapper with no known properties.',
	'jsDocObjPropDesc': ' - Object with a specific property',
	'jsDocObjPropDoc': 'Represents an object wrapper with one or more known properties.',
	'jsDocAnyTypeDesc': ' - No type information',
	'jsDocAnyTypeDoc': 'Represents that no type information is known. The type is described as the \'any\' type.',
	
	// JS formatting settings
	'javascriptFormatter' : 'Javascript Formatter',
	'javascriptFormattingSettings' : 'Formatting Settings for Javascript',
	'javascriptFormatting' : 'Javascript Formatting',
	'js_indent_size' : 'Indention size:',
	'js_indent_char' : 'Indentation character:',
	'js_eol' : 'Character(s) to use as line terminators:',
	'js_end_with_newline' : 'End output with newline:',
	'indentation_unix' : 'Unix',
	'indentation_mac' : 'Mac',
	'indentation_windows' : 'Windows',
	'indentation_space' : 'space',
	'indentation_tab' : 'tab',
	'jsFormattingSettings' : 'Formatting Settings for Javascript',
	'indent_level': 'Initial indentation level:',
	'before_newline' : 'Before new line',
	'after_newline' : 'After new line',
	'preserve_newline' : 'Preserve new line',
	'collapse_preserve_inline' : 'Collapse Preserve inline',
	'collapse' : 'Collapse',
	'expand' : 'Expand',
	'end_expand' : 'End expand',
	'js_preserve_newlines' : 'Preserve line-breaks:',
	'js_max_preserve_newlines' : 'Number of line-breaks to be preserved in one chunk:',
	'space_in_paren' : 'Add padding space within paren:',
	'space_in_empty_paren' : 'Add padding space in empty paren:',
	'space_after_anon_function' : "Add a space before an anonymous function's parens",
	'js_brace_style' : 'Brace Style:',
	'break_chained_methods' : 'Break chained method calls across subsequent lines:',
	'keep_array_indentation' : 'Preserve array indentation:',
	'space_before_conditional' : 'Space before condition:',
	'unescape_strings' : 'Decode printable characters encoded in xNN notation:',
	'js_wrap_line_length' : 'Wrap lines at next opportunity after N characters (0 for unlimited):',
	'e4x' : 'Pass E4X xml literal through untouched:',
	'comma_first' : 'Put commas at the beginning of new line instead of end:',
	'operator_position' : 'Position for operator:',

	// Other messages
	'unknownError': 'An unknown error occurred.',
	'failedDeleteRequest': 'Failed to delete file from Tern: ${0}',
	'failedReadRequest': 'Failed to read file into Tern: ${0}',
	'failedToComputeProposals': 'Failed to compute proposals',
	'failedToComputeProposalsNoServer': 'Failed to compute proposals, server not started',
	'failedToComputeDecl': 'Failed to compute declaration',
	'failedToComputeDeclNoServer': 'Failed to compute declaration, server not started',
	'failedToComputeImpl': 'Failed to compute implementation',
	'failedToComputeImplNoServer': 'Failed to compute implementation, server not started',
	'failedToComputeDoc': 'Failed to compute documentation',
	'failedToComputeDocNoServer': 'Failed to compute documentation, server not started',
	'failedToComputeOccurrences': 'Failed to compute occurrences',
	'failedToComputeOccurrencesNoServer': 'failed to compute occurrences, server not started',
	'failedGetInstalledPlugins': 'Failed to get installed plug-ins',
	'failedGetInstalledPluginsNoServer': 'Failed to get installed plug-ins, server not started',
	'failedGetInstalledDefs': 'Failed to get installed Tern definitions',
	'failedGetInstalledDefsNoServer': 'Failed to get installed Tern definitions, server not started',
	'failedInstallPlugins': 'Failed to install plug-ins',
	'failedInstallPluginsNoServer': 'Failed to install plug-ins, server not started',
	'failedRemovePlugins': 'Failed to remove plug-ins',
	'failedRemovePluginsNoServer': 'Failed to remove plug-ins, server not started',
	'failedEnablementPlugins': 'Failed to set enablement of plug-ins',
	'failedEnablementPluginsNoServer': 'Failed to set enablement of plug-ins, server not started',
	'failedGetEnvs': 'Failed to get contributed environments',
	'failedGetEnvsNoServer': 'Failed to get contributed environments, server not started',
	'failedRenameTern': 'Failed to compute rename changes',
	'failedRenameNoServer': 'Failed to compute rename changes, server not started',
	'failedRefs': 'Failed to find references',
	'failedRefsNoServer': 'failed to find references - server not started',
	'failedType': 'Failed to find type',
	'failedQuickfixesNoServer': 'Failed to compute quick fixes, server not started',
	'unknownRequest': 'The request \'${0}\' is unknown',
	'serverNotStarted': 'The server has not been started. Request: \'${0}\'',
	'failedToComputeProblems': 'Failed to compute ESLint problems/markers',
	'failedToComputeOutline': 'Failed to compute outline',
	'failedToFormat' : 'Failed to format',
	'failedToFormatNoServer' : 'Failed to format, server not started',
	
	//Templates
	'eslintRuleEnableDisable': 'Enable or disable ESLint rule using the ```ruleid:0/1/2``` form.\n\nExample use:\n\n>```/* eslint semi:1, no-console:0, no-redeclare:2 */```',
	'eslintEnvDirective': 'Specify which environments are used in this JavaScript file.\n\nExample use:\n\n>```/* eslint-env amd, node, broswer */```',
	'eslintRuleEnable': 'Enable a given set of ESLint rules.\n\nExample use:\n\n>```/* eslint-enable semi, no-console, no-redeclare */```',
	'eslintRuleDisable': 'Disable a given set of ESLint rules.\n\nExample use:\n\n>```/* eslint-disable semi, no-console, no-redeclare */```',
	'arrowFunc': 'Create a simple arrow function expression.',
	'arrowFuncObj': 'Create a simple arrow function expression that returns an object.',
	'importSimpleDefault': 'Create an import statement that imports the default value from a module.',
	'importStarAs': 'Create an import statement that imports an entire module as a given name.',
	'importMultiMember': 'Create an import statement that imports multiple named members.',
	'importSideEffects': 'Create an import statment that imports an entire module for side effects only',
	'letSimple': 'Create a new let statement.',
	'exportSimple': 'Create a new export statement.',
	'exportDefault': 'Create a new export statement that exports the value as the default for the module.',
	'forOf': 'Create a new for..of statement.',
	'constSimple': 'Create a new const statement.',
	'funcGenerator': 'Create a new generator function.',
	'yieldSimple': 'Create a new yield statement.',
	'yieldSimpleReturn': 'Create a new yield statement that returns the optional value passed to the generator\'s next() method.',
	'yieldDelegate': 'Create a new yield* statement that is used to delegate to another generator.',
	'asyncFunction': 'Define an asynchronous function declaration.',
	'asyncFunctionExpression': 'Create an async function expression and assign it to a variable.',
	'awaitExpression': 'The await operator is used to wait for a Promise. It can only be used inside an async function.',
	'ifSimple': 'Create a new if statement.',
	'ifElseSimple': 'Create a new if..else statement.',
	'forArray': 'Create a new for loop that iterates over an array.',
	'forArrayVar': 'Create a new for loop that iterates over an array, with a local variable.',
	'forInSimple': 'Create a for..in loop that iterates the properties of an object.',
	'whileSimple': 'Create a while loop.',
	'doSimple': 'Create a do..while loop.',
	'switchSimple': 'Create a switch statement with a case and a default case.',
	'tryCatch': 'Create a new try..catch statement.',
	'tryCatchFinally': 'Create a new try..catch..finally statement.',
	'typeofSimple': 'Create a new typeof expression.',
	'instanceofSimple': 'Create a new instanceof expression.',
	'withSimple': 'Create a new with statement.\n\n```Use of the with statement is not recommended, as it may be the source of confusing bugs and compatibility issues```.',
	'functionSimple': 'Create a new function declaration.',
	'functionProp': 'Create a new object property that is set to a function expression.',
	'defineSimple': 'Create a new AMD define function call.',
	'nlsSimple': 'Create a new //$NON-NLS-$ comment.',
	'logSimple': 'Create a new console.log expression.\n\n```This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.```',
	'requireSimple': 'Create a Node.js require statement and assign it to a local variable.',
	'caseSimple': 'Create a new case statement.',
	'classSimple': 'Create a new JavaScript class that extends another class and has a constructor.',
	'classExpr': 'Create a new JavaScript class expression that extends another class and has a constructor.',
	'amqpRequire':'Create a new Node.js require statement to import the AMQP framework.',
	'amqpConnection': 'Create a new AMQP connection.',
	'amqpOn': 'Create a new AMQP connection on call.',
	'amqpQueue': 'Create a new AMQP connection queue.',
	'amqpExchange': 'Create a new AMQP connection exchange.',
	'expressRequire': 'Create a new Node.js require statement to import Express.',
	'expressInstance': 'Create a new Express app instance.',
	'expressGet': 'Create a new Express app.get call.',
	'expressSet': 'Create a new Express app.set call.',
	'expressUse': 'Create a new Express app.use call.',
	'expressEngine': 'Create a new Express app.engine call.',
	'expressParam': 'Create a new Express app.param call.',
	'expressUseError': 'create a new Express app.use call with error handling.',
	'mongodbRequire': 'Create a Node.js require statement to import MongoDB.',
	'mongodbClient': 'Create a new MongoDB client.',
	'mongodbOpen': 'Create a new MongoDB client and open a connection.',
	'mongodbConnect': 'Connect to an existing MongoDB database.',
	'mongodbConnectCF': 'Connect to an existing MongoDB database using Cloud Foundry.',
	'mongodbCollection': 'Create a MongoDB database collection.',
	'mongodbStrictCollection': 'Create a MongoDB database strict collection.',
	'mysqlRequire': 'Create a Node.js require statement to import MySQL DB.',
	'mysqlConnection': 'Create a new MySQL DB connection.',
	'mysqlQuery': 'Create a new MySQL DB query statement.',
	'postgresRequire': 'Create a Node.js require statement to import Postgres DB.',
	'postgresClient': 'Create a new Postgres DB client.',
	'postgresConnect': 'Create a new Postgres DB client and connect.',
	'postgresQuery': 'Create a new Postgres DB query statement.',
	'redisRequire': 'Create a Node.js require statement to import Redis.',
	'redisClient': 'Create a new Redis client.',
	'redisConnect': 'Create a new Redis client and connect.',
	'redisSet': 'Create a new Redis client set call.',
	'redisGet': 'Create a new Redis client get call.',
	'redisOn': 'Create a new Redis client event handler.',

//Package.json metadata
	'packageNameDoc': 'The name is what your package is called.\n\n'+
					 'Some rules:\n'+
					 '* The name must be less than or equal to 214 characters. This includes the scope for scoped packages.\n'+
					 '* The name can\'t start with a dot or an underscore.'+
					 '* New packages must not have uppercase letters in the name.'+
					 '* The name ends up being part of a URL, an argument on the command line, and a folder name. Therefore, the name can\'t contain any non-URL-safe characters.',
	'packageVersionDoc': 'The version of your package. The version must be parseable by [node-semver](https://github.com/isaacs/node-semver).',
	'packageDescriptionDoc': 'The strig description of the package.',
	'packageKeywordsDoc': 'An array of strings that allows people discover your package as it\'s listed in npm search.',
	'packageHomepageDoc': 'The url to the project homepage.',
	'packageBugsDoc': 'The url to your project\'s issue tracker and / or the email address to which issues should be reported. These are helpful for people who encounter issues with your package.',
	'packageLicenseDoc': 'The license for your package. The license should be a [valid SPDX license idenifier](https://spdx.org/licenses/).',
	'packageAuthorDoc': 'The author of the package is one person. A person can be an object with "name" and optionally "url" and "email" fields, or a single string the NPM will parse.',
	'packageContributorsDoc': 'The contributors to a package is an array of persons. A person can be an object with "name" and optionally "url" and "email" fields, or a single string the NPM will parse.',
	'packageFilesDoc': 'This is an array of files to include in your project. If you name a folder in the array, then it will also include the files inside that folder.',
	'packageMainDoc': 'The main field is a module ID that is the primary entry point to your program.',
	'packageBinDoc': 'A string to an executable, or an object that maps names to executables to link on your PATH during NPM install.',
	'packageManDoc': 'Specify either a single file or an array of filenames to put in place for the man program to find.',
	'packageDirectoriesDoc': 'The CommonJS Packages spec details a few ways that you can indicate the structure of your package using a directories object. If you look at npm\'s package.json, you\'ll see that it has directories for doc, lib, and man.',
	'packageDirLibDoc': 'Tell people where the bulk of your library is. Nothing special is done with the lib folder in any way, but it\'s useful meta info.',
	'packageDirBinDoc': 'If you specify a bin directory in directories.bin, all the files in that folder will be added.',
	'packageDirManDoc': 'A folder that is full of man pages. Sugar to generate a "man" array by walking the folder.',
	'packageDirDocDoc': 'Put markdown files in here. Eventually, these will be displayed nicely, maybe, someday',
	'packageDirExamplesDoc': 'Put example scripts in here. Someday, it might be exposed in some clever way.',
	'packageDirTestDoc': 'Put your tests in here. It is currently not exposed, but it might be in the future.',
	'packageRepositoryDoc': 'The object that contains the type and URL for where your code lives.',
	'packageScriptsDoc': 'The "scripts" property is a dictionary containing script commands that are run at various times in the lifecycle of your package. The key is the lifecycle event, and the value is the command to run at that point.\n\n'+
						 'See [npm-scripts](https://docs.npmjs.com/misc/scripts) to find out more about writing package scripts.',
	'packageConfigDoc': 'A "config" object can be used to set configuration parameters used in package scripts that persist across upgrades.',
	'packageDependenciesDoc': 'Dependencies are specified in a simple object that maps a package name to a version range. The version range is a string which has one or more space-separated descriptors. Dependencies can also be identified with a tarball or git URL.',
	'packageDevDepsDoc': 'Optional dependencies that are required only at development time.',
	'packagePeerDepsDoc': 'Optional dependencies to express a relationship with another package of host tool without necessarily requiring it.',
	'packageBundledDepsDoc': 'This defines an array of package names that will be bundled when publishing the package.',
	'packageOptionalDepsDoc': 'Optional dependencies that can be ignored if not found when installing.',
	'packageEnginesDoc': 'The version of engines that are required to run your package.',
	'packageEngineStrictDoc': 'This feature was removed in npm 3.0.0\n\nPrior to npm 3.0.0, this feature was used to treat this package as if the user had set engine-strict. It is no longer used.',
	'packageOsDoc': 'Allows you to specify which operating systems your package will run on.',
	'packageCpuDoc': 'Allows you to specify which CPU architecture your package runs on.',
	'packagePreferGlobalDoc': 'If your package is primarily a command-line application that should be installed globally, then set this value to true to provide a warning if it is installed locally.',
	'packagePrivateDoc': 'Allows you to tell NPM to not publish your package publically (when set to true).',
	'packagePublishConfigDoc': 'This is a set of config values that will be used at publish-time.',
	'packageEslintConfigDoc': 'This allows you to provide ESLint configuration options.',
//ESlint metadata
	'envDoc': 'Allows you to tell ESLint which environments are in use in your code.',
	'rulesDoc': 'Allows you to configure rules to apply to all source files in your project (rather than in-file or using the workspace preferences).',
	'eslintPluginsDoc': 'Allows you to specify plugins to load into ESLint (currently not supported in Orion).',
	'eslintParserOptionsDoc': 'Allows you to specify options to be passed into the parser.',
	'eslintParserDoc': 'Allows you to specify a parser and options to use in ESLint.\n\nOrion does not support using alternative parsers, only Acorn is used.',
	'eslintGlobalsDoc': 'Allows you to specify variable names that are part of or exported to the global scope. Doing so will avoid marking them as unused or unknown.',
	'eslintSettingsDoc': 'Allows you to provide additional information to passed in to each rule as as they are executed.',
	'eslintExtendsDoc': 'Allows to specify another configuration file that this one will extend. You can also extend a default ESLint configuration.\n\nThis is currently not supported in Orion.',
	'eslintObjectSpread': 'Enable support for the experimental object rest/spread properties (IMPORTANT: This is an experimental feature that may change significantly in the future.',
	'eslintGlobalReturn': 'Allow return statements in the global scope.',
	'eslintImpliedStrict': 'Enable strict mode globally. Only available for ecmaVersion greater than or equal to 5.',
	'eslintJSX': 'Enable JSX support.\n\nJSX support is currently not available in Orion.',
	'eslintEcmaVersion': 'Allows you to set the ECMA version the parser will parse at.',
	'eslintSourceType': 'Allows you to set the type of the source. Can be one of "module" or "script".',
	
});


/*******************************************************************************
 * @license
 * Copyright (c) 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0
 * (https://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
 * License v1.0 (https://www.eclipse.org/org/documents/edl-v10.html).
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
define('javascript/plugins/ternMetadata',[
	'i18n!javascript/nls/messages',
], function(Messages) {

	return {
		defNames: ["ecma5", "ecma6", "ecma7", "browser", "chai"], //these are in the same order to avoid a walk of the array
		definitions: Object.freeze({
			ecma5: {
				doc: Messages["ecma5"],
				url: "https://ternjs.net/doc/manual.html#typedef"
			},
			ecma6: {
				doc: Messages["ecma6"],
				url: "https://ternjs.net/doc/manual.html#typedef"
			},
			ecma7: {
				doc: Messages["ecma7"],
				url: "https://ternjs.net/doc/manual.html#typedef"
			},
			browser: {
				doc: Messages["browserDef"],
				url: "https://ternjs.net/doc/manual.html#typedef"
			},
			chai: {
				doc: Messages["chai"],
				url: "https://ternjs.net/doc/manual.html#typedef"
			}
		}),
		attributes: Object.freeze({
			dependencyBudget: {
				doc: Messages["dependencyBudget"],
				url: "https://ternjs.net/doc/manual.html#configuration"
			},
			dontLoad: {
				doc: Messages["dontLoad"],
				url: "https://ternjs.net/doc/manual.html#configuration"
			},
			ecmaVersion: {
				doc: Messages["ecmaVersionDescription"],
				url: "https://ternjs.net/doc/manual.html#configuration"
			},
			libs: {
				doc: Messages["libs"],
				url: "https://ternjs.net/doc/manual.html#configuration"
			},
			loadEagerly: {
				doc: Messages["loadEagerly"],
				url: "https://ternjs.net/doc/manual.html#configuration"
			},
			plugins: {
				doc: Messages["plugins"],
				url: "https://ternjs.net/doc/manual.html#configuration"
			}
		}),
		plugins: Object.freeze({
			required: {
				async_await: {
					name: Messages.asyncAwaitPluginName,
					description: Messages.asyncAwaitPluginDescription,
					version: "1.0.0"
				},
				doc_comment: {
					name: Messages["ternDocPluginName"],
					description: Messages["ternDocPluginDescription"],
					url: "https://ternjs.net/doc/manual.html#plugin_doc_comment",
					fullDocs: true,
					version: "0.18.0"
				},
				plugins: {
					name: Messages["ternPluginsPluginName"],
					description: Messages["ternPluginsPluginDescription"],
					version: "1.0"
				},
				occurrences: {
					name: Messages["occurrencesPluginName"],
					description: Messages["occurrencesPluginDescription"],
					version: "1.0"
				},
				open_impl: {
					name: Messages["openImplPluginName"],
					description: Messages["openImplPluginDescription"],
					version: "1.0"
				},
				html: {
					name: Messages["htmlDepPluginName"],
					description: Messages["htmlDepPluginDescription"],
					version: "1.0"
				},
				refs: {
					name: Messages["findTypesName"],
					description: Messages["findTypesDescription"],
					version: "1.0"
				},
				jsdoc: {
					name: Messages["jsdocPluginName"],
					description: Messages["jsdocPluginDescription"],
					version: "1.0"
				},
				eslint: {
					name: Messages["eslintPluginName"],
					description: Messages["eslintPluginDescription"],
					version: "1.0"
				},
				outliner: {
					name: Messages["outlinerPluginName"],
					description: Messages["outlinerPluginDescription"],
					version: "1.0"
				},
				fixes: {
					name: Messages["fixesPluginName"],
					description: Messages["fixesPluginDescription"],
					version: "1.0"
				},
				ast: {
					name: Messages["astPluginName"],
					description: Messages["astPluginDescription"],
					version: "1.0"
				},
				templates: {
					name: Messages["templatesPlugin"],
					description: Messages["templatesPluginDescription"],
					version: "1.0"
				},
				beautifier: {
					name: Messages["beautifierPluginName"],
					description: Messages["beautifierPluginDescription"],
					version: "1.0"
				},
				resolver: {
					name: Messages["resolverPluginName"],
					description: Messages["resolverPluginDescription"],
					version: "1.0"
				},
			},
			optional: {
				amqp: {
					name: Messages["orionAMQPPluginName"],
					description: Messages["orionAMQPPluginDescription"],
					version: "0.9.1",
					env: "amqp"
				},
				angular: {
					name: Messages["orionAngularPluginName"],
					description: Messages["orionAngularPluginDescription"],
					url: "https://ternjs.net/doc/manual.html#plugin_angular",
					version: "0.18.0"
				},
				commonjs: {
					name: Messages['commonjsPluginName'],
					description: Messages['commonjsPluginDescription'],
					url: "https://ternjs.net/doc/manual.html#plugin_commonjs",
					version: "0.18.0"
				},
				complete_strings: {
					name: Messages['ternCompleteStringsPluginName'],
					description: Messages['ternCompleteStringsPluginDescription'],
					url: "https://ternjs.net/doc/manual.html#plugin_complete_strings",
					version: "0.18.0"
				},
				express: {
					name: Messages["orionExpressPluginName"],
					description: Messages["orionExpressPluginDescription"],
					version: "4.12.4",
					env: "express"
				},
				es_modules: {
					name: Messages["orionESModulesPluginName"],
					description: Messages["orionESModulesPluginDescription"],
					url: "https://ternjs.net/doc/manual.html#plugin_es_modules",
					version: "0.18.0",
				},
				mongodb: {
					name: Messages["orionMongoDBPluginName"],
					description: Messages["orionMongoDBPluginDescription"],
					version: "1.1.21",
					env: "mongodb"
				},
				mysql: {
					name: Messages["orionMySQLPluginName"],
					description: Messages["orionMySQLPluginDescription"],
					version: "2.7.0",
					env: "mysql"
				},
				node: {
					name: Messages["orionNodePluginName"],
					description: Messages["orionNodePluginDescription"],
					url: "https://ternjs.net/doc/manual.html#plugin_node",
					version: "0.18.0"
				},
				postgres: {
					name: Messages["orionPostgresPluginName"],
					description: Messages["orionPostgresPluginDescription"],
					version: "4.4.0",
					env: "pg"
				},
				redis: {
					name: Messages["orionRedisPluginName"],
					description: Messages["orionRedisPluginDescription"],
					version: "0.12.1",
					env: "redis"
				},
				requirejs: {
					name: Messages["orionRequirePluginName"],
					description: Messages["orionRequirePluginDescription"],
					url: "https://ternjs.net/doc/manual.html#plugin_requirejs",
					version: "0.18.0"
				},
				webpack: {
					name: Messages["orionWebpackPluginName"],
					description: Messages["orionWebpackPluginDescription"],
					url: "https://ternjs.net/doc/manual.html#plugin_webpack",
					version: "0.18.0"
				}
			}
		})
	};
});
/**
 * @license text 2.0.15 Copyright jQuery Foundation and other contributors.
 * Released under MIT license, http://github.com/requirejs/text/LICENSE
 */
/*jslint regexp: true */
/*global require, XMLHttpRequest, ActiveXObject,
  define, window, process, Packages,
  java, location, Components, FileUtils */

define('requirejs/text',['module'], function (module) {
    'use strict';

    var text, fs, Cc, Ci, xpcIsWindows,
        progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
        xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
        bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,
        hasLocation = typeof location !== 'undefined' && location.href,
        defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''),
        defaultHostName = hasLocation && location.hostname,
        defaultPort = hasLocation && (location.port || undefined),
        buildMap = {},
        masterConfig = (module.config && module.config()) || {};

    function useDefault(value, defaultValue) {
        return value === undefined || value === '' ? defaultValue : value;
    }

    //Allow for default ports for http and https.
    function isSamePort(protocol1, port1, protocol2, port2) {
        if (port1 === port2) {
            return true;
        } else if (protocol1 === protocol2) {
            if (protocol1 === 'http') {
                return useDefault(port1, '80') === useDefault(port2, '80');
            } else if (protocol1 === 'https') {
                return useDefault(port1, '443') === useDefault(port2, '443');
            }
        }
        return false;
    }

    text = {
        version: '2.0.15',

        strip: function (content) {
            //Strips <?xml ...?> declarations so that external SVG and XML
            //documents can be added to a document without worry. Also, if the string
            //is an HTML document, only the part inside the body tag is returned.
            if (content) {
                content = content.replace(xmlRegExp, "");
                var matches = content.match(bodyRegExp);
                if (matches) {
                    content = matches[1];
                }
            } else {
                content = "";
            }
            return content;
        },

        jsEscape: function (content) {
            return content.replace(/(['\\])/g, '\\$1')
                .replace(/[\f]/g, "\\f")
                .replace(/[\b]/g, "\\b")
                .replace(/[\n]/g, "\\n")
                .replace(/[\t]/g, "\\t")
                .replace(/[\r]/g, "\\r")
                .replace(/[\u2028]/g, "\\u2028")
                .replace(/[\u2029]/g, "\\u2029");
        },

        createXhr: masterConfig.createXhr || function () {
            //Would love to dump the ActiveX crap in here. Need IE 6 to die first.
            var xhr, i, progId;
            if (typeof XMLHttpRequest !== "undefined") {
                return new XMLHttpRequest();
            } else if (typeof ActiveXObject !== "undefined") {
                for (i = 0; i < 3; i += 1) {
                    progId = progIds[i];
                    try {
                        xhr = new ActiveXObject(progId);
                    } catch (e) {}

                    if (xhr) {
                        progIds = [progId];  // so faster next time
                        break;
                    }
                }
            }

            return xhr;
        },

        /**
         * Parses a resource name into its component parts. Resource names
         * look like: module/name.ext!strip, where the !strip part is
         * optional.
         * @param {String} name the resource name
         * @returns {Object} with properties "moduleName", "ext" and "strip"
         * where strip is a boolean.
         */
        parseName: function (name) {
            var modName, ext, temp,
                strip = false,
                index = name.lastIndexOf("."),
                isRelative = name.indexOf('./') === 0 ||
                             name.indexOf('../') === 0;

            if (index !== -1 && (!isRelative || index > 1)) {
                modName = name.substring(0, index);
                ext = name.substring(index + 1);
            } else {
                modName = name;
            }

            temp = ext || modName;
            index = temp.indexOf("!");
            if (index !== -1) {
                //Pull off the strip arg.
                strip = temp.substring(index + 1) === "strip";
                temp = temp.substring(0, index);
                if (ext) {
                    ext = temp;
                } else {
                    modName = temp;
                }
            }

            return {
                moduleName: modName,
                ext: ext,
                strip: strip
            };
        },

        xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/,

        /**
         * Is an URL on another domain. Only works for browser use, returns
         * false in non-browser environments. Only used to know if an
         * optimized .js version of a text resource should be loaded
         * instead.
         * @param {String} url
         * @returns Boolean
         */
        useXhr: function (url, protocol, hostname, port) {
            var uProtocol, uHostName, uPort,
                match = text.xdRegExp.exec(url);
            if (!match) {
                return true;
            }
            uProtocol = match[2];
            uHostName = match[3];

            uHostName = uHostName.split(':');
            uPort = uHostName[1];
            uHostName = uHostName[0];

            return (!uProtocol || uProtocol === protocol) &&
                   (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) &&
                   ((!uPort && !uHostName) || isSamePort(uProtocol, uPort, protocol, port));
        },

        finishLoad: function (name, strip, content, onLoad) {
            content = strip ? text.strip(content) : content;
            if (masterConfig.isBuild) {
                buildMap[name] = content;
            }
            onLoad(content);
        },

        load: function (name, req, onLoad, config) {
            //Name has format: some.module.filext!strip
            //The strip part is optional.
            //if strip is present, then that means only get the string contents
            //inside a body tag in an HTML string. For XML/SVG content it means
            //removing the <?xml ...?> declarations so the content can be inserted
            //into the current doc without problems.

            // Do not bother with the work if a build and text will
            // not be inlined.
            if (config && config.isBuild && !config.inlineText) {
                onLoad();
                return;
            }

            masterConfig.isBuild = config && config.isBuild;

            var parsed = text.parseName(name),
                nonStripName = parsed.moduleName +
                    (parsed.ext ? '.' + parsed.ext : ''),
                url = req.toUrl(nonStripName),
                useXhr = (masterConfig.useXhr) ||
                         text.useXhr;

            // Do not load if it is an empty: url
            if (url.indexOf('empty:') === 0) {
                onLoad();
                return;
            }

            //Load the text. Use XHR if possible and in a browser.
            if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) {
                text.get(url, function (content) {
                    text.finishLoad(name, parsed.strip, content, onLoad);
                }, function (err) {
                    if (onLoad.error) {
                        onLoad.error(err);
                    }
                });
            } else {
                //Need to fetch the resource across domains. Assume
                //the resource has been optimized into a JS module. Fetch
                //by the module name + extension, but do not include the
                //!strip part to avoid file system issues.
                req([nonStripName], function (content) {
                    text.finishLoad(parsed.moduleName + '.' + parsed.ext,
                                    parsed.strip, content, onLoad);
                });
            }
        },

        write: function (pluginName, moduleName, write, config) {
            if (buildMap.hasOwnProperty(moduleName)) {
                var content = text.jsEscape(buildMap[moduleName]);
                write.asModule(pluginName + "!" + moduleName,
                               "define(function () { return '" +
                                   content +
                               "';});\n");
            }
        },

        writeFile: function (pluginName, moduleName, req, write, config) {
            var parsed = text.parseName(moduleName),
                extPart = parsed.ext ? '.' + parsed.ext : '',
                nonStripName = parsed.moduleName + extPart,
                //Use a '.js' file name so that it indicates it is a
                //script that can be loaded across domains.
                fileName = req.toUrl(parsed.moduleName + extPart) + '.js';

            //Leverage own load() method to load plugin value, but only
            //write out values that do not have the strip argument,
            //to avoid any potential issues with ! in file names.
            text.load(nonStripName, req, function (value) {
                //Use own write() method to construct full module value.
                //But need to create shell that translates writeFile's
                //write() to the right interface.
                var textWrite = function (contents) {
                    return write(fileName, contents);
                };
                textWrite.asModule = function (moduleName, contents) {
                    return write.asModule(moduleName, fileName, contents);
                };

                text.write(pluginName, nonStripName, textWrite, config);
            }, config);
        }
    };

    if (masterConfig.env === 'node' || (!masterConfig.env &&
            typeof process !== "undefined" &&
            process.versions &&
            !!process.versions.node &&
            !process.versions['node-webkit'] &&
            !process.versions['atom-shell'])) {
        //Using special require.nodeRequire, something added by r.js.
        fs = require.nodeRequire('fs');

        text.get = function (url, callback, errback) {
            try {
                var file = fs.readFileSync(url, 'utf8');
                //Remove BOM (Byte Mark Order) from utf8 files if it is there.
                if (file[0] === '\uFEFF') {
                    file = file.substring(1);
                }
                callback(file);
            } catch (e) {
                if (errback) {
                    errback(e);
                }
            }
        };
    } else if (masterConfig.env === 'xhr' || (!masterConfig.env &&
            text.createXhr())) {
        text.get = function (url, callback, errback, headers) {
            var xhr = text.createXhr(), header;
            xhr.open('GET', url, true);

            //Allow plugins direct access to xhr headers
            if (headers) {
                for (header in headers) {
                    if (headers.hasOwnProperty(header)) {
                        xhr.setRequestHeader(header.toLowerCase(), headers[header]);
                    }
                }
            }

            //Allow overrides specified in config
            if (masterConfig.onXhr) {
                masterConfig.onXhr(xhr, url);
            }

            xhr.onreadystatechange = function (evt) {
                var status, err;
                //Do not explicitly handle errors, those should be
                //visible via console output in the browser.
                if (xhr.readyState === 4) {
                    status = xhr.status || 0;
                    if (status > 399 && status < 600) {
                        //An http 4xx or 5xx error. Signal an error.
                        err = new Error(url + ' HTTP status: ' + status);
                        err.xhr = xhr;
                        if (errback) {
                            errback(err);
                        }
                    } else {
                        callback(xhr.responseText);
                    }

                    if (masterConfig.onXhrComplete) {
                        masterConfig.onXhrComplete(xhr, url);
                    }
                }
            };
            xhr.send(null);
        };
    } else if (masterConfig.env === 'rhino' || (!masterConfig.env &&
            typeof Packages !== 'undefined' && typeof java !== 'undefined')) {
        //Why Java, why is this so awkward?
        text.get = function (url, callback) {
            var stringBuffer, line,
                encoding = "utf-8",
                file = new java.io.File(url),
                lineSeparator = java.lang.System.getProperty("line.separator"),
                input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)),
                content = '';
            try {
                stringBuffer = new java.lang.StringBuffer();
                line = input.readLine();

                // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
                // http://www.unicode.org/faq/utf_bom.html

                // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
                // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
                if (line && line.length() && line.charAt(0) === 0xfeff) {
                    // Eat the BOM, since we've already found the encoding on this file,
                    // and we plan to concatenating this buffer with others; the BOM should
                    // only appear at the top of a file.
                    line = line.substring(1);
                }

                if (line !== null) {
                    stringBuffer.append(line);
                }

                while ((line = input.readLine()) !== null) {
                    stringBuffer.append(lineSeparator);
                    stringBuffer.append(line);
                }
                //Make sure we return a JavaScript string and not a Java string.
                content = String(stringBuffer.toString()); //String
            } finally {
                input.close();
            }
            callback(content);
        };
    } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env &&
            typeof Components !== 'undefined' && Components.classes &&
            Components.interfaces)) {
        //Avert your gaze!
        Cc = Components.classes;
        Ci = Components.interfaces;
        Components.utils['import']('resource://gre/modules/FileUtils.jsm');
        xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc);

        text.get = function (url, callback) {
            var inStream, convertStream, fileObj,
                readData = {};

            if (xpcIsWindows) {
                url = url.replace(/\//g, '\\');
            }

            fileObj = new FileUtils.File(url);

            //XPCOM, you so crazy
            try {
                inStream = Cc['@mozilla.org/network/file-input-stream;1']
                           .createInstance(Ci.nsIFileInputStream);
                inStream.init(fileObj, 1, 0, false);

                convertStream = Cc['@mozilla.org/intl/converter-input-stream;1']
                                .createInstance(Ci.nsIConverterInputStream);
                convertStream.init(inStream, "utf-8", inStream.available(),
                Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);

                convertStream.readString(inStream.available(), readData);
                convertStream.close();
                inStream.close();
                callback(readData.value);
            } catch (e) {
                throw new Error((fileObj && fileObj.path || '') + ': ' + e);
            }
        };
    }
    return text;
});

/*******************************************************************************
 * @license
 * Copyright (c) 2015 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 * 
 * Contributors: IBM Corporation - initial API and implementation
 ******************************************************************************/
/* eslint-env amd */
define('json',[
'requirejs/text'
], function(text) {
	
	var map = Object.create(null);
	
	return {
        load : function(name, req, onLoad, config) {
    		text.get(req.toUrl(name), function(json) {
                    if (config.isBuild) {
                    	map[name] = json; //during build caching: http://requirejs.org/docs/plugins.html#apiwrite
                        onLoad(json);
                    } else {
                        try {
                            var _json = JSON.parse(json);
                        } catch (e) {
                            onLoad.error(e);
                        }
                        onLoad(_json);
                    }
                },
                onLoad.error, 
                {accept: 'application/json'} //$NON-NLS-1$
             );
		},
		write : function(pluginName, moduleName, write) {
			var _json = map[moduleName];
            if(_json) {
                write('define("'+ pluginName +'!'+ moduleName +'", function(){ return '+ _json +';});\n'); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            }
        }
	};
});

define("json!tern/defs/ecma5.json", function(){ return {
  "!name": "ecma5",
  "!define": {"Error.prototype": "Error.prototype"},
  "Infinity": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Infinity",
    "!doc": "A numeric value representing infinity."
  },
  "undefined": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/undefined",
    "!doc": "The value undefined."
  },
  "NaN": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/NaN",
    "!doc": "A value representing Not-A-Number."
  },
  "Object": {
    "!type": "fn()",
    "getPrototypeOf": {
      "!type": "fn(obj: ?) -> ?",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getPrototypeOf",
      "!doc": "Returns the prototype (i.e. the internal prototype) of the specified object."
    },
    "create": {
      "!type": "fn(proto: ?) -> !custom:Object_create",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create",
      "!doc": "Creates a new object with the specified prototype object and properties."
    },
    "defineProperty": {
      "!type": "fn(obj: ?, prop: string, desc: ?) -> !custom:Object_defineProperty",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty",
      "!doc": "Defines a new property directly on an object, or modifies an existing property on an object, and returns the object. If you want to see how to use the Object.defineProperty method with a binary-flags-like syntax, see this article."
    },
    "defineProperties": {
      "!type": "fn(obj: ?, props: ?) -> !custom:Object_defineProperties",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty",
      "!doc": "Defines a new property directly on an object, or modifies an existing property on an object, and returns the object. If you want to see how to use the Object.defineProperty method with a binary-flags-like syntax, see this article."
    },
    "getOwnPropertyDescriptor": {
      "!type": "fn(obj: ?, prop: string) -> ?",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor",
      "!doc": "Returns a property descriptor for an own property (that is, one directly present on an object, not present by dint of being along an object's prototype chain) of a given object."
    },
    "keys": {
      "!type": "fn(obj: ?) -> [string]",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys",
      "!doc": "Returns an array of a given object's own enumerable properties, in the same order as that provided by a for-in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well)."
    },
    "getOwnPropertyNames": {
      "!type": "fn(obj: ?) -> [string]",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames",
      "!doc": "Returns an array of all properties (enumerable or not) found directly upon a given object."
    },
    "seal": {
      "!type": "fn(obj: ?)",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/seal",
      "!doc": "Seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable."
    },
    "isSealed": {
      "!type": "fn(obj: ?) -> bool",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/isSealed",
      "!doc": "Determine if an object is sealed."
    },
    "freeze": {
      "!type": "fn(obj: ?) -> !0",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/freeze",
      "!doc": "Freezes an object: that is, prevents new properties from being added to it; prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object is made effectively immutable. The method returns the object being frozen."
    },
    "isFrozen": {
      "!type": "fn(obj: ?) -> bool",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/isFrozen",
      "!doc": "Determine if an object is frozen."
    },
    "preventExtensions": {
      "!type": "fn(obj: ?)",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions",
      "!doc": "Prevents new properties from ever being added to an object."
    },
    "isExtensible": {
      "!type": "fn(obj: ?) -> bool",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible",
      "!doc": "The Object.isExtensible() method determines if an object is extensible (whether it can have new properties added to it)."
    },
    "prototype": {
      "!stdProto": "Object",
      "toString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/toString",
        "!doc": "Returns a string representing the object."
      },
      "toLocaleString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/toLocaleString",
        "!doc": "Returns a string representing the object. This method is meant to be overriden by derived objects for locale-specific purposes."
      },
      "valueOf": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/valueOf",
        "!doc": "Returns the primitive value of the specified object"
      },
      "hasOwnProperty": {
        "!type": "fn(prop: string) -> bool",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/hasOwnProperty",
        "!doc": "Returns a boolean indicating whether the object has the specified property."
      },
      "propertyIsEnumerable": {
        "!type": "fn(prop: string) -> bool",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable",
        "!doc": "Returns a Boolean indicating whether the specified property is enumerable."
      },
      "isPrototypeOf": {
        "!type": "fn(obj: ?) -> bool",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf",
        "!doc": "Tests for an object in another object's prototype chain."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object",
    "!doc": "Creates an object wrapper."
  },
  "Function": {
    "!type": "fn(body: string) -> fn()",
    "prototype": {
      "!stdProto": "Function",
      "apply": {
        "!type": "fn(this: ?, args: [?])",
        "!effects": [
          "call and return !this this=!0 !1.<i> !1.<i> !1.<i>"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply",
        "!doc": "Calls a function with a given this value and arguments provided as an array (or an array like object)."
      },
      "call": {
        "!type": "fn(this: ?, args?: ?) -> !this.!ret",
        "!effects": [
          "call and return !this this=!0 !1 !2 !3 !4"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/call",
        "!doc": "Calls a function with a given this value and arguments provided individually."
      },
      "bind": {
        "!type": "fn(this: ?, args?: ?) -> !custom:Function_bind",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind",
        "!doc": "Creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function was called."
      },
      "prototype": "?"
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function",
    "!doc": "Every function in JavaScript is actually a Function object."
  },
  "Array": {
    "!type": "fn(size: number) -> !custom:Array_ctor",
    "isArray": {
      "!type": "fn(value: ?) -> bool",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/isArray",
      "!doc": "Returns true if an object is an array, false if it is not."
    },
    "prototype": {
      "!stdProto": "Array",
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/length",
        "!doc": "An unsigned, 32-bit integer that specifies the number of elements in an array."
      },
      "concat": {
        "!type": "fn(other: [?]) -> !this",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/concat",
        "!doc": "Returns a new array comprised of this array joined with other array(s) and/or value(s)."
      },
      "join": {
        "!type": "fn(separator?: string) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/join",
        "!doc": "Joins all elements of an array into a string."
      },
      "splice": {
        "!type": "fn(pos: number, amount: number, newelt?: ?) -> [?]",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/splice",
        "!doc": "Changes the content of an array, adding new elements while removing old elements."
      },
      "pop": {
        "!type": "fn() -> !this.<i>",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/pop",
        "!doc": "Removes the last element from an array and returns that element."
      },
      "push": {
        "!type": "fn(newelt: ?) -> number",
        "!effects": [
          "propagate !0 !this.<i>"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/push",
        "!doc": "Mutates an array by appending the given elements and returning the new length of the array."
      },
      "shift": {
        "!type": "fn() -> !this.<i>",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/shift",
        "!doc": "Removes the first element from an array and returns that element. This method changes the length of the array."
      },
      "unshift": {
        "!type": "fn(newelt: ?) -> number",
        "!effects": [
          "propagate !0 !this.<i>"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/unshift",
        "!doc": "Adds one or more elements to the beginning of an array and returns the new length of the array."
      },
      "slice": {
        "!type": "fn(from?: number, to?: number) -> !this",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/slice",
        "!doc": "Returns a shallow copy of a portion of an array."
      },
      "reverse": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/reverse",
        "!doc": "Reverses an array in place.  The first array element becomes the last and the last becomes the first."
      },
      "sort": {
        "!type": "fn(compare?: fn(a: ?, b: ?) -> number)",
        "!effects": [
          "call !0 !this.<i> !this.<i>"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort",
        "!doc": "Sorts the elements of an array in place and returns the array."
      },
      "indexOf": {
        "!type": "fn(elt: ?, from?: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf",
        "!doc": "Returns the first index at which a given element can be found in the array, or -1 if it is not present."
      },
      "lastIndexOf": {
        "!type": "fn(elt: ?, from?: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/lastIndexOf",
        "!doc": "Returns the last index at which a given element can be found in the array, or -1 if it is not present. The array is searched backwards, starting at fromIndex."
      },
      "every": {
        "!type": "fn(test: fn(elt: ?, i: number, array: +Array) -> bool, context?: ?) -> bool",
        "!effects": [
          "call !0 this=!1 !this.<i> number !this"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/every",
        "!doc": "Tests whether all elements in the array pass the test implemented by the provided function."
      },
      "some": {
        "!type": "fn(test: fn(elt: ?, i: number, array: +Array) -> bool, context?: ?) -> bool",
        "!effects": [
          "call !0 this=!1 !this.<i> number !this"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/some",
        "!doc": "Tests whether some element in the array passes the test implemented by the provided function."
      },
      "filter": {
        "!type": "fn(test: fn(elt: ?, i: number, array: +Array) -> bool, context?: ?) -> !this",
        "!effects": [
          "call !0 this=!1 !this.<i> number !this"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter",
        "!doc": "Creates a new array with all elements that pass the test implemented by the provided function."
      },
      "forEach": {
        "!type": "fn(f: fn(elt: ?, i: number, array: +Array), context?: ?)",
        "!effects": [
          "call !0 this=!1 !this.<i> number !this"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach",
        "!doc": "Executes a provided function once per array element."
      },
      "map": {
        "!type": "fn(f: fn(elt: ?, i: number, array: +Array) -> ?, context?: ?) -> [!0.!ret]",
        "!effects": [
          "call !0 this=!1 !this.<i> number !this"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map",
        "!doc": "Creates a new array with the results of calling a provided function on every element in this array."
      },
      "reduce": {
        "!type": "fn(combine: fn(sum: ?, elt: ?, i: number, array: +Array) -> ?, init?: ?) -> !0.!ret",
        "!effects": [
          "call !0 !1 !this.<i> number !this"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/Reduce",
        "!doc": "Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value."
      },
      "reduceRight": {
        "!type": "fn(combine: fn(sum: ?, elt: ?, i: number, array: +Array) -> ?, init?: ?) -> !0.!ret",
        "!effects": [
          "call !0 !1 !this.<i> number !this"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/ReduceRight",
        "!doc": "Apply a function simultaneously against two values of the array (from right-to-left) as to reduce it to a single value."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array",
    "!doc": "The JavaScript Array global object is a constructor for arrays, which are high-level, list-like objects."
  },
  "String": {
    "!type": "fn(value: ?) -> string",
    "fromCharCode": {
      "!type": "fn(code: number) -> string",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/fromCharCode",
      "!doc": "Returns a string created by using the specified sequence of Unicode values."
    },
    "prototype": {
      "!stdProto": "String",
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/String/length",
        "!doc": "Represents the length of a string."
      },
      "<i>": "string",
      "charAt": {
        "!type": "fn(i: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/charAt",
        "!doc": "Returns the specified character from a string."
      },
      "charCodeAt": {
        "!type": "fn(i: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/charCodeAt",
        "!doc": "Returns the numeric Unicode value of the character at the given index (except for unicode codepoints > 0x10000)."
      },
      "indexOf": {
        "!type": "fn(char: string, from?: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/indexOf",
        "!doc": "Returns the index within the calling String object of the first occurrence of the specified value, starting the search at fromIndex,\nreturns -1 if the value is not found."
      },
      "lastIndexOf": {
        "!type": "fn(char: string, from?: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/lastIndexOf",
        "!doc": "Returns the index within the calling String object of the last occurrence of the specified value, or -1 if not found. The calling string is searched backward, starting at fromIndex."
      },
      "substring": {
        "!type": "fn(from: number, to?: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/substring",
        "!doc": "Returns a subset of a string between one index and another, or through the end of the string."
      },
      "substr": {
        "!type": "fn(from: number, length?: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/substr",
        "!doc": "Returns the characters in a string beginning at the specified location through the specified number of characters."
      },
      "slice": {
        "!type": "fn(from: number, to?: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/slice",
        "!doc": "Extracts a section of a string and returns a new string."
      },
      "trim": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/Trim",
        "!doc": "Removes whitespace from both ends of the string."
      },
      "toUpperCase": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase",
        "!doc": "Returns the calling string value converted to uppercase."
      },
      "toLowerCase": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLowerCase",
        "!doc": "Returns the calling string value converted to lowercase."
      },
      "toLocaleUpperCase": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLocaleUpperCase",
        "!doc": "Returns the calling string value converted to upper case, according to any locale-specific case mappings."
      },
      "toLocaleLowerCase": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLocaleLowerCase",
        "!doc": "Returns the calling string value converted to lower case, according to any locale-specific case mappings."
      },
      "split": {
        "!type": "fn(pattern?: string|+RegExp, limit?: number) -> [string]",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/split",
        "!doc": "Splits a String object into an array of strings by separating the string into substrings."
      },
      "concat": {
        "!type": "fn(other: string) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/concat",
        "!doc": "Combines the text of two or more strings and returns a new string."
      },
      "localeCompare": {
        "!type": "fn(other: string) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/localeCompare",
        "!doc": "Returns a number indicating whether a reference string comes before or after or is the same as the given string in sort order."
      },
      "match": {
        "!type": "fn(pattern: +RegExp) -> [string]",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/match",
        "!doc": "Used to retrieve the matches when matching a string against a regular expression."
      },
      "replace": {
        "!type": "fn(pattern: string|+RegExp, replacement: string) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/replace",
        "!doc": "Returns a new string with some or all matches of a pattern replaced by a replacement.  The pattern can be a string or a RegExp, and the replacement can be a string or a function to be called for each match."
      },
      "search": {
        "!type": "fn(pattern: +RegExp) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/search",
        "!doc": "Executes the search for a match between a regular expression and this String object."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String",
    "!doc": "The String global object is a constructor for strings, or a sequence of characters."
  },
  "Number": {
    "!type": "fn(value: ?) -> number",
    "MAX_VALUE": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/MAX_VALUE",
      "!doc": "The maximum numeric value representable in JavaScript."
    },
    "MIN_VALUE": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/MIN_VALUE",
      "!doc": "The smallest positive numeric value representable in JavaScript."
    },
    "POSITIVE_INFINITY": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/POSITIVE_INFINITY",
      "!doc": "A value representing the positive Infinity value."
    },
    "NEGATIVE_INFINITY": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/NEGATIVE_INFINITY",
      "!doc": "A value representing the negative Infinity value."
    },
    "prototype": {
      "!stdProto": "Number",
      "toString": {
        "!type": "fn(radix?: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toString",
        "!doc": "Returns a string representing the specified Number object"
      },
      "toFixed": {
        "!type": "fn(digits: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toFixed",
        "!doc": "Formats a number using fixed-point notation"
      },
      "toExponential": {
        "!type": "fn(digits: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toExponential",
        "!doc": "Returns a string representing the Number object in exponential notation"
      },
      "toPrecision": {
        "!type": "fn(digits: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toPrecision",
        "!doc": "The toPrecision() method returns a string representing the number to the specified precision."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number",
    "!doc": "The Number JavaScript object is a wrapper object allowing you to work with numerical values. A Number object is created using the Number() constructor."
  },
  "Boolean": {
    "!type": "fn(value: ?) -> bool",
    "prototype": {
      "!stdProto": "Boolean"
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Boolean",
    "!doc": "The Boolean object is an object wrapper for a boolean value."
  },
  "RegExp": {
    "!type": "fn(source: string, flags?: string)",
    "prototype": {
      "!stdProto": "RegExp",
      "exec": {
        "!type": "fn(input: string) -> [string]",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/exec",
        "!doc": "Executes a search for a match in a specified string. Returns a result array, or null."
      },
      "test": {
        "!type": "fn(input: string) -> bool",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/test",
        "!doc": "Executes the search for a match between a regular expression and a specified string. Returns true or false."
      },
      "global": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp",
        "!doc": "Creates a regular expression object for matching text with a pattern."
      },
      "ignoreCase": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp",
        "!doc": "Creates a regular expression object for matching text with a pattern."
      },
      "multiline": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/multiline",
        "!doc": "Reflects whether or not to search in strings across multiple lines.\n"
      },
      "source": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/source",
        "!doc": "A read-only property that contains the text of the pattern, excluding the forward slashes.\n"
      },
      "lastIndex": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/lastIndex",
        "!doc": "A read/write integer property that specifies the index at which to start the next match."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp",
    "!doc": "Creates a regular expression object for matching text with a pattern."
  },
  "Date": {
    "!type": "fn(ms: number)",
    "parse": {
      "!type": "fn(source: string) -> +Date",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/parse",
      "!doc": "Parses a string representation of a date, and returns the number of milliseconds since January 1, 1970, 00:00:00 UTC."
    },
    "UTC": {
      "!type": "fn(year: number, month: number, date: number, hour?: number, min?: number, sec?: number, ms?: number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/UTC",
      "!doc": "Accepts the same parameters as the longest form of the constructor, and returns the number of milliseconds in a Date object since January 1, 1970, 00:00:00, universal time."
    },
    "now": {
      "!type": "fn() -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/now",
      "!doc": "Returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC."
    },
    "prototype": {
      "toUTCString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toUTCString",
        "!doc": "Converts a date to a string, using the universal time convention."
      },
      "toISOString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toISOString",
        "!doc": "JavaScript provides a direct way to convert a date object into a string in ISO format, the ISO 8601 Extended Format."
      },
      "toDateString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toDateString",
        "!doc": "Returns the date portion of a Date object in human readable form in American English."
      },
      "toTimeString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toTimeString",
        "!doc": "Returns the time portion of a Date object in human readable form in American English."
      },
      "toLocaleDateString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toLocaleDateString",
        "!doc": "Converts a date to a string, returning the \"date\" portion using the operating system's locale's conventions.\n"
      },
      "toLocaleTimeString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString",
        "!doc": "Converts a date to a string, returning the \"time\" portion using the current locale's conventions."
      },
      "getTime": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getTime",
        "!doc": "Returns the numeric value corresponding to the time for the specified date according to universal time."
      },
      "getFullYear": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getFullYear",
        "!doc": "Returns the year of the specified date according to local time."
      },
      "getYear": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getYear",
        "!doc": "Returns the year in the specified date according to local time."
      },
      "getMonth": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMonth",
        "!doc": "Returns the month in the specified date according to local time."
      },
      "getUTCMonth": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCMonth",
        "!doc": "Returns the month of the specified date according to universal time.\n"
      },
      "getDate": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getDate",
        "!doc": "Returns the day of the month for the specified date according to local time."
      },
      "getUTCDate": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCDate",
        "!doc": "Returns the day (date) of the month in the specified date according to universal time.\n"
      },
      "getDay": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getDay",
        "!doc": "Returns the day of the week for the specified date according to local time."
      },
      "getUTCDay": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCDay",
        "!doc": "Returns the day of the week in the specified date according to universal time.\n"
      },
      "getHours": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getHours",
        "!doc": "Returns the hour for the specified date according to local time."
      },
      "getUTCHours": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCHours",
        "!doc": "Returns the hours in the specified date according to universal time.\n"
      },
      "getMinutes": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMinutes",
        "!doc": "Returns the minutes in the specified date according to local time."
      },
      "getUTCMinutes": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date",
        "!doc": "Creates JavaScript Date instances which let you work with dates and times."
      },
      "getSeconds": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getSeconds",
        "!doc": "Returns the seconds in the specified date according to local time."
      },
      "getUTCSeconds": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCSeconds",
        "!doc": "Returns the seconds in the specified date according to universal time.\n"
      },
      "getMilliseconds": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMilliseconds",
        "!doc": "Returns the milliseconds in the specified date according to local time."
      },
      "getUTCMilliseconds": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCMilliseconds",
        "!doc": "Returns the milliseconds in the specified date according to universal time.\n"
      },
      "getTimezoneOffset": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset",
        "!doc": "Returns the time-zone offset from UTC, in minutes, for the current locale."
      },
      "setTime": {
        "!type": "fn(date: +Date) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setTime",
        "!doc": "Sets the Date object to the time represented by a number of milliseconds since January 1, 1970, 00:00:00 UTC.\n"
      },
      "setFullYear": {
        "!type": "fn(year: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setFullYear",
        "!doc": "Sets the full year for a specified date according to local time.\n"
      },
      "setUTCFullYear": {
        "!type": "fn(year: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCFullYear",
        "!doc": "Sets the full year for a specified date according to universal time.\n"
      },
      "setMonth": {
        "!type": "fn(month: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMonth",
        "!doc": "Set the month for a specified date according to local time."
      },
      "setUTCMonth": {
        "!type": "fn(month: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMonth",
        "!doc": "Sets the month for a specified date according to universal time.\n"
      },
      "setDate": {
        "!type": "fn(day: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setDate",
        "!doc": "Sets the day of the month for a specified date according to local time."
      },
      "setUTCDate": {
        "!type": "fn(day: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCDate",
        "!doc": "Sets the day of the month for a specified date according to universal time.\n"
      },
      "setHours": {
        "!type": "fn(hour: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setHours",
        "!doc": "Sets the hours for a specified date according to local time, and returns the number of milliseconds since 1 January 1970 00:00:00 UTC until the time represented by the updated Date instance."
      },
      "setUTCHours": {
        "!type": "fn(hour: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCHours",
        "!doc": "Sets the hour for a specified date according to universal time.\n"
      },
      "setMinutes": {
        "!type": "fn(min: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMinutes",
        "!doc": "Sets the minutes for a specified date according to local time."
      },
      "setUTCMinutes": {
        "!type": "fn(min: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMinutes",
        "!doc": "Sets the minutes for a specified date according to universal time.\n"
      },
      "setSeconds": {
        "!type": "fn(sec: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setSeconds",
        "!doc": "Sets the seconds for a specified date according to local time."
      },
      "setUTCSeconds": {
        "!type": "fn(sec: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCSeconds",
        "!doc": "Sets the seconds for a specified date according to universal time.\n"
      },
      "setMilliseconds": {
        "!type": "fn(ms: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMilliseconds",
        "!doc": "Sets the milliseconds for a specified date according to local time.\n"
      },
      "setUTCMilliseconds": {
        "!type": "fn(ms: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMilliseconds",
        "!doc": "Sets the milliseconds for a specified date according to universal time.\n"
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date",
    "!doc": "Creates JavaScript Date instances which let you work with dates and times."
  },
  "Error": {
    "!type": "fn(message: string)",
    "prototype": {
      "name": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error/name",
        "!doc": "A name for the type of error."
      },
      "message": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error/message",
        "!doc": "A human-readable description of the error."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error",
    "!doc": "Creates an error object."
  },
  "SyntaxError": {
    "!type": "fn(message: string)",
    "prototype": "Error.prototype",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/SyntaxError",
    "!doc": "Represents an error when trying to interpret syntactically invalid code."
  },
  "ReferenceError": {
    "!type": "fn(message: string)",
    "prototype": "Error.prototype",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/ReferenceError",
    "!doc": "Represents an error when a non-existent variable is referenced."
  },
  "URIError": {
    "!type": "fn(message: string)",
    "prototype": "Error.prototype",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/URIError",
    "!doc": "Represents an error when a malformed URI is encountered."
  },
  "EvalError": {
    "!type": "fn(message: string)",
    "prototype": "Error.prototype",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/EvalError",
    "!doc": "Represents an error regarding the eval function."
  },
  "RangeError": {
    "!type": "fn(message: string)",
    "prototype": "Error.prototype",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RangeError",
    "!doc": "Represents an error when a number is not within the correct range allowed."
  },
  "TypeError": {
    "!type": "fn(message: string)",
    "prototype": "Error.prototype",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/TypeError",
    "!doc": "Represents an error an error when a value is not of the expected type."
  },
  "parseInt": {
    "!type": "fn(string: string, radix?: number) -> number",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/parseInt",
    "!doc": "Parses a string argument and returns an integer of the specified radix or base."
  },
  "parseFloat": {
    "!type": "fn(string: string) -> number",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/parseFloat",
    "!doc": "Parses a string argument and returns a floating point number."
  },
  "isNaN": {
    "!type": "fn(value: number) -> bool",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/isNaN",
    "!doc": "Determines whether a value is NaN or not. Be careful, this function is broken. You may be interested in ECMAScript 6 Number.isNaN."
  },
  "isFinite": {
    "!type": "fn(value: number) -> bool",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/isFinite",
    "!doc": "Determines whether the passed value is a finite number."
  },
  "eval": {
    "!type": "fn(code: string) -> ?",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/eval",
    "!doc": "Evaluates JavaScript code represented as a string."
  },
  "encodeURI": {
    "!type": "fn(uri: string) -> string",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURI",
    "!doc": "Encodes a Uniform Resource Identifier (URI) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two \"surrogate\" characters)."
  },
  "encodeURIComponent": {
    "!type": "fn(uri: string) -> string",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent",
    "!doc": "Encodes a Uniform Resource Identifier (URI) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two \"surrogate\" characters)."
  },
  "decodeURI": {
    "!type": "fn(uri: string) -> string",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/decodeURI",
    "!doc": "Decodes a Uniform Resource Identifier (URI) previously created by encodeURI or by a similar routine."
  },
  "decodeURIComponent": {
    "!type": "fn(uri: string) -> string",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/decodeURIComponent",
    "!doc": "Decodes a Uniform Resource Identifier (URI) component previously created by encodeURIComponent or by a similar routine."
  },
  "Math": {
    "E": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/E",
      "!doc": "The base of natural logarithms, e, approximately 2.718."
    },
    "LN2": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LN2",
      "!doc": "The natural logarithm of 2, approximately 0.693."
    },
    "LN10": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LN10",
      "!doc": "The natural logarithm of 10, approximately 2.302."
    },
    "LOG2E": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LOG2E",
      "!doc": "The base 2 logarithm of E (approximately 1.442)."
    },
    "LOG10E": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LOG10E",
      "!doc": "The base 10 logarithm of E (approximately 0.434)."
    },
    "SQRT1_2": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/SQRT1_2",
      "!doc": "The square root of 1/2; equivalently, 1 over the square root of 2, approximately 0.707."
    },
    "SQRT2": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/SQRT2",
      "!doc": "The square root of 2, approximately 1.414."
    },
    "PI": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/PI",
      "!doc": "The ratio of the circumference of a circle to its diameter, approximately 3.14159."
    },
    "abs": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/abs",
      "!doc": "Returns the absolute value of a number."
    },
    "cos": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/cos",
      "!doc": "Returns the cosine of a number."
    },
    "sin": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/sin",
      "!doc": "Returns the sine of a number."
    },
    "tan": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/tan",
      "!doc": "Returns the tangent of a number."
    },
    "acos": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/acos",
      "!doc": "Returns the arccosine (in radians) of a number."
    },
    "asin": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/asin",
      "!doc": "Returns the arcsine (in radians) of a number."
    },
    "atan": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/atan",
      "!doc": "Returns the arctangent (in radians) of a number."
    },
    "atan2": {
      "!type": "fn(y: number, x: number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/atan2",
      "!doc": "Returns the arctangent of the quotient of its arguments."
    },
    "ceil": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/ceil",
      "!doc": "Returns the smallest integer greater than or equal to a number."
    },
    "floor": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/floor",
      "!doc": "Returns the largest integer less than or equal to a number."
    },
    "round": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/round",
      "!doc": "Returns the value of a number rounded to the nearest integer."
    },
    "exp": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/exp",
      "!doc": "Returns Ex, where x is the argument, and E is Euler's constant, the base of the natural logarithms."
    },
    "log": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/log",
      "!doc": "Returns the natural logarithm (base E) of a number."
    },
    "sqrt": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/sqrt",
      "!doc": "Returns the square root of a number."
    },
    "pow": {
      "!type": "fn(number, number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/pow",
      "!doc": "Returns base to the exponent power, that is, baseexponent."
    },
    "max": {
      "!type": "fn(number, number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/max",
      "!doc": "Returns the largest of zero or more numbers."
    },
    "min": {
      "!type": "fn(number, number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/min",
      "!doc": "Returns the smallest of zero or more numbers."
    },
    "random": {
      "!type": "fn() -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/random",
      "!doc": "Returns a floating-point, pseudo-random number in the range [0, 1) that is, from 0 (inclusive) up to but not including 1 (exclusive), which you can then scale to your desired range."
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math",
    "!doc": "A built-in object that has properties and methods for mathematical constants and functions."
  },
  "JSON": {
    "parse": {
      "!type": "fn(json: string, reviver?: fn(key: string, value: ?) -> ?) -> ?",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/parse",
      "!doc": "Parse a string as JSON, optionally transforming the value produced by parsing."
    },
    "stringify": {
      "!type": "fn(value: ?, replacer?: fn(key: string, value: ?) -> ?, space?: string|number) -> string",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify",
      "!doc": "Convert a value to JSON, optionally replacing values if a replacer function is specified, or optionally including only the specified properties if a replacer array is specified."
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JSON",
    "!doc": "JSON (JavaScript Object Notation) is a data-interchange format.  It closely resembles a subset of JavaScript syntax, although it is not a strict subset. (See JSON in the JavaScript Reference for full details.)  It is useful when writing any kind of JavaScript-based application, including websites and browser extensions.  For example, you might store user information in JSON format in a cookie, or you might store extension preferences in JSON in a string-valued browser preference."
  }
}
;});


define("json!tern/defs/ecma6.json", function(){ return {
  "!name": "ecma6",
  "!define": {
    "Promise.prototype": {
      "catch": {
        "!doc": "The catch() method returns a Promise and deals with rejected cases only. It behaves the same as calling Promise.prototype.then(undefined, onRejected).",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch",
        "!type": "fn(onRejected: fn(reason: ?)) -> !this"
      },
      "then": {
        "!doc": "The then() method returns a Promise. It takes two arguments, both are callback functions for the success and failure cases of the Promise.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then",
        "!type": "fn(onFulfilled: fn(value: ?), onRejected: fn(reason: ?)) -> !custom:Promise_then",
        "!effects": ["call !0 !this.:t"]
      }
    },
    "Promise_reject": {
      "!type": "fn(reason: ?) -> !this",
      "!doc": "The Promise.reject(reason) method returns a Promise object that is rejected with the given reason.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject"
    },
    "iter_prototype": {
      ":Symbol.iterator": "fn() -> !this"
    },
    "iter": {
      "!proto": "iter_prototype",
      "next": {
        "!type": "fn() -> +iter_result[value=!this.:t]",
        "!doc": "Return the next item in the sequence.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators"
      },
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators"
    },
    "iter_result": {
      "done": "bool",
      "value": "?"
    },
    "generator_prototype": {
      "!proto": "iter_prototype",
      "next": "fn(value?: ?) -> iter_result",
      "return": "fn(value?: ?) -> iter_result",
      "throw": "fn(exception: +Error)"
    },
    "Proxy_handler": {
      "!doc": "The proxy's handler object is a placeholder object which contains traps for proxies.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler",
      "getPrototypeOf": "fn(target: ?)",
      "setPrototypeOf": "fn(target: ?, prototype: ?)",
      "isExtensible": "fn(target: ?)",
      "preventExtensions": "fn(target: ?)",
      "getOwnPropertyDescriptor": "fn(target: ?, property: string)",
      "defineProperty": "fn(target: ?, property: string, descriptor: ?)",
      "has": "fn(target: ?, property: string)",
      "get": "fn(target: ?, property: string)",
      "set": "fn(target: ?, property: string, value: ?)",
      "deleteProperty": "fn(target: ?, property: string)",
      "enumerate": "fn(target: ?)",
      "ownKeys": "fn(target: ?)",
      "apply": "fn(target: ?, self: ?, arguments: [?])",
      "construct": "fn(target: ?, arguments: [?])"
    },
    "Proxy_revocable": {
      "proxy": "+Proxy",
      "revoke": "fn()"
    },
    "TypedArray": {
      "!type": "fn(size: number)",
      "!doc": "A TypedArray object describes an array-like view of an underlying binary data buffer. There is no global property named TypedArray, nor is there a directly visible TypedArray constructor.  Instead, there are a number of different global properties, whose values are typed array constructors for specific element types, listed below. On the following pages you will find common properties and methods that can be used with any typed array containing elements of any type.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray",
      "from": {
        "!type": "fn(arrayLike: ?, mapFn?: fn(elt: ?, i: number) -> number, thisArg?: ?) -> +TypedArray",
        "!effects": ["call !1 this=!2 !0.<i> number"],
        "!doc": "Creates a new typed array from an array-like or iterable object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from"
      },
      "of": {
        "!type": "fn(elements: number) -> +TypedArray",
        "!doc": "Creates a new typed array from a variable number of arguments.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of"
      },
      "BYTES_PER_ELEMENT": {
        "!type": "number",
        "!doc": "The TypedArray.BYTES_PER_ELEMENT property represents the size in bytes of each element in an typed array.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/BYTES_PER_ELEMENT"
      },
      "name": {
        "!type": "string",
        "!doc": "The TypedArray.name property represents a string value of the typed array constructor name.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/name"
      },
      "prototype": {
        "<i>": "number",
        "buffer": {
          "!type": "+ArrayBuffer",
          "!doc": "The buffer accessor property represents the ArrayBuffer referenced by a TypedArray at construction time.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/buffer"
        },
        "byteLength": {
          "!type": "number",
          "!doc": "The byteLength accessor property represents the length (in bytes) of a typed array from the start of its ArrayBuffer.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/byteLength"
        },
        "byteOffset": {
          "!type": "number",
          "!doc": "The byteOffset accessor property represents the offset (in bytes) of a typed array from the start of its ArrayBuffer.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/byteOffset"
        },
        "copyWithin": {
          "!type": "fn(target: number, start: number, end?: number) -> ?",
          "!doc": "The copyWithin() method copies the sequence of array elements within the array to the position starting at target. The copy is taken from the index positions of the second and third arguments start and end. The end argument is optional and defaults to the length of the array. This method has the same algorithm as Array.prototype.copyWithin. TypedArray is one of the typed array types here.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/copyWithin"
        },
        "entries": {
          "!type": "fn() -> +iter[:t=number]",
          "!doc": "The entries() method returns a new Array Iterator object that contains the key/value pairs for each index in the array.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/entries"
        },
        "every": {
          "!type": "fn(callback: fn(element: number, index: number, array: TypedArray) -> bool, thisArg?: ?) -> bool",
          "!effects": ["call !0 this=!1 number number !this"],
          "!doc": "The every() method tests whether all elements in the typed array pass the test implemented by the provided function. This method has the same algorithm as Array.prototype.every(). TypedArray is one of the typed array types here.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/every"
        },
        "fill": {
          "!type": "fn(value: number, start?: number, end?: number)",
          "!doc": "The fill() method fills all the elements of a typed array from a start index to an end index with a static value. This method has the same algorithm as Array.prototype.fill(). TypedArray is one of the typed array types here.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/fill"
        },
        "filter": {
          "!type": "fn(test: fn(element: number, i: number) -> bool, context?: ?) -> !this",
          "!effects": ["call !0 this=!1 number number"],
          "!doc": "Creates a new array with all of the elements of this array for which the provided filtering function returns true. See also Array.prototype.filter().",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/filter"
        },
        "find": {
          "!type": "fn(callback: fn(element: number, index: number, array: +TypedArray) -> bool, thisArg?: ?) -> number",
          "!effects": ["call !0 this=!1 number number !this"],
          "!doc": "The find() method returns a value in the typed array, if an element satisfies the provided testing function. Otherwise undefined is returned. TypedArray is one of the typed array types here.\nSee also the findIndex() method, which returns the index of a found element in the typed array instead of its value.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/find"
        },
        "findIndex": {
          "!type": "fn(callback: fn(element: number, index: number, array: +TypedArray) -> bool, thisArg?: ?) -> number",
          "!effects": ["call !0 this=!1 number number !this"],
          "!doc": "The findIndex() method returns an index in the typed array, if an element in the typed array satisfies the provided testing function. Otherwise -1 is returned.\nSee also the find() method, which returns the value of a found element in the typed array instead of its index.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/findIndex"
        },
        "forEach": {
          "!type": "fn(callback: fn(value: number, key: number, array: +TypedArray), thisArg?: ?)",
          "!effects": ["call !0 this=!1 number number !this"],
          "!doc": "Executes a provided function once per array element.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/forEach"
        },
        "indexOf": {
          "!type": "fn(searchElement: number, fromIndex?: number) -> number",
          "!doc": "The indexOf() method returns the first index at which a given element can be found in the typed array, or -1 if it is not present. This method has the same algorithm as Array.prototype.indexOf(). TypedArray is one of the typed array types here.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/indexOf"
        },
        "join": {
          "!type": "fn(separator?: string) -> string",
          "!doc": "The join() method joins all elements of an array into a string. This method has the same algorithm as Array.prototype.join(). TypedArray is one of the typed array types here.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/join"
        },
        "keys": {
          "!type": "fn() -> +iter[:t=number]",
          "!doc": "The keys() method returns a new Array Iterator object that contains the keys for each index in the array.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/keys"
        },
        "lastIndexOf": {
          "!type": "fn(searchElement: number, fromIndex?: number) -> number",
          "!doc": "The lastIndexOf() method returns the last index at which a given element can be found in the typed array, or -1 if it is not present. The typed array is searched backwards, starting at fromIndex. This method has the same algorithm as Array.prototype.lastIndexOf(). TypedArray is one of the typed array types here.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/lastIndexOf"
        },
        "length": {
          "!type": "number",
          "!doc": "Returns the number of elements hold in the typed array. Fixed at construction time and thus read only.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/length"
        },
        "map": {
          "!type": "fn(f: fn(element: number, i: number) -> number, context?: ?) -> +TypedArray",
          "!effects": ["call !0 this=!1 number number"],
          "!doc": "Creates a new array with the results of calling a provided function on every element in this array. See also Array.prototype.map().",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/map"
        },
        "reduce": {
          "!type": "fn(combine: fn(sum: ?, elt: number, i: number) -> ?, init?: ?) -> !0.!ret",
          "!effects": ["call !0 !1 number number"],
          "!doc": "Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value. See also Array.prototype.reduce().",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/reduce"
        },
        "reduceRight": {
          "!type": "fn(combine: fn(sum: ?, elt: number, i: number) -> ?, init?: ?) -> !0.!ret",
          "!effects": ["call !0 !1 number number"],
          "!doc": "Apply a function against an accumulator and each value of the array (from right-to-left) as to reduce it to a single value. See also Array.prototype.reduceRight().",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/reduceRight"
        },
        "reverse": {
          "!type": "fn()",
          "!doc": "The reverse() method reverses a typed array in place. The first typed array element becomes the last and the last becomes the first. This method has the same algorithm as Array.prototype.reverse(). TypedArray is one of the typed array types here.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/reverse"
        },
        "set": {
          "!type": "fn(array: [number], offset?: number)",
          "!doc": "The set() method stores multiple values in the typed array, reading input values from a specified array.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/set"
        },
        "slice": {
          "!type": "fn(from: number, to?: number) -> +TypedArray",
          "!doc": "Extracts a section of an array and returns a new array. See also Array.prototype.slice().",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/slice"
        },
        "some": {
          "!type": "fn(test: fn(elt: number, i: number) -> bool, context?: ?) -> bool",
          "!effects": ["call !0 this=!1 number number"],
          "!doc": "The some() method tests whether some element in the typed array passes the test implemented by the provided function. This method has the same algorithm as Array.prototype.some(). TypedArray is one of the typed array types here.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/some"
        },
        "sort": {
          "!type": "fn(compare?: fn(a: number, b: number) -> number)",
          "!effects": ["call !0 number number"],
          "!doc": "Sorts the elements of an array in place and returns the array. See also Array.prototype.sort().",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/sort"
        },
        "subarray": {
          "!type": "fn(begin?: number, end?: number) -> +TypedArray",
          "!doc": "The subarray() method returns a new TypedArray on the same ArrayBuffer store and with the same element types as for this TypedArray object. The begin offset is inclusive and the end offset is exclusive. TypedArray is one of the typed array types.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray"
        },
        "values": {
          "!type": "fn() -> +iter[:t=number]",
          "!doc": "The values() method returns a new Array Iterator object that contains the values for each index in the array.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/values"
        },
        ":Symbol.iterator": {
          "!type": "fn() -> +iter[:t=number]",
          "!doc": "Returns a new Array Iterator object that contains the values for each index in the array.",
          "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/@@iterator"
        }
      }
    }
  },
  "Array": {
    "from": {
      "!type": "fn(arrayLike: ?, mapFn?: fn(elt: ?, i: number) -> ?, thisArg?: ?) -> [!0.<i>]",
      "!effects": [
        "call !1 this=!2 !0.<i> number"
      ],
      "!doc": "The Array.from() method creates a new Array instance from an array-like or iterable object.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from"
    },
    "of": {
      "!type": "fn(elementN: ?) -> [!0]",
      "!doc": "The Array.of() method creates a new Array instance with a variable number of arguments, regardless of number or type of the arguments.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of"
    },
    "prototype": {
      "copyWithin": {
        "!type": "fn(target: number, start: number, end?: number) -> !this",
        "!doc": "The copyWithin() method copies the sequence of array elements within the array to the position starting at target. The copy is taken from the index positions of the second and third arguments start and end.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin"
      },
      "entries": {
        "!type": "fn() -> +iter[:t=[number, !this.<i>]]",
        "!doc": "The entries() method returns a new Array Iterator object that contains the key/value pairs for each index in the array.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries"
      },
      "fill": {
        "!type": "fn(value: ?, start?: number, end?: number) -> !this",
        "!doc": "The fill() method fills all the elements of an array from a start index to an end index with a static value.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill"
      },
      "find": {
        "!type": "fn(callback: fn(element: ?, index: number, array: [?]) -> bool, thisArg?: ?) -> !this.<i>",
        "!effects": ["call !0 this=!2 !this.<i> number"],
        "!doc": "The find() method returns a value in the array, if an element in the array satisfies the provided testing function. Otherwise undefined is returned.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find"
      },
      "findIndex": {
        "!type": "fn(callback: fn(element: ?, index: number, array: [?]), thisArg?: ?) -> number",
        "!effects": ["call !0 this=!2 !this.<i> number"],
        "!doc": "The findIndex() method returns an index in the array, if an element in the array satisfies the provided testing function. Otherwise -1 is returned.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex"
      },
      "keys": {
        "!type": "fn() -> +iter[:t=number]",
        "!doc": "The keys() method returns a new Array Iterator that contains the keys for each index in the array.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/keys"
      },
      "values": {
        "!type": "fn() -> +iter[:t=!this.<i>]",
        "!doc": "The values() method returns a new Array Iterator object that contains the values for each index in the array.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values"
      },
      ":Symbol.iterator": {
        "!type": "fn() -> +iter[:t=!this.<i>]",
        "!doc": "Returns a new Array Iterator object that contains the values for each index in the array.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/@@iterator"
      }
    }
  },
  "ArrayBuffer": {
    "!type": "fn(length: number)",
    "!doc": "The ArrayBuffer object is used to represent a generic, fixed-length raw binary data buffer.",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer",
    "isView": {
      "!type": "fn(arg: +ArrayBuffer) -> bool",
      "!doc": "The ArrayBuffer.isView() method returns true if arg is one of the ArrayBuffer views, such as typed array objects or a DataView; false otherwise.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/isView"
    },
    "prototype": {
      "byteLength": {
        "!type": "number",
        "!doc": "The byteLength accessor property represents the length of an ArrayBuffer in bytes.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/byteLength"
      },
      "slice": {
        "!type": "fn(begin: number, end?: number) -> +ArrayBuffer",
        "!doc": "The slice() method returns a new ArrayBuffer whose contents are a copy of this ArrayBuffer's bytes from begin, inclusive, up to end, exclusive.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/slice"
      }
    }
  },
  "DataView": {
    "!type": "fn(buffer: +ArrayBuffer, byteOffset?: number, byteLength?: number)",
    "!doc": "The DataView view provides a low-level interface for reading data from and writing it to an ArrayBuffer.",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView",
    "prototype": {
      "buffer": {
        "!type": "+ArrayBuffer",
        "!doc": "The buffer accessor property represents the ArrayBuffer referenced by the DataView at construction time.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/buffer"
      },
      "byteLength": {
        "!type": "number",
        "!doc": "The byteLength accessor property represents the length (in bytes) of this view from the start of its ArrayBuffer.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/byteLength"
      },
      "byteOffset": {
        "!type": "number",
        "!doc": "The byteOffset accessor property represents the offset (in bytes) of this view from the start of its ArrayBuffer.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/byteOffset"
      },
      "getFloat32": {
        "!type": "fn(byteOffset: number, littleEndian?: bool) -> number",
        "!doc": "The getFloat32() method gets a signed 32-bit integer (float) at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat32"
      },
      "getFloat64": {
        "!type": "fn(byteOffset: number, littleEndian?: bool) -> number",
        "!doc": "The getFloat64() method gets a signed 64-bit float (double) at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat64"
      },
      "getInt16": {
        "!type": "fn(byteOffset: number, littleEndian?: bool) -> number",
        "!doc": "The getInt16() method gets a signed 16-bit integer (short) at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt16"
      },
      "getInt32": {
        "!type": "fn(byteOffset: number, littleEndian?: bool) -> number",
        "!doc": "The getInt32() method gets a signed 32-bit integer (long) at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt32"
      },
      "getInt8": {
        "!type": "fn(byteOffset: number, littleEndian?: bool) -> number",
        "!doc": "The getInt8() method gets a signed 8-bit integer (byte) at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt8"
      },
      "getUint16": {
        "!type": "fn(byteOffset: number, littleEndian?: bool) -> number",
        "!doc": "The getUint16() method gets an unsigned 16-bit integer (unsigned short) at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint16"
      },
      "getUint32": {
        "!type": "fn(byteOffset: number, littleEndian?: bool) -> number",
        "!doc": "The getUint32() method gets an unsigned 32-bit integer (unsigned long) at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32"
      },
      "getUint8": {
        "!type": "fn(byteOffset: number) -> number",
        "!doc": "The getUint8() method gets an unsigned 8-bit integer (unsigned byte) at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint8"
      },
      "setFloat32": {
        "!type": "fn(byteOffset: number, value: number, littleEndian?: bool)",
        "!doc": "The setFloat32() method stores a signed 32-bit integer (float) value at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat32"
      },
      "setFloat64": {
        "!type": "fn(byteOffset: number, value: number, littleEndian?: bool)",
        "!doc": "The setFloat64() method stores a signed 64-bit integer (double) value at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat64"
      },
      "setInt16": {
        "!type": "fn(byteOffset: number, value: number, littleEndian?: bool)",
        "!doc": "The setInt16() method stores a signed 16-bit integer (short) value at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt16"
      },
      "setInt32": {
        "!type": "fn(byteOffset: number, value: number, littleEndian?: bool)",
        "!doc": "The setInt32() method stores a signed 32-bit integer (long) value at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt32"
      },
      "setInt8": {
        "!type": "fn(byteOffset: number, value: number)",
        "!doc": "The setInt8() method stores a signed 8-bit integer (byte) value at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt8"
      },
      "setUint16": {
        "!type": "fn(byteOffset: number, value: number, littleEndian?: bool)",
        "!doc": "The setUint16() method stores an unsigned 16-bit integer (unsigned short) value at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint16"
      },
      "setUint32": {
        "!type": "fn(byteOffset: number, value: number, littleEndian?: bool)",
        "!doc": "The setUint32() method stores an unsigned 32-bit integer (unsigned long) value at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint32"
      },
      "setUint8": {
        "!type": "fn(byteOffset: number, value: number)",
        "!doc": "The setUint8() method stores an unsigned 8-bit integer (byte) value at the specified byte offset from the start of the DataView.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint8"
      }
    }
  },
  "Date": {
    "prototype": {
      "toJSON": {
        "!type": "fn() -> string",
        "!doc": "Returns a string (using toISOString()) representing the Date object's value.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toJSON"
      }
    }
  },
  "Float32Array": "TypedArray",
  "Float64Array": "TypedArray",
  "Int16Array": "TypedArray",
  "Int32Array": "TypedArray",
  "Int8Array": "TypedArray",
  "Map": {
    "!type": "fn(iterable?: [?])",
    "!doc": "The Map object is a simple key/value map. Any value (both objects and primitive values) may be used as either a key or a value.",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map",
    "prototype": {
      "clear": {
        "!type": "fn()",
        "!doc": "The clear() method removes all elements from a Map object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear"
      },
      "delete": {
        "!type": "fn(key: ?)",
        "!doc": "The delete() method removes the specified element from a Map object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete"
      },
      "entries": {
        "!type": "fn() -> +iter[:t=[!this.:key, !this.:value]]",
        "!doc": "The entries() method returns a new Iterator object that contains the [key, value] pairs for each element in the Map object in insertion order.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries"
      },
      "forEach": {
        "!type": "fn(callback: fn(value: ?, key: ?, map: +Map), thisArg?: ?)",
        "!effects": ["call !0 this=!1 !this.:value !this.:key !this"],
        "!doc": "The forEach() method executes a provided function once per each key/value pair in the Map object, in insertion order.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach"
      },
      "get": {
        "!type": "fn(key: ?) -> !this.:value",
        "!doc": "The get() method returns a specified element from a Map object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get"
      },
      "has": {
        "!type": "fn(key: ?) -> bool",
        "!doc": "The has() method returns a boolean indicating whether an element with the specified key exists or not.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has"
      },
      "keys": {
        "!type": "fn() -> +iter[:t=!this.:key]",
        "!doc": "The keys() method returns a new Iterator object that contains the keys for each element in the Map object in insertion order.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys"
      },
      "set": {
        "!type": "fn(key: ?, value: ?) -> !this",
        "!effects": ["propagate !0 !this.:key", "propagate !1 !this.:value"],
        "!doc": "The set() method adds a new element with a specified key and value to a Map object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set"
      },
      "size": {
        "!type": "number",
        "!doc": "The size accessor property returns the number of elements in a Map object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size"
      },
      "values": {
        "!type": "fn() -> +iter[:t=!this.:value]",
        "!doc": "The values() method returns a new Iterator object that contains the values for each element in the Map object in insertion order.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values"
      },
      ":Symbol.iterator": {
        "!type": "fn() -> +iter[:t=[!this.:key, !this.:value]]",
        "!doc": "Returns a new Iterator object that contains the [key, value] pairs for each element in the Map object in insertion order.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator"
      }
    }
  },
  "Math": {
    "acosh": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.acosh() function returns the hyperbolic arc-cosine of a number.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh"
    },
    "asinh": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.asinh() function returns the hyperbolic arcsine of a number.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh"
    },
    "atanh": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.atanh() function returns the hyperbolic arctangent of a number.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh"
    },
    "cbrt": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.cbrt() function returns the cube root of a number.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt"
    },
    "clz32": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.clz32() function returns the number of leading zero bits in the 32-bit binary representation of a number.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32"
    },
    "cosh": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.cosh() function returns the hyperbolic cosine of a number, that can be expressed using the constant e:",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh"
    },
    "expm1": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.expm1() function returns ex - 1, where x is the argument, and e the base of the natural logarithms.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/expm1"
    },
    "fround": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.fround() function returns the nearest single precision float representation of a number.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround"
    },
    "hypot": {
      "!type": "fn(value: number) -> number",
      "!doc": "The Math.hypot() function returns the square root of the sum of squares of its arguments.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot"
    },
    "imul": {
      "!type": "fn(a: number, b: number) -> number",
      "!doc": "The Math.imul() function returns the result of the C-like 32-bit multiplication of the two parameters.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul"
    },
    "log10": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.log10() function returns the base 10 logarithm of a number.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10"
    },
    "log1p": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.log1p() function returns the natural logarithm (base e) of 1 + a number.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log1p"
    },
    "log2": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.log2() function returns the base 2 logarithm of a number.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2"
    },
    "sign": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.sign() function returns the sign of a number, indicating whether the number is positive, negative or zero.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign"
    },
    "sinh": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.sinh() function returns the hyperbolic sine of a number, that can be expressed using the constant e:",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh"
    },
    "tanh": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.tanh() function returns the hyperbolic tangent of a number.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh"
    },
    "trunc": {
      "!type": "fn(x: number) -> number",
      "!doc": "The Math.trunc() function returns the integral part of a number by removing any fractional digits. It does not round any numbers. The function can be expressed with the floor() and ceil() function:",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc"
    }
  },
  "Number": {
    "EPSILON": {
      "!type": "number",
      "!doc": "The Number.EPSILON property represents the difference between one and the smallest value greater than one that can be represented as a Number.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON"
    },
    "MAX_SAFE_INTEGER": {
      "!type": "number",
      "!doc": "The Number.MAX_SAFE_INTEGER constant represents the maximum safe integer in JavaScript (253 - 1).",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER"
    },
    "MIN_SAFE_INTEGER": {
      "!type": "number",
      "!doc": "The Number.MIN_SAFE_INTEGER constant represents the minimum safe integer in JavaScript (-(253 - 1)).",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER"
    },
    "isFinite": {
      "!type": "fn(testValue: ?) -> bool",
      "!doc": "The Number.isFinite() method determines whether the passed value is finite.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite"
    },
    "isInteger": {
      "!type": "fn(testValue: ?) -> bool",
      "!doc": "The Number.isInteger() method determines whether the passed value is an integer.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger"
    },
    "isNaN": {
      "!type": "fn(testValue: ?) -> bool",
      "!doc": "The Number.isNaN() method determines whether the passed value is NaN. More robust version of the original global isNaN().",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN"
    },
    "isSafeInteger": {
      "!type": "fn(testValue: ?) -> bool",
      "!doc": "The Number.isSafeInteger() method determines whether the provided value is a number that is a safe integer. A safe integer is an integer that",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger"
    },
    "parseFloat": {
      "!type": "fn(string: string) -> number",
      "!doc": "The Number.parseFloat() method parses a string argument and returns a floating point number.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseFloat"
    },
    "parseInt": {
      "!type": "fn(string: string, radix?: number) -> number",
      "!doc": "The Number.parseInt() method parses a string argument and returns an integer of the specified radix or base.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseInt"
    }
  },
  "Object": {
    "assign": {
      "!type": "fn(target: ?, source: ?, source?: ?) -> !0",
      "!effects": ["copy !1 !0"],
      "!doc": "The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign"
    },
    "getOwnPropertySymbols": {
      "!type": "fn(obj: ?) -> !custom:getOwnPropertySymbols",
      "!doc": "The Object.getOwnPropertySymbols() method returns an array of all symbol properties found directly upon a given object.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols"
    },
    "is": {
      "!type": "fn(value1: ?, value2: ?) -> bool",
      "!doc": "The Object.is() method determines whether two values are the same value.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is"
    },
    "setPrototypeOf": {
      "!type": "fn(obj: ?, prototype: ?)",
      "!doc": "The Object.setPrototype() method sets the prototype (i.e., the internal [[Prototype]] property) of a specified object to another object or null.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf"
    }
  },
  "Promise": {
    "!type": "fn(executor: fn(resolve: fn(value: ?), reject: fn(reason: ?))) -> !custom:Promise_ctor",
    "!doc": "The Promise object is used for deferred and asynchronous computations. A Promise is in one of the three states:",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise",
    "all": {
      "!type": "fn(iterable: [+Promise]) -> +Promise[:t=[!0.<i>.:t]]",
      "!doc": "The Promise.all(iterable) method returns a promise that resolves when all of the promises in the iterable argument have resolved.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all"
    },
    "race": {
      "!type": "fn(iterable: [+Promise]) -> !0.<i>",
      "!doc": "The Promise.race(iterable) method returns a promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects, with the value or reason from that promise.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race"
    },
    "reject": "Promise_reject",
    "resolve": {
      "!type": "fn(value: ?) -> +Promise[:t=!0]",
      "!doc": "The Promise.resolve(value) method returns a Promise object that is resolved with the given value. If the value is a thenable (i.e. has a then method), the returned promise will 'follow' that thenable, adopting its eventual state; otherwise the returned promise will be fulfilled with the value.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve"
    },
    "prototype": "Promise.prototype"
  },
  "Proxy": {
    "!type": "fn(target: ?, handler: Proxy_handler)",
    "!doc": "The Proxy object is used to define the custom behavior in JavaScript fundamental operation (e.g. property lookup, assignment, enumeration, function invocation, etc).",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy",
    "revocable": {
      "!type": "fn(target: ?, handler: Proxy_handler) -> Proxy_revocable",
      "!doc": "The Proxy.revocable() method is used to create a revocable Proxy object.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/revocable"
    }
  },
  "Reflect": {
    "!doc": "Reflect is a built-in object that provides methods for interceptable JavaScript operations.",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect",
    "apply": {
      "!type": "fn(target: fn(), thisArg?: ?, argumentList?: [?]) -> !0.!ret",
      "!doc": "Calls a target function with arguments as specified.",
      "!url":  "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/apply"
    },
    "construct": {
      "!type": "fn(target: fn(), argumentList?: [?]) -> ?",
      "!doc": "Acts like the new operator as a function. It is equivalent to calling new target(...args).",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/construct"
    },
    "defineProperty": {
      "!type": "fn(target: ?, property: string, attributes: ?) -> bool",
      "!doc": "The static Reflect.defineProperty() method is like Object.defineProperty() but returns a Boolean.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/defineProperty"
    },
    "deleteProperty": {
      "!type": "fn(target: ?, property: string) -> bool",
      "!doc": "Works like the delete operator as a function.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/deleteProperty"
    },
    "enumerate": {
      "!type": "fn(target: ?) -> +iter[:t=string]",
      "!doc": "Returns an iterator with the enumerable own and inherited properties of the target object.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/enumerate"
    },
    "get": {
      "!type": "fn(target: ?, property: string) -> ?",
      "!doc": "Gets a property from an object.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get"
    },
    "getOwnPropertyDescriptor": {
      "!type": "fn(target: ?, property: string) -> ?",
      "!doc": "Returns a property descriptor of the given property if it exists on the object, undefined otherwise.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getOwnPropertyDescriptor"
    },
    "getPrototypeOf": {
      "!type": "fn(target: ?) -> ?",
      "!doc": "Returns the prototype of the specified object.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getPrototypeOf"
    },
    "has": {
      "!type": "fn(target: ?, property: string) -> bool",
      "!doc": "The static Reflect.has() method works like the in operator as a function.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/has"
    },
    "isExtensible": {
      "!type": "fn(target: ?) -> bool",
      "!doc": "Determines if an object is extensible (whether it can have new properties added to it).",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/isExtensible"
    },
    "ownKeys": {
      "!type": "fn(target: ?) -> [string]",
      "!doc": "Returns an array of the target object's own property keys.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/ownKeys"
    },
    "preventExtensions": {
      "!type": "fn(target: ?) -> bool",
      "!doc": "Prevents new properties from ever being added to an object.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/preventExtensions"
    },
    "set": {
      "!type": "fn(target: ?, property: string, value: ?) -> bool",
      "!doc": "Set a property on an object.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/set"
    },
    "setPrototypeOf": {
      "!type": "fn(target: ?, prototype: ?) -> bool",
      "!doc": "Sets the prototype of a specified object to another object or to null.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/setPrototypeOf"
    }
  },
  "RegExp": {    
    "prototype": {
      "flags": {
        "!type": "string",
        "!doc": "The flags property returns a string consisting of the flags of the current regular expression object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags"
      },
      "sticky": {
        "!type": "bool",
        "!doc": "The sticky property reflects whether or not the search is sticky (searches in strings only from the index indicated by the lastIndex property of this regular expression). sticky is a read-only property of an individual regular expression object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky"
      },
      "unicode": {
        "!type": "bool",
        "!doc": "The 'u' flag enables various Unicode-related features.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode"
      }
    }
  },
  "Set": {
    "!type": "fn(iterable?: [?])",
    "!doc": "The Set object lets you store unique values of any type, whether primitive values or object references.",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set",
    "prototype": {
      "add": {
        "!type": "fn(value: ?) -> !this",
        "!effects": ["propagate !0 !this.:t"],
        "!doc": "The add() method appends a new element with a specified value to the end of a Set object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/add"
      },
      "clear": {
        "!type": "fn()",
        "!doc": "The clear() method removes all elements from a Set object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/clear"
      },
      "delete": {
        "!type": "fn(value: ?) -> bool",
        "!doc": "The delete() method removes the specified element from a Set object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/delete"
      },
      "entries": {
        "!type": "fn() -> +iter[:t=[!this.:t]]",
        "!doc": "The entries() method returns a new Iterator object that contains an array of [value, value] for each element in the Set object, in insertion order. For Set objects there is no key like in Map objects. However, to keep the API similar to the Map object, each entry has the same value for its key and value here, so that an array [value, value] is returned.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/entries"
      },
      "forEach": {
        "!type": "fn(callback: fn(value: ?, value2: ?, set: +Set), thisArg?: ?)",
        "!effects": ["call !0 this=!1 !this.:t number !this"],
        "!doc": "The forEach() method executes a provided function once per each value in the Set object, in insertion order.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/forEach"
      },
      "has": {
        "!type": "fn(value: ?) -> bool",
        "!doc": "The has() method returns a boolean indicating whether an element with the specified value exists in a Set object or not.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/has"
      },
      "keys": {
        "!type": "fn() -> +iter[:t=!this.:t]",
        "!doc": "The values() method returns a new Iterator object that contains the values for each element in the Set object in insertion order.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/keys"
      },
      "size": {
        "!type": "number",
        "!doc": "The size accessor property returns the number of elements in a Set object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/size"
      },
      "values": {
        "!type": "fn() -> +iter[:t=!this.:t]",
        "!doc": "The values() method returns a new Iterator object that contains the values for each element in the Set object in insertion order.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/values"
      },
      ":Symbol.iterator": {
        "!type": "fn() -> +iter[:t=!this.:t]",
        "!doc": "Returns a new Iterator object that contains the values for each element in the Set object in insertion order.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/@@iterator"
      }
    }
  },
  "String": {
    "fromCodePoint": {
      "!type": "fn(point: number, point?: number) -> string",
      "!doc": "The static String.fromCodePoint() method returns a string created by using the specified sequence of code points.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint"
    },
    "raw": {
      "!type": "fn(template: [string], substitutions: ?, templateString: ?) -> string",
      "!doc": "The static String.raw() method is a tag function of template strings, used to get the raw string form of template strings.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw"
    },
    "prototype": {
      "codePointAt": {
        "!type": "fn(pos: number) -> number",
        "!doc": "The codePointAt() method returns a non-negative integer that is the UTF-16 encoded code point value.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt"
      },
      "endsWith": {
        "!type": "fn(searchString: string, position?: number) -> bool",
        "!doc": "The endsWith() method determines whether a string ends with the characters of another string, returning true or false as appropriate.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith"
      },
      "includes": {
        "!type": "fn(searchString: string, position?: number) -> bool",
        "!doc": "The includes() method determines whether one string may be found within another string, returning true or false as appropriate.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/contains"
      },
      "normalize": {
        "!type": "fn(form: string) -> string",
        "!doc": "The normalize() method returns the Unicode Normalization Form of a given string (if the value isn't a string, it will be converted to one first).",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize"
      },
      "repeat": {
        "!type": "fn(count: number) -> string",
        "!doc": "The repeat() method constructs and returns a new string which contains the specified number of copies of the string on which it was called, concatenated together.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat"
      },
      "startsWith": {
        "!type": "fn(searchString: string, position?: number) -> bool",
        "!doc": "The startsWith() method determines whether a string begins with the characters of another string, returning true or false as appropriate.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith"
      },
      ":Symbol.iterator": {
        "!type": "fn() -> +iter[:t=string]",
        "!doc": "Returns a new Iterator object that iterates over the code points of a String value, returning each code point as a String value.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/@@iterator"
      }
    }
  },
  "Symbol": {
    "!type": "fn(description?: string) -> !custom:getSymbol",
    "!doc": "A symbol is a unique and immutable data type and may be used as an identifier for object properties. The symbol object is an implicit object wrapper for the symbol primitive data type.",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol",
    "for": {
      "!type": "fn(key: string) -> !custom:getSymbol",
      "!doc": "The Symbol.for(key) method searches for existing symbols in a runtime-wide symbol registry with the given key and returns it if found. Otherwise a new symbol gets created in the global symbol registry with this key.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for"
    },
    "keyFor": {
      "!type": "fn(sym: +Symbol) -> string",
      "!doc": "The Symbol.keyFor(sym) method retrieves a shared symbol key from the global symbol registry for the given symbol.",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/keyFor"
    },
    "hasInstance": ":Symbol.hasInstance",
    "isConcatSpreadable": ":Symbol.isConcatSpreadable",
    "iterator": ":Symbol.iterator",
    "keyFor": ":Symbol.keyFor",
    "match": ":Symbol.match",
    "replace": ":Symbol.replace",
    "search": ":Symbol.search",
    "species": ":Symbol.species",
    "split": ":Symbol.split",
    "toStringTag": ":Symbol.toStringTag",
    "unscopables": ":Symbol.unscopables",
    "prototype": {
      "!stdProto": "Symbol"
    }
  },
  "Uint16Array": "TypedArray",
  "Uint32Array": "TypedArray",
  "Uint8Array": "TypedArray",
  "Uint8ClampedArray": "TypedArray",
  "WeakMap": {
    "!type": "fn(iterable?: [?])",
    "!doc": "The WeakMap object is a collection of key/value pairs in which the keys are objects and the values can be arbitrary values.",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap",
    "prototype": {
      "delete": {
        "!type": "fn(key: ?) -> bool",
        "!doc": "The delete() method removes the specified element from a WeakMap object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/delete"
      },
      "get": {
        "!type": "fn(key: ?) -> !this.:value",
        "!doc": "The get() method returns a specified element from a WeakMap object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/get"
      },
      "has": {
        "!type": "fn(key: ?) -> bool",
        "!doc": "The has() method returns a boolean indicating whether an element with the specified key exists in the WeakMap object or not.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/has"
      },
      "set": {
        "!type": "fn(key: ?, value: ?)",
        "!effects": ["propagate !0 !this.:key", "propagate !1 !this.:value"],
        "!doc": "The set() method adds a new element with a specified key and value to a WeakMap object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/set"
      }
    }
  },
  "WeakSet": {
    "!type": "fn(iterable?: [?])",
    "!doc": "The WeakSet object lets you store weakly held objects in a collection.",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet",
    "prototype": {
      "add": {
        "!type": "fn(value: ?)",
        "!doc": "The add() method appends a new object to the end of a WeakSet object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet/add"
      },
      "delete": {
        "!type": "fn(value: ?) -> bool",
        "!doc": "The delete() method removes the specified element from a WeakSet object.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet/delete"
      },
      "has": {
        "!type": "fn(value: ?) -> bool",
        "!doc": "The has() method returns a boolean indicating whether an object exists in a WeakSet or not.",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet/has"
      }
    }
  }
}
;});


define("json!tern/defs/ecma7.json", function(){ return {
  "!name": "ecma7",
  "Array": {
    "prototype": {
      "includes": {
        "!type": "fn(value: ?) -> bool",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes",
        "!doc": "Determines whether an array includes a certain element, returning true or false as appropriate."
      }
    }
  }
};});


define("json!tern/defs/browser.json", function(){ return {
  "!name": "browser",
  "location": {
    "assign": {
      "!type": "fn(url: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "Load the document at the provided URL."
    },
    "replace": {
      "!type": "fn(url: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "Replace the current document with the one at the provided URL. The difference from the assign() method is that after using replace() the current page will not be saved in session history, meaning the user won't be able to use the Back button to navigate to it."
    },
    "reload": {
      "!type": "fn()",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "Reload the document from the current URL. forceget is a boolean, which, when it is true, causes the page to always be reloaded from the server. If it is false or not specified, the browser may reload the page from its cache."
    },
    "origin": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The origin of the URL."
    },
    "hash": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The part of the URL that follows the # symbol, including the # symbol."
    },
    "search": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The part of the URL that follows the ? symbol, including the ? symbol."
    },
    "pathname": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The path (relative to the host)."
    },
    "port": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The port number of the URL."
    },
    "hostname": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The host name (without the port number or square brackets)."
    },
    "host": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The host name and port number."
    },
    "protocol": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The protocol of the URL."
    },
    "href": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The entire URL."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
    "!doc": "Returns a location object with information about the current location of the document. Assigning to the location property changes the current page to the new address."
  },
  "Node": {
    "!type": "fn()",
    "prototype": {
      "parentElement": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.parentElement",
        "!doc": "Returns the DOM node's parent Element, or null if the node either has no parent, or its parent isn't a DOM Element."
      },
      "textContent": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.textContent",
        "!doc": "Gets or sets the text content of a node and its descendants."
      },
      "baseURI": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.baseURI",
        "!doc": "The absolute base URI of a node or null if unable to obtain an absolute URI."
      },
      "localName": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.localName",
        "!doc": "Returns the local part of the qualified name of this node."
      },
      "prefix": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.prefix",
        "!doc": "Returns the namespace prefix of the specified node, or null if no prefix is specified. This property is read only."
      },
      "namespaceURI": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.namespaceURI",
        "!doc": "The namespace URI of the node, or null if the node is not in a namespace (read-only). When the node is a document, it returns the XML namespace for the current document."
      },
      "ownerDocument": {
        "!type": "+Document",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.ownerDocument",
        "!doc": "The ownerDocument property returns the top-level document object for this node."
      },
      "attributes": {
        "!type": "+NamedNodeMap",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.attributes",
        "!doc": "A collection of all attribute nodes registered to the specified node. It is a NamedNodeMap,not an Array, so it has no Array methods and the Attr nodes' indexes may differ among browsers."
      },
      "nextSibling": {
        "!type": "+Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nextSibling",
        "!doc": "Returns the node immediately following the specified one in its parent's childNodes list, or null if the specified node is the last node in that list."
      },
      "previousSibling": {
        "!type": "+Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.previousSibling",
        "!doc": "Returns the node immediately preceding the specified one in its parent's childNodes list, null if the specified node is the first in that list."
      },
      "lastChild": {
        "!type": "+Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.lastChild",
        "!doc": "Returns the last child of a node."
      },
      "firstChild": {
        "!type": "+Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.firstChild",
        "!doc": "Returns the node's first child in the tree, or null if the node is childless. If the node is a Document, it returns the first node in the list of its direct children."
      },
      "childNodes": {
        "!type": "+NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.childNodes",
        "!doc": "Returns a collection of child nodes of the given element."
      },
      "parentNode": {
        "!type": "+Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.parentNode",
        "!doc": "Returns the parent of the specified node in the DOM tree."
      },
      "nodeType": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nodeType",
        "!doc": "Returns an integer code representing the type of the node."
      },
      "nodeValue": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nodeValue",
        "!doc": "Returns or sets the value of the current node."
      },
      "nodeName": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nodeName",
        "!doc": "Returns the name of the current node as a string."
      },
      "tagName": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nodeName",
        "!doc": "Returns the name of the current node as a string."
      },
      "insertBefore": {
        "!type": "fn(newElt: +Element, before: +Element) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.insertBefore",
        "!doc": "Inserts the specified node before a reference element as a child of the current node."
      },
      "replaceChild": {
        "!type": "fn(newChild: +Node, oldChild: +Node) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.replaceChild",
        "!doc": "Replaces one child node of the specified element with another."
      },
      "removeChild": {
        "!type": "fn(oldNode: +Node) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.removeChild",
        "!doc": "Removes a child node from the DOM. Returns removed node."
      },
      "appendChild": {
        "!type": "fn(newNode: +Node) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.appendChild",
        "!doc": "Adds a node to the end of the list of children of a specified parent node. If the node already exists it is removed from current parent node, then added to new parent node."
      },
      "hasChildNodes": {
        "!type": "fn() -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.hasChildNodes",
        "!doc": "Returns a Boolean value indicating whether the current Node has child nodes or not."
      },
      "cloneNode": {
        "!type": "fn(deep: bool) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.cloneNode",
        "!doc": "Returns a duplicate of the node on which this method was called."
      },
      "normalize": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.normalize",
        "!doc": "Puts the specified node and all of its subtree into a \"normalized\" form. In a normalized subtree, no text nodes in the subtree are empty and there are no adjacent text nodes."
      },
      "isSupported": {
        "!type": "fn(features: string, version: number) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.isSupported",
        "!doc": "Tests whether the DOM implementation implements a specific feature and that feature is supported by this node."
      },
      "hasAttributes": {
        "!type": "fn() -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.hasAttributes",
        "!doc": "Returns a boolean value of true or false, indicating if the current element has any attributes or not."
      },
      "lookupPrefix": {
        "!type": "fn(uri: string) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.lookupPrefix",
        "!doc": "Returns the prefix for a given namespaceURI if present, and null if not. When multiple prefixes are possible, the result is implementation-dependent."
      },
      "isDefaultNamespace": {
        "!type": "fn(uri: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.isDefaultNamespace",
        "!doc": "Accepts a namespace URI as an argument and returns true if the namespace is the default namespace on the given node or false if not."
      },
      "lookupNamespaceURI": {
        "!type": "fn(uri: string) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.lookupNamespaceURI",
        "!doc": "Takes a prefix and returns the namespaceURI associated with it on the given node if found (and null if not). Supplying null for the prefix will return the default namespace."
      },
      "addEventListener": {
        "!type": "fn(type: string, listener: fn(e: +Event), capture: bool)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.addEventListener",
        "!doc": "Registers a single event listener on a single target. The event target may be a single element in a document, the document itself, a window, or an XMLHttpRequest."
      },
      "removeEventListener": {
        "!type": "fn(type: string, listener: fn(), capture: bool)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.removeEventListener",
        "!doc": "Allows the removal of event listeners from the event target."
      },
      "isSameNode": {
        "!type": "fn(other: +Node) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.isSameNode",
        "!doc": "Tests whether two nodes are the same, that is they reference the same object."
      },
      "isEqualNode": {
        "!type": "fn(other: +Node) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.isEqualNode",
        "!doc": "Tests whether two nodes are equal."
      },
      "compareDocumentPosition": {
        "!type": "fn(other: +Node) -> number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.compareDocumentPosition",
        "!doc": "Compares the position of the current node against another node in any other document."
      },
      "contains": {
        "!type": "fn(other: +Node) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.contains",
        "!doc": "Indicates whether a node is a descendent of a given node."
      },
      "dispatchEvent": {
        "!type": "fn(event: +Event) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.dispatchEvent",
        "!doc": "Dispatches an event into the event system. The event is subject to the same capturing and bubbling behavior as directly dispatched events."
      },
      "ELEMENT_NODE": "number",
      "ATTRIBUTE_NODE": "number",
      "TEXT_NODE": "number",
      "CDATA_SECTION_NODE": "number",
      "ENTITY_REFERENCE_NODE": "number",
      "ENTITY_NODE": "number",
      "PROCESSING_INSTRUCTION_NODE": "number",
      "COMMENT_NODE": "number",
      "DOCUMENT_NODE": "number",
      "DOCUMENT_TYPE_NODE": "number",
      "DOCUMENT_FRAGMENT_NODE": "number",
      "NOTATION_NODE": "number",
      "DOCUMENT_POSITION_DISCONNECTED": "number",
      "DOCUMENT_POSITION_PRECEDING": "number",
      "DOCUMENT_POSITION_FOLLOWING": "number",
      "DOCUMENT_POSITION_CONTAINS": "number",
      "DOCUMENT_POSITION_CONTAINED_BY": "number",
      "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC": "number"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Node",
    "!doc": "A Node is an interface from which a number of DOM types inherit, and allows these various types to be treated (or tested) similarly."
  },
  "Element": {
    "!type": "fn()",
    "prototype": {
      "!proto": "Node.prototype",
      "getAttribute": {
        "!type": "fn(name: string) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getAttribute",
        "!doc": "Returns the value of the named attribute on the specified element. If the named attribute does not exist, the value returned will either be null or \"\" (the empty string)."
      },
      "setAttribute": {
        "!type": "fn(name: string, value: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.setAttribute",
        "!doc": "Adds a new attribute or changes the value of an existing attribute on the specified element."
      },
      "removeAttribute": {
        "!type": "fn(name: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.removeAttribute",
        "!doc": "Removes an attribute from the specified element."
      },
      "getAttributeNode": {
        "!type": "fn(name: string) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getAttributeNode",
        "!doc": "Returns the specified attribute of the specified element, as an Attr node."
      },
      "getElementsByTagName": {
        "!type": "fn(tagName: string) -> +NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getElementsByTagName",
        "!doc": "Returns a list of elements with the given tag name. The subtree underneath the specified element is searched, excluding the element itself. The returned list is live, meaning that it updates itself with the DOM tree automatically. Consequently, there is no need to call several times element.getElementsByTagName with the same element and arguments."
      },
      "getElementsByTagNameNS": {
        "!type": "fn(ns: string, tagName: string) -> +NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getElementsByTagNameNS",
        "!doc": "Returns a list of elements with the given tag name belonging to the given namespace."
      },
      "getAttributeNS": {
        "!type": "fn(ns: string, name: string) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getAttributeNS",
        "!doc": "Returns the string value of the attribute with the specified namespace and name. If the named attribute does not exist, the value returned will either be null or \"\" (the empty string)."
      },
      "setAttributeNS": {
        "!type": "fn(ns: string, name: string, value: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.setAttributeNS",
        "!doc": "Adds a new attribute or changes the value of an attribute with the given namespace and name."
      },
      "removeAttributeNS": {
        "!type": "fn(ns: string, name: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.removeAttributeNS",
        "!doc": "removeAttributeNS removes the specified attribute from an element."
      },
      "getAttributeNodeNS": {
        "!type": "fn(ns: string, name: string) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getAttributeNodeNS",
        "!doc": "Returns the Attr node for the attribute with the given namespace and name."
      },
      "hasAttribute": {
        "!type": "fn(name: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.hasAttribute",
        "!doc": "hasAttribute returns a boolean value indicating whether the specified element has the specified attribute or not."
      },
      "hasAttributeNS": {
        "!type": "fn(ns: string, name: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.hasAttributeNS",
        "!doc": "hasAttributeNS returns a boolean value indicating whether the current element has the specified attribute."
      },
      "focus": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.focus",
        "!doc": "Sets focus on the specified element, if it can be focused."
      },
      "blur": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.blur",
        "!doc": "The blur method removes keyboard focus from the current element."
      },
      "scrollIntoView": {
        "!type": "fn(top: bool)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollIntoView",
        "!doc": "The scrollIntoView() method scrolls the element into view."
      },
      "scrollByLines": {
        "!type": "fn(lines: number)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollByLines",
        "!doc": "Scrolls the document by the given number of lines."
      },
      "scrollByPages": {
        "!type": "fn(pages: number)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollByPages",
        "!doc": "Scrolls the current document by the specified number of pages."
      },
      "getElementsByClassName": {
        "!type": "fn(name: string) -> +NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementsByClassName",
        "!doc": "Returns a set of elements which have all the given class names. When called on the document object, the complete document is searched, including the root node. You may also call getElementsByClassName on any element; it will return only elements which are descendants of the specified root element with the given class names."
      },
      "querySelector": {
        "!type": "fn(selectors: string) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.querySelector",
        "!doc": "Returns the first element that is a descendent of the element on which it is invoked that matches the specified group of selectors."
      },
      "querySelectorAll": {
        "!type": "fn(selectors: string) -> +NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.querySelectorAll",
        "!doc": "Returns a non-live NodeList of all elements descended from the element on which it is invoked that match the specified group of CSS selectors."
      },
      "getClientRects": {
        "!type": "fn() -> [+ClientRect]",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects",
        "!doc": "Returns a collection of rectangles that indicate the bounding rectangles for each box in a client."
      },
      "getBoundingClientRect": {
        "!type": "fn() -> +ClientRect",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getBoundingClientRect",
        "!doc": "Returns a text rectangle object that encloses a group of text rectangles."
      },
      "setAttributeNode": {
        "!type": "fn(attr: +Attr) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.setAttributeNode",
        "!doc": "Adds a new Attr node to the specified element."
      },
      "removeAttributeNode": {
        "!type": "fn(attr: +Attr) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.removeAttributeNode",
        "!doc": "Removes the specified attribute from the current element."
      },
      "setAttributeNodeNS": {
        "!type": "fn(attr: +Attr) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.setAttributeNodeNS",
        "!doc": "Adds a new namespaced attribute node to an element."
      },
      "insertAdjacentHTML": {
        "!type": "fn(position: string, text: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.insertAdjacentHTML",
        "!doc": "Parses the specified text as HTML or XML and inserts the resulting nodes into the DOM tree at a specified position. It does not reparse the element it is being used on and thus it does not corrupt the existing elements inside the element. This, and avoiding the extra step of serialization make it much faster than direct innerHTML manipulation."
      },
      "children": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.children",
        "!doc": "Returns a collection of child elements of the given element."
      },
      "childElementCount": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.childElementCount",
        "!doc": "Returns the number of child elements of the given element."
      },
      "className": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.className",
        "!doc": "Gets and sets the value of the class attribute of the specified element."
      },
      "style": {
        "cssText": "string",
        "alignmentBaseline": "string",
        "background": "string",
        "backgroundAttachment": "string",
        "backgroundClip": "string",
        "backgroundColor": "string",
        "backgroundImage": "string",
        "backgroundOrigin": "string",
        "backgroundPosition": "string",
        "backgroundPositionX": "string",
        "backgroundPositionY": "string",
        "backgroundRepeat": "string",
        "backgroundRepeatX": "string",
        "backgroundRepeatY": "string",
        "backgroundSize": "string",
        "baselineShift": "string",
        "border": "string",
        "borderBottom": "string",
        "borderBottomColor": "string",
        "borderBottomLeftRadius": "string",
        "borderBottomRightRadius": "string",
        "borderBottomStyle": "string",
        "borderBottomWidth": "string",
        "borderCollapse": "string",
        "borderColor": "string",
        "borderImage": "string",
        "borderImageOutset": "string",
        "borderImageRepeat": "string",
        "borderImageSlice": "string",
        "borderImageSource": "string",
        "borderImageWidth": "string",
        "borderLeft": "string",
        "borderLeftColor": "string",
        "borderLeftStyle": "string",
        "borderLeftWidth": "string",
        "borderRadius": "string",
        "borderRight": "string",
        "borderRightColor": "string",
        "borderRightStyle": "string",
        "borderRightWidth": "string",
        "borderSpacing": "string",
        "borderStyle": "string",
        "borderTop": "string",
        "borderTopColor": "string",
        "borderTopLeftRadius": "string",
        "borderTopRightRadius": "string",
        "borderTopStyle": "string",
        "borderTopWidth": "string",
        "borderWidth": "string",
        "bottom": "string",
        "boxShadow": "string",
        "boxSizing": "string",
        "captionSide": "string",
        "clear": "string",
        "clip": "string",
        "clipPath": "string",
        "clipRule": "string",
        "color": "string",
        "colorInterpolation": "string",
        "colorInterpolationFilters": "string",
        "colorProfile": "string",
        "colorRendering": "string",
        "content": "string",
        "counterIncrement": "string",
        "counterReset": "string",
        "cursor": "string",
        "direction": "string",
        "display": "string",
        "dominantBaseline": "string",
        "emptyCells": "string",
        "enableBackground": "string",
        "fill": "string",
        "fillOpacity": "string",
        "fillRule": "string",
        "filter": "string",
        "float": "string",
        "floodColor": "string",
        "floodOpacity": "string",
        "font": "string",
        "fontFamily": "string",
        "fontSize": "string",
        "fontStretch": "string",
        "fontStyle": "string",
        "fontVariant": "string",
        "fontWeight": "string",
        "glyphOrientationHorizontal": "string",
        "glyphOrientationVertical": "string",
        "height": "string",
        "imageRendering": "string",
        "kerning": "string",
        "left": "string",
        "letterSpacing": "string",
        "lightingColor": "string",
        "lineHeight": "string",
        "listStyle": "string",
        "listStyleImage": "string",
        "listStylePosition": "string",
        "listStyleType": "string",
        "margin": "string",
        "marginBottom": "string",
        "marginLeft": "string",
        "marginRight": "string",
        "marginTop": "string",
        "marker": "string",
        "markerEnd": "string",
        "markerMid": "string",
        "markerStart": "string",
        "mask": "string",
        "maxHeight": "string",
        "maxWidth": "string",
        "minHeight": "string",
        "minWidth": "string",
        "opacity": "string",
        "orphans": "string",
        "outline": "string",
        "outlineColor": "string",
        "outlineOffset": "string",
        "outlineStyle": "string",
        "outlineWidth": "string",
        "overflow": "string",
        "overflowWrap": "string",
        "overflowX": "string",
        "overflowY": "string",
        "padding": "string",
        "paddingBottom": "string",
        "paddingLeft": "string",
        "paddingRight": "string",
        "paddingTop": "string",
        "page": "string",
        "pageBreakAfter": "string",
        "pageBreakBefore": "string",
        "pageBreakInside": "string",
        "pointerEvents": "string",
        "position": "string",
        "quotes": "string",
        "resize": "string",
        "right": "string",
        "shapeRendering": "string",
        "size": "string",
        "speak": "string",
        "src": "string",
        "stopColor": "string",
        "stopOpacity": "string",
        "stroke": "string",
        "strokeDasharray": "string",
        "strokeDashoffset": "string",
        "strokeLinecap": "string",
        "strokeLinejoin": "string",
        "strokeMiterlimit": "string",
        "strokeOpacity": "string",
        "strokeWidth": "string",
        "tabSize": "string",
        "tableLayout": "string",
        "textAlign": "string",
        "textAnchor": "string",
        "textDecoration": "string",
        "textIndent": "string",
        "textLineThrough": "string",
        "textLineThroughColor": "string",
        "textLineThroughMode": "string",
        "textLineThroughStyle": "string",
        "textLineThroughWidth": "string",
        "textOverflow": "string",
        "textOverline": "string",
        "textOverlineColor": "string",
        "textOverlineMode": "string",
        "textOverlineStyle": "string",
        "textOverlineWidth": "string",
        "textRendering": "string",
        "textShadow": "string",
        "textTransform": "string",
        "textUnderline": "string",
        "textUnderlineColor": "string",
        "textUnderlineMode": "string",
        "textUnderlineStyle": "string",
        "textUnderlineWidth": "string",
        "top": "string",
        "unicodeBidi": "string",
        "unicodeRange": "string",
        "vectorEffect": "string",
        "verticalAlign": "string",
        "visibility": "string",
        "whiteSpace": "string",
        "width": "string",
        "wordBreak": "string",
        "wordSpacing": "string",
        "wordWrap": "string",
        "writingMode": "string",
        "zIndex": "string",
        "zoom": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.style",
        "!doc": "Returns an object that represents the element's style attribute."
      },
      "classList": {
        "!type": "+DOMTokenList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.classList",
        "!doc": "Returns a token list of the class attribute of the element."
      },
      "contentEditable": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.contentEditable",
        "!doc": "Indicates whether or not the element is editable."
      },
      "firstElementChild": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.firstElementChild",
        "!doc": "Returns the element's first child element or null if there are no child elements."
      },
      "lastElementChild": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.lastElementChild",
        "!doc": "Returns the element's last child element or null if there are no child elements."
      },
      "nextElementSibling": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.nextElementSibling",
        "!doc": "Returns the element immediately following the specified one in its parent's children list, or null if the specified element is the last one in the list."
      },
      "previousElementSibling": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.previousElementSibling",
        "!doc": "Returns the element immediately prior to the specified one in its parent's children list, or null if the specified element is the first one in the list."
      },
      "tabIndex": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.tabIndex",
        "!doc": "Gets/sets the tab order of the current element."
      },
      "title": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.title",
        "!doc": "Establishes the text to be displayed in a 'tool tip' popup when the mouse is over the displayed node."
      },
      "width": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetWidth",
        "!doc": "Returns the layout width of an element."
      },
      "height": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetHeight",
        "!doc": "Height of an element relative to the element's offsetParent."
      },
      "getContext": {
        "!type": "fn(id: string) -> CanvasRenderingContext2D",
        "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCanvasElement",
        "!doc": "DOM canvas elements expose the HTMLCanvasElement interface, which provides properties and methods for manipulating the layout and presentation of canvas elements. The HTMLCanvasElement interface inherits the properties and methods of the element object interface."
      },
      "supportsContext": "fn(id: string) -> bool",
      "oncopy": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.oncopy",
        "!doc": "The oncopy property returns the onCopy event handler code on the current element."
      },
      "oncut": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.oncut",
        "!doc": "The oncut property returns the onCut event handler code on the current element."
      },
      "onpaste": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onpaste",
        "!doc": "The onpaste property returns the onPaste event handler code on the current element."
      },
      "onbeforeunload": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/HTML/Element/body",
        "!doc": "The HTML <body> element represents the main content of an HTML document. There is only one <body> element in a document."
      },
      "onfocus": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onfocus",
        "!doc": "The onfocus property returns the onFocus event handler code on the current element."
      },
      "onblur": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onblur",
        "!doc": "The onblur property returns the onBlur event handler code, if any, that exists on the current element."
      },
      "onchange": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onchange",
        "!doc": "The onchange property sets and returns the onChange event handler code for the current element."
      },
      "onclick": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onclick",
        "!doc": "The onclick property returns the onClick event handler code on the current element."
      },
      "ondblclick": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.ondblclick",
        "!doc": "The ondblclick property returns the onDblClick event handler code on the current element."
      },
      "onmousedown": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmousedown",
        "!doc": "The onmousedown property returns the onMouseDown event handler code on the current element."
      },
      "onmouseup": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseup",
        "!doc": "The onmouseup property returns the onMouseUp event handler code on the current element."
      },
      "onmousewheel": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/wheel",
        "!doc": "The wheel event is fired when a wheel button of a pointing device (usually a mouse) is rotated. This event deprecates the legacy mousewheel event."
      },
      "onmouseover": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseover",
        "!doc": "The onmouseover property returns the onMouseOver event handler code on the current element."
      },
      "onmouseout": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseout",
        "!doc": "The onmouseout property returns the onMouseOut event handler code on the current element."
      },
      "onmousemove": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmousemove",
        "!doc": "The onmousemove property returns the mousemove event handler code on the current element."
      },
      "oncontextmenu": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/window.oncontextmenu",
        "!doc": "An event handler property for right-click events on the window. Unless the default behavior is prevented, the browser context menu will activate. Note that this event will occur with any non-disabled right-click event and does not depend on an element possessing the \"contextmenu\" attribute."
      },
      "onkeydown": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeydown",
        "!doc": "The onkeydown property returns the onKeyDown event handler code on the current element."
      },
      "onkeyup": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeyup",
        "!doc": "The onkeyup property returns the onKeyUp event handler code for the current element."
      },
      "onkeypress": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeypress",
        "!doc": "The onkeypress property sets and returns the onKeyPress event handler code for the current element."
      },
      "onresize": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onresize",
        "!doc": "onresize returns the element's onresize event handler code. It can also be used to set the code to be executed when the resize event occurs."
      },
      "onscroll": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onscroll",
        "!doc": "The onscroll property returns the onScroll event handler code on the current element."
      },
      "ondragstart": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations",
        "!doc": "The following describes the steps that occur during a drag and drop operation."
      },
      "ondragover": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/dragover",
        "!doc": "The dragover event is fired when an element or text selection is being dragged over a valid drop target (every few hundred milliseconds)."
      },
      "ondragleave": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/dragleave",
        "!doc": "The dragleave event is fired when a dragged element or text selection leaves a valid drop target."
      },
      "ondragenter": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/dragenter",
        "!doc": "The dragenter event is fired when a dragged element or text selection enters a valid drop target."
      },
      "ondragend": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/dragend",
        "!doc": "The dragend event is fired when a drag operation is being ended (by releasing a mouse button or hitting the escape key)."
      },
      "ondrag": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/drag",
        "!doc": "The drag event is fired when an element or text selection is being dragged (every few hundred milliseconds)."
      },
      "offsetTop": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetTop",
        "!doc": "Returns the distance of the current element relative to the top of the offsetParent node."
      },
      "offsetLeft": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetLeft",
        "!doc": "Returns the number of pixels that the upper left corner of the current element is offset to the left within the offsetParent node."
      },
      "offsetHeight": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetHeight",
        "!doc": "Height of an element relative to the element's offsetParent."
      },
      "offsetWidth": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetWidth",
        "!doc": "Returns the layout width of an element."
      },
      "scrollTop": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollTop",
        "!doc": "Gets or sets the number of pixels that the content of an element is scrolled upward."
      },
      "scrollLeft": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollLeft",
        "!doc": "Gets or sets the number of pixels that an element's content is scrolled to the left."
      },
      "scrollHeight": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollHeight",
        "!doc": "Height of the scroll view of an element; it includes the element padding but not its margin."
      },
      "scrollWidth": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollWidth",
        "!doc": "Read-only property that returns either the width in pixels of the content of an element or the width of the element itself, whichever is greater."
      },
      "clientTop": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.clientTop",
        "!doc": "The width of the top border of an element in pixels. It does not include the top margin or padding. clientTop is read-only."
      },
      "clientLeft": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.clientLeft",
        "!doc": "The width of the left border of an element in pixels. It includes the width of the vertical scrollbar if the text direction of the element is right-to-left and if there is an overflow causing a left vertical scrollbar to be rendered. clientLeft does not include the left margin or the left padding. clientLeft is read-only."
      },
      "clientHeight": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.clientHeight",
        "!doc": "Returns the inner height of an element in pixels, including padding but not the horizontal scrollbar height, border, or margin."
      },
      "clientWidth": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.clientWidth",
        "!doc": "The inner width of an element in pixels. It includes padding but not the vertical scrollbar (if present, if rendered), border or margin."
      },
      "innerHTML": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.innerHTML",
        "!doc": "Sets or gets the HTML syntax describing the element's descendants."
      },
      "createdCallback": {
        "!type": "fn()",
        "!url": "https://w3c.github.io/webcomponents/spec/custom/index.html#dfn-created-callback",
        "!doc": "This callback is invoked after custom element instance is created and its definition is registered. The actual timing of this callback is defined further in this specification."
      },
      "attachedCallback": {
        "!type": "fn()",
        "!url": "https://w3c.github.io/webcomponents/spec/custom/index.html#dfn-entered-view-callback",
        "!doc": "Unless specified otherwise, this callback must be enqueued whenever custom element is inserted into a document and this document has a browsing context."
      },
      "detachedCallback": {
        "!type": "fn()",
        "!url": "https://w3c.github.io/webcomponents/spec/custom/index.html#dfn-left-view-callback",
        "!doc": "Unless specified otherwise, this callback must be enqueued whenever custom element is removed from the document and this document has a browsing context."
      },
      "attributeChangedCallback": {
        "!type": "fn()",
        "!url": "https://w3c.github.io/webcomponents/spec/custom/index.html#dfn-attribute-changed-callback",
        "!doc": "Unless specified otherwise, this callback must be enqueued whenever custom element's attribute is added, changed or removed."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Element",
    "!doc": "Represents an element in an HTML or XML document."
  },
  "Text": {
    "!type": "fn()",
    "prototype": {
      "!proto": "Node.prototype",
      "wholeText": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Text.wholeText",
        "!doc": "Returns all text of all Text nodes logically adjacent to the node.  The text is concatenated in document order.  This allows you to specify any text node and obtain all adjacent text as a single string."
      },
      "splitText": {
        "!type": "fn(offset: number) -> +Text",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Text.splitText",
        "!doc": "Breaks the Text node into two nodes at the specified offset, keeping both nodes in the tree as siblings."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Text",
    "!doc": "In the DOM, the Text interface represents the textual content of an Element or Attr.  If an element has no markup within its content, it has a single child implementing Text that contains the element's text.  However, if the element contains markup, it is parsed into information items and Text nodes that form its children."
  },
  "Document": {
    "!type": "fn()",
    "prototype": {
      "!proto": "Node.prototype",
      "activeElement": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.activeElement",
        "!doc": "Returns the currently focused element, that is, the element that will get keystroke events if the user types any. This attribute is read only."
      },
      "compatMode": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.compatMode",
        "!doc": "Indicates whether the document is rendered in Quirks mode or Strict mode."
      },
      "designMode": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.designMode",
        "!doc": "Can be used to make any document editable, for example in a <iframe />:"
      },
      "dir": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Document.dir",
        "!doc": "This property should indicate and allow the setting of the directionality of the text of the document, whether left to right (default) or right to left."
      },
      "height": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.height",
        "!doc": "Returns the height of the <body> element of the current document."
      },
      "width": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.width",
        "!doc": "Returns the width of the <body> element of the current document in pixels."
      },
      "characterSet": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.characterSet",
        "!doc": "Returns the character encoding of the current document."
      },
      "readyState": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.readyState",
        "!doc": "Returns \"loading\" while the document is loading, \"interactive\" once it is finished parsing but still loading sub-resources, and \"complete\" once it has loaded."
      },
      "location": {
        "!type": "location",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.location",
        "!doc": "Returns a Location object, which contains information about the URL of the document and provides methods for changing that URL."
      },
      "lastModified": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.lastModified",
        "!doc": "Returns a string containing the date and time on which the current document was last modified."
      },
      "head": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.head",
        "!doc": "Returns the <head> element of the current document. If there are more than one <head> elements, the first one is returned."
      },
      "body": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.body",
        "!doc": "Returns the <body> or <frameset> node of the current document."
      },
      "cookie": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.cookie",
        "!doc": "Get and set the cookies associated with the current document."
      },
      "URL": "string",
      "domain": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.domain",
        "!doc": "Gets/sets the domain portion of the origin of the current document, as used by the same origin policy."
      },
      "referrer": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.referrer",
        "!doc": "Returns the URI of the page that linked to this page."
      },
      "title": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.title",
        "!doc": "Gets or sets the title of the document."
      },
      "defaultView": {
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.defaultView",
        "!doc": "In browsers returns the window object associated with the document or null if none available."
      },
      "documentURI": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.documentURI",
        "!doc": "Returns the document location as string. It is read-only per DOM4 specification."
      },
      "xmlStandalone": "bool",
      "xmlVersion": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.xmlVersion",
        "!doc": "Returns the version number as specified in the XML declaration (e.g., <?xml version=\"1.0\"?>) or \"1.0\" if the declaration is absent."
      },
      "xmlEncoding": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Document.xmlEncoding",
        "!doc": "Returns the encoding as determined by the XML declaration. Should be null if unspecified or unknown."
      },
      "inputEncoding": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.inputEncoding",
        "!doc": "Returns a string representing the encoding under which the document was parsed (e.g. ISO-8859-1)."
      },
      "documentElement": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.documentElement",
        "!doc": "Read-only"
      },
      "implementation": {
        "hasFeature": "fn(feature: string, version: number) -> bool",
        "createDocumentType": {
          "!type": "fn(qualifiedName: string, publicId: string, systemId: string) -> +Node",
          "!url": "https://developer.mozilla.org/en/docs/DOM/DOMImplementation.createDocumentType",
          "!doc": "Returns a DocumentType object which can either be used with DOMImplementation.createDocument upon document creation or they can be put into the document via Node.insertBefore() or Node.replaceChild(): https://www.w3.org/TR/DOM-Level-3-Cor...l#ID-B63ED1A31 (less ideal due to features not likely being as accessible: https://www.w3.org/TR/DOM-Level-3-Cor...createDocument ). In any case, entity declarations and notations will not be available: https://www.w3.org/TR/DOM-Level-3-Cor...-createDocType   "
        },
        "createHTMLDocument": {
          "!type": "fn(title: string) -> +Document",
          "!url": "https://developer.mozilla.org/en/docs/DOM/DOMImplementation.createHTMLDocument",
          "!doc": "This method (available from document.implementation) creates a new HTML document."
        },
        "createDocument": {
          "!type": "fn(namespaceURI: string, qualifiedName: string, type: +Node) -> +Document",
          "!url": "https://developer.mozilla.org/en-US/docs/DOM/DOMImplementation.createHTMLDocument",
          "!doc": "This method creates a new HTML document."
        },
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.implementation",
        "!doc": "Returns a DOMImplementation object associated with the current document."
      },
      "doctype": {
        "!type": "+Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.doctype",
        "!doc": "Returns the Document Type Declaration (DTD) associated with current document. The returned object implements the DocumentType interface. Use DOMImplementation.createDocumentType() to create a DocumentType."
      },
      "open": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.open",
        "!doc": "The document.open() method opens a document for writing."
      },
      "close": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.close",
        "!doc": "The document.close() method finishes writing to a document, opened with document.open()."
      },
      "write": {
        "!type": "fn(html: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.write",
        "!doc": "Writes a string of text to a document stream opened by document.open()."
      },
      "writeln": {
        "!type": "fn(html: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.writeln",
        "!doc": "Writes a string of text followed by a newline character to a document."
      },
      "clear": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.clear",
        "!doc": "In recent versions of Mozilla-based applications as well as in Internet Explorer and Netscape 4 this method does nothing."
      },
      "hasFocus": {
        "!type": "fn() -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.hasFocus",
        "!doc": "Returns a Boolean value indicating whether the document or any element inside the document has focus. This method can be used to determine whether the active element in a document has focus."
      },
      "createElement": {
        "!type": "fn(tagName: string) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createElement",
        "!doc": "Creates the specified element."
      },
      "createElementNS": {
        "!type": "fn(ns: string, tagName: string) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createElementNS",
        "!doc": "Creates an element with the specified namespace URI and qualified name."
      },
      "createDocumentFragment": {
        "!type": "fn() -> +DocumentFragment",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createDocumentFragment",
        "!doc": "Creates a new empty DocumentFragment."
      },
      "createTextNode": {
        "!type": "fn(content: string) -> +Text",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createTextNode",
        "!doc": "Creates a new Text node."
      },
      "createComment": {
        "!type": "fn(content: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createComment",
        "!doc": "Creates a new comment node, and returns it."
      },
      "createCDATASection": {
        "!type": "fn(content: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createCDATASection",
        "!doc": "Creates a new CDATA section node, and returns it. "
      },
      "createProcessingInstruction": {
        "!type": "fn(content: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createProcessingInstruction",
        "!doc": "Creates a new processing instruction node, and returns it."
      },
      "createAttribute": {
        "!type": "fn(name: string) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createAttribute",
        "!doc": "Creates a new attribute node, and returns it."
      },
      "createAttributeNS": {
        "!type": "fn(ns: string, name: string) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Attr",
        "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types."
      },
      "importNode": {
        "!type": "fn(node: +Node, deep: bool) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.importNode",
        "!doc": "Creates a copy of a node from an external document that can be inserted into the current document."
      },
      "getElementById": {
        "!type": "fn(id: string) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementById",
        "!doc": "Returns a reference to the element by its ID."
      },
      "getElementsByTagName": {
        "!type": "fn(tagName: string) -> +NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementsByTagName",
        "!doc": "Returns a NodeList of elements with the given tag name. The complete document is searched, including the root node. The returned NodeList is live, meaning that it updates itself automatically to stay in sync with the DOM tree without having to call document.getElementsByTagName again."
      },
      "getElementsByTagNameNS": {
        "!type": "fn(ns: string, tagName: string) -> +NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementsByTagNameNS",
        "!doc": "Returns a list of elements with the given tag name belonging to the given namespace. The complete document is searched, including the root node."
      },
      "createEvent": {
        "!type": "fn(type: string) -> +Event",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createEvent",
        "!doc": "Creates an event of the type specified. The returned object should be first initialized and can then be passed to element.dispatchEvent."
      },
      "createRange": {
        "!type": "fn() -> +Range",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createRange",
        "!doc": "Returns a new Range object."
      },
      "evaluate": {
        "!type": "fn(expr: ?) -> +XPathResult",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.evaluate",
        "!doc": "Returns an XPathResult based on an XPath expression and other given parameters."
      },
      "execCommand": {
        "!type": "fn(cmd: string)",
        "!url": "https://developer.mozilla.org/en-US/docs/Rich-Text_Editing_in_Mozilla#Executing_Commands",
        "!doc": "Run command to manipulate the contents of an editable region."
      },
      "queryCommandEnabled": {
        "!type": "fn(cmd: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document",
        "!doc": "Returns true if the Midas command can be executed on the current range."
      },
      "queryCommandIndeterm": {
        "!type": "fn(cmd: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document",
        "!doc": "Returns true if the Midas command is in a indeterminate state on the current range."
      },
      "queryCommandState": {
        "!type": "fn(cmd: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document",
        "!doc": "Returns true if the Midas command has been executed on the current range."
      },
      "queryCommandSupported": {
        "!type": "fn(cmd: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.queryCommandSupported",
        "!doc": "Reports whether or not the specified editor query command is supported by the browser."
      },
      "queryCommandValue": {
        "!type": "fn(cmd: string) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document",
        "!doc": "Returns the current value of the current range for Midas command."
      },
      "getElementsByName": {
        "!type": "fn(name: string) -> +HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementsByName",
        "!doc": "Returns a list of elements with a given name in the HTML document."
      },
      "elementFromPoint": {
        "!type": "fn(x: number, y: number) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.elementFromPoint",
        "!doc": "Returns the element from the document whose elementFromPoint method is being called which is the topmost element which lies under the given point.  To get an element, specify the point via coordinates, in CSS pixels, relative to the upper-left-most point in the window or frame containing the document."
      },
      "getSelection": {
        "!type": "fn() -> +Selection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.getSelection",
        "!doc": "The DOM getSelection() method is available on the Window and Document interfaces."
      },
      "adoptNode": {
        "!type": "fn(node: +Node) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.adoptNode",
        "!doc": "Adopts a node from an external document. The node and its subtree is removed from the document it's in (if any), and its ownerDocument is changed to the current document. The node can then be inserted into the current document."
      },
      "createTreeWalker": {
        "!type": "fn(root: +Node, mask: number) -> ?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createTreeWalker",
        "!doc": "Returns a new TreeWalker object."
      },
      "createExpression": {
        "!type": "fn(text: string) -> ?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createExpression",
        "!doc": "This method compiles an XPathExpression which can then be used for (repeated) evaluations."
      },
      "createNSResolver": {
        "!type": "fn(node: +Node)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createNSResolver",
        "!doc": "Creates an XPathNSResolver which resolves namespaces with respect to the definitions in scope for a specified node."
      },
      "scripts": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Document.scripts",
        "!doc": "Returns a list of the <script> elements in the document. The returned object is an HTMLCollection."
      },
      "plugins": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.plugins",
        "!doc": "Returns an HTMLCollection object containing one or more HTMLEmbedElements or null which represent the <embed> elements in the current document."
      },
      "embeds": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.embeds",
        "!doc": "Returns a list of the embedded OBJECTS within the current document."
      },
      "anchors": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.anchors",
        "!doc": "Returns a list of all of the anchors in the document."
      },
      "links": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.links",
        "!doc": "The links property returns a collection of all AREA elements and anchor elements in a document with a value for the href attribute. "
      },
      "forms": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.forms",
        "!doc": "Returns a collection (an HTMLCollection) of the form elements within the current document."
      },
      "styleSheets": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.styleSheets",
        "!doc": "Returns a list of stylesheet objects for stylesheets explicitly linked into or embedded in a document."
      },
      "currentScript": {
        "!type": "+Node",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/API/document.currentScript",
        "!doc": "Returns the <script> element whose script is currently being processed."
      },
      "registerElement": {
        "!type": "fn(type: string, options?: ?)",
        "!url": "https://w3c.github.io/webcomponents/spec/custom/#extensions-to-document-interface-to-register",
        "!doc": "The registerElement method of the Document interface provides a way to register a custom element and returns its custom element constructor."
      },
      "getElementsByClassName": "Element.prototype.getElementsByClassName",
      "querySelector": "Element.prototype.querySelector",
      "querySelectorAll": "Element.prototype.querySelectorAll"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/document",
    "!doc": "Each web page loaded in the browser has its own document object. This object serves as an entry point to the web page's content (the DOM tree, including elements such as <body> and <table>) and provides functionality global to the document (such as obtaining the page's URL and creating new elements in the document)."
  },
  "document": {
    "!type": "+Document",
    "!url": "https://developer.mozilla.org/en/docs/DOM/document",
    "!doc": "Each web page loaded in the browser has its own document object. This object serves as an entry point to the web page's content (the DOM tree, including elements such as <body> and <table>) and provides functionality global to the document (such as obtaining the page's URL and creating new elements in the document)."
  },
  "XMLDocument": {
    "!type": "fn()",
    "prototype": "Document.prototype",
    "!url": "https://developer.mozilla.org/en/docs/Parsing_and_serializing_XML",
    "!doc": "The Web platform provides the following objects for parsing and serializing XML:"
  },
  "HTMLElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement"
  },
  "HTMLAnchorElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement"
  },
  "HTMLAreaElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLAreaElement"
  },
  "HTMLAudioElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement"
  },
  "HTMLBaseElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLBaseElement"
  },
  "HTMLBodyElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLBodyElement"
  },
  "HTMLBRElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLBRElement"
  },
  "HTMLButtonElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement"
  },
  "HTMLCanvasElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement"
  },
  "HTMLDataElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLDataElement"
  },
  "HTMLDataListElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLDataListElement"
  },
  "HTMLDivElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement"
  },
  "HTMLDListElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLDListElement"
  },
  "HTMLDocument": {
    "!type": "Document",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLDocument"
  },
  "HTMLEmbedElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLEmbedElement"
  },
  "HTMLFieldSetElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLFieldSetElement"
  },
  "HTMLFormControlsCollection": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormControlsCollection"
  },
  "HTMLFormElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement"
  },
  "HTMLHeadElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLHeadElement"
  },
  "HTMLHeadingElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLHeadingElement"
  },
  "HTMLHRElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLHRElement"
  },
  "HTMLHtmlElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLHtmlElement"
  },
  "HTMLIFrameElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement"
  },
  "HTMLImageElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement"
  },
  "HTMLInputElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement"
  },
  "HTMLKeygenElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLKeygenElement"
  },
  "HTMLLabelElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement"
  },
  "HTMLLegendElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLLegendElement"
  },
  "HTMLLIElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLLIElement"
  },
  "HTMLLinkElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement"
  },
  "HTMLMapElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLMapElement"
  },
  "HTMLMediaElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement"
  },
  "HTMLMetaElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLMetaElement"
  },
  "HTMLMeterElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLMeterElement"
  },
  "HTMLModElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLModElement"
  },
  "HTMLObjectElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement"
  },
  "HTMLOListElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLOListElement"
  },
  "HTMLOptGroupElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLOptGroupElement"
  },
  "HTMLOptionElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLOptionElement"
  },
  "HTMLOptionsCollection": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLOptionsCollection"
  },
  "HTMLOutputElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLOutputElement"
  },
  "HTMLParagraphElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLParagraphElement"
  },
  "HTMLParamElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLParamElement"
  },
  "HTMLPreElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLPreElement"
  },
  "HTMLProgressElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLProgressElement"
  },
  "HTMLQuoteElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLQuoteElement"
  },
  "HTMLScriptElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement"
  },
  "HTMLSelectElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLSelectElement"
  },
  "HTMLSourceElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLSourceElement"
  },
  "HTMLSpanElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLSpanElement"
  },
  "HTMLStyleElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLStyleElement"
  },
  "HTMLTableCaptionElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCaptionElement"
  },
  "HTMLTableCellElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement"
  },
  "HTMLTableColElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableColElement"
  },
  "HTMLTableDataCellElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableDataCellElement"
  },
  "HTMLTableElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement"
  },
  "HTMLTableHeaderCellElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableHeaderCellElement"
  },
  "HTMLTableRowElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableRowElement"
  },
  "HTMLTableSectionElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableSectionElement"
  },
  "HTMLTextAreaElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement"
  },
  "HTMLTimeElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLTimeElement"
  },
  "HTMLTitleElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLTitleElement"
  },
  "HTMLTrackElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLTrackElement"
  },
  "HTMLUListElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLUListElement"
  },
  "HTMLUnknownElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLUnknownElement"
  },
  "HTMLVideoElement": {
    "!type": "Element",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement"
  },
  "Attr": {
    "!type": "fn()",
    "prototype": {
      "isId": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Attr",
        "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types."
      },
      "name": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Attr",
        "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types."
      },
      "value": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Attr",
        "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Attr",
    "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types."
  },
  "NodeList": {
    "!type": "fn()",
    "prototype": {
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.length",
        "!doc": "Returns the number of items in a NodeList."
      },
      "item": {
        "!type": "fn(i: number) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NodeList.item",
        "!doc": "Returns a node from a NodeList by index."
      },
      "<i>": "+Element"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/NodeList",
    "!doc": "NodeList objects are collections of nodes returned by getElementsByTagName, getElementsByTagNameNS, Node.childNodes, querySelectorAll, getElementsByClassName, etc."
  },
  "HTMLCollection": {
    "!type": "fn()",
    "prototype": {
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCollection",
        "!doc": "The number of items in the collection."
      },
      "item": {
        "!type": "fn(i: number) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCollection",
        "!doc": "Returns the specific node at the given zero-based index into the list. Returns null if the index is out of range."
      },
      "namedItem": {
        "!type": "fn(name: string) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCollection",
        "!doc": "Returns the specific node whose ID or, as a fallback, name matches the string specified by name. Matching by name is only done as a last resort, only in HTML, and only if the referenced element supports the name attribute. Returns null if no node exists by the given name."
      },
      "<i>": "+Element"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCollection",
    "!doc": "HTMLCollection is an interface representing a generic collection of elements (in document order) and offers methods and properties for traversing the list."
  },
  "NamedNodeMap": {
    "!type": "fn()",
    "prototype": {
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "The number of items in the map."
      },
      "getNamedItem": {
        "!type": "fn(name: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Gets a node by name."
      },
      "setNamedItem": {
        "!type": "fn(node: +Node) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Adds (or replaces) a node by its nodeName."
      },
      "removeNamedItem": {
        "!type": "fn(name: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Removes a node (or if an attribute, may reveal a default if present)."
      },
      "item": {
        "!type": "fn(i: number) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Returns the item at the given index (or null if the index is higher or equal to the number of nodes)."
      },
      "getNamedItemNS": {
        "!type": "fn(ns: string, name: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Gets a node by namespace and localName."
      },
      "setNamedItemNS": {
        "!type": "fn(node: +Node) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Adds (or replaces) a node by its localName and namespaceURI."
      },
      "removeNamedItemNS": {
        "!type": "fn(ns: string, name: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Removes a node (or if an attribute, may reveal a default if present)."
      },
      "<i>": "+Node"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
    "!doc": "A collection of nodes returned by Element.attributes (also potentially for DocumentType.entities, DocumentType.notations). NamedNodeMaps are not in any particular order (unlike NodeList), although they may be accessed by an index as in an array (they may also be accessed with the item() method). A NamedNodeMap object are live and will thus be auto-updated if changes are made to their contents internally or elsewhere."
  },
  "DocumentFragment": {
    "!type": "fn()",
    "prototype": {
      "!proto": "Node.prototype"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/document.createDocumentFragment",
    "!doc": "Creates a new empty DocumentFragment."
  },
  "DOMTokenList": {
    "!type": "fn()",
    "prototype": {
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
        "!doc": "The amount of items in the list."
      },
      "item": {
        "!type": "fn(i: number) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
        "!doc": "Returns an item in the list by its index."
      },
      "contains": {
        "!type": "fn(token: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
        "!doc": "Return true if the underlying string contains token, otherwise false."
      },
      "add": {
        "!type": "fn(token: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
        "!doc": "Adds token to the underlying string."
      },
      "remove": {
        "!type": "fn(token: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
        "!doc": "Remove token from the underlying string."
      },
      "toggle": {
        "!type": "fn(token: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
        "!doc": "Removes token from string and returns false. If token doesn't exist it's added and the function returns true."
      },
      "<i>": "string"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
    "!doc": "This type represents a set of space-separated tokens. Commonly returned by HTMLElement.classList, HTMLLinkElement.relList, HTMLAnchorElement.relList or HTMLAreaElement.relList. It is indexed beginning with 0 as with JavaScript arrays. DOMTokenList is always case-sensitive."
  },
  "XPathResult": {
    "!type": "fn()",
    "prototype": {
      "boolValue": "bool",
      "invalidIteratorState": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript",
        "!doc": "This document describes the interface for using XPath in JavaScript internally, in extensions, and from websites. Mozilla implements a fair amount of the DOM 3 XPath. Which means that XPath expressions can be run against both HTML and XML documents."
      },
      "numberValue": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/XPathResult",
        "!doc": "Refer to nsIDOMXPathResult for more detail."
      },
      "resultType": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.evaluate",
        "!doc": "Returns an XPathResult based on an XPath expression and other given parameters."
      },
      "singleNodeValue": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript",
        "!doc": "This document describes the interface for using XPath in JavaScript internally, in extensions, and from websites. Mozilla implements a fair amount of the DOM 3 XPath. Which means that XPath expressions can be run against both HTML and XML documents."
      },
      "snapshotLength": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/XPathResult",
        "!doc": "Refer to nsIDOMXPathResult for more detail."
      },
      "stringValue": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript",
        "!doc": "This document describes the interface for using XPath in JavaScript internally, in extensions, and from websites. Mozilla implements a fair amount of the DOM 3 XPath. Which means that XPath expressions can be run against both HTML and XML documents."
      },
      "iterateNext": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript",
        "!doc": "This document describes the interface for using XPath in JavaScript internally, in extensions, and from websites. Mozilla implements a fair amount of the DOM 3 XPath. Which means that XPath expressions can be run against both HTML and XML documents."
      },
      "snapshotItem": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en-US/docs/XPathResult#snapshotItem()"
      },
      "ANY_TYPE": "number",
      "NUMBER_TYPE": "number",
      "STRING_TYPE": "number",
      "BOOL_TYPE": "number",
      "UNORDERED_NODE_ITERATOR_TYPE": "number",
      "ORDERED_NODE_ITERATOR_TYPE": "number",
      "UNORDERED_NODE_SNAPSHOT_TYPE": "number",
      "ORDERED_NODE_SNAPSHOT_TYPE": "number",
      "ANY_UNORDERED_NODE_TYPE": "number",
      "FIRST_ORDERED_NODE_TYPE": "number"
    },
    "!url": "https://developer.mozilla.org/en/docs/XPathResult",
    "!doc": "Refer to nsIDOMXPathResult for more detail."
  },
  "ClientRect": {
    "!type": "fn()",
    "prototype": {
      "top": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects",
        "!doc": "Top of the box, in pixels, relative to the viewport."
      },
      "left": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects",
        "!doc": "Left of the box, in pixels, relative to the viewport."
      },
      "bottom": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects",
        "!doc": "Bottom of the box, in pixels, relative to the viewport."
      },
      "right": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects",
        "!doc": "Right of the box, in pixels, relative to the viewport."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects",
    "!doc": "Returns a collection of rectangles that indicate the bounding rectangles for each box in a client."
  },
  "Event": {
    "!type": "fn()",
    "prototype": {
      "stopPropagation": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.stopPropagation",
        "!doc": "Prevents further propagation of the current event."
      },
      "preventDefault": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.preventDefault",
        "!doc": "Cancels the event if it is cancelable, without stopping further propagation of the event."
      },
      "initEvent": {
        "!type": "fn(type: string, bubbles: bool, cancelable: bool)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.initEvent",
        "!doc": "The initEvent method is used to initialize the value of an event created using document.createEvent."
      },
      "stopImmediatePropagation": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.stopImmediatePropagation",
        "!doc": "Prevents other listeners of the same event to be called."
      },
      "type": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/API/event.type",
        "!doc": "Returns a string containing the type of event."
      },
      "NONE": "number",
      "CAPTURING_PHASE": "number",
      "AT_TARGET": "number",
      "BUBBLING_PHASE": "number",
      "MOUSEDOWN": "number",
      "MOUSEUP": "number",
      "MOUSEOVER": "number",
      "MOUSEOUT": "number",
      "MOUSEMOVE": "number",
      "MOUSEDRAG": "number",
      "CLICK": "number",
      "DBLCLICK": "number",
      "KEYDOWN": "number",
      "KEYUP": "number",
      "KEYPRESS": "number",
      "DRAGDROP": "number",
      "FOCUS": "number",
      "BLUR": "number",
      "SELECT": "number",
      "CHANGE": "number",
      "target": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget",
        "!doc": "An EventTarget is a DOM interface implemented by objects that can receive DOM events and have listeners for them. The most common EventTargets are DOM elements, although other objects can be EventTargets too, for example document, window, XMLHttpRequest, and others."
      },
      "relatedTarget": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.relatedTarget",
        "!doc": "Identifies a secondary target for the event."
      },
      "pageX": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.pageX",
        "!doc": "Returns the horizontal coordinate of the event relative to whole document."
      },
      "pageY": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.pageY",
        "!doc": "Returns the vertical coordinate of the event relative to the whole document."
      },
      "clientX": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.clientX",
        "!doc": "Returns the horizontal coordinate within the application's client area at which the event occurred (as opposed to the coordinates within the page). For example, clicking in the top-left corner of the client area will always result in a mouse event with a clientX value of 0, regardless of whether the page is scrolled horizontally."
      },
      "clientY": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.clientY",
        "!doc": "Returns the vertical coordinate within the application's client area at which the event occurred (as opposed to the coordinates within the page). For example, clicking in the top-left corner of the client area will always result in a mouse event with a clientY value of 0, regardless of whether the page is scrolled vertically."
      },
      "keyCode": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.keyCode",
        "!doc": "Returns the Unicode value of a non-character key in a keypress event or any key in any other type of keyboard event."
      },
      "charCode": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.charCode",
        "!doc": "Returns the Unicode value of a character key pressed during a keypress event."
      },
      "which": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.which",
        "!doc": "Returns the numeric keyCode of the key pressed, or the character code (charCode) for an alphanumeric key pressed."
      },
      "button": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.button",
        "!doc": "Indicates which mouse button caused the event."
      },
      "shiftKey": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.shiftKey",
        "!doc": "Indicates whether the SHIFT key was pressed when the event fired."
      },
      "ctrlKey": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.ctrlKey",
        "!doc": "Indicates whether the CTRL key was pressed when the event fired."
      },
      "altKey": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.altKey",
        "!doc": "Indicates whether the ALT key was pressed when the event fired."
      },
      "metaKey": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.metaKey",
        "!doc": "Indicates whether the META key was pressed when the event fired."
      },
      "returnValue": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/window.onbeforeunload",
        "!doc": "An event that fires when a window is about to unload its resources. The document is still visible and the event is still cancelable."
      },
      "cancelBubble": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.cancelBubble",
        "!doc": "bool is the boolean value of true or false."
      },
      "dataTransfer": {
        "dropEffect": {
          "!type": "string",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/DataTransfer",
          "!doc": "The actual effect that will be used, and should always be one of the possible values of effectAllowed."
        },
        "effectAllowed": {
          "!type": "string",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations",
          "!doc": "Specifies the effects that are allowed for this drag."
        },
        "files": {
          "!type": "+FileList",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/DataTransfer",
          "!doc": "Contains a list of all the local files available on the data transfer."
        },
        "types": {
          "!type": "[string]",
          "!url": "https://developer.mozilla.org/en-US/docs/DragDrop/DataTransfer",
          "!doc": "Holds a list of the format types of the data that is stored for the first item, in the same order the data was added. An empty list will be returned if no data was added."
        },
        "addElement": {
          "!type": "fn(element: +Element)",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/DataTransfer",
          "!doc": "Set the drag source."
        },
        "clearData": {
          "!type": "fn(type?: string)",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations",
          "!doc": "Remove the data associated with a given type."
        },
        "getData": {
          "!type": "fn(type: string) -> string",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations",
          "!doc": "Retrieves the data for a given type, or an empty string if data for that type does not exist or the data transfer contains no data."
        },
        "setData": {
          "!type": "fn(type: string, data: string)",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations",
          "!doc": "Set the data for a given type."
        },
        "setDragImage": {
          "!type": "fn(image: +Element)",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations",
          "!doc": "Set the image to be used for dragging if a custom one is desired."
        },
        "!url": "https://developer.mozilla.org/en/docs/DragDrop/DataTransfer",
        "!doc": "This object is available from the dataTransfer property of all drag events. It cannot be created separately."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/event",
    "!doc": "The DOM Event interface is accessible from within the handler function, via the event object passed as the first argument."
  },
  "TouchEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/Touch_events",
    "!doc": "In order to provide quality support for touch-based user interfaces, touch events offer the ability to interpret finger activity on touch screens or trackpads."
  },
  "WheelEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/WheelEvent",
    "!doc": "The DOM WheelEvent represents events that occur due to the user moving a mouse wheel or similar input device."
  },
  "MouseEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/MouseEvent",
    "!doc": "The DOM MouseEvent represents events that occur due to the user interacting with a pointing device (such as a mouse). It's represented by the nsINSDOMMouseEvent interface, which extends the nsIDOMMouseEvent interface."
  },
  "KeyboardEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/KeyboardEvent",
    "!doc": "KeyboardEvent objects describe a user interaction with the keyboard. Each event describes a key; the event type (keydown, keypress, or keyup) identifies what kind of activity was performed."
  },
  "HashChangeEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onhashchange",
    "!doc": "The hashchange event fires when a window's hash changes."
  },
  "ErrorEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/DOM_event_reference/error",
    "!doc": "The error event is fired whenever a resource fails to load."
  },
  "CustomEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/Event/CustomEvent",
    "!doc": "The DOM CustomEvent are events initialized by an application for any purpose."
  },
  "BeforeLoadEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window",
    "!doc": "This section provides a brief reference for all of the methods, properties, and events available through the DOM window object. The window object implements the Window interface, which in turn inherits from the AbstractView interface. Some additional global functions, namespaces objects, and constructors, not typically associated with the window, but available on it, are listed in the JavaScript Reference."
  },
  "WebSocket": {
    "!type": "fn(url: string)",
    "prototype": {
      "close": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/CloseEvent",
        "!doc": "A CloseEvent is sent to clients using WebSockets when the connection is closed. This is delivered to the listener indicated by the WebSocket object's onclose attribute."
      },
      "send": {
        "!type": "fn(data: string)",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket",
        "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection."
      },
      "binaryType": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket",
        "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection."
      },
      "bufferedAmount": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/Writing_WebSocket_client_applications",
        "!doc": "WebSockets is a technology that makes it possible to open an interactive communication session between the user's browser and a server. Using a WebSocket connection, Web applications can perform real-time communication instead of having to poll for changes back and forth."
      },
      "extensions": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket",
        "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection."
      },
      "onclose": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/CloseEvent",
        "!doc": "A CloseEvent is sent to clients using WebSockets when the connection is closed. This is delivered to the listener indicated by the WebSocket object's onclose attribute."
      },
      "onerror": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/Writing_WebSocket_client_applications",
        "!doc": "WebSockets is a technology that makes it possible to open an interactive communication session between the user's browser and a server. Using a WebSocket connection, Web applications can perform real-time communication instead of having to poll for changes back and forth."
      },
      "onmessage": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket",
        "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection."
      },
      "onopen": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket",
        "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection."
      },
      "protocol": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets",
        "!doc": "WebSockets is an advanced technology that makes it possible to open an interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply."
      },
      "url": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/Writing_WebSocket_client_applications",
        "!doc": "WebSockets is a technology that makes it possible to open an interactive communication session between the user's browser and a server. Using a WebSocket connection, Web applications can perform real-time communication instead of having to poll for changes back and forth."
      },
      "CONNECTING": "number",
      "OPEN": "number",
      "CLOSING": "number",
      "CLOSED": "number"
    },
    "!url": "https://developer.mozilla.org/en/docs/WebSockets",
    "!doc": "WebSockets is an advanced technology that makes it possible to open an interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply."
  },
  "Worker": {
    "!type": "fn(scriptURL: string)",
    "prototype": {
      "postMessage": {
        "!type": "fn(message: ?)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Worker",
        "!doc": "Sends a message to the worker's inner scope. This accepts a single parameter, which is the data to send to the worker. The data may be any value or JavaScript object handled by the structured clone algorithm, which includes cyclical references."
      },
      "terminate": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Worker",
        "!doc": "Immediately terminates the worker. This does not offer the worker an opportunity to finish its operations; it is simply stopped at once."
      },
      "onmessage": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Worker",
        "!doc": "An event listener that is called whenever a MessageEvent with type message bubbles through the worker. The message is stored in the event's data member."
      },
      "onerror": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Worker",
        "!doc": "An event listener that is called whenever an ErrorEvent with type error bubbles through the worker."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Worker",
    "!doc": "Workers are background tasks that can be easily created and can send messages back to their creators. Creating a worker is as simple as calling the Worker() constructor, specifying a script to be run in the worker thread."
  },
  "Storage": {
    "length": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/API/Storage/length",
      "!doc": "The length read-only property of the Storage interface returns an integer representing the number of data items stored in the Storage object."
    },
    "setItem": {
      "!type": "fn(name: string, value: string)",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem",
      "!doc": "The setItem() method of the Storage interface, when passed a key name and value, will add that key to the storage, or update that key's value if it already exists."
    },
    "getItem": {
      "!type": "fn(name: string) -> string",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem",
      "!doc": "The getItem() method of the Storage interface, when passed a key name, will return that key's value."
    },
    "key": {
      "!type": "fn(index: number) -> string",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/API/Storage/key",
      "!doc": "The key() method of the Storage interface, when passed a number n, returns the name of the nth key in the storage. The order of keys is user-agent defined, so you should not rely on it."
    },
    "removeItem": {
      "!type": "fn(key: string)",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/API/Storage/removeItem",
      "!doc": "The removeItem() method of the Storage interface, when passed a key name, will remove that key from the storage."
    },
    "clear": {
      "!type": "fn()",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/API/Storage/clear",
      "!doc": "The clear() method of the Storage interface, when invoked, will empty all keys out of the storage"
    }
  },
  "localStorage": {
    "!type": "Storage",
    "!url": "https://developer.mozilla.org/en/docs/Web/API/Window/localStorage",
    "!doc": "The localStorage property allows you to access a local Storage object. localStorage is similar to sessionStorage. The only difference is that, while data stored in localStorage has no expiration time, data stored in sessionStorage gets cleared when the browsing session ends—that is, when the browser is closed.\n\nIt should be noted that data stored in either localStorage or sessionStorage is specific to the protocol of the page."
  },
  "sessionStorage": {
    "!type": "Storage",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage",
    "!doc": "The sessionStorage property allows you to access a session Storage object for the current origin. sessionStorage is similar to Window.localStorage, the only difference is while data stored in localStorage has no expiration set, data stored in sessionStorage gets cleared when the page session ends. A page session lasts for as long as the browser is open and survives over page reloads and restores. Opening a page in a new tab or window will cause a new session to be initiated, which differs from how session cookies work.\n\nIt should be noted that data stored in either sessionStorage or localStorage is specific to the protocol of the page."
  },
  "FileList": {
    "!type": "fn()",
    "prototype": {
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileList",
        "!doc": "A read-only value indicating the number of files in the list."
      },
      "item": {
        "!type": "fn(i: number) -> +File",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileList",
        "!doc": "Returns a File object representing the file at the specified index in the file list."
      },
      "<i>": "+File"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/FileList",
    "!doc": "An object of this type is returned by the files property of the HTML input element; this lets you access the list of files selected with the <input type=\"file\"> element. It's also used for a list of files dropped into web content when using the drag and drop API."
  },
  "File": {
    "!type": "fn()",
    "prototype": {
      "!proto": "Blob.prototype",
      "fileName": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/File.fileName",
        "!doc": "Returns the name of the file. For security reasons the path is excluded from this property."
      },
      "fileSize": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/File.fileSize",
        "!doc": "Returns the size of a file in bytes."
      },
      "lastModifiedDate": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/File.lastModifiedDate",
        "!doc": "Returns the last modified date of the file. Files without a known last modified date use the current date instead."
      },
      "name": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/File.name",
        "!doc": "Returns the name of the file. For security reasons, the path is excluded from this property."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/File",
    "!doc": "The File object provides information about -- and access to the contents of -- files. These are generally retrieved from a FileList object returned as a result of a user selecting files using the input element, or from a drag and drop operation's DataTransfer object."
  },
  "Blob": {
    "!type": "fn(parts: [?], properties?: ?)",
    "prototype": {
      "size": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Blob",
        "!doc": "The size, in bytes, of the data contained in the Blob object. Read only."
      },
      "type": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Blob",
        "!doc": "An ASCII-encoded string, in all lower case, indicating the MIME type of the data contained in the Blob. If the type is unknown, this string is empty. Read only."
      },
      "slice": {
        "!type": "fn(start: number, end?: number, type?: string) -> +Blob",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Blob",
        "!doc": "Returns a new Blob object containing the data in the specified range of bytes of the source Blob."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Blob",
    "!doc": "A Blob object represents a file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system."
  },
  "FileReader": {
    "!type": "fn()",
    "prototype": {
      "abort": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Aborts the read operation. Upon return, the readyState will be DONE."
      },
      "readAsArrayBuffer": {
        "!type": "fn(blob: +Blob)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Starts reading the contents of the specified Blob, producing an ArrayBuffer."
      },
      "readAsBinaryString": {
        "!type": "fn(blob: +Blob)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Starts reading the contents of the specified Blob, producing raw binary data."
      },
      "readAsDataURL": {
        "!type": "fn(blob: +Blob)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Starts reading the contents of the specified Blob, producing a data: url."
      },
      "readAsText": {
        "!type": "fn(blob: +Blob, encoding?: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Starts reading the contents of the specified Blob, producing a string."
      },
      "EMPTY": "number",
      "LOADING": "number",
      "DONE": "number",
      "error": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "The error that occurred while reading the file. Read only."
      },
      "readyState": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Indicates the state of the FileReader. This will be one of the State constants. Read only."
      },
      "result": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "The file's contents. This property is only valid after the read operation is complete, and the format of the data depends on which of the methods was used to initiate the read operation. Read only."
      },
      "onabort": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Called when the read operation is aborted."
      },
      "onerror": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Called when an error occurs."
      },
      "onload": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Called when the read operation is successfully completed."
      },
      "onloadend": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Called when the read is completed, whether successful or not. This is called after either onload or onerror."
      },
      "onloadstart": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Called when reading the data is about to begin."
      },
      "onprogress": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Called periodically while the data is being read."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
    "!doc": "The FileReader object lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user's computer, using File or Blob objects to specify the file or data to read. File objects may be obtained from a FileList object returned as a result of a user selecting files using the <input> element, from a drag and drop operation's DataTransfer object, or from the mozGetAsFile() API on an HTMLCanvasElement."
  },
  "URL": {
    "createObjectURL": {
      "!type": "fn(blob: +Blob) -> string",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL",
      "!doc": "The URL.createObjectURL() static method creates a DOMString containing an URL representing the object given in parameter."

    },
    "revokeObjectURL": {
      "!type": "fn(string)",
      "!url": "https://developer.mozilla.org/en-US/docs/Web/API/URL.revokeObjectURL",
      "!doc": "The URL.revokeObjectURL() static method releases an existing object URL which was previously created by calling window.URL.createObjectURL()."
    }
  },
  "Range": {
    "!type": "fn()",
    "prototype": {
      "collapsed": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.collapsed",
        "!doc": "Returns a boolean indicating whether the range's start and end points are at the same position."
      },
      "commonAncestorContainer": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.commonAncestorContainer",
        "!doc": "Returns the deepest Node that contains the  startContainer and  endContainer Nodes."
      },
      "endContainer": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.endContainer",
        "!doc": "Returns the Node within which the Range ends."
      },
      "endOffset": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.endOffset",
        "!doc": "Returns a number representing where in the  endContainer the Range ends."
      },
      "startContainer": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.startContainer",
        "!doc": "Returns the Node within which the Range starts."
      },
      "startOffset": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.startOffset",
        "!doc": "Returns a number representing where in the startContainer the Range starts."
      },
      "setStart": {
        "!type": "fn(node: +Element, offset: number)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.setStart",
        "!doc": "Sets the start position of a Range."
      },
      "setEnd": {
        "!type": "fn(node: +Element, offset: number)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.setEnd",
        "!doc": "Sets the end position of a Range."
      },
      "setStartBefore": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.setStartBefore",
        "!doc": "Sets the start position of a Range relative to another Node."
      },
      "setStartAfter": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.setStartAfter",
        "!doc": "Sets the start position of a Range relative to a Node."
      },
      "setEndBefore": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.setEndBefore",
        "!doc": "Sets the end position of a Range relative to another Node."
      },
      "setEndAfter": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.setEndAfter",
        "!doc": "Sets the end position of a Range relative to another Node."
      },
      "selectNode": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.selectNode",
        "!doc": "Sets the Range to contain the Node and its contents."
      },
      "selectNodeContents": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.selectNodeContents",
        "!doc": "Sets the Range to contain the contents of a Node."
      },
      "collapse": {
        "!type": "fn(toStart: bool)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.collapse",
        "!doc": "Collapses the Range to one of its boundary points."
      },
      "cloneContents": {
        "!type": "fn() -> +DocumentFragment",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.cloneContents",
        "!doc": "Returns a DocumentFragment copying the Nodes of a Range."
      },
      "deleteContents": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.deleteContents",
        "!doc": "Removes the contents of a Range from the Document."
      },
      "extractContents": {
        "!type": "fn() -> +DocumentFragment",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.extractContents",
        "!doc": "Moves contents of a Range from the document tree into a DocumentFragment."
      },
      "insertNode": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.insertNode",
        "!doc": "Insert a node at the start of a Range."
      },
      "surroundContents": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.surroundContents",
        "!doc": "Moves content of a Range into a new node, placing the new node at the start of the specified range."
      },
      "compareBoundaryPoints": {
        "!type": "fn(how: number, other: +Range) -> number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.compareBoundaryPoints",
        "!doc": "Compares the boundary points of two Ranges."
      },
      "cloneRange": {
        "!type": "fn() -> +Range",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.cloneRange",
        "!doc": "Returns a Range object with boundary points identical to the cloned Range."
      },
      "detach": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.detach",
        "!doc": "Releases a Range from use to improve performance. This lets the browser choose to release resources associated with this Range. Subsequent attempts to use the detached range will result in a DOMException being thrown with an error code of INVALID_STATE_ERR."
      },
      "END_TO_END": "number",
      "END_TO_START": "number",
      "START_TO_END": "number",
      "START_TO_START": "number"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/range.detach",
    "!doc": "Releases a Range from use to improve performance. This lets the browser choose to release resources associated with this Range. Subsequent attempts to use the detached range will result in a DOMException being thrown with an error code of INVALID_STATE_ERR."
  },
  "XMLHttpRequest": {
    "!type": "fn()",
    "prototype": {
      "abort": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Aborts the request if it has already been sent."
      },
      "getAllResponseHeaders": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Returns all the response headers as a string, or null if no response has been received. Note: For multipart requests, this returns the headers from the current part of the request, not from the original channel."
      },
      "getResponseHeader": {
        "!type": "fn(header: string) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Returns the string containing the text of the specified header, or null if either the response has not yet been received or the header doesn't exist in the response."
      },
      "open": {
        "!type": "fn(method: string, url: string, async?: bool, user?: string, password?: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Initializes a request."
      },
      "overrideMimeType": {
        "!type": "fn(type: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Overrides the MIME type returned by the server."
      },
      "send": {
        "!type": "fn(data?: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Sends the request. If the request is asynchronous (which is the default), this method returns as soon as the request is sent. If the request is synchronous, this method doesn't return until the response has arrived."
      },
      "setRequestHeader": {
        "!type": "fn(header: string, value: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Sets the value of an HTTP request header.You must call setRequestHeader() after open(), but before send()."
      },
      "onabort": {
        "!type": "fn()",
        "!url" : "https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onabort",
        "!doc": "The XMLHttpRequestEventTarget.onabort is the function called when an XMLHttpRequest transaction is aborted, such as when the XMLHttpRequest.abort() function is called."
      },
      "onerror": {
        "!type": "fn()",
        "!url" : "https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onerror",
        "!doc": "The XMLHttpRequestEventTarget.onerror is the function called when an XMLHttpRequest transaction fails due to an error."
      },
      "onload": {
        "!type": "fn()",
        "!url" : "https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onload",
        "!doc": "The XMLHttpRequestEventTarget.onload is the function called when an XMLHttpRequest transaction completes successfully."
      },
      "onloadstart": {
        "!type": "fn()",
        "!url" : "https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onloadstart",
        "!doc": "The XMLHttpRequestEventTarget.onloadstart is the function called when an XMLHttpRequest transaction starts transferring data."
      },
      "onloadend": {
        "!type": "fn()",
        "!url" : "https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onloadend",
        "!doc": "The XMLHttpRequestEventTarget.onloadend is the function called when an XMLHttpRequest transaction completes transferring data."
      },
      "onprogress": {
        "!type": "fn()",
        "!url" : "https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onprogress",
        "!doc": "The XMLHttpRequestEventTarget.onprogress is the function called periodically with information when an XMLHttpRequest before success completely."
      },
      "ontimeout": {
        "!type": "fn()",
        "!url" : "https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/ontimeout",
        "!doc": "The XMLHttpRequestEventTarget.ontimeout is the function called when the XMLHttpRequest times out."
      },
      "addEventListener": {
        "!type": "fn(type: string, listener: fn(), options?: ?, useCapture?: bool)",
        "!url" : "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener",
        "!doc": "The EventTarget.addEventListener() method registers the specified listener on the EventTarget it's called on. The event target may be an Element in a document, the Document itself, a Window, or any other object that supports events (such as XMLHttpRequest)."
      },
      "removeEventListener": {
        "!type": "fn(type: string, listener: fn(), options?: ?, useCapture?: bool)",
        "!url" : "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener",
        "!doc": "Removes the event listener previously registered with EventTarget.addEventListener()."
      },
      "dispatchEvent": {
        "!type": "fn(event: ?)",
        "!url" : "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent",
        "!doc": "Dispatches an Event at the specified EventTarget, invoking the affected EventListeners in the appropriate order. The normal event processing rules (including the capturing and optional bubbling phase) apply to events dispatched manually with dispatchEvent()."
      },
      "onreadystatechange": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "A JavaScript function object that is called whenever the readyState attribute changes."
      },
      "readyState": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "The state of the request. (0=unsent, 1=opened, 2=headers_received, 3=loading, 4=done)"
      },
      "response": {
        "!type": "+Document",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "The response entity body according to responseType, as an ArrayBuffer, Blob, Document, JavaScript object (for \"json\"), or string. This is null if the request is not complete or was not successful."
      },
      "responseText": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "The response to the request as text, or null if the request was unsuccessful or has not yet been sent."
      },
      "responseType": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Can be set to change the response type."
      },
      "responseXML": {
        "!type": "+Document",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "The response to the request as a DOM Document object, or null if the request was unsuccessful, has not yet been sent, or cannot be parsed as XML or HTML."
      },
      "status": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "The status of the response to the request. This is the HTTP result code"
      },
      "statusText": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "The response string returned by the HTTP server. Unlike status, this includes the entire text of the response message (\"200 OK\", for example)."
      },
      "timeout": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest/Synchronous_and_Asynchronous_Requests",
        "!doc": "The number of milliseconds a request can take before automatically being terminated. A value of 0 (which is the default) means there is no timeout."
      },
      "UNSENT": "number",
      "OPENED": "number",
      "HEADERS_RECEIVED": "number",
      "LOADING": "number",
      "DONE": "number"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
    "!doc": "XMLHttpRequest is a JavaScript object that was designed by Microsoft and adopted by Mozilla, Apple, and Google. It's now being standardized in the W3C. It provides an easy way to retrieve data at a URL. Despite its name, XMLHttpRequest can be used to retrieve any type of data, not just XML, and it supports protocols other than HTTP (including file and ftp)."
  },
  "DOMParser": {
    "!type": "fn()",
    "prototype": {
      "parseFromString": {
        "!type": "fn(data: string, mime: string) -> +Document",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMParser",
        "!doc": "DOMParser can parse XML or HTML source stored in a string into a DOM Document. DOMParser is specified in DOM Parsing and Serialization."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/DOMParser",
    "!doc": "DOMParser can parse XML or HTML source stored in a string into a DOM Document. DOMParser is specified in DOM Parsing and Serialization."
  },
  "FormData": {
    "!type": "fn()",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData",
    "prototype": {
      "append": {
        "!type": "fn(name: string, value: ?, filename: string)",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/append",
        "!doc": "Appends a new value onto an existing key inside a FormData object, or adds the key if it does not already exist."
      },
      "delete": {
        "!type": "fn(name: string)",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/delete",
        "!doc": "Deletes a key/value pair from a FormData object."
      },
      "get": {
        "!type": "fn(name: string)",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/get",
        "!doc": "Returns the first value associated with a given key from within a FormData object."
      },
      "getAll": {
        "!type": "fn(name: string)",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/getAll",
        "!doc": "Returns an array of all the values associated with a given key from within a FormData."
      },
      "has": {
        "!type": "fn(name: string)",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/has",
        "!doc": "Returns a boolean stating whether a FormData object contains a certain key/value pair."
      },
      "set": {
        "!type": "fn(name: string, value: ?, filename: string)",
        "!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/set",
        "!doc": "Sets a new value for an existing key inside a FormData object, or adds the key/value if it does not already exist."
      }
    }
  },
  "Selection": {
    "!type": "fn()",
    "prototype": {
      "anchorNode": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/anchorNode",
        "!doc": "Returns the node in which the selection begins."
      },
      "anchorOffset": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/anchorOffset",
        "!doc": "Returns the number of characters that the selection's anchor is offset within the anchorNode."
      },
      "focusNode": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/focusNode",
        "!doc": "Returns the node in which the selection ends."
      },
      "focusOffset": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/focusOffset",
        "!doc": "Returns the number of characters that the selection's focus is offset within the focusNode. "
      },
      "isCollapsed": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/isCollapsed",
        "!doc": "Returns a boolean indicating whether the selection's start and end points are at the same position."
      },
      "rangeCount": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/rangeCount",
        "!doc": "Returns the number of ranges in the selection."
      },
      "getRangeAt": {
        "!type": "fn(i: number) -> +Range",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/getRangeAt",
        "!doc": "Returns a range object representing one of the ranges currently selected."
      },
      "collapse": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/collapse",
        "!doc": "Collapses the current selection to a single point. The document is not modified. If the content is focused and editable, the caret will blink there."
      },
      "extend": {
        "!type": "fn(node: +Element, offset: number)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/extend",
        "!doc": "Moves the focus of the selection to a specified point. The anchor of the selection does not move. The selection will be from the anchor to the new focus regardless of direction."
      },
      "collapseToStart": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/collapseToStart",
        "!doc": "Collapses the selection to the start of the first range in the selection.  If the content of the selection is focused and editable, the caret will blink there."
      },
      "collapseToEnd": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/collapseToEnd",
        "!doc": "Collapses the selection to the end of the last range in the selection.  If the content the selection is in is focused and editable, the caret will blink there."
      },
      "selectAllChildren": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/selectAllChildren",
        "!doc": "Adds all the children of the specified node to the selection. Previous selection is lost."
      },
      "addRange": {
        "!type": "fn(range: +Range)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/addRange",
        "!doc": "Adds a Range to a Selection."
      },
      "removeRange": {
        "!type": "fn(range: +Range)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/removeRange",
        "!doc": "Removes a range from the selection."
      },
      "removeAllRanges": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/removeAllRanges",
        "!doc": "Removes all ranges from the selection, leaving the anchorNode and focusNode properties equal to null and leaving nothing selected. "
      },
      "deleteFromDocument": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/deleteFromDocument",
        "!doc": "Deletes the actual text being represented by a selection object from the document's DOM."
      },
      "containsNode": {
        "!type": "fn(node: +Element) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/containsNode",
        "!doc": "Indicates if the node is part of the selection."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Selection",
    "!doc": "Selection is the class of the object returned by window.getSelection() and other methods. It represents the text selection in the greater page, possibly spanning multiple elements, when the user drags over static text and other parts of the page. For information about text selection in an individual text editing element."
  },
  "console": {
    "assert": {
      "!type": "fn(assertion: bool, text: string)",
      "!url": "https://developer.mozilla.org/en/docs/Web/API/Console.assert",
      "!doc": "Writes an error message to the console if the assertion is false."
    },
    "count": {
      "!type": "fn(label?: string)",
      "!url": "https://developer.mozilla.org/en/docs/Web/API/Console.count",
      "!doc": "Logs the number of times that this particular call to count() has been called."
    },
    "dir": {
      "!type": "fn(object: ?)",
      "!url": "https://developer.mozilla.org/en/docs/Web/API/Console.dir",
      "!doc": "Displays an interactive list of the properties of the specified JavaScript object."
    },
    "error": {
      "!type": "fn(text: ?)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/console.error",
      "!doc": "Outputs an error message to the Web Console."
    },
    "group": {
      "!type": "fn()",
      "!url": "https://developer.mozilla.org/en/docs/Web/API/Console.group",
      "!doc": "Creates a new inline group in the Web Console log."
    },
    "groupCollapsed": {
      "!type": "fn()",
      "!url": "https://developer.mozilla.org/en/docs/Web/API/Console.groupCollapsed",
      "!doc": "Creates a new inline group in the Web Console log."
    },
    "groupEnd": {
      "!type": "fn()",
      "!url": "https://developer.mozilla.org/en/docs/Web/API/Console.groupEnd",
      "!doc": "Exits the current inline group in the Web Console."
    },
    "info": {
      "!type": "fn(text: ?)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/console.info",
      "!doc": "Outputs an informational message to the Web Console."
    },
    "log": {
      "!type": "fn(text: ?)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/console.log",
      "!doc": "Outputs a message to the Web Console."
    },
    "time": {
      "!type": "fn(timerName: string)",
      "!url": "https://developer.mozilla.org/en/docs/Web/API/Console.time",
      "!doc": "Starts a timer you can use to track how long an operation takes."
    },
    "timeEnd": {
      "!type": "fn(timerName: string)",
      "!url": "https://developer.mozilla.org/en/docs/Web/API/Console.timeEnd",
      "!doc": "Stops a timer that was previously started by calling console.time()."
    },
    "trace": {
      "!type": "fn()",
      "!url": "https://developer.mozilla.org/en/docs/Web/API/Console.trace",
      "!doc": "Outputs a stack trace to the Web Console."
    },
    "warn": {
      "!type": "fn(text: ?)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/console.warn",
      "!doc": "Outputs a warning message to the Web Console."
    },
    "!url": "https://developer.mozilla.org/en/docs/Web/API/Console",
    "!doc": "The console object provides access to the browser's debugging console. The specifics of how it works vary from browser to browser, but there is a de facto set of features that are typically provided."
  },
  "top": {
    "!type": "<top>",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.top",
    "!doc": "Returns a reference to the topmost window in the window hierarchy."
  },
  "parent": {
    "!type": "<top>",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.parent",
    "!doc": "A reference to the parent of the current window or subframe."
  },
  "window": {
    "!type": "<top>",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window",
    "!doc": "The window object represents a window containing a DOM document."
  },
  "opener": {
    "!type": "<top>",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.opener",
    "!doc": "Returns a reference to the window that opened this current window."
  },
  "self": {
    "!type": "<top>",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.self",
    "!doc": "Returns an object reference to the window object. "
  },
  "devicePixelRatio": "number",
  "name": {
    "!type": "string",
    "!url": "https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Function/name",
    "!doc": "The name of the function."
  },
  "closed": {
    "!type": "bool",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.closed",
    "!doc": "This property indicates whether the referenced window is closed or not."
  },
  "pageYOffset": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollY",
    "!doc": "Returns the number of pixels that the document has already been scrolled vertically."
  },
  "pageXOffset": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollX",
    "!doc": "Returns the number of pixels that the document has already been scrolled vertically."
  },
  "scrollY": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollY",
    "!doc": "Returns the number of pixels that the document has already been scrolled vertically."
  },
  "scrollX": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollX",
    "!doc": "Returns the number of pixels that the document has already been scrolled vertically."
  },
  "screenTop": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.top",
    "!doc": "Returns the distance in pixels from the top side of the current screen."
  },
  "screenLeft": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.left",
    "!doc": "Returns the distance in pixels from the left side of the main screen to the left side of the current screen."
  },
  "screenY": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/event.screenY",
    "!doc": "Returns the vertical coordinate of the event within the screen as a whole."
  },
  "screenX": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/event.screenX",
    "!doc": "Returns the horizontal coordinate of the event within the screen as a whole."
  },
  "innerWidth": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.innerWidth",
    "!doc": "Width (in pixels) of the browser window viewport including, if rendered, the vertical scrollbar."
  },
  "innerHeight": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.innerHeight",
    "!doc": "Height (in pixels) of the browser window viewport including, if rendered, the horizontal scrollbar."
  },
  "outerWidth": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.outerWidth",
    "!doc": "window.outerWidth gets the width of the outside of the browser window. It represents the width of the whole browser window including sidebar (if expanded), window chrome and window resizing borders/handles."
  },
  "outerHeight": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.outerHeight",
    "!doc": "window.outerHeight gets the height in pixels of the whole browser window."
  },
  "frameElement": {
    "!type": "+Element",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.frameElement",
    "!doc": "Returns the element (such as <iframe> or <object>) in which the window is embedded, or null if the window is top-level."
  },
  "crypto": {
    "getRandomValues": {
      "!type": "fn([number])",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.crypto.getRandomValues",
      "!doc": "This methods lets you get cryptographically random values."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.crypto.getRandomValues",
    "!doc": "This methods lets you get cryptographically random values."
  },
  "navigator": {
    "appName": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.appName",
      "!doc": "Returns the name of the browser. The HTML5 specification also allows any browser to return \"Netscape\" here, for compatibility reasons."
    },
    "appVersion": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.appVersion",
      "!doc": "Returns the version of the browser as a string. It may be either a plain version number, like \"5.0\", or a version number followed by more detailed information. The HTML5 specification also allows any browser to return \"4.0\" here, for compatibility reasons."
    },
    "language": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.language",
      "!doc": "Returns a string representing the language version of the browser."
    },
    "platform": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.platform",
      "!doc": "Returns a string representing the platform of the browser."
    },
    "plugins": {
      "!type": "[?]",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.plugins",
      "!doc": "Returns a PluginArray object, listing the plugins installed in the application."
    },
    "userAgent": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.userAgent",
      "!doc": "Returns the user agent string for the current browser."
    },
    "vendor": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.vendor",
      "!doc": "Returns the name of the browser vendor for the current browser."
    },
    "javaEnabled": {
      "!type": "bool",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.javaEnabled",
      "!doc": "This method indicates whether the current browser is Java-enabled or not."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator",
    "!doc": "Returns a reference to the navigator object, which can be queried for information about the application running the script."
  },
  "history": {
    "state": {
      "!type": "?",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
      "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
    },
    "length": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
      "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
    },
    "go": {
      "!type": "fn(delta: number)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.history",
      "!doc": "Returns a reference to the History object, which provides an interface for manipulating the browser session history (pages visited in the tab or frame that the current page is loaded in)."
    },
    "forward": {
      "!type": "fn()",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
      "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
    },
    "back": {
      "!type": "fn()",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
      "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
    },
    "pushState": {
      "!type": "fn(data: ?, title: string, url?: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
      "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
    },
    "replaceState": {
      "!type": "fn(data: ?, title: string, url?: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
      "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
    "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
  },
  "requestAnimationFrame": {
    "!type": "fn(callback: fn(timestamp: number)) -> number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.requestAnimationFrame",
    "!doc": "The Window.requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint. The method takes as an argument a callback to be invoked before the repaint."
  },
  "cancelAnimationFrame": {
    "!type": "fn(number)n",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.cancelAnimationFrame",
    "!doc": "Cancels a previously scheduled animation frame request."
  },
  "screen": {
    "availWidth": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.availWidth",
      "!doc": "Returns the amount of horizontal space in pixels available to the window."
    },
    "availHeight": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.availHeight",
      "!doc": "Returns the amount of vertical space available to the window on the screen."
    },
    "availTop": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.availTop",
      "!doc": "Specifies the y-coordinate of the first pixel that is not allocated to permanent or semipermanent user interface features."
    },
    "availLeft": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.availLeft",
      "!doc": "Returns the first available pixel available from the left side of the screen."
    },
    "pixelDepth": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.pixelDepth",
      "!doc": "Returns the bit depth of the screen."
    },
    "colorDepth": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.colorDepth",
      "!doc": "Returns the color depth of the screen."
    },
    "width": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.width",
      "!doc": "Returns the width of the screen."
    },
    "height": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.height",
      "!doc": "Returns the height of the screen in pixels."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen",
    "!doc": "Returns a reference to the screen object associated with the window."
  },
  "postMessage": {
    "!type": "fn(message: string, targetOrigin: string)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.postMessage",
    "!doc": "window.postMessage, when called, causes a MessageEvent to be dispatched at the target window when any pending script that must be executed completes (e.g. remaining event handlers if window.postMessage is called from an event handler, previously-set pending timeouts, etc.). The MessageEvent has the type message, a data property which is set to the value of the first argument provided to window.postMessage, an origin property corresponding to the origin of the main document in the window calling window.postMessage at the time window.postMessage was called, and a source property which is the window from which window.postMessage is called. (Other standard properties of events are present with their expected values.)"
  },
  "close": {
    "!type": "fn()",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.close",
    "!doc": "Closes the current window, or a referenced window."
  },
  "blur": {
    "!type": "fn()",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.blur",
    "!doc": "The blur method removes keyboard focus from the current element."
  },
  "focus": {
    "!type": "fn()",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.focus",
    "!doc": "Sets focus on the specified element, if it can be focused."
  },
  "onload": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onload",
    "!doc": "An event handler for the load event of a window."
  },
  "onunload": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onunload",
    "!doc": "The unload event is raised when the window is unloading its content and resources. The resources removal is processed after the unload event occurs."
  },
  "onscroll": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onscroll",
    "!doc": "Specifies the function to be called when the window is scrolled."
  },
  "onresize": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onresize",
    "!doc": "An event handler for the resize event on the window."
  },
  "ononline": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/document.ononline",
    "!doc": "\"online\" event is fired when the browser switches between online and offline mode."
  },
  "onoffline": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/Online_and_offline_events",
    "!doc": "Some browsers implement Online/Offline events from the WHATWG Web Applications 1.0 specification."
  },
  "onmousewheel": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/DOM_event_reference/mousewheel",
    "!doc": "The DOM mousewheel event is fired asynchronously when mouse wheel or similar device is operated. It's represented by the MouseWheelEvent interface."
  },
  "onmouseup": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onmouseup",
    "!doc": "An event handler for the mouseup event on the window."
  },
  "onmouseover": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseover",
    "!doc": "The onmouseover property returns the onMouseOver event handler code on the current element."
  },
  "onmouseout": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseout",
    "!doc": "The onmouseout property returns the onMouseOut event handler code on the current element."
  },
  "onmousemove": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmousemove",
    "!doc": "The onmousemove property returns the mousemove event handler code on the current element."
  },
  "onmousedown": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onmousedown",
    "!doc": "An event handler for the mousedown event on the window."
  },
  "onclick": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onclick",
    "!doc": "The onclick property returns the onClick event handler code on the current element."
  },
  "ondblclick": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.ondblclick",
    "!doc": "The ondblclick property returns the onDblClick event handler code on the current element."
  },
  "onmessage": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/Worker",
    "!doc": "Dedicated Web Workers provide a simple means for web content to run scripts in background threads.  Once created, a worker can send messages to the spawning task by posting messages to an event handler specified by the creator."
  },
  "onkeyup": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeyup",
    "!doc": "The onkeyup property returns the onKeyUp event handler code for the current element."
  },
  "onkeypress": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeypress",
    "!doc": "The onkeypress property sets and returns the onKeyPress event handler code for the current element."
  },
  "onkeydown": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onkeydown",
    "!doc": "An event handler for the keydown event on the window."
  },
  "oninput": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/DOM_event_reference/input",
    "!doc": "The DOM input event is fired synchronously when the value of an <input> or <textarea> element is changed. Additionally, it's also fired on contenteditable editors when its contents are changed. In this case, the event target is the editing host element. If there are two or more elements which have contenteditable as true, \"editing host\" is the nearest ancestor element whose parent isn't editable. Similarly, it's also fired on root element of designMode editors."
  },
  "onpopstate": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onpopstate",
    "!doc": "An event handler for the popstate event on the window."
  },
  "onhashchange": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onhashchange",
    "!doc": "The hashchange event fires when a window's hash changes."
  },
  "onfocus": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onfocus",
    "!doc": "The onfocus property returns the onFocus event handler code on the current element."
  },
  "onblur": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onblur",
    "!doc": "The onblur property returns the onBlur event handler code, if any, that exists on the current element."
  },
  "onerror": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onerror",
    "!doc": "An event handler for runtime script errors."
  },
  "ondrop": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/drop",
    "!doc": "The drop event is fired when an element or text selection is dropped on a valid drop target."
  },
  "ondragstart": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragstart",
    "!doc": "The dragstart event is fired when the user starts dragging an element or text selection."
  },
  "ondragover": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragover",
    "!doc": "The dragover event is fired when an element or text selection is being dragged over a valid drop target (every few hundred milliseconds)."
  },
  "ondragleave": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragleave",
    "!doc": "The dragleave event is fired when a dragged element or text selection leaves a valid drop target."
  },
  "ondragenter": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragenter",
    "!doc": "The dragenter event is fired when a dragged element or text selection enters a valid drop target."
  },
  "ondragend": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragend",
    "!doc": "The dragend event is fired when a drag operation is being ended (by releasing a mouse button or hitting the escape key)."
  },
  "ondrag": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/drag",
    "!doc": "The drag event is fired when an element or text selection is being dragged (every few hundred milliseconds)."
  },
  "oncontextmenu": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.oncontextmenu",
    "!doc": "An event handler property for right-click events on the window. Unless the default behavior is prevented, the browser context menu will activate (though IE8 has a bug with this and will not activate the context menu if a contextmenu event handler is defined). Note that this event will occur with any non-disabled right-click event and does not depend on an element possessing the \"contextmenu\" attribute."
  },
  "onchange": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onchange",
    "!doc": "The onchange property sets and returns the onChange event handler code for the current element."
  },
  "onbeforeunload": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onbeforeunload",
    "!doc": "An event that fires when a window is about to unload its resources. The document is still visible and the event is still cancelable."
  },
  "onabort": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onabort",
    "!doc": "An event handler for abort events sent to the window."
  },
  "getSelection": {
    "!type": "fn() -> +Selection",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.getSelection",
    "!doc": "Returns a selection object representing the range of text selected by the user. "
  },
  "alert": {
    "!type": "fn(message: string)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.alert",
    "!doc": "Display an alert dialog with the specified content and an OK button."
  },
  "confirm": {
    "!type": "fn(message: string) -> bool",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.confirm",
    "!doc": "Displays a modal dialog with a message and two buttons, OK and Cancel."
  },
  "prompt": {
    "!type": "fn(message: string, value: string) -> string",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.prompt",
    "!doc": "Displays a dialog with a message prompting the user to input some text."
  },
  "scrollBy": {
    "!type": "fn(x: number, y: number)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollBy",
    "!doc": "Scrolls the document in the window by the given amount."
  },
  "scrollTo": {
    "!type": "fn(x: number, y: number)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollTo",
    "!doc": "Scrolls to a particular set of coordinates in the document."
  },
  "scroll": {
    "!type": "fn(x: number, y: number)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scroll",
    "!doc": "Scrolls the window to a particular place in the document."
  },
  "setTimeout": {
    "!type": "fn(f: fn(), ms: number) -> number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.setTimeout",
    "!doc": "Calls a function or executes a code snippet after specified delay."
  },
  "clearTimeout": {
    "!type": "fn(timeout: number)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.clearTimeout",
    "!doc": "Clears the delay set by window.setTimeout()."
  },
  "setInterval": {
    "!type": "fn(f: fn(), ms: number) -> number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.setInterval",
    "!doc": "Calls a function or executes a code snippet repeatedly, with a fixed time delay between each call to that function."
  },
  "clearInterval": {
    "!type": "fn(interval: number)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.clearInterval",
    "!doc": "Cancels repeated action which was set up using setInterval."
  },
  "atob": {
    "!type": "fn(encoded: string) -> string",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.atob",
    "!doc": "Decodes a string of data which has been encoded using base-64 encoding."
  },
  "btoa": {
    "!type": "fn(data: string) -> string",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.btoa",
    "!doc": "Creates a base-64 encoded ASCII string from a string of binary data."
  },
  "addEventListener": {
    "!type": "fn(type: string, listener: fn(e: +Event), capture: bool)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.addEventListener",
    "!doc": "Registers a single event listener on a single target. The event target may be a single element in a document, the document itself, a window, or an XMLHttpRequest."
  },
  "removeEventListener": {
    "!type": "fn(type: string, listener: fn(), capture: bool)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.removeEventListener",
    "!doc": "Allows the removal of event listeners from the event target."
  },
  "dispatchEvent": {
    "!type": "fn(event: +Event) -> bool",
    "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.dispatchEvent",
    "!doc": "Dispatches an event into the event system. The event is subject to the same capturing and bubbling behavior as directly dispatched events."
  },
  "getComputedStyle": {
    "!type": "fn(node: +Element, pseudo?: string) -> Element.prototype.style",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.getComputedStyle",
    "!doc": "Gives the final used values of all the CSS properties of an element."
  },
  "CanvasRenderingContext2D": {
    "canvas": "+Element",
    "width": "number",
    "height": "number",
    "commit": "fn()",
    "save": "fn()",
    "restore": "fn()",
    "currentTransform": "?",
    "scale": "fn(x: number, y: number)",
    "rotate": "fn(angle: number)",
    "translate": "fn(x: number, y: number)",
    "transform": "fn(a: number, b: number, c: number, d: number, e: number, f: number)",
    "setTransform": "fn(a: number, b: number, c: number, d: number, e: number, f: number)",
    "resetTransform": "fn()",
    "globalAlpha": "number",
    "globalCompositeOperation": "string",
    "imageSmoothingEnabled": "bool",
    "strokeStyle": "string",
    "fillStyle": "string",
    "createLinearGradient": "fn(x0: number, y0: number, x1: number, y1: number) -> ?",
    "createPattern": "fn(image: ?, repetition: string) -> ?",
    "shadowOffsetX": "number",
    "shadowOffsetY": "number",
    "shadowBlur": "number",
    "shadowColor": "string",
    "clearRect": "fn(x: number, y: number, w: number, h: number)",
    "fillRect": "fn(x: number, y: number, w: number, h: number)",
    "strokeRect": "fn(x: number, y: number, w: number, h: number)",
    "fillRule": "string",
    "fill": "fn()",
    "beginPath": "fn()",
    "stroke": "fn()",
    "clip": "fn()",
    "resetClip": "fn()",
    "fillText": "fn(text: string, x: number, y: number, maxWidth: number)",
    "strokeText": "fn(text: string, x: number, y: number, maxWidth: number)",
    "measureText": "fn(text: string) -> ?",
    "drawImage": "fn(image: ?, dx: number, dy: number)",
    "createImageData": "fn(sw: number, sh: number) -> ?",
    "getImageData": "fn(sx: number, sy: number, sw: number, sh: number) -> ?",
    "putImageData": "fn(imagedata: ?, dx: number, dy: number)",
    "lineWidth": "number",
    "lineCap": "string",
    "lineJoin": "string",
    "miterLimit": "number",
    "setLineDash": "fn(segments: [number])",
    "getLineDash": "fn() -> [number]",
    "lineDashOffset": "number",
    "font": "string",
    "textAlign": "string",
    "textBaseline": "string",
    "direction": "string",
    "closePath": "fn()",
    "moveTo": "fn(x: number, y: number)",
    "lineTo": "fn(x: number, y: number)",
    "quadraticCurveTo": "fn(cpx: number, cpy: number, x: number, y: number)",
    "bezierCurveTo": "fn(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number)",
    "arcTo": "fn(x1: number, y1: number, x2: number, y2: number, radius: number)",
    "rect": "fn(x: number, y: number, w: number, h: number)",
    "arc": "fn(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: bool)",
    "ellipse": "fn(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, anticlockwise: bool)"
  },
  "Image": {
    "!type": "fn(width?: number, height?: number) -> +HTMLImageElement",
    "!url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image",
    "!doc": "Image Element constructor. Accepts two optional parameters: Image([unsigned long width, unsigned long height]). Returns an HTMLImageElement instance just as document.createElement('img') would."
  }
}
;});


define("json!tern/defs/chai.json", function(){ return {
  "!name": "chai",
  "chai": {
    "should": {
      "Language": {
        "!doc": "Language Chains\n\n\n\nThe following are provided as chainable getters to\nimprove the readability of your assertions. They\ndo not provide an testing capability unless they\nhave been overwritten by a plugin.Chains\n\n",
        "!url": "https://chaijs.com/api/bdd/#Language",
        "!type": "fn() -> bool"
      },
      "not": {
        "!doc": ".not\n\n\n\nNegates any of assertions following in the chain.\n\nexpect(foo).to.not.equal('bar');\nexpect(goodFn).to.not.throw(Error);\nexpect({ foo: 'baz' }).to.have.property('foo')\n  .and.not.equal('bar');",
        "!url": "https://chaijs.com/api/bdd/#not",
        "!type": "fn() -> bool"
      },
      "deep": {
        "!doc": ".deep\n\n\n\nSets the deep flag, later used by the equal and\nproperty assertions.\n\nexpect(foo).to.deep.equal({ bar: 'baz' });\nexpect({ foo: { bar: { baz: 'quux' } } })\n  .to.have.deep.property('foo.bar.baz', 'quux');",
        "!url": "https://chaijs.com/api/bdd/#deep",
        "!type": "fn() -> bool"
      },
      "a": {
        "!doc": ".a(type)\n\n  * @param { String } type\n  * @param { String } message_optional_\n\nThe a and an assertions are aliases that can be\nused either as language chains or to assert a value's\ntype.\n\n// typeof\nexpect('test').to.be.a('string');\nexpect({ foo: 'bar' }).to.be.an('object');\nexpect(null).to.be.a('null');\nexpect(undefined).to.be.an('undefined');\n\n// language chain\nexpect(foo).to.be.an.instanceof(Foo);",
        "!url": "https://chaijs.com/api/bdd/#a",
        "!type": "fn(type: string, message: string) -> bool"
      },
      "include": {
        "!doc": ".include(value)\n\n  * @param { Object | String | Number } obj\n  * @param { String } message_optional_\n\nThe include and contain assertions can be used as either property\nbased language chains or as methods to assert the inclusion of an object\nin an array or a substring in a string. When used as language chains,\nthey toggle the contain flag for the keys assertion.\n\nexpect([1,2,3]).to.include(2);\nexpect('foobar').to.contain('foo');\nexpect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo');",
        "!url": "https://chaijs.com/api/bdd/#include",
        "!type": "fn(obj: object, message: string) -> bool"
      },
      "ok": {
        "!doc": ".ok\n\n\n\nAsserts that the target is truthy.\n\nexpect('everthing').to.be.ok;\nexpect(1).to.be.ok;\nexpect(false).to.not.be.ok;\nexpect(undefined).to.not.be.ok;\nexpect(null).to.not.be.ok;",
        "!url": "https://chaijs.com/api/bdd/#ok",
        "!type": "fn() -> bool"
      },
      "true": {
        "!doc": ".true\n\n\n\nAsserts that the target is true.\n\nexpect(true).to.be.true;\nexpect(1).to.not.be.true;",
        "!url": "https://chaijs.com/api/bdd/#true",
        "!type": "fn() -> bool"
      },
      "false": {
        "!doc": ".false\n\n\n\nAsserts that the target is false.\n\nexpect(false).to.be.false;\nexpect(0).to.not.be.false;",
        "!url": "https://chaijs.com/api/bdd/#false",
        "!type": "fn() -> bool"
      },
      "null": {
        "!doc": ".null\n\n\n\nAsserts that the target is null.\n\nexpect(null).to.be.null;\nexpect(undefined).not.to.be.null;",
        "!url": "https://chaijs.com/api/bdd/#null",
        "!type": "fn() -> bool"
      },
      "undefined": {
        "!doc": ".undefined\n\n\n\nAsserts that the target is undefined.\n\nexpect(undefined).to.be.undefined;\nexpect(null).to.not.be.undefined;",
        "!url": "https://chaijs.com/api/bdd/#undefined",
        "!type": "fn() -> bool"
      },
      "exist": {
        "!doc": ".exist\n\n\n\nAsserts that the target is neither null nor undefined.\n\nvar foo = 'hi'\n  , bar = null\n  , baz;\n\nexpect(foo).to.exist;\nexpect(bar).to.not.exist;\nexpect(baz).to.not.exist;",
        "!url": "https://chaijs.com/api/bdd/#exist",
        "!type": "fn() -> bool"
      },
      "empty": {
        "!doc": ".empty\n\n\n\nAsserts that the target's length is 0. For arrays, it checks\nthe length property. For objects, it gets the count of\nenumerable keys.\n\nexpect([]).to.be.empty;\nexpect('').to.be.empty;\nexpect({}).to.be.empty;",
        "!url": "https://chaijs.com/api/bdd/#empty",
        "!type": "fn() -> bool"
      },
      "arguments": {
        "!doc": ".arguments\n\n\n\nAsserts that the target is an arguments object.\n\nfunction test () {\n  expect(arguments).to.be.arguments;\n}",
        "!url": "https://chaijs.com/api/bdd/#arguments",
        "!type": "fn() -> bool"
      },
      "equal": {
        "!doc": ".equal(value)\n\n  * @param { Mixed } value\n  * @param { String } message_optional_\n\nAsserts that the target is strictly equal (===) to value.\nAlternately, if the deep flag is set, asserts that\nthe target is deeply equal to value.\n\nexpect('hello').to.equal('hello');\nexpect(42).to.equal(42);\nexpect(1).to.not.equal(true);\nexpect({ foo: 'bar' }).to.not.equal({ foo: 'bar' });\nexpect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' });",
        "!url": "https://chaijs.com/api/bdd/#equal",
        "!type": "fn(value: mixed, message: string) -> bool"
      },
      "eql": {
        "!doc": ".eql(value)\n\n  * @param { Mixed } value\n  * @param { String } message_optional_\n\nAsserts that the target is deeply equal to value.\n\nexpect({ foo: 'bar' }).to.eql({ foo: 'bar' });\nexpect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]);",
        "!url": "https://chaijs.com/api/bdd/#eql",
        "!type": "fn(value: mixed, message: string) -> bool"
      },
      "above": {
        "!doc": ".above(value)\n\n  * @param { Number } value\n  * @param { String } message_optional_\n\nAsserts that the target is greater than value.Can also be used in conjunction with length to\nassert a minimum length. The benefit being a\nmore informative error message than if the length\nwas supplied directly.\n\nexpect(10).to.be.above(5);expect('foo').to.have.length.above(2);\nexpect([ 1, 2, 3 ]).to.have.length.above(2);",
        "!url": "https://chaijs.com/api/bdd/#above",
        "!type": "fn(value: number, message: string) -> bool"
      },
      "least": {
        "!doc": ".least(value)\n\n  * @param { Number } value\n  * @param { String } message_optional_\n\nAsserts that the target is greater than or equal to value.Can also be used in conjunction with length to\nassert a minimum length. The benefit being a\nmore informative error message than if the length\nwas supplied directly.\n\nexpect(10).to.be.at.least(10);expect('foo').to.have.length.of.at.least(2);\nexpect([ 1, 2, 3 ]).to.have.length.of.at.least(3);",
        "!url": "https://chaijs.com/api/bdd/#least",
        "!type": "fn(value: number, message: string) -> bool"
      },
      "below": {
        "!doc": ".below(value)\n\n  * @param { Number } value\n  * @param { String } message_optional_\n\nAsserts that the target is less than value.Can also be used in conjunction with length to\nassert a maximum length. The benefit being a\nmore informative error message than if the length\nwas supplied directly.\n\nexpect(5).to.be.below(10);expect('foo').to.have.length.below(4);\nexpect([ 1, 2, 3 ]).to.have.length.below(4);",
        "!url": "https://chaijs.com/api/bdd/#below",
        "!type": "fn(value: number, message: string) -> bool"
      },
      "most": {
        "!doc": ".most(value)\n\n  * @param { Number } value\n  * @param { String } message_optional_\n\nAsserts that the target is less than or equal to value.Can also be used in conjunction with length to\nassert a maximum length. The benefit being a\nmore informative error message than if the length\nwas supplied directly.\n\nexpect(5).to.be.at.most(5);expect('foo').to.have.length.of.at.most(4);\nexpect([ 1, 2, 3 ]).to.have.length.of.at.most(3);",
        "!url": "https://chaijs.com/api/bdd/#most",
        "!type": "fn(value: number, message: string) -> bool"
      },
      "within": {
        "!doc": ".within(start, finish)\n\n  * @param { Number } startlowerbound inclusive\n  * @param { Number } finishupperbound inclusive\n  * @param { String } message_optional_\n\nAsserts that the target is within a range.Can also be used in conjunction with length to\nassert a length range. The benefit being a\nmore informative error message than if the length\nwas supplied directly.\n\nexpect(7).to.be.within(5,10);expect('foo').to.have.length.within(2,4);\nexpect([ 1, 2, 3 ]).to.have.length.within(2,4);",
        "!url": "https://chaijs.com/api/bdd/#within",
        "!type": "fn(start: number, finish: number, message: string) -> bool"
      },
      "instanceof": {
        "!doc": ".instanceof(constructor)\n\n  * @param { Constructor } constructor\n  * @param { String } message_optional_\n\nAsserts that the target is an instance of constructor.\n\nvar Tea = function (name) { this.name = name; }\n  , Chai = new Tea('chai');\n\nexpect(Chai).to.be.an.instanceof(Tea);\nexpect([ 1, 2, 3 ]).to.be.instanceof(Array);",
        "!url": "https://chaijs.com/api/bdd/#instanceof",
        "!type": "fn(constructor: constructor, message: string) -> bool"
      },
      "property": {
        "!doc": ".property(name, [value])\n\n  * @param { String } name\n  * @param { Mixed } value(optional)\n  * @param { String } message_optional_\n\nAsserts that the target has a property name, optionally asserting that\nthe value of that property is strictly equal to  value.\nIf the deep flag is set, you can use dot- and bracket-notation for deep\nreferences into objects and arrays.You can also use an array as the starting point of a deep.property\nassertion, or traverse nested arrays.Furthermore, property changes the subject of the assertion\nto be the value of that property from the original object. This\npermits for further chainable assertions on that property.\n\n// simple referencing\nvar obj = { foo: 'bar' };\nexpect(obj).to.have.property('foo');\nexpect(obj).to.have.property('foo', 'bar');\n\n// deep referencing\nvar deepObj = {\n    green: { tea: 'matcha' }\n  , teas: [ 'chai', 'matcha', { tea: 'konacha' } ]\n};\n\nexpect(deepObj).to.have.deep.property('green.tea', 'matcha');\nexpect(deepObj).to.have.deep.property('teas[1]', 'matcha');\nexpect(deepObj).to.have.deep.property('teas[2].tea', 'konacha');var arr = [\n    [ 'chai', 'matcha', 'konacha' ]\n  , [ { tea: 'chai' }\n    , { tea: 'matcha' }\n    , { tea: 'konacha' } ]\n];\n\nexpect(arr).to.have.deep.property('[0][1]', 'matcha');\nexpect(arr).to.have.deep.property('[1][2].tea', 'konacha');expect(obj).to.have.property('foo')\n  .that.is.a('string');\nexpect(deepObj).to.have.property('green')\n  .that.is.an('object')\n  .that.deep.equals({ tea: 'matcha' });\nexpect(deepObj).to.have.property('teas')\n  .that.is.an('array')\n  .with.deep.property('[2]')\n    .that.deep.equals({ tea: 'konacha' });",
        "!url": "https://chaijs.com/api/bdd/#property",
        "!type": "fn(name: string, value: [mixed], message: string) -> bool"
      },
      "ownProperty": {
        "!doc": ".ownProperty(name)\n\n  * @param { String } name\n  * @param { String } message_optional_\n\nAsserts that the target has an own property name.\n\nexpect('test').to.have.ownProperty('length');",
        "!url": "https://chaijs.com/api/bdd/#ownProperty",
        "!type": "fn(name: string, message: string) -> bool"
      },
      "length": {
        "!doc": ".length(value)\n\n  * @param { Number } length\n  * @param { String } message_optional_\n\nAsserts that the target's length property has\nthe expected value.Can also be used as a chain precursor to a value\ncomparison for the length property.\n\nexpect([ 1, 2, 3]).to.have.length(3);\nexpect('foobar').to.have.length(6);expect('foo').to.have.length.above(2);\nexpect([ 1, 2, 3 ]).to.have.length.above(2);\nexpect('foo').to.have.length.below(4);\nexpect([ 1, 2, 3 ]).to.have.length.below(4);\nexpect('foo').to.have.length.within(2,4);\nexpect([ 1, 2, 3 ]).to.have.length.within(2,4);",
        "!url": "https://chaijs.com/api/bdd/#length",
        "!type": "fn(length: number, message: string) -> bool"
      },
      "match": {
        "!doc": ".match(regexp)\n\n  * @param { RegExp } RegularExpression\n  * @param { String } message_optional_\n\nAsserts that the target matches a regular expression.\n\nexpect('foobar').to.match(/^foo/);",
        "!url": "https://chaijs.com/api/bdd/#match",
        "!type": "fn(RegularExpression: regexp, message: string) -> bool"
      },
      "string": {
        "!doc": ".string(string)\n\n  * @param { String } string\n  * @param { String } message_optional_\n\nAsserts that the string target contains another string.\n\nexpect('foobar').to.have.string('bar');",
        "!url": "https://chaijs.com/api/bdd/#string",
        "!type": "fn(string: string, message: string) -> bool"
      },
      "keys": {
        "!doc": ".keys(key1, [key2], [...])\n\n  * @param { String... | Array } keys\n\nAsserts that the target has exactly the given keys, or\nasserts the inclusion of some keys when using the\ninclude or contain modifiers.\n\nexpect({ foo: 1, bar: 2 }).to.have.keys(['foo', 'bar']);\nexpect({ foo: 1, bar: 2, baz: 3 }).to.contain.keys('foo', 'bar');",
        "!url": "https://chaijs.com/api/bdd/#keys",
        "!type": "fn(keys: string) -> bool"
      },
      "throw": {
        "!doc": ".throw(constructor)\n\n  * @param { ErrorConstructor } constructor\n  * @param { String | RegExp } expectederror message\n  * @param { String } message_optional_\n  * @seehttps://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types  \n\nAsserts that the function target will throw a specific error, or specific type of error\n(as determined using instanceof), optionally with a RegExp or string inclusion test\nfor the error's message.Please note that when a throw expectation is negated, it will check each\nparameter independently, starting with error constructor type. The appropriate way\nto check for the existence of a type of error but for a message that does not match\nis to use and.\n\nvar err = new ReferenceError('This is a bad function.');\nvar fn = function () { throw err; }\nexpect(fn).to.throw(ReferenceError);\nexpect(fn).to.throw(Error);\nexpect(fn).to.throw(/bad function/);\nexpect(fn).to.not.throw('good function');\nexpect(fn).to.throw(ReferenceError, /bad function/);\nexpect(fn).to.throw(err);\nexpect(fn).to.not.throw(new RangeError('Out of range.'));expect(fn).to.throw(ReferenceError)\n   .and.not.throw(/good function/);",
        "!url": "https://chaijs.com/api/bdd/#throw",
        "!type": "fn(constructor: errorconstructor, expected: string, message: string) -> bool"
      },
      "respondTo": {
        "!doc": ".respondTo(method)\n\n  * @param { String } method\n  * @param { String } message_optional_\n\nAsserts that the object or class target will respond to a method.To check if a constructor will respond to a static function,\nset the itself flag.\n\nKlass.prototype.bar = function(){};\nexpect(Klass).to.respondTo('bar');\nexpect(obj).to.respondTo('bar');Klass.baz = function(){};\nexpect(Klass).itself.to.respondTo('baz');",
        "!url": "https://chaijs.com/api/bdd/#respondTo",
        "!type": "fn(method: string, message: string) -> bool"
      },
      "itself": {
        "!doc": ".itself\n\n\n\nSets the itself flag, later used by the respondTo assertion.\n\nfunction Foo() {}\nFoo.bar = function() {}\nFoo.prototype.baz = function() {}\n\nexpect(Foo).itself.to.respondTo('bar');\nexpect(Foo).itself.not.to.respondTo('baz');",
        "!url": "https://chaijs.com/api/bdd/#itself",
        "!type": "fn() -> bool"
      },
      "satisfy": {
        "!doc": ".satisfy(method)\n\n  * @param { Function } matcher\n  * @param { String } message_optional_\n\nAsserts that the target passes a given truth test.\n\nexpect(1).to.satisfy(function(num) { return num > 0; });",
        "!url": "https://chaijs.com/api/bdd/#satisfy",
        "!type": "fn(matcher: function, message: string) -> bool"
      },
      "closeTo": {
        "!doc": ".closeTo(expected, delta)\n\n  * @param { Number } expected\n  * @param { Number } delta\n  * @param { String } message_optional_\n\nAsserts that the target is equal expected, to within a +/- delta range.\n\nexpect(1.5).to.be.closeTo(1, 0.5);",
        "!url": "https://chaijs.com/api/bdd/#closeTo",
        "!type": "fn(expected: number, delta: number, message: string) -> bool"
      },
      "members": {
        "!doc": ".members(set)\n\n  * @param { Array } set\n  * @param { String } message_optional_\n\nAsserts that the target is a superset of set,\nor that the target and set have the same members.\n\nexpect([1, 2, 3]).to.include.members([3, 2]);\nexpect([1, 2, 3]).to.not.include.members([3, 2, 8]);\n\nexpect([4, 2]).to.have.members([2, 4]);\nexpect([5, 2]).to.not.have.members([5, 2, 1]);",
        "!url": "https://chaijs.com/api/bdd/#members",
        "!type": "fn(set: array, message: string) -> bool"
      }
    },
    "assert": {
      "assert": {
        "!doc": "assert(expression, message)\n\n  * @param { Mixed } expressionto test for truthiness\n  * @param { String } messageto display on error\n\nWrite your own test expressions.\n\nassert('foo' !== 'bar', 'foo is not bar');\nassert(Array.isArray([]), 'empty arrays are arrays');",
        "!url": "https://chaijs.com/api/assert/#assert",
        "!type": "fn(expression: mixed, message: string) -> bool"
      },
      "fail": {
        "!doc": ".fail(actual, expected, [message], [operator])\n\n  * @param { Mixed } actual\n  * @param { Mixed } expected\n  * @param { String } message\n  * @param { String } operator\n\nThrow a failure. Node.js assert module-compatible.\n\n",
        "!url": "https://chaijs.com/api/assert/#fail",
        "!type": "fn(actual: mixed, expected: mixed, message: [string], operator: [string]) -> bool"
      },
      "ok": {
        "!doc": ".ok(object, [message])\n\n  * @param { Mixed } objectto test\n  * @param { String } message\n\nAsserts that object is truthy.\n\nassert.ok('everything', 'everything is ok');\nassert.ok(false, 'this will fail');",
        "!url": "https://chaijs.com/api/assert/#ok",
        "!type": "fn(object: mixed, message: [string]) -> bool"
      },
      "notOk": {
        "!doc": ".notOk(object, [message])\n\n  * @param { Mixed } objectto test\n  * @param { String } message\n\nAsserts that object is falsy.\n\nassert.notOk('everything', 'this will fail');\nassert.notOk(false, 'this will pass');",
        "!url": "https://chaijs.com/api/assert/#notOk",
        "!type": "fn(object: mixed, message: [string]) -> bool"
      },
      "equal": {
        "!doc": ".equal(actual, expected, [message])\n\n  * @param { Mixed } actual\n  * @param { Mixed } expected\n  * @param { String } message\n\nAsserts non-strict equality (==) of actual and expected.\n\nassert.equal(3, '3', '== coerces values to strings');",
        "!url": "https://chaijs.com/api/assert/#equal",
        "!type": "fn(actual: mixed, expected: mixed, message: [string]) -> bool"
      },
      "notEqual": {
        "!doc": ".notEqual(actual, expected, [message])\n\n  * @param { Mixed } actual\n  * @param { Mixed } expected\n  * @param { String } message\n\nAsserts non-strict inequality (!=) of actual and expected.\n\nassert.notEqual(3, 4, 'these numbers are not equal');",
        "!url": "https://chaijs.com/api/assert/#notEqual",
        "!type": "fn(actual: mixed, expected: mixed, message: [string]) -> bool"
      },
      "strictEqual": {
        "!doc": ".strictEqual(actual, expected, [message])\n\n  * @param { Mixed } actual\n  * @param { Mixed } expected\n  * @param { String } message\n\nAsserts strict equality (===) of actual and expected.\n\nassert.strictEqual(true, true, 'these booleans are strictly equal');",
        "!url": "https://chaijs.com/api/assert/#strictEqual",
        "!type": "fn(actual: mixed, expected: mixed, message: [string]) -> bool"
      },
      "notStrictEqual": {
        "!doc": ".notStrictEqual(actual, expected, [message])\n\n  * @param { Mixed } actual\n  * @param { Mixed } expected\n  * @param { String } message\n\nAsserts strict inequality (!==) of actual and expected.\n\nassert.notStrictEqual(3, '3', 'no coercion for strict equality');",
        "!url": "https://chaijs.com/api/assert/#notStrictEqual",
        "!type": "fn(actual: mixed, expected: mixed, message: [string]) -> bool"
      },
      "deepEqual": {
        "!doc": ".deepEqual(actual, expected, [message])\n\n  * @param { Mixed } actual\n  * @param { Mixed } expected\n  * @param { String } message\n\nAsserts that actual is deeply equal to expected.\n\nassert.deepEqual({ tea: 'green' }, { tea: 'green' });",
        "!url": "https://chaijs.com/api/assert/#deepEqual",
        "!type": "fn(actual: mixed, expected: mixed, message: [string]) -> bool"
      },
      "notDeepEqual": {
        "!doc": ".notDeepEqual(actual, expected, [message])\n\n  * @param { Mixed } actual\n  * @param { Mixed } expected\n  * @param { String } message\n\nAssert that actual is not deeply equal to expected.\n\nassert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' });",
        "!url": "https://chaijs.com/api/assert/#notDeepEqual",
        "!type": "fn(actual: mixed, expected: mixed, message: [string]) -> bool"
      },
      "isTrue": {
        "!doc": ".isTrue(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is true.\n\nvar teaServed = true;\nassert.isTrue(teaServed, 'the tea has been served');",
        "!url": "https://chaijs.com/api/assert/#isTrue",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isFalse": {
        "!doc": ".isFalse(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is false.\n\nvar teaServed = false;\nassert.isFalse(teaServed, 'no tea yet? hmm...');",
        "!url": "https://chaijs.com/api/assert/#isFalse",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isNull": {
        "!doc": ".isNull(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is null.\n\nassert.isNull(err, 'there was no error');",
        "!url": "https://chaijs.com/api/assert/#isNull",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isNotNull": {
        "!doc": ".isNotNull(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is not null.\n\nvar tea = 'tasty chai';\nassert.isNotNull(tea, 'great, time for tea!');",
        "!url": "https://chaijs.com/api/assert/#isNotNull",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isUndefined": {
        "!doc": ".isUndefined(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is undefined.\n\nvar tea;\nassert.isUndefined(tea, 'no tea defined');",
        "!url": "https://chaijs.com/api/assert/#isUndefined",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isDefined": {
        "!doc": ".isDefined(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is not undefined.\n\nvar tea = 'cup of chai';\nassert.isDefined(tea, 'tea has been defined');",
        "!url": "https://chaijs.com/api/assert/#isDefined",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isFunction": {
        "!doc": ".isFunction(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is a function.\n\nfunction serveTea() { return 'cup of tea'; };\nassert.isFunction(serveTea, 'great, we can have tea now');",
        "!url": "https://chaijs.com/api/assert/#isFunction",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isNotFunction": {
        "!doc": ".isNotFunction(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is not a function.\n\nvar serveTea = [ 'heat', 'pour', 'sip' ];\nassert.isNotFunction(serveTea, 'great, we have listed the steps');",
        "!url": "https://chaijs.com/api/assert/#isNotFunction",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isObject": {
        "!doc": ".isObject(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is an object (as revealed by\nObject.prototype.toString).\n\nvar selection = { name: 'Chai', serve: 'with spices' };\nassert.isObject(selection, 'tea selection is an object');",
        "!url": "https://chaijs.com/api/assert/#isObject",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isNotObject": {
        "!doc": ".isNotObject(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is not an object.\n\nvar selection = 'chai'\nassert.isObject(selection, 'tea selection is not an object');\nassert.isObject(null, 'null is not an object');",
        "!url": "https://chaijs.com/api/assert/#isNotObject",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isArray": {
        "!doc": ".isArray(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is an array.\n\nvar menu = [ 'green', 'chai', 'oolong' ];\nassert.isArray(menu, 'what kind of tea do we want?');",
        "!url": "https://chaijs.com/api/assert/#isArray",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isNotArray": {
        "!doc": ".isNotArray(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is not an array.\n\nvar menu = 'green|chai|oolong';\nassert.isNotArray(menu, 'what kind of tea do we want?');",
        "!url": "https://chaijs.com/api/assert/#isNotArray",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isString": {
        "!doc": ".isString(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is a string.\n\nvar teaOrder = 'chai';\nassert.isString(teaOrder, 'order placed');",
        "!url": "https://chaijs.com/api/assert/#isString",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isNotString": {
        "!doc": ".isNotString(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is not a string.\n\nvar teaOrder = 4;\nassert.isNotString(teaOrder, 'order placed');",
        "!url": "https://chaijs.com/api/assert/#isNotString",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isNumber": {
        "!doc": ".isNumber(value, [message])\n\n  * @param { Number } value\n  * @param { String } message\n\nAsserts that value is a number.\n\nvar cups = 2;\nassert.isNumber(cups, 'how many cups');",
        "!url": "https://chaijs.com/api/assert/#isNumber",
        "!type": "fn(value: number, message: [string]) -> bool"
      },
      "isNotNumber": {
        "!doc": ".isNotNumber(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is not a number.\n\nvar cups = '2 cups please';\nassert.isNotNumber(cups, 'how many cups');",
        "!url": "https://chaijs.com/api/assert/#isNotNumber",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isBoolean": {
        "!doc": ".isBoolean(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is a boolean.\n\nvar teaReady = true\n  , teaServed = false;\n\nassert.isBoolean(teaReady, 'is the tea ready');\nassert.isBoolean(teaServed, 'has tea been served');",
        "!url": "https://chaijs.com/api/assert/#isBoolean",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "isNotBoolean": {
        "!doc": ".isNotBoolean(value, [message])\n\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that value is not a boolean.\n\nvar teaReady = 'yep'\n  , teaServed = 'nope';\n\nassert.isNotBoolean(teaReady, 'is the tea ready');\nassert.isNotBoolean(teaServed, 'has tea been served');",
        "!url": "https://chaijs.com/api/assert/#isNotBoolean",
        "!type": "fn(value: mixed, message: [string]) -> bool"
      },
      "typeOf": {
        "!doc": ".typeOf(value, name, [message])\n\n  * @param { Mixed } value\n  * @param { String } name\n  * @param { String } message\n\nAsserts that value's type is name, as determined by\nObject.prototype.toString.\n\nassert.typeOf({ tea: 'chai' }, 'object', 'we have an object');\nassert.typeOf(['chai', 'jasmine'], 'array', 'we have an array');\nassert.typeOf('tea', 'string', 'we have a string');\nassert.typeOf(/tea/, 'regexp', 'we have a regular expression');\nassert.typeOf(null, 'null', 'we have a null');\nassert.typeOf(undefined, 'undefined', 'we have an undefined');",
        "!url": "https://chaijs.com/api/assert/#typeOf",
        "!type": "fn(value: mixed, name: string, message: [string]) -> bool"
      },
      "notTypeOf": {
        "!doc": ".notTypeOf(value, name, [message])\n\n  * @param { Mixed } value\n  * @param { String } typeofname\n  * @param { String } message\n\nAsserts that value's type is not name, as determined by\nObject.prototype.toString.\n\nassert.notTypeOf('tea', 'number', 'strings are not numbers');",
        "!url": "https://chaijs.com/api/assert/#notTypeOf",
        "!type": "fn(value: mixed, typeof: string, message: [string]) -> bool"
      },
      "instanceOf": {
        "!doc": ".instanceOf(object, constructor, [message])\n\n  * @param { Object } object\n  * @param { Constructor } constructor\n  * @param { String } message\n\nAsserts that value is an instance of constructor.\n\nvar Tea = function (name) { this.name = name; }\n  , chai = new Tea('chai');\n\nassert.instanceOf(chai, Tea, 'chai is an instance of tea');",
        "!url": "https://chaijs.com/api/assert/#instanceOf",
        "!type": "fn(object: object, constructor: constructor, message: [string]) -> bool"
      },
      "notInstanceOf": {
        "!doc": ".notInstanceOf(object, constructor, [message])\n\n  * @param { Object } object\n  * @param { Constructor } constructor\n  * @param { String } message\n\nAsserts value is not an instance of constructor.\n\nvar Tea = function (name) { this.name = name; }\n  , chai = new String('chai');\n\nassert.notInstanceOf(chai, Tea, 'chai is not an instance of tea');",
        "!url": "https://chaijs.com/api/assert/#notInstanceOf",
        "!type": "fn(object: object, constructor: constructor, message: [string]) -> bool"
      },
      "include": {
        "!doc": ".include(haystack, needle, [message])\n\n  * @param { Array | String } haystack\n  * @param { Mixed } needle\n  * @param { String } message\n\nAsserts that haystack includes needle. Works\nfor strings and arrays.\n\nassert.include('foobar', 'bar', 'foobar contains string \"bar\"');\nassert.include([ 1, 2, 3 ], 3, 'array contains value');",
        "!url": "https://chaijs.com/api/assert/#include",
        "!type": "fn(haystack: array, needle: mixed, message: [string]) -> bool"
      },
      "notInclude": {
        "!doc": ".notInclude(haystack, needle, [message])\n\n  * @param { Array | String } haystack\n  * @param { Mixed } needle\n  * @param { String } message\n\nAsserts that haystack does not include needle. Works\nfor strings and arrays.\ni\n    assert.notInclude('foobar', 'baz', 'string not include substring');\n    assert.notInclude([ 1, 2, 3 ], 4, 'array not include contain value');\n\n",
        "!url": "https://chaijs.com/api/assert/#notInclude",
        "!type": "fn(haystack: array, needle: mixed, message: [string]) -> bool"
      },
      "match": {
        "!doc": ".match(value, regexp, [message])\n\n  * @param { Mixed } value\n  * @param { RegExp } regexp\n  * @param { String } message\n\nAsserts that value matches the regular expression regexp.\n\nassert.match('foobar', /^foo/, 'regexp matches');",
        "!url": "https://chaijs.com/api/assert/#match",
        "!type": "fn(value: mixed, regexp: regexp, message: [string]) -> bool"
      },
      "notMatch": {
        "!doc": ".notMatch(value, regexp, [message])\n\n  * @param { Mixed } value\n  * @param { RegExp } regexp\n  * @param { String } message\n\nAsserts that value does not match the regular expression regexp.\n\nassert.notMatch('foobar', /^foo/, 'regexp does not match');",
        "!url": "https://chaijs.com/api/assert/#notMatch",
        "!type": "fn(value: mixed, regexp: regexp, message: [string]) -> bool"
      },
      "property": {
        "!doc": ".property(object, property, [message])\n\n  * @param { Object } object\n  * @param { String } property\n  * @param { String } message\n\nAsserts that object has a property named by property.\n\nassert.property({ tea: { green: 'matcha' }}, 'tea');",
        "!url": "https://chaijs.com/api/assert/#property",
        "!type": "fn(object: object, property: string, message: [string]) -> bool"
      },
      "notProperty": {
        "!doc": ".notProperty(object, property, [message])\n\n  * @param { Object } object\n  * @param { String } property\n  * @param { String } message\n\nAsserts that object does not have a property named by property.\n\nassert.notProperty({ tea: { green: 'matcha' }}, 'coffee');",
        "!url": "https://chaijs.com/api/assert/#notProperty",
        "!type": "fn(object: object, property: string, message: [string]) -> bool"
      },
      "deepProperty": {
        "!doc": ".deepProperty(object, property, [message])\n\n  * @param { Object } object\n  * @param { String } property\n  * @param { String } message\n\nAsserts that object has a property named by property, which can be a\nstring using dot- and bracket-notation for deep reference.\n\nassert.deepProperty({ tea: { green: 'matcha' }}, 'tea.green');",
        "!url": "https://chaijs.com/api/assert/#deepProperty",
        "!type": "fn(object: object, property: string, message: [string]) -> bool"
      },
      "notDeepProperty": {
        "!doc": ".notDeepProperty(object, property, [message])\n\n  * @param { Object } object\n  * @param { String } property\n  * @param { String } message\n\nAsserts that object does not have a property named by property, which\ncan be a string using dot- and bracket-notation for deep reference.\n\nassert.notDeepProperty({ tea: { green: 'matcha' }}, 'tea.oolong');",
        "!url": "https://chaijs.com/api/assert/#notDeepProperty",
        "!type": "fn(object: object, property: string, message: [string]) -> bool"
      },
      "propertyVal": {
        "!doc": ".propertyVal(object, property, value, [message])\n\n  * @param { Object } object\n  * @param { String } property\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that object has a property named by property with value given\nby value.\n\nassert.propertyVal({ tea: 'is good' }, 'tea', 'is good');",
        "!url": "https://chaijs.com/api/assert/#propertyVal",
        "!type": "fn(object: object, property: string, value: mixed, message: [string]) -> bool"
      },
      "propertyNotVal": {
        "!doc": ".propertyNotVal(object, property, value, [message])\n\n  * @param { Object } object\n  * @param { String } property\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that object has a property named by property, but with a value\ndifferent from that given by value.\n\nassert.propertyNotVal({ tea: 'is good' }, 'tea', 'is bad');",
        "!url": "https://chaijs.com/api/assert/#propertyNotVal",
        "!type": "fn(object: object, property: string, value: mixed, message: [string]) -> bool"
      },
      "deepPropertyVal": {
        "!doc": ".deepPropertyVal(object, property, value, [message])\n\n  * @param { Object } object\n  * @param { String } property\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that object has a property named by property with value given\nby value. property can use dot- and bracket-notation for deep\nreference.\n\nassert.deepPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha');",
        "!url": "https://chaijs.com/api/assert/#deepPropertyVal",
        "!type": "fn(object: object, property: string, value: mixed, message: [string]) -> bool"
      },
      "deepPropertyNotVal": {
        "!doc": ".deepPropertyNotVal(object, property, value, [message])\n\n  * @param { Object } object\n  * @param { String } property\n  * @param { Mixed } value\n  * @param { String } message\n\nAsserts that object has a property named by property, but with a value\ndifferent from that given by value. property can use dot- and\nbracket-notation for deep reference.\n\nassert.deepPropertyNotVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha');",
        "!url": "https://chaijs.com/api/assert/#deepPropertyNotVal",
        "!type": "fn(object: object, property: string, value: mixed, message: [string]) -> bool"
      },
      "lengthOf": {
        "!doc": ".lengthOf(object, length, [message])\n\n  * @param { Mixed } object\n  * @param { Number } length\n  * @param { String } message\n\nAsserts that object has a length property with the expected value.\n\nassert.lengthOf([1,2,3], 3, 'array has length of 3');\nassert.lengthOf('foobar', 5, 'string has length of 6');",
        "!url": "https://chaijs.com/api/assert/#lengthOf",
        "!type": "fn(object: mixed, length: number, message: [string]) -> bool"
      },
      "throws": {
        "!doc": ".throws(function, [constructor/string/regexp], [string/regexp], [message])\n\n  * @param { Function } function\n  * @param { ErrorConstructor } constructor\n  * @param { RegExp } regexp\n  * @param { String } message\n  * @seehttps://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types  \n\nAsserts that function will throw an error that is an instance of\nconstructor, or alternately that it will throw an error with message\nmatching regexp.\n\nassert.throw(fn, 'function throws a reference error');\nassert.throw(fn, /function throws a reference error/);\nassert.throw(fn, ReferenceError);\nassert.throw(fn, ReferenceError, 'function throws a reference error');\nassert.throw(fn, ReferenceError, /function throws a reference error/);",
        "!url": "https://chaijs.com/api/assert/#throws",
        "!type": "fn(function: function, constructor: [errorconstructor], regexp: [regexp], message: [string]) -> bool"
      },
      "doesNotThrow": {
        "!doc": ".doesNotThrow(function, [constructor/regexp], [message])\n\n  * @param { Function } function\n  * @param { ErrorConstructor } constructor\n  * @param { RegExp } regexp\n  * @param { String } message\n  * @seehttps://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types  \n\nAsserts that function will not throw an error that is an instance of\nconstructor, or alternately that it will not throw an error with message\nmatching regexp.\n\nassert.doesNotThrow(fn, Error, 'function does not throw');",
        "!url": "https://chaijs.com/api/assert/#doesNotThrow",
        "!type": "fn(function: function, constructor: [errorconstructor], regexp: [regexp], message: string) -> bool"
      },
      "operator": {
        "!doc": ".operator(val1, operator, val2, [message])\n\n  * @param { Mixed } val1\n  * @param { String } operator\n  * @param { Mixed } val2\n  * @param { String } message\n\nCompares two values using operator.\n\nassert.operator(1, '<', 2, 'everything is ok');\nassert.operator(1, '>', 2, 'this will fail');",
        "!url": "https://chaijs.com/api/assert/#operator",
        "!type": "fn(val1: mixed, operator: string, val2: mixed, message: [string]) -> bool"
      },
      "closeTo": {
        "!doc": ".closeTo(actual, expected, delta, [message])\n\n  * @param { Number } actual\n  * @param { Number } expected\n  * @param { Number } delta\n  * @param { String } message\n\nAsserts that the target is equal expected, to within a +/- delta range.\n\nassert.closeTo(1.5, 1, 0.5, 'numbers are close');",
        "!url": "https://chaijs.com/api/assert/#closeTo",
        "!type": "fn(actual: number, expected: number, delta: number, message: [string]) -> bool"
      },
      "sameMembers": {
        "!doc": ".sameMembers(set1, set2, [message])\n\n  * @param { Array } superset\n  * @param { Array } subset\n  * @param { String } message\n\nAsserts that set1 and set2 have the same members.\nOrder is not taken into account.\n\nassert.sameMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'same members');",
        "!url": "https://chaijs.com/api/assert/#sameMembers",
        "!type": "fn(superset: array, subset: array, message: [string]) -> bool"
      },
      "includeMembers": {
        "!doc": ".includeMembers(superset, subset, [message])\n\n  * @param { Array } superset\n  * @param { Array } subset\n  * @param { String } message\n\nAsserts that subset is included in superset.\nOrder is not taken into account.\n\nassert.includeMembers([ 1, 2, 3 ], [ 2, 1 ], 'include members');",
        "!url": "https://chaijs.com/api/assert/#includeMembers",
        "!type": "fn(superset: array, subset: array, message: [string]) -> bool"
      }
    },
    "expect": {
      "Language": {
        "!doc": "Language Chains\n\n\n\nThe following are provided as chainable getters to\nimprove the readability of your assertions. They\ndo not provide an testing capability unless they\nhave been overwritten by a plugin.Chains\n\n",
        "!url": "https://chaijs.com/api/bdd/#Language",
        "!type": "fn() -> bool"
      },
      "not": {
        "!doc": ".not\n\n\n\nNegates any of assertions following in the chain.\n\nexpect(foo).to.not.equal('bar');\nexpect(goodFn).to.not.throw(Error);\nexpect({ foo: 'baz' }).to.have.property('foo')\n  .and.not.equal('bar');",
        "!url": "https://chaijs.com/api/bdd/#not",
        "!type": "fn() -> bool"
      },
      "deep": {
        "!doc": ".deep\n\n\n\nSets the deep flag, later used by the equal and\nproperty assertions.\n\nexpect(foo).to.deep.equal({ bar: 'baz' });\nexpect({ foo: { bar: { baz: 'quux' } } })\n  .to.have.deep.property('foo.bar.baz', 'quux');",
        "!url": "https://chaijs.com/api/bdd/#deep",
        "!type": "fn() -> bool"
      },
      "a": {
        "!doc": ".a(type)\n\n  * @param { String } type\n  * @param { String } message_optional_\n\nThe a and an assertions are aliases that can be\nused either as language chains or to assert a value's\ntype.\n\n// typeof\nexpect('test').to.be.a('string');\nexpect({ foo: 'bar' }).to.be.an('object');\nexpect(null).to.be.a('null');\nexpect(undefined).to.be.an('undefined');\n\n// language chain\nexpect(foo).to.be.an.instanceof(Foo);",
        "!url": "https://chaijs.com/api/bdd/#a",
        "!type": "fn(type: string, message: string) -> bool"
      },
      "include": {
        "!doc": ".include(value)\n\n  * @param { Object | String | Number } obj\n  * @param { String } message_optional_\n\nThe include and contain assertions can be used as either property\nbased language chains or as methods to assert the inclusion of an object\nin an array or a substring in a string. When used as language chains,\nthey toggle the contain flag for the keys assertion.\n\nexpect([1,2,3]).to.include(2);\nexpect('foobar').to.contain('foo');\nexpect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo');",
        "!url": "https://chaijs.com/api/bdd/#include",
        "!type": "fn(obj: object, message: string) -> bool"
      },
      "ok": {
        "!doc": ".ok\n\n\n\nAsserts that the target is truthy.\n\nexpect('everthing').to.be.ok;\nexpect(1).to.be.ok;\nexpect(false).to.not.be.ok;\nexpect(undefined).to.not.be.ok;\nexpect(null).to.not.be.ok;",
        "!url": "https://chaijs.com/api/bdd/#ok",
        "!type": "fn() -> bool"
      },
      "true": {
        "!doc": ".true\n\n\n\nAsserts that the target is true.\n\nexpect(true).to.be.true;\nexpect(1).to.not.be.true;",
        "!url": "https://chaijs.com/api/bdd/#true",
        "!type": "fn() -> bool"
      },
      "false": {
        "!doc": ".false\n\n\n\nAsserts that the target is false.\n\nexpect(false).to.be.false;\nexpect(0).to.not.be.false;",
        "!url": "https://chaijs.com/api/bdd/#false",
        "!type": "fn() -> bool"
      },
      "null": {
        "!doc": ".null\n\n\n\nAsserts that the target is null.\n\nexpect(null).to.be.null;\nexpect(undefined).not.to.be.null;",
        "!url": "https://chaijs.com/api/bdd/#null",
        "!type": "fn() -> bool"
      },
      "undefined": {
        "!doc": ".undefined\n\n\n\nAsserts that the target is undefined.\n\nexpect(undefined).to.be.undefined;\nexpect(null).to.not.be.undefined;",
        "!url": "https://chaijs.com/api/bdd/#undefined",
        "!type": "fn() -> bool"
      },
      "exist": {
        "!doc": ".exist\n\n\n\nAsserts that the target is neither null nor undefined.\n\nvar foo = 'hi'\n  , bar = null\n  , baz;\n\nexpect(foo).to.exist;\nexpect(bar).to.not.exist;\nexpect(baz).to.not.exist;",
        "!url": "https://chaijs.com/api/bdd/#exist",
        "!type": "fn() -> bool"
      },
      "empty": {
        "!doc": ".empty\n\n\n\nAsserts that the target's length is 0. For arrays, it checks\nthe length property. For objects, it gets the count of\nenumerable keys.\n\nexpect([]).to.be.empty;\nexpect('').to.be.empty;\nexpect({}).to.be.empty;",
        "!url": "https://chaijs.com/api/bdd/#empty",
        "!type": "fn() -> bool"
      },
      "arguments": {
        "!doc": ".arguments\n\n\n\nAsserts that the target is an arguments object.\n\nfunction test () {\n  expect(arguments).to.be.arguments;\n}",
        "!url": "https://chaijs.com/api/bdd/#arguments",
        "!type": "fn() -> bool"
      },
      "equal": {
        "!doc": ".equal(value)\n\n  * @param { Mixed } value\n  * @param { String } message_optional_\n\nAsserts that the target is strictly equal (===) to value.\nAlternately, if the deep flag is set, asserts that\nthe target is deeply equal to value.\n\nexpect('hello').to.equal('hello');\nexpect(42).to.equal(42);\nexpect(1).to.not.equal(true);\nexpect({ foo: 'bar' }).to.not.equal({ foo: 'bar' });\nexpect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' });",
        "!url": "https://chaijs.com/api/bdd/#equal",
        "!type": "fn(value: mixed, message: string) -> bool"
      },
      "eql": {
        "!doc": ".eql(value)\n\n  * @param { Mixed } value\n  * @param { String } message_optional_\n\nAsserts that the target is deeply equal to value.\n\nexpect({ foo: 'bar' }).to.eql({ foo: 'bar' });\nexpect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]);",
        "!url": "https://chaijs.com/api/bdd/#eql",
        "!type": "fn(value: mixed, message: string) -> bool"
      },
      "above": {
        "!doc": ".above(value)\n\n  * @param { Number } value\n  * @param { String } message_optional_\n\nAsserts that the target is greater than value.Can also be used in conjunction with length to\nassert a minimum length. The benefit being a\nmore informative error message than if the length\nwas supplied directly.\n\nexpect(10).to.be.above(5);expect('foo').to.have.length.above(2);\nexpect([ 1, 2, 3 ]).to.have.length.above(2);",
        "!url": "https://chaijs.com/api/bdd/#above",
        "!type": "fn(value: number, message: string) -> bool"
      },
      "least": {
        "!doc": ".least(value)\n\n  * @param { Number } value\n  * @param { String } message_optional_\n\nAsserts that the target is greater than or equal to value.Can also be used in conjunction with length to\nassert a minimum length. The benefit being a\nmore informative error message than if the length\nwas supplied directly.\n\nexpect(10).to.be.at.least(10);expect('foo').to.have.length.of.at.least(2);\nexpect([ 1, 2, 3 ]).to.have.length.of.at.least(3);",
        "!url": "https://chaijs.com/api/bdd/#least",
        "!type": "fn(value: number, message: string) -> bool"
      },
      "below": {
        "!doc": ".below(value)\n\n  * @param { Number } value\n  * @param { String } message_optional_\n\nAsserts that the target is less than value.Can also be used in conjunction with length to\nassert a maximum length. The benefit being a\nmore informative error message than if the length\nwas supplied directly.\n\nexpect(5).to.be.below(10);expect('foo').to.have.length.below(4);\nexpect([ 1, 2, 3 ]).to.have.length.below(4);",
        "!url": "https://chaijs.com/api/bdd/#below",
        "!type": "fn(value: number, message: string) -> bool"
      },
      "most": {
        "!doc": ".most(value)\n\n  * @param { Number } value\n  * @param { String } message_optional_\n\nAsserts that the target is less than or equal to value.Can also be used in conjunction with length to\nassert a maximum length. The benefit being a\nmore informative error message than if the length\nwas supplied directly.\n\nexpect(5).to.be.at.most(5);expect('foo').to.have.length.of.at.most(4);\nexpect([ 1, 2, 3 ]).to.have.length.of.at.most(3);",
        "!url": "https://chaijs.com/api/bdd/#most",
        "!type": "fn(value: number, message: string) -> bool"
      },
      "within": {
        "!doc": ".within(start, finish)\n\n  * @param { Number } startlowerbound inclusive\n  * @param { Number } finishupperbound inclusive\n  * @param { String } message_optional_\n\nAsserts that the target is within a range.Can also be used in conjunction with length to\nassert a length range. The benefit being a\nmore informative error message than if the length\nwas supplied directly.\n\nexpect(7).to.be.within(5,10);expect('foo').to.have.length.within(2,4);\nexpect([ 1, 2, 3 ]).to.have.length.within(2,4);",
        "!url": "https://chaijs.com/api/bdd/#within",
        "!type": "fn(start: number, finish: number, message: string) -> bool"
      },
      "instanceof": {
        "!doc": ".instanceof(constructor)\n\n  * @param { Constructor } constructor\n  * @param { String } message_optional_\n\nAsserts that the target is an instance of constructor.\n\nvar Tea = function (name) { this.name = name; }\n  , Chai = new Tea('chai');\n\nexpect(Chai).to.be.an.instanceof(Tea);\nexpect([ 1, 2, 3 ]).to.be.instanceof(Array);",
        "!url": "https://chaijs.com/api/bdd/#instanceof",
        "!type": "fn(constructor: constructor, message: string) -> bool"
      },
      "property": {
        "!doc": ".property(name, [value])\n\n  * @param { String } name\n  * @param { Mixed } value(optional)\n  * @param { String } message_optional_\n\nAsserts that the target has a property name, optionally asserting that\nthe value of that property is strictly equal to  value.\nIf the deep flag is set, you can use dot- and bracket-notation for deep\nreferences into objects and arrays.You can also use an array as the starting point of a deep.property\nassertion, or traverse nested arrays.Furthermore, property changes the subject of the assertion\nto be the value of that property from the original object. This\npermits for further chainable assertions on that property.\n\n// simple referencing\nvar obj = { foo: 'bar' };\nexpect(obj).to.have.property('foo');\nexpect(obj).to.have.property('foo', 'bar');\n\n// deep referencing\nvar deepObj = {\n    green: { tea: 'matcha' }\n  , teas: [ 'chai', 'matcha', { tea: 'konacha' } ]\n};\n\nexpect(deepObj).to.have.deep.property('green.tea', 'matcha');\nexpect(deepObj).to.have.deep.property('teas[1]', 'matcha');\nexpect(deepObj).to.have.deep.property('teas[2].tea', 'konacha');var arr = [\n    [ 'chai', 'matcha', 'konacha' ]\n  , [ { tea: 'chai' }\n    , { tea: 'matcha' }\n    , { tea: 'konacha' } ]\n];\n\nexpect(arr).to.have.deep.property('[0][1]', 'matcha');\nexpect(arr).to.have.deep.property('[1][2].tea', 'konacha');expect(obj).to.have.property('foo')\n  .that.is.a('string');\nexpect(deepObj).to.have.property('green')\n  .that.is.an('object')\n  .that.deep.equals({ tea: 'matcha' });\nexpect(deepObj).to.have.property('teas')\n  .that.is.an('array')\n  .with.deep.property('[2]')\n    .that.deep.equals({ tea: 'konacha' });",
        "!url": "https://chaijs.com/api/bdd/#property",
        "!type": "fn(name: string, value: [mixed], message: string) -> bool"
      },
      "ownProperty": {
        "!doc": ".ownProperty(name)\n\n  * @param { String } name\n  * @param { String } message_optional_\n\nAsserts that the target has an own property name.\n\nexpect('test').to.have.ownProperty('length');",
        "!url": "https://chaijs.com/api/bdd/#ownProperty",
        "!type": "fn(name: string, message: string) -> bool"
      },
      "length": {
        "!doc": ".length(value)\n\n  * @param { Number } length\n  * @param { String } message_optional_\n\nAsserts that the target's length property has\nthe expected value.Can also be used as a chain precursor to a value\ncomparison for the length property.\n\nexpect([ 1, 2, 3]).to.have.length(3);\nexpect('foobar').to.have.length(6);expect('foo').to.have.length.above(2);\nexpect([ 1, 2, 3 ]).to.have.length.above(2);\nexpect('foo').to.have.length.below(4);\nexpect([ 1, 2, 3 ]).to.have.length.below(4);\nexpect('foo').to.have.length.within(2,4);\nexpect([ 1, 2, 3 ]).to.have.length.within(2,4);",
        "!url": "https://chaijs.com/api/bdd/#length",
        "!type": "fn(length: number, message: string) -> bool"
      },
      "match": {
        "!doc": ".match(regexp)\n\n  * @param { RegExp } RegularExpression\n  * @param { String } message_optional_\n\nAsserts that the target matches a regular expression.\n\nexpect('foobar').to.match(/^foo/);",
        "!url": "https://chaijs.com/api/bdd/#match",
        "!type": "fn(RegularExpression: regexp, message: string) -> bool"
      },
      "string": {
        "!doc": ".string(string)\n\n  * @param { String } string\n  * @param { String } message_optional_\n\nAsserts that the string target contains another string.\n\nexpect('foobar').to.have.string('bar');",
        "!url": "https://chaijs.com/api/bdd/#string",
        "!type": "fn(string: string, message: string) -> bool"
      },
      "keys": {
        "!doc": ".keys(key1, [key2], [...])\n\n  * @param { String... | Array } keys\n\nAsserts that the target has exactly the given keys, or\nasserts the inclusion of some keys when using the\ninclude or contain modifiers.\n\nexpect({ foo: 1, bar: 2 }).to.have.keys(['foo', 'bar']);\nexpect({ foo: 1, bar: 2, baz: 3 }).to.contain.keys('foo', 'bar');",
        "!url": "https://chaijs.com/api/bdd/#keys",
        "!type": "fn(keys: string) -> bool"
      },
      "throw": {
        "!doc": ".throw(constructor)\n\n  * @param { ErrorConstructor } constructor\n  * @param { String | RegExp } expectederror message\n  * @param { String } message_optional_\n  * @seehttps://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types  \n\nAsserts that the function target will throw a specific error, or specific type of error\n(as determined using instanceof), optionally with a RegExp or string inclusion test\nfor the error's message.Please note that when a throw expectation is negated, it will check each\nparameter independently, starting with error constructor type. The appropriate way\nto check for the existence of a type of error but for a message that does not match\nis to use and.\n\nvar err = new ReferenceError('This is a bad function.');\nvar fn = function () { throw err; }\nexpect(fn).to.throw(ReferenceError);\nexpect(fn).to.throw(Error);\nexpect(fn).to.throw(/bad function/);\nexpect(fn).to.not.throw('good function');\nexpect(fn).to.throw(ReferenceError, /bad function/);\nexpect(fn).to.throw(err);\nexpect(fn).to.not.throw(new RangeError('Out of range.'));expect(fn).to.throw(ReferenceError)\n   .and.not.throw(/good function/);",
        "!url": "https://chaijs.com/api/bdd/#throw",
        "!type": "fn(constructor: errorconstructor, expected: string, message: string) -> bool"
      },
      "respondTo": {
        "!doc": ".respondTo(method)\n\n  * @param { String } method\n  * @param { String } message_optional_\n\nAsserts that the object or class target will respond to a method.To check if a constructor will respond to a static function,\nset the itself flag.\n\nKlass.prototype.bar = function(){};\nexpect(Klass).to.respondTo('bar');\nexpect(obj).to.respondTo('bar');Klass.baz = function(){};\nexpect(Klass).itself.to.respondTo('baz');",
        "!url": "https://chaijs.com/api/bdd/#respondTo",
        "!type": "fn(method: string, message: string) -> bool"
      },
      "itself": {
        "!doc": ".itself\n\n\n\nSets the itself flag, later used by the respondTo assertion.\n\nfunction Foo() {}\nFoo.bar = function() {}\nFoo.prototype.baz = function() {}\n\nexpect(Foo).itself.to.respondTo('bar');\nexpect(Foo).itself.not.to.respondTo('baz');",
        "!url": "https://chaijs.com/api/bdd/#itself",
        "!type": "fn() -> bool"
      },
      "satisfy": {
        "!doc": ".satisfy(method)\n\n  * @param { Function } matcher\n  * @param { String } message_optional_\n\nAsserts that the target passes a given truth test.\n\nexpect(1).to.satisfy(function(num) { return num > 0; });",
        "!url": "https://chaijs.com/api/bdd/#satisfy",
        "!type": "fn(matcher: function, message: string) -> bool"
      },
      "closeTo": {
        "!doc": ".closeTo(expected, delta)\n\n  * @param { Number } expected\n  * @param { Number } delta\n  * @param { String } message_optional_\n\nAsserts that the target is equal expected, to within a +/- delta range.\n\nexpect(1.5).to.be.closeTo(1, 0.5);",
        "!url": "https://chaijs.com/api/bdd/#closeTo",
        "!type": "fn(expected: number, delta: number, message: string) -> bool"
      },
      "members": {
        "!doc": ".members(set)\n\n  * @param { Array } set\n  * @param { String } message_optional_\n\nAsserts that the target is a superset of set,\nor that the target and set have the same members.\n\nexpect([1, 2, 3]).to.include.members([3, 2]);\nexpect([1, 2, 3]).to.not.include.members([3, 2, 8]);\n\nexpect([4, 2]).to.have.members([2, 4]);\nexpect([5, 2]).to.not.have.members([5, 2, 1]);",
        "!url": "https://chaijs.com/api/bdd/#members",
        "!type": "fn(set: array, message: string) -> bool"
      }
    }
  }
}
;});

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(exports);
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/lib/comment',["exports"], mod);
  mod(tern.comment || (tern.comment = {}));
})(function(exports) {
  function isSpace(ch) {
    return (ch < 14 && ch > 8) || ch === 32 || ch === 160;
  }

  function onOwnLine(text, pos) {
    for (; pos > 0; --pos) {
      var ch = text.charCodeAt(pos - 1);
      if (ch == 10) break;
      if (!isSpace(ch)) return false;
    }
    return true;
  }

  // Gather comments directly before a function
  exports.commentsBefore = function(text, pos) {
    var found = null, emptyLines = 0, topIsLineComment;
    out: while (pos > 0) {
      var prev = text.charCodeAt(pos - 1);
      if (prev == 10) {
        for (var scan = --pos, sawNonWS = false; scan > 0; --scan) {
          prev = text.charCodeAt(scan - 1);
          if (prev == 47 && text.charCodeAt(scan - 2) == 47) {
            if (!onOwnLine(text, scan - 2)) break out;
            var content = text.slice(scan, pos);
            if (!emptyLines && topIsLineComment) found[0] = content + "\n" + found[0];
            else (found || (found = [])).unshift(content);
            topIsLineComment = true;
            emptyLines = 0;
            pos = scan - 2;
            break;
          } else if (prev == 10) {
            if (!sawNonWS && ++emptyLines > 1) break out;
            break;
          } else if (!sawNonWS && !isSpace(prev)) {
            sawNonWS = true;
          }
        }
      } else if (prev == 47 && text.charCodeAt(pos - 2) == 42) {
        for (var scan = pos - 2; scan > 1; --scan) {
          if (text.charCodeAt(scan - 1) == 42 && text.charCodeAt(scan - 2) == 47) {
            if (!onOwnLine(text, scan - 2)) break out;
            (found || (found = [])).unshift(text.slice(scan, pos - 2));
            topIsLineComment = false;
            emptyLines = 0;
            break;
          }
        }
        pos = scan - 2;
      } else if (isSpace(prev)) {
        --pos;
      } else {
        break;
      }
    }
    return found;
  };

  exports.commentAfter = function(text, pos) {
    while (pos < text.length) {
      var next = text.charCodeAt(pos);
      if (next == 47) {
        var after = text.charCodeAt(pos + 1), end;
        if (after == 47) // line comment
          end = text.indexOf("\n", pos + 2);
        else if (after == 42) // block comment
          end = text.indexOf("*/", pos + 2);
        else
          return;
        return text.slice(pos + 2, end < 0 ? text.length : end);
      } else if (isSpace(next)) {
        ++pos;
      }
    }
  };

  exports.ensureCommentsBefore = function(text, node) {
    if (node.hasOwnProperty("commentsBefore")) return node.commentsBefore;
    return node.commentsBefore = exports.commentsBefore(text, node.start);
  };
});

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(require("../lib/infer"), require("../lib/tern"), require("../lib/comment"),
               require("acorn/dist/walk"));
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/plugin/angular',["../lib/infer", "../lib/tern", "../lib/comment", "acorn/dist/walk"], mod);
  mod(tern, tern, tern.comment, acorn.walk);
})(function(infer, tern, comment, walk) {
  "use strict";

  var SetDoc = infer.constraint({
    construct: function(doc) { this.doc = doc; },
    addType: function(type) {
      if (!type.doc) type.doc = this.doc;
    }
  });

  function Injector() {
    this.fields = Object.create(null);
    this.forward = [];
  }

  Injector.prototype.get = function(name) {
    if (name == "$scope") return new infer.Obj(globalInclude("$rootScope").getType(), "$scope");
    if (name in this.fields) return this.fields[name];
    var field = this.fields[name] = new infer.AVal;
    return field;
  };
  Injector.prototype.set = function(name, val, doc, node, depth) {
    if (name == "$scope" || depth && depth > 10) return;
    var field = this.fields[name] || (this.fields[name] = new infer.AVal);
    if (!depth) field.local = true;
    if (!field.origin) field.origin = infer.cx().curOrigin;
    if (typeof node == "string" && !field.span) field.span = node;
    else if (node && typeof node == "object" && !field.originNode) field.originNode = node;
    if (doc) { field.doc = doc; field.propagate(new SetDoc(doc)); }
    val.propagate(field);
    for (var i = 0; i < this.forward.length; ++i)
      this.forward[i].set(name, val, doc, node, (depth || 0) + 1);
  };
  Injector.prototype.forwardTo = function(injector) {
    this.forward.push(injector);
    for (var field in this.fields) {
      var val = this.fields[field];
      injector.set(field, val, val.doc, val.span || val.originNode, 1);
    }
  };

  function globalInclude(name) {
    var service = infer.cx().definitions.angular.service;
    if (service.hasProp(name)) return service.getProp(name);
  }

  function getInclude(mod, name) {
    var glob = globalInclude(name);
    if (glob) return glob;
    if (!mod.injector) return infer.ANull;
    return mod.injector ? mod.injector.get(name) : infer.ANull;
  }

  function applyWithInjection(mod, fnType, node, asNew) {
    var deps = [];
    if (/FunctionExpression/.test(node.type)) {
      for (var i = 0; i < node.params.length; ++i)
        deps.push(getInclude(mod, node.params[i].name));
    } else if (node.type == "ArrayExpression") {
      for (var i = 0; i < node.elements.length - 1; ++i) {
        var elt = node.elements[i];
        if (elt.type == "Literal" && typeof elt.value == "string")
          deps.push(getInclude(mod, elt.value));
        else
          deps.push(infer.ANull);
      }
      var last = node.elements[node.elements.length - 1];
      if (last && /FunctionExpression/.test(last.type))
        fnType = last.scope.fnType;
    }
    var result = new infer.AVal;
    if (asNew) {
      var self = new infer.AVal;
      fnType.propagate(new infer.IsCtor(self));
      self.propagate(result, 90);
      fnType.propagate(new infer.IsCallee(self, deps, null, new infer.IfObj(result)));
    } else {
      fnType.propagate(new infer.IsCallee(infer.cx().topScope, deps, null, result));
    }
    return result;
  }

  infer.registerFunction("angular_callInject", function(argN) {
    return function(self, args, argNodes) {
      var mod = self.getType();
      if (mod && argNodes && argNodes[argN])
        applyWithInjection(mod, args[argN], argNodes[argN]);
    };
  });

  infer.registerFunction("angular_regFieldCall", function(self, args, argNodes) {
    var mod = self.getType();
    if (mod && argNodes && argNodes.length > 1) {
      var result = applyWithInjection(mod, args[1], argNodes[1]);
      if (mod.injector && argNodes[0].type == "Literal")
        mod.injector.set(argNodes[0].value, result, argNodes[0].angularDoc, argNodes[0]);
    }
  });

  infer.registerFunction("angular_regFieldNew", function(self, args, argNodes) {
    var mod = self.getType();
    if (mod && argNodes && argNodes.length > 1) {
      var result = applyWithInjection(mod, args[1], argNodes[1], true);
      if (mod.injector && argNodes[0].type == "Literal")
        mod.injector.set(argNodes[0].value, result, argNodes[0].angularDoc, argNodes[0]);
    }
  });

  infer.registerFunction("angular_regField", function(self, args, argNodes) {
    var mod = self.getType();
    if (mod && mod.injector && argNodes && argNodes[0] && argNodes[0].type == "Literal" && args[1])
      mod.injector.set(argNodes[0].value, args[1], argNodes[0].angularDoc, argNodes[0]);
  });

  function arrayNodeToStrings(node) {
    var strings = [];
    if (node && node.type == "ArrayExpression")
      for (var i = 0; i < node.elements.length; ++i) {
        var elt = node.elements[i];
        if (elt.type == "Literal" && typeof elt.value == "string")
          strings.push(elt.value);
      }
    return strings;
  }

  function moduleProto(cx) {
    var ngDefs = cx.definitions.angular;
    return ngDefs && ngDefs.Module.getProp("prototype").getType();
  }

  function declareMod(name, includes) {
    var cx = infer.cx(), data = cx.parent.mod.angular;
    var proto = moduleProto(cx);
    var mod = new infer.Obj(proto || true);
    if (!proto) data.nakedModules.push(mod);
    mod.origin = cx.curOrigin;
    mod.injector = new Injector();
    mod.metaData = {includes: includes};
    for (var i = 0; i < includes.length; ++i) {
      var depMod = data.modules[includes[i]];
      if (!depMod)
        (data.pendingImports[includes[i]] || (data.pendingImports[includes[i]] = [])).push(mod.injector);
      else if (depMod.injector)
        depMod.injector.forwardTo(mod.injector);
    }
    if (typeof name == "string") {
      data.modules[name] = mod;
      var pending = data.pendingImports[name];
      if (pending) {
        delete data.pendingImports[name];
        for (var i = 0; i < pending.length; ++i)
          mod.injector.forwardTo(pending[i]);
      }
    }
    return mod;
  }

  infer.registerFunction("angular_module", function(_self, _args, argNodes) {
    var mod, name = argNodes && argNodes[0] && argNodes[0].type == "Literal" && argNodes[0].value;
    if (typeof name == "string")
      mod = infer.cx().parent.mod.angular.modules[name];
    if (!mod)
      mod = declareMod(name, arrayNodeToStrings(argNodes && argNodes[1]));
    return mod;
  });

  var IsBound = infer.constraint({
    construct: function(self, args, target) {
      this.self = self; this.args = args; this.target = target;
    },
    addType: function(tp) {
      if (!(tp instanceof infer.Fn)) return;
      this.target.addType(new infer.Fn(tp.name, tp.self, tp.args.slice(this.args.length),
                                       tp.argNames.slice(this.args.length), tp.retval));
      this.self.propagate(tp.self);
      for (var i = 0; i < Math.min(tp.args.length, this.args.length); ++i)
        this.args[i].propagate(tp.args[i]);
    }
  });

  infer.registerFunction("angular_bind", function(_self, args) {
    if (args.length < 2) return infer.ANull;
    var result = new infer.AVal;
    args[1].propagate(new IsBound(args[0], args.slice(2), result));
    return result;
  });

  function postParse(ast, text) {
    walk.simple(ast, {
      CallExpression: function(node) {
        if (node.callee.type == "MemberExpression" &&
            !node.callee.computed && node.arguments.length &&
            /^(value|constant|controller|factory|provider)$/.test(node.callee.property.name)) {
          var before = comment.commentsBefore(text, node.callee.property.start - 1);
          if (before) {
            var first = before[0], dot = first.search(/\.\s/);
            if (dot > 5) first = first.slice(0, dot + 1);
            first = first.trim().replace(/\s*\n\s*\*\s*|\s{1,}/g, " ");
            node.arguments[0].angularDoc = first;
          }
        }
      }
    });
  }

  function postLoadDef(json) {
    var cx = infer.cx(), defName = json["!name"], defs = cx.definitions[defName];
    if (defName == "angular") {
      var proto = moduleProto(cx), naked = cx.parent.mod.angular.nakedModules;
      if (proto) for (var i = 0; i < naked.length; ++i) naked[i].proto = proto;
      return;
    }
    var mods = defs && defs["!ng"];
    if (mods) for (var name in mods.props) {
      var obj = mods.props[name].getType();
      var mod = declareMod(name.replace(/`/g, "."), obj.metaData && obj.metaData.includes || []);
      mod.origin = defName;
      for (var prop in obj.props) {
        var val = obj.props[prop], tp = val.getType();
        if (!tp) continue;
        if (/^_inject_/.test(prop)) {
          if (!tp.name) tp.name = prop.slice(8);
          mod.injector.set(prop.slice(8), tp, val.doc, val.span);
        } else {
          obj.props[prop].propagate(mod.defProp(prop));
        }
      }
    }
  }

  function preCondenseReach(state) {
    var mods = infer.cx().parent.mod.angular.modules;
    var modObj = new infer.Obj(null), found = 0;
    for (var name in mods) {
      var mod = mods[name];
      if (state.origins.indexOf(mod.origin) > -1) {
        var propName = name.replace(/\./g, "`");
        modObj.defProp(propName).addType(mod);
        mod.condenseForceInclude = true;
        ++found;
        if (mod.injector) for (var inj in mod.injector.fields) {
          var field = mod.injector.fields[inj];
          if (field.local) state.roots["!ng." + propName + "._inject_" + inj] = field;
        }
      }
    }
    if (found) state.roots["!ng"] = modObj;
  }

  function postCondenseReach(state) {
    var mods = infer.cx().parent.mod.angular.modules;
    for (var path in state.types) {
      var m;
      if (m = path.match(/^!ng\.([^\.]+)\._inject_([^\.]+)^/)) {
        var mod = mods[m[1].replace(/`/g, ".")];
        var field = mod.injector.fields[m[2]];
        var data = state.types[path];
        if (field.span) data.span = field.span;
        if (field.doc) data.doc = field.doc;
      }
    }
  }

  function initServer(server) {
    server.mod.angular = {
      modules: Object.create(null),
      pendingImports: Object.create(null),
      nakedModules: []
    };
  }

  tern.registerPlugin("angular", function(server) {
    initServer(server);

    server.on("reset", function() { initServer(server); });
    server.on("postParse", postParse)
    server.on("postLoadDef", postLoadDef)
    server.on("preCondenseReach", preCondenseReach)
    server.on("postCondenseReach", postCondenseReach)

    server.addDefs(defs, true)
  });

  var defs = {
    "!name": "angular",
    "!define": {
      cacheObj: {
        info: "fn() -> ?",
        put: "fn(key: string, value: ?) -> !1",
        get: "fn(key: string) -> ?",
        remove: "fn(key: string)",
        removeAll: "fn()",
        destroy: "fn()"
      },
      eventObj: {
        targetScope: "service.$rootScope",
        currentScope: "service.$rootScope",
        name: "string",
        stopPropagation: "fn()",
        preventDefault: "fn()",
        defaultPrevented: "bool"
      },
      directiveObj: {
        multiElement: {
          "!type": "bool",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-multielement-",
          "!doc": "When this property is set to true, the HTML compiler will collect DOM nodes between nodes with the attributes directive-name-start and directive-name-end, and group them together as the directive elements. It is recommended that this feature be used on directives which are not strictly behavioural (such as ngClick), and which do not manipulate or replace child nodes (such as ngInclude)."
        },
        priority: {
          "!type": "number",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-priority-",
          "!doc": "When there are multiple directives defined on a single DOM element, sometimes it is necessary to specify the order in which the directives are applied. The priority is used to sort the directives before their compile functions get called. Priority is defined as a number. Directives with greater numerical priority are compiled first. Pre-link functions are also run in priority order, but post-link functions are run in reverse order. The order of directives with the same priority is undefined. The default priority is 0."
        },
        terminal: {
          "!type": "bool",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-terminal-",
          "!doc": "If set to true then the current priority will be the last set of directives which will execute (any directives at the current priority will still execute as the order of execution on same priority is undefined). Note that expressions and other directives used in the directive's template will also be excluded from execution."
        },
        scope: {
          "!type": "?",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-scope-",
          "!doc": "If set to true, then a new scope will be created for this directive. If multiple directives on the same element request a new scope, only one new scope is created. The new scope rule does not apply for the root of the template since the root of the template always gets a new scope. If set to {} (object hash), then a new 'isolate' scope is created. The 'isolate' scope differs from normal scope in that it does not prototypically inherit from the parent scope. This is useful when creating reusable components, which should not accidentally read or modify data in the parent scope."
        },
        bindToController: {
          "!type": "bool",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-bindtocontroller-",
          "!doc": "When an isolate scope is used for a component (see above), and controllerAs is used, bindToController: true will allow a component to have its properties bound to the controller, rather than to scope. When the controller is instantiated, the initial values of the isolate scope bindings are already available."
        },
        controller: {
          "!type": "fn()",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-require-",
          "!doc": "Controller constructor function. The controller is instantiated before the pre-linking phase and it is shared with other directives (see require attribute). This allows the directives to communicate with each other and augment each other's behavior."
        },
        require: {
          "!type": "string",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-controller-",
          "!doc": "Require another directive and inject its controller as the fourth argument to the linking function. The require takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the injected argument will be an array in corresponding order. If no such directive can be found, or if the directive does not have a controller, then an error is raised."
        },
        controllerAs: {
          "!type": "string",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-controlleras-",
          "!doc": "Controller alias at the directive scope. An alias for the controller so it can be referenced at the directive template. The directive needs to define a scope for this configuration to be used. Useful in the case when directive is used as component."
        },
        restrict: {
          "!type": "string",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-restrict-",
          "!doc": "String of subset of EACM which restricts the directive to a specific directive declaration style. If omitted, the defaults (elements and attributes) are used. E - Element name (default): <my-directive></my-directive>. A - Attribute (default): <div my-directive='exp'></div>. C - Class: <div class='my-directive: exp;'></div>. M - Comment: <!-- directive: my-directive exp --> "
        },
        templateNamespace: {
          "!type": "string",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-templatenamespace-",
          "!doc": "String representing the document type used by the markup in the template. AngularJS needs this information as those elements need to be created and cloned in a special way when they are defined outside their usual containers like <svg> and <math>."
        },
        template: {
          "!type": "string",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-template-",
          "!doc": "HTML markup that may: Replace the contents of the directive's element (default). Replace the directive's element itself (if replace is true - DEPRECATED). Wrap the contents of the directive's element (if transclude is true)."
        },
        templateUrl: {
          "!type": "string",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-templateurl-",
          "!doc": "This is similar to template but the template is loaded from the specified URL, asynchronously."
        },
        transclude: {
          "!type": "bool",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-transclude-",
          "!doc": "Extract the contents of the element where the directive appears and make it available to the directive. The contents are compiled and provided to the directive as a transclusion function."
        },
        compile: {
          "!type": "fn(tElement: +Element, tAttrs: +Attr)",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-transclude-",
          "!doc": "The compile function deals with transforming the template DOM. Since most directives do not do template transformation, it is not used often."
        },
        link: {
          "!type": "fn(scope: ?, iElement: +Element, iAttrs: +Attr, controller: ?, transcludeFn: fn())",
          "!url": "https://docs.angularjs.org/api/ng/service/$compile#-link-",
          "!doc": "The link function is responsible for registering DOM listeners as well as updating the DOM. It is executed after the template has been cloned. This is where most of the directive logic will be put."
        }
      },
      Module: {
        "!url": "https://docs.angularjs.org/api/angular.Module",
        "!doc": "Interface for configuring angular modules.",
        prototype: {
          animation: {
            "!type": "fn(name: string, animationFactory: fn()) -> !this",
            "!url": "https://docs.angularjs.org/api/angular.Module#animation",
            "!doc": "Defines an animation hook that can be later used with $animate service and directives that use this service."
          },
          config: {
            "!type": "fn(configFn: fn()) -> !this",
            "!effects": ["custom angular_callInject 0"],
            "!url": "https://docs.angularjs.org/api/angular.Module#config",
            "!doc": "Use this method to register work which needs to be performed on module loading."
          },
          constant: "service.$provide.constant",
          controller: {
            "!type": "fn(name: string, constructor: fn()) -> !this",
            "!effects": ["custom angular_regFieldCall"],
            "!url": "https://docs.angularjs.org/api/ng.$controllerProvider",
            "!doc": "Register a controller."
          },
          directive: {
            "!type": "fn(name: string, directiveFactory: fn() -> directiveObj) -> !this",
            "!effects": ["custom angular_regFieldCall"],
            "!url": "https://docs.angularjs.org/api/ng.$compileProvider#directive",
            "!doc": "Register a new directive with the compiler."
          },
          factory: "service.$provide.factory",
          filter: {
            "!type": "fn(name: string, filterFactory: fn()) -> !this",
            "!effects": ["custom angular_callInject 1"],
            "!url": "https://docs.angularjs.org/api/ng.$filterProvider",
            "!doc": "Register filter factory function."
          },
          provider: "service.$provide.provider",
          run: {
            "!type": "fn(initializationFn: fn()) -> !this",
            "!effects": ["custom angular_callInject 0"],
            "!url": "https://docs.angularjs.org/api/angular.Module#run",
            "!doc": "Register work which should be performed when the injector is done loading all modules."
          },
          service: "service.$provide.service",
          value: "service.$provide.value",
          name: {
            "!type": "string",
            "!url": "https://docs.angularjs.org/api/angular.Module#name",
            "!doc": "Name of the module."
          },
          requires: {
            "!type": "[string]",
            "!url": "https://docs.angularjs.org/api/angular.Module#requires",
            "!doc": "List of module names which must be loaded before this module."
          }
        }
      },
      Promise: {
        "!url": "https://docs.angularjs.org/api/ng.$q",
        "!doc": "Allow for interested parties to get access to the result of the deferred task when it completes.",
        prototype: {
          then: "fn(successCallback: fn(value: ?), errorCallback: fn(reason: ?), notifyCallback: fn(value: ?)) -> +Promise",
          "catch": "fn(errorCallback: fn(reason: ?))",
          "finally": "fn(callback: fn()) -> +Promise",
          success: "fn(callback: fn(data: ?, status: number, headers: ?, config: ?)) -> +Promise",
          error: "fn(callback: fn(data: ?, status: number, headers: ?, config: ?)) -> +Promise"
        }
      },
      Deferred: {
        "!url": "https://docs.angularjs.org/api/ng.$q",
        prototype: {
          resolve: "fn(value: ?)",
          reject: "fn(reason: ?)",
          notify: "fn(value: ?)",
          promise: "+Promise"
        }
      },
      ResourceClass: {
        "!url": "https://docs.angularjs.org/api/ngResource.$resource",
        prototype: {
          $promise: "+Promise",
          $save: "fn()"
        }
      },
      Resource: {
        "!url": "https://docs.angularjs.org/api/ngResource.$resource",
        prototype: {
          get: "fn(params: ?, callback: fn()) -> +ResourceClass",
          save: "fn(params: ?, callback: fn()) -> +ResourceClass",
          query: "fn(params: ?, callback: fn()) -> +ResourceClass",
          remove: "fn(params: ?, callback: fn()) -> +ResourceClass",
          "delete": "fn(params: ?, callback: fn()) -> +ResourceClass"
        }
      },
      service: {
        $anchorScroll: {
          "!type": "fn()",
          "!url": "https://docs.angularjs.org/api/ng.$anchorScroll",
          "!doc": "Checks current value of $location.hash() and scroll to related element."
        },
        $animate: {
          "!url": "https://docs.angularjs.org/api/ng.$animate",
          "!doc": "Rudimentary DOM manipulation functions to insert, remove, move elements within the DOM.",
          addClass: {
            "!type": "fn(element: +Element, className: string, done?: fn()) -> !this",
            "!url": "https://docs.angularjs.org/api/ng.$animate#addClass",
            "!doc": "Adds the provided className CSS class value to the provided element."
          },
          enter: {
            "!type": "fn(element: +Element, parent: +Element, after: +Element, done?: fn()) -> !this",
            "!url": "https://docs.angularjs.org/api/ng.$animate#enter",
            "!doc": "Inserts the element into the DOM either after the after element or within the parent element."
          },
          leave: {
            "!type": "fn(element: +Element, done?: fn()) -> !this",
            "!url": "https://docs.angularjs.org/api/ng.$animate#leave",
            "!doc": "Removes the element from the DOM."
          },
          move: {
            "!type": "fn(element: +Element, parent: +Element, after: +Element, done?: fn()) -> !this",
            "!url": "https://docs.angularjs.org/api/ng.$animate#move",
            "!doc": "Moves element to be placed either after the after element or inside of the parent element."
          },
          removeClass: {
            "!type": "fn(element: +Element, className: string, done?: fn()) -> !this",
            "!url": "https://docs.angularjs.org/api/ng.$animate#removeClass",
            "!doc": "Removes the provided className CSS class value from the provided element."
          }
        },
        $cacheFactory: {
          "!type": "fn(cacheId: string, options?: ?) -> cacheObj",
          "!url": "https://docs.angularjs.org/api/ng.$cacheFactory",
          "!doc": "Factory that constructs cache objects and gives access to them."
        },
        $compile: {
          "!type": "fn(element: +Element, transclude: fn(scope: ?), maxPriority: number)",
          "!url": "https://docs.angularjs.org/api/ng.$compile",
          "!doc": "Compiles a piece of HTML string or DOM into a template and produces a template function."
        },
        $controller: {
          "!type": "fn(controller: fn(), locals: ?) -> ?",
          "!url": "https://docs.angularjs.org/api/ng.$controller",
          "!doc": "Instantiates controllers."
        },
        $document: {
          "!type": "jQuery.fn",
          "!url": "https://docs.angularjs.org/api/ng.$document",
          "!doc": "A jQuery (lite)-wrapped reference to the browser's window.document element."
        },
        $exceptionHandler: {
          "!type": "fn(exception: +Error, cause?: string)",
          "!url": "https://docs.angularjs.org/api/ng.$exceptionHandler",
          "!doc": "Any uncaught exception in angular expressions is delegated to this service."
        },
        $filter: {
          "!type": "fn(name: string) -> fn(input: string) -> string",
          "!url": "https://docs.angularjs.org/api/ng.$filter",
          "!doc": "Retrieve a filter function."
        },
        $http: {
          "!type": "fn(config: ?) -> service.$q",
          "!url": "https://docs.angularjs.org/api/ng.$http",
          "!doc": "Facilitates communication with remote HTTP servers.",
          "delete": "fn(url: string, config?: ?) -> +Promise",
          get: "fn(url: string, config?: ?) -> +Promise",
          head: "fn(url: string, config?: ?) -> +Promise",
          jsonp: "fn(url: string, config?: ?) -> +Promise",
          post: "fn(url: string, data: ?, config?: ?) -> +Promise",
          put: "fn(url: string, data: ?, config?: ?) -> +Promise"
        },
        $interpolate: {
          "!type": "fn(text: string, mustHaveExpression?: bool, trustedContext?: string) -> fn(context: ?) -> string",
          "!url": "https://docs.angularjs.org/api/ng.$interpolate",
          "!doc": "Compiles a string with markup into an interpolation function."
        },
        $locale: {
          "!url": "https://docs.angularjs.org/api/ng.$locale",
          id: "string"
        },
        $location: {
          "!url": "https://docs.angularjs.org/api/ng.$location",
          "!doc": "Parses the URL in the browser address bar.",
          absUrl: {
            "!type": "fn() -> string",
            "!url": "https://docs.angularjs.org/api/ng.$location#absUrl",
            "!doc": "Return full url representation."
          },
          hash: {
            "!type": "fn(value?: string) -> string",
            "!url": "https://docs.angularjs.org/api/ng.$location#hash",
            "!doc": "Get or set the hash fragment."
          },
          host: {
            "!type": "fn() -> string",
            "!url": "https://docs.angularjs.org/api/ng.$location#host",
            "!doc": "Return host of current url."
          },
          path: {
            "!type": "fn(value?: string) -> string",
            "!url": "https://docs.angularjs.org/api/ng.$location#path",
            "!doc": "Get or set the URL path."
          },
          port: {
            "!type": "fn() -> number",
            "!url": "https://docs.angularjs.org/api/ng.$location#port",
            "!doc": "Returns the port of the current url."
          },
          protocol: {
            "!type": "fn() -> string",
            "!url": "https://docs.angularjs.org/api/ng.$location#protocol",
            "!doc": "Return protocol of current url."
          },
          replace: {
            "!type": "fn()",
            "!url": "https://docs.angularjs.org/api/ng.$location#replace",
            "!doc": "Changes to $location during current $digest will be replacing current history record, instead of adding new one."
          },
          search: {
            "!type": "fn(search: string, paramValue?: string) -> string",
            "!url": "https://docs.angularjs.org/api/ng.$location#search",
            "!doc": "Get or set the URL query."
          },
          url: {
            "!type": "fn(url: string, replace?: string) -> string",
            "!url": "https://docs.angularjs.org/api/ng.$location#url",
            "!doc": "Get or set the current url."
          }
        },
        $log: {
          "!url": "https://docs.angularjs.org/api/ng.$log",
          "!doc": "Simple service for logging.",
          debug: {
            "!type": "fn(message: string)",
            "!url": "https://docs.angularjs.org/api/ng.$log#debug",
            "!doc": "Write a debug message."
          },
          error: {
            "!type": "fn(message: string)",
            "!url": "https://docs.angularjs.org/api/ng.$log#error",
            "!doc": "Write an error message."
          },
          info: {
            "!type": "fn(message: string)",
            "!url": "https://docs.angularjs.org/api/ng.$log#info",
            "!doc": "Write an info message."
          },
          log: {
            "!type": "fn(message: string)",
            "!url": "https://docs.angularjs.org/api/ng.$log#log",
            "!doc": "Write a log message."
          },
          warn: {
            "!type": "fn(message: string)",
            "!url": "https://docs.angularjs.org/api/ng.$log#warn",
            "!doc": "Write a warning message."
          }
        },
        $parse: {
          "!type": "fn(expression: string) -> fn(context: ?, locals: ?) -> ?",
          "!url": "https://docs.angularjs.org/api/ng.$parse",
          "!doc": "Converts Angular expression into a function."
        },
        $q: {
          "!type": "fn(executor: fn(resolve: fn(value: ?) -> +Promise, reject: fn(value: ?) -> +Promise)) -> +Promise",
          "!url": "https://docs.angularjs.org/api/ng.$q",
          "!doc": "A promise/deferred implementation.",
          all: {
            "!type": "fn(promises: [+Promise]) -> +Promise",
            "!url": "https://docs.angularjs.org/api/ng.$q#all",
            "!doc": "Combines multiple promises into a single promise."
          },
          defer: {
            "!type": "fn() -> +Deferred",
            "!url": "https://docs.angularjs.org/api/ng.$q#defer",
            "!doc": "Creates a Deferred object which represents a task which will finish in the future."
          },
          reject: {
            "!type": "fn(reason: ?) -> +Promise",
            "!url": "https://docs.angularjs.org/api/ng.$q#reject",
            "!doc": "Creates a promise that is resolved as rejected with the specified reason."
          },
          when: {
            "!type": "fn(value: ?) -> +Promise",
            "!url": "https://docs.angularjs.org/api/ng.$q#when",
            "!doc": "Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise."
          }
        },
        $rootElement: {
          "!type": "+Element",
          "!url": "https://docs.angularjs.org/api/ng.$rootElement",
          "!doc": "The root element of Angular application."
        },
        $rootScope: {
          "!url": "https://docs.angularjs.org/api/ng.$rootScope",
          $apply: {
            "!type": "fn(expression: string)",
            "!url": "https://docs.angularjs.org/api/ng.$rootScope.Scope#$apply",
            "!doc": "Execute an expression in angular from outside of the angular framework."
          },
          $broadcast: {
            "!type": "fn(name: string, args?: ?) -> eventObj",
            "!url": "https://docs.angularjs.org/api/ng.$rootScope.Scope#$broadcast",
            "!doc": "Dispatches an event name downwards to all child scopes."
          },
          $destroy: {
            "!type": "fn()",
            "!url": "https://docs.angularjs.org/api/ng.$rootScope.Scope#$destroy",
            "!doc": "Removes the current scope (and all of its children) from the parent scope."
          },
          $digest: {
            "!type": "fn()",
            "!url": "https://docs.angularjs.org/api/ng.$rootScope.Scope#$digest",
            "!doc": "Processes all of the watchers of the current scope and its children."
          },
          $emit: {
            "!type": "fn(name: string, args?: ?) -> eventObj",
            "!url": "https://docs.angularjs.org/api/ng.$rootScope.Scope#$emit",
            "!doc": "Dispatches an event name upwards through the scope hierarchy."
          },
          $eval: {
            "!type": "fn(expression: string) -> ?",
            "!url": "https://docs.angularjs.org/api/ng.$rootScope.Scope#$eval",
            "!doc": "Executes the expression on the current scope and returns the result."
          },
          $evalAsync: {
            "!type": "fn(expression: string)",
            "!url": "https://docs.angularjs.org/api/ng.$rootScope.Scope#$evalAsync",
            "!doc": "Executes the expression on the current scope at a later point in time."
          },
          $new: {
            "!type": "fn(isolate: bool) -> service.$rootScope",
            "!url": "https://docs.angularjs.org/api/ng.$rootScope.Scope#$new",
            "!doc": "Creates a new child scope."
          },
          $on: {
            "!type": "fn(name: string, listener: fn(event: ?)) -> fn()",
            "!url": "https://docs.angularjs.org/api/ng.$rootScope.Scope#$on",
            "!doc": "Listens on events of a given type."
          },
          $watch: {
            "!type": "fn(watchExpression: string, listener?: fn(), objectEquality?: bool) -> fn()",
            "!url": "https://docs.angularjs.org/api/ng.$rootScope.Scope#$watch",
            "!doc": "Registers a listener callback to be executed whenever the watchExpression changes."
          },
          $watchCollection: {
            "!type": "fn(obj: string, listener: fn()) -> fn()",
            "!url": "https://docs.angularjs.org/api/ng.$rootScope.Scope#$watchCollection",
            "!doc": "Shallow watches the properties of an object and fires whenever any of the properties."
          },
          $id: {
            "!type": "number",
            "!url": "https://docs.angularjs.org/api/ng.$rootScope.Scope#$id",
            "!doc": "Unique scope ID."
          }
        },
        $sce: {
          HTML: "string",
          CSS: "string",
          URL: "string",
          RESOURCE_URL: "string",
          JS: "string",
          getTrusted: "fn(type: string, maybeTrusted: ?) -> !1",
          getTrustedCss: "fn(maybeTrusted: ?) -> !0",
          getTrustedHtml: "fn(maybeTrusted: ?) -> !0",
          getTrustedJs: "fn(maybeTrusted: ?) -> !0",
          getTrustedResourceUrl: "fn(maybeTrusted: ?) -> !0",
          getTrustedUrl: "fn(maybeTrusted: ?) -> !0",
          parse: "fn(type: string, expression: string) -> fn(context: ?, locals: ?) -> ?",
          parseAsCss: "fn(expression: string) -> fn(context: ?, locals: ?) -> ?",
          parseAsHtml: "fn(expression: string) -> fn(context: ?, locals: ?) -> ?",
          parseAsJs: "fn(expression: string) -> fn(context: ?, locals: ?) -> ?",
          parseAsResourceUrl: "fn(expression: string) -> fn(context: ?, locals: ?) -> ?",
          parseAsUrl: "fn(expression: string) -> fn(context: ?, locals: ?) -> ?",
          trustAs: "fn(type: string, value: ?) -> !1",
          trustAsHtml: "fn(value: ?) -> !0",
          trustAsJs: "fn(value: ?) -> !0",
          trustAsResourceUrl: "fn(value: ?) -> !0",
          trustAsUrl: "fn(value: ?) -> !0",
          isEnabled: "fn() -> bool"
        },
        $templateCache: {
          "!url": "https://docs.angularjs.org/api/ng.$templateCache",
          "!proto": "cacheObj"
        },
        $timeout: {
          "!type": "fn(fn: fn(), delay?: number, invokeApply?: bool) -> +Promise",
          "!url": "https://docs.angularjs.org/api/ng.$timeout",
          "!doc": "Angular's wrapper for window.setTimeout.",
          cancel: "fn(promise: +Promise)"
        },
        $window: "<top>",
        $injector: {
          "!url": "https://docs.angularjs.org/api/AUTO.$injector",
          "!doc": "Retrieve object instances as defined by provider.",
          annotate: {
            "!type": "fn(f: fn()) -> [string]",
            "!url": "https://docs.angularjs.org/api/AUTO.$injector#annotate",
            "!doc": "Returns an array of service names which the function is requesting for injection."
          },
          get: {
            "!type": "fn(name: string) -> ?",
            "!url": "https://docs.angularjs.org/api/AUTO.$injector#get",
            "!doc": "Return an instance of a service."
          },
          has: {
            "!type": "fn(name: string) -> bool",
            "!url": "https://docs.angularjs.org/api/AUTO.$injector#has",
            "!doc": "Allows the user to query if the particular service exist."
          },
          instantiate: {
            "!type": "fn(type: fn(), locals?: ?) -> +!0",
            "!url": "https://docs.angularjs.org/api/AUTO.$injector#instantiate",
            "!doc": "Create a new instance of JS type."
          },
          invoke: {
            "!type": "fn(type: fn(), self?: ?, locals?: ?) -> !0.!ret",
            "!url": "https://docs.angularjs.org/api/AUTO.$injector#invoke",
            "!doc": "Invoke the method and supply the method arguments from the $injector."
          }
        },
        $provide: {
          "!url": "https://docs.angularjs.org/api/AUTO.$provide",
          "!doc": "Use $provide to register new providers with the $injector.",
          constant: {
            "!type": "fn(name: string, value: ?) -> !this",
            "!effects": ["custom angular_regField"],
            "!url": "https://docs.angularjs.org/api/AUTO.$provide#constant",
            "!doc": "A constant value."
          },
          decorator: {
            "!type": "fn(name: string, decorator: fn())",
            "!effects": ["custom angular_regFieldCall"],
            "!url": "https://docs.angularjs.org/api/AUTO.$provide#decorator",
            "!doc": "Decoration of service, allows the decorator to intercept the service instance creation."
          },
          factory: {
            "!type": "fn(name: string, providerFunction: fn()) -> !this",
            "!effects": ["custom angular_regFieldCall"],
            "!url": "https://docs.angularjs.org/api/AUTO.$provide#factory",
            "!doc": "A short hand for configuring services if only $get method is required."
          },
          provider: {
            "!type": "fn(name: string, providerType: fn()) -> !this",
            "!effects": ["custom angular_regFieldCall"],
            "!url": "https://docs.angularjs.org/api/AUTO.$provide#provider",
            "!doc": "Register a provider for a service."
          },
          service: {
            "!type": "fn(name: string, constructor: fn()) -> !this",
            "!effects": ["custom angular_regFieldNew"],
            "!url": "https://docs.angularjs.org/api/AUTO.$provide#provider",
            "!doc": "Register a provider for a service."
          },
          value: {
            "!type": "fn(name: string, object: ?) -> !this",
            "!effects": ["custom angular_regField"],
            "!url": "https://docs.angularjs.org/api/AUTO.$providevalue",
            "!doc": "A short hand for configuring services if the $get method is a constant."
          }
        },
        $cookies: {
          "!url": "https://docs.angularjs.org/api/ngCookies.$cookies",
          "!doc": "Provides read/write access to browser's cookies.",
          text: "string"
        },
        $resource: {
          "!type": "fn(url: string, paramDefaults?: ?, actions?: ?) -> +Resource",
          "!url": "https://docs.angularjs.org/api/ngResource.$resource",
          "!doc": "Creates a resource object that lets you interact with RESTful server-side data sources."
        },
        $route: {
          "!url": "https://docs.angularjs.org/api/ngRoute.$route",
          "!doc": "Deep-link URLs to controllers and views.",
          reload: {
            "!type": "fn()",
            "!url": "https://docs.angularjs.org/api/ngRoute.$route#reload",
            "!doc": "Reload the current route even if $location hasn't changed."
          },
          current: {
            "!url": "https://docs.angularjs.org/api/ngRoute.$route#current",
            "!doc": "Reference to the current route definition.",
            controller: "?",
            locals: "?"
          },
          routes: "[?]"
        },
        $sanitize: {
          "!type": "fn(string) -> string",
          "!url": "https://docs.angularjs.org/api/ngSanitize.$sanitize",
          "!doc": "Sanitize HTML input."
        },
        $swipe: {
          "!url": "https://docs.angularjs.org/api/ngTouch.$swipe",
          "!doc": "A service that abstracts the messier details of hold-and-drag swipe behavior.",
          bind: {
            "!type": "fn(element: +Element, handlers: ?)",
            "!url": "https://docs.angularjs.org/api/ngTouch.$swipe#bind",
            "!doc": "Abstracts the messier details of hold-and-drag swipe behavior."
          }
        }
      }
    },
    angular: {
      bind: {
        "!type": "fn(self: ?, fn: fn(), args?: ?) -> !custom:angular_bind",
        "!url": "https://docs.angularjs.org/api/angular.bind",
        "!doc": "Returns a function which calls function fn bound to self."
      },
      bootstrap: {
        "!type": "fn(element: +Element, modules?: [string]) -> service.$injector",
        "!url": "https://docs.angularjs.org/api/angular.bootstrap",
        "!doc": "Use this function to manually start up angular application."
      },
      copy: {
        "!type": "fn(source: ?, target?: ?) -> !0",
        "!url": "https://docs.angularjs.org/api/angular.copy",
        "!doc": "Creates a deep copy of source, which should be an object or an array."
      },
      element: {
        "!type": "fn(element: +Element) -> jQuery.fn",
        "!url": "https://docs.angularjs.org/api/angular.element",
        "!doc": "Wraps a raw DOM element or HTML string as a jQuery element."
      },
      equals: {
        "!type": "fn(o1: ?, o2: ?) -> bool",
        "!url": "https://docs.angularjs.org/api/angular.equals",
        "!doc": "Determines if two objects or two values are equivalent."
      },
      extend: {
        "!type": "fn(dst: ?, src: ?) -> !0",
        "!url": "https://docs.angularjs.org/api/angular.extend",
        "!doc": "Extends the destination object dst by copying all of the properties from the src object(s) to dst."
      },
      forEach: {
        "!type": "fn(obj: ?, iterator: fn(value: ?, key: ?), context?: ?) -> !0",
        "!effects": ["call !1 this=!2 !0.<i> number"],
        "!url": "https://docs.angularjs.org/api/angular.forEach",
        "!doc": "Invokes the iterator function once for each item in obj collection, which can be either an object or an array."
      },
      fromJson: {
        "!type": "fn(json: string) -> ?",
        "!url": "https://docs.angularjs.org/api/angular.fromJson",
        "!doc": "Deserializes a JSON string."
      },
      identity: {
        "!type": "fn(val: ?) -> !0",
        "!url": "https://docs.angularjs.org/api/angular.identity",
        "!doc": "A function that returns its first argument."
      },
      injector: {
        "!type": "fn(modules: [string]) -> service.$injector",
        "!url": "https://docs.angularjs.org/api/angular.injector",
        "!doc": "Creates an injector function"
      },
      isArray: {
        "!type": "fn(val: ?) -> bool",
        "!url": "https://docs.angularjs.org/api/angular.isArray",
        "!doc": "Determines if a reference is an Array."
      },
      isDate: {
        "!type": "fn(val: ?) -> bool",
        "!url": "https://docs.angularjs.org/api/angular.isDate",
        "!doc": "Determines if a reference is a date."
      },
      isDefined: {
        "!type": "fn(val: ?) -> bool",
        "!url": "https://docs.angularjs.org/api/angular.isDefined",
        "!doc": "Determines if a reference is defined."
      },
      isElement: {
        "!type": "fn(val: ?) -> bool",
        "!url": "https://docs.angularjs.org/api/angular.isElement",
        "!doc": "Determines if a reference is a DOM element."
      },
      isFunction: {
        "!type": "fn(val: ?) -> bool",
        "!url": "https://docs.angularjs.org/api/angular.isFunction",
        "!doc": "Determines if a reference is a function."
      },
      isNumber: {
        "!type": "fn(val: ?) -> bool",
        "!url": "https://docs.angularjs.org/api/angular.isNumber",
        "!doc": "Determines if a reference is a number."
      },
      isObject: {
        "!type": "fn(val: ?) -> bool",
        "!url": "https://docs.angularjs.org/api/angular.isObject",
        "!doc": "Determines if a reference is an object."
      },
      isString: {
        "!type": "fn(val: ?) -> bool",
        "!url": "https://docs.angularjs.org/api/angular.isString",
        "!doc": "Determines if a reference is a string."
      },
      isUndefined: {
        "!type": "fn(val: ?) -> bool",
        "!url": "https://docs.angularjs.org/api/angular.isUndefined",
        "!doc": "Determines if a reference is undefined."
      },
      lowercase: {
        "!type": "fn(val: string) -> string",
        "!url": "https://docs.angularjs.org/api/angular.lowercase",
        "!doc": "Converts the specified string to lowercase."
      },
      module: {
        "!type": "fn(name: string, deps: [string]) -> !custom:angular_module",
        "!url": "https://docs.angularjs.org/api/angular.module",
        "!doc": "A global place for creating, registering and retrieving Angular modules."
      },
      Module: "Module",
      noop: {
        "!type": "fn()",
        "!url": "https://docs.angularjs.org/api/angular.noop",
        "!doc": "A function that performs no operations."
      },
      toJson: {
        "!type": "fn(val: ?) -> string",
        "!url": "https://docs.angularjs.org/api/angular.toJson",
        "!doc": "Serializes input into a JSON-formatted string."
      },
      uppercase: {
        "!type": "fn(string) -> string",
        "!url": "https://docs.angularjs.org/api/angular.uppercase",
        "!doc": "Converts the specified string to uppercase."
      },
      version: {
        "!url": "https://docs.angularjs.org/api/angular.version",
        full: "string",
        major: "number",
        minor: "number",
        dot: "number",
        codename: "string"
      }
    }
  };
});

// Parses comments above variable declarations, function declarations,
// and object properties as docstrings and JSDoc-style type
// annotations.

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(require("../lib/infer"), require("../lib/tern"), require("../lib/comment"),
               require("acorn"), require("acorn/dist/walk"));
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/plugin/doc_comment',["../lib/infer", "../lib/tern", "../lib/comment", "acorn/dist/acorn", "acorn/dist/walk"], mod);
  mod(tern, tern, tern.comment, acorn, acorn.walk);
})(function(infer, tern, comment, acorn, walk) {
  "use strict";

  var WG_MADEUP = 1, WG_STRONG = 101;

  tern.registerPlugin("doc_comment", function(server, options) {
    server.mod.jsdocTypedefs = Object.create(null);
    server.on("reset", function() {
      server.mod.jsdocTypedefs = Object.create(null);
    });
    server.mod.docComment = {
      weight: options && options.strong ? WG_STRONG : undefined,
      fullDocs: options && options.fullDocs
    };

    server.on("postParse", postParse)
    server.on("postInfer", postInfer)
    server.on("postLoadDef", postLoadDef)
  });

  function postParse(ast, text) {
    function attachComments(node) { comment.ensureCommentsBefore(text, node); }

    walk.simple(ast, {
      VariableDeclaration: attachComments,
      FunctionDeclaration: attachComments,
      MethodDefinition: attachComments,
      Property: attachComments,
      ClassDeclaration: attachComments,
      AssignmentExpression: function(node) {
        if (node.operator == "=") attachComments(node);
      },
      CallExpression: function(node) {
        if (isDefinePropertyCall(node)) attachComments(node);
      },
      ExportNamedDeclaration: function ExportNamedDeclaration(node) {
      	attachComments(node);
      },
      ExportDefaultDeclaration: function ExportDefaultDeclaration(node) {
      	attachComments(node);
      }
     });
  }

  function isDefinePropertyCall(node) {
    return node.callee.type == "MemberExpression" &&
      node.callee.object.name == "Object" &&
      node.callee.property.name == "defineProperty" &&
      node.arguments.length >= 3 &&
      typeof node.arguments[1].value == "string";
  }

  function postInfer(ast, scope) {
    jsdocParseTypedefs(ast.sourceFile.text, scope);

    walk.simple(ast, {
      VariableDeclaration: function(node, scope) {
        var decl = node.declarations[0].id
        if (node.commentsBefore && decl.type == "Identifier")
          interpretComments(node, node.commentsBefore, scope,
                            scope.getProp(node.declarations[0].id.name));
      },
      FunctionDeclaration: function(node, scope) {
        if (node.commentsBefore)
          interpretComments(node, node.commentsBefore, scope,
                            scope.getProp(node.id.name),
                            node.scope.fnType);
      },
      ClassDeclaration: function(node, scope) {
        if (node.commentsBefore)
          interpretComments(node, node.commentsBefore, scope,
                            scope.getProp(node.id.name),
                            node.objType);
      },
      AssignmentExpression: function(node, scope) {
        if (node.commentsBefore)
          interpretComments(node, node.commentsBefore, scope,
                            infer.expressionType({node: node.left, state: scope}));
      },
      ObjectExpression: function(node, scope) {
        for (var i = 0; i < node.properties.length; ++i) {
          var prop = node.properties[i], name = infer.propName(prop)
          if (name != "<i>" && prop.commentsBefore)
            interpretComments(prop, prop.commentsBefore, scope, node.objType.getProp(name))
        }
      },
      Class: function(node, scope) {
        var proto = node.objType.getProp("prototype").getObjType()
        if (!proto) return
        for (var i = 0; i < node.body.body.length; i++) {
          var method = node.body.body[i], name
          if (!method.commentsBefore) continue
          if (method.kind == "constructor")
            interpretComments(method, method.commentsBefore, scope, node.objType)
          else if ((name = infer.propName(method)) != "<i>")
            interpretComments(method, method.commentsBefore, scope, proto.getProp(name))
        }
      },
      CallExpression: function(node, scope) {
        if (node.commentsBefore && isDefinePropertyCall(node)) {
          var type = infer.expressionType({node: node.arguments[0], state: scope}).getObjType();
          if (type && type instanceof infer.Obj) {
            var prop = type.props[node.arguments[1].value];
            if (prop) interpretComments(node, node.commentsBefore, scope, prop);
          }
        }
      },
      // ORION
      ExportNamedDeclaration: function(node, scope) {
        if (node.commentsBefore && node.declaration && node.declaration.type === 'FunctionDeclaration') {
          interpretComments(node.declaration, node.commentsBefore, scope,
                            scope.getProp(node.declaration.id.name),
                            node.declaration.scope.fnType);
        }
      },
      //ORION
      ExportDefaultDeclaration: function(node, scope) {
        if (node.commentsBefore && node.declaration && node.declaration.type === 'FunctionDeclaration') {
          interpretComments(node.declaration, node.commentsBefore, scope,
                            scope.getProp(node.declaration.id.name),
                            node.declaration.scope.fnType);
        }
      }
    }, infer.searchVisitor, scope);
  }

  function postLoadDef(data) {
    var defs = data["!typedef"];
    var cx = infer.cx(), orig = data["!name"];
    if (defs) for (var name in defs)
      cx.parent.mod.jsdocTypedefs[name] =
        maybeInstance(infer.def.parse(defs[name], orig, name), name);
  }

  // COMMENT INTERPRETATION

  function stripLeadingChars(lines) {
    for (var head, i = 1; i < lines.length; i++) {
      var line = lines[i], lineHead = line.match(/^[\s\*]*/)[0];
      if (lineHead != line) {
        if (head == null) {
          head = lineHead;
        } else {
          var same = 0;
          while (same < head.length && head.charCodeAt(same) == lineHead.charCodeAt(same)) ++same;
          if (same < head.length) head = head.slice(0, same)
        }
      }
    }
    lines = lines.map(function(line, i) {
      line = line.replace(/\s+$/, "");
      if (i == 0 && head != null) {
        for (var j = 0; j < head.length; j++) {
          var found = line.indexOf(head.slice(j));
          if (found == 0) return line.slice(head.length - j);
        }
      }
      if (head == null || i == 0) return line.replace(/^[\s\*]*/, "");
      if (line.length < head.length) return "";
      return line.slice(head.length);
    });
    while (lines.length && !lines[lines.length - 1]) lines.pop();
    while (lines.length && !lines[0]) lines.shift();
    return lines;
  }

  function interpretComments(node, comments, scope, aval, type) {
    jsdocInterpretComments(node, scope, aval, comments);
    var cx = infer.cx();

    if (!type && aval instanceof infer.AVal && aval.types.length) {
      type = aval.types[aval.types.length - 1];
      if (!(type instanceof infer.Obj) || type.origin != cx.curOrigin || type.doc)
        type = null;
    }

    for (var i = comments.length - 1; i >= 0; i--) {
      var text = stripLeadingChars(comments[i].split(/\r\n?|\n/)).join("\n");
      if (text) {
        if (aval instanceof infer.AVal) aval.doc = text;
        if (type) type.doc = text;
        break;
      }
    }
  }

  // Parses a subset of JSDoc-style comments in order to include the
  // explicitly defined types in the analysis.

  function skipSpace(str, pos) {
    while (/\s/.test(str.charAt(pos))) ++pos;
    return pos;
  }

  function isIdentifier(string) {
    if (!acorn.isIdentifierStart(string.charCodeAt(0))) return false;
    for (var i = 1; i < string.length; i++)
      if (!acorn.isIdentifierChar(string.charCodeAt(i))) return false;
    return true;
  }

  function parseLabelList(scope, str, pos, close) {
    var labels = [], types = [], madeUp = false;
    for (var first = true; ; first = false) {
      pos = skipSpace(str, pos);
      if (first && str.charAt(pos) == close) break;
      var colon = str.indexOf(":", pos);
      if (colon < 0) return null;
      var label = str.slice(pos, colon);
      if (!isIdentifier(label)) return null;
      labels.push(label);
      pos = colon + 1;
      var type = parseType(scope, str, pos);
      if (!type) return null;
      pos = type.end;
      madeUp = madeUp || type.madeUp;
      types.push(type.type);
      pos = skipSpace(str, pos);
      var next = str.charAt(pos);
      ++pos;
      if (next == close) break;
      if (next != ",") return null;
    }
    return {labels: labels, types: types, end: pos, madeUp: madeUp};
  }

  function parseTypeAtom(scope, str, pos) {
    var result = parseTypeInner(scope, str, pos)
    if (!result) return null
    if (str.slice(result.end, result.end + 2) == "[]")
      return {madeUp: result.madeUp, end: result.end + 2, type: new infer.Arr(result.type)}
    else return result
  }

  function parseType(scope, str, pos) {
    var type, union = false, madeUp = false;
    for (;;) {
      var inner = parseTypeAtom(scope, str, pos);
      if (!inner) return null;
      madeUp = madeUp || inner.madeUp;
      if (union) inner.type.propagate(union);
      else type = inner.type;
      pos = skipSpace(str, inner.end);
      if (str.charAt(pos) != "|") break;
      pos++;
      if (!union) {
        union = new infer.AVal;
        type.propagate(union);
        type = union;
      }
    }
    var isOptional = false;
    if (str.charAt(pos) == "=") {
      ++pos;
      isOptional = true;
    }
    return {type: type, end: pos, isOptional: isOptional, madeUp: madeUp};
  }

  function parseTypeInner(scope, str, pos) {
    pos = skipSpace(str, pos);
    var type, madeUp = false;

    if (str.indexOf("function(", pos) == pos) {
      var args = parseLabelList(scope, str, pos + 9, ")"), ret = infer.ANull;
      if (!args) return null;
      pos = skipSpace(str, args.end);
      if (str.charAt(pos) == ":") {
        ++pos;
        var retType = parseType(scope, str, pos + 1);
        if (!retType) return null;
        pos = retType.end;
        ret = retType.type;
        madeUp = retType.madeUp;
      }
      type = new infer.Fn(null, infer.ANull, args.types, args.labels, ret);
    } else if (str.charAt(pos) == "[") {
      var inner = parseType(scope, str, pos + 1);
      if (!inner) return null;
      pos = skipSpace(str, inner.end);
      madeUp = inner.madeUp;
      if (str.charAt(pos) != "]") return null;
      ++pos;
      type = new infer.Arr(inner.type);
    } else if (str.charAt(pos) == "{") {
      var fields = parseLabelList(scope, str, pos + 1, "}");
      if (!fields) return null;
      type = new infer.Obj(true);
      for (var i = 0; i < fields.types.length; ++i) {
        var field = type.defProp(fields.labels[i]);
        field.initializer = true;
        fields.types[i].propagate(field);
      }
      pos = fields.end;
      madeUp = fields.madeUp;
    } else if (str.charAt(pos) == "(") {
      var inner = parseType(scope, str, pos + 1);
      if (!inner) return null;
      pos = skipSpace(str, inner.end);
      if (str.charAt(pos) != ")") return null;
      ++pos;
      type = inner.type;
    } else {
      var start = pos;
      if (!acorn.isIdentifierStart(str.charCodeAt(pos))) return null;
      while (acorn.isIdentifierChar(str.charCodeAt(pos))) ++pos;
      if (start == pos) return null;
      var word = str.slice(start, pos);
      if (/^(number|integer)$/i.test(word)) type = infer.cx().num;
      else if (/^bool(ean)?$/i.test(word)) type = infer.cx().bool;
      else if (/^string$/i.test(word)) type = infer.cx().str;
      else if (/^(null|undefined)$/i.test(word)) type = infer.ANull;
      else if (/^array$/i.test(word)) {
        var inner = null;
        if (str.charAt(pos) == "." && str.charAt(pos + 1) == "<") {
          var inAngles = parseType(scope, str, pos + 2);
          if (!inAngles) return null;
          pos = skipSpace(str, inAngles.end);
          madeUp = inAngles.madeUp;
          if (str.charAt(pos++) != ">") return null;
          inner = inAngles.type;
        }
        type = new infer.Arr(inner);
      } else if (/^object$/i.test(word)) {
        type = new infer.Obj(true);
        if (str.charAt(pos) == "." && str.charAt(pos + 1) == "<") {
          var key = parseType(scope, str, pos + 2);
          if (!key) return null;
          pos = skipSpace(str, key.end);
          madeUp = madeUp || key.madeUp;
          if (str.charAt(pos++) != ",") return null;
          var val = parseType(scope, str, pos);
          if (!val) return null;
          pos = skipSpace(str, val.end);
          madeUp = key.madeUp || val.madeUp;
          if (str.charAt(pos++) != ">") return null;
          val.type.propagate(type.defProp("<i>"));
        }
      } else {
        while (str.charCodeAt(pos) == 46 ||
               acorn.isIdentifierChar(str.charCodeAt(pos))) ++pos;
        var path = str.slice(start, pos);
        var cx = infer.cx(), defs = cx.parent && cx.parent.mod.jsdocTypedefs, found;
        if (defs && (path in defs)) {
          type = defs[path];
        } else if (found = infer.def.parsePath(path, scope).getObjType()) {
          type = maybeInstance(found, path);
        } else {
          if (!cx.jsdocPlaceholders) cx.jsdocPlaceholders = Object.create(null);
          if (!(path in cx.jsdocPlaceholders))
            type = cx.jsdocPlaceholders[path] = new infer.Obj(null, path);
          else
            type = cx.jsdocPlaceholders[path];
          madeUp = true;
        }
      }
    }

    return {type: type, end: pos, madeUp: madeUp};
  }

  function maybeInstance(type, path) {
    if (type instanceof infer.Fn && /(?:^|\.)[A-Z][^\.]*$/.test(path)) {
      var proto = type.getProp("prototype").getObjType();
      if (proto instanceof infer.Obj) return infer.getInstance(proto);
    }
    return type;
  }

  function parseTypeOuter(scope, str, pos) {
    pos = skipSpace(str, pos || 0);
    if (str.charAt(pos) != "{") return null;
    var result = parseType(scope, str, pos + 1);
    if (!result) return null;
    var end = skipSpace(str, result.end);
    if (str.charAt(end) != "}") return null;
    result.end = end + 1;
    return result;
  }

  function jsdocInterpretComments(node, scope, aval, comments) {
    var type, args, ret, foundOne, self, parsed;

    for (var i = 0; i < comments.length; ++i) {
      var comment = comments[i];
      var decl = /(?:\n|$|\*)\s*@(type|param|arg(?:ument)?|returns?|this|class|constructor)\s+(.*)/g, m;
      while (m = decl.exec(comment)) {
        if (m[1] == "class" || m[1] == "constructor") {
          self = foundOne = true;
          continue;
        }

        if (m[1] == "this" && (parsed = parseType(scope, m[2], 0))) {
          self = parsed;
          foundOne = true;
          continue;
        }

        if (!(parsed = parseTypeOuter(scope, m[2]))) continue;
        foundOne = true;

        switch(m[1]) {
        case "returns": case "return":
          ret = parsed; break;
        case "type":
          type = parsed; break;
        case "param": case "arg": case "argument":
            var name = m[2].slice(parsed.end).match(/^\s*(\[?)\s*([^\]\s=]+)\s*(?:=[^\]]+\s*)?(\]?).*/);
            if (!name) continue;
            var argname = name[2] + (parsed.isOptional || (name[1] === '[' && name[3] === ']') ? "?" : "");
          (args || (args = Object.create(null)))[argname] = parsed;
          break;
        }
      }
    }

    if (foundOne) applyType(type, self, args, ret, node, aval);
  };

  function jsdocParseTypedefs(text, scope) {
    var cx = infer.cx();

    var re = /\s@typedef\s+(.*)/g, m;
    while (m = re.exec(text)) {
      var parsed = parseTypeOuter(scope, m[1]);
      var name = parsed && m[1].slice(parsed.end).match(/^\s*(\S+)/);
      if (name && parsed.type instanceof infer.Obj) {
        var rest = text.slice(m.index + m[0].length)
        while (m = /\s+@prop(?:erty)?\s+(.*)/.exec(rest)) {
          var propType = parseTypeOuter(scope, m[1]), propName
          if (propType && (propName = m[1].slice(propType.end).match(/^\s*(\S+)/)))
            propType.type.propagate(parsed.type.defProp(propName[1]))
          rest = rest.slice(m[0].length)
        }
        cx.parent.mod.jsdocTypedefs[name[1]] = parsed.type;
      }
    }
  }

  function propagateWithWeight(type, target) {
    var weight = infer.cx().parent.mod.docComment.weight;
    type.type.propagate(target, weight || (type.madeUp ? WG_MADEUP : undefined));
  }

  function isFunExpr(node) { return node.type == "FunctionExpression" || node.type == "ArrowFunctionExpression" }

  function applyType(type, self, args, ret, node, aval) {
    var fn;
    if (node.type == "VariableDeclaration") {
      var decl = node.declarations[0];
      if (decl.init && isFunExpr(decl.init)) fn = decl.init.scope.fnType;
    } else if (node.type == "FunctionDeclaration") {
      fn = node.scope.fnType;
    } else if (node.type == "AssignmentExpression") {
      if (isFunExpr(node.right))
        fn = node.right.scope.fnType;
    } else if (node.type == "CallExpression") {
    } else if(node.type === "ClassDeclaration") {
    } else { // An object property
      if (isFunExpr(node.value)) fn = node.value.scope.fnType;
    }

    if (fn && (args || ret || self)) {
      if (args) for (var i = 0; i < fn.argNames.length; ++i) {
        var name = fn.argNames[i], known = args[name];
        if (!known && (known = args[name + "?"]))
          fn.argNames[i] += "?";
        if (known) propagateWithWeight(known, fn.args[i]);
      }
      if (ret) {
        if (fn.retval == infer.ANull) fn.retval = new infer.AVal;
        propagateWithWeight(ret, fn.retval);
      }
      if (self === true) {
        var proto = fn.getProp("prototype").getObjType();
        self = proto && {type: infer.getInstance(proto, fn)};
      }
      if (self) propagateWithWeight(self, fn.self);
    } else if (type) {
      propagateWithWeight(type, aval);
    }
  };
});

  /* eslint-disable */
(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(require("../lib/infer"), require("../lib/tern"), require("../lib/signal"), require)
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/plugin/modules',["../lib/infer", "../lib/tern", "../lib/signal"], mod)
  mod(tern, tern, tern.signal)
})(function(infer, tern, signal, require) {
  "use strict"

  function Modules(server, options) {
    this.server = server
    this.options = options || {}
    this.modules = Object.create(null)
    this.nonRelative = Object.create(null)
    this.knownModules = Object.create(null)
    this.resolvers = []
    this.modNameTests = []
    this.importTests = []
    this.completableTypes = Object.create(null)
  }

  Modules.prototype = signal.mixin({
    buildWrappingScope: function(parent, origin, node) {
      var scope = new infer.Scope(parent, node)
      scope.origin = origin
      this.signal("wrapScope", scope)
      return scope
    },

    maybeOverride: function(name) {
      if (!this.options.modules || !this.options.modules.hasOwnProperty(name))
        return false
      if (this.modules[name]) return this.modules[name]

      var override = this.options.modules[name]
      if (typeof(override) == "string" && override.charAt(0) == "=")
        return infer.def.parsePath(override.slice(1))

      var scope = this.buildWrappingScope(infer.cx().topScope, name)
      infer.def.load(override, scope)
      return this.modules[name] = scope.exports
    },

    resolveModule: function(name, parentFile) {
      var modName = name; // ORION we need the original name for linting
      var over = this.maybeOverride(name)
      if (over) return over
      var known = this.knownModules[name]
      if (known) return known
      if (this.options.dontLoad == true ||
          this.options.dontLoad && new RegExp(this.options.dontLoad).test(name) ||
          this.options.load && !new RegExp(this.options.load).test(name))
        return infer.ANull

      var resolved, relative = isRelative(name)
      for (var i = 0; !resolved && i < this.resolvers.length; i++)
        resolved = this.resolvers[i](name, parentFile)
      if (!resolved) resolved = defaultResolver(name, parentFile)
      if (!resolved) return infer.ANull
      
      // ORION Check if this file has contents
      var contents = null;
      if (typeof resolved === 'object' && typeof resolved.contents === 'string'){
      	contents = resolved.contents;
      	resolved = resolved.file;
      }
      
      if (typeof resolved != "string") {
        if (!relative) this.nonRelative[name] = true
        return resolved
      }

      var known = this.modules[resolved]
      // ORION tag module with original name for unknown-require rule, update in case contents changed
      if (known && contents){
      	known.modName = modName;
      }
      //ORION we need to force a text change and recompute since it was opened (and likely edited)
      if (known && !known.poisoned) {
      	return known;
	  }
      if (/\.js$|(?:^\/)[^\.]+$/.test(resolved))
        this.server.addFile(resolved, contents, parentFile)
      if (!relative) this.nonRelative[name] = resolved
      var val = new infer.AVal;
      
      // ORION tag module with original name for unknown-require rule
      if (contents){
        val.modName = modName;
  	  }

      return this.modules[resolved] = val;
    },

    findIn: function(array, node, pos) {
      for (var i = 0; i < array.length; i++) {
        var name = array[i](node, pos)
        if (name != null) return name
      }
    },

    isModName: function(node, pos) { return this.findIn(this.modNameTests, node, pos) },
    isImport: function(node, pos) { return this.findIn(this.importTests, node, pos) },

    get: function(name) {
      return this.modules[name] || (this.modules[name] = new infer.AVal);
    },

    completeModuleName: function(completions, query, word) {
      function fromObj(obj, useVal) {
        for (var name in obj)
          if (filter(word, name, query))
            tern.addCompletion(query, completions, name, useVal && obj[name])
      }

      fromObj(this.knownModules, true)
      if (this.options.modules) fromObj(this.options.modules, false)

      var pathsSeen = Object.create(null)
      for (var prop in this.nonRelative) {
        var val = this.nonRelative[prop]
        // ORION We want to always include name completions that have a slash
        if (val || prop.indexOf("/") == -1) {
          if (filter(word, prop, query)) tern.addCompletion(query, completions, prop)
        } else if (prop.indexOf(word) == 0 && word.indexOf("/") > -1) {
          var afterSlash = /.*?\/(.*)/.exec(prop)[1]
          var found = val.indexOf(afterSlash)
          if (found > -1) {
            var dir = val.slice(0, found) + (/.*?\/(.*\/)?/.exec(word)[1] || "")
            if (dir in pathsSeen) continue
            pathsSeen[dir] = true
            this.completeFileName(completions, query, null, word, dir)
          }
        }
      }
    },

    completeFileName: function(completions, query, parentFile, word, _dir) {
      var path = parentFile ? resolvePath(dirName(parentFile), word) : baseName(word)
      for (var prop in this.modules) {
        if (prop != parentFile && filter(path, prop, query)) {
          if (/\.js$/.test(prop)) prop = prop.slice(0, prop.length - 3)
          var added = prop.slice(path.length)
          tern.addCompletion(query, completions, word + added, this.modules[prop])
        }
      }
    },

    getModType: function(node) {
      var modName = this.isModName(node), imp, prop
      if (modName == null && (imp = this.isImport(node))) {
        modName = imp.name
        prop = imp.prop
      }
      if (modName == null) return

		// TODO Orion check that node.sourceFile is actually available, ast is not
      var type = this.resolveModule(modName, node.sourceFile.name)
      if (prop) {
        var obj = type.getObjType()
        type = obj && obj.hasProp(prop) || type.getProp(prop)
      }
      return type
    }
  })

  function resolvePath(parent, sub) {
    if (/^https?:|^\//.test(sub)) return sub
    if (parent && !/\/$/.test(parent)) parent = parent + "/"
    var m
    while (m = /^\.(\.)?\//.exec(sub)) {
      if (m[1] && parent.length > 1) {
        var lastSlash = parent.lastIndexOf("/", parent.length - 2)
        parent = lastSlash == -1 ? "" : parent.slice(0, lastSlash + 1)
      }
      sub = sub.slice(m[0].length)
    }
    return parent + sub
  }

  function dirName(path) {
    var lastSlash = path.lastIndexOf("/")
    if (lastSlash == -1) return ""
    return path.slice(0, lastSlash + 1)
  }
  function baseName(path) {
    var lastSlash = path.lastIndexOf("/")
    if (lastSlash == -1) return path
    else return path.slice(lastSlash + 1)
  }

  function defaultResolver(name, parentFile) {
    if (!/^\.\.?\//.test(name)) return
    var path = resolvePath(dirName(parentFile), name)
    var server = infer.cx().parent
    if (server.findFile(path)) return path
    if (server.findFile(path + ".js")) return path + ".js"
  }

  // Under node, replace completeFileName with a version that actually
  // queries the file system
  if (require) (function() {
    var fs = require("fs"), path = require("path")

    Modules.prototype.completeFileName = function(completions, query, parentFile, word, dir) {
      var pDir = this.server.projectDir
      var endSlash = /\/$/.test(word)
      if (parentFile) {
        var pt = path.resolve(pDir, path.dirname(parentFile), word)
        dir = endSlash ? pt : path.dirname(pt)
      }
      var base = endSlash ? word : path.dirname(word) + "/"
      var filePart = endSlash ? "" : path.basename(word)

      var me = this
      fs.readdirSync(dir).forEach(function(file) {
        if (/^\./.test(file)) return
        if (filter(filePart, file, query)) {
          var projectPath = me.server.normalizeFilename(path.relative(pDir, path.resolve(dir, file)))
          if (projectPath == parentFile) return
          var value = me.modules[projectPath]
          if (/\.js$/.test(file)) file = file.slice(0, file.length - 3)
          tern.addCompletion(query, completions, base + file, value)
        }
      })
    }
  }())

  function isRelative(path) {
    return /^\.\.?\//.test(path)
  }

  function filter(word, string, query) {
    return query.filter === false || !word ||
      (query.caseInsensitive ? string.toLowerCase() : string).indexOf(word) == 0
  }

  function preCondenseReach(state) {
    var mods = infer.cx().parent.mod.modules.modules
    var node = state.roots["!modules"] = new infer.Obj(null)
    for (var name in mods) {
      var mod = mods[name]
      var id = mod.origin || name
      var prop = node.defProp(id.replace(/\./g, "`"))
      prop.origin = mod.origin
      mod.propagate(prop)
    }
  }

  function postLoadDef(data) {
    var cx = infer.cx(), me = cx.parent.mod.modules
    var mods = cx.definitions[data["!name"]]["!modules"]
    if (mods) for (var name in mods.props) {
      var origin = name.replace(/`/g, ".")
      var mod = me.get(origin)
      mod.origin = origin
      mods.props[name].propagate(mod)
    }
    var known = cx.definitions[data["!name"]]["!known_modules"]
    if (known) for (var name in known.props)
      me.knownModules[name.replace(/`/g, ".")] = known.props[name]
  }

  function findTypeAt(_file, _pos, expr, type) {
    if (!expr) return type
    var me = infer.cx().parent.mod.modules
    var modType = me.getModType(expr.node)
    if (!modType) return type

    // The `type` is a value shared for all string literals.
    // We must create a copy before modifying `origin` and `originNode`.
    // Otherwise all string literals would point to the last jump location
    var inner = modType.getType(false) || {}
    type = Object.create(type)
    type.origin = modType.origin || inner.origin
    type.originNode = modType.originNode || inner.originNode
    if (modType.doc) type.doc = modType.doc
    else if (inner.doc) type.doc = inner.doc
    if (modType.url) type.url = modType.url
    else if (inner.url) type.url = inner.url
    return type
  }

  // Complete previously seen module names when completing in strings passed to require
  function findCompletions(file, query) {
    var me = infer.cx().parent.mod.modules
    var wordEnd = tern.resolvePos(file, query.end), types = me.completableTypes
    var expr = infer.findExpressionAround(file.ast, null, wordEnd, file.scope,
                                          function(type) { return type in types })
    if (!expr) return null

    if (me.isModName(expr.node, wordEnd) != null)
      return findModuleCompletions(me, file, query, expr.node, wordEnd)

    var imp = me.isImport(expr.node, wordEnd)
    if (imp && imp.name && imp.prop != null)
      return findImportCompletions(me, file, query, expr.node, imp, wordEnd)
  }

  function findImportCompletions(me, file, query, node, imp, wordEnd) {
    var completions = []
    var word = node.name ? node.name.slice(0, wordEnd - node.start) : ""
    if (query.caseInsensitive) word = word.toLowerCase()

	// TODO Orion make sure node.sourceFile is available, AST is not
    var modType = me.resolveModule(imp.name, node.sourceFile.name).getType()
    if (!modType) return null
    infer.forAllPropertiesOf(modType, function(prop, obj, depth) {
      if (obj == infer.cx().protos.Object) return
      if (query.filter !== false && word &&
          (query.caseInsensitive ? prop.toLowerCase() : prop).indexOf(word) !== 0) return
      tern.addCompletion(query, completions, prop, obj && obj.props[prop], depth)
    })
    return {
      start: tern.outputPos(query, file, node.name ? node.start : wordEnd),
      end: tern.outputPos(query, file, wordEnd),
      completions: completions,
      isSpecifier: true
    }
  }

  function findModuleCompletions(me, file, query, argNode, wordEnd) {
    if (argNode.type != "Literal" || typeof argNode.value != "string" ||
        argNode.start > wordEnd || argNode.end < wordEnd) return

    var word = argNode.raw.slice(1, wordEnd - argNode.start), quote = argNode.raw.charAt(0)
    if (word && word.charAt(word.length - 1) == quote)
      word = word.slice(0, word.length - 1)
    if (query.caseInsensitive) word = word.toLowerCase()

    var completions = []
    if (isRelative(word)) me.completeFileName(completions, query, file.name, word)
    else me.completeModuleName(completions, query, word)

    if (argNode.end == wordEnd + 1 && file.text.charAt(wordEnd) == quote)
      ++wordEnd
    return {
      start: tern.outputPos(query, file, argNode.start),
      end: tern.outputPos(query, file, wordEnd),
      isProperty: false,
      completions: completions.map(function(rec) {
        var name = typeof rec == "string" ? rec : rec.name
        
        // TODO ORION: Stringify the name adds the quotes around the proposal which Orion doesn't handle in sortProposals
        var string = name;
//        var string = JSON.stringify(name)
//        if (quote == "'") string = quote + string.slice(1, string.length -1).replace(/'/g, "\\'") + quote
        if (typeof rec == "string") return string
        rec.displayName = name
        rec.name = string
        rec.type = "string"; //ORION tag the completion with the correct type
        return rec
      })
    }
  }

  tern.registerPlugin("modules", function(server, options) {
    server.mod.modules = new Modules(server, options)
    
    
    /**
	 * @description Returns whether the given file is using a dependency system handled by this modules plugin ('node' and 'es_modules')
	 * @param {Object} file The file object
	 * @returns {Boolean} If we should modify scopes or do other work with this plugin
	 * Orion
	 */
	function isUsingModules(file) {
		if(file.ast){
			if (file.ast.environments && (file.ast.environments.node || file.ast.environments.es_modules) && !file.ast.environments.simplifiedCommonJS) {
				return true;
			} else if (file.ast.dependencies){
				for (var i=0; i<file.ast.dependencies.length; i++) {
					var dep = file.ast.dependencies[i];
					if (dep.env === 'node' || dep.env === 'es_modules'){
						return true;
					}
				}
			}
      	}
      	return false;
	}

    server.on("beforeLoad", function(file) {
      // ORION Only modify the scope if we are using node for dependencies in this file or we cannot use globals from other files
      if (isUsingModules(file)){
      	file.scope = this.mod.modules.buildWrappingScope(file.scope, file.name, file.ast);
      	var mod = this.mod.modules.get(file.name);
      	if(mod) {
      		//found an existing module that has been loaded in an editor, tag it
      		mod.poisoned = true;
      	}
  	  }
    })

    server.on("afterLoad", function(file) {
      // ORION Only collect exports for this file if we are using for dependencies in this file
      if (isUsingModules(file)){
        var mod = this.mod.modules.get(file.name)
        mod.origin = file.name
        this.mod.modules.signal("getExports", file, mod)
      }
    })

    server.on("reset", function() {
      this.mod.modules.modules = Object.create(null)
    })

    server.on("preCondenseReach", preCondenseReach)
    server.on("postLoadDef", postLoadDef)
    server.on("typeAt", findTypeAt)
    server.on("completion", findCompletions)
  })

  tern.defineQueryType("exports", {
    takesFile: true,
    run: function(server, query, file) {
      function describe(aval) {
        var target = {}, type = aval.getType(false)
        target.type = infer.toString(type, 3)
        var doc = aval.doc || (type && type.doc), url = aval.url || (type && type.url)
        if (doc) target.doc = doc
        if (url) target.url = url
        var span = tern.getSpan(aval) || (type && tern.getSpan(type))
        if (span) tern.storeSpan(server, query, span, target)
        return target
      }

      var mod = server.mod.modules, known = mod && mod.modules[file.name]
      if (!known) return {}
      var resp = describe(known);
      var type = known.getType(false)
      if (type instanceof infer.Obj) {
        var props = resp.props = {}
        for (var prop in type.props)
          props[prop] = describe(type.props[prop])
      }
      return resp
    }
  })
});
(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(require("../lib/infer"), require("../lib/tern"), require("acorn/dist/walk"), require("./modules"))
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/plugin/es_modules',["../lib/infer", "../lib/tern", "acorn/dist/walk", "./modules"], mod)
  mod(tern, tern, acorn.walk)
})(function(infer, tern, walk) {
  "use strict"

  var WG_IMPORT_DEFAULT_FALLBACK = 80

  function connectModule(file, out) {
    var modules = infer.cx().parent.mod.modules
    var outObj = null
    function exp(prop, type, originNode) {
      if (!outObj) {
        outObj = new infer.Obj(true)
        outObj.origin = file.name
        outObj.originNode = file.ast
        out.addType(outObj)
      }
      var prop = outObj.defProp(prop, originNode)
      prop.origin = file.name
      type.propagate(prop)
    }

    walk.simple(file.ast, {
      ImportDeclaration: function(node) {
        var input = modules.resolveModule(node.source.value, file.name)
        for (var i = 0; i < node.specifiers.length; i++) {
          var spec = node.specifiers[i]
          var aval = file.scope.getProp(spec.local.name)
          if (spec.type == "ImportNamespaceSpecifier") {
            input.propagate(aval)
          } else if (spec.type == "ImportDefaultSpecifier") {
            input.getProp("default").propagate(aval)
            input.propagate(aval, WG_IMPORT_DEFAULT_FALLBACK)
          } else {
            input.getProp(spec.imported.name).propagate(aval)
          }
        }
      },
      ExportAllDeclaration: function(node) {
        var input = modules.resolveModule(node.source.value, file.name)
        input.forAllProps(function(prop, val, local) {
          if (local) exp(prop, val, val.originNode)
        })
      },
      ExportDefaultDeclaration: function(node) {
        var decl = node.declaration.id || node.declaration
        exp("default", infer.expressionType({node: decl, state: file.scope}), decl)
      },
      ExportNamedDeclaration: function(node) {
        var decl = node.declaration
        if (decl) {
          if (decl.type == "VariableDeclaration") {
            for (var i = 0; i < decl.declarations.length; ++i) {
              var cur = decl.declarations[i]
              if (cur.id.type == "Identifier")
                exp(cur.id.name, file.scope.getProp(cur.id.name), cur.id)
            }
          } else if(decl.id) {
            exp(decl.id.name, file.scope.getProp(decl.id.name), decl.id)
          }
        }
        if (node.specifiers.length) {
          var src = node.source ? modules.resolveModule(node.source.value, file.name) : file.scope
          for (var i = 0; i < node.specifiers.length; i++) {
            var spec = node.specifiers[i]
            exp(spec.exported.name, src.getProp(spec.local.name), spec.local)
          }
        }
      }
    })
  }
  
  function _getAST(node){
  	// ORION In our version of Acorn the AST is not available on the given node
    var ast = node.sourceFile.ast;
    if (!ast){
        var server = infer.cx().parent;
        ast = server.fileMap[node.sourceFile.name];
        if (!ast) return;
        ast = ast.ast;
    }
    return ast;
  }

  function isModuleName(node) {
    if (node.type != "Literal" || typeof node.value != "string") return

    var decl = infer.findExpressionAround(_getAST(node), null, node.end, null, function(_, node) {
      return node.type == "ImportDeclaration" || /Export(All|Named)Declaration/.test(node.type)
    })
    if (!decl || decl.node.source != node) return
    return node.value
  }

  function isImport(node, pos) {
    if (node.type == "Identifier") {
      var imp = infer.findExpressionAround(_getAST(node), null, node.end, null, "ImportDeclaration")
      if (!imp) return
      var specs = imp.node.specifiers
      for (var i = 0; i < specs.length; i++) {
        var spec = specs[i]
        if (spec.local != node) continue
        var result = {name: imp.node.source.value, prop: null}
        if (spec.type == "ImportDefaultSpecifier") result.prop = "default"
        else if (spec.type == "ImportSpecifier") result.prop = spec.imported.name
        return result
      }
     // TODO Orion the sourceFile may not have contents available, see _getAST(node)
    } else if (node.type == "ImportDeclaration" &&
               /^import\s+\{\s*([\w$]+\s*,\s*)*$/.test(node.sourceFile.text.slice(node.start, pos))) {
      return {name: node.source.value, prop: ""}
    }
  }

  tern.registerPlugin("es_modules", function(server) {
    server.loadPlugin("modules")
    server.mod.modules.on("getExports", connectModule)
    server.mod.modules.modNameTests.push(isModuleName)
    server.mod.modules.importTests.push(isImport)
    server.mod.modules.completableTypes.Identifier = true
    server.mod.modules.completableTypes.Literal = true
    server.mod.modules.completableTypes.ImportDeclaration = true
  })
})
;
(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(require("../lib/infer"), require("../lib/tern"), require("./modules"))
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/plugin/commonjs',["../lib/infer", "../lib/tern", "./modules"], mod)
  mod(tern, tern)
})(function(infer, tern) {
  "use strict"

  var WG_DEFAULT_EXPORT = 95

  function initScope(scope) {
  	/* TODO ORION
  		Orion filters based on eslint-env name, preventing commonjs defs from showing up.  Below the plugin adds
  		modules/exports into every scope which is a problem if you are using RequireJS.  For now we add require
  		to every scope, and only add any of the types if we know the file uses a node environment.  A better solution
  		would be to always skip adding modules/exports to every scope, and remove the eslint-env filtering that prevents
  		commonjs defs infos from showing up.
  	*/
  	// ORION: This puts exports and modules into every scope's completions, we only want them when inside a file using node
    if (scope.originNode && scope.originNode.environments && scope.originNode.environments.node){
	    var defs = infer.cx().definitions.commonjs
	    defs.require.propagate(scope.defProp("require"))
	    
	    // ORION: Add require to the list of completions available when node is being used
	    var require = new infer.Obj(true);
	    require.propagate(scope.defProp("require"));
	    
	    var module = new infer.Obj(defs.Module.getProp("prototype").getType())
	    module.propagate(scope.defProp("module"))
	    var exports = new infer.Obj(true)
	    module.origin = exports.origin = scope.origin
	    module.originNode = exports.originNode = scope.originNode
	    exports.propagate(scope.defProp("exports"))
	    var moduleExports = scope.exports = module.defProp("exports")
	    exports.propagate(moduleExports, WG_DEFAULT_EXPORT)
    }
  }

  infer.registerFunction("require", function(_self, _args, argNodes) {
    if (!argNodes || !argNodes.length || argNodes[0].type != "Literal" || typeof argNodes[0].value != "string")
      return infer.ANull
    var cx = infer.cx(), server = cx.parent
    var currentFile = argNodes[0].sourceFile.name

    var name = argNodes[0].value
    var resolved = server.mod.modules.resolveModule(name, currentFile)
    return resolved
  })

  function isStaticRequire(node) {
    if (node.type != "CallExpression" || node.callee.type != "Identifier" || node.callee.name != "require") return
    var arg = node.arguments[0]
    if (arg && arg.type == "Literal" && typeof arg.value == "string") return arg.value
  }
  
  function _getAST(node){
  	// ORION In our version of Acorn the AST is not available on the given node
    var ast = node.sourceFile.ast;
    if (!ast){
        var server = infer.cx().parent;
        ast = server.fileMap[node.sourceFile.name];
        if (!ast) return;
        ast = ast.ast;
    }
    return ast;
  }

  function isModuleName(node) {
    if (node.type != "Literal" || typeof node.value != "string") return
    var call = infer.findExpressionAround(_getAST(node), null, node.end, null,
                                          function(_, n) { return isStaticRequire(n) != null })
    if (call && call.node.arguments[0] == node) return node.value
  }

  function isImport(node) {
    if (node.type != "Identifier") return
    
    var decl = infer.findExpressionAround(_getAST(node), null, node.end, null, "VariableDeclarator"), name
    if (!decl || decl.node.id != node) return
    var init = decl.node.init
    if (init && (name = isStaticRequire(init)) != null)
      return {name: name, prop: null}
    if (init && init.type == "MemberExpression" && !init.computed && (name = isStaticRequire(init.object)) != null)
      return {name: name, prop: init.property.name}
  }

  function hasProps(obj) {
    if (obj) for (var _prop in obj) return true
  }

  tern.registerPlugin("commonjs", function(server) {
    server.loadPlugin("modules")
    server.mod.modules.on("wrapScope", initScope)
    server.mod.modules.on("getExports", function(file, mod) {
      var exports = file.scope.exports
      // TODO ORION: Because of changes to initScope, scope.exports may not exist
      if (exports){
      	if (exports.types.length > 1 || hasProps(exports.getObjType()))
	        exports.propagate(mod)
        }
    })

    server.mod.modules.modNameTests.push(isModuleName)
    server.mod.modules.importTests.push(isImport)
    server.mod.modules.completableTypes.Identifier = true
    server.mod.modules.completableTypes.Literal = true

    server.addDefs(defs)
  })

  var defs = {
    "!name": "commonjs",
    "!define": {
      require: {
        "!type": "fn(id: string) -> !custom:require",
        resolve: {
          "!type": "fn() -> string",
          "!url": "https://nodejs.org/api/globals.html#globals_require_resolve",
          "!doc": "Use the internal require() machinery to look up the location of a module, but rather than loading the module, just return the resolved filename."
        },
        cache: {
          "!url": "https://nodejs.org/api/globals.html#globals_require_cache",
          "!doc": "Modules are cached in this object when they are required. By deleting a key value from this object, the next require will reload the module."
        },
        extensions: {
          "!url": "https://nodejs.org/api/globals.html#globals_require_extensions",
          "!doc": "Instruct require on how to handle certain file extensions."
        },
        "!url": "https://nodejs.org/api/globals.html#globals_require",
        "!doc": "To require modules."
      },
      Module: {
        "!type": "fn()",
        "!url": "https://nodejs.org/api/modules.html",
        "!doc": "Node has a simple module loading system. In Node, files and modules are in one-to-one correspondence.",
        prototype: {
          exports: {
            "!type": "?",
            "!url": "https://nodejs.org/api/modules.html#modules_module_exports",
            "!doc": "The exports object is created by the Module system. Sometimes this is not acceptable, many want their module to be an instance of some class. To do this assign the desired export object to module.exports. For example suppose we were making a module called a.js"
          },
          require: {
            "!type": "require",
            "!url": "https://nodejs.org/api/modules.html#modules_module_require_id",
            "!doc": "The module.require method provides a way to load a module as if require() was called from the original module."
          },
          id: {
            "!type": "string",
            "!url": "https://nodejs.org/api/modules.html#modules_module_id",
            "!doc": "The identifier for the module. Typically this is the fully resolved filename."
          },
          filename: {
            "!type": "string",
            "!url": "https://nodejs.org/api/modules.html#modules_module_filename",
            "!doc": "The fully resolved filename to the module."
          },
          loaded: {
            "!type": "bool",
            "!url": "https://nodejs.org/api/modules.html#modules_module_loaded",
            "!doc": "Whether or not the module is done loading, or is in the process of loading."
          },
          parent: {
            "!type": "+Module",
            "!url": "https://nodejs.org/api/modules.html#modules_module_parent",
            "!doc": "The module that required this one."
          },
          children: {
            "!type": "[+Module]",
            "!url": "https://nodejs.org/api/modules.html#modules_module_children",
            "!doc": "The module objects required by this one."
          }
        }
      },
      module: {}
    },
    module: {
      "!type": "+Module",
      "!url": "https://nodejs.org/api/globals.html#globals_module",
      "!doc": "A reference to the current module. In particular module.exports is the same as the exports object. module isn't actually a global but rather local to each module."
    }
  }
})
;
/*eslint-disable */ 
(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(require("../lib/infer"), require("../lib/tern"), require("./commonjs"), require)
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/plugin/node_resolve',["../lib/infer", "../lib/tern", "./commonjs"], mod)
  mod(tern, tern)
})(function(infer, tern, _, require) {
  "use strict"

  function resolve(name, parentFile) {
    var resolved = resolveToFile(name, parentFile)
    return resolved && infer.cx().parent.normalizeFilename(resolved)
  }
  function findDeclaredDeps() {}

  var resolveToFile
  if (require) (function() {
    var module_ = require("module"), path = require("path"), fs = require("fs")

    resolveToFile = function(name, parentFile) {
      var projectDir = infer.cx().parent.projectDir
      var fullParent = path.resolve(projectDir, parentFile)
      var parentDir = path.dirname(fullParent)
      if (/^\.\.?\//.test(name))
        name = path.resolve(projectDir, parentDir, name)

      var parentModule = {
        id: fullParent,
        paths: module_._nodeModulePaths(parentDir).concat(module_.globalPaths)
      }
      try {
        return module_._resolveFilename(name, parentModule)
      } catch(e) {
        return null
      }
    }

    function findPackageFile(dir) {
      for (;;) {
        try {
          return JSON.parse(fs.readFileSync(path.resolve(dir, "package.json")))
        } catch(e) {}
        var shorter = path.dirname(dir)
        if (shorter == dir) return null
        dir = shorter
      }
    }

    findDeclaredDeps = function(path, knownModules) {
      var packageFile = findPackageFile(path)
      if (!packageFile) return null

      function add(obj) {
        for (var name in obj) if (!(name in knownModules)) knownModules[name] = null
      }
      add(packageFile.dependencies)
      add(packageFile.devDependencies)
      add(packageFile.peerDependencies)
    }

  })(); else (function() {
    function resolvePath(base, path) {
      if (path[0] == "/") return path;
      var slash = base.lastIndexOf("/"), m;
      if (slash >= 0) path = base.slice(0, slash + 1) + path;
      while (m = /[^\/]*[^\/\.][^\/]*\/\.\.\//.exec(path))
        path = path.slice(0, m.index) + path.slice(m.index + m[0].length);
      return path.replace(/(^|[^\.])\.\//g, "$1");
    }

    resolveToFile = function(name, parentFile) {
      return /^\.\.?\//.test(name) ? resolvePath(parentFile, name) : name
    }
  })()

  tern.registerPlugin("node_resolve", function(server) {
    server.loadPlugin("commonjs")
    server.mod.modules.resolvers.push(resolve)
    findDeclaredDeps(server.projectDir, server.mod.modules.knownModules)
  })
})
;
(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(require("../lib/tern"), require("./node_resolve"));
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/plugin/node',["../lib/tern", "./node_resolve"], mod);
  mod(tern, tern);
})(function(tern) {
  "use strict"

  tern.registerPlugin("node", function(server) {
    server.loadPlugin("node_resolve")
    server.on("postReset", function() {
      var mods = server.mod.modules, locals = server.cx.definitions.node
      for (var name in locals) if (/^[a-z_]*$/.test(name))
        mods.knownModules[name] = locals[name]
    })
    server.addDefs(defs)
  })

  var defs = {
    "!name": "node",
    "!define": {
      events: {
        "!url": "https://nodejs.org/api/events.html",
        "!doc": "Many objects in Node emit events: a net.Server emits an event each time a peer connects to it, a fs.readStream emits an event when the file is opened. All objects which emit events are instances of events.EventEmitter.",
        EventEmitter: {
          prototype: {
            addListener: {
              "!type": "fn(event: string, listener: fn())",
              "!url": "https://nodejs.org/api/events.html#events_emitter_addlistener_event_listener",
              "!doc": "Adds a listener to the end of the listeners array for the specified event."
            },
            on: {
              "!type": "fn(event: string, listener: fn())",
              "!url": "https://nodejs.org/api/events.html#events_emitter_on_event_listener",
              "!doc": "Adds a listener to the end of the listeners array for the specified event."
            },
            once: {
              "!type": "fn(event: string, listener: fn())",
              "!url": "https://nodejs.org/api/events.html#events_emitter_once_event_listener",
              "!doc": "Adds a one time listener for the event. This listener is invoked only the next time the event is fired, after which it is removed."
            },
            removeListener: {
              "!type": "fn(event: string, listener: fn())",
              "!url": "https://nodejs.org/api/events.html#events_emitter_removelistener_event_listener",
              "!doc": "Remove a listener from the listener array for the specified event. Caution: changes array indices in the listener array behind the listener."
            },
            removeAllListeners: {
              "!type": "fn(event: string)",
              "!url": "https://nodejs.org/api/events.html#events_emitter_removealllisteners_event",
              "!doc": "Removes all listeners, or those of the specified event."
            },
            setMaxListeners: {
              "!type": "fn(n: number)",
              "!url": "https://nodejs.org/api/events.html#events_emitter_setmaxlisteners_n",
              "!doc": "By default EventEmitters will print a warning if more than 10 listeners are added for a particular event. This is a useful default which helps finding memory leaks. Obviously not all Emitters should be limited to 10. This function allows that to be increased. Set to zero for unlimited."
            },
            listeners: {
              "!type": "fn(event: string) -> [fn()]",
              "!url": "https://nodejs.org/api/events.html#events_emitter_listeners_event",
              "!doc": "Returns an array of listeners for the specified event."
            },
            emit: {
              "!type": "fn(event: string)",
              "!url": "https://nodejs.org/api/events.html#events_emitter_emit_event_arg1_arg2",
              "!doc": "Execute each of the listeners in order with the supplied arguments."
            }
          },
          "!url": "https://nodejs.org/api/events.html#events_class_events_eventemitter",
          "!doc": "To access the EventEmitter class, require('events').EventEmitter."
        }
      },
      stream: {
        "!type": "fn()",
        prototype: {
          "!proto": "events.EventEmitter.prototype",
          pipe: {
            "!type": "fn(destination: +stream.Writable, options?: ?)",
            "!url": "https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options",
            "!doc": "Connects this readable stream to destination WriteStream. Incoming data on this stream gets written to destination. Properly manages back-pressure so that a slow destination will not be overwhelmed by a fast readable stream."
          }
        },
        Writable: {
          "!type": "fn(options?: ?)",
          prototype: {
            "!proto": "stream.prototype",
            write: {
              "!type": "fn(chunk: string|+Buffer, encoding?: string, callback?: fn()) -> bool",
              "!url": "https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback_1",
              "!doc": "Writes chunk to the stream. Returns true if the data has been flushed to the underlying resource. Returns false to indicate that the buffer is full, and the data will be sent out in the future. The 'drain' event will indicate when the buffer is empty again."
            },
            cork: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/stream.html#stream_writable_cork",
              "!doc": "Forces buffering of all writes. Buffered data will be flushed either at .uncork() or at .end() call."
            },
            uncork: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/stream.html#stream_writable_uncork",
              "!doc": "Flush all data, buffered since .cork() call."
            },
            setDefaultEncoding: {
              "!type": "fn(encoding: string) -> bool",
              "!url": "https://nodejs.org/api/stream.html#stream_writable_setdefaultencoding_encoding",
              "!doc": "Sets the default encoding for a writable stream. Returns true if the encoding is valid and is set. Otherwise returns false."
            },
            end: {
              "!type": "fn(chunk?: string|+Buffer, encoding?: string, callback?: fn()) -> bool",
              "!url": "https://nodejs.org/api/stream.html#stream_writable_end_chunk_encoding_callback",
              "!doc": "Call this method to signal the end of the data being written to the stream."
            }
          },
          "!url": "https://nodejs.org/api/stream.html#stream_class_stream_writable",
          "!doc": "A Writable Stream has the following methods, members, and events."
        },
        Readable: {
          "!type": "fn(options?: ?)",
          prototype: {
            "!proto": "stream.prototype",
            setEncoding: {
              "!type": "fn(encoding: string)",
              "!url": "https://nodejs.org/api/stream.html#stream_readable_setencoding_encoding",
              "!doc": "Makes the 'data' event emit a string instead of a Buffer. encoding can be 'utf8', 'utf16le' ('ucs2'), 'ascii', or 'hex'."
            },
            pause: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/stream.html#stream_readable_pause",
              "!doc": "Switches the readable stream into \"old mode\", where data is emitted using a 'data' event rather than being buffered for consumption via the read() method."
            },
            resume: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/stream.html#stream_readable_resume",
              "!doc": "Switches the readable stream into \"old mode\", where data is emitted using a 'data' event rather than being buffered for consumption via the read() method."
            },
            destroy: "fn()",
            unpipe: {
              "!type": "fn(dest?: +stream.Writable)",
              "!url": "https://nodejs.org/api/stream.html#stream_readable_unpipe_destination",
              "!doc": "Undo a previously established pipe(). If no destination is provided, then all previously established pipes are removed."
            },
            push: {
              "!type": "fn(chunk: +Buffer) -> bool",
              "!url": "https://nodejs.org/api/stream.html#stream_readable_push_chunk",
              "!doc": "Explicitly insert some data into the read queue. If called with null, will signal the end of the data."
            },
            unshift: {
              "!type": "fn(chunk: +Buffer) -> bool",
              "!url": "https://nodejs.org/api/stream.html#stream_readable_unshift_chunk",
              "!doc": "This is the corollary of readable.push(chunk). Rather than putting the data at the end of the read queue, it puts it at the front of the read queue."
            },
            wrap: {
              "!type": "fn(stream: ?) -> +stream.Readable",
              "!url": "https://nodejs.org/api/stream.html#stream_readable_wrap_stream",
              "!doc": "If you are using an older Node library that emits 'data' events and has a pause() method that is advisory only, then you can use the wrap() method to create a Readable stream that uses the old stream as its data source."
            },
            read: {
              "!type": "fn(size?: number) -> +Buffer",
              "!url": "https://nodejs.org/api/stream.html#stream_readable_read_size_1",
              "!doc": "Call this method to consume data once the 'readable' event is emitted."
            }
          },
          "!url": "https://nodejs.org/api/stream.html#stream_class_stream_readable",
          "!doc": "A Readable Stream has the following methods, members, and events."
        },
        Duplex: {
          "!type": "fn(options?: ?)",
          prototype: {
            "!proto": "stream.Readable.prototype",
            write: "fn(chunk: +Buffer, encoding?: string, callback?: fn()) -> bool",
            end: "fn(chunk: +Buffer, encoding?: string, callback?: fn()) -> bool"
          },
          "!url": "https://nodejs.org/api/stream.html#stream_class_stream_duplex",
          "!doc": "A \"duplex\" stream is one that is both Readable and Writable, such as a TCP socket connection."
        },
        Transform: {
          "!type": "fn(options?: ?)",
          prototype: {
            "!proto": "stream.Duplex.prototype"
          },
          "!url": "https://nodejs.org/api/stream.html#stream_class_stream_transform",
          "!doc": "A \"transform\" stream is a duplex stream where the output is causally connected in some way to the input, such as a zlib stream or a crypto stream."
        },
        PassThrough: "stream.Transform",
        "!url": "https://nodejs.org/api/stream.html#stream_stream",
        "!doc": "A stream is an abstract interface implemented by various objects in Node. For example a request to an HTTP server is a stream, as is stdout. Streams are readable, writable, or both. All streams are instances of EventEmitter"
      },
      querystring: {
        "!url": "https://nodejs.org/api/querystring.html",
        "!doc": "This module provides utilities for dealing with query strings.",
        stringify: {
          "!type": "fn(obj: ?, sep?: string, eq?: string) -> string",
          "!url": "https://nodejs.org/api/querystring.html#querystring_querystring_stringify_obj_sep_eq",
          "!doc": "Serialize an object to a query string. Optionally override the default separator ('&') and assignment ('=') characters."
        },
        parse: {
          "!type": "fn(str: string, sep?: string, eq?: string, options?: ?) -> ?",
          "!url": "https://nodejs.org/api/querystring.html#querystring_querystring_parse_str_sep_eq_options",
          "!doc": "Deserialize a query string to an object. Optionally override the default separator ('&') and assignment ('=') characters."
        },
        escape: {
          "!type": "fn(string) -> string",
          "!url": "https://nodejs.org/api/querystring.html#querystring_querystring_escape",
          "!doc": "The escape function used by querystring.stringify, provided so that it could be overridden if necessary."
        },
        unescape: {
          "!type": "fn(string) -> string",
          "!url": "https://nodejs.org/api/querystring.html#querystring_querystring_unescape",
          "!doc": "The unescape function used by querystring.parse, provided so that it could be overridden if necessary."
        }
      },
      http: {
        "!url": "https://nodejs.org/api/http.html",
        "!doc": "The HTTP interfaces in Node are designed to support many features of the protocol which have been traditionally difficult to use. In particular, large, possibly chunk-encoded, messages. The interface is careful to never buffer entire requests or responses--the user is able to stream data.",
        STATUS_CODES: {},
        createServer: {
          "!type": "fn(listener?: fn(request: +http.IncomingMessage, response: +http.ServerResponse)) -> +http.Server",
          "!url": "https://nodejs.org/api/http.html#http_http_createserver_requestlistener",
          "!doc": "Returns a new web server object."
        },
        Server: {
          "!type": "fn()",
          prototype: {
            "!proto": "events.EventEmitter.prototype",
            listen: {
              "!type": "fn(port: number, hostname?: string, backlog?: number, callback?: fn())",
              "!url": "https://nodejs.org/api/http.html#http_server_listen_port_hostname_backlog_callback",
              "!doc": "Begin accepting connections on the specified port and hostname. If the hostname is omitted, the server will accept connections directed to any IPv4 address (INADDR_ANY)."
            },
            close: {
              "!type": "fn(callback?: ?)",
              "!url": "https://nodejs.org/api/http.html#http_server_close_callback",
              "!doc": "Stops the server from accepting new connections."
            },
            maxHeadersCount: {
              "!type": "number",
              "!url": "https://nodejs.org/api/http.html#http_server_maxheaderscount",
              "!doc": "Limits maximum incoming headers count, equal to 1000 by default. If set to 0 - no limit will be applied."
            },
            setTimeout: {
              "!type": "fn(timeout: number, callback?: fn())",
              "!url": "https://nodejs.org/api/http.html#http_server_settimeout_msecs_callback",
              "!doc": "Sets the timeout value for sockets, and emits a 'timeout' event on the Server object, passing the socket as an argument, if a timeout occurs."
            },
            timeout: {
              "!type": "number",
              "!url": "https://nodejs.org/api/http.html#http_server_timeout",
              "!doc": "The number of milliseconds of inactivity before a socket is presumed to have timed out."
            }
          },
          "!url": "https://nodejs.org/api/http.html#http_class_http_server",
          "!doc": "Class for HTTP server objects."
        },
        ServerResponse: {
          "!type": "fn()",
          prototype: {
            "!proto": "stream.Writable.prototype",
            writeContinue: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/http.html#http_response_writecontinue",
              "!doc": "Sends a HTTP/1.1 100 Continue message to the client, indicating that the request body should be sent."
            },
            writeHead: {
              "!type": "fn(statusCode: number, headers?: ?)",
              "!url": "https://nodejs.org/api/http.html#http_response_writehead_statuscode_reasonphrase_headers",
              "!doc": "Sends a response header to the request. The status code is a 3-digit HTTP status code, like 404. The last argument, headers, are the response headers. Optionally one can give a human-readable reasonPhrase as the second argument."
            },
            setTimeout: {
              "!type": "fn(timeout: number, callback?: fn())",
              "!url": "https://nodejs.org/api/http.html#http_response_settimeout_msecs_callback",
              "!doc": "Sets the Socket's timeout value to msecs. If a callback is provided, then it is added as a listener on the 'timeout' event on the response object."
            },
            statusCode: {
              "!type": "number",
              "!url": "https://nodejs.org/api/http.html#http_response_statuscode",
              "!doc": "When using implicit headers (not calling response.writeHead() explicitly), this property controls the status code that will be sent to the client when the headers get flushed."
            },
            setHeader: {
              "!type": "fn(name: string, value: string)",
              "!url": "https://nodejs.org/api/http.html#http_response_setheader_name_value",
              "!doc": "Sets a single header value for implicit headers. If this header already exists in the to-be-sent headers, its value will be replaced. Use an array of strings here if you need to send multiple headers with the same name."
            },
            headersSent: {
              "!type": "bool",
              "!url": "https://nodejs.org/api/http.html#http_response_headerssent",
              "!doc": "Boolean (read-only). True if headers were sent, false otherwise."
            },
            sendDate: {
              "!type": "bool",
              "!url": "https://nodejs.org/api/http.html#http_response_senddate",
              "!doc": "When true, the Date header will be automatically generated and sent in the response if it is not already present in the headers. Defaults to true."
            },
            getHeader: {
              "!type": "fn(name: string) -> string",
              "!url": "https://nodejs.org/api/http.html#http_response_getheader_name",
              "!doc": "Reads out a header that's already been queued but not sent to the client. Note that the name is case insensitive. This can only be called before headers get implicitly flushed."
            },
            removeHeader: {
              "!type": "fn(name: string)",
              "!url": "https://nodejs.org/api/http.html#http_response_removeheader_name",
              "!doc": "Removes a header that's queued for implicit sending."
            },
            addTrailers: {
              "!type": "fn(headers: ?)",
              "!url": "https://nodejs.org/api/http.html#http_response_addtrailers_headers",
              "!doc": "This method adds HTTP trailing headers (a header but at the end of the message) to the response."
            }
          },
          "!url": "https://nodejs.org/api/http.html#http_class_http_serverresponse",
          "!doc": "This object is created internally by a HTTP server--not by the user. It is passed as the second parameter to the 'request' event."
        },
        request: {
          "!type": "fn(options: ?, callback?: fn(res: +http.IncomingMessage)) -> +http.ClientRequest",
          "!url": "https://nodejs.org/api/http.html#http_http_request_options_callback",
          "!doc": "Node maintains several connections per server to make HTTP requests. This function allows one to transparently issue requests."
        },
        get: {
          "!type": "fn(options: ?, callback?: fn(res: +http.IncomingMessage)) -> +http.ClientRequest",
          "!url": "https://nodejs.org/api/http.html#http_http_get_options_callback",
          "!doc": "Since most requests are GET requests without bodies, Node provides this convenience method. The only difference between this method and http.request() is that it sets the method to GET and calls req.end() automatically."
        },
        globalAgent: {
          "!type": "+http.Agent",
          "!url": "https://nodejs.org/api/http.html#http_http_globalagent",
          "!doc": "Global instance of Agent which is used as the default for all http client requests."
        },
        Agent: {
          "!type": "fn()",
          prototype: {
            maxSockets: {
              "!type": "number",
              "!url": "https://nodejs.org/api/http.html#http_agent_maxsockets",
              "!doc": "By default set to 5. Determines how many concurrent sockets the agent can have open per host."
            },
            sockets: {
              "!type": "[+net.Socket]",
              "!url": "https://nodejs.org/api/http.html#http_agent_sockets",
              "!doc": "An object which contains arrays of sockets currently in use by the Agent. Do not modify."
            },
            requests: {
              "!type": "[+http.ClientRequest]",
              "!url": "https://nodejs.org/api/http.html#http_agent_requests",
              "!doc": "An object which contains queues of requests that have not yet been assigned to sockets. Do not modify."
            }
          },
          "!url": "https://nodejs.org/api/http.html#http_class_http_agent",
          "!doc": "In node 0.5.3+ there is a new implementation of the HTTP Agent which is used for pooling sockets used in HTTP client requests."
        },
        ClientRequest: {
          "!type": "fn()",
          prototype: {
            "!proto": "stream.Writable.prototype",
            abort: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/http.html#http_request_abort",
              "!doc": "Aborts a request. (New since v0.3.8.)"
            },
            setTimeout: {
              "!type": "fn(timeout: number, callback?: fn())",
              "!url": "https://nodejs.org/api/http.html#http_request_settimeout_timeout_callback",
              "!doc": "Once a socket is assigned to this request and is connected socket.setTimeout() will be called."
            },
            setNoDelay: {
              "!type": "fn(noDelay?: fn())",
              "!url": "https://nodejs.org/api/http.html#http_request_setnodelay_nodelay",
              "!doc": "Once a socket is assigned to this request and is connected socket.setNoDelay() will be called."
            },
            setSocketKeepAlive: {
              "!type": "fn(enable?: bool, initialDelay?: number)",
              "!url": "https://nodejs.org/api/http.html#http_request_setsocketkeepalive_enable_initialdelay",
              "!doc": "Once a socket is assigned to this request and is connected socket.setKeepAlive() will be called."
            }
          },
          "!url": "https://nodejs.org/api/http.html#http_class_http_clientrequest",
          "!doc": "This object is created internally and returned from http.request(). It represents an in-progress request whose header has already been queued. The header is still mutable using the setHeader(name, value), getHeader(name), removeHeader(name) API. The actual header will be sent along with the first data chunk or when closing the connection."
        },
        IncomingMessage: {
          "!type": "fn()",
          prototype: {
            "!proto": "stream.Readable.prototype",
            httpVersion: {
              "!type": "string",
              "!url": "https://nodejs.org/api/http.html#http_message_httpversion",
              "!doc": "In case of server request, the HTTP version sent by the client. In the case of client response, the HTTP version of the connected-to server. Probably either '1.1' or '1.0'."
            },
            headers: {
              "!type": "?",
              "!url": "https://nodejs.org/api/http.html#http_message_headers",
              "!doc": "The request/response headers object."
            },
            trailers: {
              "!type": "?",
              "!url": "https://nodejs.org/api/http.html#http_message_trailers",
              "!doc": "The request/response trailers object. Only populated after the 'end' event."
            },
            setTimeout: {
              "!type": "fn(timeout: number, callback?: fn())",
              "!url": "https://nodejs.org/api/http.html#http_message_settimeout_msecs_callback",
              "!doc": "Calls message.connection.setTimeout(msecs, callback)."
            },
            setEncoding: {
              "!type": "fn(encoding?: string)",
              "!url": "https://nodejs.org/api/http.html#http_message_setencoding_encoding",
              "!doc": "Set the encoding for data emitted by the 'data' event."
            },
            pause: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/http.html#http_message_pause",
              "!doc": "Pauses request/response from emitting events. Useful to throttle back a download."
            },
            resume: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/http.html#http_message_resume",
              "!doc": "Resumes a paused request/response."
            },
            method: {
              "!type": "string",
              "!url": "https://nodejs.org/api/http.html#http_message_method",
              "!doc": "Only valid for request obtained from http.Server."
            },
            url: {
              "!type": "string",
              "!url": "https://nodejs.org/api/http.html#http_message_url",
              "!doc": "Only valid for request obtained from http.Server."
            },
            statusCode: {
              "!type": "number",
              "!url": "https://nodejs.org/api/http.html#http_message_statuscode",
              "!doc": "Only valid for response obtained from http.ClientRequest."
            },
            socket: {
              "!type": "+net.Socket",
              "!url": "https://nodejs.org/api/http.html#http_message_socket",
              "!doc": "The net.Socket object associated with the connection."
            }
          },
          "!url": "https://nodejs.org/api/http.html#http_http_incomingmessage",
          "!doc": "An IncomingMessage object is created by http.Server or http.ClientRequest and passed as the first argument to the 'request' and 'response' event respectively. It may be used to access response status, headers and data."
        }
      },
      https: {
        "!url": "https://nodejs.org/api/http.html",
        "!doc": "HTTPS is the HTTP protocol over TLS/SSL. In Node this is implemented as a separate module.",
        Server: "http.Server",
        createServer: {
          "!type": "fn(listener?: fn(request: +http.IncomingMessage, response: +http.ServerResponse)) -> +https.Server",
          "!url": "https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener",
          "!doc": "Returns a new HTTPS web server object. The options is similar to tls.createServer(). The requestListener is a function which is automatically added to the 'request' event."
        },
        request: {
          "!type": "fn(options: ?, callback?: fn(res: +http.IncomingMessage)) -> +http.ClientRequest",
          "!url": "https://nodejs.org/api/https.html#https_https_request_options_callback",
          "!doc": "Makes a request to a secure web server."
        },
        get: {
          "!type": "fn(options: ?, callback?: fn(res: +http.IncomingMessage)) -> +http.ClientRequest",
          "!url": "https://nodejs.org/api/https.html#https_https_get_options_callback",
          "!doc": "Like http.get() but for HTTPS."
        },
        Agent: "http.Agent",
        globalAgent: "http.globalAgent"
      },
      cluster: {
        "!proto": "events.EventEmitter.prototype",
        settings: {
          exec: "string",
          args: "[string]",
          silent: "bool",
          "!url": "https://nodejs.org/api/cluster.html#cluster_cluster_settings",
          "!doc": "All settings set by the .setupMaster is stored in this settings object. This object is not supposed to be changed or set manually, by you."
        },
        Worker: {
          "!type": "fn()",
          prototype: {
            "!proto": "events.EventEmitter.prototype",
            id: {
              "!type": "string",
              "!url": "https://nodejs.org/api/cluster.html#cluster_worker_id",
              "!doc": "Each new worker is given its own unique id, this id is stored in the id."
            },
            process: {
              "!type": "+child_process.ChildProcess",
              "!url": "https://nodejs.org/api/cluster.html#cluster_worker_process",
              "!doc": "All workers are created using child_process.fork(), the returned object from this function is stored in process."
            },
            suicide: {
              "!type": "bool",
              "!url": "https://nodejs.org/api/cluster.html#cluster_worker_suicide",
              "!doc": "This property is a boolean. It is set when a worker dies after calling .kill() or immediately after calling the .disconnect() method. Until then it is undefined."
            },
            send: {
              "!type": "fn(message: ?, sendHandle?: ?)",
              "!url": "https://nodejs.org/api/cluster.html#cluster_worker_send_message_sendhandle",
              "!doc": "This function is equal to the send methods provided by child_process.fork(). In the master you should use this function to send a message to a specific worker. However in a worker you can also use process.send(message), since this is the same function."
            },
            destroy: "fn()",
            disconnect: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/cluster.html#cluster_worker_disconnect",
              "!doc": "When calling this function the worker will no longer accept new connections, but they will be handled by any other listening worker. Existing connection will be allowed to exit as usual. When no more connections exist, the IPC channel to the worker will close allowing it to die graceful. When the IPC channel is closed the disconnect event will emit, this is then followed by the exit event, there is emitted when the worker finally die."
            },
            kill: {
              "!type": "fn(signal?: string)",
              "!url": "https://nodejs.org/api/cluster.html#cluster_worker_kill_signal_sigterm",
              "!doc": "This function will kill the worker, and inform the master to not spawn a new worker. The boolean suicide lets you distinguish between voluntary and accidental exit."
            }
          },
          "!url": "https://nodejs.org/api/cluster.html#cluster_class_worker",
          "!doc": "A Worker object contains all public information and method about a worker. In the master it can be obtained using cluster.workers. In a worker it can be obtained using cluster.worker."
        },
        isMaster: {
          "!type": "bool",
          "!url": "https://nodejs.org/api/cluster.html#cluster_cluster_ismaster",
          "!doc": "True if the process is a master. This is determined by the process.env.NODE_UNIQUE_ID. If process.env.NODE_UNIQUE_ID is undefined, then isMaster is true."
        },
        isWorker: {
          "!type": "bool",
          "!url": "https://nodejs.org/api/cluster.html#cluster_cluster_isworker",
          "!doc": "This boolean flag is true if the process is a worker forked from a master. If the process.env.NODE_UNIQUE_ID is set to a value, then isWorker is true."
        },
        setupMaster: {
          "!type": "fn(settings?: cluster.settings)",
          "!url": "https://nodejs.org/api/cluster.html#cluster_cluster_setupmaster_settings",
          "!doc": "setupMaster is used to change the default 'fork' behavior. The new settings are effective immediately and permanently, they cannot be changed later on."
        },
        fork: {
          "!type": "fn(env?: ?) -> +cluster.Worker",
          "!url": "https://nodejs.org/api/cluster.html#cluster_cluster_fork_env",
          "!doc": "Spawn a new worker process. This can only be called from the master process."
        },
        disconnect: {
          "!type": "fn(callback?: fn())",
          "!url": "https://nodejs.org/api/cluster.html#cluster_cluster_disconnect_callback",
          "!doc": "When calling this method, all workers will commit a graceful suicide. When they are disconnected all internal handlers will be closed, allowing the master process to die graceful if no other event is waiting."
        },
        worker: {
          "!type": "+cluster.Worker",
          "!url": "https://nodejs.org/api/cluster.html#cluster_cluster_worker",
          "!doc": "A reference to the current worker object. Not available in the master process."
        },
        workers: {
          "!type": "[+cluster.Worker]",
          "!url": "https://nodejs.org/api/cluster.html#cluster_cluster_workers",
          "!doc": "A hash that stores the active worker objects, keyed by id field. Makes it easy to loop through all the workers. It is only available in the master process."
        },
        "!url": "https://nodejs.org/api/cluster.html#cluster_cluster",
        "!doc": "A single instance of Node runs in a single thread. To take advantage of multi-core systems the user will sometimes want to launch a cluster of Node processes to handle the load."
      },
      zlib: {
        "!url": "https://nodejs.org/api/zlib.html",
        "!doc": "This provides bindings to Gzip/Gunzip, Deflate/Inflate, and DeflateRaw/InflateRaw classes. Each class takes the same options, and is a readable/writable Stream.",
        Zlib: {
          "!type": "fn()",
          prototype: {
            "!proto": "stream.Duplex.prototype",
            flush: {
              "!type": "fn(callback: fn())",
              "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_flush_callback",
              "!doc": "Flush pending data. Don't call this frivolously, premature flushes negatively impact the effectiveness of the compression algorithm."
            },
            reset: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_reset",
              "!doc": "Reset the compressor/decompressor to factory defaults. Only applicable to the inflate and deflate algorithms."
            }
          },
          "!url": "https://nodejs.org/api/zlib.html#zlib_class_zlib_zlib",
          "!doc": "Not exported by the zlib module. It is documented here because it is the base class of the compressor/decompressor classes."
        },
        deflate: {
          "!type": "fn(buf: +Buffer, callback: fn())",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_deflate_buf_callback",
          "!doc": "Compress a string with Deflate."
        },
        deflateRaw: {
          "!type": "fn(buf: +Buffer, callback: fn())",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_deflateraw_buf_callback",
          "!doc": "Compress a string with DeflateRaw."
        },
        gzip: {
          "!type": "fn(buf: +Buffer, callback: fn())",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_gzip_buf_callback",
          "!doc": "Compress a string with Gzip."
        },
        gunzip: {
          "!type": "fn(buf: +Buffer, callback: fn())",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_gunzip_buf_callback",
          "!doc": "Decompress a raw Buffer with Gunzip."
        },
        inflate: {
          "!type": "fn(buf: +Buffer, callback: fn())",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_inflate_buf_callback",
          "!doc": "Decompress a raw Buffer with Inflate."
        },
        inflateRaw: {
          "!type": "fn(buf: +Buffer, callback: fn())",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_inflateraw_buf_callback",
          "!doc": "Decompress a raw Buffer with InflateRaw."
        },
        unzip: {
          "!type": "fn(buf: +Buffer, callback: fn())",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_unzip_buf_callback",
          "!doc": "Decompress a raw Buffer with Unzip."
        },
        Gzip: {
          "!type": "fn()",
          "!url": "https://nodejs.org/api/zlib.html#zlib_class_zlib_gzip",
          "!doc": "Compress data using gzip.",
          prototype: {"!proto:": "zlib.Zlib.prototype"}
        },
        createGzip: {
          "!type": "fn(options: ?) -> +zlib.Zlib",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_creategzip_options",
          "!doc": "Returns a new Gzip object with an options."
        },
        Gunzip: {
          "!type": "fn()",
          "!url": "https://nodejs.org/api/zlib.html#zlib_class_zlib_gunzip",
          "!doc": "Decompress a gzip stream.",
          prototype: {"!proto:": "zlib.Zlib.prototype"}
        },
        createGunzip: {
          "!type": "fn(options: ?) -> +zlib.Gunzip",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_creategunzip_options",
          "!doc": "Returns a new Gunzip object with an options."
        },
        Deflate: {
          "!type": "fn()",
          "!url": "https://nodejs.org/api/zlib.html#zlib_class_zlib_deflate",
          "!doc": "Compress data using deflate.",
          prototype: {"!proto:": "zlib.Zlib.prototype"}
        },
        createDeflate: {
          "!type": "fn(options: ?) -> +zlib.Deflate",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_createdeflate_options",
          "!doc": "Returns a new Deflate object with an options."
        },
        Inflate: {
          "!type": "fn()",
          "!url": "https://nodejs.org/api/zlib.html#zlib_class_zlib_inflate",
          "!doc": "Decompress a deflate stream.",
          prototype: {"!proto:": "zlib.Zlib.prototype"}
        },
        createInflate: {
          "!type": "fn(options: ?) -> +zlib.Inflate",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_createinflate_options",
          "!doc": "Returns a new Inflate object with an options."
        },
        InflateRaw: {
          "!type": "fn()",
          "!url": "https://nodejs.org/api/zlib.html#zlib_class_zlib_inflateraw",
          "!doc": "Decompress a raw deflate stream.",
          prototype: {"!proto:": "zlib.Zlib.prototype"}
        },
        createInflateRaw: {
          "!type": "fn(options: ?) -> +zlib.InflateRaw",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_createinflateraw_options",
          "!doc": "Returns a new InflateRaw object with an options."
        },
        DeflateRaw: {
          "!type": "fn()",
          "!url": "https://nodejs.org/api/zlib.html#zlib_class_zlib_deflateraw",
          "!doc": "Compress data using deflate, and do not append a zlib header.",
          prototype: {"!proto:": "zlib.Zlib.prototype"}
        },
        createDeflateRaw: {
          "!type": "fn(options: ?) -> +zlib.DeflateRaw",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options",
          "!doc": "Returns a new DeflateRaw object with an options."
        },
        Unzip: {
          "!type": "fn()",
          "!url": "https://nodejs.org/api/zlib.html#zlib_class_zlib_unzip",
          "!doc": "Decompress either a Gzip- or Deflate-compressed stream by auto-detecting the header.",
          prototype: {"!proto:": "zlib.Zlib.prototype"}
        },
        createUnzip: {
          "!type": "fn(options: ?) -> +zlib.Unzip",
          "!url": "https://nodejs.org/api/zlib.html#zlib_zlib_createunzip_options",
          "!doc": "Returns a new Unzip object with an options."
        },
        Z_NO_FLUSH: "number",
        Z_PARTIAL_FLUSH: "number",
        Z_SYNC_FLUSH: "number",
        Z_FULL_FLUSH: "number",
        Z_FINISH: "number",
        Z_BLOCK: "number",
        Z_TREES: "number",
        Z_OK: "number",
        Z_STREAM_END: "number",
        Z_NEED_DICT: "number",
        Z_ERRNO: "number",
        Z_STREAM_ERROR: "number",
        Z_DATA_ERROR: "number",
        Z_MEM_ERROR: "number",
        Z_BUF_ERROR: "number",
        Z_VERSION_ERROR: "number",
        Z_NO_COMPRESSION: "number",
        Z_BEST_SPEED: "number",
        Z_BEST_COMPRESSION: "number",
        Z_DEFAULT_COMPRESSION: "number",
        Z_FILTERED: "number",
        Z_HUFFMAN_ONLY: "number",
        Z_RLE: "number",
        Z_FIXED: "number",
        Z_DEFAULT_STRATEGY: "number",
        Z_BINARY: "number",
        Z_TEXT: "number",
        Z_ASCII: "number",
        Z_UNKNOWN: "number",
        Z_DEFLATED: "number",
        Z_NULL: "number"
      },
      os: {
        "!url": "https://nodejs.org/api/os.html",
        "!doc": "Provides a few basic operating-system related utility functions.",
        tmpdir: {
          "!type": "fn() -> string",
          "!url": "https://nodejs.org/api/os.html#os_os_tmpdir",
          "!doc": "Returns the operating system's default directory for temp files."
        },
        endianness: {
          "!type": "fn() -> string",
          "!url": "https://nodejs.org/api/os.html#os_os_endianness",
          "!doc": "Returns the endianness of the CPU. Possible values are \"BE\" or \"LE\"."
        },
        hostname: {
          "!type": "fn() -> string",
          "!url": "https://nodejs.org/api/os.html#os_os_hostname",
          "!doc": "Returns the hostname of the operating system."
        },
        type: {
          "!type": "fn() -> string",
          "!url": "https://nodejs.org/api/os.html#os_os_type",
          "!doc": "Returns the operating system name."
        },
        platform: {
          "!type": "fn() -> string",
          "!url": "https://nodejs.org/api/os.html#os_os_platform",
          "!doc": "Returns the operating system platform."
        },
        arch: {
          "!type": "fn() -> string",
          "!url": "https://nodejs.org/api/os.html#os_os_arch",
          "!doc": "Returns the operating system CPU architecture."
        },
        release: {
          "!type": "fn() -> string",
          "!url": "https://nodejs.org/api/os.html#os_os_release",
          "!doc": "Returns the operating system release."
        },
        uptime: {
          "!type": "fn() -> number",
          "!url": "https://nodejs.org/api/os.html#os_os_uptime",
          "!doc": "Returns the system uptime in seconds."
        },
        loadavg: {
          "!type": "fn() -> [number]",
          "!url": "https://nodejs.org/api/os.html#os_os_loadavg",
          "!doc": "Returns an array containing the 1, 5, and 15 minute load averages."
        },
        totalmem: {
          "!type": "fn() -> number",
          "!url": "https://nodejs.org/api/os.html#os_os_totalmem",
          "!doc": "Returns the total amount of system memory in bytes."
        },
        freemem: {
          "!type": "fn() -> number",
          "!url": "https://nodejs.org/api/os.html#os_os_freemem",
          "!doc": "Returns the amount of free system memory in bytes."
        },
        cpus: {
          "!type": "fn() -> [os.cpuSpec]",
          "!url": "https://nodejs.org/api/os.html#os_os_cpus",
          "!doc": "Returns an array of objects containing information about each CPU/core installed: model, speed (in MHz), and times (an object containing the number of milliseconds the CPU/core spent in: user, nice, sys, idle, and irq)."
        },
        networkInterfaces: {
          "!type": "fn() -> ?",
          "!url": "https://nodejs.org/api/os.html#os_os_networkinterfaces",
          "!doc": "Get a list of network interfaces."
        },
        EOL: {
          "!type": "string",
          "!url": "https://nodejs.org/api/os.html#os_os_eol",
          "!doc": "A constant defining the appropriate End-of-line marker for the operating system."
        }
      },
      punycode: {
        "!url": "https://nodejs.org/api/punycode.html",
        "!doc": "Punycode.js is bundled with Node.js v0.6.2+. Use require('punycode') to access it. (To use it with other Node.js versions, use npm to install the punycode module first.)",
        decode: {
          "!type": "fn(string: string) -> string",
          "!url": "https://nodejs.org/api/punycode.html#punycode_punycode_decode_string",
          "!doc": "Converts a Punycode string of ASCII code points to a string of Unicode code points."
        },
        encode: {
          "!type": "fn(string: string) -> string",
          "!url": "https://nodejs.org/api/punycode.html#punycode_punycode_encode_string",
          "!doc": "Converts a string of Unicode code points to a Punycode string of ASCII code points."
        },
        toUnicode: {
          "!type": "fn(domain: string) -> string",
          "!url": "https://nodejs.org/api/punycode.html#punycode_punycode_tounicode_domain",
          "!doc": "Converts a Punycode string representing a domain name to Unicode. Only the Punycoded parts of the domain name will be converted, i.e. it doesn't matter if you call it on a string that has already been converted to Unicode."
        },
        toASCII: {
          "!type": "fn(domain: string) -> string",
          "!url": "https://nodejs.org/api/punycode.html#punycode_punycode_toascii_domain",
          "!doc": "Converts a Unicode string representing a domain name to Punycode. Only the non-ASCII parts of the domain name will be converted, i.e. it doesn't matter if you call it with a domain that's already in ASCII."
        },
        ucs2: {
          decode: {
            "!type": "fn(string: string) -> string",
            "!url": "https://nodejs.org/api/punycode.html#punycode_punycode_ucs2_decode_string",
            "!doc": "Creates an array containing the decimal code points of each Unicode character in the string. While JavaScript uses UCS-2 internally, this function will convert a pair of surrogate halves (each of which UCS-2 exposes as separate characters) into a single code point, matching UTF-16."
          },
          encode: {
            "!type": "fn(codePoints: [number]) -> string",
            "!url": "https://nodejs.org/api/punycode.html#punycode_punycode_ucs2_encode_codepoints",
            "!doc": "Creates a string based on an array of decimal code points."
          }
        },
        version: {
          "!type": "?",
          "!url": "https://nodejs.org/api/punycode.html#punycode_punycode_version",
          "!doc": "A string representing the current Punycode.js version number."
        }
      },
      repl: {
        "!url": "https://nodejs.org/api/repl.html",
        "!doc": "A Read-Eval-Print-Loop (REPL) is available both as a standalone program and easily includable in other programs. The REPL provides a way to interactively run JavaScript and see the results. It can be used for debugging, testing, or just trying things out.",
        start: {
          "!type": "fn(options: ?) -> +events.EventEmitter",
          "!url": "https://nodejs.org/api/repl.html#repl_repl_start_options",
          "!doc": "Returns and starts a REPLServer instance."
        }
      },
      readline: {
        "!url": "https://nodejs.org/api/readline.html",
        "!doc": "Readline allows reading of a stream (such as process.stdin) on a line-by-line basis.",
        createInterface: {
          "!type": "fn(options: ?) -> +readline.Interface",
          "!url": "https://nodejs.org/api/readline.html#readline_readline_createinterface_options",
          "!doc": "Creates a readline Interface instance."
        },
        Interface: {
          "!type": "fn()",
          prototype: {
            "!proto": "events.EventEmitter.prototype",
            setPrompt: {
              "!type": "fn(prompt: string, length: number)",
              "!url": "https://nodejs.org/api/readline.html#readline_rl_setprompt_prompt_length",
              "!doc": "Sets the prompt, for example when you run node on the command line, you see > , which is node's prompt."
            },
            prompt: {
              "!type": "fn(preserveCursor?: bool)",
              "!url": "https://nodejs.org/api/readline.html#readline_rl_prompt_preservecursor",
              "!doc": "Readies readline for input from the user, putting the current setPrompt options on a new line, giving the user a new spot to write. Set preserveCursor to true to prevent the cursor placement being reset to 0."
            },
            question: {
              "!type": "fn(query: string, callback: fn())",
              "!url": "https://nodejs.org/api/readline.html#readline_rl_question_query_callback",
              "!doc": "Prepends the prompt with query and invokes callback with the user's response. Displays the query to the user, and then invokes callback with the user's response after it has been typed."
            },
            pause: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/readline.html#readline_rl_pause",
              "!doc": "Pauses the readline input stream, allowing it to be resumed later if needed."
            },
            resume: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/readline.html#readline_rl_resume",
              "!doc": "Resumes the readline input stream."
            },
            close: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/readline.html#readline_rl_close",
              "!doc": "Closes the Interface instance, relinquishing control on the input and output streams. The \"close\" event will also be emitted."
            },
            write: {
              "!type": "fn(data: ?, key?: ?)",
              "!url": "https://nodejs.org/api/readline.html#readline_rl_write_data_key",
              "!doc": "Writes data to output stream. key is an object literal to represent a key sequence; available if the terminal is a TTY."
            }
          },
          "!url": "https://nodejs.org/api/readline.html#readline_class_interface",
          "!doc": "The class that represents a readline interface with an input and output stream."
        }
      },
      vm: {
        "!url": "https://nodejs.org/api/vm.html",
        "!doc": "JavaScript code can be compiled and run immediately or compiled, saved, and run later.",
        createContext: {
          "!type": "fn(initSandbox?: ?) -> ?",
          "!url": "https://nodejs.org/api/vm.html#vm_vm_createcontext_initsandbox",
          "!doc": "vm.createContext creates a new context which is suitable for use as the 2nd argument of a subsequent call to vm.runInContext. A (V8) context comprises a global object together with a set of build-in objects and functions. The optional argument initSandbox will be shallow-copied to seed the initial contents of the global object used by the context."
        },
        Script: {
          "!type": "fn()",
          prototype: {
            runInThisContext: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/vm.html#vm_script_runinthiscontext",
              "!doc": "Similar to vm.runInThisContext but a method of a precompiled Script object. script.runInThisContext runs the code of script and returns the result. Running code does not have access to local scope, but does have access to the global object (v8: in actual context)."
            },
            runInNewContext: {
              "!type": "fn(sandbox?: ?)",
              "!url": "https://nodejs.org/api/vm.html#vm_script_runinnewcontext_sandbox",
              "!doc": "Similar to vm.runInNewContext a method of a precompiled Script object. script.runInNewContext runs the code of script with sandbox as the global object and returns the result. Running code does not have access to local scope. sandbox is optional."
            }
          },
          "!url": "https://nodejs.org/api/vm.html#vm_class_script",
          "!doc": "A class for running scripts. Returned by vm.createScript."
        },
        runInThisContext: {
          "!type": "fn(code: string, filename?: string)",
          "!url": "https://nodejs.org/api/vm.html#vm_vm_runinthiscontext_code_filename",
          "!doc": "vm.runInThisContext() compiles code, runs it and returns the result. Running code does not have access to local scope. filename is optional, it's used only in stack traces."
        },
        runInNewContext: {
          "!type": "fn(code: string, sandbox?: ?, filename?: string)",
          "!url": "https://nodejs.org/api/vm.html#vm_vm_runinnewcontext_code_sandbox_filename",
          "!doc": "vm.runInNewContext compiles code, then runs it in sandbox and returns the result. Running code does not have access to local scope. The object sandbox will be used as the global object for code. sandbox and filename are optional, filename is only used in stack traces."
        },
        runInContext: {
          "!type": "fn(code: string, context: ?, filename?: string)",
          "!url": "https://nodejs.org/api/vm.html#vm_vm_runincontext_code_context_filename",
          "!doc": "vm.runInContext compiles code, then runs it in context and returns the result. A (V8) context comprises a global object, together with a set of built-in objects and functions. Running code does not have access to local scope and the global object held within context will be used as the global object for code. filename is optional, it's used only in stack traces."
        },
        createScript: {
          "!type": "fn(code: string, filename?: string) -> +vm.Script",
          "!url": "https://nodejs.org/api/vm.html#vm_vm_createscript_code_filename",
          "!doc": "createScript compiles code but does not run it. Instead, it returns a vm.Script object representing this compiled code. This script can be run later many times using methods below. The returned script is not bound to any global object. It is bound before each run, just for that run. filename is optional, it's only used in stack traces."
        }
      },
      child_process: {
        "!url": "https://nodejs.org/api/child_process.html",
        "!doc": "Node provides a tri-directional popen(3) facility through the child_process module.",
        ChildProcess: {
          "!type": "fn()",
          prototype: {
            "!proto": "events.EventEmitter.prototype",
            stdin: {
              "!type": "+stream.Writable",
              "!url": "https://nodejs.org/api/child_process.html#child_process_child_stdin",
              "!doc": "A Writable Stream that represents the child process's stdin. Closing this stream via end() often causes the child process to terminate."
            },
            stdout: {
              "!type": "+stream.Readable",
              "!url": "https://nodejs.org/api/child_process.html#child_process_child_stdout",
              "!doc": "A Readable Stream that represents the child process's stdout."
            },
            stderr: {
              "!type": "+stream.Readable",
              "!url": "https://nodejs.org/api/child_process.html#child_process_child_stderr",
              "!doc": "A Readable Stream that represents the child process's stderr."
            },
            pid: {
              "!type": "number",
              "!url": "https://nodejs.org/api/child_process.html#child_process_child_pid",
              "!doc": "The PID of the child process."
            },
            kill: {
              "!type": "fn(signal?: string)",
              "!url": "https://nodejs.org/api/child_process.html#child_process_child_kill_signal",
              "!doc": "Send a signal to the child process. If no argument is given, the process will be sent 'SIGTERM'."
            },
            send: {
              "!type": "fn(message: ?, sendHandle?: ?)",
              "!url": "https://nodejs.org/api/child_process.html#child_process_child_send_message_sendhandle",
              "!doc": "When using child_process.fork() you can write to the child using child.send(message, [sendHandle]) and messages are received by a 'message' event on the child."
            },
            disconnect: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/child_process.html#child_process_child_disconnect",
              "!doc": "To close the IPC connection between parent and child use the child.disconnect() method. This allows the child to exit gracefully since there is no IPC channel keeping it alive. When calling this method the disconnect event will be emitted in both parent and child, and the connected flag will be set to false. Please note that you can also call process.disconnect() in the child process."
            }
          },
          "!url": "https://nodejs.org/api/child_process.html#child_process_class_childprocess",
          "!doc": "ChildProcess is an EventEmitter."
        },
        spawn: {
          "!type": "fn(command: string, args?: [string], options?: ?) -> +child_process.ChildProcess",
          "!url": "https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options",
          "!doc": "Launches a new process with the given command, with command line arguments in args. If omitted, args defaults to an empty Array."
        },
        exec: {
          "!type": "fn(command: string, callback: fn(error: ?, stdout: +Buffer, stderr: +Buffer)) -> +child_process.ChildProcess",
          "!url": "https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback",
          "!doc": "Runs a command in a shell and buffers the output."
        },
        execFile: {
          "!type": "fn(file: string, args: [string], options: ?, callback: fn(error: ?, stdout: +Buffer, stderr: +Buffer)) -> +child_process.ChildProcess",
          "!url": "https://nodejs.org/api/child_process.html#child_process_child_process_execfile_file_args_options_callback",
          "!doc": "This is similar to child_process.exec() except it does not execute a subshell but rather the specified file directly. This makes it slightly leaner than child_process.exec. It has the same options."
        },
        fork: {
          "!type": "fn(modulePath: string, args?: [string], options?: ?) -> +child_process.ChildProcess",
          "!url": "https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options",
          "!doc": "This is a special case of the spawn() functionality for spawning Node processes. In addition to having all the methods in a normal ChildProcess instance, the returned object has a communication channel built-in."
        }
      },
      url: {
        "!url": "https://nodejs.org/api/url.html",
        "!doc": "This module has utilities for URL resolution and parsing. ",
        parse: {
          "!type": "fn(urlStr: string, parseQueryString?: bool, slashesDenoteHost?: bool) -> url.type",
          "!url": "https://nodejs.org/api/url.html#url_url_parse_urlstr_parsequerystring_slashesdenotehost",
          "!doc": "Take a URL string, and return an object."
        },
        format: {
          "!type": "fn(url: url.type) -> string",
          "!url": "https://nodejs.org/api/url.html#url_url_format_urlobj",
          "!doc": "Take a parsed URL object, and return a formatted URL string."
        },
        resolve: {
          "!type": "fn(from: string, to: string) -> string",
          "!url": "https://nodejs.org/api/url.html#url_url_resolve_from_to",
          "!doc": "Take a base URL, and a href URL, and resolve them as a browser would for an anchor tag."
        }
      },
      dns: {
        "!url": "https://nodejs.org/api/dns.html",
        "!doc": "This module contains functions that belong to two different categories:\n1) Functions that use the underlying operating system facilities to perform name resolution, and that do not necessarily do any network communication.\n2) Functions that connect to an actual DNS server to perform name resolution, and that always use the network to perform DNS queries.",
        lookup: {
          "!type": "fn(domain: string, callback: fn(err: +Error, address: string, family: number)) -> string",
          "!url": "https://nodejs.org/api/dns.html#dns_dns_lookup_domain_family_callback",
          "!doc": "Resolves a domain (e.g. 'google.com') into the first found A (IPv4) or AAAA (IPv6) record. The family can be the integer 4 or 6. Defaults to null that indicates both Ip v4 and v6 address family."
        },
        resolve: {
          "!type": "fn(domain: string, callback: fn(err: +Error, addresses: [string])) -> [string]",
          "!url": "https://nodejs.org/api/dns.html#dns_dns_resolve_domain_rrtype_callback",
          "!doc": "Resolves a domain (e.g. 'google.com') into an array of the record types specified by rrtype. Valid rrtypes are 'A' (IPV4 addresses, default), 'AAAA' (IPV6 addresses), 'MX' (mail exchange records), 'TXT' (text records), 'SRV' (SRV records), 'PTR' (used for reverse IP lookups), 'NS' (name server records) and 'CNAME' (canonical name records)."
        },
        resolve4: {
          "!type": "fn(domain: string, callback: fn(err: +Error, addresses: [string])) -> [string]",
          "!url": "https://nodejs.org/api/dns.html#dns_dns_resolve4_domain_callback",
          "!doc": "The same as dns.resolve(), but only for IPv4 queries (A records). addresses is an array of IPv4 addresses (e.g. ['74.125.79.104', '74.125.79.105', '74.125.79.106'])."
        },
        resolve6: {
          "!type": "fn(domain: string, callback: fn(err: +Error, addresses: [string])) -> [string]",
          "!url": "https://nodejs.org/api/dns.html#dns_dns_resolve6_domain_callback",
          "!doc": "The same as dns.resolve4() except for IPv6 queries (an AAAA query)."
        },
        resolveMx: {
          "!type": "fn(domain: string, callback: fn(err: +Error, addresses: [string])) -> [string]",
          "!url": "https://nodejs.org/api/dns.html#dns_dns_resolvemx_domain_callback",
          "!doc": "The same as dns.resolve(), but only for mail exchange queries (MX records)."
        },
        resolveTxt: {
          "!type": "fn(domain: string, callback: fn(err: +Error, addresses: [string])) -> [string]",
          "!url": "https://nodejs.org/api/dns.html#dns_dns_resolvetxt_domain_callback",
          "!doc": "The same as dns.resolve(), but only for text queries (TXT records). addresses is an array of the text records available for domain (e.g., ['v=spf1 ip4:0.0.0.0 ~all'])."
        },
        resolveSrv: {
          "!type": "fn(domain: string, callback: fn(err: +Error, addresses: [string])) -> [string]",
          "!url": "https://nodejs.org/api/dns.html#dns_dns_resolvesrv_domain_callback",
          "!doc": "The same as dns.resolve(), but only for service records (SRV records). addresses is an array of the SRV records available for domain. Properties of SRV records are priority, weight, port, and name (e.g., [{'priority': 10, {'weight': 5, 'port': 21223, 'name': 'service.example.com'}, ...])."
        },
        resolveNs: {
          "!type": "fn(domain: string, callback: fn(err: +Error, addresses: [string])) -> [string]",
          "!url": "https://nodejs.org/api/dns.html#dns_dns_resolvens_domain_callback",
          "!doc": "The same as dns.resolve(), but only for name server records (NS records). addresses is an array of the name server records available for domain (e.g., ['ns1.example.com', 'ns2.example.com'])."
        },
        resolveCname: {
          "!type": "fn(domain: string, callback: fn(err: +Error, addresses: [string])) -> [string]",
          "!url": "https://nodejs.org/api/dns.html#dns_dns_resolvecname_domain_callback",
          "!doc": "The same as dns.resolve(), but only for canonical name records (CNAME records). addresses is an array of the canonical name records available for domain (e.g., ['bar.example.com'])."
        },
        reverse: {
          "!type": "fn(ip: string, callback: fn(err: +Error, domains: [string])) -> [string]",
          "!url": "https://nodejs.org/api/dns.html#dns_dns_reverse_ip_callback",
          "!doc": "Reverse resolves an ip address to an array of domain names."
        }
      },
      net: {
        "!url": "https://nodejs.org/api/net.html",
        "!doc": "The net module provides you with an asynchronous network wrapper. It contains methods for creating both servers and clients (called streams).",
        createServer: {
          "!type": "fn(options?: ?, connectionListener?: fn(socket: +net.Socket)) -> +net.Server",
          "!url": "https://nodejs.org/api/net.html#net_net_createserver_options_connectionlistener",
          "!doc": "Creates a new TCP server. The connectionListener argument is automatically set as a listener for the 'connection' event."
        },
        Server: {
          "!type": "fn()",
          prototype: {
            "!proto": "net.Socket.prototype",
            listen: {
              "!type": "fn(port: number, hostname?: string, backlog?: number, callback?: fn())",
              "!url": "https://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback",
              "!doc": "Begin accepting connections on the specified port and host. If the host is omitted, the server will accept connections directed to any IPv4 address (INADDR_ANY). A port value of zero will assign a random port."
            },
            close: {
              "!type": "fn(callback?: fn())",
              "!url": "https://nodejs.org/api/net.html#net_server_close_callback",
              "!doc": "Stops the server from accepting new connections and keeps existing connections. This function is asynchronous, the server is finally closed when all connections are ended and the server emits a 'close' event. Optionally, you can pass a callback to listen for the 'close' event."
            },
            maxConnections: {
              "!type": "number",
              "!url": "https://nodejs.org/api/net.html#net_server_maxconnections",
              "!doc": "Set this property to reject connections when the server's connection count gets high."
            },
            getConnections: {
              "!type": "fn(callback: fn(err: +Error, count: number))",
              "!url": "https://nodejs.org/api/net.html#net_server_getconnections_callback",
              "!doc": "Asynchronously get the number of concurrent connections on the server. Works when sockets were sent to forks."
            }
          },
          "!url": "https://nodejs.org/api/net.html#net_class_net_server",
          "!doc": "This class is used to create a TCP or UNIX server. A server is a net.Socket that can listen for new incoming connections."
        },
        Socket: {
          "!type": "fn(options: ?)",
          prototype: {
            "!proto": "events.EventEmitter.prototype",
            connect: {
              "!type": "fn(port: number, host?: string, connectionListener?: fn())",
              "!url": "https://nodejs.org/api/net.html#net_socket_connect_port_host_connectlistener",
              "!doc": "Opens the connection for a given socket. If port and host are given, then the socket will be opened as a TCP socket, if host is omitted, localhost will be assumed. If a path is given, the socket will be opened as a unix socket to that path."
            },
            bufferSize: {
              "!type": "number",
              "!url": "https://nodejs.org/api/net.html#net_socket_buffersize",
              "!doc": "net.Socket has the property that socket.write() always works. This is to help users get up and running quickly. The computer cannot always keep up with the amount of data that is written to a socket - the network connection simply might be too slow. Node will internally queue up the data written to a socket and send it out over the wire when it is possible. (Internally it is polling on the socket's file descriptor for being writable)."
            },
            setEncoding: {
              "!type": "fn(encoding?: string)",
              "!url": "https://nodejs.org/api/net.html#net_socket_setencoding_encoding",
              "!doc": "Set the encoding for the socket as a Readable Stream."
            },
            write: {
              "!type": "fn(data: +Buffer, encoding?: string, callback?: fn())",
              "!url": "https://nodejs.org/api/net.html#net_socket_write_data_encoding_callback",
              "!doc": "Sends data on the socket. The second parameter specifies the encoding in the case of a string--it defaults to UTF8 encoding."
            },
            end: {
              "!type": "fn(data?: +Buffer, encoding?: string)",
              "!url": "https://nodejs.org/api/net.html#net_socket_end_data_encoding",
              "!doc": "Half-closes the socket. i.e., it sends a FIN packet. It is possible the server will still send some data."
            },
            destroy: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/net.html#net_socket_destroy",
              "!doc": "Ensures that no more I/O activity happens on this socket. Only necessary in case of errors (parse error or so)."
            },
            pause: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/net.html#net_socket_pause",
              "!doc": "Pauses the reading of data. That is, 'data' events will not be emitted. Useful to throttle back an upload."
            },
            resume: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/net.html#net_socket_resume",
              "!doc": "Resumes reading after a call to pause()."
            },
            setTimeout: {
              "!type": "fn(timeout: number, callback?: fn())",
              "!url": "https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback",
              "!doc": "Sets the socket to timeout after timeout milliseconds of inactivity on the socket. By default net.Socket do not have a timeout."
            },
            setKeepAlive: {
              "!type": "fn(enable?: bool, initialDelay?: number)",
              "!url": "https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay",
              "!doc": "Enable/disable keep-alive functionality, and optionally set the initial delay before the first keepalive probe is sent on an idle socket. enable defaults to false."
            },
            address: {
              "!type": "fn() -> net.address",
              "!url": "https://nodejs.org/api/net.html#net_socket_address",
              "!doc": "Returns the bound address, the address family name and port of the socket as reported by the operating system. Returns an object with three properties, e.g. { port: 12346, family: 'IPv4', address: '127.0.0.1' }"
            },
            unref: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/net.html#net_socket_unref",
              "!doc": "Calling unref on a socket will allow the program to exit if this is the only active socket in the event system. If the socket is already unrefd calling unref again will have no effect."
            },
            ref: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/net.html#net_socket_ref",
              "!doc": "Opposite of unref, calling ref on a previously unrefd socket will not let the program exit if it's the only socket left (the default behavior). If the socket is refd calling ref again will have no effect."
            },
            remoteAddress: {
              "!type": "string",
              "!url": "https://nodejs.org/api/net.html#net_socket_remoteaddress",
              "!doc": "The string representation of the remote IP address. For example, '74.125.127.100' or '2001:4860:a005::68'."
            },
            remotePort: {
              "!type": "number",
              "!url": "https://nodejs.org/api/net.html#net_socket_remoteport",
              "!doc": "The numeric representation of the remote port. For example, 80 or 21."
            },
            localPort: {
              "!type": "number",
              "!url": "https://nodejs.org/api/net.html#net_socket_localport",
              "!doc": "The numeric representation of the local port. For example, 80 or 21."
            },
            bytesRead: {
              "!type": "number",
              "!url": "https://nodejs.org/api/net.html#net_socket_bytesread",
              "!doc": "The amount of received bytes."
            },
            bytesWritten: {
              "!type": "number",
              "!url": "https://nodejs.org/api/net.html#net_socket_byteswritten",
              "!doc": "The amount of bytes sent."
            },
            setNoDelay: {
              "!type": "fn(noDelay?: fn())",
              "!url": "https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay",
              "!doc": "Disables the Nagle algorithm. By default TCP connections use the Nagle algorithm, they buffer data before sending it off. Setting true for noDelay will immediately fire off data each time socket.write() is called. noDelay defaults to true."
            },
            localAddress: {
              "!type": "string",
              "!url": "https://nodejs.org/api/net.html#net_socket_localaddress",
              "!doc": "The string representation of the local IP address the remote client is connecting on. For example, if you are listening on '0.0.0.0' and the client connects on '192.168.1.1', the value would be '192.168.1.1'."
            }
          },
          "!url": "https://nodejs.org/api/net.html#net_class_net_socket",
          "!doc": "This object is an abstraction of a TCP or UNIX socket. net.Socket instances implement a duplex Stream interface. They can be created by the user and used as a client (with connect()) or they can be created by Node and passed to the user through the 'connection' event of a server."
        },
        connect: {
          "!type": "fn(options: ?, connectionListener?: fn()) -> +net.Socket",
          "!url": "https://nodejs.org/api/net.html#net_net_connect_options_connectionlistener",
          "!doc": "Constructs a new socket object and opens the socket to the given location. When the socket is established, the 'connect' event will be emitted."
        },
        createConnection: {
          "!type": "fn(options: ?, connectionListener?: fn()) -> +net.Socket",
          "!url": "https://nodejs.org/api/net.html#net_net_createconnection_options_connectionlistener",
          "!doc": "Constructs a new socket object and opens the socket to the given location. When the socket is established, the 'connect' event will be emitted."
        },
        isIP: {
          "!type": "fn(input: string) -> number",
          "!url": "https://nodejs.org/api/net.html#net_net_isip_input",
          "!doc": "Tests if input is an IP address. Returns 0 for invalid strings, returns 4 for IP version 4 addresses, and returns 6 for IP version 6 addresses."
        },
        isIPv4: {
          "!type": "fn(input: string) -> bool",
          "!url": "https://nodejs.org/api/net.html#net_net_isipv4_input",
          "!doc": "Returns true if input is a version 4 IP address, otherwise returns false."
        },
        isIPv6: {
          "!type": "fn(input: string) -> bool",
          "!url": "https://nodejs.org/api/net.html#net_net_isipv6_input",
          "!doc": "Returns true if input is a version 6 IP address, otherwise returns false."
        }
      },
      dgram: {
        "!url": "https://nodejs.org/api/dgram.html",
        "!doc": "UDP / Datagram Sockets",
        createSocket: {
          "!type": "fn(type: string, callback?: fn()) -> +dgram.Socket",
          "!url": "https://nodejs.org/api/dgram.html#dgram_dgram_createsocket_type_callback",
          "!doc": "Creates a datagram Socket of the specified types. Valid types are udp4 and udp6."
        },
        Socket: {
          "!type": "fn()",
          prototype: {
            "!proto": "events.EventEmitter.prototype",
            send: {
              "!type": "fn(buf: +Buffer, offset: number, length: number, port: number, address: string, callback?: fn())",
              "!url": "https://nodejs.org/api/dgram.html#dgram_socket_send_buf_offset_length_port_address_callback",
              "!doc": "For UDP sockets, the destination port and IP address must be specified. A string may be supplied for the address parameter, and it will be resolved with DNS. An optional callback may be specified to detect any DNS errors and when buf may be re-used. Note that DNS lookups will delay the time that a send takes place, at least until the next tick. The only way to know for sure that a send has taken place is to use the callback."
            },
            bind: {
              "!type": "fn(port: number, address?: string)",
              "!url": "https://nodejs.org/api/dgram.html#dgram_socket_bind_port_address_callback",
              "!doc": "For UDP sockets, listen for datagrams on a named port and optional address. If address is not specified, the OS will try to listen on all addresses."
            },
            close: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/dgram.html#dgram_socket_close",
              "!doc": "Close the underlying socket and stop listening for data on it."
            },
            address: {
              address: "string",
              family: "string",
              port: "number",
              "!url": "https://nodejs.org/api/dgram.html#dgram_socket_address",
              "!doc": "Returns an object containing the address information for a socket. For UDP sockets, this object will contain address , family and port."
            },
            setBroadcast: {
              "!type": "fn(flag: bool)",
              "!url": "https://nodejs.org/api/dgram.html#dgram_socket_setbroadcast_flag",
              "!doc": "Sets or clears the SO_BROADCAST socket option. When this option is set, UDP packets may be sent to a local interface's broadcast address."
            },
            setTTL: {
              "!type": "fn(ttl: number)",
              "!url": "https://nodejs.org/api/dgram.html#dgram_socket_setttl_ttl",
              "!doc": "Sets the IP_TTL socket option. TTL stands for \"Time to Live,\" but in this context it specifies the number of IP hops that a packet is allowed to go through. Each router or gateway that forwards a packet decrements the TTL. If the TTL is decremented to 0 by a router, it will not be forwarded. Changing TTL values is typically done for network probes or when multicasting."
            },
            setMulticastTTL: {
              "!type": "fn(ttl: number)",
              "!url": "https://nodejs.org/api/dgram.html#dgram_socket_setmulticastttl_ttl",
              "!doc": "Sets the IP_MULTICAST_TTL socket option. TTL stands for \"Time to Live,\" but in this context it specifies the number of IP hops that a packet is allowed to go through, specifically for multicast traffic. Each router or gateway that forwards a packet decrements the TTL. If the TTL is decremented to 0 by a router, it will not be forwarded."
            },
            setMulticastLoopback: {
              "!type": "fn(flag: bool)",
              "!url": "https://nodejs.org/api/dgram.html#dgram_socket_setmulticastloopback_flag",
              "!doc": "Sets or clears the IP_MULTICAST_LOOP socket option. When this option is set, multicast packets will also be received on the local interface."
            },
            addMembership: {
              "!type": "fn(multicastAddress: string, multicastInterface?: string)",
              "!url": "https://nodejs.org/api/dgram.html#dgram_socket_addmembership_multicastaddress_multicastinterface",
              "!doc": "Tells the kernel to join a multicast group with IP_ADD_MEMBERSHIP socket option."
            },
            dropMembership: {
              "!type": "fn(multicastAddress: string, multicastInterface?: string)",
              "!url": "https://nodejs.org/api/dgram.html#dgram_socket_dropmembership_multicastaddress_multicastinterface",
              "!doc": "Opposite of addMembership - tells the kernel to leave a multicast group with IP_DROP_MEMBERSHIP socket option. This is automatically called by the kernel when the socket is closed or process terminates, so most apps will never need to call this."
            }
          },
          "!url": "https://nodejs.org/api/dgram.html#dgram_class_dgram_socket",
          "!doc": "The dgram Socket class encapsulates the datagram functionality. It should be created via dgram.createSocket(type, [callback])."
        }
      },
      fs: {
        "!url": "https://nodejs.org/api/fs.html",
        "!doc": "File I/O is provided by simple wrappers around standard POSIX functions. To use this module do require('fs').\nAll the methods have asynchronous and synchronous forms.",
        rename: {
          "!type": "fn(oldPath: string, newPath: string, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_rename_oldpath_newpath_callback",
          "!doc": "Asynchronous rename(2). No arguments other than a possible exception are given to the completion callback."
        },
        renameSync: {
          "!type": "fn(oldPath: string, newPath: string)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_renamesync_oldpath_newpath",
          "!doc": "Synchronous rename(2)."
        },
        ftruncate: {
          "!type": "fn(fd: number, len: number, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_ftruncate_fd_len_callback",
          "!doc": "Asynchronous ftruncate(2). No arguments other than a possible exception are given to the completion callback."
        },
        ftruncateSync: {
          "!type": "fn(fd: number, len: number)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_ftruncatesync_fd_len",
          "!doc": "Synchronous ftruncate(2)."
        },
        truncate: {
          "!type": "fn(path: string, len: number, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_truncate_path_len_callback",
          "!doc": "Asynchronous truncate(2). No arguments other than a possible exception are given to the completion callback."
        },
        truncateSync: {
          "!type": "fn(path: string, len: number)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_truncatesync_path_len",
          "!doc": "Synchronous truncate(2)."
        },
        chown: {
          "!type": "fn(path: string, uid: number, gid: number, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_chown_path_uid_gid_callback",
          "!doc": "Asynchronous chown(2). No arguments other than a possible exception are given to the completion callback."
        },
        chownSync: {
          "!type": "fn(path: string, uid: number, gid: number)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_chownsync_path_uid_gid",
          "!doc": "Synchronous chown(2)."
        },
        fchown: {
          "!type": "fn(fd: number, uid: number, gid: number, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_fchown_fd_uid_gid_callback",
          "!doc": "Asynchronous fchown(2). No arguments other than a possible exception are given to the completion callback."
        },
        fchownSync: {
          "!type": "fn(fd: number, uid: number, gid: number)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_fchownsync_fd_uid_gid",
          "!doc": "Synchronous fchown(2)."
        },
        lchown: {
          "!type": "fn(path: string, uid: number, gid: number, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_lchown_path_uid_gid_callback",
          "!doc": "Asynchronous lchown(2). No arguments other than a possible exception are given to the completion callback."
        },
        lchownSync: {
          "!type": "fn(path: string, uid: number, gid: number)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_lchownsync_path_uid_gid",
          "!doc": "Synchronous lchown(2)."
        },
        chmod: {
          "!type": "fn(path: string, mode: string, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_chmod_path_mode_callback",
          "!doc": "Asynchronous chmod(2). No arguments other than a possible exception are given to the completion callback."
        },
        chmodSync: {
          "!type": "fn(path: string, mode: string)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_chmodsync_path_mode",
          "!doc": "Synchronous chmod(2)."
        },
        fchmod: {
          "!type": "fn(fd: number, mode: string, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_fchmod_fd_mode_callback",
          "!doc": "Asynchronous fchmod(2). No arguments other than a possible exception are given to the completion callback."
        },
        fchmodSync: {
          "!type": "fn(fd: number, mode: string)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_fchmodsync_fd_mode",
          "!doc": "Synchronous fchmod(2)."
        },
        lchmod: {
          "!type": "fn(path: string, mode: number, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_lchmod_path_mode_callback",
          "!doc": "Asynchronous lchmod(2). No arguments other than a possible exception are given to the completion callback."
        },
        lchmodSync: {
          "!type": "fn(path: string, mode: string)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_lchmodsync_path_mode",
          "!doc": "Synchronous lchmod(2)."
        },
        stat: {
          "!type": "fn(path: string, callback?: fn(err: +Error, stats: +fs.Stats) -> ?) -> +fs.Stats",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_stat_path_callback",
          "!doc": "Asynchronous stat(2). The callback gets two arguments (err, stats) where stats is a fs.Stats object."
        },
        lstat: {
          "!type": "fn(path: string, callback?: fn(err: +Error, stats: +fs.Stats) -> ?) -> +fs.Stats",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_lstat_path_callback",
          "!doc": "Asynchronous lstat(2). The callback gets two arguments (err, stats) where stats is a fs.Stats object. lstat() is identical to stat(), except that if path is a symbolic link, then the link itself is stat-ed, not the file that it refers to."
        },
        fstat: {
          "!type": "fn(fd: number, callback?: fn(err: +Error, stats: +fs.Stats) -> ?) -> +fs.Stats",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_fstat_fd_callback",
          "!doc": "Asynchronous fstat(2). The callback gets two arguments (err, stats) where stats is a fs.Stats object. fstat() is identical to stat(), except that the file to be stat-ed is specified by the file descriptor fd."
        },
        statSync: {
          "!type": "fn(path: string) -> +fs.Stats",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_statsync_path",
          "!doc": "Synchronous stat(2). Returns an instance of fs.Stats."
        },
        lstatSync: {
          "!type": "fn(path: string) -> +fs.Stats",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_lstatsync_path",
          "!doc": "Synchronous lstat(2). Returns an instance of fs.Stats."
        },
        fstatSync: {
          "!type": "fn(fd: number) -> +fs.Stats",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_fstatsync_fd",
          "!doc": "Synchronous fstat(2). Returns an instance of fs.Stats."
        },
        link: {
          "!type": "fn(srcpath: string, dstpath: string, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_link_srcpath_dstpath_callback",
          "!doc": "Asynchronous link(2). No arguments other than a possible exception are given to the completion callback."
        },
        linkSync: {
          "!type": "fn(srcpath: string, dstpath: string)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_linksync_srcpath_dstpath",
          "!doc": "Synchronous link(2)."
        },
        symlink: {
          "!type": "fn(srcpath: string, dstpath: string, type?: string, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_symlink_srcpath_dstpath_type_callback",
          "!doc": "Asynchronous symlink(2). No arguments other than a possible exception are given to the completion callback. type argument can be either 'dir', 'file', or 'junction' (default is 'file'). It is only used on Windows (ignored on other platforms). Note that Windows junction points require the destination path to be absolute. When using 'junction', the destination argument will automatically be normalized to absolute path."
        },
        symlinkSync: {
          "!type": "fn(srcpath: string, dstpath: string, type?: string)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_symlinksync_srcpath_dstpath_type",
          "!doc": "Synchronous symlink(2)."
        },
        readlink: {
          "!type": "fn(path: string, callback?: fn(err: +Error, linkString: string))",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_readlink_path_callback",
          "!doc": "Asynchronous readlink(2). The callback gets two arguments (err, linkString)."
        },
        readlinkSync: {
          "!type": "fn(path: string)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_readlinksync_path",
          "!doc": "Synchronous readlink(2). Returns the symbolic link's string value."
        },
        realpath: {
          "!type": "fn(path: string, cache: bool, callback: fn(err: +Error, resolvedPath: string))",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_realpath_path_cache_callback",
          "!doc": "Asynchronous realpath(2). The callback gets two arguments (err, resolvedPath). May use process.cwd to resolve relative paths. cache is an object literal of mapped paths that can be used to force a specific path resolution or avoid additional fs.stat calls for known real paths."
        },
        realpathSync: {
          "!type": "fn(path: string, cache?: bool) -> string",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_realpathsync_path_cache",
          "!doc": "Synchronous realpath(2). Returns the resolved path."
        },
        unlink: {
          "!type": "fn(path: string, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_unlink_path_callback",
          "!doc": "Asynchronous unlink(2). No arguments other than a possible exception are given to the completion callback."
        },
        unlinkSync: {
          "!type": "fn(path: string)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_unlinksync_path",
          "!doc": "Synchronous unlink(2)."
        },
        rmdir: {
          "!type": "fn(path: string, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_rmdir_path_callback",
          "!doc": "Asynchronous rmdir(2). No arguments other than a possible exception are given to the completion callback."
        },
        rmdirSync: {
          "!type": "fn(path: string)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_rmdirsync_path",
          "!doc": "Synchronous rmdir(2)."
        },
        mkdir: {
          "!type": "fn(path: string, mode?: ?, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_mkdir_path_mode_callback",
          "!doc": "Asynchronous mkdir(2). No arguments other than a possible exception are given to the completion callback. mode defaults to 0777."
        },
        mkdirSync: {
          "!type": "fn(path: string, mode?: string)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_mkdirsync_path_mode",
          "!doc": "Synchronous mkdir(2)."
        },
        readdir: {
          "!type": "fn(path: string, callback?: fn(err: +Error, files: [string]))",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_readdir_path_callback",
          "!doc": "Asynchronous readdir(3). Reads the contents of a directory. The callback gets two arguments (err, files) where files is an array of the names of the files in the directory excluding '.' and '..'."
        },
        readdirSync: {
          "!type": "fn(path: string) -> [string]",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_readdirsync_path",
          "!doc": "Synchronous readdir(3). Returns an array of filenames excluding '.' and '..'."
        },
        close: {
          "!type": "fn(fd: number, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_close_fd_callback",
          "!doc": "Asynchronous close(2). No arguments other than a possible exception are given to the completion callback."
        },
        closeSync: {
          "!type": "fn(fd: number)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_closesync_fd",
          "!doc": "Synchronous close(2)."
        },
        open: {
          "!type": "fn(path: string, flags: string, mode?: string, callback?: fn(err: +Error, fd: number))",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_open_path_flags_mode_callback",
          "!doc": "Asynchronous file open."
        },
        openSync: {
          "!type": "fn(path: string, flags: string, mode?: string) -> number",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_opensync_path_flags_mode",
          "!doc": "Synchronous open(2)."
        },
        utimes: {
          "!type": "fn(path: string, atime: number, mtime: number, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_utimes_path_atime_mtime_callback",
          "!doc": "Change file timestamps of the file referenced by the supplied path."
        },
        utimesSync: {
          "!type": "fn(path: string, atime: number, mtime: number)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_utimessync_path_atime_mtime",
          "!doc": "Change file timestamps of the file referenced by the supplied path."
        },
        futimes: {
          "!type": "fn(fd: number, atime: number, mtime: number, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_futimes_fd_atime_mtime_callback",
          "!doc": "Change the file timestamps of a file referenced by the supplied file descriptor."
        },
        futimesSync: {
          "!type": "fn(fd: number, atime: number, mtime: number)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_futimessync_fd_atime_mtime",
          "!doc": "Change the file timestamps of a file referenced by the supplied file descriptor."
        },
        fsync: {
          "!type": "fn(fd: number, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_fsync_fd_callback",
          "!doc": "Asynchronous fsync(2). No arguments other than a possible exception are given to the completion callback."
        },
        fsyncSync: {
          "!type": "fn(fd: number)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_fsyncsync_fd",
          "!doc": "Synchronous fsync(2)."
        },
        write: {
          "!type": "fn(fd: number, buffer: +Buffer, offset: number, length: number, position: number, callback?: fn(err: +Error, written: number, buffer: +Buffer))",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_write_fd_buffer_offset_length_position_callback",
          "!doc": "Write buffer to the file specified by fd."
        },
        writeSync: {
          "!type": "fn(fd: number, buffer: +Buffer, offset: number, length: number, position: number) -> number",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_writesync_fd_buffer_offset_length_position",
          "!doc": "Synchronous version of fs.write(). Returns the number of bytes written."
        },
        read: {
          "!type": "fn(fd: number, buffer: +Buffer, offset: number, length: number, position: number, callback?: fn(err: +Error, bytesRead: number, buffer: +Buffer))",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_read_fd_buffer_offset_length_position_callback",
          "!doc": "Read data from the file specified by fd."
        },
        readSync: {
          "!type": "fn(fd: number, buffer: +Buffer, offset: number, length: number, position: number) -> number",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_readsync_fd_buffer_offset_length_position",
          "!doc": "Synchronous version of fs.read. Returns the number of bytesRead."
        },
        readFile: {
          "!type": "fn(filename: string, callback: fn(err: +Error, data: +Buffer))",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_readfile_filename_options_callback",
          "!doc": "Asynchronously reads the entire contents of a file."
        },
        readFileSync: {
          "!type": "fn(filename: string, encoding: string) -> +Buffer",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_readfilesync_filename_options",
          "!doc": "Synchronous version of fs.readFile. Returns the contents of the filename."
        },
        writeFile: {
          "!type": "fn(filename: string, data: string|+Buffer, encoding?: string, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_writefile_filename_data_options_callback",
          "!doc": "Asynchronously writes data to a file, replacing the file if it already exists. data can be a string or a buffer."
        },
        writeFileSync: {
          "!type": "fn(filename: string, data: string|+Buffer, encoding?: string)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_writefilesync_filename_data_options",
          "!doc": "The synchronous version of fs.writeFile."
        },
        appendFile: {
          "!type": "fn(filename: string, data: string|+Buffer, encoding?: string, callback?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_appendfile_filename_data_options_callback",
          "!doc": "Asynchronously append data to a file, creating the file if it not yet exists. data can be a string or a buffer."
        },
        appendFileSync: {
          "!type": "fn(filename: string, data: string|+Buffer, encoding?: string)",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_appendfilesync_filename_data_options",
          "!doc": "The synchronous version of fs.appendFile."
        },
        watchFile: {
          "!type": "fn(filename: string, options: ?, listener: fn(current: +fs.Stats, prev: +fs.Stats))",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_watchfile_filename_options_listener",
          "!doc": "Watch for changes on filename. The callback listener will be called each time the file is accessed."
        },
        unwatchFile: {
          "!type": "fn(filename: string, listener?: fn())",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_unwatchfile_filename_listener",
          "!doc": "Stop watching for changes on filename. If listener is specified, only that particular listener is removed. Otherwise, all listeners are removed and you have effectively stopped watching filename."
        },
        watch: {
          "!type": "fn(filename: string, options?: ?, listener?: fn(event: string, filename: string)) -> +fs.FSWatcher",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_watch_filename_options_listener",
          "!doc": "Watch for changes on filename, where filename is either a file or a directory. The returned object is a fs.FSWatcher."
        },
        exists: {
          "!type": "fn(path: string, callback?: fn(exists: bool))",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_exists_path_callback",
          "!doc": "Test whether or not the given path exists by checking with the file system. Then call the callback argument with either true or false."
        },
        existsSync: {
          "!type": "fn(path: string) -> bool",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_existssync_path",
          "!doc": "Synchronous version of fs.exists."
        },
        Stats: {
          "!type": "fn()",
          prototype: {
            isFile: "fn() -> bool",
            isDirectory: "fn() -> bool",
            isBlockDevice: "fn() -> bool",
            isCharacterDevice: "fn() -> bool",
            isSymbolicLink: "fn() -> bool",
            isFIFO: "fn() -> bool",
            isSocket: "fn() -> bool",
            dev: "number",
            ino: "number",
            mode: "number",
            nlink: "number",
            uid: "number",
            gid: "number",
            rdev: "number",
            size: "number",
            blksize: "number",
            blocks: "number",
            atime: "+Date",
            mtime: "+Date",
            ctime: "+Date"
          },
          "!url": "https://nodejs.org/api/fs.html#fs_class_fs_stats",
          "!doc": "Objects returned from fs.stat(), fs.lstat() and fs.fstat() and their synchronous counterparts are of this type."
        },
        createReadStream: {
          "!type": "fn(path: string, options?: ?) -> +stream.Readable",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options",
          "!doc": "Returns a new ReadStream object."
        },
        createWriteStream: {
          "!type": "fn(path: string, options?: ?) -> +stream.Writable",
          "!url": "https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options",
          "!doc": "Returns a new WriteStream object."
        },
        FSWatcher: {
          "!type": "fn()",
          prototype: {
            close: "fn()"
          },
          "!url": "https://nodejs.org/api/fs.html#fs_class_fs_fswatcher",
          "!doc": "Objects returned from fs.watch() are of this type."
        }
      },
      path: {
        "!url": "https://nodejs.org/api/path.html",
        "!doc": "This module contains utilities for handling and transforming file paths. Almost all these methods perform only string transformations. The file system is not consulted to check whether paths are valid.",
        normalize: {
          "!type": "fn(p: string) -> string",
          "!url": "https://nodejs.org/api/path.html#path_path_normalize_p",
          "!doc": "Normalize a string path, taking care of '..' and '.' parts."
        },
        join: {
          "!type": "fn() -> string",
          "!url": "https://nodejs.org/api/path.html#path_path_join_path1_path2",
          "!doc": "Join all arguments together and normalize the resulting path."
        },
        resolve: {
          "!type": "fn(from: string, from2: string, from3: string, from4: string, from5: string, to: string) -> string",
          "!url": "https://nodejs.org/api/path.html#path_path_resolve_from_to",
          "!doc": "Resolves to to an absolute path."
        },
        relative: {
          "!type": "fn(from: string, to: string) -> string",
          "!url": "https://nodejs.org/api/path.html#path_path_relative_from_to",
          "!doc": "Solve the relative path from from to to."
        },
        dirname: {
          "!type": "fn(p: string) -> string",
          "!url": "https://nodejs.org/api/path.html#path_path_dirname_p",
          "!doc": "Return the directory name of a path. Similar to the Unix dirname command."
        },
        basename: {
          "!type": "fn(p: string, ext?: string) -> string",
          "!url": "https://nodejs.org/api/path.html#path_path_basename_p_ext",
          "!doc": "Return the last portion of a path. Similar to the Unix basename command."
        },
        extname: {
          "!type": "fn(p: string) -> string",
          "!url": "https://nodejs.org/api/path.html#path_path_extname_p",
          "!doc": "Return the extension of the path, from the last '.' to end of string in the last portion of the path. If there is no '.' in the last portion of the path or the first character of it is '.', then it returns an empty string."
        },
        sep: {
          "!type": "string",
          "!url": "https://nodejs.org/api/path.html#path_path_sep",
          "!doc": "The platform-specific file separator. '\\\\' or '/'."
        },
        delimiter: {
          "!type": "string",
          "!url": "https://nodejs.org/api/path.html#path_path_delimiter",
          "!doc": "The platform-specific path delimiter, ; or ':'."
        }
      },
      string_decoder: {
        "!url": "https://nodejs.org/api/string_decoder.html",
        "!doc": "StringDecoder decodes a buffer to a string. It is a simple interface to buffer.toString() but provides additional support for utf8.",
        StringDecoder: {
          "!type": "fn(encoding?: string)",
          prototype: {
            write: {
              "!type": "fn(buffer: +Buffer) -> string",
              "!url": "https://nodejs.org/api/string_decoder.html#string_decoder_decoder_write_buffer",
              "!doc": "Returns a decoded string."
            },
            end: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/string_decoder.html#string_decoder_decoder_end",
              "!doc": "Returns any trailing bytes that were left in the buffer."
            }
          },
          "!url": "https://nodejs.org/api/string_decoder.html#string_decoder_class_stringdecoder",
          "!doc": "Accepts a single argument, encoding which defaults to utf8."
        }
      },
      tls: {
        "!url": "https://nodejs.org/api/tls.html",
        "!doc": "The tls module uses OpenSSL to provide Transport Layer Security and/or Secure Socket Layer: encrypted stream communication.",
        CLIENT_RENEG_LIMIT: "number",
        CLIENT_RENEG_WINDOW: "number",
        SLAB_BUFFER_SIZE: "number",
        getCiphers: {
          "!type": "fn() -> [string]",
          "!url": "https://nodejs.org/api/tls.html#tls_tls_getciphers",
          "!doc": "Returns an array with the names of the supported SSL ciphers."
        },
        Server: {
          "!type": "fn()",
          prototype: {
            "!proto": "net.Server.prototype",
            listen: {
              "!type": "fn(port: number, host?: string, callback?: fn())",
              "!url": "https://nodejs.org/api/tls.html#tls_server_listen_port_host_callback",
              "!doc": "Begin accepting connections on the specified port and host. If the host is omitted, the server will accept connections directed to any IPv4 address (INADDR_ANY)."
            },
            close: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/tls.html#tls_server_close",
              "!doc": "Stops the server from accepting new connections. This function is asynchronous, the server is finally closed when the server emits a 'close' event."
            },
            addContext: {
              "!type": "fn(hostName: string, credentials: tls.Server.credentials)",
              "!url": "https://nodejs.org/api/tls.html#tls_server_addcontext_hostname_credentials",
              "!doc": "Add secure context that will be used if client request's SNI hostname is matching passed hostname (wildcards can be used). credentials can contain key, cert and ca."
            }
          },
          "!url": "https://nodejs.org/api/tls.html#tls_class_tls_server",
          "!doc": "This class is a subclass of net.Server and has the same methods on it. Instead of accepting just raw TCP connections, this accepts encrypted connections using TLS or SSL."
        },
        createServer: {
          "!type": "fn(options?: ?, connectionListener?: fn(stream: +tls.CleartextStream)) -> +tls.Server",
          "!url": "https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener",
          "!doc": "Creates a new tls.Server. The connectionListener argument is automatically set as a listener for the secureConnection event."
        },
        CleartextStream: {
          "!type": "fn()",
          prototype: {
            "!proto": "stream.Duplex.prototype",
            authorized: {
              "!type": "bool",
              "!url": "https://nodejs.org/api/tls.html#tls_cleartextstream_authorized",
              "!doc": "A boolean that is true if the peer certificate was signed by one of the specified CAs, otherwise false"
            },
            authorizationError: {
              "!type": "+Error",
              "!url": "https://nodejs.org/api/tls.html#tls_cleartextstream_authorizationerror",
              "!doc": "The reason why the peer's certificate has not been verified. This property becomes available only when cleartextStream.authorized === false."
            },
            getPeerCertificate: {
              "!type": "fn() -> ?",
              "!url": "https://nodejs.org/api/tls.html#tls_cleartextstream_getpeercertificate",
              "!doc": "Returns an object representing the peer's certificate. The returned object has some properties corresponding to the field of the certificate."
            },
            getCipher: {
              "!type": "fn() -> tls.cipher",
              "!url": "https://nodejs.org/api/tls.html#tls_cleartextstream_getcipher",
              "!doc": "Returns an object representing the cipher name and the SSL/TLS protocol version of the current connection."
            },
            address: {
              "!type": "net.address",
              "!url": "https://nodejs.org/api/tls.html#tls_cleartextstream_address",
              "!doc": "Returns the bound address, the address family name and port of the underlying socket as reported by the operating system. Returns an object with three properties, e.g. { port: 12346, family: 'IPv4', address: '127.0.0.1' }"
            },
            remoteAddress: {
              "!type": "string",
              "!url": "https://nodejs.org/api/tls.html#tls_cleartextstream_remoteaddress",
              "!doc": "The string representation of the remote IP address. For example, '74.125.127.100' or '2001:4860:a005::68'."
            },
            remotePort: {
              "!type": "number",
              "!url": "https://nodejs.org/api/tls.html#tls_cleartextstream_remoteport",
              "!doc": "The numeric representation of the remote port. For example, 443."
            }
          },
          "!url": "https://nodejs.org/api/tls.html#tls_class_tls_cleartextstream",
          "!doc": "This is a stream on top of the Encrypted stream that makes it possible to read/write an encrypted data as a cleartext data."
        },
        connect: {
          "!type": "fn(port: number, host?: string, options: ?, listener: fn()) -> +tls.CleartextStream",
          "!url": "https://nodejs.org/api/tls.html#tls_tls_connect_options_callback",
          "!doc": "Creates a new client connection to the given port and host (old API) or options.port and options.host. (If host is omitted, it defaults to localhost.)"
        },
        createSecurePair: {
          "!type": "fn(credentials?: crypto.credentials, isServer?: bool, requestCert?: bool, rejectUnauthorized?: bool) -> +tls.SecurePair",
          "!url": "https://nodejs.org/api/tls.html#tls_tls_createsecurepair_credentials_isserver_requestcert_rejectunauthorized",
          "!doc": "Creates a new secure pair object with two streams, one of which reads/writes encrypted data, and one reads/writes cleartext data. Generally the encrypted one is piped to/from an incoming encrypted data stream, and the cleartext one is used as a replacement for the initial encrypted stream."
        },
        SecurePair: {
          "!type": "fn()",
          prototype: {
            "!proto": "events.EventEmitter.prototype",
            cleartext: {
              "!type": "+tls.CleartextStream",
              "!url": "https://nodejs.org/api/tls.html#tls_class_securepair",
              "!doc": "Returned by tls.createSecurePair."
            },
            encrypted: {
              "!type": "+stream.Duplex",
              "!url": "https://nodejs.org/api/tls.html#tls_class_securepair",
              "!doc": "Returned by tls.createSecurePair."
            }
          },
          "!url": "https://nodejs.org/api/tls.html#tls_class_securepair",
          "!doc": "Returned by tls.createSecurePair."
        }
      },
      crypto: {
        "!url": "https://nodejs.org/api/crypto.html",
        "!doc": "The crypto module offers a way of encapsulating secure credentials to be used as part of a secure HTTPS net or http connection.\nIt also offers a set of wrappers for OpenSSL's hash, hmac, cipher, decipher, sign and verify methods.",
        getCiphers: {
          "!type": "fn() -> [string]",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_getciphers",
          "!doc": "Returns an array with the names of the supported ciphers."
        },
        getHashes: {
          "!type": "fn() -> [string]",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_gethashes",
          "!doc": "Returns an array with the names of the supported hash algorithms."
        },
        createCredentials: {
          "!type": "fn(details?: ?) -> crypto.credentials",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_createcredentials_details",
          "!doc": "Creates a credentials object."
        },
        createHash: {
          "!type": "fn(algorithm: string) -> +crypto.Hash",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm",
          "!doc": "Creates and returns a hash object, a cryptographic hash with the given algorithm which can be used to generate hash digests."
        },
        Hash: {
          "!type": "fn()",
          prototype: {
            "!proto": "stream.Duplex.prototype",
            update: {
              "!type": "fn(data: +Buffer, encoding?: string)",
              "!url": "https://nodejs.org/api/crypto.html#crypto_hash_update_data_input_encoding",
              "!doc": "Updates the hash content with the given data, the encoding of which is given in input_encoding and can be 'utf8', 'ascii' or 'binary'. If no encoding is provided, then a buffer is expected."
            },
            digest: {
              "!type": "fn(encoding?: string) -> +Buffer",
              "!url": "https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding",
              "!doc": "Calculates the digest of all of the passed data to be hashed. The encoding can be 'hex', 'binary' or 'base64'. If no encoding is provided, then a buffer is returned."
            }
          },
          "!url": "https://nodejs.org/api/crypto.html#crypto_class_hash",
          "!doc": "The class for creating hash digests of data."
        },
        createHmac: {
          "!type": "fn(algorithm: string, key: string) -> +crypto.Hmac",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_createhmac_algorithm_key",
          "!doc": "Creates and returns a hmac object, a cryptographic hmac with the given algorithm and key."
        },
        Hmac: {
          "!type": "fn()",
          prototype: {
            update: {
              "!type": "fn(data: +Buffer)",
              "!url": "https://nodejs.org/api/crypto.html#crypto_hmac_update_data",
              "!doc": "Update the hmac content with the given data. This can be called many times with new data as it is streamed."
            },
            digest: {
              "!type": "fn(encoding?: string) -> +Buffer",
              "!url": "https://nodejs.org/api/crypto.html#crypto_hmac_digest_encoding",
              "!doc": "Calculates the digest of all of the passed data to the hmac. The encoding can be 'hex', 'binary' or 'base64'. If no encoding is provided, then a buffer is returned."
            }
          },
          "!url": "https://nodejs.org/api/crypto.html#crypto_class_hmac",
          "!doc": "Class for creating cryptographic hmac content."
        },
        createCipher: {
          "!type": "fn(algorithm: string, password: string) -> +crypto.Cipher",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password",
          "!doc": "Creates and returns a cipher object, with the given algorithm and password."
        },
        createCipheriv: {
          "!type": "fn(algorithm: string, password: string, iv: string) -> +crypto.Cipher",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv",
          "!doc": "Creates and returns a cipher object, with the given algorithm, key and iv."
        },
        Cipher: {
          "!type": "fn()",
          prototype: {
            "!proto": "stream.Duplex.prototype",
            update: {
              "!type": "fn(data: +Buffer, input_encoding?: string, output_encoding?: string) -> +Buffer",
              "!url": "https://nodejs.org/api/crypto.html#crypto_cipher_update_data_input_encoding_output_encoding",
              "!doc": "Updates the cipher with data, the encoding of which is given in input_encoding and can be 'utf8', 'ascii' or 'binary'. If no encoding is provided, then a buffer is expected."
            },
            "final": {
              "!type": "fn(output_encoding?: string) -> +Buffer",
              "!url": "https://nodejs.org/api/crypto.html#crypto_cipher_final_output_encoding",
              "!doc": "Returns any remaining enciphered contents, with output_encoding being one of: 'binary', 'base64' or 'hex'. If no encoding is provided, then a buffer is returned."
            },
            setAutoPadding: {
              "!type": "fn(auto_padding: bool)",
              "!url": "https://nodejs.org/api/crypto.html#crypto_cipher_setautopadding_auto_padding_true",
              "!doc": "You can disable automatic padding of the input data to block size. If auto_padding is false, the length of the entire input data must be a multiple of the cipher's block size or final will fail. Useful for non-standard padding, e.g. using 0x0 instead of PKCS padding. You must call this before cipher.final."
            }
          },
          "!url": "https://nodejs.org/api/crypto.html#crypto_class_cipher",
          "!doc": "Class for encrypting data."
        },
        createDecipher: {
          "!type": "fn(algorithm: string, password: string) -> +crypto.Decipher",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_createdecipher_algorithm_password",
          "!doc": "Creates and returns a decipher object, with the given algorithm and key. This is the mirror of the createCipher() above."
        },
        createDecipheriv: {
          "!type": "fn(algorithm: string, key: string, iv: string) -> +crypto.Decipher",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_createdecipheriv_algorithm_key_iv",
          "!doc": "Creates and returns a decipher object, with the given algorithm, key and iv. This is the mirror of the createCipheriv() above."
        },
        Decipher: {
          "!type": "fn()",
          prototype: {
            "!proto": "stream.Duplex.prototype",
            update: {
              "!type": "fn(data: +Buffer, input_encoding?: string, output_encoding?: string)",
              "!url": "https://nodejs.org/api/crypto.html#crypto_decipher_update_data_input_encoding_output_encoding",
              "!doc": "Updates the decipher with data, which is encoded in 'binary', 'base64' or 'hex'. If no encoding is provided, then a buffer is expected."
            },
            "final": {
              "!type": "fn(output_encoding?: string) -> +Buffer",
              "!url": "https://nodejs.org/api/crypto.html#crypto_decipher_final_output_encoding",
              "!doc": "Returns any remaining plaintext which is deciphered, with output_encoding being one of: 'binary', 'ascii' or 'utf8'. If no encoding is provided, then a buffer is returned."
            },
            setAutoPadding: {
              "!type": "fn(auto_padding: bool)",
              "!url": "https://nodejs.org/api/crypto.html#crypto_decipher_setautopadding_auto_padding_true",
              "!doc": "You can disable auto padding if the data has been encrypted without standard block padding to prevent decipher.final from checking and removing it. Can only work if the input data's length is a multiple of the ciphers block size. You must call this before streaming data to decipher.update."
            }
          },
          "!url": "https://nodejs.org/api/crypto.html#crypto_class_decipher",
          "!doc": "Class for decrypting data."
        },
        createSign: {
          "!type": "fn(algorithm: string) -> +crypto.Sign",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_createsign_algorithm",
          "!doc": "Creates and returns a signing object, with the given algorithm. On recent OpenSSL releases, openssl list-public-key-algorithms will display the available signing algorithms. Examples are 'RSA-SHA256'."
        },
        Sign: {
          "!type": "fn()",
          prototype: {
            "!proto": "stream.Writable.prototype",
            update: {
              "!type": "fn(data: +Buffer)",
              "!url": "https://nodejs.org/api/crypto.html#crypto_sign_update_data",
              "!doc": "Updates the sign object with data. This can be called many times with new data as it is streamed."
            },
            sign: {
              "!type": "fn(private_key: string, output_format: string) -> +Buffer",
              "!url": "https://nodejs.org/api/crypto.html#crypto_sign_sign_private_key_output_format",
              "!doc": "Calculates the signature on all the updated data passed through the sign. private_key is a string containing the PEM encoded private key for signing."
            }
          },
          "!url": "https://nodejs.org/api/crypto.html#crypto_class_sign",
          "!doc": "Class for generating signatures."
        },
        createVerify: {
          "!type": "fn(algorith: string) -> +crypto.Verify",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_createverify_algorithm",
          "!doc": "Creates and returns a verification object, with the given algorithm. This is the mirror of the signing object above."
        },
        Verify: {
          "!type": "fn()",
          prototype: {
            "!proto": "stream.Writable.prototype",
            update: {
              "!type": "fn(data: +Buffer)",
              "!url": "https://nodejs.org/api/crypto.html#crypto_verifier_update_data",
              "!doc": "Updates the verifier object with data. This can be called many times with new data as it is streamed."
            },
            verify: {
              "!type": "fn(object: string, signature: string, signature_format?: string) -> bool",
              "!url": "https://nodejs.org/api/crypto.html#crypto_verifier_verify_object_signature_signature_format",
              "!doc": "Verifies the signed data by using the object and signature. object is a string containing a PEM encoded object, which can be one of RSA public key, DSA public key, or X.509 certificate. signature is the previously calculated signature for the data, in the signature_format which can be 'binary', 'hex' or 'base64'. If no encoding is specified, then a buffer is expected."
            }
          },
          "!url": "https://nodejs.org/api/crypto.html#crypto_class_verify",
          "!doc": "Class for verifying signatures."
        },
        createDiffieHellman: {
          "!type": "fn(prime: number, encoding?: string) -> +crypto.DiffieHellman",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_creatediffiehellman_prime_length",
          "!doc": "Creates a Diffie-Hellman key exchange object and generates a prime of the given bit length. The generator used is 2."
        },
        DiffieHellman: {
          "!type": "fn()",
          prototype: {
            generateKeys: {
              "!type": "fn(encoding?: string) -> +Buffer",
              "!url": "https://nodejs.org/api/crypto.html#crypto_diffiehellman_generatekeys_encoding",
              "!doc": "Generates private and public Diffie-Hellman key values, and returns the public key in the specified encoding. This key should be transferred to the other party. Encoding can be 'binary', 'hex', or 'base64'. If no encoding is provided, then a buffer is returned."
            },
            computeSecret: {
              "!type": "fn(other_public_key: +Buffer, input_encoding?: string, output_encoding?: string) -> +Buffer",
              "!url": "https://nodejs.org/api/crypto.html#crypto_diffiehellman_computesecret_other_public_key_input_encoding_output_encoding",
              "!doc": "Computes the shared secret using other_public_key as the other party's public key and returns the computed shared secret. Supplied key is interpreted using specified input_encoding, and secret is encoded using specified output_encoding. Encodings can be 'binary', 'hex', or 'base64'. If the input encoding is not provided, then a buffer is expected."
            },
            getPrime: {
              "!type": "fn(encoding?: string) -> +Buffer",
              "!url": "https://nodejs.org/api/crypto.html#crypto_diffiehellman_getprime_encoding",
              "!doc": "Returns the Diffie-Hellman prime in the specified encoding, which can be 'binary', 'hex', or 'base64'. If no encoding is provided, then a buffer is returned."
            },
            getGenerator: {
              "!type": "fn(encoding: string) -> +Buffer",
              "!url": "https://nodejs.org/api/crypto.html#crypto_diffiehellman_getgenerator_encoding",
              "!doc": "Returns the Diffie-Hellman prime in the specified encoding, which can be 'binary', 'hex', or 'base64'. If no encoding is provided, then a buffer is returned."
            },
            getPublicKey: {
              "!type": "fn(encoding?: string) -> +Buffer",
              "!url": "https://nodejs.org/api/crypto.html#crypto_diffiehellman_getpublickey_encoding",
              "!doc": "Returns the Diffie-Hellman public key in the specified encoding, which can be 'binary', 'hex', or 'base64'. If no encoding is provided, then a buffer is returned."
            },
            getPrivateKey: {
              "!type": "fn(encoding?: string) -> +Buffer",
              "!url": "https://nodejs.org/api/crypto.html#crypto_diffiehellman_getprivatekey_encoding",
              "!doc": "Returns the Diffie-Hellman private key in the specified encoding, which can be 'binary', 'hex', or 'base64'. If no encoding is provided, then a buffer is returned."
            },
            setPublicKey: {
              "!type": "fn(public_key: +Buffer, encoding?: string)",
              "!url": "https://nodejs.org/api/crypto.html#crypto_diffiehellman_setpublickey_public_key_encoding",
              "!doc": "Sets the Diffie-Hellman public key. Key encoding can be 'binary', 'hex' or 'base64'. If no encoding is provided, then a buffer is expected."
            },
            setPrivateKey: {
              "!type": "fn(public_key: +Buffer, encoding?: string)",
              "!url": "https://nodejs.org/api/crypto.html#crypto_diffiehellman_setprivatekey_private_key_encoding",
              "!doc": "Sets the Diffie-Hellman private key. Key encoding can be 'binary', 'hex' or 'base64'. If no encoding is provided, then a buffer is expected."
            }
          },
          "!url": "https://nodejs.org/api/crypto.html#crypto_class_diffiehellman",
          "!doc": "The class for creating Diffie-Hellman key exchanges."
        },
        getDiffieHellman: {
          "!type": "fn(group_name: string) -> +crypto.DiffieHellman",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_getdiffiehellman_group_name",
          "!doc": "Creates a predefined Diffie-Hellman key exchange object. The supported groups are: 'modp1', 'modp2', 'modp5' (defined in RFC 2412) and 'modp14', 'modp15', 'modp16', 'modp17', 'modp18' (defined in RFC 3526). The returned object mimics the interface of objects created by crypto.createDiffieHellman() above, but will not allow to change the keys (with diffieHellman.setPublicKey() for example). The advantage of using this routine is that the parties don't have to generate nor exchange group modulus beforehand, saving both processor and communication time."
        },
        pbkdf2: {
          "!type": "fn(password: string, salt: string, iterations: number, keylen: number, callback: fn(err: +Error, derivedKey: string))",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_pbkdf2_password_salt_iterations_keylen_callback",
          "!doc": "Asynchronous PBKDF2 applies pseudorandom function HMAC-SHA1 to derive a key of given length from the given password, salt and iterations. The callback gets two arguments (err, derivedKey)."
        },
        pbkdf2Sync: {
          "!type": "fn(password: string, salt: string, iterations: number, keylen: number) -> string",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_pbkdf2sync_password_salt_iterations_keylen",
          "!doc": "Synchronous PBKDF2 function. Returns derivedKey or throws error."
        },
        randomBytes: {
          "!type": "fn(size: number, callback?: fn(err: +Error, buf: +Buffer))",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback",
          "!doc": "Generates cryptographically strong pseudo-random data."
        },
        pseudoRandomBytes: {
          "!type": "fn(size: number, callback?: fn(err: +Error, buf: +Buffer))",
          "!url": "https://nodejs.org/api/crypto.html#crypto_crypto_pseudorandombytes_size_callback",
          "!doc": "Generates non-cryptographically strong pseudo-random data. The data returned will be unique if it is sufficiently long, but is not necessarily unpredictable. For this reason, the output of this function should never be used where unpredictability is important, such as in the generation of encryption keys."
        },
        DEFAULT_ENCODING: "string"
      },
      util: {
        "!url": "https://nodejs.org/api/util.html",
        "!doc": "The util module is primarily designed to support the needs of Node's internal APIs. Many of these utilities are useful for your own programs. If you find that these functions are lacking for your purposes, however, you are encouraged to write your own utilities. We are not interested in any future additions to the util module that are unnecessary for Node's internal functionality.",
        format: {
          "!type": "fn(format: string) -> string",
          "!url": "https://nodejs.org/api/util.html#util_util_format_format",
          "!doc": "Returns a formatted string using the first argument as a printf-like format."
        },
        debug: {
          "!type": "fn(msg: string)",
          "!url": "https://nodejs.org/api/util.html#util_util_debug_string",
          "!doc": "A synchronous output function. Will block the process and output string immediately to stderr."
        },
        error: {
          "!type": "fn(msg: string)",
          "!url": "https://nodejs.org/api/util.html#util_util_error",
          "!doc": "Same as util.debug() except this will output all arguments immediately to stderr."
        },
        puts: {
          "!type": "fn(data: string)",
          "!url": "https://nodejs.org/api/util.html#util_util_puts",
          "!doc": "A synchronous output function. Will block the process and output all arguments to stdout with newlines after each argument."
        },
        print: {
          "!type": "fn(data: string)",
          "!url": "https://nodejs.org/api/util.html#util_util_print",
          "!doc": "A synchronous output function. Will block the process, cast each argument to a string then output to stdout. Does not place newlines after each argument."
        },
        log: {
          "!type": "fn(string: string)",
          "!url": "https://nodejs.org/api/util.html#util_util_log_string",
          "!doc": "Output with timestamp on stdout."
        },
        inspect: {
          "!type": "fn(object: ?, options: ?) -> string",
          "!url": "https://nodejs.org/api/util.html#util_util_inspect_object_options",
          "!doc": "Return a string representation of object, which is useful for debugging."
        },
        isArray: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isarray_object",
          "!doc": "Returns true if the given \"object\" is an Array. false otherwise."
        },
        isRegExp: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isregexp_object",
          "!doc": "Returns true if the given \"object\" is a RegExp. false otherwise."
        },
        isDate: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isdate_object",
          "!doc": "Returns true if the given \"object\" is a Date. false otherwise."
        },
        isError: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_iserror_object",
          "!doc": "Returns true if the given \"object\" is an Error. false otherwise."
        },
        isBoolean: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isboolean_object",
          "!doc": "Returns true if the given \"object\" is a Boolean. false otherwise."
        },
        isNull: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isnull_object",
          "!doc": "Returns true if the given \"object\" is strictly null. false otherwise."
        },
        isNullOrUndefined: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isnullorundefined_object",
          "!doc": "Returns true if the given \"object\" is null or undefined. false otherwise."
        },
        isNumber: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isnumber_object",
          "!doc": "Returns true if the given \"object\" is a Number. false otherwise."
        },
        isString: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isstring_object",
          "!doc": "Returns true if the given \"object\" is a String. false otherwise."
        },
        isSymbol: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_issymbol_object",
          "!doc": "Returns true if the given \"object\" is a Symbol. false otherwise."
        },
        isUndefined: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isundefined_object",
          "!doc": "Returns true if the given \"object\" is undefined. false otherwise."
        },
        isObject: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isobject_object",
          "!doc": "Returns true if the given \"object\" is strictly an Object and not a Function. false otherwise."
        },
        isFunction: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isfunction_object",
          "!doc": "Returns true if the given \"object\" is a Function. false otherwise."
        },
        isPrimitive: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isprimitive_object",
          "!doc": "Returns true if the given \"object\" is a primitive type. false otherwise."
        },
        isBuffer: {
          "!type": "fn(object: ?) -> bool",
          "!url": "https://nodejs.org/api/util.html#util_util_isbuffer_object",
          "!doc": "Returns true if the given \"object\" is a Buffer. false otherwise."
        },
        inherits: {
          "!type": "fn(constructor: ?, superConstructor: ?)",
          "!url": "https://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor",
          "!doc": "Inherit the prototype methods from one constructor into another. The prototype of constructor will be set to a new object created from superConstructor."
        }
      },
      assert: {
        "!url": "https://nodejs.org/api/assert.html",
        "!doc": "The assert module provides a simple set of assertion tests that can be used to test invariants. The module is intended for internal use by Node.js, but can be used in application code via require('assert'). However, assert is not a testing framework, and is not intended to be used as a general purpose assertion library.",
        "!type": "fn(value: ?, message?: string)",
        ok: {
          "!type": "fn(value: ?, message?: string)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_value_message_assert_ok_value_message",
          "!doc": "Tests if value is truthy. It is equivalent to assert.equal(!!value, true, message)."
        },
        deepEqual: {
          "!type": "fn(actual: ?, expected: ?, message?: string)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_deepequal_actual_expected_message",
          "!doc": "Tests for deep equality between the actual and expected parameters. Primitive values are compared with the equal comparison operator ( == )."
        },
        deepStrictEqual: {
          "!type": "fn(actual: ?, expected: ?, message?: string)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_deepstrictequal_actual_expected_message",
          "!doc": "Generally identical to assert.deepEqual with the exception that primitive values are compared using the strict equality operator ( === )."
        },
        doesNotThrow: {
          "!type": "fn(block: fn(), error?: ?, message?: string)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_doesnotthrow_block_error_message",
          "!doc": "Asserts that the function block does not throw an error. See assert.throws() for more details."
        },
        equal: {
          "!type": "fn(actual: ?, expected: ?, message?: string)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_equal_actual_expected_message",
          "!doc": "Tests shallow, coercive equality between the actual and expected parameters using the equal comparison operator ( == )."
        },
        fail: {
          "!type": "fn(actual: ?, expected: ?, message: string, operator: string)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_fail_actual_expected_message_operator",
          "!doc": "Throws an AssertionError. If message is falsy, the error message is set as the values of actual and expected separated by the provided operator. Otherwise, the error message is the value of message."
        },
        ifError: {
          "!type": "fn(value: ?)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_iferror_value",
          "!doc": "Throws value if value is truthy. This is useful when testing the error argument in callbacks."
        },
        notDeepEqual: {
          "!type": "fn(actual: ?, expected: ?, message?: string)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_notdeepequal_actual_expected_message",
          "!doc": "Tests for any deep inequality. Opposite of assert.deepEqual."
        },
        notDeepStrictEqual: {
          "!type": "fn(actual: ?, expected: ?, message?: string)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_notdeepstrictequal_actual_expected_message",
          "!doc": "Tests for deep strict inequality. Opposite of assert.deepStrictEqual."
        },
        notEqual: {
          "!type": "fn(actual: ?, expected: ?, message?: string)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_notequal_actual_expected_message",
          "!doc": "Tests shallow, coercive inequality with the not equal comparison operator ( != )."
        },
        notStrictEqual: {
          "!type": "fn(actual: ?, expected: ?, message?: string)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_notstrictequal_actual_expected_message",
          "!doc": "Tests strict inequality as determined by the strict not equal operator ( !== )."
        },
        strictEqual: {
          "!type": "fn(actual: ?, expected: ?, message?: string)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_strictequal_actual_expected_message",
          "!doc": "Tests strict equality as determined by the strict equality operator ( === )."
        },
        throws: {
          "!type": "fn(block: fn(), error?: ?, message?: string)",
          "!url": "https://nodejs.org/api/assert.html#assert_assert_throws_block_error_message",
          "!doc": "Expects the function block to throw an error. If specified, error can be a constructor, RegExp, or validation function."
        }
      },
      tty: {
        "!url": "https://nodejs.org/api/tty.html",
        "!doc": "The tty module houses the tty.ReadStream and tty.WriteStream classes. In most cases, you will not need to use this module directly.",
        isatty: {
          "!type": "fn(fd: number) -> bool",
          "!url": "https://nodejs.org/api/tty.html#tty_tty_isatty_fd",
          "!doc": "Returns true or false depending on if the fd is associated with a terminal."
        }
      },
      domain: {
        "!url": "https://nodejs.org/api/domain.html",
        "!doc": "Domains provide a way to handle multiple different IO operations as a single group. If any of the event emitters or callbacks registered to a domain emit an error event, or throw an error, then the domain object will be notified, rather than losing the context of the error in the process.on('uncaughtException') handler, or causing the program to exit immediately with an error code.",
        create: {
          "!type": "fn() -> +events.EventEmitter",
          "!url": "https://nodejs.org/api/domain.html#domain_domain_create",
          "!doc": "Returns a new Domain object."
        },
        Domain: {
          "!type": "fn()",
          prototype: {
            "!proto": "events.EventEmitter.prototype",
            run: {
              "!type": "fn(fn: fn())",
              "!url": "https://nodejs.org/api/domain.html#domain_domain_run_fn",
              "!doc": "Run the supplied function in the context of the domain, implicitly binding all event emitters, timers, and lowlevel requests that are created in that context."
            },
            members: {
              "!type": "[+events.EventEmitter]",
              "!url": "https://nodejs.org/api/domain.html#domain_domain_members",
              "!doc": "An array of timers and event emitters that have been explicitly added to the domain."
            },
            add: {
              "!type": "fn(emitter: +events.EventEmitter)",
              "!url": "https://nodejs.org/api/domain.html#domain_domain_add_emitter",
              "!doc": "Explicitly adds an emitter to the domain. If any event handlers called by the emitter throw an error, or if the emitter emits an error event, it will be routed to the domain's error event, just like with implicit binding."
            },
            remove: {
              "!type": "fn(emitter: +events.EventEmitter)",
              "!url": "https://nodejs.org/api/domain.html#domain_domain_remove_emitter",
              "!doc": "The opposite of domain.add(emitter). Removes domain handling from the specified emitter."
            },
            bind: {
              "!type": "fn(callback: fn(err: +Error, data: ?)) -> !0",
              "!url": "https://nodejs.org/api/domain.html#domain_domain_bind_callback",
              "!doc": "The returned function will be a wrapper around the supplied callback function. When the returned function is called, any errors that are thrown will be routed to the domain's error event."
            },
            intercept: {
              "!type": "fn(cb: fn(data: ?)) -> !0",
              "!url": "https://nodejs.org/api/domain.html#domain_domain_intercept_callback",
              "!doc": "This method is almost identical to domain.bind(callback). However, in addition to catching thrown errors, it will also intercept Error objects sent as the first argument to the function."
            },
            dispose: {
              "!type": "fn()",
              "!url": "https://nodejs.org/api/domain.html#domain_domain_dispose",
              "!doc": "The dispose method destroys a domain, and makes a best effort attempt to clean up any and all IO that is associated with the domain. Streams are aborted, ended, closed, and/or destroyed. Timers are cleared. Explicitly bound callbacks are no longer called. Any error events that are raised as a result of this are ignored."
            }
          },
          "!url": "https://nodejs.org/api/domain.html#domain_class_domain",
          "!doc": "The Domain class encapsulates the functionality of routing errors and uncaught exceptions to the active Domain object."
        }
      },
      "os.cpuSpec": {
        model: "string",
        speed: "number",
        times: {
          user: "number",
          nice: "number",
          sys: "number",
          idle: "number",
          irq: "number"
        }
      },
      "process.memoryUsage.type": {
        rss: "number",
        heapTotal: "?",
        number: "?",
        heapUsed: "number"
      },
      "net.address": {
        port: "number",
        family: "string",
        address: "string"
      },
      "url.type": {
        href: "string",
        protocol: "string",
        auth: "string",
        hostname: "string",
        port: "string",
        host: "string",
        pathname: "string",
        search: "string",
        query: "string",
        slashes: "bool",
        hash: "string"
      },
      "tls.Server.credentials": {
        key: "string",
        cert: "string",
        ca: "string"
      },
      "tls.cipher": {
        name: "string",
        version: "string"
      },
      "crypto.credentials": {
        pfx: "string",
        key: "string",
        passphrase: "string",
        cert: "string",
        ca: "string",
        crl: "string",
        ciphers: "string"
      },
      buffer: {
        "!url": "https://nodejs.org/api/buffer.html",
        "!doc": "Prior to the introduction of TypedArray in ECMAScript 2015 (ES6), the JavaScript language had no mechanism for reading or manipulating streams of binary data. The Buffer class was introduced as part of the Node.js API to make it possible to interact with octet streams in the context of things like TCP streams and file system operations.",
        Buffer: "Buffer",
        SlowBuffer: "Buffer",
        INSPECT_MAX_BYTES: {
          "!type": "number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buffer_inspect_max_bytes",
          "!doc": "Returns the maximum number of bytes that will be returned when buffer.inspect() is called. This can be overridden by user modules. See util.inspect() for more details on buffer.inspect() behavior."
        }
      },
      module: {},
      timers: {
        clearImmediate: {
          "!type": "fn(immediateObject: ?)",
          "!url": "https://nodejs.org/api/timers.html#timers_clearimmediate_immediateobject",
          "!doc": "Stops an immediate from triggering."
        },
        clearInterval: {
          "!type": "fn(intervalObject: timers.Timer)",
          "!url": "https://nodejs.org/api/timers.html#timers_clearinterval_intervalobject",
          "!doc": "Stops an interval from triggering."
        },
        clearTimeout: {
          "!type": "fn(timeoutObject: timers.Timer)",
          "!url": "https://nodejs.org/api/timers.html#timers_cleartimeout_timeoutobject",
          "!doc": "Prevents a timeout from triggering."
        },
        setImmediate: {
          "!type": "fn(callback: fn(), args?: ?) -> ?",
          "!url": "https://nodejs.org/api/timers.html#timers_setimmediate_callback_arg",
          "!doc": "To schedule the \"immediate\" execution of callback after I/O events callbacks and before setTimeout and setInterval. Returns an immediateObject for possible use with clearImmediate(). Optionally you can also pass arguments to the callback."
        },
        setInterval: {
          "!type": "fn(callback: fn(), delay: number, args?: ?) -> timers.Timer",
          "!url": "https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_arg",
          "!doc": "To schedule the repeated execution of callback every delay milliseconds. Returns a intervalObject for possible use with clearInterval(). Optionally you can also pass arguments to the callback."
        },
        setTimeout: {
          "!type": "fn(callback: fn(), delay: number, args?: ?) -> timers.Timer",
          "!url": "https://nodejs.org/api/timers.html#timers_settimeout_callback_delay_arg",
          "!doc": "To schedule execution of a one-time callback after delay milliseconds. Returns a timeoutObject for possible use with clearTimeout(). Optionally you can also pass arguments to the callback."
        },
        Timer: {
          ref: {
            "!type": "fn() -> timers.Timer",
            "!url": "https://nodejs.org/api/timers.html#timers_ref",
            "!doc": "If you had previously unref()d a timer you can call ref() to explicitly request the timer hold the program open. If the timer is already refd calling ref again will have no effect."
          },
          unref: {
            "!type": "fn() -> timers.Timer",
            "!url": "https://nodejs.org/api/timers.html#timers_unref",
            "!doc": "The opaque value returned by setTimeout and setInterval also has the method timer.unref() which will allow you to create a timer that is active but if it is the only item left in the event loop, it won't keep the program running. If the timer is already unrefd calling unref again will have no effect."
          }
        }
      }
    },
    process: {
      stdout: {
        "!type": "+stream.Writable",
        "!url": "https://nodejs.org/api/process.html#process_process_stdout",
        "!doc": "A Writable Stream to stdout."
      },
      stderr: {
        "!type": "+stream.Writable",
        "!url": "https://nodejs.org/api/process.html#process_process_stderr",
        "!doc": "A writable stream to stderr."
      },
      stdin: {
        "!type": "+stream.Readable",
        "!url": "https://nodejs.org/api/process.html#process_process_stdin",
        "!doc": "A Readable Stream for stdin. The stdin stream is paused by default, so one must call process.stdin.resume() to read from it."
      },
      argv: {
        "!type": "[string]",
        "!url": "https://nodejs.org/api/process.html#process_process_argv",
        "!doc": "An array containing the command line arguments. The first element will be 'node', the second element will be the name of the JavaScript file. The next elements will be any additional command line arguments."
      },
      execPath: {
        "!type": "string",
        "!url": "https://nodejs.org/api/process.html#process_process_execpath",
        "!doc": "This is the absolute pathname of the executable that started the process."
      },
      abort: {
        "!type": "fn()",
        "!url": "https://nodejs.org/api/process.html#process_process_abort",
        "!doc": "This causes node to emit an abort. This will cause node to exit and generate a core file."
      },
      chdir: {
        "!type": "fn(directory: string)",
        "!url": "https://nodejs.org/api/process.html#process_process_chdir_directory",
        "!doc": "Changes the current working directory of the process or throws an exception if that fails."
      },
      cwd: {
        "!type": "fn()",
        "!url": "https://nodejs.org/api/process.html#process_process_cwd",
        "!doc": "Returns the current working directory of the process."
      },
      env: {
        "!url": "https://nodejs.org/api/process.html#process_process_env",
        "!doc": "An object containing the user environment."
      },
      exit: {
        "!type": "fn(code?: number)",
        "!url": "https://nodejs.org/api/process.html#process_process_exit_code",
        "!doc": "Ends the process with the specified code. If omitted, exit uses the 'success' code 0."
      },
      getgid: {
        "!type": "fn() -> number",
        "!url": "https://nodejs.org/api/process.html#process_process_getgid",
        "!doc": "Gets the group identity of the process. This is the numerical group id, not the group name."
      },
      setgid: {
        "!type": "fn(id: number)",
        "!url": "https://nodejs.org/api/process.html#process_process_setgid_id",
        "!doc": "Sets the group identity of the process. This accepts either a numerical ID or a groupname string. If a groupname is specified, this method blocks while resolving it to a numerical ID."
      },
      getuid: {
        "!type": "fn() -> number",
        "!url": "https://nodejs.org/api/process.html#process_process_getuid",
        "!doc": "Gets the user identity of the process. This is the numerical userid, not the username."
      },
      setuid: {
        "!type": "fn(id: number)",
        "!url": "https://nodejs.org/api/process.html#process_process_setuid_id",
        "!doc": "Sets the user identity of the process. This accepts either a numerical ID or a username string. If a username is specified, this method blocks while resolving it to a numerical ID."
      },
      version: {
        "!type": "string",
        "!url": "https://nodejs.org/api/process.html#process_process_version",
        "!doc": "A compiled-in property that exposes NODE_VERSION."
      },
      versions: {
        http_parser: "string",
        node: "string",
        v8: "string",
        ares: "string",
        uv: "string",
        zlib: "string",
        openssl: "string",
        "!url": "https://nodejs.org/api/process.html#process_process_versions",
        "!doc": "A property exposing version strings of node and its dependencies."
      },
      config: {
        target_defaults: {
          cflags: "[?]",
          default_configuration: "string",
          defines: "[string]",
          include_dirs: "[string]",
          libraries: "[string]"
        },
        variables: {
          clang: "number",
          host_arch: "string",
          node_install_npm: "bool",
          node_install_waf: "bool",
          node_prefix: "string",
          node_shared_openssl: "bool",
          node_shared_v8: "bool",
          node_shared_zlib: "bool",
          node_use_dtrace: "bool",
          node_use_etw: "bool",
          node_use_openssl: "bool",
          target_arch: "string",
          v8_no_strict_aliasing: "number",
          v8_use_snapshot: "bool",
          visibility: "string"
        },
        "!url": "https://nodejs.org/api/process.html#process_process_config",
        "!doc": "An Object containing the JavaScript representation of the configure options that were used to compile the current node executable. This is the same as the \"config.gypi\" file that was produced when running the ./configure script."
      },
      kill: {
        "!type": "fn(pid: number, signal?: string)",
        "!url": "https://nodejs.org/api/process.html#process_process_kill_pid_signal",
        "!doc": "Send a signal to a process. pid is the process id and signal is the string describing the signal to send. Signal names are strings like 'SIGINT' or 'SIGUSR1'. If omitted, the signal will be 'SIGTERM'."
      },
      pid: {
        "!type": "number",
        "!url": "https://nodejs.org/api/process.html#process_process_pid",
        "!doc": "The PID of the process."
      },
      title: {
        "!type": "string",
        "!url": "https://nodejs.org/api/process.html#process_process_title",
        "!doc": "Getter/setter to set what is displayed in 'ps'."
      },
      arch: {
        "!type": "string",
        "!url": "https://nodejs.org/api/process.html#process_process_arch",
        "!doc": "What processor architecture you're running on: 'arm', 'ia32', or 'x64'."
      },
      platform: {
        "!type": "string",
        "!url": "https://nodejs.org/api/process.html#process_process_platform",
        "!doc": "What platform you're running on: 'darwin', 'freebsd', 'linux', 'sunos' or 'win32'"
      },
      memoryUsage: {
        "!type": "fn() -> process.memoryUsage.type",
        "!url": "https://nodejs.org/api/process.html#process_process_memoryusage",
        "!doc": "Returns an object describing the memory usage of the Node process measured in bytes."
      },
      nextTick: {
        "!type": "fn(callback: fn())",
        "!url": "https://nodejs.org/api/process.html#process_process_nexttick_callback",
        "!doc": "On the next loop around the event loop call this callback. This is not a simple alias to setTimeout(fn, 0), it's much more efficient. It typically runs before any other I/O events fire, but there are some exceptions."
      },
      maxTickDepth: {
        "!type": "number",
        "!url": "https://nodejs.org/api/process.html#process_process_maxtickdepth",
        "!doc": "The maximum depth of nextTick-calling nextTick-callbacks that will be evaluated before allowing other forms of I/O to occur."
      },
      umask: {
        "!type": "fn(mask?: number) -> number",
        "!url": "https://nodejs.org/api/process.html#process_process_umask_mask",
        "!doc": "Sets or reads the process's file mode creation mask. Child processes inherit the mask from the parent process. Returns the old mask if mask argument is given, otherwise returns the current mask."
      },
      uptime: {
        "!type": "fn() -> number",
        "!url": "https://nodejs.org/api/process.html#process_process_uptime",
        "!doc": "Number of seconds Node has been running."
      },
      hrtime: {
        "!type": "fn() -> [number]",
        "!url": "https://nodejs.org/api/process.html#process_process_hrtime",
        "!doc": "Returns the current high-resolution real time in a [seconds, nanoseconds] tuple Array. It is relative to an arbitrary time in the past. It is not related to the time of day and therefore not subject to clock drift. The primary use is for measuring performance between intervals."
      },
      "!url": "https://nodejs.org/api/globals.html#globals_process",
      "!doc": "The process object."
    },
    global: {
      "!type": "<top>",
      "!url": "https://nodejs.org/api/globals.html#globals_global",
      "!doc": "In browsers, the top-level scope is the global scope. That means that in browsers if you're in the global scope var something will define a global variable. In Node this is different. The top-level scope is not the global scope; var something inside a Node module will be local to that module."
    },
    console: {
      log: {
        "!type": "fn(text: string)",
        "!url": "https://nodejs.org/api/stdio.html#stdio_console_log_data",
        "!doc": "Prints to stdout with newline. This function can take multiple arguments in a printf()-like way."
      },
      info: {
        "!type": "fn(text: string)",
        "!url": "https://nodejs.org/api/stdio.html#stdio_console_info_data",
        "!doc": "Same as console.log."
      },
      error: {
        "!type": "fn(text: string)",
        "!url": "https://nodejs.org/api/stdio.html#stdio_console_error_data",
        "!doc": "Same as console.log but prints to stderr."
      },
      warn: {
        "!type": "fn(text: string)",
        "!url": "https://nodejs.org/api/stdio.html#stdio_console_warn_data",
        "!doc": "Same as console.error."
      },
      dir: {
        "!type": "fn(obj: ?)",
        "!url": "https://nodejs.org/api/stdio.html#stdio_console_dir_obj",
        "!doc": "Uses util.inspect on obj and prints resulting string to stdout."
      },
      time: {
        "!type": "fn(label: string)",
        "!url": "https://nodejs.org/api/stdio.html#stdio_console_time_label",
        "!doc": "Mark a time."
      },
      timeEnd: {
        "!type": "fn(label: string)",
        "!url": "https://nodejs.org/api/stdio.html#stdio_console_timeend_label",
        "!doc": "Finish timer, record output."
      },
      trace: {
        "!type": "fn(label: string)",
        "!url": "https://nodejs.org/api/stdio.html#stdio_console_trace_label",
        "!doc": "Print a stack trace to stderr of the current position."
      },
      assert: {
        "!type": "fn(expression: bool)",
        "!url": "https://nodejs.org/api/stdio.html#stdio_console_assert_expression_message",
        "!doc": "Same as assert.ok() where if the expression evaluates as false throw an AssertionError with message."
      },
      "!url": "https://nodejs.org/api/globals.html#globals_console",
      "!doc": "Used to print to stdout and stderr."
    },
    __filename: {
      "!type": "string",
      "!url": "https://nodejs.org/api/globals.html#globals_filename",
      "!doc": "The filename of the code being executed. This is the resolved absolute path of this code file. For a main program this is not necessarily the same filename used in the command line. The value inside a module is the path to that module file."
    },
    __dirname: {
      "!type": "string",
      "!url": "https://nodejs.org/api/globals.html#globals_dirname",
      "!doc": "The name of the directory that the currently executing script resides in."
    },
    clearImmediate: "timers.clearImmediate",
    clearInterval: "timers.clearInterval",
    clearTimeout: "timers.clearTimeout",
    setImmediate: "timers.setImmediate",
    setInterval: "timers.setInterval",
    setTimeout: "timers.setTimeout",
    Buffer: {
      "!type": "fn() -> +Buffer",
      "!url": "https://nodejs.org/api/buffer.html#buffer_class_buffer",
      "!doc": "The Buffer class is a global type for dealing with binary data directly. It can be constructed in a variety of ways.",
      byteLength: {
        "!type": "fn(string: string, encoding?: string) -> number",
        "!url": "https://nodejs.org/api/buffer.html#buffer_class_method_buffer_bytelength_string_encoding",
        "!doc": "Returns the actual byte length of a string. This is not the same as String.prototype.length since that returns the number of characters in a string."
      },
      compare: {
        "!type": "fn(buf1: +Buffer, buf2: +Buffer) -> number",
        "!url": "https://nodejs.org/api/buffer.html#buffer_class_method_buffer_compare_buf1_buf2",
        "!doc": "Compares buf1 to buf2 typically for the purpose of sorting arrays of Buffers. This is equivalent is calling buf1.compare(buf2)."
      },
      concat: {
        "!type": "fn(list: ?, totalLength?: number) -> +Buffer",
        "!url": "https://nodejs.org/api/buffer.html#buffer_class_method_buffer_concat_list_totallength",
        "!doc": "Returns a new Buffer which is the result of concatenating all the Buffers in the list together."
      },
      isBuffer: {
        "!type": "fn(obj: ?) -> bool",
        "!url": "https://nodejs.org/api/buffer.html#buffer_class_method_buffer_isbuffer_obj",
        "!doc": "Returns 'true' if obj is a Buffer."
      },
      isEncoding: {
        "!type": "fn(encoding: string) -> bool",
        "!url": "https://nodejs.org/api/buffer.html#buffer_class_method_buffer_isencoding_encoding",
        "!doc": "Returns true if the encoding is a valid encoding argument, or false otherwise."
      },
      prototype: {
        "!proto": "Uint8Array.prototype",
        length: {
          "!type": "number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_length",
          "!doc": "Returns the amount of memory allocated for the Buffer in number of bytes. Note that this does not necessarily reflect the amount of usable data within the Buffer. For instance, in the example below, a Buffer with 1234 bytes is allocated, but only 11 ASCII bytes are written."
        },
        compare: {
          "!type": "fn(otherBuffer: +Buffer) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_compare_otherbuffer",
          "!doc": "Compares two Buffer instances and returns a number indicating whether buf comes before, after, or is the same as the otherBuffer in sort order. Comparison is based on the actual sequence of bytes in each Buffer."
        },
        copy: {
          "!type": "fn(targetBuffer: +Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_copy_targetbuffer_targetstart_sourcestart_sourceend",
          "!doc": "Copies data from a region of this Buffer to a region in the target Buffer even if the target memory region overlaps with the source. If undefined, the targetStart and sourceStart parameters default to 0 while sourceEnd defaults to buffer.length."
        },
        entries: {
          "!type": "fn()",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_entries",
          "!doc": "Creates and returns an iterator of [index, byte] pairs from the Buffer contents."
        },
        equals: {
          "!type": "fn(otherBuffer: +Buffer) -> bool",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_equals_otherbuffer",
          "!doc": "Returns a boolean indicating whether this and otherBuffer have exactly the same bytes."
        },
        fill: {
          "!type": "fn(value: ?, offset?: number, end?: number)",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_fill_value_offset_end",
          "!doc": "Fills the Buffer with the specified value. If the offset (defaults to 0) and end (defaults to buffer.length) are not given it will fill the entire Buffer. The method returns a reference to the Buffer so calls can be chained."
        },
        indexOf: {
          "!type": "fn(value: ?, byteOffset?: number, encoding?: string) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_indexof_value_byteoffset_encoding",
          "!doc": "Operates similar to Array#indexOf() in that it returns either the starting index position of value in Buffer or -1 if the Buffer does not contain value. The value can be a String, Buffer or Number. Strings are by default interpreted as UTF8. Buffers will use the entire Buffer (to compare a partial Buffer use Buffer#slice()). Numbers can range from 0 to 255."
        },
        includes: {
          "!type": "fn(value: ?, byteOffset?: number, encoding?: string)",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_includes_value_byteoffset_encoding",
          "!doc": "Operates similar to Array#includes(). The value can be a String, Buffer or Number. Strings are interpreted as UTF8 unless overridden with the encoding argument. Buffers will use the entire Buffer (to compare a partial Buffer use Buffer#slice()). Numbers can range from 0 to 255."
        },
        keys: {
          "!type": "fn()",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_keys",
          "!doc": "Creates and returns an iterator of Buffer keys (indices)."
        },
        readDoubleBE: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readdoublebe_offset_noassert",
          "!doc": "Reads a 64-bit double from the Buffer at the specified offset with specified endian format (readDoubleBE() returns big endian, readDoubleLE() returns little endian)."
        },
        readDoubleLE: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readdoublele_offset_noassert",
          "!doc": "Reads a 64-bit double from the Buffer at the specified offset with specified endian format (readDoubleBE() returns big endian, readDoubleLE() returns little endian)."
        },
        readFloatBE: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readfloatbe_offset_noassert",
          "!doc": "Reads a 32-bit float from the Buffer at the specified offset with specified endian format (readFloatBE() returns big endian, readFloatLE() returns little endian)."
        },
        readFloatLE: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readfloatle_offset_noassert",
          "!doc": "Reads a 32-bit float from the Buffer at the specified offset with specified endian format (readFloatBE() returns big endian, readFloatLE() returns little endian)."
        },
        readInt8: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readint8_offset_noassert",
          "!doc": "Reads a signed 8-bit integer from the Buffer at the specified offset."
        },
        readInt16BE: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readint16be_offset_noassert",
          "!doc": "Reads a signed 16-bit integer from the Buffer at the specified offset with the specified endian format (readInt16BE() returns big endian, readInt16LE() returns little endian)."
        },
        readInt16LE: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readint16le_offset_noassert",
          "!doc": "Reads a signed 16-bit integer from the Buffer at the specified offset with the specified endian format (readInt16BE() returns big endian, readInt16LE() returns little endian)."
        },
        readInt32BE: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readint32be_offset_noassert",
          "!doc": "Reads a signed 32-bit integer from the Buffer at the specified offset with the specified endian format (readInt32BE() returns big endian, readInt32LE() returns little endian)."
        },
        readInt32LE: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readint32le_offset_noassert",
          "!doc": "Reads a signed 32-bit integer from the Buffer at the specified offset with the specified endian format (readInt32BE() returns big endian, readInt32LE() returns little endian)."
        },
        readIntBE: {
          "!type": "fn(offset: number, byteLength: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readintbe_offset_bytelength_noassert",
          "!doc": "Reads byteLength number of bytes from the Buffer at the specified offset and interprets the result as a two's complement signed value. Supports up to 48 bits of accuracy. For example:"
        },
        readIntLE: {
          "!type": "fn(offset: number, byteLength: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readintle_offset_bytelength_noassert",
          "!doc": "Reads byteLength number of bytes from the Buffer at the specified offset and interprets the result as a two's complement signed value. Supports up to 48 bits of accuracy. For example:"
        },
        readUInt8: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readuint8_offset_noassert",
          "!doc": "Reads an unsigned 8-bit integer from the Buffer at the specified offset."
        },
        readUInt16BE: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readuint16be_offset_noassert",
          "!doc": "Reads an unsigned 16-bit integer from the Buffer at the specified offset with specified endian format (readInt32BE() returns big endian, readInt32LE() returns little endian)."
        },
        readUInt16LE: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readuint16le_offset_noassert",
          "!doc": "Reads an unsigned 16-bit integer from the Buffer at the specified offset with specified endian format (readInt32BE() returns big endian, readInt32LE() returns little endian)."
        },
        readUInt32BE: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readuint32be_offset_noassert",
          "!doc": "Reads an unsigned 32-bit integer from the Buffer at the specified offset with specified endian format (readInt32BE() returns big endian, readInt32LE() returns little endian)."
        },
        readUInt32LE: {
          "!type": "fn(offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readuint32le_offset_noassert",
          "!doc": "Reads an unsigned 32-bit integer from the Buffer at the specified offset with specified endian format (readInt32BE() returns big endian, readInt32LE() returns little endian)."
        },
        readUIntBE: {
          "!type": "fn(offset: number, byteLength: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readuintbe_offset_bytelength_noassert",
          "!doc": "Reads byteLength number of bytes from the Buffer at the specified offset and interprets the result as an unsigned integer. Supports up to 48 bits of accuracy. For example:"
        },
        readUIntLE: {
          "!type": "fn(offset: number, byteLength: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_readuintle_offset_bytelength_noassert",
          "!doc": "Reads byteLength number of bytes from the Buffer at the specified offset and interprets the result as an unsigned integer. Supports up to 48 bits of accuracy. For example:"
        },
        slice: {
          "!type": "fn(start: number, end?: number)",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_slice_start_end",
          "!doc": "Returns a new Buffer that references the same memory as the original, but offset and cropped by the start (defaults to 0) and end (defaults to buffer.length) indexes."
        },
        toString: {
          "!type": "fn(encoding?: string, start?: number, end?: number) -> string",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_tostring_encoding_start_end",
          "!doc": "Decodes and returns a string from the Buffer data using the specified character set encoding."
        },
        toJSON: {
          "!type": "fn()",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_tojson",
          "!doc": "Returns a JSON representation of the Buffer instance. JSON.stringify implicitly calls this function when stringifying a Buffer instance."
        },
        values: {
          "!type": "fn()",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_values",
          "!doc": "Creates and returns an iterator for Buffer values (bytes). This function is called automatically when the Buffer is used in a for..of statement."
        },
        write: {
          "!type": "fn(string: string, offset?: number, length?: number, encoding?: string) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_write_string_offset_length_encoding",
          "!doc": "Writes string to the Buffer at offset using the given encoding. The length parameter is the number of bytes to write. If the Buffer did not contain enough space to fit the entire string, only a partial amount of the string will be written however, the will not write only partially encoded characters."
        },
        writeDoubleBE: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writedoublebe_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset with specified endian format (writeDoubleBE() writes big endian, writeDoubleLE() writes little endian). The value argument must be a valid 64-bit double."
        },
        writeDoubleLE: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writedoublele_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset with specified endian format (writeDoubleBE() writes big endian, writeDoubleLE() writes little endian). The value argument must be a valid 64-bit double."
        },
        writeFloatBE: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writefloatbe_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset with specified endian format (writeFloatBE() writes big endian, writeFloatLE() writes little endian). Behavior is unspecified if value is anything other than a 32-bit float."
        },
        writeFloatLE: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writefloatle_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset with specified endian format (writeFloatBE() writes big endian, writeFloatLE() writes little endian). Behavior is unspecified if value is anything other than a 32-bit float."
        },
        writeInt8: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeint8_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset. The value must be a valid signed 8-bit integer."
        },
        writeInt16BE: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeint16be_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset with specified endian format (writeInt16BE() writes big endian, writeInt16LE() writes little endian). The value must be a valid signed 16-bit integer."
        },
        writeInt16LE: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeint16le_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset with specified endian format (writeInt16BE() writes big endian, writeInt16LE() writes little endian). The value must be a valid signed 16-bit integer."
        },
        writeInt32BE: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeint32be_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset with specified endian format (writeInt32BE() writes big endian, writeInt32LE() writes little endian). The value must be a valid signed 32-bit integer."
        },
        writeInt32LE: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeint32le_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset with specified endian format (writeInt32BE() writes big endian, writeInt32LE() writes little endian). The value must be a valid signed 32-bit integer."
        },
        writeIntBE: {
          "!type": "fn(value: number, offset: number, byteLength: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeintbe_value_offset_bytelength_noassert",
          "!doc": "Writes value to the Buffer at the specified offset and byteLength. Supports up to 48 bits of accuracy. For example:"
        },
        writeIntLE: {
          "!type": "fn(value: number, offset: number, byteLength: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeintle_value_offset_bytelength_noassert",
          "!doc": "Writes value to the Buffer at the specified offset and byteLength. Supports up to 48 bits of accuracy. For example:"
        },
        writeUInt8: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeuint8_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset. The value must be a valid unsigned 8-bit integer."
        },
        writeUInt16BE: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeuint16be_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset with specified endian format (writeUInt16BE() writes big endian, writeUInt16LE() writes little endian). The value must be a valid unsigned 16-bit integer."
        },
        writeUInt16LE: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeuint16le_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset with specified endian format (writeUInt16BE() writes big endian, writeUInt16LE() writes little endian). The value must be a valid unsigned 16-bit integer."
        },
        writeUInt32BE: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeuint32be_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset with specified endian format (writeUInt32BE() writes big endian, writeUInt32LE() writes little endian). The value must be a valid unsigned 32-bit integer."
        },
        writeUInt32LE: {
          "!type": "fn(value: number, offset: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeuint32le_value_offset_noassert",
          "!doc": "Writes value to the Buffer at the specified offset with specified endian format (writeUInt32BE() writes big endian, writeUInt32LE() writes little endian). The value must be a valid unsigned 32-bit integer."
        },
        writeUIntBE: {
          "!type": "fn(value: number, offset: number, byteLength: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeuintbe_value_offset_bytelength_noassert",
          "!doc": "Writes value to the Buffer at the specified offset and byteLength. Supports up to 48 bits of accuracy. For example:"
        },
        writeUIntLE: {
          "!type": "fn(value: number, offset: number, byteLength: number, noAssert?: bool) -> number",
          "!url": "https://nodejs.org/api/buffer.html#buffer_buf_writeuintle_value_offset_bytelength_noassert",
          "!doc": "Writes value to the Buffer at the specified offset and byteLength. Supports up to 48 bits of accuracy. For example:"
        }
      }
    }
  };
});

/*******************************************************************************
 * @license
 * Copyright (c) 2014 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd*/
define('javascript/lru',[], function() {

	function node(key, value) {
		var n = Object.create(null);
		n._p = null;
		n._n = null;
		n._v = {
			key: key,
			value: value
		};
		return n;
	}

	/**
	 * @description Creates a new LRU cache with the given maximum size. If no size is given 
	 * an unbounded cache is created.
	 * 
	 * @constructor 
	 * @param {Number} size The maximum size of the LRU or -1 for an unbounded cache
	 * @returns {javascript.LRU} A new LRU instance
	 * @since 8.0
	 */
	function LRU(size) {
		if (typeof size === 'undefined') {
			this._max = -1;
		} else {
			this._max = size;
		}
		this._start = this._end = null;
		this._size = 0;
		this._cache = Object.create(null);
	}

	/**
	 * @description Clears the entire cache
	 * @function
	 */
	LRU.prototype.clear = function clear() {
		this._cache = Object.create(null);
		this._start = null;
		this._end = null;
		this._size = 0;
	};
	/**
	 * @description Returns the current size of the map
	 * @function
	 * @returns {Number} The size of the map
	 */
	LRU.prototype.size = function size() {
		return this._size;
	};
	/**
	 * @description If the map contains the given key
	 * @function
	 * @param {String} key The key to check
	 * @returns {Boolean} If the map contains the key or not
	 */
	LRU.prototype.containsKey = function containsKey(key) {
		return typeof this._cache[key] !== 'undefined';
	};
	/**
	 * @description Adds the given key / value pair to the map. If the addition is
	 * greater than the given maximum map size, the last entry will be removed 
	 * and the new entry added to the head of the map.
	 * 
	 * Putting a value that already exists in the map will move it to the head
	 * of the LRU discarding the existing value.
	 * 
	 * @function
	 * @param {String} key The key to map the given value to
	 * @param {*} value The value to map to the given key
	 */
	LRU.prototype.put = function put(key, value) {
		if (this._max !== -1 && this._size + 1 > this._max) {
			//shuffle one off the end
			this.remove(this._end._v.key);
		}
		this.remove(key); //torch the existing value
		var entry = node(key, value);
		if (!this._start) {
			this._start = this._end = entry;
		} else {
			entry = node(key, value);
			entry._n = this._start;
			this._start._p = entry;
			this._start = entry;
		}
		this._cache[key] = entry;
		this._size++;
	};
	/**
	 * @description Gets the value from the map with the given key. Returns
	 * null if no mapping exists.
	 * @function
	 * @param {String} key The key to look up
	 * @returns {*} The value mapped to the given key
	 */
	LRU.prototype.get = function get(key) {
		if (this._size > 0) {
			var entry = this._cache[key];
			if (entry && entry._v) {
				return entry._v.value;
			}
		}
		return null;
	};
	/**
	 * @description Removes the key and mapped value from the map and returnns
	 * the removed value or null if nothign was removed.
	 * @function
	 * @param {String} key The key to remove
	 * @returns {*} The removed value or null
	 */
	LRU.prototype.remove = function remove(key) {
		if (this._size === 0) {
			return null;
		}
		var entry = this._cache[key];
		if (entry) {
			var p = entry._p;
			if (this._end === entry) {
				this._end = p;
			}
			var n = entry._n;
			if (this._start === entry) {
				this._start = entry._n;
			}
			if (p) {
				p._n = n;
			}
			if (n) {
				n._p = p;
			}
			delete this._cache[key];
			this._size--;
			return entry._v.value;
		}
		return null;
	};
	/**
	 * @description Returns the array of keys found in the map in the order they were inserted,
	 * so for this LRU map the first key would be the oldest mapped value
	 * @function
	 * @returns {String[]} The keys in the map in insertion order
	 */
	LRU.prototype.keys = function keys() {
		var keys = [];
		if (this._end) {
			var n = this._end;
			while (n) {
				keys.push(n._v.key);
				n = n._p;
			}
		}
		return keys;
	};

	return LRU;
});
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
define('javascript/ternPlugins/resolver',[
	"tern/lib/tern",
	"tern/lib/infer",
	"javascript/lru"
], function(tern, infer, LRU) {
	
	var _resolved = new LRU(),
		_files = new LRU();
	
	function moduleResolve(_name /*, parentFile*/) {
		var resolved = getResolved(_name);
		if (resolved && typeof resolved.contents === "string") {
			return resolved;
		}
		return null;
	}
	
	/**
	 * @description Get the resolved file for the given logical name
	 * @param {String} _name The logical name 
	 * @sinnce 9.0
	 */
	function getResolved(_name) {
		var val = _resolved.get(_name);
		if(val) {
			var _f = _files.get(val.file);
			if(_f && _f.file) {
				return _f;
			}
			return val;
		}
		return null;
	}
	
	/**
	 * @description Resolves the computed dependencies
	 * @param {TernServer} server The Tern server
	 * @param {String} loc The original file context location (from the AST)
	 * @since 9.0
	 */
	function resolveDependencies(server, loc) {
	    _resolved.keys().forEach(/* @callback */ function(key) {
	        //we will try again for a timed out read
	        var value = _resolved.get(key);
	        if (value && (value.pending || value.file)) {
	      	  return;
	        }
	  		resolve(server, key, loc);
	    });
	}
	
	/**
	 * @description Resolves the given key (logical name) via the server. This function starts an asynchronous job to resolve the
	 * script via the scriptResolver in the client
	 * @param {TernServer} server The server
	 * @param {String} key The logical name to resolve
	 * @param {String} loc The original file context location (from the AST)
	 * @since 9.0
	 */
	function resolve(server, key, loc) {
		var item = _resolved.get(key);
		if(item.pending || item.err) {
			//if we are waiting don't fire off another request
			return;
		}
		var resetPending = function(key) {
			var r = _resolved.get(key);
			clearTimeout(r.timeout);
			r.file = null;
			r.contents = '';
			r.err = "Read operation timed out."; //$NON-NLS-1$
			delete r.pending;
			server.finishAsyncAction(r.err);
		};
  		server.startAsyncAction();
  		item.pending = true;
  		item.timeout = setTimeout(resetPending, 10000, key);
		server.options.getFile({logical: key, file: loc, env: item.env}, function(err, _file) {
			clearTimeout(item.timeout);
			item.file = _file.file;
			if(!_files.containsKey(_file.file)) {
				_files.put(_file.file, {file: _file.file, contents: typeof _file.contents === 'string' ? _file.contents : ''});
			}
	   		item.logical = _file.logical;
	   		item.err = err;
	   		delete item.pending;
	   		server.finishAsyncAction(err);
		});
	}
	
	/**
	 * @description Callback to cycle waiting for async jobs to finish
	 * @param {TernServer} server The server
	 */
	function waitOnResolve(server) {
    	var done = function() {
      		clearTimeout(timeout);
      		doPreInfer(server);
    	};
    	var timeout = setTimeout(done, server.options.fetchTimeout);
	}
	/**
	 * @description Default callback to be used durning the pre-infer phase of plugin loading
	 * @param {TernServer} server The server
	 * @param {Object} resolved The object containing names to be resolved
	 * @since 9.0
	 */
	function doPreInfer(server) {
	  	if(server.pending) {
			return waitOnResolve(server);
		}
		var done = true;
		var keys = _resolved.keys();
		for(var i = 0; i < keys.length; i++) {
			if(_resolved.get(keys[i]).pending) {
				done = false;
				break;
			}
		}
		if(!done) {
			return waitOnResolve(server);
		}
	}
	
	/**
	 * @description Default callback to be used durning the post-parse phase of plugin loading
	 * @param {TernServer} server The server
	 * @param {Object} ast The backing AST that was just parsed
	 * @param {Object} ignores A mapping of names that can be ignored
	 * @param {Function} test An optional function callback to test the name of the dependency
	 * @since 9.0
	 */
	function doPostParse(server, ast, ignores, test) {
		if(Array.isArray(ast.dependencies) && ast.dependencies.length > 0) {
			for(var i = 0; i < ast.dependencies.length; i++) {
				var _d = _getDependencyName(ast.dependencies[i]);
				if(_d) {
					var val = _resolved.get(_d);
					if(val && typeof val === 'object') {
						continue; //we already resolved it or are trying, keep going
					}
					if(typeof ignores === 'object') {
						if(ignores[_d]) {
							continue;
						}
						if(typeof ignores.node === 'object' && ignores.node[_d]) {
							continue;
						}
						if(typeof ignores.requirejs === 'object' && ignores.requirejs[_d]) {
							continue;
						}
					}
					/**
					 * @since 11.0
					 * @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=481271
					 */
					if(typeof test === 'function' && !test(_d)) {
						continue;
					}
					var f = Object.create(null);
					f.env = ast.dependencies[i].env;
					_resolved.put(_d, f);
				}
			}
			resolveDependencies(server, ast.sourceFile ? ast.sourceFile.name : null);
		}  	
	}
	
	/**
	 * @description Resolve the dependency name
	 * @param {Object|String} dep
	 * @since 10.0
	 */
	function _getDependencyName(dep) {
		if(typeof dep === 'string') {
			return dep;
		} else if(dep && typeof dep === 'object') {
			return dep.value;
		}
		return null;
	}
	
	/**
	 * @name possibleMatch
	 * @description Checks if the given file name is a possible match to the given logical name
	 * @param {String} fileName The full path of the file loaded by Tern
	 * @param {String} logicalName The logical name from the cached entry in error state 
	 * @returns {bool} True if it is a possible match, false otherwise
	 */
	function possibleMatch(fileName, logicalName) {
		if(fileName && logicalName) {
			var f = fileName;
			f = f.slice(0, f.lastIndexOf('.js'));
			return f.lastIndexOf(logicalName) === f.length-1;
		}
		return false;
	}
	
	/**
	 * @name doReset
	 * @description Perform any reset actions
	 * @since 15.0
	 */
	function doReset() {
		_resolved.clear();
	    _files.clear();
	}
	
	/**
	 * @name doPreParse
	 * @description Perform any pre-parse actions
	 * @param {string} text The text of the file about to be parsed
	 * @param {?} options The map of options for the parse
	 * @since 15.0
	 */
	function doPreParse(text, options) {
		var file = options.directSourceFile;
    	if(file && file.name) {
    		//update the cached source for this file if it exists
    		var f = _files.get(file.name);
			if(f) {
				f.contents = text;
			}
    		//if a file is parsed and is cached in an error state, it means the file has been created
			//in the editor - remove it from the cache so it will be re-fetched the next time it is 
			//requested as a dependency
			_resolved.keys().forEach(function(key) {
				var val = _resolved.get(key);
				if(val && val.err) {
					if(possibleMatch(file.name, val.logical)) {
						_resolved.remove(key);
						console.log("removed: "+file.name+" from resolver cache");
					}
				}
			});
    	}
	}
	
	tern.registerPlugin("resolver", /* @callback */ function resolverPluginHandler(server, options) {
	    server.loadPlugin("modules"); //$NON-NLS-1$
	    server.mod.modules.resolvers.push(moduleResolve);
		server.on("postParse", /* @callback */ function postParseHandler(ast, text) {
			var cx = infer.cx();
			doPostParse(server, ast, cx ? cx.definitions : null, null);
	    });
	    server.on("preInfer", /* @callback */ function preInferHandler(ast, scope){
	    	doPreInfer(server);
	    });
	    server.on("preParse", function preParseHandler(text, options) {
	    	doPreParse(text, options);
	    });
	    server.on("reset", function resetHandler() {
	    	doReset();
	    });
  });
  
  return {
			doPostParse: doPostParse,
			doPreInfer: doPreInfer,
			getResolved: getResolved,
			doReset: doReset,
			doPreParse: doPreParse
		};
});
/* eslint-disable */
(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(require("../lib/infer"), require("../lib/tern"));
  if (typeof define == "function" && define.amd) // AMD
    return define('tern/plugin/requirejs',["../lib/infer", "../lib/tern", "javascript/ternPlugins/resolver"], mod);
  mod(tern, tern);
})(function(infer, tern, resolver) {
  "use strict";
  
  function flattenPath(path) {
    if (!/(^|\/)(\.\/|[^\/]+\/\.\.\/)/.test(path)) return path;
    var parts = path.split("/");
    for (var i = 0; i < parts.length; ++i) {
      if (parts[i] == "." || !parts[i]) parts.splice(i--, 1);
      else if (i && parts[i] == "..") { parts.splice(i - 1, 2); i -= 2; }
    }
    return parts.join("/");
  }

  function resolveName(name, data) {
    var excl = name.indexOf("!");
    if (excl > -1) name = name.slice(0, excl);

    var opts = data.options;
    var hasExt = /\.js$/.test(name);
    if (hasExt || /^(?:\w+:|\/)/.test(name))
      return name + (hasExt ? "" : ".js");

    var base = opts.baseURL || "";
    if (base && base.charAt(base.length - 1) != "/") base += "/";
    if (opts.paths) {
      var known = opts.paths[name];
      if (known) return flattenPath(base + known + ".js");
      var dir = name.match(/^([^\/]+)(\/.*)$/);
      if (dir) {
        known = opts.paths[dir[1]];
        if (known) return flattenPath(base + known + dir[2] + ".js");
      }
    }
    return flattenPath(base + name + ".js");
  }

  function getRequire(data) {
    if (!data.require) {
      data.require = new infer.Fn("require", infer.ANull, [infer.cx().str], ["module"], new infer.AVal);
      data.require.computeRet = function(_self, _args, argNodes) {
        if (argNodes.length && argNodes[0].type == "Literal" && typeof argNodes[0].value == "string")
          return getInterface(argNodes[0].value, data); //ORION
        return infer.ANull;
      };
    }
    return data.require;
  }

  function getModuleInterface(data, exports) {
    var mod = new infer.Obj(infer.cx().definitions.requirejs.module, "module");
    var expProp = mod.defProp("exports");
    
    expProp.propagate(getModule(data.currentFile, data));
    exports.propagate(expProp, EXPORT_OBJ_WEIGHT);
    return mod;
  }

  function getExports(data) {
    var exports = new infer.Obj(true, "exports");
    getModule(data.currentFile, data).addType(exports, EXPORT_OBJ_WEIGHT);
    return exports;
  }

  function getInterface(name, data) {
    if (data.options.override && Object.prototype.hasOwnProperty.call(data.options.override, name)) {
      var over = data.options.override[name];
      if (typeof over == "string" && over.charAt(0) == "=") return infer.def.parsePath(over.slice(1));
      if (typeof over == "object") {
        var known = getKnownModule(name, data);
        if (known) return known;
        var scope = data.interfaces[stripJSExt(name)] = new infer.Obj(null, stripJSExt(name));
        infer.def.load(over, scope);
        return scope;
      }
      name = over;
    }
	//ORION
	known = getModule(name, data);
    if (known && known.origin) {
    	var contents = known.contents;
    	if(data.server.fileMap[known.origin]) {
    		contents = null; //don't force a purge for a context that will not be recomputed
    	}
      data.server.addFile(known.origin, contents, data.currentFile);
    }
    return known || infer.ANull;
  }

  function getKnownModule(name, data) {
    var val = resolver.getResolved(name); //ORION
  	if(val && val.file) {
    	return data.interfaces[stripJSExt(val.file)];
    }
    return null;
  }

  function getModule(name, data) {
  	if(name === data.currentFile) {
  		var _f = stripJSExt(name);
  		var known = data.interfaces[_f];
  		if(!known) {
  			known = new infer.AVal();
  			known.origin = name;
  			data.interfaces[_f] = known;
  		}
  		return known;
  	}
  	var known = getKnownModule(name, data);
    if (!known) {
      var val = resolver.getResolved(name); //ORION
      if(val && val.file) {
	      known = data.interfaces[stripJSExt(val.file)] = new infer.AVal();
	      data.shortNames[stripJSExt(val.file)] = name; // ORION Collect short names for module name completion
	      known.origin = val.file;
	      known.contents = val.contents;
	      known.reqName = name;
      }
	}
    return known;
  }

  var EXPORT_OBJ_WEIGHT = 50;

  function stripJSExt(f) {
    return f.replace(/\.js$/, '');
  }

  var path = {
    dirname: function(path) {
      var lastSep = path.lastIndexOf("/");
      return lastSep == -1 ? "" : path.slice(0, lastSep);
    },
    relative: function(from, to) {
      if (to.indexOf(from) == 0) return to.slice(from.length);
      else return to;
    },
    join: function(a, b) {
      if (b && b.charAt(0) != ".") return b;
      if (a && b) return a + "/" + b;
      else return (a || "") + (b || "");
    }
  };

  function runModule(server, args, argNodes, out) {
    var data = server.mod.requireJS;
    var deps = [], fn, exports, mod;

    function interf(name) {
      if (name == "require") return getRequire(data);
      if (name == "exports") return exports || (exports = getExports(data));
      if (name == "module") return mod || (mod = getModuleInterface(data, exports || (exports = getExports(data))));
      return getInterface(name, data);
    }

    if (argNodes && args.length > 1) {
      var node = argNodes[args.length == 2 ? 0 : 1];
      var base = path.relative(server.projectDir, path.dirname(node.sourceFile.name));
      if (node.type == "Literal" && typeof node.value == "string") {
        node.required = interf(node.value); //ORION
        deps.push(node.required);
      } else if (node.type == "ArrayExpression") for (var i = 0; i < node.elements.length; ++i) {
        var elt = node.elements[i];
        if (elt && elt.type == "Literal" && typeof elt.value == "string") { //ORION elt might be null
          elt.required = interf(elt.value); //ORION
          deps.push(elt.required);
        }
      }
    } else if (argNodes && args.length == 1 &&
               /FunctionExpression/.test(argNodes[0].type) &&
               argNodes[0].params.length) {
      // Simplified CommonJS call
      deps.push(interf("require", data), interf("exports", data), interf("module", data));
      fn = args[0];
    }

    if (!fn) {
      fn = args[Math.min(args.length - 1, 2)];
      if (!fn.isEmpty() && !fn.getFunctionType()) fn = null;
    }

    if (fn) {
      fn.propagate(new infer.IsCallee(infer.ANull, deps, null, out || infer.ANull));
      if (out) out.originNode = fn.originNode;
    } else if (out) {
      args[0].propagate(out)
    }

    return infer.ANull;
  }

  infer.registerFunction("requirejs_define", function(_self, args, argNodes) {
    if (!args.length) return infer.ANull

    var server = infer.cx().parent, data = server.mod.requireJS
    return runModule(server, args, argNodes, getModule(data.currentFile, data))
  });

  infer.registerFunction("requirejs_require", function(_self, args, argNodes) {
    if (!args.length) return infer.ANull
    return runModule(infer.cx().parent, args, argNodes)
  });

  // Parse simple ObjectExpression AST nodes to their corresponding JavaScript objects.
  function parseExprNode(node) {
    switch (node.type) {
    case "ArrayExpression":
      return node.elements.map(parseExprNode);
    case "Literal":
      return node.value;
    case "ObjectExpression":
      var obj = {};
      node.properties.forEach(function(prop) {
        var key = prop.key.name || prop.key.value;
        obj[key] = parseExprNode(prop.value);
      });
      return obj;
    }
  }

  infer.registerFunction("requirejs_config", function(_self, _args, argNodes) {
    var server = infer.cx().parent, data = server && server.mod.requireJS;
    if (data && argNodes && argNodes.length && argNodes[0].type == "ObjectExpression") {
      var config = parseExprNode(argNodes[0]);
      for (var key in config) if (config.hasOwnProperty(key)) {
        var value = config[key], exists = data.options[key];
        if (!exists) {
          data.options[key] = value;
        } else if (key == "paths") {
          for (var path in value) if (value.hasOwnProperty(path) && !data.options.paths[path])
            data.options.paths[path] = value[path];
        }
      }
    }
    return infer.ANull;
  });

  function preCondenseReach(state) {
    var interfaces = infer.cx().parent.mod.requireJS.interfaces;
    var rjs = state.roots["!requirejs"] = new infer.Obj(null);
    for (var name in interfaces) {
      var prop = rjs.defProp(name.replace(/\./g, "`"));
      interfaces[name].propagate(prop);
      prop.origin = interfaces[name].origin;
    }
  }

  function postLoadDef(data) {
    var cx = infer.cx(), interfaces = cx.definitions[data["!name"]]["!requirejs"];
    var data = cx.parent.mod.requireJS;
    if (interfaces) for (var name in interfaces.props) {
      interfaces.props[name].propagate(getInterface(name, data));
    }
  }

  tern.registerPlugin("requirejs", function(server, options) {
    server.mod.requireJS = {
      interfaces: Object.create(null),
      options: options || {},
      currentFile: null,
      server: server
    };

    server.on("beforeLoad", function(file) {
      this.mod.requireJS.currentFile = file.name;
    });
    server.on("reset", function() {
      this.mod.requireJS.interfaces = Object.create(null);
      this.mod.requireJS.shortNames = Object.create(null); // ORION Collect the short names rather than full Orion path for module completion
      this.mod.requireJS.require = null;
      resolver.doReset();
    });
	server.on("preParse", function preParseHandler(text, options) {
		resolver.doPreParse(text, options);
	});
    server.on("preCondenseReach", preCondenseReach)
    server.on("postLoadDef", postLoadDef)
    server.on("typeAt", findTypeAt)
    server.on("completion", findCompletions)
    
    // ORION Hook into postParse, preInfer events
    server.on("postParse", function(ast, text){
    	resolver.doPostParse(server, ast, infer.cx().definitions);
    });
    server.on("preInfer", function(ast, scope){
    	resolver.doPreInfer(server);
    });

    server.addDefs(defs)
  });

  function findTypeAt(_file, _pos, expr, type) {
    if (!expr || expr.node.type != "Literal" ||
        typeof expr.node.value != "string" || !expr.node.required)
      return type;

    // The `type` is a value shared for all string literals.
    // We must create a copy before modifying `origin` and `originNode`.
    // Otherwise all string literals would point to the last jump location
    type = Object.create(type);

    // Provide a custom origin location pointing to the require()d file
    var exportedType = expr.node.required;
    type.origin = exportedType.origin;
    type.originNode = exportedType.originNode;
    if (exportedType.doc) type.doc = exportedType.doc
    if (exportedType.url) type.url = exportedType.url
    return type;
  }

  function findCompletions(file, query) {
    var wordEnd = tern.resolvePos(file, query.end);
    var callExpr = infer.findExpressionAround(file.ast, null, wordEnd, file.scope, "CallExpression");
    if (!callExpr) return;
    var callNode = callExpr.node;
    if (callNode.callee.type != "Identifier" ||
        !(callNode.callee.name == "define" || callNode.callee.name == "require" || callNode.callee.name == "requirejs")||
        callNode.arguments.length < 1 || callNode.arguments[0].type != "ArrayExpression") return;
    var argNode = findRequireModule(callNode.arguments[0].elements, wordEnd);
    if (!argNode) return;
    var word = argNode.raw.slice(1, wordEnd - argNode.start), quote = argNode.raw.charAt(0);
    if (word && word.charAt(word.length - 1) == quote)
      word = word.slice(0, word.length - 1);
    var completions = completeModuleName(query, word, file.name);
    if (argNode.end == wordEnd + 1 && file.text.charAt(wordEnd) == quote)
      ++wordEnd;
    return {
      start: tern.outputPos(query, file, argNode.start),
      end: tern.outputPos(query, file, wordEnd),
      isProperty: false,
      isObjectKey: false,
      completions: completions.map(function(rec) {
        var name = typeof rec == "string" ? rec : rec.name;
        
        // TODO ORION: Stringify the name adds the quotes around the proposal which Orion doesn't handle in sortProposals
        var string = name;
//        var string = JSON.stringify(name)
//        if (quote == "'") string = quote + string.slice(1, string.length -1).replace(/'/g, "\\'") + quote
        
        if (typeof rec == "string") return string;
        rec.displayName = name;
        rec.name = string;
        return rec;
      })
    };
  }

  function findRequireModule(argsNode, wordEnd) {
    for (var i = 0; i < argsNode.length; i++) {
      var argNode = argsNode[i];
      if (argNode.type == "Literal" && typeof argNode.value == "string" &&
          argNode.start < wordEnd && argNode.end > wordEnd) return argNode;
    }
  }

  function completeModuleName(query, word, parentFile) {
    var cx = infer.cx(), server = cx.parent, data = server.mod.requireJS;
    var currentName = stripJSExt(parentFile);
    var base = data.options.baseURL || "";
    if (base && base.charAt(base.length - 1) != "/") base += "/";

    if (query.caseInsensitive) word = word.toLowerCase();

 	// ORION Use short names for completion rather than resolved Orion path that is stored in data.interfaces
    var completions = [],
    	modules = data.interfaces;
    for (var name in modules) {
    	// ORION Allow empty files to be completed
      if (name == currentName /*|| !modules[name].getType()*/) continue;
      
      // ORION Use short name
      if (!data.shortNames[name]) continue;
      name = data.shortNames[name];

      var moduleName = name.substring(base.length, name.length);
      if (moduleName &&
          !(query.filter !== false && word &&
            (query.caseInsensitive ? moduleName.toLowerCase() : moduleName).indexOf(word) !== 0))
        tern.addCompletion(query, completions, moduleName, modules[name]);
    }
    return completions;
  }

  var defs = {
    "!name": "requirejs",
    "!define": {
      module: {
        id: "string",
        uri: "string",
        config: "fn() -> ?"
      },
      config: {
        "!url": "https://requirejs.org/docs/api.html#config",
        baseUrl: {
          "!type": "string",
          "!doc": "the root path to use for all module lookups",
          "!url": "https://requirejs.org/docs/api.html#config-baseUrl"
        },
        paths: {
          "!type": "?",
          "!doc": "path mappings for module names not found directly under baseUrl. The path settings are assumed to be relative to baseUrl, unless the paths setting starts with a '/' or has a URL protocol in it ('like https:').",
          "!url": "https://requirejs.org/docs/api.html#config-paths"
        },
        shim: {
          "!type": "?",
          "!doc": "Configure the dependencies, exports, and custom initialization for older, traditional 'browser globals' scripts that do not use define() to declare the dependencies and set a module value.",
          "!url": "https://requirejs.org/docs/api.html#config-shim"
        },
        map: {
          "!type": "?",
          "!doc": "For the given module prefix, instead of loading the module with the given ID, substitute a different module ID.",
          "!url": "https://requirejs.org/docs/api.html#config-map"
        },
        config: {
          "!type": "?",
          "!doc": "There is a common need to pass configuration info to a module. That configuration info is usually known as part of the application, and there needs to be a way to pass that down to a module. In RequireJS, that is done with the config option for requirejs.config(). Modules can then read that info by asking for the special dependency 'module' and calling module.config().",
          "!url": "https://requirejs.org/docs/api.html#config-moduleconfig"
        },
        packages: {
          "!type": "?",
          "!doc": "configures loading modules from CommonJS packages. See the packages topic for more information.",
          "!url": "https://requirejs.org/docs/api.html#config-packages"
        },
        nodeIdCompat: {
          "!type": "?",
          "!doc": "Node treats module ID example.js and example the same. By default these are two different IDs in RequireJS. If you end up using modules installed from npm, then you may need to set this config value to true to avoid resolution issues.",
          "!url": "https://requirejs.org/docs/api.html#config-nodeIdCompat"
        },
        waitSeconds: {
          "!type": "number",
          "!doc": "The number of seconds to wait before giving up on loading a script. Setting it to 0 disables the timeout. The default is 7 seconds.",
          "!url": "https://requirejs.org/docs/api.html#config-waitSeconds"
        },
        context: {
          "!type": "number",
          "!doc": "A name to give to a loading context. This allows require.js to load multiple versions of modules in a page, as long as each top-level require call specifies a unique context string. To use it correctly, see the Multiversion Support section.",
          "!url": "https://requirejs.org/docs/api.html#config-context"
        },
        deps: {
          "!type": "?",
          "!doc": "An array of dependencies to load. Useful when require is defined as a config object before require.js is loaded, and you want to specify dependencies to load as soon as require() is defined. Using deps is just like doing a require([]) call, but done as soon as the loader has processed the configuration. It does not block any other require() calls from starting their requests for modules, it is just a way to specify some modules to load asynchronously as part of a config block.",
          "!url": "https://requirejs.org/docs/api.html#config-deps"
        },
        callback: {
          "!type": "fn()",
          "!doc": "A function to execute after deps have been loaded. Useful when require is defined as a config object before require.js is loaded, and you want to specify a function to require after the configuration's deps array has been loaded.",
          "!url": "https://requirejs.org/docs/api.html#config-callback"
        },
        enforceDefine: {
          "!type": "bool",
          "!doc": "If set to true, an error will be thrown if a script loads that does not call define() or have a shim exports string value that can be checked. See Catching load failures in IE for more information.",
          "!url": "https://requirejs.org/docs/api.html#config-enforceDefine"
        },
        xhtml: {
          "!type": "bool",
          "!doc": "If set to true, document.createElementNS() will be used to create script elements.",
          "!url": "https://requirejs.org/docs/api.html#config-xhtml"
        },
        urlArgs: {
          "!type": "string",
          "!doc": "Extra query string arguments appended to URLs that RequireJS uses to fetch resources. Most useful to cache bust when the browser or server is not configured correctly.",
          "!url": "https://requirejs.org/docs/api.html#config-urlArgs"
        },
        scriptType: {
          "!type": "string",
          "!doc": "Specify the value for the type='' attribute used for script tags inserted into the document by RequireJS. Default is 'text/javascript'. To use Firefox's JavaScript 1.8 features, use 'text/javascript;version=1.8'.",
          "!url": "https://requirejs.org/docs/api.html#config-scriptType"
        },
        skipDataMain: {
          "!type": "bool",
          "!doc": "Introduced in RequireJS 2.1.9: If set to true, skips the data-main attribute scanning done to start module loading. Useful if RequireJS is embedded in a utility library that may interact with other RequireJS library on the page, and the embedded version should not do data-main loading.",
          "!url": "https://requirejs.org/docs/api.html#config-skipDataMain"
        }
      },
      RequireJSError: {
        "prototype" : {
          "!proto": "Error.prototype",
          "requireType": {
            "!type": "string",
            "!doc": "A string value with a general classification, like 'timeout', 'nodefine', 'scripterror'.",
            "!url": "https://requirejs.org/docs/api.html#errors"
          },
          "requireModules": {
            "!type": "[string]",
            "!doc": "An array of module names/URLs that timed out.",
            "!url": "https://requirejs.org/docs/api.html#errors"
          }
        }
      }
    },
    requirejs: {
      "!type": "fn(deps: [string], callback: fn(), errback?: fn(err: +RequireJSError)) -> !custom:requirejs_require",
      onError: {
        "!type": "fn(err: +RequireJSError)",
        "!doc": "To detect errors that are not caught by local errbacks, you can override requirejs.onError()",
        "!url": "https://requirejs.org/docs/api.html#requirejsonerror"
      },
      load: {
        "!type": "fn(context: ?, moduleName: string, url: string)"
      },
      config: "fn(config: config) -> !custom:requirejs_config",
      version: "string",
      isBrowser: "bool"
    },
    require: "requirejs",
    define: {
      "!type": "fn(deps: [string], callback: fn()) -> !custom:requirejs_define",
      amd: {
        jQuery: "bool"
      }
    }
  };
});

(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define('estraverse/estraverse',[],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.estraverse = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/*
  Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com>
  Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*jslint vars:false, bitwise:true*/
/*jshint indent:4*/
/*global exports:true*/
(function clone(exports) {
    'use strict';

    var Syntax,
        isArray,
        VisitorOption,
        VisitorKeys,
        objectCreate,
        objectKeys,
        BREAK,
        SKIP,
        REMOVE;

    function ignoreJSHintError() { }

    isArray = Array.isArray;
    if (!isArray) {
        isArray = function isArray(array) {
            return Object.prototype.toString.call(array) === '[object Array]';
        };
    }

    function deepCopy(obj) {
        var ret = {}, key, val;
        for (key in obj) {
            if (obj.hasOwnProperty(key)) {
                val = obj[key];
                if (typeof val === 'object' && val !== null) {
                    ret[key] = deepCopy(val);
                } else {
                    ret[key] = val;
                }
            }
        }
        return ret;
    }

    function shallowCopy(obj) {
        var ret = {}, key;
        for (key in obj) {
            if (obj.hasOwnProperty(key)) {
                ret[key] = obj[key];
            }
        }
        return ret;
    }
    ignoreJSHintError(shallowCopy);

    // based on LLVM libc++ upper_bound / lower_bound
    // MIT License

    function upperBound(array, func) {
        var diff, len, i, current;

        len = array.length;
        i = 0;

        while (len) {
            diff = len >>> 1;
            current = i + diff;
            if (func(array[current])) {
                len = diff;
            } else {
                i = current + 1;
                len -= diff + 1;
            }
        }
        return i;
    }

    function lowerBound(array, func) {
        var diff, len, i, current;

        len = array.length;
        i = 0;

        while (len) {
            diff = len >>> 1;
            current = i + diff;
            if (func(array[current])) {
                i = current + 1;
                len -= diff + 1;
            } else {
                len = diff;
            }
        }
        return i;
    }
    ignoreJSHintError(lowerBound);

    objectCreate = Object.create || (function () {
        function F() { }

        return function (o) {
            F.prototype = o;
            return new F();
        };
    })();

    objectKeys = Object.keys || function (o) {
        var keys = [], key;
        for (key in o) {
            keys.push(key);
        }
        return keys;
    };

    function extend(to, from) {
        var keys = objectKeys(from), key, i, len;
        for (i = 0, len = keys.length; i < len; i += 1) {
            key = keys[i];
            to[key] = from[key];
        }
        return to;
    }

    Syntax = {
        AssignmentExpression: 'AssignmentExpression',
        AssignmentPattern: 'AssignmentPattern',
        ArrayExpression: 'ArrayExpression',
        ArrayPattern: 'ArrayPattern',
        ArrowFunctionExpression: 'ArrowFunctionExpression',
        AwaitExpression: 'AwaitExpression', // CAUTION: It's deferred to ES7.
        BlockStatement: 'BlockStatement',
        BinaryExpression: 'BinaryExpression',
        BreakStatement: 'BreakStatement',
        CallExpression: 'CallExpression',
        CatchClause: 'CatchClause',
        ClassBody: 'ClassBody',
        ClassDeclaration: 'ClassDeclaration',
        ClassExpression: 'ClassExpression',
        ComprehensionBlock: 'ComprehensionBlock',  // CAUTION: It's deferred to ES7.
        ComprehensionExpression: 'ComprehensionExpression',  // CAUTION: It's deferred to ES7.
        ConditionalExpression: 'ConditionalExpression',
        ContinueStatement: 'ContinueStatement',
        DebuggerStatement: 'DebuggerStatement',
        DirectiveStatement: 'DirectiveStatement',
        DoWhileStatement: 'DoWhileStatement',
        EmptyStatement: 'EmptyStatement',
        ExportAllDeclaration: 'ExportAllDeclaration',
        ExportDefaultDeclaration: 'ExportDefaultDeclaration',
        ExportNamedDeclaration: 'ExportNamedDeclaration',
        ExportSpecifier: 'ExportSpecifier',
        ExpressionStatement: 'ExpressionStatement',
        ForStatement: 'ForStatement',
        ForInStatement: 'ForInStatement',
        ForOfStatement: 'ForOfStatement',
        FunctionDeclaration: 'FunctionDeclaration',
        FunctionExpression: 'FunctionExpression',
        GeneratorExpression: 'GeneratorExpression',  // CAUTION: It's deferred to ES7.
        Identifier: 'Identifier',
        IfStatement: 'IfStatement',
        ImportDeclaration: 'ImportDeclaration',
        ImportDefaultSpecifier: 'ImportDefaultSpecifier',
        ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',
        ImportSpecifier: 'ImportSpecifier',
        Literal: 'Literal',
        LabeledStatement: 'LabeledStatement',
        LogicalExpression: 'LogicalExpression',
        MemberExpression: 'MemberExpression',
        MetaProperty: 'MetaProperty',
        MethodDefinition: 'MethodDefinition',
        ModuleSpecifier: 'ModuleSpecifier',
        NewExpression: 'NewExpression',
        ObjectExpression: 'ObjectExpression',
        ObjectPattern: 'ObjectPattern',
        Program: 'Program',
        Property: 'Property',
        RestElement: 'RestElement',
        ReturnStatement: 'ReturnStatement',
        SequenceExpression: 'SequenceExpression',
        SpreadElement: 'SpreadElement',
        Super: 'Super',
        SwitchStatement: 'SwitchStatement',
        SwitchCase: 'SwitchCase',
        TaggedTemplateExpression: 'TaggedTemplateExpression',
        TemplateElement: 'TemplateElement',
        TemplateLiteral: 'TemplateLiteral',
        ThisExpression: 'ThisExpression',
        ThrowStatement: 'ThrowStatement',
        TryStatement: 'TryStatement',
        UnaryExpression: 'UnaryExpression',
        UpdateExpression: 'UpdateExpression',
        VariableDeclaration: 'VariableDeclaration',
        VariableDeclarator: 'VariableDeclarator',
        WhileStatement: 'WhileStatement',
        WithStatement: 'WithStatement',
        YieldExpression: 'YieldExpression'
    };

    VisitorKeys = {
        AssignmentExpression: ['left', 'right'],
        AssignmentPattern: ['left', 'right'],
        ArrayExpression: ['elements'],
        ArrayPattern: ['elements'],
        ArrowFunctionExpression: ['params', 'body'],
        AwaitExpression: ['argument'], // CAUTION: It's deferred to ES7.
        BlockStatement: ['body'],
        BinaryExpression: ['left', 'right'],
        BreakStatement: ['label'],
        CallExpression: ['callee', 'arguments'],
        CatchClause: ['param', 'body'],
        ClassBody: ['body'],
        ClassDeclaration: ['id', 'superClass', 'body'],
        ClassExpression: ['id', 'superClass', 'body'],
        ComprehensionBlock: ['left', 'right'],  // CAUTION: It's deferred to ES7.
        ComprehensionExpression: ['blocks', 'filter', 'body'],  // CAUTION: It's deferred to ES7.
        ConditionalExpression: ['test', 'consequent', 'alternate'],
        ContinueStatement: ['label'],
        DebuggerStatement: [],
        DirectiveStatement: [],
        DoWhileStatement: ['body', 'test'],
        EmptyStatement: [],
        ExportAllDeclaration: ['source'],
        ExportDefaultDeclaration: ['declaration'],
        ExportNamedDeclaration: ['declaration', 'specifiers', 'source'],
        // TODO ORION The local identifier comes first in source/tokens, not exported, this caused problems with Finder.  See https://github.com/estools/estraverse/issues/71
        ExportSpecifier: ['local', 'exported'],
        ExpressionStatement: ['expression'],
        ForStatement: ['init', 'test', 'update', 'body'],
        ForInStatement: ['left', 'right', 'body'],
        ForOfStatement: ['left', 'right', 'body'],
        FunctionDeclaration: ['id', 'params', 'body'],
        FunctionExpression: ['id', 'params', 'body'],
        GeneratorExpression: ['blocks', 'filter', 'body'],  // CAUTION: It's deferred to ES7.
        Identifier: [],
        IfStatement: ['test', 'consequent', 'alternate'],
        ImportDeclaration: ['specifiers', 'source'],
        ImportDefaultSpecifier: ['local'],
        ImportNamespaceSpecifier: ['local'],
        ImportSpecifier: ['imported', 'local'],
        Literal: [],
        LabeledStatement: ['label', 'body'],
        LogicalExpression: ['left', 'right'],
        MemberExpression: ['object', 'property'],
        MetaProperty: ['meta', 'property'],
        MethodDefinition: ['key', 'value'],
        ModuleSpecifier: [],
        NewExpression: ['callee', 'arguments'],
        ObjectExpression: ['properties'],
        ObjectPattern: ['properties'],
        Program: ['body'],
        Property: ['key', 'value'],
        RestElement: [ 'argument' ],
        ReturnStatement: ['argument'],
        SequenceExpression: ['expressions'],
        SpreadElement: ['argument'],
        Super: [],
        SwitchStatement: ['discriminant', 'cases'],
        SwitchCase: ['test', 'consequent'],
        TaggedTemplateExpression: ['tag', 'quasi'],
        TemplateElement: [],
        TemplateLiteral: ['quasis', 'expressions'],
        ThisExpression: [],
        ThrowStatement: ['argument'],
        TryStatement: ['block', 'handler', 'finalizer'],
        UnaryExpression: ['argument'],
        UpdateExpression: ['argument'],
        VariableDeclaration: ['declarations'],
        VariableDeclarator: ['id', 'init'],
        WhileStatement: ['test', 'body'],
        WithStatement: ['object', 'body'],
        YieldExpression: ['argument']
    };

    // unique id
    BREAK = {};
    SKIP = {};
    REMOVE = {};

    VisitorOption = {
        Break: BREAK,
        Skip: SKIP,
        Remove: REMOVE
    };

    function Reference(parent, key) {
        this.parent = parent;
        this.key = key;
    }

    Reference.prototype.replace = function replace(node) {
        this.parent[this.key] = node;
    };

    Reference.prototype.remove = function remove() {
        if (isArray(this.parent)) {
            this.parent.splice(this.key, 1);
            return true;
        } else {
            this.replace(null);
            return false;
        }
    };

    function Element(node, path, wrap, ref) {
        this.node = node;
        this.path = path;
        this.wrap = wrap;
        this.ref = ref;
    }

    function Controller() { }

    // API:
    // return property path array from root to current node
    Controller.prototype.path = function path() {
        var i, iz, j, jz, result, element;

        function addToPath(result, path) {
            if (isArray(path)) {
                for (j = 0, jz = path.length; j < jz; ++j) {
                    result.push(path[j]);
                }
            } else {
                result.push(path);
            }
        }

        // root node
        if (!this.__current.path) {
            return null;
        }

        // first node is sentinel, second node is root element
        result = [];
        for (i = 2, iz = this.__leavelist.length; i < iz; ++i) {
            element = this.__leavelist[i];
            addToPath(result, element.path);
        }
        addToPath(result, this.__current.path);
        return result;
    };

    // API:
    // return type of current node
    Controller.prototype.type = function () {
        var node = this.current();
        return node.type || this.__current.wrap;
    };

    // API:
    // return array of parent elements
    Controller.prototype.parents = function parents() {
        var i, iz, result;

        // first node is sentinel
        result = [];
        for (i = 1, iz = this.__leavelist.length; i < iz; ++i) {
            result.push(this.__leavelist[i].node);
        }

        return result;
    };

    // API:
    // return current node
    Controller.prototype.current = function current() {
        return this.__current.node;
    };

    Controller.prototype.__execute = function __execute(callback, element) {
        var previous, result;

        result = undefined;

        previous  = this.__current;
        this.__current = element;
        this.__state = null;
        if (callback) {
            result = callback.call(this, element.node, this.__leavelist[this.__leavelist.length - 1].node);
        }
        this.__current = previous;

        return result;
    };

    // API:
    // notify control skip / break
    Controller.prototype.notify = function notify(flag) {
        this.__state = flag;
    };

    // API:
    // skip child nodes of current node
    Controller.prototype.skip = function () {
        this.notify(SKIP);
    };

    // API:
    // break traversals
    Controller.prototype['break'] = function () {
        this.notify(BREAK);
    };

    // API:
    // remove node
    Controller.prototype.remove = function () {
        this.notify(REMOVE);
    };

    Controller.prototype.__initialize = function(root, visitor) {
        this.visitor = visitor;
        this.root = root;
        this.__worklist = [];
        this.__leavelist = [];
        this.__current = null;
        this.__state = null;
        this.__fallback = null;
        if (visitor.fallback === 'iteration') {
            this.__fallback = objectKeys;
        } else if (typeof visitor.fallback === 'function') {
            this.__fallback = visitor.fallback;
        }

        this.__keys = VisitorKeys;
        if (visitor.keys) {
            this.__keys = extend(objectCreate(this.__keys), visitor.keys);
        }
    };

    function isNode(node) {
        if (node == null) {
            return false;
        }
        return typeof node === 'object' && typeof node.type === 'string';
    }

    function isProperty(nodeType, key) {
        return (nodeType === Syntax.ObjectExpression || nodeType === Syntax.ObjectPattern) && 'properties' === key;
    }

    Controller.prototype.traverse = function traverse(root, visitor) {
        var worklist,
            leavelist,
            element,
            node,
            nodeType,
            ret,
            key,
            current,
            current2,
            candidates,
            candidate,
            sentinel;

        this.__initialize(root, visitor);

        sentinel = {};

        // reference
        worklist = this.__worklist;
        leavelist = this.__leavelist;

        // initialize
        worklist.push(new Element(root, null, null, null));
        leavelist.push(new Element(null, null, null, null));

        while (worklist.length) {
            element = worklist.pop();

            if (element === sentinel) {
                element = leavelist.pop();

                ret = this.__execute(visitor.leave, element);

                if (this.__state === BREAK || ret === BREAK) {
                    return;
                }
                continue;
            }

            if (element.node) {

                ret = this.__execute(visitor.enter, element);

                if (this.__state === BREAK || ret === BREAK) {
                    return;
                }

                worklist.push(sentinel);
                leavelist.push(element);

                if (this.__state === SKIP || ret === SKIP) {
                    continue;
                }

                node = element.node;
                nodeType = node.type || element.wrap;
                candidates = this.__keys[nodeType];
                if (!candidates) {
                    if (this.__fallback) {
                        candidates = this.__fallback(node);
                    } else {
                        throw new Error('Unknown node type ' + nodeType + '.');
                    }
                }

                current = candidates.length;
                while ((current -= 1) >= 0) {
                    key = candidates[current];
                    candidate = node[key];
                    if (!candidate) {
                        continue;
                    }

                    if (isArray(candidate)) {
                        current2 = candidate.length;
                        while ((current2 -= 1) >= 0) {
                            if (!candidate[current2]) {
                                continue;
                            }
                            if (isProperty(nodeType, candidates[current])) {
                                element = new Element(candidate[current2], [key, current2], 'Property', null);
                            } else if (isNode(candidate[current2])) {
                                element = new Element(candidate[current2], [key, current2], null, null);
                            } else {
                                continue;
                            }
                            worklist.push(element);
                        }
                    } else if (isNode(candidate)) {
                        worklist.push(new Element(candidate, key, null, null));
                    }
                }
            }
        }
    };

    Controller.prototype.replace = function replace(root, visitor) {
        var worklist,
            leavelist,
            node,
            nodeType,
            target,
            element,
            current,
            current2,
            candidates,
            candidate,
            sentinel,
            outer,
            key;

        function removeElem(element) {
            var i,
                key,
                nextElem,
                parent;

            if (element.ref.remove()) {
                // When the reference is an element of an array.
                key = element.ref.key;
                parent = element.ref.parent;

                // If removed from array, then decrease following items' keys.
                i = worklist.length;
                while (i--) {
                    nextElem = worklist[i];
                    if (nextElem.ref && nextElem.ref.parent === parent) {
                        if  (nextElem.ref.key < key) {
                            break;
                        }
                        --nextElem.ref.key;
                    }
                }
            }
        }

        this.__initialize(root, visitor);

        sentinel = {};

        // reference
        worklist = this.__worklist;
        leavelist = this.__leavelist;

        // initialize
        outer = {
            root: root
        };
        element = new Element(root, null, null, new Reference(outer, 'root'));
        worklist.push(element);
        leavelist.push(element);

        while (worklist.length) {
            element = worklist.pop();

            if (element === sentinel) {
                element = leavelist.pop();

                target = this.__execute(visitor.leave, element);

                // node may be replaced with null,
                // so distinguish between undefined and null in this place
                if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) {
                    // replace
                    element.ref.replace(target);
                }

                if (this.__state === REMOVE || target === REMOVE) {
                    removeElem(element);
                }

                if (this.__state === BREAK || target === BREAK) {
                    return outer.root;
                }
                continue;
            }

            target = this.__execute(visitor.enter, element);

            // node may be replaced with null,
            // so distinguish between undefined and null in this place
            if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) {
                // replace
                element.ref.replace(target);
                element.node = target;
            }

            if (this.__state === REMOVE || target === REMOVE) {
                removeElem(element);
                element.node = null;
            }

            if (this.__state === BREAK || target === BREAK) {
                return outer.root;
            }

            // node may be null
            node = element.node;
            if (!node) {
                continue;
            }

            worklist.push(sentinel);
            leavelist.push(element);

            if (this.__state === SKIP || target === SKIP) {
                continue;
            }

            nodeType = node.type || element.wrap;
            candidates = this.__keys[nodeType];
            if (!candidates) {
                if (this.__fallback) {
                    candidates = this.__fallback(node);
                } else {
                    throw new Error('Unknown node type ' + nodeType + '.');
                }
            }

            current = candidates.length;
            while ((current -= 1) >= 0) {
                key = candidates[current];
                candidate = node[key];
                if (!candidate) {
                    continue;
                }

                if (isArray(candidate)) {
                    current2 = candidate.length;
                    while ((current2 -= 1) >= 0) {
                        if (!candidate[current2]) {
                            continue;
                        }
                        if (isProperty(nodeType, candidates[current])) {
                            element = new Element(candidate[current2], [key, current2], 'Property', new Reference(candidate, current2));
                        } else if (isNode(candidate[current2])) {
                            element = new Element(candidate[current2], [key, current2], null, new Reference(candidate, current2));
                        } else {
                            continue;
                        }
                        worklist.push(element);
                    }
                } else if (isNode(candidate)) {
                    worklist.push(new Element(candidate, key, null, new Reference(node, key)));
                }
            }
        }

        return outer.root;
    };

    function traverse(root, visitor) {
        var controller = new Controller();
        return controller.traverse(root, visitor);
    }

    function replace(root, visitor) {
        var controller = new Controller();
        return controller.replace(root, visitor);
    }

    function extendCommentRange(comment, tokens) {
        var target;

        target = upperBound(tokens, function search(token) {
            return token.range[0] > comment.range[0];
        });

        comment.extendedRange = [comment.range[0], comment.range[1]];

        if (target !== tokens.length) {
            comment.extendedRange[1] = tokens[target].range[0];
        }

        target -= 1;
        if (target >= 0) {
            comment.extendedRange[0] = tokens[target].range[1];
        }

        return comment;
    }

    function attachComments(tree, providedComments, tokens) {
        // At first, we should calculate extended comment ranges.
        var comments = [], comment, len, i, cursor;

        if (!tree.range) {
            throw new Error('attachComments needs range information');
        }

        // tokens array is empty, we attach comments to tree as 'leadingComments'
        if (!tokens.length) {
            if (providedComments.length) {
                for (i = 0, len = providedComments.length; i < len; i += 1) {
                    comment = deepCopy(providedComments[i]);
                    comment.extendedRange = [0, tree.range[0]];
                    comments.push(comment);
                }
                tree.leadingComments = comments;
            }
            return tree;
        }

        for (i = 0, len = providedComments.length; i < len; i += 1) {
            comments.push(extendCommentRange(deepCopy(providedComments[i]), tokens));
        }

        // This is based on John Freeman's implementation.
        cursor = 0;
        traverse(tree, {
            enter: function (node) {
                var comment;

                while (cursor < comments.length) {
                    comment = comments[cursor];
                    if (comment.extendedRange[1] > node.range[0]) {
                        break;
                    }

                    if (comment.extendedRange[1] === node.range[0]) {
                        if (!node.leadingComments) {
                            node.leadingComments = [];
                        }
                        node.leadingComments.push(comment);
                        comments.splice(cursor, 1);
                    } else {
                        cursor += 1;
                    }
                }

                // already out of owned node
                if (cursor === comments.length) {
                    return VisitorOption.Break;
                }

                if (comments[cursor].extendedRange[0] > node.range[1]) {
                    return VisitorOption.Skip;
                }
            }
        });

        cursor = 0;
        traverse(tree, {
            leave: function (node) {
                var comment;

                while (cursor < comments.length) {
                    comment = comments[cursor];
                    if (node.range[1] < comment.extendedRange[0]) {
                        break;
                    }

                    if (node.range[1] === comment.extendedRange[0]) {
                        if (!node.trailingComments) {
                            node.trailingComments = [];
                        }
                        node.trailingComments.push(comment);
                        comments.splice(cursor, 1);
                    } else {
                        cursor += 1;
                    }
                }

                // already out of owned node
                if (cursor === comments.length) {
                    return VisitorOption.Break;
                }

                if (comments[cursor].extendedRange[0] > node.range[1]) {
                    return VisitorOption.Skip;
                }
            }
        });

        return tree;
    }

    exports.version = require('./package.json').version;
    exports.Syntax = Syntax;
    exports.traverse = traverse;
    exports.replace = replace;
    exports.attachComments = attachComments;
    exports.VisitorKeys = VisitorKeys;
    exports.VisitorOption = VisitorOption;
    exports.Controller = Controller;
    exports.cloneEnvironment = function () { return clone({}); };

    return exports;
}(exports));
/* vim: set sw=4 ts=4 et tw=80 : */

},{"./package.json":2}],2:[function(require,module,exports){
module.exports={
  "name": "estraverse",
  "description": "ECMAScript JS AST traversal functions",
  "homepage": "https://github.com/estools/estraverse",
  "main": "estraverse.js",
  "version": "4.2.0",
  "engines": {
    "node": ">=0.10.0"
  },
  "maintainers": [
    {
      "name": "Yusuke Suzuki",
      "email": "utatane.tea@gmail.com",
      "web": "https://github.com/Constellation"
    }
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/estools/estraverse.git"
  },
  "devDependencies": {
    "babel-preset-es2015": "^6.3.13",
    "babel-register": "^6.3.13",
    "chai": "^2.1.1",
    "espree": "^1.11.0",
    "gulp": "^3.8.10",
    "gulp-bump": "^0.2.2",
    "gulp-filter": "^2.0.0",
    "gulp-git": "^1.0.1",
    "gulp-tag-version": "^1.2.1",
    "jshint": "^2.5.6",
    "mocha": "^2.1.0"
  },
  "license": "BSD-2-Clause",
  "scripts": {
    "test": "npm run-script lint && npm run-script unit-test",
    "lint": "jshint estraverse.js",
    "unit-test": "mocha --compilers js:babel-register"
  }
}

},{}]},{},[1])(1)
});
/* eslint-env amd */
define('eslint/conf/globals',[], function() {
	return {
		"builtin": {
			"Array": false,
			"ArrayBuffer": false,
			"Boolean": false,
			"constructor": false,
			"DataView": false,
			"Date": false,
			"decodeURI": false,
			"decodeURIComponent": false,
			"encodeURI": false,
			"encodeURIComponent": false,
			"Error": false,
			"escape": false,
			"eval": false,
			"EvalError": false,
			"Float32Array": false,
			"Float64Array": false,
			"Function": false,
			"hasOwnProperty": false,
			"Infinity": false,
			"Int16Array": false,
			"Int32Array": false,
			"Int8Array": false,
			"isFinite": false,
			"isNaN": false,
			"isPrototypeOf": false,
			"JSON": false,
			"Map": false,
			"Math": false,
			"NaN": false,
			"Number": false,
			"Object": false,
			"parseFloat": false,
			"parseInt": false,
			"Promise": false,
			"propertyIsEnumerable": false,
			"Proxy": false,
			"RangeError": false,
			"ReferenceError": false,
			"Reflect": false,
			"RegExp": false,
			"Set": false,
			"String": false,
			"Symbol": false,
			"SyntaxError": false,
			"System": false,
			"toLocaleString": false,
			"toString": false,
			"TypeError": false,
			"Uint16Array": false,
			"Uint32Array": false,
			"Uint8Array": false,
			"Uint8ClampedArray": false,
			"undefined": false,
			"unescape": false,
			"URIError": false,
			"valueOf": false,
			"WeakMap": false,
			"WeakSet": false
		},
		"es5": {
			"Array": false,
			"Boolean": false,
			"constructor": false,
			"Date": false,
			"decodeURI": false,
			"decodeURIComponent": false,
			"encodeURI": false,
			"encodeURIComponent": false,
			"Error": false,
			"escape": false,
			"eval": false,
			"EvalError": false,
			"Function": false,
			"hasOwnProperty": false,
			"Infinity": false,
			"isFinite": false,
			"isNaN": false,
			"isPrototypeOf": false,
			"JSON": false,
			"Math": false,
			"NaN": false,
			"Number": false,
			"Object": false,
			"parseFloat": false,
			"parseInt": false,
			"propertyIsEnumerable": false,
			"RangeError": false,
			"ReferenceError": false,
			"RegExp": false,
			"String": false,
			"SyntaxError": false,
			"toLocaleString": false,
			"toString": false,
			"TypeError": false,
			"undefined": false,
			"unescape": false,
			"URIError": false,
			"valueOf": false
		},
		"es6": {
			"Array": false,
			"ArrayBuffer": false,
			"Boolean": false,
			"constructor": false,
			"DataView": false,
			"Date": false,
			"decodeURI": false,
			"decodeURIComponent": false,
			"encodeURI": false,
			"encodeURIComponent": false,
			"Error": false,
			"escape": false,
			"eval": false,
			"EvalError": false,
			"Float32Array": false,
			"Float64Array": false,
			"Function": false,
			"hasOwnProperty": false,
			"Infinity": false,
			"Int16Array": false,
			"Int32Array": false,
			"Int8Array": false,
			"isFinite": false,
			"isNaN": false,
			"isPrototypeOf": false,
			"JSON": false,
			"Map": false,
			"Math": false,
			"NaN": false,
			"Number": false,
			"Object": false,
			"parseFloat": false,
			"parseInt": false,
			"Promise": false,
			"propertyIsEnumerable": false,
			"Proxy": false,
			"RangeError": false,
			"ReferenceError": false,
			"Reflect": false,
			"RegExp": false,
			"Set": false,
			"String": false,
			"Symbol": false,
			"SyntaxError": false,
			"System": false,
			"toLocaleString": false,
			"toString": false,
			"TypeError": false,
			"Uint16Array": false,
			"Uint32Array": false,
			"Uint8Array": false,
			"Uint8ClampedArray": false,
			"undefined": false,
			"unescape": false,
			"URIError": false,
			"valueOf": false,
			"WeakMap": false,
			"WeakSet": false
		},
		"browser": {
			"addEventListener": false,
			"alert": false,
			"AnalyserNode": false,
			"Animation": false,
			"AnimationEffectReadOnly": false,
			"AnimationEffectTiming": false,
			"AnimationEffectTimingReadOnly": false,
			"AnimationEvent": false,
			"AnimationPlaybackEvent": false,
			"AnimationTimeline": false,
			"applicationCache": false,
			"ApplicationCache": false,
			"ApplicationCacheErrorEvent": false,
			"atob": false,
			"Attr": false,
			"Audio": false,
			"AudioBuffer": false,
			"AudioBufferSourceNode": false,
			"AudioContext": false,
			"AudioDestinationNode": false,
			"AudioListener": false,
			"AudioNode": false,
			"AudioParam": false,
			"AudioProcessingEvent": false,
			"AutocompleteErrorEvent": false,
			"BarProp": false,
			"BatteryManager": false,
			"BeforeUnloadEvent": false,
			"BiquadFilterNode": false,
			"Blob": false,
			"blur": false,
			"btoa": false,
			"Cache": false,
			"caches": false,
			"CacheStorage": false,
			"cancelAnimationFrame": false,
			"CanvasGradient": false,
			"CanvasPattern": false,
			"CanvasRenderingContext2D": false,
			"CDATASection": false,
			"ChannelMergerNode": false,
			"ChannelSplitterNode": false,
			"CharacterData": false,
			"clearInterval": false,
			"clearTimeout": false,
			"clientInformation": false,
			"ClientRect": false,
			"ClientRectList": false,
			"ClipboardEvent": false,
			"close": false,
			"closed": false,
			"CloseEvent": false,
			"Comment": false,
			"CompositionEvent": false,
			"confirm": false,
			"console": false,
			"ConvolverNode": false,
			"Credential": false,
			"CredentialsContainer": false,
			"crypto": false,
			"Crypto": false,
			"CryptoKey": false,
			"CSS": false,
			"CSSAnimation": false,
			"CSSFontFaceRule": false,
			"CSSImportRule": false,
			"CSSKeyframeRule": false,
			"CSSKeyframesRule": false,
			"CSSMediaRule": false,
			"CSSPageRule": false,
			"CSSRule": false,
			"CSSRuleList": false,
			"CSSStyleDeclaration": false,
			"CSSStyleRule": false,
			"CSSStyleSheet": false,
			"CSSSupportsRule": false,
			"CSSTransition": false,
			"CSSUnknownRule": false,
			"CSSViewportRule": false,
			"CustomEvent": false,
			"DataTransfer": false,
			"DataTransferItem": false,
			"DataTransferItemList": false,
			"Debug": false,
			"defaultStatus": false,
			"defaultstatus": false,
			"DelayNode": false,
			"DeviceMotionEvent": false,
			"DeviceOrientationEvent": false,
			"devicePixelRatio": false,
			"dispatchEvent": false,
			"document": false,
			"Document": false,
			"DocumentFragment": false,
			"DocumentTimeline": false,
			"DocumentType": false,
			"DOMError": false,
			"DOMException": false,
			"DOMImplementation": false,
			"DOMParser": false,
			"DOMSettableTokenList": false,
			"DOMStringList": false,
			"DOMStringMap": false,
			"DOMTokenList": false,
			"DragEvent": false,
			"DynamicsCompressorNode": false,
			"Element": false,
			"ElementTimeControl": false,
			"ErrorEvent": false,
			"event": false,
			"Event": false,
			"EventSource": false,
			"EventTarget": false,
			"external": false,
			"FederatedCredential": false,
			"fetch": false,
			"File": false,
			"FileError": false,
			"FileList": false,
			"FileReader": false,
			"find": false,
			"focus": false,
			"FocusEvent": false,
			"FontFace": false,
			"FormData": false,
			"frameElement": false,
			"frames": false,
			"GainNode": false,
			"Gamepad": false,
			"GamepadButton": false,
			"GamepadEvent": false,
			"getComputedStyle": false,
			"getSelection": false,
			"HashChangeEvent": false,
			"Headers": false,
			"history": false,
			"History": false,
			"HTMLAllCollection": false,
			"HTMLAnchorElement": false,
			"HTMLAppletElement": false,
			"HTMLAreaElement": false,
			"HTMLAudioElement": false,
			"HTMLBaseElement": false,
			"HTMLBlockquoteElement": false,
			"HTMLBodyElement": false,
			"HTMLBRElement": false,
			"HTMLButtonElement": false,
			"HTMLCanvasElement": false,
			"HTMLCollection": false,
			"HTMLContentElement": false,
			"HTMLDataListElement": false,
			"HTMLDetailsElement": false,
			"HTMLDialogElement": false,
			"HTMLDirectoryElement": false,
			"HTMLDivElement": false,
			"HTMLDListElement": false,
			"HTMLDocument": false,
			"HTMLElement": false,
			"HTMLEmbedElement": false,
			"HTMLFieldSetElement": false,
			"HTMLFontElement": false,
			"HTMLFormControlsCollection": false,
			"HTMLFormElement": false,
			"HTMLFrameElement": false,
			"HTMLFrameSetElement": false,
			"HTMLHeadElement": false,
			"HTMLHeadingElement": false,
			"HTMLHRElement": false,
			"HTMLHtmlElement": false,
			"HTMLIFrameElement": false,
			"HTMLImageElement": false,
			"HTMLInputElement": false,
			"HTMLIsIndexElement": false,
			"HTMLKeygenElement": false,
			"HTMLLabelElement": false,
			"HTMLLayerElement": false,
			"HTMLLegendElement": false,
			"HTMLLIElement": false,
			"HTMLLinkElement": false,
			"HTMLMapElement": false,
			"HTMLMarqueeElement": false,
			"HTMLMediaElement": false,
			"HTMLMenuElement": false,
			"HTMLMetaElement": false,
			"HTMLMeterElement": false,
			"HTMLModElement": false,
			"HTMLObjectElement": false,
			"HTMLOListElement": false,
			"HTMLOptGroupElement": false,
			"HTMLOptionElement": false,
			"HTMLOptionsCollection": false,
			"HTMLOutputElement": false,
			"HTMLParagraphElement": false,
			"HTMLParamElement": false,
			"HTMLPictureElement": false,
			"HTMLPreElement": false,
			"HTMLProgressElement": false,
			"HTMLQuoteElement": false,
			"HTMLScriptElement": false,
			"HTMLSelectElement": false,
			"HTMLShadowElement": false,
			"HTMLSourceElement": false,
			"HTMLSpanElement": false,
			"HTMLStyleElement": false,
			"HTMLTableCaptionElement": false,
			"HTMLTableCellElement": false,
			"HTMLTableColElement": false,
			"HTMLTableElement": false,
			"HTMLTableRowElement": false,
			"HTMLTableSectionElement": false,
			"HTMLTemplateElement": false,
			"HTMLTextAreaElement": false,
			"HTMLTitleElement": false,
			"HTMLTrackElement": false,
			"HTMLUListElement": false,
			"HTMLUnknownElement": false,
			"HTMLVideoElement": false,
			"IDBCursor": false,
			"IDBCursorWithValue": false,
			"IDBDatabase": false,
			"IDBEnvironment": false,
			"IDBFactory": false,
			"IDBIndex": false,
			"IDBKeyRange": false,
			"IDBObjectStore": false,
			"IDBOpenDBRequest": false,
			"IDBRequest": false,
			"IDBTransaction": false,
			"IDBVersionChangeEvent": false,
			"Image": false,
			"ImageBitmap": false,
			"ImageData": false,
			"indexedDB": false,
			"innerHeight": false,
			"innerWidth": false,
			"InputEvent": false,
			"InputMethodContext": false,
			"IntersectionObserver": false,
			"IntersectionObserverEntry": false,
			"Intl": false,
			"KeyboardEvent": false,
			"KeyframeEffect": false,
			"KeyframeEffectReadOnly": false,
			"length": false,
			"localStorage": false,
			"location": false,
			"Location": false,
			"locationbar": false,
			"matchMedia": false,
			"MediaElementAudioSourceNode": false,
			"MediaEncryptedEvent": false,
			"MediaError": false,
			"MediaKeyError": false,
			"MediaKeyEvent": false,
			"MediaKeyMessageEvent": false,
			"MediaKeys": false,
			"MediaKeySession": false,
			"MediaKeyStatusMap": false,
			"MediaKeySystemAccess": false,
			"MediaList": false,
			"MediaQueryList": false,
			"MediaQueryListEvent": false,
			"MediaSource": false,
			"MediaStream": false,
			"MediaStreamAudioDestinationNode": false,
			"MediaStreamAudioSourceNode": false,
			"MediaStreamEvent": false,
			"MediaStreamTrack": false,
			"menubar": false,
			"MessageChannel": false,
			"MessageEvent": false,
			"MessagePort": false,
			"MIDIAccess": false,
			"MIDIConnectionEvent": false,
			"MIDIInput": false,
			"MIDIInputMap": false,
			"MIDIMessageEvent": false,
			"MIDIOutput": false,
			"MIDIOutputMap": false,
			"MIDIPort": false,
			"MimeType": false,
			"MimeTypeArray": false,
			"MouseEvent": false,
			"moveBy": false,
			"moveTo": false,
			"MutationEvent": false,
			"MutationObserver": false,
			"MutationRecord": false,
			"name": false,
			"NamedNodeMap": false,
			"navigator": false,
			"Navigator": false,
			"Node": false,
			"NodeFilter": false,
			"NodeIterator": false,
			"NodeList": false,
			"Notification": false,
			"OfflineAudioCompletionEvent": false,
			"OfflineAudioContext": false,
			"offscreenBuffering": false,
			"onbeforeunload": true,
			"onblur": true,
			"onerror": true,
			"onfocus": true,
			"onload": true,
			"onresize": true,
			"onunload": true,
			"open": false,
			"openDatabase": false,
			"opener": false,
			"opera": false,
			"Option": false,
			"OscillatorNode": false,
			"outerHeight": false,
			"outerWidth": false,
			"PageTransitionEvent": false,
			"pageXOffset": false,
			"pageYOffset": false,
			"parent": false,
			"PasswordCredential": false,
			"Path2D": false,
			"performance": false,
			"Performance": false,
			"PerformanceEntry": false,
			"PerformanceMark": false,
			"PerformanceMeasure": false,
			"PerformanceNavigation": false,
			"PerformanceResourceTiming": false,
			"PerformanceTiming": false,
			"PeriodicWave": false,
			"Permissions": false,
			"PermissionStatus": false,
			"personalbar": false,
			"Plugin": false,
			"PluginArray": false,
			"PopStateEvent": false,
			"postMessage": false,
			"print": false,
			"ProcessingInstruction": false,
			"ProgressEvent": false,
			"prompt": false,
			"PushManager": false,
			"PushSubscription": false,
			"RadioNodeList": false,
			"Range": false,
			"ReadableByteStream": false,
			"ReadableStream": false,
			"removeEventListener": false,
			"Request": false,
			"requestAnimationFrame": false,
			"resizeBy": false,
			"resizeTo": false,
			"Response": false,
			"RTCIceCandidate": false,
			"RTCSessionDescription": false,
			"RTCPeerConnection": false,
			"screen": false,
			"Screen": false,
			"screenLeft": false,
			"ScreenOrientation": false,
			"screenTop": false,
			"screenX": false,
			"screenY": false,
			"ScriptProcessorNode": false,
			"scroll": false,
			"scrollbars": false,
			"scrollBy": false,
			"scrollTo": false,
			"scrollX": false,
			"scrollY": false,
			"SecurityPolicyViolationEvent": false,
			"Selection": false,
			"self": false,
			"ServiceWorker": false,
			"ServiceWorkerContainer": false,
			"ServiceWorkerRegistration": false,
			"sessionStorage": false,
			"setInterval": false,
			"setTimeout": false,
			"ShadowRoot": false,
			"SharedKeyframeList": false,
			"SharedWorker": false,
			"showModalDialog": false,
			"SiteBoundCredential": false,
			"speechSynthesis": false,
			"SpeechSynthesisEvent": false,
			"SpeechSynthesisUtterance": false,
			"status": false,
			"statusbar": false,
			"stop": false,
			"Storage": false,
			"StorageEvent": false,
			"styleMedia": false,
			"StyleSheet": false,
			"StyleSheetList": false,
			"SubtleCrypto": false,
			"SVGAElement": false,
			"SVGAltGlyphDefElement": false,
			"SVGAltGlyphElement": false,
			"SVGAltGlyphItemElement": false,
			"SVGAngle": false,
			"SVGAnimateColorElement": false,
			"SVGAnimatedAngle": false,
			"SVGAnimatedBoolean": false,
			"SVGAnimatedEnumeration": false,
			"SVGAnimatedInteger": false,
			"SVGAnimatedLength": false,
			"SVGAnimatedLengthList": false,
			"SVGAnimatedNumber": false,
			"SVGAnimatedNumberList": false,
			"SVGAnimatedPathData": false,
			"SVGAnimatedPoints": false,
			"SVGAnimatedPreserveAspectRatio": false,
			"SVGAnimatedRect": false,
			"SVGAnimatedString": false,
			"SVGAnimatedTransformList": false,
			"SVGAnimateElement": false,
			"SVGAnimateMotionElement": false,
			"SVGAnimateTransformElement": false,
			"SVGAnimationElement": false,
			"SVGCircleElement": false,
			"SVGClipPathElement": false,
			"SVGColor": false,
			"SVGColorProfileElement": false,
			"SVGColorProfileRule": false,
			"SVGComponentTransferFunctionElement": false,
			"SVGCSSRule": false,
			"SVGCursorElement": false,
			"SVGDefsElement": false,
			"SVGDescElement": false,
			"SVGDiscardElement": false,
			"SVGDocument": false,
			"SVGElement": false,
			"SVGElementInstance": false,
			"SVGElementInstanceList": false,
			"SVGEllipseElement": false,
			"SVGEvent": false,
			"SVGExternalResourcesRequired": false,
			"SVGFEBlendElement": false,
			"SVGFEColorMatrixElement": false,
			"SVGFEComponentTransferElement": false,
			"SVGFECompositeElement": false,
			"SVGFEConvolveMatrixElement": false,
			"SVGFEDiffuseLightingElement": false,
			"SVGFEDisplacementMapElement": false,
			"SVGFEDistantLightElement": false,
			"SVGFEDropShadowElement": false,
			"SVGFEFloodElement": false,
			"SVGFEFuncAElement": false,
			"SVGFEFuncBElement": false,
			"SVGFEFuncGElement": false,
			"SVGFEFuncRElement": false,
			"SVGFEGaussianBlurElement": false,
			"SVGFEImageElement": false,
			"SVGFEMergeElement": false,
			"SVGFEMergeNodeElement": false,
			"SVGFEMorphologyElement": false,
			"SVGFEOffsetElement": false,
			"SVGFEPointLightElement": false,
			"SVGFESpecularLightingElement": false,
			"SVGFESpotLightElement": false,
			"SVGFETileElement": false,
			"SVGFETurbulenceElement": false,
			"SVGFilterElement": false,
			"SVGFilterPrimitiveStandardAttributes": false,
			"SVGFitToViewBox": false,
			"SVGFontElement": false,
			"SVGFontFaceElement": false,
			"SVGFontFaceFormatElement": false,
			"SVGFontFaceNameElement": false,
			"SVGFontFaceSrcElement": false,
			"SVGFontFaceUriElement": false,
			"SVGForeignObjectElement": false,
			"SVGGElement": false,
			"SVGGeometryElement": false,
			"SVGGlyphElement": false,
			"SVGGlyphRefElement": false,
			"SVGGradientElement": false,
			"SVGGraphicsElement": false,
			"SVGHKernElement": false,
			"SVGICCColor": false,
			"SVGImageElement": false,
			"SVGLangSpace": false,
			"SVGLength": false,
			"SVGLengthList": false,
			"SVGLinearGradientElement": false,
			"SVGLineElement": false,
			"SVGLocatable": false,
			"SVGMarkerElement": false,
			"SVGMaskElement": false,
			"SVGMatrix": false,
			"SVGMetadataElement": false,
			"SVGMissingGlyphElement": false,
			"SVGMPathElement": false,
			"SVGNumber": false,
			"SVGNumberList": false,
			"SVGPaint": false,
			"SVGPathElement": false,
			"SVGPathSeg": false,
			"SVGPathSegArcAbs": false,
			"SVGPathSegArcRel": false,
			"SVGPathSegClosePath": false,
			"SVGPathSegCurvetoCubicAbs": false,
			"SVGPathSegCurvetoCubicRel": false,
			"SVGPathSegCurvetoCubicSmoothAbs": false,
			"SVGPathSegCurvetoCubicSmoothRel": false,
			"SVGPathSegCurvetoQuadraticAbs": false,
			"SVGPathSegCurvetoQuadraticRel": false,
			"SVGPathSegCurvetoQuadraticSmoothAbs": false,
			"SVGPathSegCurvetoQuadraticSmoothRel": false,
			"SVGPathSegLinetoAbs": false,
			"SVGPathSegLinetoHorizontalAbs": false,
			"SVGPathSegLinetoHorizontalRel": false,
			"SVGPathSegLinetoRel": false,
			"SVGPathSegLinetoVerticalAbs": false,
			"SVGPathSegLinetoVerticalRel": false,
			"SVGPathSegList": false,
			"SVGPathSegMovetoAbs": false,
			"SVGPathSegMovetoRel": false,
			"SVGPatternElement": false,
			"SVGPoint": false,
			"SVGPointList": false,
			"SVGPolygonElement": false,
			"SVGPolylineElement": false,
			"SVGPreserveAspectRatio": false,
			"SVGRadialGradientElement": false,
			"SVGRect": false,
			"SVGRectElement": false,
			"SVGRenderingIntent": false,
			"SVGScriptElement": false,
			"SVGSetElement": false,
			"SVGStopElement": false,
			"SVGStringList": false,
			"SVGStylable": false,
			"SVGStyleElement": false,
			"SVGSVGElement": false,
			"SVGSwitchElement": false,
			"SVGSymbolElement": false,
			"SVGTests": false,
			"SVGTextContentElement": false,
			"SVGTextElement": false,
			"SVGTextPathElement": false,
			"SVGTextPositioningElement": false,
			"SVGTitleElement": false,
			"SVGTransform": false,
			"SVGTransformable": false,
			"SVGTransformList": false,
			"SVGTRefElement": false,
			"SVGTSpanElement": false,
			"SVGUnitTypes": false,
			"SVGURIReference": false,
			"SVGUseElement": false,
			"SVGViewElement": false,
			"SVGViewSpec": false,
			"SVGVKernElement": false,
			"SVGZoomAndPan": false,
			"SVGZoomEvent": false,
			"Text": false,
			"TextDecoder": false,
			"TextEncoder": false,
			"TextEvent": false,
			"TextMetrics": false,
			"TextTrack": false,
			"TextTrackCue": false,
			"TextTrackCueList": false,
			"TextTrackList": false,
			"TimeEvent": false,
			"TimeRanges": false,
			"toolbar": false,
			"top": false,
			"Touch": false,
			"TouchEvent": false,
			"TouchList": false,
			"TrackEvent": false,
			"TransitionEvent": false,
			"TreeWalker": false,
			"UIEvent": false,
			"URL": false,
			"URLSearchParams": false,
			"ValidityState": false,
			"VTTCue": false,
			"WaveShaperNode": false,
			"WebGLActiveInfo": false,
			"WebGLBuffer": false,
			"WebGLContextEvent": false,
			"WebGLFramebuffer": false,
			"WebGLProgram": false,
			"WebGLRenderbuffer": false,
			"WebGLRenderingContext": false,
			"WebGLShader": false,
			"WebGLShaderPrecisionFormat": false,
			"WebGLTexture": false,
			"WebGLUniformLocation": false,
			"WebSocket": false,
			"WheelEvent": false,
			"window": false,
			"Window": false,
			"Worker": false,
			"XDomainRequest": false,
			"XMLDocument": false,
			"XMLHttpRequest": false,
			"XMLHttpRequestEventTarget": false,
			"XMLHttpRequestProgressEvent": false,
			"XMLHttpRequestUpload": false,
			"XMLSerializer": false,
			"XPathEvaluator": false,
			"XPathException": false,
			"XPathExpression": false,
			"XPathNamespace": false,
			"XPathNSResolver": false,
			"XPathResult": false,
			"XSLTProcessor": false
		},
		"worker": {
			"applicationCache": false,
			"atob": false,
			"Blob": false,
			"BroadcastChannel": false,
			"btoa": false,
			"Cache": false,
			"caches": false,
			"clearInterval": false,
			"clearTimeout": false,
			"close": true,
			"console": false,
			"fetch": false,
			"FileReaderSync": false,
			"FormData": false,
			"Headers": false,
			"IDBCursor": false,
			"IDBCursorWithValue": false,
			"IDBDatabase": false,
			"IDBFactory": false,
			"IDBIndex": false,
			"IDBKeyRange": false,
			"IDBObjectStore": false,
			"IDBOpenDBRequest": false,
			"IDBRequest": false,
			"IDBTransaction": false,
			"IDBVersionChangeEvent": false,
			"ImageData": false,
			"importScripts": true,
			"indexedDB": false,
			"location": false,
			"MessageChannel": false,
			"MessagePort": false,
			"name": false,
			"navigator": false,
			"Notification": false,
			"onclose": true,
			"onconnect": true,
			"onerror": true,
			"onlanguagechange": true,
			"onmessage": true,
			"onoffline": true,
			"ononline": true,
			"onrejectionhandled": true,
			"onunhandledrejection": true,
			"performance": false,
			"Performance": false,
			"PerformanceEntry": false,
			"PerformanceMark": false,
			"PerformanceMeasure": false,
			"PerformanceNavigation": false,
			"PerformanceResourceTiming": false,
			"PerformanceTiming": false,
			"postMessage": true,
			"Promise": false,
			"Request": false,
			"Response": false,
			"self": true,
			"ServiceWorkerRegistration": false,
			"setInterval": false,
			"setTimeout": false,
			"TextDecoder": false,
			"TextEncoder": false,
			"URL": false,
			"URLSearchParams": false,
			"WebSocket": false,
			"Worker": false,
			"XMLHttpRequest": false
		},
		"node": {
			"__dirname": false,
			"__filename": false,
			"arguments": false,
			"Buffer": false,
			"clearImmediate": false,
			"clearInterval": false,
			"clearTimeout": false,
			"console": false,
			"exports": true,
			"GLOBAL": false,
			"global": false,
			"Intl": false,
			"module": false,
			"process": false,
			"require": false,
			"root": false,
			"setImmediate": false,
			"setInterval": false,
			"setTimeout": false
		},
		"commonjs": {
			"exports": true,
			"module": false,
			"require": false,
			"global": false
		},
		"amd": {
			"define": false,
			"require": false
		},
		"mocha": {
			"after": false,
			"afterEach": false,
			"before": false,
			"beforeEach": false,
			"context": false,
			"describe": false,
			"it": false,
			"mocha": false,
			"setup": false,
			"specify": false,
			"suite": false,
			"suiteSetup": false,
			"suiteTeardown": false,
			"teardown": false,
			"test": false,
			"xcontext": false,
			"xdescribe": false,
			"xit": false,
			"xspecify": false
		},
		"jasmine": {
			"afterAll": false,
			"afterEach": false,
			"beforeAll": false,
			"beforeEach": false,
			"describe": false,
			"expect": false,
			"fail": false,
			"fdescribe": false,
			"fit": false,
			"it": false,
			"jasmine": false,
			"pending": false,
			"runs": false,
			"spyOn": false,
			"waits": false,
			"waitsFor": false,
			"xdescribe": false,
			"xit": false
		},
		"jest": {
			"afterEach": false,
			"beforeEach": false,
			"check": false,
			"describe": false,
			"expect": false,
			"gen": false,
			"it": false,
			"fit": false,
			"jest": false,
			"pit": false,
			"require": false,
			"test": false,
			"xdescribe": false,
			"xit": false
		},
		"qunit": {
			"asyncTest": false,
			"deepEqual": false,
			"equal": false,
			"expect": false,
			"module": false,
			"notDeepEqual": false,
			"notEqual": false,
			"notOk": false,
			"notPropEqual": false,
			"notStrictEqual": false,
			"ok": false,
			"propEqual": false,
			"QUnit": false,
			"raises": false,
			"start": false,
			"stop": false,
			"strictEqual": false,
			"test": false,
			"throws": false
		},
		"phantomjs": {
			"console": true,
			"exports": true,
			"phantom": true,
			"require": true,
			"WebPage": true
		},
		"couch": {
			"emit": false,
			"exports": false,
			"getRow": false,
			"log": false,
			"module": false,
			"provides": false,
			"require": false,
			"respond": false,
			"send": false,
			"start": false,
			"sum": false
		},
		"rhino": {
			"defineClass": false,
			"deserialize": false,
			"gc": false,
			"help": false,
			"importClass": false,
			"importPackage": false,
			"java": false,
			"load": false,
			"loadClass": false,
			"Packages": false,
			"print": false,
			"quit": false,
			"readFile": false,
			"readUrl": false,
			"runCommand": false,
			"seal": false,
			"serialize": false,
			"spawn": false,
			"sync": false,
			"toint32": false,
			"version": false
		},
		"nashorn": {
			"__DIR__": false,
			"__FILE__": false,
			"__LINE__": false,
			"com": false,
			"edu": false,
			"exit": false,
			"Java": false,
			"java": false,
			"javafx": false,
			"JavaImporter": false,
			"javax": false,
			"JSAdapter": false,
			"load": false,
			"loadWithNewGlobal": false,
			"org": false,
			"Packages": false,
			"print": false,
			"quit": false
		},
		"wsh": {
			"ActiveXObject": true,
			"Enumerator": true,
			"GetObject": true,
			"ScriptEngine": true,
			"ScriptEngineBuildVersion": true,
			"ScriptEngineMajorVersion": true,
			"ScriptEngineMinorVersion": true,
			"VBArray": true,
			"WScript": true,
			"WSH": true,
			"XDomainRequest": true
		},
		"jquery": {
			"$": false,
			"jQuery": false
		},
		"yui": {
			"Y": false,
			"YUI": false,
			"YUI_config": false
		},
		"shelljs": {
			"cat": false,
			"cd": false,
			"chmod": false,
			"config": false,
			"cp": false,
			"dirs": false,
			"echo": false,
			"env": false,
			"error": false,
			"exec": false,
			"exit": false,
			"find": false,
			"grep": false,
			"ls": false,
			"ln": false,
			"mkdir": false,
			"mv": false,
			"popd": false,
			"pushd": false,
			"pwd": false,
			"rm": false,
			"sed": false,
			"target": false,
			"tempdir": false,
			"test": false,
			"which": false
		},
		"prototypejs": {
			"$": false,
			"$$": false,
			"$A": false,
			"$break": false,
			"$continue": false,
			"$F": false,
			"$H": false,
			"$R": false,
			"$w": false,
			"Abstract": false,
			"Ajax": false,
			"Autocompleter": false,
			"Builder": false,
			"Class": false,
			"Control": false,
			"Draggable": false,
			"Draggables": false,
			"Droppables": false,
			"Effect": false,
			"Element": false,
			"Enumerable": false,
			"Event": false,
			"Field": false,
			"Form": false,
			"Hash": false,
			"Insertion": false,
			"ObjectRange": false,
			"PeriodicalExecuter": false,
			"Position": false,
			"Prototype": false,
			"Scriptaculous": false,
			"Selector": false,
			"Sortable": false,
			"SortableObserver": false,
			"Sound": false,
			"Template": false,
			"Toggle": false,
			"Try": false
		},
		"meteor": {
			"$": false,
			"_": false,
			"Accounts": false,
			"AccountsClient": false,
			"AccountsServer": false,
			"AccountsCommon": false,
			"App": false,
			"Assets": false,
			"Blaze": false,
			"check": false,
			"Cordova": false,
			"DDP": false,
			"DDPServer": false,
			"DDPRateLimiter": false,
			"Deps": false,
			"EJSON": false,
			"Email": false,
			"HTTP": false,
			"Log": false,
			"Match": false,
			"Meteor": false,
			"Mongo": false,
			"MongoInternals": false,
			"Npm": false,
			"Package": false,
			"Plugin": false,
			"process": false,
			"Random": false,
			"ReactiveDict": false,
			"ReactiveVar": false,
			"Router": false,
			"ServiceConfiguration": false,
			"Session": false,
			"share": false,
			"Spacebars": false,
			"Template": false,
			"Tinytest": false,
			"Tracker": false,
			"UI": false,
			"Utils": false,
			"WebApp": false,
			"WebAppInternals": false
		},
		"mongo": {
			"_isWindows": false,
			"_rand": false,
			"BulkWriteResult": false,
			"cat": false,
			"cd": false,
			"connect": false,
			"db": false,
			"getHostName": false,
			"getMemInfo": false,
			"hostname": false,
			"ISODate": false,
			"listFiles": false,
			"load": false,
			"ls": false,
			"md5sumFile": false,
			"mkdir": false,
			"Mongo": false,
			"NumberInt": false,
			"NumberLong": false,
			"ObjectId": false,
			"PlanCache": false,
			"print": false,
			"printjson": false,
			"pwd": false,
			"quit": false,
			"removeFile": false,
			"rs": false,
			"sh": false,
			"UUID": false,
			"version": false,
			"WriteResult": false
		},
		"applescript": {
			"$": false,
			"Application": false,
			"Automation": false,
			"console": false,
			"delay": false,
			"Library": false,
			"ObjC": false,
			"ObjectSpecifier": false,
			"Path": false,
			"Progress": false,
			"Ref": false
		},
		"serviceworker": {
			"caches": false,
			"Cache": false,
			"CacheStorage": false,
			"Client": false,
			"clients": false,
			"Clients": false,
			"ExtendableEvent": false,
			"ExtendableMessageEvent": false,
			"FetchEvent": false,
			"importScripts": false,
			"registration": false,
			"self": false,
			"ServiceWorker": false,
			"ServiceWorkerContainer": false,
			"ServiceWorkerGlobalScope": false,
			"ServiceWorkerMessageEvent": false,
			"ServiceWorkerRegistration": false,
			"skipWaiting": false,
			"WindowClient": false
		},
		"atomtest": {
			"advanceClock": false,
			"fakeClearInterval": false,
			"fakeClearTimeout": false,
			"fakeSetInterval": false,
			"fakeSetTimeout": false,
			"resetTimeouts": false,
			"waitsForPromise": false
		},
		"embertest": {
			"andThen": false,
			"click": false,
			"currentPath": false,
			"currentRouteName": false,
			"currentURL": false,
			"fillIn": false,
			"find": false,
			"findWithAssert": false,
			"keyEvent": false,
			"pauseTest": false,
			"triggerEvent": false,
			"visit": false
		},
		"protractor": {
			"$": false,
			"$$": false,
			"browser": false,
			"By": false,
			"by": false,
			"DartObject": false,
			"element": false,
			"protractor": false
		},
		"shared-node-browser": {
			"clearInterval": false,
			"clearTimeout": false,
			"console": false,
			"setInterval": false,
			"setTimeout": false
		},
		"webextensions": {
			"browser": false,
			"chrome": false,
			"opr": false
		},
		"greasemonkey": {
			"GM_addStyle": false,
			"GM_deleteValue": false,
			"GM_getResourceText": false,
			"GM_getResourceURL": false,
			"GM_getValue": false,
			"GM_info": false,
			"GM_listValues": false,
			"GM_log": false,
			"GM_openInTab": false,
			"GM_registerMenuCommand": false,
			"GM_setClipboard": false,
			"GM_setValue": false,
			"GM_xmlhttpRequest": false,
			"unsafeWindow": false
		}
	};
});
/*eslint-env amd */
define('eslint/conf/environments',[
    './globals'
], function(globals) {
/**
 * @fileoverview Defines environment settings and globals.
 * @author Elan Shanker
 */


var exports = {
	builtin: globals.es5,
	browser: {
		globals: globals.browser
	},
	node: {
		globals: globals.node,
		parserOptions: {
			ecmaFeatures: {
				globalReturn: true
			}
		}
	},
	commonjs: {
		globals: globals.commonjs,
		parserOptions: {
			ecmaFeatures: {
				globalReturn: true
			}
		}
	},
	"shared-node-browser": {
		globals: globals["shared-node-browser"]
	},
	worker: {
		globals: globals.worker
	},
	amd: {
		globals: globals.amd
	},
	mocha: {
		globals: globals.mocha
	},
	jasmine: {
		globals: globals.jasmine
	},
	jest: {
		globals: globals.jest
	},
	phantomjs: {
		globals: globals.phantomjs
	},
	jquery: {
		globals: globals.jquery
	},
	qunit: {
		globals: globals.qunit
	},
	prototypejs: {
		globals: globals.prototypejs
	},
	shelljs: {
		globals: globals.shelljs
	},
	meteor: {
		globals: globals.meteor
	},
	mongo: {
		globals: globals.mongo
	},
	protractor: {
		globals: globals.protractor
	},
	applescript: {
		globals: globals.applescript
	},
	nashorn: {
		globals: globals.nashorn
	},
	serviceworker: {
		globals: globals.serviceworker
	},
	atomtest: {
		globals: globals.atomtest
	},
	embertest: {
		globals: globals.embertest
	},
	webextensions: {
		globals: globals.webextensions
	},
	es6: {
		globals: globals.es6,
		parserOptions: {
			ecmaVersion: 6
		}
	},
	greasemonkey: {
		globals: globals.greasemonkey
	}
};

	return exports;
});

/*******************************************************************************
 * @license
 * Copyright (c) 2013, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd*/
define('javascript/finder',[
'estraverse/estraverse',
'eslint/conf/environments'
], function(Estraverse, ESlintEnv) {
	
	var Finder = {
		
		visitor: null,
		
		punc: '\n\t\r (){}[]:;,.+=-*^&@!%~`\'\"\/\\',  //$NON-NLS-0$
		
		/**
		 * @name findWord
		 * @description Finds the word from the start position
		 * @function
		 * @public
		 * @memberof javascript.Finder
		 * @param {String} text The text of the source to find the word in
		 * @param {Number} start The current start position of the carat
		 * @returns {String} Returns the computed word from the given string and offset or <code>null</code>
		 */
		findWord: function(text, start) {
			if(text && start > -1) {
				var ispunc = this.punc.indexOf(text.charAt(start)) > -1;
				var pos = ispunc && start > 0 ? start-1 : start;
				while(pos >= 0) {
					if(this.punc.indexOf(text.charAt(pos)) > -1) {
						break;
					}
					pos--;
				}
				var s = pos;
				pos = start;
				while(pos <= text.length) {
					if(this.punc.indexOf(text.charAt(pos)) > -1) {
						break;
					}
					pos++;
				}
				if((s === start || (ispunc && (s === start-1))) && pos === start) {
					return null;
				}
				else if(s === start) {
					return text.substring(s, pos);
				}
				else {
					return text.substring(s+1, pos);
				}
			}
			return null;
		},
		
		/**
		 * @name findNode
		 * @description Finds the AST node for the given offset
		 * @function
		 * @public
		 * @memberof javascript.Finder
		 * @param {Number} offset The offset into the source file
		 * @param {Object} ast The AST to search
		 * @param {Object} options The optional options
		 * @returns The AST node at the given offset or <code>null</code> if it could not be computed.
		 */
		findNode: function(offset, ast, options) {
			var found = null;
			var parents = options && options.parents ? [] : null;
			var next = options && options.next ? options.next : false;
			if(typeof offset === 'number' && offset > -1 && ast) {
				Estraverse.traverse(ast, {
					/**
					 * start visiting an AST node
					 */
					enter: function(node) {
						//only check nodes that are typed, we don't care about any others
						if(node.type && node.range) {
							if(!next && node.type === Estraverse.Syntax.Program && offset < node.range[0]) {
								//https://bugs.eclipse.org/bugs/show_bug.cgi?id=447454
								return Estraverse.VisitorOption.Break;
							}
							// Class and method declarations count offsets including the curly braces {} Bug 494484
							// When offset is touching both identifier and body, we want finder to return the identifier
							var bracesIncluded = false;							
							if (node.range[0] === offset && found && found.range[1] === offset && node.type === 'ClassBody' || node.type === 'FunctionExpression' && found && found.type === 'Identifier'){
								bracesIncluded = true;
							}

							if((!bracesIncluded && node.range[0] <= offset) || (bracesIncluded && node.range[0] < offset) ){
								found = node;
								if(parents) {
									parents.push(node);
								}
							} else {
								if(next) {
									found = node;
									if(parents) {
										parents.push(node);
									}
								}
								if(found.type !== Estraverse.Syntax.Program) {
									//we don't want to find the next node as the program root
									//if program has no children it will be returned on the next pass
									//https://bugs.eclipse.org/bugs/show_bug.cgi?id=442411
									return Estraverse.VisitorOption.Break;
								}
							}
						}
					},
					/** override */
					leave: function(node) {
						if(parents && offset > node.range[1]) {
							parents.pop();
						}
					}
				});
			}
			if(found && parents && parents.length > 0) {
				var p = parents[parents.length-1];
				if(p.type !== 'Program' && p.range[0] === found.range[0] && p.range[1] === found.range[1]) {
					//a node can't be its own parent
					parents.pop();
				}
				found.parents = parents;
			}
			return found;
		},
		
		/**
		 * @description Finds the first non-comment AST node immediately following the given comment node
		 * @param {Object} comment The comment node
		 * @param {Object} ast The AST 
		 * @since 10.0
		 */
		findNodeAfterComment: function(comment, ast) {
			var found = null;
			var parents = [];
			if(Array.isArray(comment.range) && ast) {
				var offset = comment.range[1];
				Estraverse.traverse(ast, {
					/**
					 * start visiting an AST node
					 */
					enter: function(node, last) {
						if(node.type && node.range) {
							if(last) {
								parents.push(last);
							}
							if(offset > node.range[0]) {
								found = node;
							} else {
								found = node;
								if(node.type !== Estraverse.Syntax.Program) {
									return Estraverse.VisitorOption.Break;
								}

							}
						}
					}
				});
			}
			if(found) {
				found.parents = parents;
			}
			return found;
		},
		
		/**
		 * @description Finds all of the AST nodes that start within the given range
		 * @function
		 * @param {Object} ast The AST to inspect
		 * @param {Number} start The starting offset
		 * @param {Number} end The ending offset
		 * @returns {Array.<Object>} Returns the array of AST nodes that start within the given range
		 * @since 11.0
		 */
		findNodesForRange: function findeNodesForRange(ast, start, end) {
			var nodes = [];
			if(ast) {
				Estraverse.traverse(ast, {
					enter: function(node) {
						if(node.range[0] >= start && node.range[0] < end) {
							nodes.push(node);
						}
						if(node.range[0] >= end) {
							return Estraverse.VisitorOption.BREAK;
						}
					}
				});
			}
			return nodes;
		},
		
		/**
		 * @name findToken
		 * @description Finds the token in the given token stream for the given start offset
		 * @function
		 * @public
		 * @memberof javascript.Finder
		 * @param {Number} offset The offset intot the source
		 * @param {Array|Object} tokens The array of tokens to search
		 * @returns {Object} The AST token that starts at the given start offset
		 */
		findToken: function(offset, tokens) {
			if(typeof offset === 'number' && offset > -1 && tokens && tokens.length > 0) {
				var min = 0,
					max = tokens.length-1,
					token, 
					idx = 0;
					token = tokens[0];
				if(offset >= token.range[0] && offset < token.range[1]) {
					token.index = 0;
					return token;
				}
				token = tokens[max];
				if(offset >= token.range[0]) {
					token.index = max;
					return token;
				}
				token = null;
				while(min <= max) {
					idx = Math.floor((min + max) / 2);
					token = tokens[idx];
					if(offset < token.range[0]) {
						max = idx-1;
					}
					else if(offset > token.range[1]) {
						min = idx+1;
					}
					else if(offset === token.range[1]) {
						var next = tokens[idx+1];
						if(next.range[0] === token.range[1]) {
							min = idx+1;
						}
						else {
							token.index = idx;
							return token;
						}
					}
					else if(offset >= token.range[0] && offset < token.range[1]) {
						token.index = idx;
						return token;
					}
					if(min === max) {
						token = tokens[min];
						if(offset >= token.range[0] && offset <= token.range[1]) {
							token.index = min;
							return token;
						}
						return null;
					}
				}
			}
			return null;
		},
		
		/**
		 * @description Finds the doc comment at the given offset. Returns null if there
		 * is no comment at the given offset
		 * @function
		 * @public
		 * @param {Number} offset The offset into the source
		 * @param {Object} ast The AST to search
		 * @returns {Object} Returns the comment node for the given offset or null
		 */
		findComment: function(offset, ast) {
			if(ast.comments) {
				var comments = ast.comments;
				var len = comments.length;
				for(var i = 0; i < len; i++) {
					var comment = comments[i];
					if(comment.range[0] < offset && comment.range[1] >= offset) {
						return comment;
					} else if(offset === ast.range[1] && offset === comment.range[1]) {
					   return comment;
					} else if(offset > ast.range[1] && offset <= comment.range[1]) {
						return comment;
					} else if(comment.range[0] > offset) {
						//we've passed the node
						return null;
					}
				}
				return null;
			}
		},
		
		/**
		 * @description Finds the script blocks from an HTML file and returns the code and offset for found blocks. The returned array may not be sorted.
		 * @function
		 * @public
		 * @param {String} buffer The file contents
		 * @param {Number} offset The offset into the buffer to find the enclosing block for
		 * @returns {Object} An object of script block items {text, offset}
		 * @since 6.0
		 */
		findScriptBlocks: function(buffer, offset) {
			var blocks = [];
			var val = null;
			
			// Find script tags
			var regex = /<\s*script([^>]*)(?:\/>|>((?:.|\r?\n)*?)<\s*\/script[^<>]*>)/ig;
			var langRegex = /(type|language)\s*=\s*"([^"]*)"/i;
			var srcRegex = /src\s*=\s*"([^"]*)"/i;			
			var comments = this.findHtmlCommentBlocks(buffer, offset);
			loop: while((val = regex.exec(buffer)) !== null) {
				var attributes = val[1];
				var text = val[2];
				var deps = null;
				if (attributes){
					var lang = langRegex.exec(attributes);
					// No type/language attribute or empty values default to javascript
					if (lang && lang[2]){
						var type = lang[2];
						if (lang[1] === "language"){
							// Language attribute does not include 'text' prefix
							type = "text/" + type; //$NON-NLS-1$
						}
						if (!/^(application|text)\/(ecmascript|javascript(\d.\d)?|livescript|jscript|x\-ecmascript|x\-javascript)$/ig.test(type)) {
							continue;
						}
					}
					var src = srcRegex.exec(attributes);
					if (src){
						deps = src[1];
					}
				}
				if (!text && deps){
					blocks.push({text: "", offset: 0, dependencies: deps});
					continue;
				}
				if (text === undefined){
					// Inline script blocks with no dependents are not valid i.e. <script/>
					continue;
				}
				var index = val.index+val[0].indexOf('>')+1;
				if(typeof offset !== 'number' || (index <= offset && index+text.length >= offset)) {
					for(var i = 0; i < comments.length; i++) {
						if(comments[i].start <= index && comments[i].end >= index) {
							continue loop;
						}
					}
					blocks.push({
						text: text,
						offset: index,
						dependencies: deps
						
					});
				}
			}
			
			// Find onevent attribute values
			var eventAttributes = {'blur':true, 'change':true, 'click':true, 'dblclick':true, 'focus':true, 'keydown':true, 'keypress':true, 'keyup':true, 'load':true, 'mousedown':true, 'mousemove':true, 'mouseout':true, 'mouseover':true, 'mouseup':true, 'reset':true, 'select':true, 'submit':true, 'unload':true};
			var eventRegex = /(\s+)on(\w*)(\s*=\s*")([^"]*)"/ig;
			var count = 0;
			loop: while((val = eventRegex.exec(buffer)) !== null) {
				count++;
				var leadingWhitespace = val[1];
				var attribute = val[2];
				var assignment = val[3];
				text = val[4];
				if (attribute && attribute.toLowerCase() in eventAttributes){
					if(!text){
						text = "";
					}
					index = val.index + leadingWhitespace.length + 2 + attribute.length + assignment.length;
					if(typeof offset !== 'number' || (index <= offset && index+text.length >= offset)) {
						for(var j = 0; j < comments.length; j++) {
							if(comments[j].start <= index && comments[j].end >= index) {
								continue loop;
							}
						}
						blocks.push({
							text: text,
							offset: index,
							isWrappedFunctionCall: true
						});
					}
				}
			}
			return blocks;
		},
		
		/**
		 * @description Finds all of the block comments in an HTML file
		 * @function
		 * @public
		 * @param {String} buffer The file contents
		 * @param {Number} offset The optional offset to compute the block(s) for
		 * @return {Array} The array of block objects {text, start, end}
		 * @since 6.0
		 */
		findHtmlCommentBlocks: function(buffer, offset) {
			var blocks = [];
			var val = null, regex = /<!--((?:.|\r?\n)*?)-->/ig;
			while((val = regex.exec(buffer)) != null) {
				var text = val[1];
				if(text.length < 1) {
					continue;
				}
				if(typeof offset !== 'number' || (val.index <= offset && val.index+text.length >= val.index)) {
					blocks.push({
						text: text,
						start: val.index,
						end: val.index+text.length
					});
				}
			}
			return blocks;
		},
		
		/**
		 * @description Asks the ESLint environment description if it knows about the given member name and if so
		 * returns the index name it was found in
		 * @function
		 * @param {String} name The name of the member to look up
		 * @returns {String} The name of the ESLint environment it was found in or <code>null</code>
		 * @since 8.0
		 */
		findESLintEnvForMember: function findESLintEnvForMember(name) {
			var keys = Object.keys(ESlintEnv);
			if(keys) {
				var len = keys.length;
				for(var i = 0; i < len; i++) {
					var env = ESlintEnv[keys[i]];
					if(typeof env[name] !== 'undefined') {
						return keys[i];
					}
					var globals = env['globals'];
					if(globals && typeof globals[name] !== 'undefined') {
						return keys[i];
					}
				}
			}
			return null;
		},
		
		/**
		 * @description Find the directive comment with the given name in the given AST
		 * @function
		 * @param {Object} ast The AST to search
		 * @param {String} name The name of the directive to look for. e.g. eslint-env
		 * @returns {Object} The AST comment node or <code>null</code>
		 * @since 8.0
		 */
		findDirective: function findDirective(ast, name) {
			if(ast && typeof name !== 'undefined') {
				var len = ast.comments.length;
				for(var i = 0; i < len; i++) {
					var match = /^\s*(eslint-\w+|eslint|globals?)(\s|$)/.exec(ast.comments[i].value);
					if(match !== null && typeof match !== 'undefined' && match[1] === name) {
						return ast.comments[i];
					}
				}
			}
			return null;
		},
		
		/**
		 * @description Find all directive comments with the given name in the given AST
		 * @function
		 * @param {Object} ast The AST to search
		 * @param {String} name The name of the directive to look for. e.g. eslint-env
		 * @returns {Array} Array of AST comment nodes, possibly empty
		 * @since 14.0
		 */
		findDirectives: function findDirectives(ast, name) {
			var result = [];
			if(ast && typeof name !== 'undefined') {
				var len = ast.comments.length;
				for(var i = 0; i < len; i++) {
					var match = /^\s*(eslint-\w+|eslint|globals?)(\s|$)/.exec(ast.comments[i].value);
					if(match !== null && typeof match !== 'undefined' && match[1] === name) {
						result.push(ast.comments[i]);
					}
				}
			}
			return result;
		},
		
		/**
		 * @description Returns range of the first non-comment token in the body of the ast.
		 * @param {AST} ast The AST
		 * @returns {Array} The range array of first token
		 */
		findProgramStartRange: function findProgramStartNode(ast) {
			if (ast.tokens && ast.tokens.length > 0){
				return ast.tokens[0].range;
			}
			if (ast.comments && ast.comments.length > 0){
				return [ast.comments[0].range[0], ast.comments[0].range[0]+1];
			}
			if (typeof ast.end === "number"){
				return [ast.end, ast.end];
			}
			return [0, 0];
		},
		
		/**
		 * @description Tries to find the comment for the given node. If more than one is found in the array
		 * the last entry is considered 'attached' to the node
		 * @function
		 * @private
		 * @param {Object} node The AST node
		 * @returns {Object} The comment object from the AST or null
		 * @since 8.0
		 */
		findCommentForNode: function findCommentForNode(node) {
			var comments = node.leadingComments;
			var comment = null;
			if(comments && comments.length > 0) {
				//simple case: the node has an attaced comment, take the last comment in the leading array
				comment = comments[comments.length-1];
				if(comment.type === 'Block') {
					comment.node = node;
					return comment;
				}
			} else if(node.type === 'Property') { //TODO https://github.com/jquery/esprima/issues/1071
				comment = findCommentForNode(node.key);
				if(comment) {
					comment.node = node;
					return comment;
				}
			} else if(node.type === 'FunctionDeclaration') { //TODO https://github.com/jquery/esprima/issues/1071
				comment = findCommentForNode(node.id);
				if(comment) {
					comment.node = node;
					return comment;
				}
			}
			//we still want to show a hover for something with no doc
			comment = Object.create(null);
			comment.node = node;
			comment.value = '';
			return comment;
		},
		
		/**
		 * @description Finds the parent function for the given node if one exists
		 * @function
		 * @param {Object} node The AST node
		 * @returns {Object} The function node that directly encloses the given node or ```null```
		 * @since 9.0
		 */
		findParentFunction: function findParentFunction(node) {
			if(node) {
				if(node.parents) {
					//the node has been computed with the parents array from Finder#findNode
					var parents = node.parents;
					var parent = parents.pop();
					while(parent) {
						if(parent.type === 'FunctionDeclaration' || parent.type === 'FunctionExpression') {
							return parent;
						}
						parent = parents.pop();
					}
				} else if(node.parent) {
					//eslint has tagged the AST with herarchy infos
					parent = node.parent;
					while(parent) {
						if(parent.type === 'FunctionDeclaration' || parent.type === 'FunctionExpression') {
							return parent;
						}
						parent = parent.parent;
					}
				}
			}
			return null;
		},
		
		/**
		 * @description Returns if the given node is within an async function declaration or expression
		 * @param {?} node The AST node
		 * @since 16.0
		 * @returns {boolean} Returns true if the node or one of its direct parents is an async function. False otherwise
		 */
		inAsync: function inAsync(node) {
			if(node) {
				if(node.async) {
					return true;
				}
				if(Array.isArray(node.parents)) {
					for(var i = 0, len = node.parents.length; i < len; i++) {
						if(node.parents[i].async) {
							return true;
						}					
					}
				}
			}
			return false;
		},
		
		/**
		 * @description Computes the kind of context to complete in
		 * @param {Object} ast The backing AST to visit
		 * @param {Number} offset The offset into the source
		 * @return {Object} Returns the deferred node and the completion kind
		 * @since 12.0
		 */
		findCompletionKind: function findCompletionKind(ast, offset) {
	    	var node = this.findNode(offset, ast, {parents:true});
	    	if(node) {
	    		var isasync = this.inAsync(node);
	    		var k = {kind: 'top', isasync: isasync};
	    		if(node.type === 'Literal') {
	    			switch(typeof node.value) {
	    				case 'boolean':
	    				case 'number': {
	    					if(offset > node.range[0] && offset <= node.range[1]) {
		    					k.kind = 'unknown';
	    					}
	    					break;
	    				}
	    				case 'string': {
	    					if(offset > node.range[0] && offset < node.range[1]) {
	    						k.kind = 'string';
	    					}
	    					break;
	    				}
	    				case 'object': {
	    					if(node.regex && offset > node.range[0] && offset <= node.range[1]) {
	    						k.kind = 'regex';
							}
							break;
	    				}
	    			}
	    		}
	    		if(node.parents && node.parents.length > 0) {
		    		var prent = node.parents.pop();
		    		switch(prent.type) {
							case 'MemberExpression':
								k.kind = 'member';
								break;
							case 'Program':
							case 'BlockStatement':
								break;
							case 'VariableDeclarator':
								if(!prent.init || prent.init.name === '✖' || offset < prent.init.range[0]) {
									k.kind = 'assign';
								}
								break;
							case 'FunctionDeclaration':
							case 'FunctionExpression':
								if(offset < prent.body.range[0]) {
									k.kind = 'unknown';
								}
								break;
							case 'Property':
								k.kind = 'unknown';
								if(offset-1 >= prent.value.range[0] && offset-1 <= prent.value.range[1]) {
									k.kind = 'prop';
								}
								break;
							case 'SwitchStatement':
								k.kind = 'swtch';
						}
				}
	    	}
	    	node = Finder.findComment(offset, ast);
	    	if(node) {
	    		k.kind = 'doc';
	    		k.node = node;
	    	}
			return k;
		},
		/**
		 * @description Returns the templates that apply to the given completion kind
		 * @public
		 * @param {Array.<Object>} templates The array of template objects to search
		 * @param {String} kind The kind of the completion
		 * @returns {Array} The array of templates that apply to the given completion kind
		 * @since 12.0
		 */
		findTemplatesForKind: function findTemplatesForKind(templates, kind, ecma) {
			var tmplates = [];
			for(var i = 0, len = templates.length; i < len; i++) {
				var template = templates[i];
				if(template.nodes && template.nodes[kind] && (template.ecma <= ecma || template.ecma === undefined)) {
					tmplates.push(template);
				}
			}
			return tmplates;
		}
	};

	return Finder;
});

/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd */
define('javascript/ternPlugins/amqp',[
	"tern/lib/tern",
	"javascript/finder",
	"i18n!javascript/nls/messages"
], function(tern, Finder, Messages) {

	var templates = [
	/* eslint-disable missing-nls */
		{
			name: "amqp",
			nodes: {top:true, member:false, prop:false},
			template: "var amqp = require('amqp');\n",
			doc: Messages['amqpRequire'],
			url: "https://www.npmjs.com/package/amqp"
		},
		{
			name: "amqp connection",
			nodes: {top:true, member:false, prop:false},
			template: "var amqp = require('amqp');\n" +
					  "var ${connection} = amqp.createConnection({\n" +  
					  "\thost: ${host},\n" + 
					  "\tport: ${port},\n" + 
					  "\tlogin: ${login},\n" + 
					  "\tpassword: ${password}\n" + 
					  "});\n",
			doc: Messages['amqpConnection'],
			url: "https://www.npmjs.com/package/amqp"
		},
		{
			name: "amqp on",
			nodes: {top:true, member:false, prop:false},
			template: "${connection}.on(${event}, function() {\n" +  
					  "\t${cursor}\n" + 
					  "});\n",
			doc: Messages['amqpOn'],
			url: "https://www.npmjs.com/package/amqp"
		},
		{
			name: "amqp queue",
			nodes: {top:true, member:false, prop:false},
			template: "${connection}.queue(${id}, function(queue) {\n" + 
					  "\tqueue.bind(\'#\'); //catch all messages\n" +
					  "\tqueue.subscribe(function (message, headers, deliveryInfo) {\n" +
					  "\t\t// Receive messages\n" +
					  "\t});\n" +
					  "\t${cursor}\n" + 
					  "});\n",
			doc: Messages['amqpQueue'],
			url: "https://www.npmjs.com/package/amqp"
		},
		{
			name: "amqp exchange",
			nodes: {top:true, member:false, prop:false},
			template: "var exchange = ${connection}.exchange(${id}, {type: \'topic\'}, function(exchange) {\n" +  
					  "\t${cursor}\n" + 
					  "});\n",
			doc: Messages['amqpExchange'],
			url: "https://www.npmjs.com/package/amqp"
		}
		/* eslint-enable missing-nls */
	];
	
	var cachedQuery;
	
	/* eslint-enable missing-nls */
	tern.registerPlugin("amqp", /* @callback */ function(server, options) { //$NON-NLS-1$
	    return {
	      defs : defs,
	      passes: {
	      	/**
	      	 * @callback
	      	 */
	      	completion: function(file, query) {
	      		cachedQuery = query;
	      	},
	      	/**
	      	 * @callback
	      	 */
	      	variableCompletion: function(file, start, end, gather) {
	      		if(cachedQuery.includeTemplates || cachedQuery.includeTemplates === undefined) {
		      		var kind = Finder.findCompletionKind(file.ast, end);
		      		if(kind && kind.kind) {
			      		var tmpls = Finder.findTemplatesForKind(templates, kind.kind, cachedQuery.ecma ? cachedQuery.ecma : 6);
			      		tmpls.forEach(function(template) {
							gather(template.name, null, 0, function(c) {
								c.template = template.template;
								c.doc = template.doc;
								c.url = template.url;
								c.type = 'template'; //$NON-NLS-1$
								c.ecma = template.ecma;
								c.origin = 'amqp';
								c.overwrite = true;
							});
						});
			      	}
		      	}
	      	}
	      }
	    };
	});
	
	/* eslint-disable missing-nls */
	var defs = {
  "!name": "amqp",
  "!define": {
  	"!known_modules": {
	      "amqp": {
	      	 "Connection" : "Connection",
		     "createConnection": "fn(options: Object, implOptions: Object, readyCallback: fn()) -> +Connection",
          }
      },
    "Message.!1": {
      "parseError": "+Error",
      "rawData": "string"
    },
    "serializer.serializeFields.!2": {
      "reserved1": "number",
      "routingKey": "string",
      "noWait": "bool"
    },
    "Exchange.!3": {
      "type": "string"
    },
    "Queue.prototype.subscribeRaw.!0": {
      "state": "string"
    },
    "Connection.prototype._bodyToBuffer.!ret": "[Connection.prototype._bodyToBuffer.!ret.<i>]",
    "Connection.prototype._bodyToBuffer.!ret.<i>": {
      "contentType": "string"
    },
    "Connection.prototype._parseURLOptions.!ret": {
      "ssl": {
        "enabled": "bool"
      }
    },
    "createExchangeErrorHandlerFor.!ret": "fn(err: ?)",
    "parseTable.!ret": {
      "!doc": "XXX check if bitIndex greater than 7?"
    },
    "parseFields.!1": "[?]",
    "parseFields.!ret": {}
  },
  "Message": {
    "prototype": {
      "acknowledge": {
        "!type": "fn(all: ?)",
        "!doc": "Acknowledge receipt of message.\nSet first arg to 'true' to acknowledge this and all previous messages\nreceived on this queue."
      },
      "reject": {
        "!type": "fn(requeue: ?)",
        "!doc": "Reject an incoming message.\nSet first arg to 'true' to requeue the message."
      }
    },
    "!type": "fn(queue: +Queue, args: ?)",
    "!doc": "Properties:\n- routingKey\n- size\n- deliveryTag\n\n- contentType (default 'application/octet-stream')\n- contentEncoding\n- headers\n- deliveryMode\n- priority (0-9)\n- correlationId\n- replyTo\n- experation\n- messageId\n- timestamp\n- userId\n- appId\n- clusterId",
    "queue": "+Queue",
    "read": "number",
    "size": "number"
  },
  "serializer": {
    "serializeFloat": "fn(b: ?, size: number, value: ?, bigEndian: ?)",
    "serializeInt": "fn(b: ?, size: number, int: number)",
    "serializeShortString": "fn(b: ?, string: ?)",
    "serializeLongString": "fn(b: ?, string: ?)",
    "serializeDate": "fn(b: ?, date: ?)",
    "serializeBuffer": "fn(b: ?, buffer: ?)",
    "serializeBase64": "fn(b: ?, buffer: ?)",
    "isBigInt": "fn(value: ?) -> bool",
    "getCode": "fn(dec: ?) -> string",
    "isFloat": "fn(value: ?) -> bool",
    "serializeValue": "fn(b: ?, value: ?)",
    "serializeTable": "fn(b: ?, object: ?)",
    "serializeArray": "fn(b: ?, arr: ?)",
    "serializeFields": "fn(buffer: ?, fields: ?, args: ?, strict: bool)"
  },
  "methods": {
    "<i>": {
      "!doc": "debug(name);"
    },
    "!doc": "methods keyed on their name"
  },
  "methodTable": {
    "<i>": {
      "<i>": "methods.<i>"
    },
    "!doc": "a look up table for methods recieved\nindexed on class id, method id"
  },
  "classes": {
    "!doc": "classes keyed on their index"
  },
  "Exchange": {
    "prototype": {
      "_onMethod": "fn(channel: ?, method: ?, args: ?) -> bool",
      "publish": {
        "!type": "fn(routingKey: ?, data: ?, options: ?, callback: ?)",
        "!doc": "exchange.publish('routing.key', 'body');\n\nthe third argument can specify additional options\n- mandatory (boolean, default false)\n- immediate (boolean, default false)\n- contentType (default 'application/octet-stream')\n- contentEncoding\n- headers\n- deliveryMode\n- priority (0-9)\n- correlationId\n- replyTo\n- expiration\n- messageId\n- timestamp\n- userId\n- appId\n- clusterId\n\nthe callback is optional and is only used when confirm is turned on for the exchange"
      },
      "_awaitConfirm": {
        "!type": "fn(task: ?, callback: ?)",
        "!doc": "registers tasks for confirms"
      },
      "cleanup": {
        "!type": "fn()",
        "!doc": "do any necessary cleanups eg. after queue destruction"
      },
      "destroy": "fn(ifUnused: ?)",
      "unbind": "fn()",
      "bind": "fn()",
      "bind_headers": "fn()"
    },
    "!type": "fn(connection: +Connection, channel: number, name: ?, options: Exchange.!3, openCallback: ?)",
    "binds": "number",
    "exchangeBinds": "number",
    "sourceExchanges": {
      "<i>": "+Exchange"
    },
    "_sequence": "number",
    "_unAcked": {},
    "_addedExchangeErrorHandler": "bool",
    "state": "string",
    "channel": "number",
    "connection": "+Connection",
    "_tasks": "[?]"
  },
  "Queue": {
    "prototype": {
      "subscribeRaw": "fn(options: Queue.consumerTagListeners.<i>, messageListener: Queue.consumerTagListeners.<i>)",
      "unsubscribe": "fn(consumerTag: ?)",
      "subscribe": "fn(options: ?, messageListener: ?)",
      "shift": {
        "!type": "fn(reject: ?, requeue: ?)",
        "!doc": "Acknowledges the last message"
      },
      "bind": "fn(exchange: string, routingKey: string, callback: string)",
      "unbind": "fn(exchange: string, routingKey: string)",
      "bind_headers": "fn()",
      "unbind_headers": "fn()",
      "destroy": "fn(options: ?)",
      "purge": "fn()",
      "_onMethod": "fn(channel: ?, method: ?, args: ?)",
      "_onContentHeader": "fn(channel: number, classInfo: ?, weight: number, properties: parseFields.!ret, size: number)",
      "_onContent": "fn(channel: number, data: ?)",
      "flow": "fn(active: ?)",
      "subscribeJSON": "Queue.prototype.subscribe"
    },
    "!type": "fn(connection: +Connection, channel: number, name: ?, options: ?, callback: ?)",
    "name": "string",
    "_bindings": {
      "<i>": {
        "<i>": "number"
      }
    },
    "consumerTagListeners": {
      "<i>": {
        "state": "string"
      }
    },
    "consumerTagOptions": {
      "<i>": "Queue.consumerTagListeners.<i>"
    },
    "options": {
      "autoDelete": "bool",
      "closeChannelOnUnsubscribe": "bool"
    },
    "state": "string",
    "_bindCallback": "string",
    "_sequence": "number",
    "confirm": "bool",
    "currentMessage": "+Message"
  },
  "AMQPParser": {
    "prototype": {
      "throwError": {
        "!type": "fn(error: string)",
        "!doc": "If there's an error in the parser, call the onError handler or throw"
      },
      "execute": {
        "!type": "fn(data: ?)",
        "!doc": "Everytime data is recieved on the socket, pass it to this function for\nparsing."
      },
      "_parseMethodFrame": "fn(channel: number, buffer: ?)",
      "_parseHeaderFrame": "fn(channel: number, buffer: ?)"
    },
    "!type": "fn(version: string, type: string)",
    "!doc": "An interruptible AMQP parser.\n\ntype is either 'server' or 'client'\nversion is '0-9-1'.\n\nInstances of this class have several callbacks\n- onMethod(channel, method, args);\n- onHeartBeat()\n- onContent(channel, buffer);\n- onContentHeader(channel, class, weight, properties, size);\n\nThis class does not subclass EventEmitter, in order to reduce the speed\nof emitting the callbacks. Since this is an internal class, that should\nbe fine.",
    "isClient": "bool",
    "state": "string",
    "parse": "fn(data: ?) -> AMQPParser.parse",
    "onMethod": "fn(channel: number, method: methods.<i>, args: parseFields.!ret)",
    "onContent": "fn(channel: number, data: ?)",
    "onContentHeader": "fn(channel: number, classInfo: ?, weight: number, properties: parseFields.!ret, size: number)",
    "onHeartBeat": "fn()",
    "onError": "fn(e: string)"
  },
  "maxFrameBuffer": {
    "!type": "number",
    "!doc": "parser"
  },
  "channelMax": {
    "!type": "number",
    "!doc": "copying qpid)"
  },
  "defaultPorts": {
    "amqp": "number",
    "amqps": "number"
  },
  "defaultOptions": {
    "host": "string",
    "port": "number",
    "login": "string",
    "password": "string",
    "authMechanism": "string",
    "vhost": "string",
    "connectionTimeout": "number",
    "ssl": {
      "enabled": "bool"
    }
  },
  "defaultSslOptions": {
    "port": "number",
    "ssl": {
      "rejectUnauthorized": "bool"
    }
  },
  "defaultImplOptions": {
    "defaultExchangeName": "string",
    "reconnect": "bool",
    "reconnectBackoffStrategy": "string",
    "reconnectExponentialLimit": "number",
    "reconnectBackoffTime": "number"
  },
  "defaultClientProperties": {
    "platform": "string",
    "product": "string"
  },
  "Connection": {
    "prototype": {
      "setOptions": "fn(options: ?)",
      "setImplOptions": "fn(options: ?)",
      "connect": "fn()",
      "reconnect": "fn()",
      "disconnect": "fn()",
      "addAllListeners": "fn()",
      "on": "fn(event: string, action: fn())",
      "heartbeat": "fn()",
      "exchange": {
        "!type": "fn(name: ?, options: Exchange.!3, openCallback: ?) -> +Exchange",
        "!doc": "connection.exchange('my-exchange', { type: 'topic' });\nOptions\n- type 'fanout', 'direct', or 'topic' (default)\n- passive (boolean)\n- durable (boolean)\n- autoDelete (boolean, default true)"
      },
      "exchangeClosed": {
        "!type": "fn(name: ?)",
        "!doc": "remove an exchange when it's closed (called from Exchange)"
      },
      "queue": {
        "!type": "fn(name: ?) -> +Queue",
        "!doc": "Options\n- passive (boolean)\n- durable (boolean)\n- exclusive (boolean)\n- autoDelete (boolean, default true)"
      },
      "queueClosed": {
        "!type": "fn(name: ?)",
        "!doc": "remove a queue when it's closed (called from Queue)"
      },
      "publish": {
        "!type": "fn(routingKey: ?, body: ?, options: ?, callback: ?)",
        "!doc": "Publishes a message to the default exchange."
      },
      "_bodyToBuffer": "fn(body: ?) -> [?]",
      "_inboundHeartbeatTimerReset": "fn()",
      "_outboundHeartbeatTimerReset": "fn()",
      "_saslResponse": "fn() -> ?|string",
      "_onMethod": "fn(channel: number, method: methods.<i>, args: parseFields.!ret)",
      "_parseURLOptions": {
        "!type": "fn(connectionString: ?) -> Connection.prototype._parseURLOptions.!ret",
        "!doc": "Generate connection options from URI string formatted with amqp scheme."
      },
      "_chooseHost": {
        "!type": "fn() -> !this.options.host",
        "!doc": "If you pass a array of hosts, lets choose a random host or the preferred host number, or then next one."
      },
      "_createSocket": "fn()",
      "end": "fn()",
      "_getSSLOptions": "fn() -> !this.sslConnectionOptions",
      "_startHandshake": {
        "!type": "fn()",
        "!doc": "Time to start the AMQP 7-way connection initialization handshake!\n1. The client sends the server a version string"
      },
      "_sendBody": {
        "!type": "fn(channel: number, body: ?, properties: ?)",
        "!doc": "Parse helpers"
      },
      "_sendHeader": {
        "!type": "fn(channel: number, size: ?, properties: ?)",
        "!doc": "connection: the connection\nchannel: the channel to send this on\nsize: size in bytes of the following message\nproperties: an object containing any of the following:\n- contentType (default 'application/octet-stream')\n- contentEncoding\n- headers\n- deliveryMode\n- priority (0-9)\n- correlationId\n- replyTo\n- expiration\n- messageId\n- timestamp\n- userId\n- appId\n- clusterId"
      },
      "_sendMethod": "fn(channel: number, method: ?, args: ?)",
      "generateChannelId": {
        "!type": "fn() -> !this.channelCounter",
        "!doc": "tries to find the next available id slot for a channel"
      }
    },
    "!type": "fn(connectionArgs: ?, options: ?, readyCallback: ?)",
    "connectionAttemptScheduled": {
      "!type": "bool",
      "!doc": "Set to false, so that if we fail in the reconnect attempt, we can\nschedule another one."
    },
    "_defaultExchange": "+Exchange",
    "channelCounter": "number",
    "_blocked": "bool",
    "channels": {
      "!doc": "In the case where this is a reconnection, do not trample on the existing\nchannels.\nFor your reference, channel 0 is the control channel.",
      "<i>": "+Queue"
    },
    "exchanges": {
      "<i>": "+Exchange"
    },
    "parser": {
      "!type": "+AMQPParser",
      "!doc": "Reset parser state"
    },
    "readyEmitted": {
      "!type": "bool",
      "!doc": "Set 'ready' flag for auth failure detection."
    },
    "_inboundHeartbeatTimer": "number",
    "_outboundHeartbeatTimer": "number",
    "hosti": {
      "!type": "number",
      "!doc": "If this is already set, it looks like we want to choose another one.\nAdd one to hosti but don't overflow it."
    },
    "<i>": "fn()",
    "sslConnectionOptions": {}
  },
  "Channel": {
    "prototype": {
      "closeOK": "fn()",
      "reconnect": "fn()",
      "_taskPush": "fn(reply: ?, cb: ?)",
      "_tasksFlush": "fn()",
      "_handleTaskReply": "fn(channel: ?, method: ?, args: ?) -> bool",
      "_onChannelMethod": "fn(channel: ?, method: ?, args: ?)",
      "close": "fn(reason: ?)"
    },
    "!type": "fn(connection: +Connection, channel: number)",
    "!doc": "This class is not exposed to the user. Queue and Exchange are subclasses\nof Channel. This just provides a task queue."
  },
  "createExchangeErrorHandlerFor": {
    "!type": "fn(exchange: +Exchange) -> fn(err: ?)",
    "!doc": "creates an error handler scoped to the given `exchange`"
  },
  "parseShortString": "fn(buffer: ?)",
  "parseLongString": "fn(buffer: ?)",
  "parseSignedInteger": "fn(buffer: ?) -> !0.<i>",
  "parseValue": "fn(buffer: ?) -> !0.<i>",
  "parseTable": "fn(buffer: ?) -> parseTable.!ret",
  "parseFields": "fn(buffer: ?, fields: [?]) -> parseFields.!ret",
  "i": "number",
  "o": "number",
  "bigEndian": "bool",
  "match": "[string]",
  "Error": {
    "name": "string"
  }
};
});
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd*/
define('javascript/util',[
], function() {
	/**
	 * @description Returns if the given character is upper case or not considering the locale
	 * @param {String} string A string of at least one char14acter
	 * @return {Boolean} True iff the first character of the given string is uppercase
	 */
	 function isUpperCase(string) {
		if (string.length < 1) {
		return false;
		}
		if (isNaN(string.charCodeAt(0))) {
			return false;
		}
		return string.toLocaleUpperCase().charAt(0) === string.charAt(0);
	}
	
	/**
	 * @description Match ignoring case and checking camel case.
	 * @param {String} prefix
	 * @param {String} target
	 * @returns {Boolean} If the two strings match
	 */
	function looselyMatches(prefix, target) {
		if (typeof prefix !== "string" || typeof target !== "string") {
			return false;
		}

		// Zero length string matches everything.
		if (prefix.length === 0) {
			return true;
		}

		// Exclude a bunch right away
		if (prefix.charAt(0).toLowerCase() !== target.charAt(0).toLowerCase()) {
			return false;
		}

		if (startsWith(target, prefix)) {
			return true;
		}

		var lowerCase = target.toLowerCase();
		if (startsWith(lowerCase, prefix)) {
			return true;
		}
		
		var _prefix = prefix.toLowerCase();

		var equalIndex = prefix.indexOf("=");
		if (equalIndex !== -1) {
			if (startsWith(target, prefix.substring(0, equalIndex))) {
				return true;
			}
		}
		// Test for camel characters in the prefix.
		if (prefix === _prefix) {
			return false;
		}
		//https://bugs.eclipse.org/bugs/show_bug.cgi?id=473777
		if(startsWith(lowerCase, _prefix)) {
			return true;
		}
		var prefixParts = toCamelCaseParts(prefix);
		var targetParts = toCamelCaseParts(target);

		if (prefixParts.length > targetParts.length) {
			return false;
		}

		for (var i = 0; i < prefixParts.length; ++i) {
			if (!startsWith(targetParts[i], prefixParts[i])) {
				return false;
			}
		}

		return true;
	}
	
	/**
	 * @description Returns if the string starts with the given prefix
	 * @param {String} s The string to check
	 * @param {String} pre The prefix 
	 * @returns {Boolean} True if the string starts with the prefix
	 */
	function startsWith(s, pre) {
		return s.slice(0, pre.length) === pre;
	}
	
	/**
	 * @description Convert an input string into parts delimited by upper case characters. Used for camel case matches.
	 * e.g. GroClaL = ['Gro','Cla','L'] to match say 'GroovyClassLoader'.
	 * e.g. mA = ['m','A']
	 * @function
	 * @public
	 * @param {String} str
	 * @return Array.<String>
	 */
	function toCamelCaseParts(str) {
		var parts = [];
		for (var i = str.length - 1; i >= 0; --i) {
			if (isUpperCase(str.charAt(i))) {
				parts.push(str.substring(i));
				str = str.substring(0, i);
			}
		}
		if (str.length !== 0) {
			parts.push(str);
		}
		return parts.reverse();
	}
	
	var emptyAST = {
		type: "Program", //$NON-NLS-0$
		body: [],
		comments: [],
		tokens: [],
		range: [0, 0],
		loc: {
			start: {},
			end: {}
		}
	};
	
	/**
	 * @description Creates a new empty AST for the fatal thrown error case
	 * @param {Object} error The fatal error thrown while trying to parse
	 * @param {String} name The name of the file we tried to parse
	 * @param {String} text The text we tried to parse
	 * @returns {Object} An empty AST with the fatal error attached in the errors array
	 * @since 11.0
	 */
	function errorAST(error, name, text) {
		var ast = emptyAST;
		ast.range[1] = typeof text === 'string' ? text.length : 0;
		ast.loc.start.line = error.lineNumber;
		ast.loc.start.column = 0;
		ast.loc.end.line = error.lineNumber;
		ast.loc.end.column = error.column;
		ast.errors = [error];
        ast.sourceFile  = Object.create(null);
        ast.sourceFile.text = text;
        ast.sourceFile.name = name;
        return ast;
	}
	
	/**
	 * @description Makes the errors from the given AST safe to transport (using postMessage for example)
	 * @param {Object} ast The AST to serialize errors for
	 * @returns {Array.<Object>} The searialized errors
	 * @since 11.0
	 */
	function serializeAstErrors(ast) {
		var errors = [];
		if(ast && ast.errors) {
			ast.errors.forEach(function(error) {
				var result = error ? JSON.parse(JSON.stringify(error)) : error; // sanitizing Error object
				if (error instanceof Error) {
					result.__isError = true;
					result.lineNumber = typeof result.lineNumber === 'number' ? result.lineNumber : error.lineNumber; //FF fails to include the line number from JSON.stringify
					result.message = result.message || error.message;
					result.name = result.name || error.name;
					result.stack = result.stack || error.stack;
				}
				var msg = error.message;
				result.message = msg = msg.replace(/^Line \d+: /, '');
				errors.push(result);
			});
		}
		return errors;
	}

	return {
		isUpperCase: isUpperCase,
		looselyMatches: looselyMatches,
		startsWith: startsWith,
		toCamelCaseParts: toCamelCaseParts,
		errorAST: errorAST,
		serializeAstErrors: serializeAstErrors
	};
});

/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd*/
define('javascript/orionAcorn',[
	'javascript/util',
], function(Util) {
	
	function OrionAcorn() {
		this.dependencies = {};
		this.environments = {};
		this.comments = [];
		this.tokens = [];
		this.leadingCommentsIndex = 0;
		this.trailingCommentsIndex = 0;
		this.errors = [];
		this.needReset = true;
		this.currentOptions = {};
	}

	OrionAcorn.prototype.reset = function reset() {
		this.comments = [];
		this.tokens = [];
		this.leadingCommentsIndex = 0;
		this.trailingCommentsIndex = 0;
	};
	
	OrionAcorn.prototype.initialize = function initialize() {
		this.dependencies = {};
		this.environments = {};
		this.reset();
		this.errors = [];
		this.needReset = true;
		this.currentOptions = {};
	};

	/**
	 * @name onToken
	 * @description Function called when recording a token
	 * @param token the given token to record
	 */
	OrionAcorn.prototype.onToken = function onToken(token) {
		var type = "Punctuator"; //$NON-NLS-1$
		var label = token.type.label;
		var eof = false;
		var value = token.value;
		switch(label) {
			case "num" :
				//num: new TokenType("num", startsExpr),
				type = "Numeric"; //$NON-NLS-1$
				break;
			case "regexp" :
				//regexp: new TokenType("regexp", startsExpr),
				type = "RegularExpression"; //$NON-NLS-1$
				token.value = "/" + value.pattern + "/" + (value.flags ? value.flags : "");
				break;
			case "string" :
				//string: new TokenType("string", startsExpr),
				type = "String"; //$NON-NLS-1$
				break;
			case "name" :
				// name: new TokenType("name", startsExpr),
				type = "Identifier"; //$NON-NLS-1$
				break;
			case "eof" :
				//eof: new TokenType("eof)
				eof = true;
				break;
			default:
				var keyword = token.type.keyword;
				if (keyword) {
					switch(keyword) {
						case "null" :
							type = "Null"; //$NON-NLS-1$
							break;
						case "true" :
						case "false" :
							type = "Boolean"; //$NON-NLS-1$
							break;
						default: 
							type = "Keyword"; //$NON-NLS-1$
					}
				}
		}
		if (!eof) {
			var start = token.start;
			var end = token.end;
			if (start >= end) return; // handle recovered tokens
			var result = Object.create(null);
			result.type = type;
			result.index = this.tokens.length;
			if (token.range) {
				result.range = token.range;
			}
			if (token.loc) {
				result.loc = token.loc;
			}
			result.start = start;
			result.end = end;
			if (typeof value === "undefined") {
				result.value = label;
			} else {
				result.value = token.value;
			}
			this.tokens.push(result);
		}
	};
	
	/**
	 * @name onComment
	 * @description function called when a comment is recorded
	 * @param block a boolean to indicate if this is a block comment (true) or a line comment (false)
	 * @param text the given comment contents
	 * @param start the given start position
	 * @param end the given end position
	 * @param startLoc the given start location
	 * @param endLoc the given end location
	 */
	OrionAcorn.prototype.onComment = function onComment(block, text, start, end, startLoc, endLoc) {
		var comment = {
			type: block ? 'Block' : 'Line', //$NON-NLS-1$ //$NON-NLS-2$
			value: text,
			start: start,
			end: end
		};
		if (this.currentOptions.locations) {
			comment.loc = {
				start: startLoc,
				end: endLoc
			};
		}
		if (this.currentOptions.ranges) {
			comment.range = [start, end];
		}
		this.comments.push(comment);
	};

	/**
	 * @description Collects the dependencies from call expressions and new expressions
	 * @param {Node} callee The named callee node 
	 * @param {Array.<Node>} args The list of arguments for the expression
	 * @param {Object} envs	The environemnts
	 * @param {Array.<Object>} deps The dependencies
	 */
	function collectDeps(callee, args, envs, deps) {
		var len = args.length;
		if (len === 0) return;
		if(callee.name === 'importScripts') {
			addArrayDeps(args, deps, "browser"); //importScripts('foo', 'bar'...) //$NON-NLS-1$
		} else if(callee.name === 'Worker') {
			addDep(args[0], deps, "browser"); //$NON-NLS-1$
		} else if(callee.name === 'require') {
			var _a = args[0];
			if(_a.type === 'ObjectExpression') {
				envs.amd = true;
			}
			if(_a.type === "ArrayExpression") {
				envs.amd = true;
				addArrayDeps(_a.elements, deps, "amd"); //require([foo]) //$NON-NLS-1$
			} else if(_a.type === "Literal") {
				envs.node = true;
				addDep(_a, deps, "node"); // require('foo') //$NON-NLS-1$
			}
			if(len > 1) {
				_a = args[1];
				if(_a.type === "ArrayExpression") {
					envs.node = true;
					addArrayDeps(_a.elements, deps, "node"); //$NON-NLS-1$
				}
			}
		} else if(callee.name === 'requirejs') {
			_a = args[0];
			if(_a.type === "ArrayExpression") {
				envs.amd = true;
				addArrayDeps(_a.elements, deps, "amd"); //requirejs([foo]) //$NON-NLS-1$
			}
		} else if(callee.name === 'define' && len > 0) {
			_a = args[0];
			if(_a.type === "Literal" && len > 1) {
				_a = args[1];
			}
			if(_a.type === "ArrayExpression") {
				envs.amd = true;
				addArrayDeps(_a.elements, deps, "amd"); //$NON-NLS-1$
			} else if(_a.type === "FunctionExpression"){
				envs.amd = true;
				if (_a.params && _a.params.length > 0 && _a.params[0].name === 'require'){
					envs.simplifiedCommonJS = true;
				}
			} else if (_a.type === 'ObjectExpression') {
				envs.amd = true;
			}
		}
	}
	
	/**
	 * @description Adds a dependency if it has not already been added
	 * @param {Object} node The AST node
	 * @param {Object} deps	The map for dependencies
	 * @param {String} env The environmentn kind the dep came from
	 */
	function addDep(node, deps, env) {
		if(node && node.type === "Literal") {
			if (!deps[node.value]) {
				deps[node.value] = {value: node.value, env: env};
			}
		}
	}
	
	/**
	 * @description Adds all of the entries from the array of deps to the global state
	 * @param {Array} array The array of deps to add
	 */
	function addArrayDeps(array, deps, env) {
		var len = array.length;
		for(var i = 0; i < len; i++) {
			addDep(array[i], deps, env);
		}
	}
	/**
	 * Define an acorn plugin to record the comments even if there are syntax errors (incomplete block comments),
	 * it linked comments and nodes (leadingComments and trailingComments) and it records environments and dependencies
	 */
	OrionAcorn.prototype.acornPlugin = function acornPlugin(instance, opts) {
		if (!opts) {
			return;
		}
		var that = this;
		/**
		 * Returns a deep copy of the given obj
		 */
		function deepCopy(obj) {
			var ret = {}, key, val;
			for (key in obj) {
				if (obj.hasOwnProperty(key)) {
					val = obj[key];
					if (typeof val === 'object' && val !== null) {
						ret[key] = deepCopy(val);
					} else {
						ret[key] = val;
					}
				}
			}
			return ret;
		}

		instance.extend("raise", function(nextMethod) { //$NON-NLS-1$
			function recordError(errors, error) {
				var len = errors.length;
				for (var e = 0; e < len; e++) {
					var existing = errors[e];
					if (existing.index === error.index && existing.message === error.message) {
						return; // do not add duplicate
					}
				}
				errors.push(error);
			}
			return function (pos, message) {
				try {
					return nextMethod.call(this, pos, message);
				} catch(err) {
					if (err instanceof SyntaxError) {
						if (that.needReset) {
							// we only reset tokens once. We don't want to reset them again when the syntax error is thrown during acorn_loose parsing
							that.reset();
							that.needReset = false;
						}
					}
					err.index = pos;
					err.start = pos;
					err.end = this.input.length >= pos + 1 ? pos + 1 : this.input.length;
					recordError(that.errors, err);
					throw err;
				}
			};
		});
		instance.extend("startNode", function(nextMethod) { //$NON-NLS-1$
			return function () {
				var node = nextMethod.call(this);
				// attach leading comments
				var max = that.comments.length;
				if (max !== that.leadingCommentsIndex) {
					// we got new comments since the last node
					var i = that.leadingCommentsIndex;
					loop: for (; i< max; i++) {
						var comment = that.comments[i];
						if (node.range[0] >= comment.range[1]) {
							// attach the comment to the node
							if (!node.leadingComments) {
								node.leadingComments = [];
							}
							node.leadingComments.push(deepCopy(that.comments[i]));
						} else {
							break loop;
						}
					}
					that.leadingCommentsIndex = i;
				}
				return node;
			};
		});
		instance.extend("finishNode", function(nextMethod) { //$NON-NLS-1$
			return function(node, type) {
				if (type === "CallExpression" || type === "NewExpression") {
					collectDeps(node.callee, node.arguments, that.environments, that.dependencies);
				} else if (type === 'ImportDeclaration'){
					addDep(node.source, that.dependencies, 'es_modules'); //$NON-NLS-1$
					that.environments.es_modules = true;
				} else if (type === 'ExportNamedDeclaration' || type === 'ExportDefaultDeclaration' || type === 'ExportAllDeclaration'){
					that.environments.es_modules = true;
				} else if(type === "MemberExpression") {
					if(node.property.name === "exports" && node.object.name === "module") {
						that.environments.node = true;
					}
				}
				var result = nextMethod.call(this, node, type);
				// attach trailing comments
				var max = that.comments.length;
				if (max !== that.trailingCommentsIndex) {
					// we got new comments since the last node
					var i = that.trailingCommentsIndex;
					loop: for (; i< max; i++) {
						var comment = that.comments[i];
						if (result.range[1] <= comment.range[0]) {
							// attach the comment to the node
							if (!result.trailingComments) {
								result.trailingComments = [];
							}
							result.trailingComments.push(deepCopy(that.comments[i]));
						} else {
							continue loop;
						}
					}
					that.trailingCommentsIndex = i;
				}
				result.sourceFile = that.sourceFile;
				if (result.end > that.sourceFile.text.length) {
					var actualEnd = that.sourceFile.text.length;
					result.end = actualEnd;
					if (result.range) {
						result.range[1] = actualEnd;
					}
				}
				return result;
			};
		});
		instance.extend("skipBlockComment", function(nextMethod) { //$NON-NLS-1$
			return function() {
				var lineBreak = /\r\n?|\n|\u2028|\u2029/;
				var lineBreakG = new RegExp(lineBreak.source, "g");

				var startLoc = this.curPosition();
				var start = this.pos, end = this.input.indexOf("*/", this.pos += 2); //$NON-NLS-1$
				if (end !== -1) {
					this.pos -= 2;
					return nextMethod.call(this);
				}
				this.pos += 2;
				// error recovery: the block comment is not complete
				if (this.options.locations) {
					lineBreakG.lastIndex = start;
					var match;
					while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) {
						++this.curLine;
						this.lineStart = match.index + match[0].length;
					}
				}
				if (this.options.onComment) {
					var current = this.input.length;
					this.pos = current;
					this.options.onComment(true, this.input.slice(start + 2, current), start, current, startLoc, this.curPosition());
				}
				this.pos = start;
				// call the acorn function to report unterminated block comment
				return nextMethod.call(this);
			};
		});
	};

	/**
	 * @description setup all the given options to set up the acorn parsing
	 * @param {String} text The given source code
	 * @param {Object} options The given options
	 * @param {Object} acorn The acorn object
	 * @param {Object} acornloose The acorn loose object
	 * @param {Object} file the given file
	 */
	OrionAcorn.prototype.preParse = function preParse(text, options, acorn, acornloose, file) {
		this.initialize();
		if (!acorn.plugins) {
			acorn.plugins = Object.create(null); 
		}
		acorn.plugins.acornPlugin = this.acornPlugin.bind(this);
		// enabled plugins
		options.plugins = {
			"acornPlugin" : true
		};

		if (!acornloose.pluginsLoose) {
			acornloose.pluginsLoose = Object.create(null);
		}
		acornloose.pluginsLoose.acornPlugin = this.acornPlugin.bind(this);

		// enabled plugins
		options.pluginsLoose = {
			"acornPlugin" : true
		};
		options.onToken = this.onToken.bind(this);
		options.onComment = this.onComment.bind(this);
		options.locations = true;
		options.ranges = true;
		options.sourceFile = false;
		options.allowImportExportEverywhere = false;
		if (!options.sourceType) {
			// set a default value
			options.sourceType = "script"; //$NON-NLS-1$
		}
		options.allowHashBang = true;
		if(typeof options.ecmaVersion !== 'number' || (options.ecmaVersion < 3 || options.ecmaVersion > 9)) {
			options.ecmaVersion = 7; //don't stomp on the value set in Tern
		}
		if (!options.directSourceFile && file) {
			options.directSourceFile = {
				name: file,
				text: text
			};
		}
		this.sourceFile = options.directSourceFile;
		this.currentOptions = {
			locations : options.locations,
			sourceFile : options.sourceFile,
			ranges : options.ranges
		};
	};

	/**
	 * @description set all the values in the postParse phase
	 * @param {Object} the given ast tree
	 * @param {String} text The given source code
	 * @callback
	 */
	OrionAcorn.prototype.postParse = function postParse(ast, text) {
		if (Array.isArray(this.errors) && this.errors.length !== 0) {
			if (ast.errors) {
				ast.errors.concat(this.errors);
			} else {
				ast.errors = this.errors;
			}
		}
		ast.comments = this.comments;
		ast.tokens = this.tokens;
		if (!ast.dependencies) {
			ast.dependencies = [];
		}
		Object.keys(this.dependencies).forEach(function(dep) {
			var _d = this.dependencies[dep];
			if(this.environments.amd && _d.env === 'node' || this.environments.node && _d.env === 'amd') {
				_d.env = 'commonjs'; //$NON-NLS-1$
			}
			ast.dependencies.push(_d);
		}.bind(this));
		ast.environments = this.environments;
		ast.errors = Util.serializeAstErrors(ast);
	};

	return OrionAcorn;
});

/*******************************************************************************
* @license
* Copyright (c) 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials are made 
* available under the terms of the Eclipse Public License v1.0 
* (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
* License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/
/*eslint-env amd */
define('javascript/ternPlugins/ast',[
	"tern/lib/tern",
	"javascript/orionAcorn"
], function(tern, OrionAcorn) {
	
	var orionAcorn = new OrionAcorn();

	tern.registerPlugin("ast", /* @callback */ function(server, options) { //$NON-NLS-1$
		server.on("reset", function() { //$NON-NLS-1$
			orionAcorn.initialize();
		});

		return {
			passes: {
				/**
				 * We use the custom parseOptions event rather than preParse so that we aren't skipped due to other
				 * plugins contributing to that event (such as the html plugin).
				 * @callback
				 */
				parseOptions: orionAcorn.preParse.bind(orionAcorn),
				/**
				 * @callback
				 */
				postParse: orionAcorn.postParse.bind(orionAcorn)
			}
		};
	});
});

define('eslint/lib/utils/traverser',[
	'estraverse/estraverse'
],
function(estraverse) {
	/**
	 * @fileoverview Wrapper around estraverse
	 * @author Nicholas C. Zakas
	 */
	"use strict";
	//------------------------------------------------------------------------------
	// Helpers
	//------------------------------------------------------------------------------

	var KEY_BLACKLIST = [
		"parent",
		"leadingComments",
		"trailingComments"
	];

	/**
	 * Wrapper around an estraverse controller that ensures the correct keys
	 * are visited.
	 * @constructor
	 */
	function Traverser() {

		var controller = Object.create(new estraverse.Controller()),
			originalTraverse = controller.traverse;

		// intercept call to traverse() and add the fallback key to the visitor
		controller.traverse = function(node, visitor) {
			visitor.fallback = Traverser.getKeys;
			return originalTraverse.call(this, node, visitor);
		};

		return controller;
	}

	/**
	 * Calculates the keys to use for traversal.
	 * @param {ASTNode} node The node to read keys from.
	 * @returns {string[]} An array of keys to visit on the node.
	 * @private
	 */
	Traverser.getKeys = function(node) {
		return Object.keys(node).filter(function(key) {
			return KEY_BLACKLIST.indexOf(key) === -1;
		});
	};

	return Traverser;
});
/*******************************************************************************
 * @license
 * Copyright (c) 2013 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 * 
 * Contributors: IBM Corporation - initial API and implementation
 ******************************************************************************/
/*eslint-env browser, amd*/
define('orion/objects',[], function() {
	function mixin(target/*, source..*/) {
		var hasOwnProperty = Object.prototype.hasOwnProperty;
		for (var j = 1, len = arguments.length; j < len; j++) {
			var source = arguments[j];
			for (var key in source) {
				if (hasOwnProperty.call(source, key)) {
					target[key] = source[key];
				}
			}
		}
		return target;
	}

	/**
	 * @name orion.objects
	 * @class Object-oriented helpers.
	 */
	return {
		/**
		 * Creates a shallow clone of the given <code>object</code>.
		 * @name orion.objects.clone
		 * @function
		 * @static
		 * @param {Object|Array} object The object to clone. Must be a "normal" Object or Array. Other built-ins,
		 * host objects, primitives, etc, will not work.
		 * @returns {Object|Array} A clone of <code>object</code>.
		 */
		clone: function(object) {
			if (Array.isArray(object)) {
				return Array.prototype.slice.call(object);
			}
			var clone = Object.create(Object.getPrototypeOf(object));
			mixin(clone, object);
			return clone;
		},
		/**
		 * Mixes all <code>source</code>'s own enumerable properties into <code>target</code>. Multiple source objects
		 * can be passed as varargs.
		 * @name orion.objects.mixin
		 * @function
		 * @static
		 * @param {Object} target
		 * @param {Object} source
		 */
		mixin: mixin,
		/**
		 * Wraps an object into an Array if necessary.
		 * @name orion.objects.toArray
		 * @function
		 * @static
		 * @param {Object} obj An object.
		 * @returns {Array} Returns <code>obj</code> unchanged, if <code>obj</code> is an Array. Otherwise returns a 1-element Array
		 * whose sole element is <code>obj</code>.
		 */
		toArray: function(o) {
			return Array.isArray(o) ? o : [o];
		}
	};
});
/*
  Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* eslint-env amd */
/* eslint-disable missing-nls*/
define('esrecurse/esrecurse',[
	'estraverse/estraverse',
	'orion/objects'
], function(estraverse, objects) {
'use strict';

    var objectKeys;
        
    objectKeys = Object.keys || function (o) {
        var keys = [], key;
        for (key in o) {
            keys.push(key);
        }
        return keys;
    };

    function isNode(node) {
        if (node === null) {
            return false;
        }
        return typeof node === 'object' && typeof node.type === 'string';
    }

    function isProperty(nodeType, key) {
        return (nodeType === estraverse.Syntax.ObjectExpression || nodeType === estraverse.Syntax.ObjectPattern) && key === 'properties';
    }

    function Visitor(visitor, options) {
        options = options || {};

        this.__visitor = visitor ||  this;
        this.__childVisitorKeys = options.childVisitorKeys
            ? objects.mixin({}, estraverse.VisitorKeys, options.childVisitorKeys)
            : estraverse.VisitorKeys;
        if (options.fallback === 'iteration') {
            this.__fallback = objectKeys;
        } else if (typeof options.fallback === 'function') {
            this.__fallback = options.fallback;
        }
    }

    /* Default method for visiting children.
     * When you need to call default visiting operation inside custom visiting
     * operation, you can use it with `this.visitChildren(node)`.
     */
    Visitor.prototype.visitChildren = function (node) {
        var type, children, i, iz, j, jz, child;

        if (node === null) {
            return;
        }

        type = node.type || estraverse.Syntax.Property;

        children = this.__childVisitorKeys[type];
        if (!children) {
            if (this.__fallback) {
                children = this.__fallback(node);
            } else {
                throw new Error('Unknown node type ' + type + '.');
            }
        }

        for (i = 0, iz = children.length; i < iz; ++i) {
            child = node[children[i]];
            if (child) {
                if (Array.isArray(child)) {
                    for (j = 0, jz = child.length; j < jz; ++j) {
                        if (child[j]) {
                            if (isNode(child[j]) || isProperty(type, children[i])) {
                                this.visit(child[j]);
                            }
                        }
                    }
                } else if (isNode(child)) {
                    this.visit(child);
                }
            }
        }
    };

    /* Dispatching node. */
    Visitor.prototype.visit = function (node) {
        var type;

        if (node === null) {
            return;
        }

        type = node.type || estraverse.Syntax.Property;
        if (this.__visitor[type]) {
            this.__visitor[type].call(this, node);
            return;
        }
        this.visitChildren(node);
    };

	return {
	    version: '4.1.0',
	    Visitor: Visitor,
	    visit: function (node, visitor, options) {
        	var v = new Visitor(visitor, options);
        	v.visit(node);
    	}
    };
});
/*eslint-disable */
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('estraverse/estraverse'), require('esrecurse/esrecurse')) :
  typeof define === 'function' && define.amd ? define('escope/escope',['exports', 'estraverse/estraverse', 'esrecurse/esrecurse'], factory) :
  (factory((global.escope = global.escope || {}),global.estraverse,global.esrecurse));
}(this, (function (exports,estraverse_estraverse,esrecurse) { 'use strict';

esrecurse = 'default' in esrecurse ? esrecurse['default'] : esrecurse;

/*
  Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

var READ = 0x1;
var WRITE = 0x2;
var RW = READ | WRITE;

/**
 * A Reference represents a single occurrence of an identifier in code.
 * @class Reference
 */
var Reference = function Reference(ident, scope, flag,writeExpr, maybeImplicitGlobal, partial, init) {
      /**
       * Identifier syntax node.
       * @member {esprima#Identifier} Reference#identifier
       */
      this.identifier = ident;
      /**
       * Reference to the enclosing Scope.
       * @member {Scope} Reference#from
       */
      this.from = scope;
      /**
       * Whether the reference comes from a dynamic scope (such as 'eval',
       * 'with', etc.), and may be trapped by dynamic scopes.
       * @member {boolean} Reference#tainted
       */
      this.tainted = false;
      /**
       * The variable this reference is resolved with.
       * @member {Variable} Reference#resolved
       */
      this.resolved = null;
      /**
       * The read-write mode of the reference. (Value is one of {@link
       * Reference.READ}, {@link Reference.RW}, {@link Reference.WRITE}).
       * @member {number} Reference#flag
       * @private
       */
      this.flag = flag;
      if (this.isWrite()) {
          /**
           * If reference is writeable, this is the tree being written to it.
           * @member {esprima#Node} Reference#writeExpr
           */
          this.writeExpr = writeExpr;
          /**
           * Whether the Reference might refer to a partial value of writeExpr.
           * @member {boolean} Reference#partial
           */
          this.partial = partial;
          /**
           * Whether the Reference is to write of initialization.
           * @member {boolean} Reference#init
           */
          this.init = init;
      }
      this.__maybeImplicitGlobal = maybeImplicitGlobal;
  };

  /**
   * Whether the reference is static.
   * @method Reference#isStatic
   * @return {boolean}
   */
  Reference.prototype.isStatic = function isStatic () {
      return !this.tainted && this.resolved && this.resolved.scope.isStatic();
  };

  /**
   * Whether the reference is writeable.
   * @method Reference#isWrite
   * @return {boolean}
   */
  Reference.prototype.isWrite = function isWrite () {
      return !!(this.flag & Reference.WRITE);
  };

  /**
   * Whether the reference is readable.
   * @method Reference#isRead
   * @return {boolean}
   */
  Reference.prototype.isRead = function isRead () {
      return !!(this.flag & Reference.READ);
  };

  /**
   * Whether the reference is read-only.
   * @method Reference#isReadOnly
   * @return {boolean}
   */
  Reference.prototype.isReadOnly = function isReadOnly () {
      return this.flag === Reference.READ;
  };

  /**
   * Whether the reference is write-only.
   * @method Reference#isWriteOnly
   * @return {boolean}
   */
  Reference.prototype.isWriteOnly = function isWriteOnly () {
      return this.flag === Reference.WRITE;
  };

  /**
   * Whether the reference is read-write.
   * @method Reference#isReadWrite
   * @return {boolean}
   */
  Reference.prototype.isReadWrite = function isReadWrite () {
      return this.flag === Reference.RW;
  };

/**
 * @constant Reference.READ
 * @private
 */
Reference.READ = READ;
/**
 * @constant Reference.WRITE
 * @private
 */
Reference.WRITE = WRITE;
/**
 * @constant Reference.RW
 * @private
 */
Reference.RW = RW;

/* vim: set sw=4 ts=4 et tw=80 : */

/*
  Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**
 * A Variable represents a locally scoped identifier. These include arguments to
 * functions.
 * @class Variable
 */
var Variable = function Variable(name, scope) {
      /**
       * The variable name, as given in the source code.
       * @member {String} Variable#name
       */
      this.name = name;
      /**
       * List of defining occurrences of this variable (like in 'var ...'
       * statements or as parameter), as AST nodes.
       * @member {esprima.Identifier[]} Variable#identifiers
       */
      this.identifiers = [];
      /**
       * List of {@link Reference|references} of this variable (excluding parameter entries)
       * in its defining scope and all nested scopes. For defining
       * occurrences only see {@link Variable#defs}.
       * @member {Reference[]} Variable#references
       */
      this.references = [];

      /**
       * List of defining occurrences of this variable (like in 'var ...'
       * statements or as parameter), as custom objects.
       * @member {Definition[]} Variable#defs
       */
      this.defs = [];

      this.tainted = false;
      /**
       * Whether this is a stack variable.
       * @member {boolean} Variable#stack
       */
      this.stack = true;
      /**
       * Reference to the enclosing Scope.
       * @member {Scope} Variable#scope
       */
      this.scope = scope;
  };

Variable.CatchClause = 'CatchClause';
Variable.Parameter = 'Parameter';
Variable.FunctionName = 'FunctionName';
Variable.ClassName = 'ClassName';
Variable.Variable = 'Variable';
Variable.ImportBinding = 'ImportBinding';
Variable.TDZ = 'TDZ';
Variable.ImplicitGlobalVariable = 'ImplicitGlobalVariable';

/* vim: set sw=4 ts=4 et tw=80 : */

/*
  Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**
 * @class Definition
 */
var Definition = function Definition(type, name, node, parent, index, kind) {
      /**
       * @member {String} Definition#type - type of the occurrence (e.g. "Parameter", "Variable", ...).
       */
      this.type = type;
      /**
       * @member {esprima.Identifier} Definition#name - the identifier AST node of the occurrence.
       */
      this.name = name;
      /**
       * @member {esprima.Node} Definition#node - the enclosing node of the identifier.
       */
      this.node = node;
      /**
       * @member {esprima.Node?} Definition#parent - the enclosing statement node of the identifier.
       */
      this.parent = parent;
      /**
       * @member {Number?} Definition#index - the index in the declaration statement.
       */
      this.index = index;
      /**
       * @member {String?} Definition#kind - the kind of the declaration statement.
       */
      this.kind = kind;
  };

/**
 * @class ParameterDefinition
 */
var ParameterDefinition = (function (Definition) {
  function ParameterDefinition(name, node, index, rest) {
        Definition.call(this, Variable.Parameter, name, node, null, index, null);
        /**
         * Whether the parameter definition is a part of a rest parameter.
         * @member {boolean} ParameterDefinition#rest
         */
        this.rest = rest;
    }

  if ( Definition ) ParameterDefinition.__proto__ = Definition;
  ParameterDefinition.prototype = Object.create( Definition && Definition.prototype );
  ParameterDefinition.prototype.constructor = ParameterDefinition;

  return ParameterDefinition;
}(Definition));

/*
  Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

function isStrictScope(scope, block, isMethodDefinition, useDirective) {
    var body, i, iz, stmt, expr;

    // When upper scope is exists and strict, inner scope is also strict.
    if (scope.upper && scope.upper.isStrict) {
        return true;
    }

    // ArrowFunctionExpression's scope is always strict scope.
    if (block.type === estraverse_estraverse.Syntax.ArrowFunctionExpression) {
        return true;
    }

    if (isMethodDefinition) {
        return true;
    }

    if (scope.type === 'class' || scope.type === 'module') {
        return true;
    }

    if (scope.type === 'block' || scope.type === 'switch') {
        return false;
    }

    if (scope.type === 'function') {
        if (block.type === estraverse_estraverse.Syntax.Program) {
            body = block;
        } else {
            body = block.body;
        }
    } else if (scope.type === 'global') {
        body = block;
    } else {
        return false;
    }

    // Search 'use strict' directive.
    if (useDirective) {
        for (i = 0, iz = body.body.length; i < iz; ++i) {
            stmt = body.body[i];
            if (stmt.type !== estraverse_estraverse.Syntax.DirectiveStatement) {
                break;
            }
            if (stmt.raw === '"use strict"' || stmt.raw === '\'use strict\'') {
                return true;
            }
        }
    } else {
        for (i = 0, iz = body.body.length; i < iz; ++i) {
            stmt = body.body[i];
            if (stmt.type !== estraverse_estraverse.Syntax.ExpressionStatement) {
                break;
            }
            expr = stmt.expression;
            if (expr.type !== estraverse_estraverse.Syntax.Literal || typeof expr.value !== 'string') {
                break;
            }
            if (expr.raw !== null) {
                if (expr.raw === '"use strict"' || expr.raw === '\'use strict\'') {
                    return true;
                }
            } else {
                if (expr.value === 'use strict') {
                    return true;
                }
            }
        }
    }
    return false;
}

function registerScope(scopeManager, scope) {
    var scopes;

    scopeManager.scopes.push(scope);

    scopes = scopeManager.__nodeToScope.get(scope.block);
    if (scopes) {
        scopes.push(scope);
    } else {
        scopeManager.__nodeToScope.set(scope.block, [ scope ]);
    }
}

function shouldBeStatically(def) {
    return (
        (def.type === Variable.ClassName) ||
        (def.type === Variable.Variable && def.parent.kind !== 'var')
    );
}

/**
 * @class Scope
 */
var Scope = function Scope(scopeManager, type, upperScope, block, isMethodDefinition) {
      /**
       * One of 'TDZ', 'module', 'block', 'switch', 'function', 'catch', 'with', 'function', 'class', 'global'.
       * @member {String} Scope#type
       */
      this.type = type;
       /**
       * The scoped {@link Variable}s of this scope, as <code>{ Variable.name
       * : Variable }</code>.
       * @member {Map} Scope#set
       */
      this.set = new Map();
      /**
       * The tainted variables of this scope, as <code>{ Variable.name :
       * boolean }</code>.
       * @member {Map} Scope#taints */
      this.taints = new Map();
      /**
       * Generally, through the lexical scoping of JS you can always know
       * which variable an identifier in the source code refers to. There are
       * a few exceptions to this rule. With 'global' and 'with' scopes you
       * can only decide at runtime which variable a reference refers to.
       * Moreover, if 'eval()' is used in a scope, it might introduce new
       * bindings in this or its parent scopes.
       * All those scopes are considered 'dynamic'.
       * @member {boolean} Scope#dynamic
       */
      this.dynamic = this.type === 'global' || this.type === 'with';
      /**
       * A reference to the scope-defining syntax node.
       * @member {esprima.Node} Scope#block
       */
      this.block = block;
       /**
       * The {@link Reference|references} that are not resolved with this scope.
       * @member {Reference[]} Scope#through
       */
      this.through = [];
       /**
       * The scoped {@link Variable}s of this scope. In the case of a
       * 'function' scope this includes the automatic argument <em>arguments</em> as
       * its first element, as well as all further formal arguments.
       * @member {Variable[]} Scope#variables
       */
      this.variables = [];
       /**
       * Any variable {@link Reference|reference} found in this scope. This
       * includes occurrences of local variables as well as variables from
       * parent scopes (including the global scope). For local variables
       * this also includes defining occurrences (like in a 'var' statement).
       * In a 'function' scope this does not include the occurrences of the
       * formal parameter in the parameter list.
       * @member {Reference[]} Scope#references
       */
      this.references = [];

       /**
       * For 'global' and 'function' scopes, this is a self-reference. For
       * other scope types this is the <em>variableScope</em> value of the
       * parent scope.
       * @member {Scope} Scope#variableScope
       */
      this.variableScope =
          (this.type === 'global' || this.type === 'function' || this.type === 'module') ? this : upperScope.variableScope;
       /**
       * Whether this scope is created by a FunctionExpression.
       * @member {boolean} Scope#functionExpressionScope
       */
      this.functionExpressionScope = false;
       /**
       * Whether this is a scope that contains an 'eval()' invocation.
       * @member {boolean} Scope#directCallToEvalScope
       */
      this.directCallToEvalScope = false;
       /**
       * @member {boolean} Scope#thisFound
       */
      this.thisFound = false;

      this.__left = [];

       /**
       * Reference to the parent {@link Scope|scope}.
       * @member {Scope} Scope#upper
       */
      this.upper = upperScope;
       /**
       * Whether 'use strict' is in effect in this scope.
       * @member {boolean} Scope#isStrict
       */
      this.isStrict = isStrictScope(this, block, isMethodDefinition, scopeManager.__useDirective());

       /**
       * List of nested {@link Scope}s.
       * @member {Scope[]} Scope#childScopes
       */
      this.childScopes = [];
      if (this.upper) {
          this.upper.childScopes.push(this);
      }

      this.__declaredVariables = scopeManager.__declaredVariables;

      registerScope(scopeManager, this);
  };

  Scope.prototype.__shouldStaticallyClose = function __shouldStaticallyClose (scopeManager) {
      return (!this.dynamic || scopeManager.__isOptimistic());
  };

  Scope.prototype.__shouldStaticallyCloseForGlobal = function __shouldStaticallyCloseForGlobal (ref) {
      // On global scope, let/const/class declarations should be resolved statically.
      var name = ref.identifier.name;
      if (!this.set.has(name)) {
          return false;
      }

      var variable = this.set.get(name);
      var defs = variable.defs;
      return defs.length > 0 && defs.every(shouldBeStatically);
  };

  Scope.prototype.__staticCloseRef = function __staticCloseRef (ref) {
      if (!this.__resolve(ref)) {
          this.__delegateToUpperScope(ref);
      }
  };

  Scope.prototype.__dynamicCloseRef = function __dynamicCloseRef (ref) {
      // notify all names are through to global
      var current = this;
      do {
          current.through.push(ref);
          current = current.upper;
      } while (current);
  };

  Scope.prototype.__globalCloseRef = function __globalCloseRef (ref) {
      // let/const/class declarations should be resolved statically.
      // others should be resolved dynamically.
      if (this.__shouldStaticallyCloseForGlobal(ref)) {
          this.__staticCloseRef(ref);
      } else {
          this.__dynamicCloseRef(ref);
      }
  };

  Scope.prototype.__close = function __close (scopeManager) {
        var this$1 = this;

      var closeRef;
      if (this.__shouldStaticallyClose(scopeManager)) {
          closeRef = this.__staticCloseRef;
      } else if (this.type !== 'global') {
          closeRef = this.__dynamicCloseRef;
      } else {
          closeRef = this.__globalCloseRef;
      }

      // Try Resolving all references in this scope.
      for (var i = 0, iz = this.__left.length; i < iz; ++i) {
          var ref = this$1.__left[i];
          closeRef.call(this$1, ref);
      }
      this.__left = null;

      return this.upper;
  };

  Scope.prototype.__resolve = function __resolve (ref) {
      var variable, name;
      name = ref.identifier.name;
      if (this.set.has(name)) {
          variable = this.set.get(name);
          variable.references.push(ref);
          variable.stack = variable.stack && ref.from.variableScope === this.variableScope;
          if (ref.tainted) {
              variable.tainted = true;
              this.taints.set(variable.name, true);
          }
          ref.resolved = variable;
          return true;
      }
      return false;
  };

  Scope.prototype.__delegateToUpperScope = function __delegateToUpperScope (ref) {
      if (this.upper) {
          this.upper.__left.push(ref);
      }
      this.through.push(ref);
  };

  Scope.prototype.__addDeclaredVariablesOfNode = function __addDeclaredVariablesOfNode (variable, node) {
      if (!node) {
          return;
      }

      var variables = this.__declaredVariables.get(node);
      if (!Array.isArray(variables)) {
          variables = [];
          this.__declaredVariables.set(node, variables);
      }
      if (variables.indexOf(variable) === -1) {
          variables.push(variable);
      }
  };

  Scope.prototype.__defineGeneric = function __defineGeneric (name, set, variables, node, def) {
      var variable;

      variable = set.get(name);
      if (!variable) {
          variable = new Variable(name, this);
          set.set(name, variable);
          variables.push(variable);
      }

      if (def) {
          variable.defs.push(def);
          if (def.type !== Variable.TDZ) {
              this.__addDeclaredVariablesOfNode(variable, def.node);
              this.__addDeclaredVariablesOfNode(variable, def.parent);
          }
      }
      if (node) {
          variable.identifiers.push(node);
      }
  };

  Scope.prototype.__define = function __define (node, def) {
      if (node && node.type === estraverse_estraverse.Syntax.Identifier) {
          this.__defineGeneric(
                  node.name,
                  this.set,
                  this.variables,
                  node,
                  def);
      }
  };

  Scope.prototype.__referencing = function __referencing (node, assign, writeExpr, maybeImplicitGlobal, partial, init) {
      // because Array element may be null
      if (!node || node.type !== estraverse_estraverse.Syntax.Identifier) {
          return;
      }

      // Specially handle like `this`.
      if (node.name === 'super') {
          return;
      }

      var ref = new Reference(node, this, assign || Reference.READ, writeExpr, maybeImplicitGlobal, !!partial, !!init);
      this.references.push(ref);
      this.__left.push(ref);
  };

  Scope.prototype.__detectEval = function __detectEval () {
      var current;
      current = this;
      this.directCallToEvalScope = true;
      do {
          current.dynamic = true;
          current = current.upper;
      } while (current);
  };

  Scope.prototype.__detectThis = function __detectThis () {
      this.thisFound = true;
  };

  Scope.prototype.__isClosed = function __isClosed () {
      return this.__left === null;
  };

  /**
   * returns resolved {Reference}
   * @method Scope#resolve
   * @param {Esprima.Identifier} ident - identifier to be resolved.
   * @return {Reference}
   */
  Scope.prototype.resolve = function resolve (ident) {
        var this$1 = this;

      var ref, i, iz;
      if(!this.__isClosed()) {
        	throw new Error("Scope should be closed.");
      }
      if(ident.type !== estraverse_estraverse.Syntax.Identifier) {
        	throw new Error('Target should be identifier.');
      }
      for (i = 0, iz = this.references.length; i < iz; ++i) {
          ref = this$1.references[i];
          if (ref.identifier === ident) {
              return ref;
          }
      }
      return null;
  };

  /**
   * returns this scope is static
   * @method Scope#isStatic
   * @return {boolean}
   */
  Scope.prototype.isStatic = function isStatic () {
      return !this.dynamic;
  };

  /**
   * returns this scope has materialized arguments
   * @method Scope#isArgumentsMaterialized
   * @return {boolean}
   */
  Scope.prototype.isArgumentsMaterialized = function isArgumentsMaterialized () {
      return true;
  };

  /**
   * returns this scope has materialized `this` reference
   * @method Scope#isThisMaterialized
   * @return {boolean}
   */
  Scope.prototype.isThisMaterialized = function isThisMaterialized () {
      return true;
  };

  Scope.prototype.isUsedName = function isUsedName (name) {
        var this$1 = this;

      if (this.set.has(name)) {
          return true;
      }
      for (var i = 0, iz = this.through.length; i < iz; ++i) {
          if (this$1.through[i].identifier.name === name) {
              return true;
          }
      }
      return false;
  };

var GlobalScope = (function (Scope) {
  function GlobalScope(scopeManager, block) {
        Scope.call(this, scopeManager, 'global', null, block, false);
        this.implicit = {
            set: new Map(),
            variables: [],
            /**
            * List of {@link Reference}s that are left to be resolved (i.e. which
            * need to be linked to the variable they refer to).
            * @member {Reference[]} Scope#implicit#left
            */
            left: []
        };
    }

  if ( Scope ) GlobalScope.__proto__ = Scope;
  GlobalScope.prototype = Object.create( Scope && Scope.prototype );
  GlobalScope.prototype.constructor = GlobalScope;

    GlobalScope.prototype.__close = function __close (scopeManager) {
        var this$1 = this;

        var implicit = [];
        for (var i = 0, iz = this.__left.length; i < iz; ++i) {
            var ref = this$1.__left[i];
            if (ref.__maybeImplicitGlobal && !this$1.set.has(ref.identifier.name)) {
                implicit.push(ref.__maybeImplicitGlobal);
            }
        }

        // create an implicit global variable from assignment expression
        for (var i$1 = 0, iz$1 = implicit.length; i$1 < iz$1; ++i$1) {
            var info = implicit[i$1];
            this$1.__defineImplicit(info.pattern,
                    new Definition(
                        Variable.ImplicitGlobalVariable,
                        info.pattern,
                        info.node,
                        null,
                        null,
                        null
                    ));

        }

        this.implicit.left = this.__left;

        return Scope.prototype.__close.call(this, scopeManager);
    };

    GlobalScope.prototype.__defineImplicit = function __defineImplicit (node, def) {
        if (node && node.type === estraverse_estraverse.Syntax.Identifier) {
            this.__defineGeneric(
                    node.name,
                    this.implicit.set,
                    this.implicit.variables,
                    node,
                    def);
        }
    };

  return GlobalScope;
}(Scope));

var ModuleScope = (function (Scope) {
  function ModuleScope(scopeManager, upperScope, block) {
        Scope.call(this, scopeManager, 'module', upperScope, block, false);
    }

  if ( Scope ) ModuleScope.__proto__ = Scope;
  ModuleScope.prototype = Object.create( Scope && Scope.prototype );
  ModuleScope.prototype.constructor = ModuleScope;

  return ModuleScope;
}(Scope));

var FunctionExpressionNameScope = (function (Scope) {
  function FunctionExpressionNameScope(scopeManager, upperScope, block) {
        Scope.call(this, scopeManager, 'function-expression-name', upperScope, block, false);
        this.__define(block.id,
                new Definition(
                    Variable.FunctionName,
                    block.id,
                    block,
                    null,
                    null,
                    null
                ));
        this.functionExpressionScope = true;
    }

  if ( Scope ) FunctionExpressionNameScope.__proto__ = Scope;
  FunctionExpressionNameScope.prototype = Object.create( Scope && Scope.prototype );
  FunctionExpressionNameScope.prototype.constructor = FunctionExpressionNameScope;

  return FunctionExpressionNameScope;
}(Scope));

var CatchScope = (function (Scope) {
  function CatchScope(scopeManager, upperScope, block) {
        Scope.call(this, scopeManager, 'catch', upperScope, block, false);
    }

  if ( Scope ) CatchScope.__proto__ = Scope;
  CatchScope.prototype = Object.create( Scope && Scope.prototype );
  CatchScope.prototype.constructor = CatchScope;

  return CatchScope;
}(Scope));

var WithScope = (function (Scope) {
  function WithScope(scopeManager, upperScope, block) {
        Scope.call(this, scopeManager, 'with', upperScope, block, false);
    }

  if ( Scope ) WithScope.__proto__ = Scope;
  WithScope.prototype = Object.create( Scope && Scope.prototype );
  WithScope.prototype.constructor = WithScope;

    WithScope.prototype.__close = function __close (scopeManager) {
        var this$1 = this;

        if (this.__shouldStaticallyClose(scopeManager)) {
            return Scope.prototype.__close.call(this, scopeManager);
        }

        for (var i = 0, iz = this.__left.length; i < iz; ++i) {
            var ref = this$1.__left[i];
            ref.tainted = true;
            this$1.__delegateToUpperScope(ref);
        }
        this.__left = null;

        return this.upper;
    };

  return WithScope;
}(Scope));

var TDZScope = (function (Scope) {
  function TDZScope(scopeManager, upperScope, block) {
        Scope.call(this, scopeManager, 'TDZ', upperScope, block, false);
    }

  if ( Scope ) TDZScope.__proto__ = Scope;
  TDZScope.prototype = Object.create( Scope && Scope.prototype );
  TDZScope.prototype.constructor = TDZScope;

  return TDZScope;
}(Scope));

var BlockScope = (function (Scope) {
  function BlockScope(scopeManager, upperScope, block) {
        Scope.call(this, scopeManager, 'block', upperScope, block, false);
    }

  if ( Scope ) BlockScope.__proto__ = Scope;
  BlockScope.prototype = Object.create( Scope && Scope.prototype );
  BlockScope.prototype.constructor = BlockScope;

  return BlockScope;
}(Scope));

var SwitchScope = (function (Scope) {
  function SwitchScope(scopeManager, upperScope, block) {
        Scope.call(this, scopeManager, 'switch', upperScope, block, false);
    }

  if ( Scope ) SwitchScope.__proto__ = Scope;
  SwitchScope.prototype = Object.create( Scope && Scope.prototype );
  SwitchScope.prototype.constructor = SwitchScope;

  return SwitchScope;
}(Scope));

var FunctionScope = (function (Scope) {
  function FunctionScope(scopeManager, upperScope, block, isMethodDefinition) {
        Scope.call(this, scopeManager, 'function', upperScope, block, isMethodDefinition);

        // section 9.2.13, FunctionDeclarationInstantiation.
        // NOTE Arrow functions never have an arguments objects.
        if (this.block.type !== estraverse_estraverse.Syntax.ArrowFunctionExpression) {
            this.__defineArguments();
        }
    }

  if ( Scope ) FunctionScope.__proto__ = Scope;
  FunctionScope.prototype = Object.create( Scope && Scope.prototype );
  FunctionScope.prototype.constructor = FunctionScope;

    FunctionScope.prototype.isArgumentsMaterialized = function isArgumentsMaterialized () {
        // TODO(Constellation)
        // We can more aggressive on this condition like this.
        //
        // function t() {
        //     // arguments of t is always hidden.
        //     function arguments() {
        //     }
        // }
        if (this.block.type === estraverse_estraverse.Syntax.ArrowFunctionExpression) {
            return false;
        }

        if (!this.isStatic()) {
            return true;
        }

        var variable = this.set.get('arguments');
        if(!variable) {
        	throw new Error('Always have arguments variable.');
        }
        return variable.tainted || variable.references.length  !== 0;
    };

    FunctionScope.prototype.isThisMaterialized = function isThisMaterialized () {
        if (!this.isStatic()) {
            return true;
        }
        return this.thisFound;
    };

    FunctionScope.prototype.__defineArguments = function __defineArguments () {
        this.__defineGeneric(
                'arguments',
                this.set,
                this.variables,
                null,
                null);
        this.taints.set('arguments', true);
    };

  return FunctionScope;
}(Scope));

var ForScope = (function (Scope) {
  function ForScope(scopeManager, upperScope, block) {
        Scope.call(this, scopeManager, 'for', upperScope, block, false);
    }

  if ( Scope ) ForScope.__proto__ = Scope;
  ForScope.prototype = Object.create( Scope && Scope.prototype );
  ForScope.prototype.constructor = ForScope;

  return ForScope;
}(Scope));

var ClassScope = (function (Scope) {
  function ClassScope(scopeManager, upperScope, block) {
        Scope.call(this, scopeManager, 'class', upperScope, block, false);
    }

  if ( Scope ) ClassScope.__proto__ = Scope;
  ClassScope.prototype = Object.create( Scope && Scope.prototype );
  ClassScope.prototype.constructor = ClassScope;

  return ClassScope;
}(Scope));

/* vim: set sw=4 ts=4 et tw=80 : */

/*
  Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**
 * @class ScopeManager
 */
var ScopeManager = function ScopeManager(options) {
      this.scopes = [];
      this.globalScope = null;
      this.__nodeToScope = new WeakMap();
      this.__currentScope = null;
      this.__options = options;
      this.__declaredVariables = new WeakMap();
  };

  ScopeManager.prototype.__useDirective = function __useDirective () {
      return this.__options.directive;
  };

  ScopeManager.prototype.__isOptimistic = function __isOptimistic () {
      return this.__options.optimistic;
  };

  ScopeManager.prototype.__ignoreEval = function __ignoreEval () {
      return this.__options.ignoreEval;
  };

  ScopeManager.prototype.__isNodejsScope = function __isNodejsScope () {
      return this.__options.nodejsScope;
  };

  ScopeManager.prototype.isModule = function isModule () {
      return this.__options.sourceType === 'module';
  };

  ScopeManager.prototype.isImpliedStrict = function isImpliedStrict () {
      return this.__options.impliedStrict;
  };

  ScopeManager.prototype.isStrictModeSupported = function isStrictModeSupported () {
      return this.__options.ecmaVersion >= 5;
  };

  // Returns appropriate scope for this node.
  ScopeManager.prototype.__get = function __get (node) {
      return this.__nodeToScope.get(node);
  };

  /**
   * Get variables that are declared by the node.
   *
   * "are declared by the node" means the node is same as `Variable.defs[].node` or `Variable.defs[].parent`.
   * If the node declares nothing, this method returns an empty array.
   * CAUTION: This API is experimental. See https://github.com/estools/escope/pull/69 for more details.
   *
   * @param {Esprima.Node} node - a node to get.
   * @returns {Variable[]} variables that declared by the node.
   */
  ScopeManager.prototype.getDeclaredVariables = function getDeclaredVariables (node) {
      return this.__declaredVariables.get(node) || [];
  };

  /**
   * acquire scope from node.
   * @method ScopeManager#acquire
   * @param {Esprima.Node} node - node for the acquired scope.
   * @param {boolean=} inner - look up the most inner scope, default value is false.
   * @return {Scope?}
   */
  ScopeManager.prototype.acquire = function acquire (node, inner) {
      var scopes, scope, i, iz;

      function predicate(scope) {
          if (scope.type === 'function' && scope.functionExpressionScope) {
              return false;
          }
          if (scope.type === 'TDZ') {
              return false;
          }
          return true;
      }

      scopes = this.__get(node);
      if (!scopes || scopes.length === 0) {
          return null;
      }

      // Heuristic selection from all scopes.
      // If you would like to get all scopes, please use ScopeManager#acquireAll.
      if (scopes.length === 1) {
          return scopes[0];
      }

      if (inner) {
          for (i = scopes.length - 1; i >= 0; --i) {
              scope = scopes[i];
              if (predicate(scope)) {
                  return scope;
              }
          }
      } else {
          for (i = 0, iz = scopes.length; i < iz; ++i) {
              scope = scopes[i];
              if (predicate(scope)) {
                  return scope;
              }
          }
      }

      return null;
  };

  /**
   * acquire all scopes from node.
   * @method ScopeManager#acquireAll
   * @param {Esprima.Node} node - node for the acquired scope.
   * @return {Scope[]?}
   */
  ScopeManager.prototype.acquireAll = function acquireAll (node) {
      return this.__get(node);
  };

  /**
   * release the node.
   * @method ScopeManager#release
   * @param {Esprima.Node} node - releasing node.
   * @param {boolean=} inner - look up the most inner scope, default value is false.
   * @return {Scope?} upper scope for the node.
   */
  ScopeManager.prototype.release = function release (node, inner) {
      var scopes, scope;
      scopes = this.__get(node);
      if (scopes && scopes.length) {
          scope = scopes[0].upper;
          if (!scope) {
              return null;
          }
          return this.acquire(scope.block, inner);
      }
      return null;
  };

  ScopeManager.prototype.attach = function attach () { };

  ScopeManager.prototype.detach = function detach () { };

  ScopeManager.prototype.__nestScope = function __nestScope (scope) {
      if (scope instanceof GlobalScope) {
        	if(this.__currentScope !== null) {
        		throw new Error("_currentScope is not null trying to nest scopes.");
        	}
          this.globalScope = scope;
      }
      this.__currentScope = scope;
      return scope;
  };

  ScopeManager.prototype.__nestGlobalScope = function __nestGlobalScope (node) {
      return this.__nestScope(new GlobalScope(this, node));
  };

  ScopeManager.prototype.__nestBlockScope = function __nestBlockScope (node, isMethodDefinition) {
      return this.__nestScope(new BlockScope(this, this.__currentScope, node));
  };

  ScopeManager.prototype.__nestFunctionScope = function __nestFunctionScope (node, isMethodDefinition) {
      return this.__nestScope(new FunctionScope(this, this.__currentScope, node, isMethodDefinition));
  };

  ScopeManager.prototype.__nestForScope = function __nestForScope (node) {
      return this.__nestScope(new ForScope(this, this.__currentScope, node));
  };

  ScopeManager.prototype.__nestCatchScope = function __nestCatchScope (node) {
      return this.__nestScope(new CatchScope(this, this.__currentScope, node));
  };

  ScopeManager.prototype.__nestWithScope = function __nestWithScope (node) {
      return this.__nestScope(new WithScope(this, this.__currentScope, node));
  };

  ScopeManager.prototype.__nestClassScope = function __nestClassScope (node) {
      return this.__nestScope(new ClassScope(this, this.__currentScope, node));
  };

  ScopeManager.prototype.__nestSwitchScope = function __nestSwitchScope (node) {
      return this.__nestScope(new SwitchScope(this, this.__currentScope, node));
  };

  ScopeManager.prototype.__nestModuleScope = function __nestModuleScope (node) {
      return this.__nestScope(new ModuleScope(this, this.__currentScope, node));
  };

  ScopeManager.prototype.__nestTDZScope = function __nestTDZScope (node) {
      return this.__nestScope(new TDZScope(this, this.__currentScope, node));
  };

  ScopeManager.prototype.__nestFunctionExpressionNameScope = function __nestFunctionExpressionNameScope (node) {
      return this.__nestScope(new FunctionExpressionNameScope(this, this.__currentScope, node));
  };

  ScopeManager.prototype.__isES6 = function __isES6 () {
      return this.__options.ecmaVersion >= 6;
  };

/*
  Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

function getLast(xs) {
    return xs[xs.length - 1] || null;
}

var PatternVisitor = (function (superclass) {
  function PatternVisitor(options, rootPattern, callback) {
        superclass.call(this, null, options);
        this.rootPattern = rootPattern;
        this.callback = callback;
        this.assignments = [];
        this.rightHandNodes = [];
        this.restElements = [];
    }

  if ( superclass ) PatternVisitor.__proto__ = superclass;
  PatternVisitor.prototype = Object.create( superclass && superclass.prototype );
  PatternVisitor.prototype.constructor = PatternVisitor;

    PatternVisitor.isPattern = function isPattern (node) {
        var nodeType = node.type;
        return (
            nodeType === estraverse_estraverse.Syntax.Identifier ||
            nodeType === estraverse_estraverse.Syntax.ObjectPattern ||
            nodeType === estraverse_estraverse.Syntax.ArrayPattern ||
            nodeType === estraverse_estraverse.Syntax.SpreadElement ||
            nodeType === estraverse_estraverse.Syntax.RestElement ||
            nodeType === estraverse_estraverse.Syntax.AssignmentPattern
        );
    };

  PatternVisitor.prototype.Identifier = function Identifier (pattern) {
        var lastRestElement = getLast(this.restElements);
        this.callback(pattern, {
            topLevel: pattern === this.rootPattern,
            rest: lastRestElement != null && lastRestElement.argument === pattern,
            assignments: this.assignments
        });
    };

    PatternVisitor.prototype.Property = function Property (property) {
        // Computed property's key is a right hand node.
        if (property.computed) {
            this.rightHandNodes.push(property.key);
        }

        // If it's shorthand, its key is same as its value.
        // If it's shorthand and has its default value, its key is same as its value.left (the value is AssignmentPattern).
        // If it's not shorthand, the name of new variable is its value's.
        this.visit(property.value);
    };

    PatternVisitor.prototype.ArrayPattern = function ArrayPattern (pattern) {
        var this$1 = this;

        var i, iz, element;
        for (i = 0, iz = pattern.elements.length; i < iz; ++i) {
            element = pattern.elements[i];
            this$1.visit(element);
        }
    };

    PatternVisitor.prototype.AssignmentPattern = function AssignmentPattern (pattern) {
        this.assignments.push(pattern);
        this.visit(pattern.left);
        this.rightHandNodes.push(pattern.right);
        this.assignments.pop();
    };

    PatternVisitor.prototype.RestElement = function RestElement (pattern) {
        this.restElements.push(pattern);
        this.visit(pattern.argument);
        this.restElements.pop();
    };

    PatternVisitor.prototype.MemberExpression = function MemberExpression (node) {
        // Computed property's key is a right hand node.
        if (node.computed) {
            this.rightHandNodes.push(node.property);
        }
        // the object is only read, write to its property.
        this.rightHandNodes.push(node.object);
    };

    //
    // ForInStatement.left and AssignmentExpression.left are LeftHandSideExpression.
    // By spec, LeftHandSideExpression is Pattern or MemberExpression.
    //   (see also: https://github.com/estree/estree/pull/20#issuecomment-74584758)
    // But espree 2.0 and esprima 2.0 parse to ArrayExpression, ObjectExpression, etc...
    //

    PatternVisitor.prototype.SpreadElement = function SpreadElement (node) {
        this.visit(node.argument);
    };

    PatternVisitor.prototype.ArrayExpression = function ArrayExpression (node) {
        node.elements.forEach(this.visit, this);
    };

    PatternVisitor.prototype.AssignmentExpression = function AssignmentExpression (node) {
        this.assignments.push(node);
        this.visit(node.left);
        this.rightHandNodes.push(node.right);
        this.assignments.pop();
    };

    PatternVisitor.prototype.CallExpression = function CallExpression (node) {
        var this$1 = this;

        // arguments are right hand nodes.
        node.arguments.forEach(function (a) { this$1.rightHandNodes.push(a); });
        this.visit(node.callee);
    };

  return PatternVisitor;
}(esrecurse.Visitor));

/*
  Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
function traverseIdentifierInPattern(options, rootPattern, referencer, callback) {
    // Call the callback at left hand identifier nodes, and Collect right hand nodes.
    var visitor = new PatternVisitor(options, rootPattern, callback);
    visitor.visit(rootPattern);

    // Process the right hand nodes recursively.
    if (referencer != null) {
        visitor.rightHandNodes.forEach(referencer.visit, referencer);
    }
}

// Importing ImportDeclaration.
// http://people.mozilla.org/~jorendorff/es6-draft.html#sec-moduledeclarationinstantiation
// https://github.com/estree/estree/blob/master/es6.md#importdeclaration
// FIXME: Now, we don't create module environment, because the context is
// implementation dependent.

var Importer = (function (superclass) {
  function Importer(declaration, referencer) {
        superclass.call(this, null, referencer.options);
        this.declaration = declaration;
        this.referencer = referencer;
    }

  if ( superclass ) Importer.__proto__ = superclass;
  Importer.prototype = Object.create( superclass && superclass.prototype );
  Importer.prototype.constructor = Importer;

    Importer.prototype.visitImport = function visitImport (id, specifier) {
        var this$1 = this;

        this.referencer.visitPattern(id, function (pattern) {
            this$1.referencer.currentScope().__define(pattern,
                new Definition(
                    Variable.ImportBinding,
                    pattern,
                    specifier,
                    this$1.declaration,
                    null,
                    null
                    ));
        });
    };

    Importer.prototype.ImportNamespaceSpecifier = function ImportNamespaceSpecifier (node) {
        var local = (node.local || node.id);
        if (local) {
            this.visitImport(local, node);
        }
    };

    Importer.prototype.ImportDefaultSpecifier = function ImportDefaultSpecifier (node) {
        var local = (node.local || node.id);
        this.visitImport(local, node);
    };

    Importer.prototype.ImportSpecifier = function ImportSpecifier (node) {
        var local = (node.local || node.id);
        if (node.name) {
            this.visitImport(node.name, node);
        } else {
            this.visitImport(local, node);
        }
    };

  return Importer;
}(esrecurse.Visitor));

// Referencing variables and creating bindings.
var Referencer = (function (superclass) {
  function Referencer(options, scopeManager) {
        superclass.call(this, null, options);
        this.options = options;
        this.scopeManager = scopeManager;
        this.parent = null;
        this.isInnerMethodDefinition = false;
    }

  if ( superclass ) Referencer.__proto__ = superclass;
  Referencer.prototype = Object.create( superclass && superclass.prototype );
  Referencer.prototype.constructor = Referencer;

    Referencer.prototype.currentScope = function currentScope () {
        return this.scopeManager.__currentScope;
    };

    Referencer.prototype.close = function close (node) {
        var this$1 = this;

        while (this.currentScope() && node === this.currentScope().block) {
            this$1.scopeManager.__currentScope = this$1.currentScope().__close(this$1.scopeManager);
        }
    };

    Referencer.prototype.pushInnerMethodDefinition = function pushInnerMethodDefinition (isInnerMethodDefinition) {
        var previous = this.isInnerMethodDefinition;
        this.isInnerMethodDefinition = isInnerMethodDefinition;
        return previous;
    };

    Referencer.prototype.popInnerMethodDefinition = function popInnerMethodDefinition (isInnerMethodDefinition) {
        this.isInnerMethodDefinition = isInnerMethodDefinition;
    };

    Referencer.prototype.materializeTDZScope = function materializeTDZScope (node, iterationNode) {
        // http://people.mozilla.org/~jorendorff/es6-draft.html#sec-runtime-semantics-forin-div-ofexpressionevaluation-abstract-operation
        // TDZ scope hides the declaration's names.
        this.scopeManager.__nestTDZScope(node, iterationNode);
        this.visitVariableDeclaration(this.currentScope(), Variable.TDZ, iterationNode.left, 0, true);
    };

    Referencer.prototype.materializeIterationScope = function materializeIterationScope (node) {
        var this$1 = this;

        // Generate iteration scope for upper ForIn/ForOf Statements.
        var letOrConstDecl;
        this.scopeManager.__nestForScope(node);
        letOrConstDecl = node.left;
        this.visitVariableDeclaration(this.currentScope(), Variable.Variable, letOrConstDecl, 0);
        this.visitPattern(letOrConstDecl.declarations[0].id, function (pattern) {
            this$1.currentScope().__referencing(pattern, Reference.WRITE, node.right, null, true, true);
        });
    };

    Referencer.prototype.referencingDefaultValue = function referencingDefaultValue (pattern, assignments, maybeImplicitGlobal, init) {
        var scope = this.currentScope();
        assignments.forEach(function (assignment) {
            scope.__referencing(
                pattern,
                Reference.WRITE,
                assignment.right,
                maybeImplicitGlobal,
                pattern !== assignment.left,
                init);
        });
    };

    Referencer.prototype.visitPattern = function visitPattern (node, options, callback) {
        if (typeof options === 'function') {
            callback = options;
            options = {processRightHandNodes: false}
        }
        traverseIdentifierInPattern(
            this.options,
            node,
            options.processRightHandNodes ? this : null,
            callback);
    };

    Referencer.prototype.visitFunction = function visitFunction (node) {
        var this$1 = this;

        var i, iz;
        // FunctionDeclaration name is defined in upper scope
        // NOTE: Not referring variableScope. It is intended.
        // Since
        //  in ES5, FunctionDeclaration should be in FunctionBody.
        //  in ES6, FunctionDeclaration should be block scoped.
        if (node.type === estraverse_estraverse.Syntax.FunctionDeclaration) {
            // id is defined in upper scope
            this.currentScope().__define(node.id,
                    new Definition(
                        Variable.FunctionName,
                        node.id,
                        node,
                        null,
                        null,
                        null
                    ));
        }

        // FunctionExpression with name creates its special scope;
        // FunctionExpressionNameScope.
        if (node.type === estraverse_estraverse.Syntax.FunctionExpression && node.id) {
            this.scopeManager.__nestFunctionExpressionNameScope(node);
        }

        // Consider this function is in the MethodDefinition.
        this.scopeManager.__nestFunctionScope(node, this.isInnerMethodDefinition);

        // Process parameter declarations.
        for (i = 0, iz = node.params.length; i < iz; ++i) {
            this$1.visitPattern(node.params[i], {processRightHandNodes: true}, function (pattern, info) {
                this$1.currentScope().__define(pattern,
                    new ParameterDefinition(
                        pattern,
                        node,
                        i,
                        info.rest
                    ));

                this$1.referencingDefaultValue(pattern, info.assignments, null, true);
            });
        }

        // if there's a rest argument, add that
        if (node.rest) {
            this.visitPattern({
                type: 'RestElement',
                argument: node.rest
            }, function (pattern) {
                this$1.currentScope().__define(pattern,
                    new ParameterDefinition(
                        pattern,
                        node,
                        node.params.length,
                        true
                    ));
            });
        }

        // Skip BlockStatement to prevent creating BlockStatement scope.
        if (node.body.type === estraverse_estraverse.Syntax.BlockStatement) {
            this.visitChildren(node.body);
        } else {
            this.visit(node.body);
        }

        this.close(node);
    };

    Referencer.prototype.visitClass = function visitClass (node) {
        if (node.type === estraverse_estraverse.Syntax.ClassDeclaration) {
            this.currentScope().__define(node.id,
                    new Definition(
                        Variable.ClassName,
                        node.id,
                        node,
                        null,
                        null,
                        null
                    ));
        }

        // FIXME: Maybe consider TDZ.
        this.visit(node.superClass);

        this.scopeManager.__nestClassScope(node);

        if (node.id) {
            this.currentScope().__define(node.id,
                    new Definition(
                        Variable.ClassName,
                        node.id,
                        node
                    ));
        }
        this.visit(node.body);

        this.close(node);
    };

    Referencer.prototype.visitProperty = function visitProperty (node) {
        var previous, isMethodDefinition;
        if (node.computed) {
            this.visit(node.key);
        }

        isMethodDefinition = node.type === estraverse_estraverse.Syntax.MethodDefinition;
        if (isMethodDefinition) {
            previous = this.pushInnerMethodDefinition(true);
        }
        this.visit(node.value);
        if (isMethodDefinition) {
            this.popInnerMethodDefinition(previous);
        }
    };

    Referencer.prototype.visitForIn = function visitForIn (node) {
        var this$1 = this;

        if (node.left.type === estraverse_estraverse.Syntax.VariableDeclaration && node.left.kind !== 'var') {
            this.materializeTDZScope(node.right, node);
            this.visit(node.right);
            this.close(node.right);

            this.materializeIterationScope(node);
            this.visit(node.body);
            this.close(node);
        } else {
            if (node.left.type === estraverse_estraverse.Syntax.VariableDeclaration) {
                this.visit(node.left);
                this.visitPattern(node.left.declarations[0].id, function (pattern) {
                    this$1.currentScope().__referencing(pattern, Reference.WRITE, node.right, null, true, true);
                });
            } else {
                this.visitPattern(node.left, {processRightHandNodes: true}, function (pattern, info) {
                    var maybeImplicitGlobal = null;
                    if (!this$1.currentScope().isStrict) {
                        maybeImplicitGlobal = {
                            pattern: pattern,
                            node: node
                        };
                    }
                    this$1.referencingDefaultValue(pattern, info.assignments, maybeImplicitGlobal, false);
                    this$1.currentScope().__referencing(pattern, Reference.WRITE, node.right, maybeImplicitGlobal, true, false);
                });
            }
            this.visit(node.right);
            this.visit(node.body);
        }
    };

    Referencer.prototype.visitVariableDeclaration = function visitVariableDeclaration (variableTargetScope, type, node, index, fromTDZ) {
        var this$1 = this;

        // If this was called to initialize a TDZ scope, this needs to make definitions, but doesn't make references.
        var decl, init;

        decl = node.declarations[index];
        init = decl.init;
        this.visitPattern(decl.id, {processRightHandNodes: !fromTDZ}, function (pattern, info) {
            variableTargetScope.__define(pattern,
                new Definition(
                    type,
                    pattern,
                    decl,
                    node,
                    index,
                    node.kind
                ));

            if (!fromTDZ) {
                this$1.referencingDefaultValue(pattern, info.assignments, null, true);
            }
            if (init) {
                this$1.currentScope().__referencing(pattern, Reference.WRITE, init, null, !info.topLevel, true);
            }
        });
    };

    Referencer.prototype.AssignmentExpression = function AssignmentExpression (node) {
        var this$1 = this;

        if (PatternVisitor.isPattern(node.left)) {
            if (node.operator === '=') {
                this.visitPattern(node.left, {processRightHandNodes: true}, function (pattern, info) {
                    var maybeImplicitGlobal = null;
                    if (!this$1.currentScope().isStrict) {
                        maybeImplicitGlobal = {
                            pattern: pattern,
                            node: node
                        };
                    }
                    this$1.referencingDefaultValue(pattern, info.assignments, maybeImplicitGlobal, false);
                    this$1.currentScope().__referencing(pattern, Reference.WRITE, node.right, maybeImplicitGlobal, !info.topLevel, false);
                });
            } else {
                this.currentScope().__referencing(node.left, Reference.RW, node.right);
            }
        } else {
            this.visit(node.left);
        }
        this.visit(node.right);
    };

    Referencer.prototype.CatchClause = function CatchClause (node) {
        var this$1 = this;

        this.scopeManager.__nestCatchScope(node);

        this.visitPattern(node.param, {processRightHandNodes: true}, function (pattern, info) {
            this$1.currentScope().__define(pattern,
                new Definition(
                    Variable.CatchClause,
                    node.param,
                    node,
                    null,
                    null,
                    null
                ));
            this$1.referencingDefaultValue(pattern, info.assignments, null, true);
        });
        this.visit(node.body);

        this.close(node);
    };

    Referencer.prototype.Program = function Program (node) {
        this.scopeManager.__nestGlobalScope(node);

        if (this.scopeManager.__isNodejsScope()) {
            // Force strictness of GlobalScope to false when using node.js scope.
            this.currentScope().isStrict = false;
            this.scopeManager.__nestFunctionScope(node, false);
        }

        if (this.scopeManager.__isES6() && this.scopeManager.isModule()) {
            this.scopeManager.__nestModuleScope(node);
        }

        if (this.scopeManager.isStrictModeSupported() && this.scopeManager.isImpliedStrict()) {
            this.currentScope().isStrict = true;
        }

        this.visitChildren(node);
        this.close(node);
    };

    Referencer.prototype.Identifier = function Identifier (node) {
        this.currentScope().__referencing(node);
    };

    Referencer.prototype.UpdateExpression = function UpdateExpression (node) {
        if (PatternVisitor.isPattern(node.argument)) {
            this.currentScope().__referencing(node.argument, Reference.RW, null);
        } else {
            this.visitChildren(node);
        }
    };

    Referencer.prototype.MemberExpression = function MemberExpression (node) {
        this.visit(node.object);
        if (node.computed) {
            this.visit(node.property);
        }
    };

    Referencer.prototype.Property = function Property (node) {
        this.visitProperty(node);
    };

    Referencer.prototype.MethodDefinition = function MethodDefinition (node) {
        this.visitProperty(node);
    };

    Referencer.prototype.BreakStatement = function BreakStatement () {};

    Referencer.prototype.ContinueStatement = function ContinueStatement () {};

    Referencer.prototype.LabeledStatement = function LabeledStatement (node) {
        this.visit(node.body);
    };

    Referencer.prototype.ForStatement = function ForStatement (node) {
        // Create ForStatement declaration.
        // NOTE: In ES6, ForStatement dynamically generates
        // per iteration environment. However, escope is
        // a static analyzer, we only generate one scope for ForStatement.
        if (node.init && node.init.type === estraverse_estraverse.Syntax.VariableDeclaration && node.init.kind !== 'var') {
            this.scopeManager.__nestForScope(node);
        }

        this.visitChildren(node);

        this.close(node);
    };

    Referencer.prototype.ClassExpression = function ClassExpression (node) {
        this.visitClass(node);
    };

    Referencer.prototype.ClassDeclaration = function ClassDeclaration (node) {
        this.visitClass(node);
    };

    Referencer.prototype.CallExpression = function CallExpression (node) {
        // Check this is direct call to eval
        if (!this.scopeManager.__ignoreEval() && node.callee.type === estraverse_estraverse.Syntax.Identifier && node.callee.name === 'eval') {
            // NOTE: This should be `variableScope`. Since direct eval call always creates Lexical environment and
            // let / const should be enclosed into it. Only VariableDeclaration affects on the caller's environment.
            this.currentScope().variableScope.__detectEval();
        }
        this.visitChildren(node);
    };

    Referencer.prototype.BlockStatement = function BlockStatement (node) {
        if (this.scopeManager.__isES6()) {
            this.scopeManager.__nestBlockScope(node);
        }

        this.visitChildren(node);

        this.close(node);
    };

    Referencer.prototype.ThisExpression = function ThisExpression () {
        this.currentScope().variableScope.__detectThis();
    };

    Referencer.prototype.WithStatement = function WithStatement (node) {
        this.visit(node.object);
        // Then nest scope for WithStatement.
        this.scopeManager.__nestWithScope(node);

        this.visit(node.body);

        this.close(node);
    };

    Referencer.prototype.VariableDeclaration = function VariableDeclaration (node) {
        var this$1 = this;

        var variableTargetScope, i, iz, decl;
        variableTargetScope = (node.kind === 'var') ? this.currentScope().variableScope : this.currentScope();
        for (i = 0, iz = node.declarations.length; i < iz; ++i) {
            decl = node.declarations[i];
            this$1.visitVariableDeclaration(variableTargetScope, Variable.Variable, node, i);
            if (decl.init) {
                this$1.visit(decl.init);
            }
        }
    };

    // sec 13.11.8
    Referencer.prototype.SwitchStatement = function SwitchStatement (node) {
        var this$1 = this;

        var i, iz;

        this.visit(node.discriminant);

        if (this.scopeManager.__isES6()) {
            this.scopeManager.__nestSwitchScope(node);
        }

        for (i = 0, iz = node.cases.length; i < iz; ++i) {
            this$1.visit(node.cases[i]);
        }

        this.close(node);
    };

    Referencer.prototype.FunctionDeclaration = function FunctionDeclaration (node) {
        this.visitFunction(node);
    };

    Referencer.prototype.FunctionExpression = function FunctionExpression (node) {
        this.visitFunction(node);
    };

    Referencer.prototype.ForOfStatement = function ForOfStatement (node) {
        this.visitForIn(node);
    };

    Referencer.prototype.ForInStatement = function ForInStatement (node) {
        this.visitForIn(node);
    };

    Referencer.prototype.ArrowFunctionExpression = function ArrowFunctionExpression (node) {
        this.visitFunction(node);
    };

    Referencer.prototype.ImportDeclaration = function ImportDeclaration (node) {
        var importer;
		if(!this.scopeManager.__isES6() || !this.scopeManager.isModule()) {
			// ORION throw new Error('ImportDeclaration should appear when the mode is ES6 and in the module context.');
		}
        importer = new Importer(node, this);
        importer.visit(node);
    };

    Referencer.prototype.visitExportDeclaration = function visitExportDeclaration (node) {
        if (node.source) {
            return;
        }
        if (node.declaration) {
            this.visit(node.declaration);
            return;
        }

        this.visitChildren(node);
    };

    Referencer.prototype.ExportDeclaration = function ExportDeclaration (node) {
        this.visitExportDeclaration(node);
    };

    Referencer.prototype.ExportNamedDeclaration = function ExportNamedDeclaration (node) {
        this.visitExportDeclaration(node);
    };

    Referencer.prototype.ExportSpecifier = function ExportSpecifier (node) {
        var local = (node.id || node.local);
        this.visit(local);
    };

    Referencer.prototype.MetaProperty = function MetaProperty () {
        // do nothing.
    };

  return Referencer;
}(esrecurse.Visitor));

/*
  Copyright (C) 2012-2014 Yusuke Suzuki <utatane.tea@gmail.com>
  Copyright (C) 2013 Alex Seville <hi@alexanderseville.com>
  Copyright (C) 2014 Thiago de Arruda <tpadilha84@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**
 * Escope (<a href="http://github.com/estools/escope">escope</a>) is an <a
 * href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript</a>
 * scope analyzer extracted from the <a
 * href="http://github.com/estools/esmangle">esmangle project</a/>.
 * <p>
 * <em>escope</em> finds lexical scopes in a source program, i.e. areas of that
 * program where different occurrences of the same identifier refer to the same
 * variable. With each scope the contained variables are collected, and each
 * identifier reference in code is linked to its corresponding variable (if
 * possible).
 * <p>
 * <em>escope</em> works on a syntax tree of the parsed source code which has
 * to adhere to the <a
 * href="https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API">
 * Mozilla Parser API</a>. E.g. <a href="http://esprima.org">esprima</a> is a parser
 * that produces such syntax trees.
 * <p>
 * The main interface is the {@link analyze} function.
 * @module escope
 */

/*jslint bitwise:true */

function defaultOptions() {
    return {
        optimistic: false,
        directive: false,
        nodejsScope: false,
        impliedStrict: false,
        sourceType: 'script',  // one of ['script', 'module']
        ecmaVersion: 5,
        childVisitorKeys: null,
        fallback: 'iteration'
    };
}

function updateDeeply(target, override) {
    var key, val;

    function isHashObject(target) {
        return typeof target === 'object' && target instanceof Object && !(target instanceof Array) && !(target instanceof RegExp);
    }

    for (key in override) {
        if (override.hasOwnProperty(key)) {
            val = override[key];
            if (isHashObject(val)) {
                if (isHashObject(target[key])) {
                    updateDeeply(target[key], val);
                } else {
                    target[key] = updateDeeply({}, val);
                }
            } else {
                target[key] = val;
            }
        }
    }
    return target;
}

/**
 * Main interface function. Takes an Esprima syntax tree and returns the
 * analyzed scopes.
 * @function analyze
 * @param {esprima.Tree} tree
 * @param {Object} providedOptions - Options that tailor the scope analysis
 * @param {boolean} [providedOptions.optimistic=false] - the optimistic flag
 * @param {boolean} [providedOptions.directive=false]- the directive flag
 * @param {boolean} [providedOptions.ignoreEval=false]- whether to check 'eval()' calls
 * @param {boolean} [providedOptions.nodejsScope=false]- whether the whole
 * script is executed under node.js environment. When enabled, escope adds
 * a function scope immediately following the global scope.
 * @param {boolean} [providedOptions.impliedStrict=false]- implied strict mode
 * (if ecmaVersion >= 5).
 * @param {string} [providedOptions.sourceType='script']- the source type of the script. one of 'script' and 'module'
 * @param {number} [providedOptions.ecmaVersion=5]- which ECMAScript version is considered
 * @param {Object} [providedOptions.childVisitorKeys=null] - Additional known visitor keys. See [esrecurse](https://github.com/estools/esrecurse)'s the `childVisitorKeys` option.
 * @param {string} [providedOptions.fallback='iteration'] - A kind of the fallback in order to encounter with unknown node. See [esrecurse](https://github.com/estools/esrecurse)'s the `fallback` option.
 * @return {ScopeManager}
 */
function analyze(tree, providedOptions) {
    var scopeManager, referencer, options;

    options = updateDeeply(defaultOptions(), providedOptions);

    scopeManager = new ScopeManager(options);

    referencer = new Referencer(options, scopeManager);
    referencer.visit(tree);

    if(scopeManager.__currentScope !== null) {
    	throw new Error('currentScope should be null.');
	}
    return scopeManager;
}
var version = "3.6.0";

exports.analyze = analyze;
exports.version = version;
exports.Reference = Reference;
exports.Variable = Variable;
exports.Scope = Scope;
exports.ScopeManager = ScopeManager;

Object.defineProperty(exports, '__esModule', { value: true });

})));
/* eslint-env amd */
define('eslint/lib/config/environments',[
	'eslint/conf/environments',
	'module'
],
function(envs, module) {
	/**
	 * @fileoverview Environments manager
	 * @author Nicholas C. Zakas
	 */
	"use strict";

	//------------------------------------------------------------------------------
	// Private
	//------------------------------------------------------------------------------

	var environments = Object.create(null);

	/**
	 * Loads the default environments.
	 * @returns {void}
	 * @private
	 */
	function load() {
		Object.keys(envs).forEach(function(envName) {
			environments[envName] = envs[envName];
		});
	}

	// always load default environments upfront
	load();

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	module.exports = {

		load: load,

		/**
		 * Gets the environment with the given name.
		 * @param {string} name The name of the environment to retrieve.
		 * @returns {Object?} The environment object or null if not found.
		 */
		get: function(name) {
			return environments[name] || null;
		},

		/**
		 * Defines an environment.
		 * @param {string} name The name of the environment.
		 * @param {Object} env The environment settings.
		 * @returns {void}
		 */
		define: function(name, env) {
			environments[name] = env;
		},

		/**
		 * Imports all environments from a plugin.
		 * @param {Object} plugin The plugin object.
		 * @param {string} pluginName The name of the plugin.
		 * @returns {void}
		 */
		importPlugin: function(plugin, pluginName) {
			if (plugin.environments) {
				Object.keys(plugin.environments).forEach(function(envName) {
					this.define(pluginName + "/" + envName, plugin.environments[envName]);
				}, this);
			}
		},

		/**
		 * Resets all environments. Only use for tests!
		 * @returns {void}
		 */
		testReset: function() {
			environments = Object.create(null);
			load();
		}
	};

	return module.exports;
});
/* eslint-env amd */
define('eslint/lib/utils/util',[
'exports', 
], function(exports) {
/**
 * @fileoverview Common utilities.
 */


//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

var PLUGIN_NAME_PREFIX = "eslint-plugin-";
var breakableTypePattern = /^(?:(?:Do)?While|For(?:In|Of)?|Switch)Statement$/;

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
/**
 * Merges two objects together and assigns the result to the initial object. Can be used for shallow cloning.
 * @param {Object} target of the cloning operation
 * @param {Object} source object
 * @returns {void}
 */
exports.mixin = function(target, source) {
    Object.keys(source).forEach(function(key) {
        target[key] = source[key];
    });
};

/**
 * Merges two config objects. This will not only add missing keys, but will also modify values to match.
 * @param {Object} base config object
 * @param {Object} custom config object. Overrides in this config object will take priority over base.
 * @returns {Object} merged config object.
 */
exports.mergeConfigs = function mergeConfigs(base, custom) {

    Object.keys(custom).forEach(function (key) {
        var property = custom[key];

        if (key === "plugins") {
            if (!base[key]) {
                base[key] = [];
            }

            property.forEach(function (plugin) {
                // skip duplicates
                if (base[key].indexOf(plugin) === -1) {
                    base[key].push(plugin);
                }
            });
            return;
        }

        if (Array.isArray(base[key]) && !Array.isArray(property) && typeof property === "number") {
            // assume that we are just overriding first attribute
            base[key][0] = custom[key];
            return;
        }

        if (typeof property === "object" && !Array.isArray(property)) {
            // base[key] might not exist, so be careful with recursion here
            base[key] = mergeConfigs(base[key] || {}, custom[key]);
        } else {
            base[key] = custom[key];
        }
    });

    return base;
};

/**
 * Removes the prefix `eslint-plugin-` from a plugin name.
 * @param {string} pluginName The name of the plugin which may has the prefix.
 * @returns {string} The name of the plugin without prefix.
 */
exports.removePluginPrefix = function removePluginPrefix(pluginName) {
    var nameWithoutPrefix;

    if (pluginName.indexOf(PLUGIN_NAME_PREFIX) === 0) {
        nameWithoutPrefix = pluginName.substring(PLUGIN_NAME_PREFIX.length);
    } else {
        nameWithoutPrefix = pluginName;
    }

    return nameWithoutPrefix;
};

exports.PLUGIN_NAME_PREFIX = PLUGIN_NAME_PREFIX;

/**
 * @description Looks up the given reference in the current scope and its parent scopes
 * @param {Object} ref The AST node reference
 * @param {Object} scope The current EScope object
 * @returns The AST node the declares the given reference node or null if no declaration is found
 * @since 6.0
 */
exports.getDeclaration = function(ref, scope) {
	for (var curScope = scope; true; ) {
		if (!curScope) {
			return null;
		}
		var name = (ref.name ? ref.name : ref.identifier.name);
		var decl;
		curScope.variables.some(function(v) {
			if (v.name === name) {
				decl = v;
				return true;
			}
			return false;
		});
		if (decl) {
			return decl;
		}
		curScope = curScope.upper;
	}
};

/**
 * @description Returns if the node can lead to an unreachable statement
 * @param {Object} node The AST node
 * @returns {Boolean} If the node can lead to an unreachable warning
 * @since 6.0
 */
exports.returnableStatement = function(node) {
    switch (node.type) {
        case "ReturnStatement":
        case "ThrowStatement":
        case "ContinueStatement":
        case "BreakStatement":
            return true;
    }
    return false;
};

/**
 * Gets the label if the parent node of a given node is a LabeledStatement.
 *
 * @param {ASTNode} node - A node to get.
 * @returns {string|null} The label or `null`.
 */
exports.getLabel = function(node) {
	if (node.parent.type === "LabeledStatement") {
		return node.parent.label.name;
	}
	return null;
};

exports.isBreakableStatement = function(node) {
	return breakableTypePattern.test(node.type);
};

	return exports;
});

/*******************************************************************************
 * @license
 * Copyright (c) 2014 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 * 
 ******************************************************************************/
/* eslint-env amd */
define('javascript/nls/problems',{
	root:true
});

/*******************************************************************************
 * @license
 * Copyright (c) 2014, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
 *
 ******************************************************************************/
define('javascript/nls/root/problems',{
    'eslintValidationFailure': 'ESLint failed to validate this file because an error occurred: ${0}',
	'check-tern-plugin': 'To work in the \'${0}\' environment, the \'${1}\' plugin must be running.',
	'check-tern-lib': 'To work in the \'${0}\' environment, the \'${1}\' library must be running.',
	'curly': 'Statement should be enclosed in braces.',
	'curly-description': 'Require curly braces for all control statements.',
	'eqeqeq' : 'Expected \'${0}\' and instead saw \'${1}\'.',
	'eqeqeq-description': 'Require the use of === and !==.',
	'missing-doc' : 'Missing documentation for function \'${0}\'.',
	'missing-doc-description': 'Require JSDoc for all functions.',
	'missing-nls' : 'Non-externalized string literal \'${0}\'.',
	'missing-nls-description': 'Disallow non-externalized string literals.',
	'new-parens' : 'Missing parentheses invoking constructor.',
	'new-parens-description': 'Require parenthesis for constructors.',
	'no-caller': '\'arguments.${0}\' is deprecated.',
	'no-caller-description': 'Warn on use of arguments.callee or arguments.caller.',
	'no-comma-dangle': 'Trailing commas in object expressions are discouraged.',
	'no-comma-dangle-description': 'Report extra trailing comma in object expressions.',
	'no-cond-assign': 'Expected a conditional expression and instead saw an assignment.',
	'no-cond-assign-description': 'Disallow assignment statements in control statements like if-else, do-while, while and for statements.',
	'no-console': 'Discouraged use of console in browser-based code.',
	'no-console-description': 'Disallow the use of \'console\' in browser-run code.',
	'no-constant-condition': 'Discouraged use of constant as a conditional expression.',
	'no-constant-condition-description': 'Disallow use of a constant value as a conditional expression.',
	'no-debugger': '\'debugger\' statement use is discouraged.',
	'no-debugger-description': 'Disallow use of the debugger keyword.',
	'no-dupe-keys' : 'Duplicate object key \'${0}\'.',
	'no-dupe-keys-description': 'Warn when object contains duplicate keys.',
	'no-empty-block' : 'Empty block should be removed or commented.',
	'no-empty-block-description': 'Warn when a code block is empty.',
	'no-eval' : '${0} function calls are discouraged.',
	'no-eval-description': 'Disallow use of eval function.',
	'no-extra-semi' : 'Unnecessary semicolon.',
	'no-extra-semi-description': 'Warn about extraneous semi colons.',
	'no-fallthrough' : 'Switch case may be entered by falling through the previous case.',
	'no-fallthrough-description': 'Warn when a switch case falls through.',
	'no-implied-eval' : '${0} function calls are discouraged.',
	'no-implied-eval-description': 'Disallow use of implied eval function.',
	'no-iterator' : 'Discouraged __iterator__ property use.',
	'no-iterator-description': 'Warn when the __iterator__ property is used.',
	'no-proto' : 'Discouraged __proto__ property use.',
	'no-proto-description': 'Warn when the __proto__ property is used.',
	'no-jslint' : 'The \'${0}\' directive is unsupported, please use eslint-env.',
	'no-jslint-description': 'Warn when the jslint/jshint directive is used.',
	'no-mixed-spaces-and-tabs': 'Mixed spaces and tabs.',
	'no-mixed-spaces-and-tabs-description': 'Warn about mixed spaces and tabs.',
	'no-new-array' : 'Use the array literal notation \'[]\'.',
	'no-new-array-description': 'Disallow use of the Array constructor.',
	'no-new-func' : 'The Function constructor is eval.',
	'no-new-func-description': 'Disallow use of the Function constructor.',
	'no-new-object' : 'Use the object literal notation \'{}\' or Object.create(null).',
	'no-new-object-description': 'Disallow use of the Object constructor.',
	'no-new-wrappers' : 'Do not use \'${0}\' as a constructor.',
	'no-new-wrappers-description': 'Disallow creating new String, Number or Boolean via their constructor.',
	'no-redeclare' : '\'${0}\' is already defined.',
	'no-redeclare-description': 'Warn when variable or function is redeclared.',
	'no-regex-spaces' : 'Avoid multiple spaces in regular expressions. Use \' {${0}}\' instead.',
	'no-regex-spaces-description': 'Warn when multiple spaces are used in regular expressions.',
	'no-reserved-keys' : 'Reserved words should not be used as property keys.',
	'no-reserved-keys-description': 'Warn when a reserved word is used as a property key.',
	'no-shadow' : '\'${0}\' is already declared in the upper scope.',
	'no-shadow-description': 'Warn when shadowing variable from upper scope.',
	'no-shadow-global' : 'Variable \'${0}\' shadows a global member.',
	'no-shadow-global-description': 'Warn when a variable or parameter shadows a member from the global environment.',
	'no-shadow-global-param' : 'Parameter \'${0}\' shadows a global member.',
	'no-sparse-arrays': 'Sparse array declarations should be avoided.',
	'no-sparse-arrays-description': 'Warn when sparse arrays are defined.',
	'no-throw-literal': 'Throw an Error instead.',
	'no-throw-literal-description': 'Warn when a Literal is used in a throw statement.',
	'no-undef-defined' : '\'${0}\' is undefined.',
	'no-undef-expression-defined' : '\'${0}\' is undefined.',
	'no-undef-expression-defined-object' : '\'${0}\' is undefined for \'${1}\' in ${2}.',  // 0: member name 1: object name 2: file name
	'no-undef-expression-defined-index' : '\'${0}\' is undefined for \'${1}\' in ${2}.',  // 0: member name 1: object name 2: library
	'no-undef-init': 'Avoid explicitly initializing variables to \'undefined\'.',
	'no-undef-init-description': 'Warn when variables are explicitly initialized to undefined.',
	'no-undef-description': 'Warn when used variable or function has not been defined.',
	'no-unreachable' : 'Unreachable code.',
	'no-unreachable-description': 'Warn when code is not reachable.',
	'no-unused-params' : 'Parameter \'${0}\' is never used.',
	'no-unused-params-description': 'Warn when function parameters are not used.',
	'no-unused-vars-unused' : '\'${0}\' is unused.',
	'no-unused-vars-unused-funcdecl' : 'Function \'${0}\' is unused.',
	'no-unused-vars-unread' : '\'${0}\' is unread.',
	'no-unused-vars-description': 'Warn when declared variables are not used.',
	'no-use-before-define': '\'${0}\' was used before it was defined.',
	'no-use-before-define-description': 'Warn when a variable, function or class is used before it is defined.',
	'no-with': 'Discouraged use of \'with\' statement.',
	'no-with-description': 'Warn when the with statement is used.',
	'radix': 'Missing radix parameter.',
	'unknown-require': 'This library could not be found, type information for it will be incomplete.',
	'unknown-require-plugin': 'This library is available in a Tern plugin that is not currently loaded.',
	'unknown-require-not-running': 'Type information for this library might be available, but requires the \'${0}\' plug-in to be running.',
	'missing-requirejs': 'To use AMD, the \'requirejs\' plug-in needs to be running.',
	'radix-description': 'Warn when parseInt() is called without the \'radix\' parameter.',
	'semi-missing': 'Missing semicolon.',
	'semi-extra': 'Extra semicolon.',
	'semi-description': 'Warn about missing or extra semicolons.',
	'unnecessary-nls' : 'Unnecessary $NON-NLS$ tag.',
	'unnecessary-nls-description': 'Disallow unnecessary non-NLS comments.',
	'use-isnan': 'Use the isNaN function to compare with NaN.',
	'use-isnan-description': 'Disallow comparison to the value NaN.',
	'valid-typeof' : 'Invalid typeof comparison.',
	'valid-typeof-description': 'Warn when incorrectly comparing the result of a typeof expression.',
	'noErrorDetailed' : 'Unknown error occurred',
	'esmodules-not-running' : 'import/export declarations require the \'es_modules\' plug-in to be running.',
		
	// Rules consumed from ESLint
	'accessorPairGetter' : 'Getter is not present',
	'accessorPairSetter' : 'Setter is not present',
	'accessor-pairs-description' : "Report when accessors don't come in pairs (getter, setter)",
	'noControlRegex' : 'Unexpected control character in regular expression.',
	'no-control-regex-description' : "Disallow control characters in regular expressions",
	'noConstantAssign' : '\'{{name}}\' is constant.',
	'no-const-assign-description' : "Disallow modifying variables that are declared using const",
	'noDuplicateCase' : 'Duplicate case label.',
	'no-duplicate-case-description' : 'Disallow a duplicate case label',
	'NoNegatedInLhs': 'The \'in\' expression\'s left operand is negated',
	'no-negated-in-lhs-description' : 'Disallow negated left operand of in operator',
	'noObjCalls' : '\'{{name}}\' is not a function.',
	'no-obj-calls-description' : 'Disallow global object as function calls',
	'noElseReturn' : 'Unnecessary \'else\' after \'return\'.',
	'no-else-return-description' : 'Report else after return',
	'noEmptyCharacterClass' : 'Empty class.',
	'no-empty-character-class-description' : 'Disallow empty character classes',
	'noEmptyLabel' : 'Unexpected label \"{{l}}\"',
	'no-empty-label-description' : 'No empty labels',
	'noEqNull' :  'Use \'{{op}}=\' to compare with \'null\'.',
	'no-eq-null-description' : 'Disallow null comparisons',
	'noExtendNative' : '{{builtin}} prototype is read only, properties should not be added.',
	'no-extend-native-description' : 'Disallow extending of native objects',
	'noExtraBind' : 'The function binding is unnecessary.',
	'no-extra-bind-description' : 'Disallow unnecessary function binding.',
	'noExtraBooleanCastIfStatement' : 'Redundant double negation in an if statement condition.',
	'noExtraBooleanCastDoWhileStatement' : 'Redundant double negation in a do while loop condition.',
	'noExtraBooleanCastWhileStatement' : 'Redundant double negation in a while loop condition.',
	'noExtraBooleanCastConditionalExpression' : 'Redundant double negation in a ternary condition.',
	'noExtraBooleanCastForStatement' : 'Redundant double negation in a for loop condition.',
	'noExtraBooleanCastUnaryExpression': 'Redundant multiple negation.',
	'noExtraBooleanCastToBoolean' : 'Redundant double negation in call to Boolean().',
	'noExtraBooleanCastConstructorCall' : 'Redundant double negation in Boolean constructor call.',
	'no-extra-boolean-cast-description' : 'Discourage redundant double negation',
	'noExtraParens': 'Gratuitous parentheses around expression.',
	'no-extra-parens-description' : 'Discourage redundant parentheses',
	'noInvalidRegexp' : 'Invalid flags supplied to RegExp constructor \'{{arg}}\'',
	'no-invalid-regexp-description' : 'Report invalid regular expressions',
	'noSelfCompare' : 'Comparing to itself is potentially pointless.',
	'no-self-compare-description' : 'Disallow self compare',
	'noIrregularWhitespaces' : 'Irregular whitespace not allowed',
	'no-irregular-whitespace-description' : 'No irregular whitespace:',
	'no-self-assign-description' : 'Disallow self assignment',
	'no-self-assign' : '\'${0}\' is assigned to itself.',
	'type-checked-consistent-return-description' : 'Detect inconsistent return values',
	'inconsistent-return' : 'Inconsistent return types: \'{{type1}}\', \'{{type2}}\'',
	'no-void' : 'Expected \'undefined\' and instead saw \'void\'.',
	'no-void-description' : 'Disallow use of the void operator.',
	'noImplicitCoercionBoolean' : 'use \'Boolean({{code}})\' instead.',
	'noImplicitCoercionIndexOf' : 'use \'{{code}} !== -1\' instead.',
	'noImplicitCoercionNumber' : 'use \'Number({{code}})\' instead.',
	'noImplicitCoercionString' : 'use \'String({{code}})\' instead.',
	'noImplicitCoercionString2' : 'use \'{{code}} = String({{code}})\' instead.',
	'no-implicit-coercion-description' : 'Disallow the type conversion with shorter notations.',
	'noLoneBlock' : 'Block is redundant.',
	'noLoneNestedBlock' : 'Nested block is redundant.',
	'no-lone-blocks-description' : 'Disallow unecessary nested blocks',
	'doublequote' : 'Strings must use double quotes (").',
	'singlequote' : 'Strings must use single quotes (\').',
	'backtick' : 'Strings must use backtick quotes (`).',
	'quotes-description' : 'Enforce quote style',
	'yodaLeft' : 'Expected literal to be on the left side of {{operator}}.',
	'yodaRight' : 'Expected literal to be on the right side of {{operator}}.',
	'yoda-description' : 'Require or disallow Yoda conditions',
	'noParamReassign' : 'Assignment to function parameter \'{{name}}\'.',
	'noParamPropertyReassign' : 'Assignment to property of function parameter \'{{name}}\'.',
	'no-param-reassign-description' : 'Disallow reassignment of function parameters',
	'noNativeReassign' : 'Read-only global \'{{name}}\' should not be modified.',
	'no-native-reassign-description' : 'Disallow reassignment of native objects',
	'noUnusedExpression' : 'Expected an assignment or function call and instead saw an expression.',
	'no-unused-expressions-description' : 'Disallow unused expressions',
	'noInvalidThis' : 'Unexpected \'this\'.',
	'no-invalid-this-description' : 'Disallow this keywords outside of classes or class-like objects.',
	'check-tern-plugin-description' : 'Check missing tern plugins.',
	'missing-requirejs-description' : 'Check missing require libraries.',
	'no-undef-expression-description' : 'Report undefined expressions.',
	'unknown-require-description' : 'Report missing libraries or missing tern plugins.',
	'noTrailingSpaces' : 'Trailing spaces not allowed.',
	'no-trailing-spaces-description': 'Disallow trailing whitespace at the end of lines.',

	//.tern-project file validation messages
	'notNum': "'${0}' must be a number",
	'notArray': "'${0}' must be an array of strings",
	'notEmpty': "'${0}' should not be empty",
	'onlyStrings': "'${0}' entries can only be strings",
	'notObject': "'${0}' must be an object",
	'pluginNotObject': "plugin '${0}' must be an object or boolean",
	'noDupes': "Duplicate entries are not allowed",
	'problemInFile': "There is a problem with your .tern-project-file.",
	'openFile': "Open the .tern-project file",
	'errorParsing': "There was an error parsing the JSON in your .tern-project file",
	'multiAttrProblems': "Multiple problems were found with attributes in your .tern-project file.",
	'attrProblem': "A problem with one of the attributes in your .tern-project file was found.",
	'multipleFileMatchesProblem': 'Multiple file matches found for: ${0}. Defaulting to: ${1}.',
	'noFileMatchProblem': 'No file match found for: ${0}.',
	'tooManyFileMatchProblems': '... (${0} more)',
	'fileMatchProblems': 'Problems found in the loadEagerly attribute of your .tern-project file.',
	'fileMatchProgress': 'Validating loadEagerly paths in your .tern-project file.',
	'failedWrite': "There was a problem writing to your .tern-project file."
});


/*******************************************************************************
 * @license
 * Copyright (c) 2014 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 * 
 * Contributors: IBM Corporation - initial API and implementation
 ******************************************************************************/

/*eslint-env browser, amd*/
define("orion/editor/stylers/lib/syntax", [], function() {
	return {
		id: "orion.lib",
		grammars: [{
			id: "orion.lib",
			repository: {
				brace_open: {
					match: "{",
					name: "punctuation.section.block.begin"
				},
				brace_close: {
					match: "}",
					name: "punctuation.section.block.end"
				},
				bracket_open: {
					match: "\\[",
					name: "punctuation.section.bracket.begin"
				},
				bracket_close: {
					match: "\\]",
					name: "punctuation.section.bracket.end"
				},
				parenthesis_open: {
					match: "\\(",
					name: "punctuation.section.parens.begin"
				},
				parenthesis_close: {
					match: "\\)",
					name: "punctuation.section.parens.end"
				},
				operator: {
					match: "(\\+|-|!|=|>|<|&|(\\|\\|))+",
					name: "punctuation.operator"
				},
				doc_block: {
					begin: "/\\*\\*",
					end: "\\*/",
					name: "comment.block.documentation",
					beginCaptures: {
						0: {name: "comment.block.documentation.start"}
					},
					endCaptures: {
						0: {name: "comment.block.documentation.end"}
					},
					patterns: [
						{
							match: "@(?:(?!\\*/)\\S)*",
							name: "meta.documentation.annotation"
						}, {
							match: "<[^\\s>]*>",
							name: "meta.documentation.tag"
						}, {
							match: "(\\b)(TODO)(\\b)(((?!\\*/).)*)",
							name: "meta.annotation.task.todo",
							captures: {
								2: {name: "keyword.other.documentation.task"},
								4: {name: "comment.block"}
							}
						}
					]
				},
				number_decimal: {
					match: "\\b-?(?:\\.\\d+|\\d+\\.?\\d*)(?:[eE][+-]?\\d+)?\\b",
					name: "constant.numeric.number"
				},
				number_hex: {
					match: "\\b0[xX][0-9A-Fa-f]+\\b",
					name: "constant.numeric.hex"
				},
				string_doubleQuote: {
					match: '"(?:\\\\.|[^"])*"?',
					name: "string.quoted.double"
				},
				string_singleQuote: {
					match: "'(?:\\\\.|[^'])*'?",
					name: "string.quoted.single"
				},
				todo_comment_singleLine: {
					match: "(\\b)(TODO)(\\b)(.*)",
					name: "meta.annotation.task.todo",
					captures: {
						2: {name: "keyword.other.documentation.task"},
						4: {name: "comment.line"}
					}
				}
			}
		}, {
			id: "orion.c-like",
			repository: {
				comment_singleLine: {
					match: {match: "(//).*", literal: "//"},
					name: "comment.line.double-slash",
					captures: {
						1: {name: "comment.line.double-slash.start"}
					},
					patterns: [
						{
							include: "orion.lib#todo_comment_singleLine"
						}
					]
				},
				comment_block: {
					begin: {match: "/\\*", literal: "/*"},
					end: {match: "\\*/", literal: "*/"}, 
					name: "comment.block",
					beginCaptures: {
						0: {name: "comment.block.start"}
					},
					endCaptures: {
						0: {name: "comment.block.end"}
					},
					patterns: [
						{
							match: "(\\b)(TODO)(\\b)(((?!\\*/).)*)",
							name: "meta.annotation.task.todo",
							captures: {
								2: {name: "keyword.other.documentation.task"},
								4: {name: "comment.block"}
							}
						}
					]
				}
			}
		}],
		keywords: []
	};
});

/*******************************************************************************
 * @license
 * Copyright (c) 2014, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 * 
 * Contributors: IBM Corporation - initial API and implementation
 ******************************************************************************/

/*eslint-env browser, amd*/

define("orion/editor/stylers/application_javascript/syntax", ["orion/editor/stylers/lib/syntax"], function(mLib) {
	var keywords = [
		"async", "await",
		"class", "const",
		"debugger", "delete",
		"enum", "export", "extends",
		"function",
		"implements", "import", "in", "instanceof", "interface",
		"let",
		"new",
		"of",
		"package", "private", "protected", "public",
		"static", "super",
		"typeof",
		"var", "void",
		"with"
	];
	var controlKeywords = [
		"break",
		"case", "catch", "continue",
		"default", "do",
		"else",
		"finally", "for",
		"if",
		"return",
		"switch",
		"throw", "try",
		"while",
		"yield"
	];
	var languageVariables = ["this"];
	var constants = [
		"false", "null", "true", "undefined"
	];

	var grammars = [];
	grammars.push.apply(grammars, mLib.grammars);
	grammars.push({
		id: "orion.js",
		contentTypes: ["application/javascript"],
		patterns: [
			{include: "#string_multiline_singleQuote"},
			{include: "#string_multiline_doubleQuote"},
			{include: "#templateLiteral"},
			{include: "orion.lib#string_doubleQuote"},
			{include: "orion.lib#string_singleQuote"},
			{include: "orion.c-like#comment_singleLine"},
			{include: "#regex"},
			{include: "orion.lib#doc_block"},
			{include: "orion.c-like#comment_block"},
			{include: "#jsFunctionDef"},
			{include: "orion.lib#brace_open"},
			{include: "orion.lib#brace_close"},
			{include: "orion.lib#bracket_open"},
			{include: "orion.lib#bracket_close"},
			{include: "orion.lib#parenthesis_open"},
			{include: "orion.lib#parenthesis_close"},
			{include: "orion.lib#operator"},
			{include: "orion.lib#number_decimal"},
			{include: "orion.lib#number_hex"},
			{include: "#keywordsOperator"},
			{include: "#keywordsControl"},
			{include: "#languageConstant"},
			{include: "#languageVariable"}
		],
		repository: {
			jsFunctionDef: {
				/*
				 * http://stackoverflow.com/questions/2008279/validate-a-javascript-function-name/2008444#2008444
				 * was referenced in the composition of the "begin" pattern below.
				 */
				begin: "(function)(\\s+[_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*)?\\s*\\(",
				end: "\\)",
				captures: {
					1: {name: "keyword.operator.js"},
					2: {name: "entity.name.function.js"}
				},
				patterns: [
					{include: "orion.c-like#comment_singleLine"},
					{include: "orion.c-like#comment_block"},
					{
						match: "[^\\s,]+",
						name: "variable.parameter.js"
					}
				]
			},
			keywordsControl: {
				match: "\\b(?:" + controlKeywords.join("|") + ")\\b",
				name: "keyword.control.js"
			},
			keywordsOperator: {
				match: "\\b(?:" + keywords.join("|") + ")\\b",
				name: "keyword.operator.js"
			},
			languageConstant: {
				match: "\\b(?:" + constants.join("|") + ")\\b",
				name: "constant.language.js"
			},
			languageVariable: {
				match: "\\b(?:" + languageVariables.join("|") + ")\\b",
				name: "variable.language.js"
			},
			string_multiline_singleQuote: {
				begin: "'(?:\\\\.|[^\\\\'])*\\\\$",
				end: "^(?:$|(?:\\\\.|[^\\\\'])*('|[^\\\\]$))",
				name: "string.quoted.single.js"
			},
			string_multiline_doubleQuote: {
				begin: '"(?:\\\\.|[^\\\\"])*\\\\$',
				end: '^(?:$|(?:\\\\.|[^\\\\"])*("|[^\\\\]$))',
				name: "string.quoted.double.js"
			},
			regex: {
				match: "/(?![\\s\\*])(?:\\\\.|[^/])+/(?:[gim]{0,3})",
				name: "string.regexp.js"
			},
			templateLiteral: {
				begin: "`",
				end: "`",
				name: "string.quoted.backtick.js",
				patterns: [
					{
						begin: "\\$\\{",
						end: "\\}",
						name: "string.interpolated.js",
						patterns: [
							{include: "#string_multiline_singleQuote"},
							{include: "#string_multiline_doubleQuote"},
							{include: "#templateLiteral"},
							{include: "orion.lib#string_doubleQuote"},
							{include: "orion.lib#string_singleQuote"},
							{include: "orion.c-like#comment_singleLine"},
							{include: "#regex"},
							{include: "orion.lib#doc_block"},
							{include: "orion.c-like#comment_block"},
							{include: "#jsFunctionDef"},
							{include: "orion.lib#brace_open"},
							{include: "orion.lib#brace_close"},
							{include: "orion.lib#bracket_open"},
							{include: "orion.lib#bracket_close"},
							{include: "orion.lib#parenthesis_open"},
							{include: "orion.lib#parenthesis_close"},
							{include: "orion.lib#operator"},
							{include: "orion.lib#number_decimal"},
							{include: "orion.lib#number_hex"},
							{include: "#keywordsOperator"},
							{include: "#keywordsControl"},
							{include: "#languageConstant"},
							{include: "#languageVariable"}
						]
					}
				]
			}
		}
	});

	return {
		id: grammars[grammars.length - 1].id,
		grammars: grammars,
		keywords: keywords.concat(controlKeywords).concat(languageVariables).concat(constants)
	};
});

/*eslint-env amd */
define('eslint/lib/rules/accessor-pairs',[
	'i18n!javascript/nls/problems',
	'module'
], function (ProblemMessages, module) {/**
 * @fileoverview Rule to flag wrapping non-iife in parens
 * @author Gyandeep Singh
 * @copyright 2015 Gyandeep Singh. All rights reserved.
 */



//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Checks whether or not a given node is an `Identifier` node which was named a given name.
 * @param {ASTNode} node - A node to check.
 * @param {string} name - An expected name of the node.
 * @returns {boolean} `true` if the node is an `Identifier` node which was named as expected.
 */
function isIdentifier(node, name) {
    return node.type === "Identifier" && node.name === name;
}

/**
 * Checks whether or not a given node is an argument of a specified method call.
 * @param {ASTNode} node - A node to check.
 * @param {number} index - An expected index of the node in arguments.
 * @param {string} object - An expected name of the object of the method.
 * @param {string} property - An expected name of the method.
 * @returns {boolean} `true` if the node is an argument of the specified method call.
 */
function isArgumentOfMethodCall(node, index, object, property) {
    var parent = node.parent;
    return parent.type === "CallExpression" &&
        parent.callee.type === "MemberExpression" &&
        parent.callee.computed === false &&
        isIdentifier(parent.callee.object, object) &&
        isIdentifier(parent.callee.property, property) &&
        parent.arguments[index] === node;
}

/**
 * Checks whether or not a given node is a property descriptor.
 * @param {ASTNode} node - A node to check.
 * @returns {boolean} `true` if the node is a property descriptor.
 */
function isPropertyDescriptor(node) {
    // Object.defineProperty(obj, "foo", {set: ...})
    if (isArgumentOfMethodCall(node, 2, "Object", "defineProperty") ||
        isArgumentOfMethodCall(node, 2, "Reflect", "defineProperty")
    ) {
        return true;
    }

    // Object.defineProperties(obj, {foo: {set: ...}})
    // Object.create(proto, {foo: {set: ...}})
    node = node.parent.parent;
    return node.type === "ObjectExpression" && (
        isArgumentOfMethodCall(node, 1, "Object", "create") ||
        isArgumentOfMethodCall(node, 1, "Object", "defineProperties")
    );
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {
    var config = context.options[0] || {};
    var checkGetWithoutSet = config.getWithoutSet === true;
    var checkSetWithoutGet = config.setWithoutGet !== false;

    /**
     * Checks a object expression to see if it has setter and getter both present or none.
     * @param {ASTNode} node The node to check.
     * @returns {void}
     * @private
     */
    function checkLonelySetGet(node) {
        var isSetPresent = false;
        var isGetPresent = false;
        var isDescriptor = isPropertyDescriptor(node);

        for (var i = 0, end = node.properties.length; i < end; i++) {
            var property = node.properties[i];

            var propToCheck = "";
            if (property.kind === "init") {
                if (isDescriptor && !property.computed) {
                    propToCheck = property.key.name;
                }
            } else {
                propToCheck = property.kind;
            }

            switch (propToCheck) {
                case "set":
                    isSetPresent = true;
                    break;

                case "get":
                    isGetPresent = true;
                    break;

                default:
                    // Do nothing
            }

            if (isSetPresent && isGetPresent) {
                break;
            }
        }

        if (checkSetWithoutGet && isSetPresent && !isGetPresent) {
            context.report(node, ProblemMessages.accessorPairGetter);
        } else if (checkGetWithoutSet && isGetPresent && !isSetPresent) {
            context.report(node,  ProblemMessages.accessorPairSetter);
        }
    }

    return {
        "ObjectExpression": function(node) {
            if (checkSetWithoutGet || checkGetWithoutSet) {
                checkLonelySetGet(node);
            }
        }
    };

};

module.exports.schema = [
    {
        "type": "object",
        "properties": {
            "getWithoutSet": {
                "type": "boolean"
            },
            "setWithoutGet": {
                "type": "boolean"
            }
        },
        "additionalProperties": false
    }
];
return module.exports;
});

/*eslint-env amd */
define('eslint/lib/rules/no-control-regex',[
	'i18n!javascript/nls/problems',
	'module'
], function (ProblemMessages, module) {
/**
 * @fileoverview Rule to forbid control charactes from regular expressions.
 * @author Nicholas C. Zakas
 */



//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {

    /**
     * Get the regex expression
     * @param {ASTNode} node node to evaluate
     * @returns {*} Regex if found else null
     * @private
     */
    function getRegExp(node) {

        if (node.value instanceof RegExp) {
            return node.value;
        } else if (typeof node.value === "string") {

            var parent = context.getAncestors().pop();
            if ((parent.type === "NewExpression" || parent.type === "CallExpression") &&
            parent.callee.type === "Identifier" && parent.callee.name === "RegExp") {

                // there could be an invalid regular expression string
                try {
                    return new RegExp(node.value);
                } catch (ex) {
                    return null;
                }

            }
        } else {
            return null;
        }

    }



    return {

        "Literal": function(node) {

            var computedValue,
                regex = getRegExp(node);

            if (regex) {
                computedValue = regex.toString();
                if (/[\x00-\x1f]/.test(computedValue)) {
                    context.report(node, ProblemMessages.noControlRegex);
                }
            }
        }
    };

};

module.exports.schema = [];

return module.exports;
});

/*eslint-env amd */
define('eslint/lib/rules/no-duplicate-case',[
	'i18n!javascript/nls/problems',
	'module'
], function (ProblemMessages, module) {
/**
 * @fileoverview Rule to disallow a duplicate case label.
 * @author Dieter Oberkofler
 * @author Burak Yigit Kaya
 * @copyright 2015 Dieter Oberkofler. All rights reserved.
 * @copyright 2015 Burak Yigit Kaya. All rights reserved.
 */



//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {

    return {
        "SwitchStatement": function(node) {
            var mapping = {};

            node.cases.forEach(function(switchCase) {
                var key = context.getSource(switchCase.test);
                if (mapping[key]) {
                    context.report(switchCase.test, ProblemMessages.noDuplicateCase);
                } else {
                    mapping[key] = switchCase;
                }
            });
        }
    };
};

module.exports.schema = [];

return module.exports;
});

/*eslint-env amd */
define('eslint/lib/rules/no-else-return',[
	'i18n!javascript/nls/problems',
	'module'
], function (ProblemMessages, module) {
/**
 * @fileoverview Rule to flag `else` after a `return` in `if`
 * @author Ian Christian Myers
 */



//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {

    //--------------------------------------------------------------------------
    // Helpers
    //--------------------------------------------------------------------------

    /**
     * Display the context report if rule is violated
     *
     * @param {Node} node The 'else' node
     * @returns {void}
     */
    function displayReport(node) {
        context.report(node, ProblemMessages.noElseReturn);
    }

    /**
     * Check to see if the node is a ReturnStatement
     *
     * @param {Node} node The node being evaluated
     * @returns {boolean} True if node is a return
     */
    function checkForReturn(node) {
        return node.type === "ReturnStatement";
    }

    /**
     * Naive return checking, does not iterate through the whole
     * BlockStatement because we make the assumption that the ReturnStatement
     * will be the last node in the body of the BlockStatement.
     *
     * @param {Node} node The consequent/alternate node
     * @returns {boolean} True if it has a return
     */
    function naiveHasReturn(node) {
        if (node.type === "BlockStatement") {
            var body = node.body,
                lastChildNode = body[body.length - 1];

            return lastChildNode && checkForReturn(lastChildNode);
        }
        return checkForReturn(node);
    }

    /**
     * Check to see if the node is valid for evaluation,
     * meaning it has an else and not an else-if
     *
     * @param {Node} node The node being evaluated
     * @returns {boolean} True if the node is valid
     */
    function hasElse(node) {
        return node.alternate && node.consequent && node.alternate.type !== "IfStatement";
    }

    /**
     * If the consequent is an IfStatement, check to see if it has an else
     * and both its consequent and alternate path return, meaning this is
     * a nested case of rule violation.  If-Else not considered currently.
     *
     * @param {Node} node The consequent node
     * @returns {boolean} True if this is a nested rule violation
     */
    function checkForIf(node) {
        return node.type === "IfStatement" && hasElse(node) &&
            naiveHasReturn(node.alternate) && naiveHasReturn(node.consequent);
    }

    /**
     * Check the consequent/body node to make sure it is not
     * a ReturnStatement or an IfStatement that returns on both
     * code paths.
     *
     * @param {Node} node The consequent or body node
     * @param {Node} alternate The alternate node
     * @returns {boolean} `true` if it is a Return/If node that always returns.
     */
    function checkForReturnOrIf(node) {
        return checkForReturn(node) || checkForIf(node);
    }


    /**
     * Check whether a node returns in every codepath.
     * @param {Node} node The node to be checked
     * @returns {boolean} `true` if it returns on every codepath.
     */
    function alwaysReturns(node) {
        // If we have a BlockStatement, check each consequent body node.
        if (node.type === "BlockStatement") {
            return node.body.some(checkForReturnOrIf);
        // If not a block statement, make sure the consequent isn't a ReturnStatement
        // or an IfStatement with returns on both paths
        }
        return checkForReturnOrIf(node);
    }

    //--------------------------------------------------------------------------
    // Public API
    //--------------------------------------------------------------------------

    return {

        "IfStatement": function(node) {
            var parent = context.getAncestors().pop(),
                consequents,
                alternate;

            // Only "top-level" if statements are checked, meaning the first `if`
            // in a `if-else-if-...` chain.
            if (parent.type === "IfStatement" && parent.alternate === node) {
                return;
            }

            for (consequents = []; node.type === "IfStatement"; node = node.alternate) {
                if (!node.alternate) {
                    return;
                }
                consequents.push(node.consequent);
                alternate = node.alternate;
            }

            if (consequents.every(alwaysReturns)) {
                displayReport(alternate);
            }
        }

    };

};

module.exports.schema = [];

return module.exports;
});

/*eslint-env amd */
define('eslint/lib/rules/no-empty-character-class',[
	'i18n!javascript/nls/problems',
	'module'
], function (ProblemMessages, module) {/**
 * @fileoverview Rule to flag the use of empty character classes in regular expressions
 * @author Ian Christian Myers
 */



//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/*
plain-English description of the following regexp:
0. `^` fix the match at the beginning of the string
1. `\/`: the `/` that begins the regexp
2. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following
  2.0. `[^\\[]`: any character that's not a `\` or a `[` (anything but escape sequences and character classes)
  2.1. `\\.`: an escape sequence
  2.2. `\[([^\\\]]|\\.)+\]`: a character class that isn't empty
3. `\/` the `/` that ends the regexp
4. `[gimuy]*`: optional regexp flags
5. `$`: fix the match at the end of the string
*/
var regex = /^\/([^\\[]|\\.|\[([^\\\]]|\\.)+\])*\/[gimuy]*$/;

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {

    return {

        "Literal": function(node) {
            var token = context.getFirstToken(node);
            if (token.type === "RegularExpression" && !regex.test(token.value)) {
                context.report(node, ProblemMessages.noEmptyCharacterClass);
            }
        }

    };

};

module.exports.schema = [];

return module.exports;
});

/*eslint-env amd */
define('eslint/lib/rules/no-empty-label',[
	'i18n!javascript/nls/problems',
	'module'
], function (ProblemMessages, module) {
/**
 * @fileoverview Rule to flag when label is not used for a loop or switch
 * @author Ilya Volodin
 */



//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {

    return {

        "LabeledStatement": function(node) {
            var type = node.body.type;

            if (type !== "ForStatement" && type !== "WhileStatement" && type !== "DoWhileStatement" && type !== "SwitchStatement" && type !== "ForInStatement" && type !== "ForOfStatement") {
                context.report(node, ProblemMessages.noEmptyLabel, {l: node.label.name});
            }
        }
    };

};

module.exports.schema = [];

return module.exports;
});

/*eslint-env amd */
define('eslint/lib/rules/no-eq-null',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	/**
	 * @fileoverview Rule to flag comparisons to null without a type-checking
	 * operator.
	 * @author Ian Christian Myers
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {

		function getOperatorToken(context, node) {
			var tokens = context.getTokens(node),
				len = tokens.length,
				operator = node.operator;
			for (var i = 0; i < len; i++) {
				var t = tokens[i];
				if (t.value === operator) {
					return t;
				}
			}
			return null;
		}

		return {

			"BinaryExpression": function(node) {
				var badOperator = node.operator === "==" || node.operator === "!=";
				if (node.right.type === "Literal" && node.right.raw === "null" && badOperator ||
					node.left.type === "Literal" && node.left.raw === "null" && badOperator) {
					context.report(node, ProblemMessages.noEqNull, {op: node.operator}, getOperatorToken(context, node)); //$NON-NLS-1$ //$NON-NLS-2$
				}
			}
		};

	};

	module.exports.schema = [];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-extra-boolean-cast',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	/**
	 * @fileoverview Rule to flag unnecessary double negation in Boolean contexts
	 * @author Brandon Mills
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {

		return {
			"UnaryExpression": function(node) {
				var ancestors = context.getAncestors(),
					parent = ancestors.pop(),
					grandparent = ancestors.pop();

				// Exit early if it's guaranteed not to match
				if (node.operator !== "!" ||
					parent.type !== "UnaryExpression" ||
					parent.operator !== "!") {
					return;
				}

				// if (<bool>) ...
				if (grandparent.type === "IfStatement") {
					context.report(node, ProblemMessages.noExtraBooleanCastIfStatement);

					// do ... while (<bool>)
				} else if (grandparent.type === "DoWhileStatement") {
					context.report(node, ProblemMessages.noExtraBooleanCastDoWhileStatement);

					// while (<bool>) ...
				} else if (grandparent.type === "WhileStatement") {
					context.report(node, ProblemMessages.noExtraBooleanCastWhileStatement);

					// <bool> ? ... : ...
				} else if (grandparent.type === "ConditionalExpression" &&
					parent === grandparent.test) {
					context.report(node, ProblemMessages.noExtraBooleanCastConditionalExpression);

					// for (...; <bool>; ...) ...
				} else if (grandparent.type === "ForStatement" &&
					parent === grandparent.test) {
					context.report(node, ProblemMessages.noExtraBooleanCastForStatement);

					// !<bool>
				} else if (grandparent.type === "UnaryExpression" &&
					grandparent.operator === "!") {
					context.report(node, ProblemMessages.noExtraBooleanCastUnaryExpression);

					// Boolean(<bool>)
				} else if (grandparent.type === "CallExpression" &&
					grandparent.callee.type === "Identifier" &&
					grandparent.callee.name === "Boolean") {
					context.report(node, ProblemMessages.noExtraBooleanCastToBoolean);

					// new Boolean(<bool>)
				} else if (grandparent.type === "NewExpression" &&
					grandparent.callee.type === "Identifier" &&
					grandparent.callee.name === "Boolean") {
					context.report(node, ProblemMessages.noExtraBooleanCastConstructorCall);
				}
			}
		};

	};

	module.exports.schema = [];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-extra-parens',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	/**
	 * @fileoverview Disallow parenthesising higher precedence subexpressions.
	 * @author Michael Ficarra
	 * @copyright 2014 Michael Ficarra. All rights reserved.
	 * See LICENSE file in root directory for full license.
	 */
	"use strict";

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {
		var sourceCode = context.getSourceCode();
		
		/**
		 * Determines if a node is surrounded by parentheses.
		 * @param {SourceCode} sourceCode The ESLint source code object
		 * @param {ASTNode} node The node to be checked.
		 * @returns {boolean} True if the node is parenthesised.
		 * @private
		 */
		function isParenthesised(node) {
			var previousToken = sourceCode.getTokenBefore(node),
				nextToken = sourceCode.getTokenAfter(node);

			return Boolean(previousToken && nextToken) &&
				previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
				nextToken.value === ")" && nextToken.range[0] >= node.range[1];
		}

		var ALL_NODES = context.options[0] !== "functions";
		var EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false;
		var NESTED_BINARY = ALL_NODES && context.options[1] && context.options[1].nestedBinaryExpressions === false;
		var EXCEPT_RETURN_ASSIGN = ALL_NODES && context.options[1] && context.options[1].returnAssign === false;

		function precedence(node) {
			switch (node.type) {
				case "SequenceExpression":
					return 0;

				case "AssignmentExpression":
				case "ArrowFunctionExpression":
				case "YieldExpression":
					return 1;

				case "ConditionalExpression":
					return 3;

				case "LogicalExpression":
					switch (node.operator) {
						case "||":
							return 4;
						case "&&":
							return 5;

							// no default
					}

					/* falls through */

				case "BinaryExpression":

					switch (node.operator) {
						case "|":
							return 6;
						case "^":
							return 7;
						case "&":
							return 8;
						case "==":
						case "!=":
						case "===":
						case "!==":
							return 9;
						case "<":
						case "<=":
						case ">":
						case ">=":
						case "in":
						case "instanceof":
							return 10;
						case "<<":
						case ">>":
						case ">>>":
							return 11;
						case "+":
						case "-":
							return 12;
						case "*":
						case "/":
						case "%":
							return 13;

							// no default
					}

					/* falls through */

				case "UnaryExpression":
					return 14;

				case "UpdateExpression":
					return 15;

				case "CallExpression":

					// IIFE is allowed to have parens in any position (#655)
					if (node.callee.type === "FunctionExpression") {
						return -1;
					}
					return 16;

				case "NewExpression":
					return 17;

					// no default
			}
			return 18;
		}
		/**
		 * Determines if this rule should be enforced for a node given the current configuration.
		 * @param {ASTNode} node - The node to be checked.
		 * @returns {boolean} True if the rule should be enforced for this node.
		 * @private
		 */
		function ruleApplies(node) {
			return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
		}

		/**
		 * Determines if a node is surrounded by parentheses twice.
		 * @param {ASTNode} node - The node to be checked.
		 * @returns {boolean} True if the node is doubly parenthesised.
		 * @private
		 */
		function isParenthesisedTwice(node) {
			var previousToken = sourceCode.getTokenBefore(node, 1),
				nextToken = sourceCode.getTokenAfter(node, 1);

			return isParenthesised(node) && previousToken && nextToken &&
				previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
				nextToken.value === ")" && nextToken.range[0] >= node.range[1];
		}

		/**
		 * Determines if a node is surrounded by (potentially) invalid parentheses.
		 * @param {ASTNode} node - The node to be checked.
		 * @returns {boolean} True if the node is incorrectly parenthesised.
		 * @private
		 */
		function hasExcessParens(node) {
			return ruleApplies(node) && isParenthesised(node);
		}

		/**
		 * Determines if a node that is expected to be parenthesised is surrounded by
		 * (potentially) invalid extra parentheses.
		 * @param {ASTNode} node - The node to be checked.
		 * @returns {boolean} True if the node is has an unexpected extra pair of parentheses.
		 * @private
		 */
		function hasDoubleExcessParens(node) {
			return ruleApplies(node) && isParenthesisedTwice(node);
		}

		/**
		 * Determines if a node test expression is allowed to have a parenthesised assignment
		 * @param {ASTNode} node - The node to be checked.
		 * @returns {boolean} True if the assignment can be parenthesised.
		 * @private
		 */
		function isCondAssignException(node) {
			return EXCEPT_COND_ASSIGN && node.test.type === "AssignmentExpression";
		}

		/**
		 * Determines if a node is in a return statement
		 * @param {ASTNode} node - The node to be checked.
		 * @returns {boolean} True if the node is in a return statement.
		 * @private
		 */
		function isInReturnStatement(node) {
			while (node) {
				if (node.type === "ReturnStatement" ||
					(node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement")) {
					return true;
				}
				node = node.parent;
			}

			return false;
		}

		/**
		 * Determines if a node is or contains an assignment expression
		 * @param {ASTNode} node - The node to be checked.
		 * @returns {boolean} True if the node is or contains an assignment expression.
		 * @private
		 */
		function containsAssignment(node) {
			if (node.type === "AssignmentExpression") {
				return true;
			} else if (node.type === "ConditionalExpression" &&
				(node.consequent.type === "AssignmentExpression" || node.alternate.type === "AssignmentExpression")) {
				return true;
			} else if ((node.left && node.left.type === "AssignmentExpression") ||
				(node.right && node.right.type === "AssignmentExpression")) {
				return true;
			}

			return false;
		}

		/**
		 * Determines if a node is contained by or is itself a return statement and is allowed to have a parenthesised assignment
		 * @param {ASTNode} node - The node to be checked.
		 * @returns {boolean} True if the assignment can be parenthesised.
		 * @private
		 */
		function isReturnAssignException(node) {
			if (!EXCEPT_RETURN_ASSIGN || !isInReturnStatement(node)) {
				return false;
			}

			if (node.type === "ReturnStatement") {
				return node.argument && containsAssignment(node.argument);
			} else if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") {
				return containsAssignment(node.body);
			}
			return containsAssignment(node);
		}

		/**
		 * Determines if a node following a [no LineTerminator here] restriction is
		 * surrounded by (potentially) invalid extra parentheses.
		 * @param {Token} token - The token preceding the [no LineTerminator here] restriction.
		 * @param {ASTNode} node - The node to be checked.
		 * @returns {boolean} True if the node is incorrectly parenthesised.
		 * @private
		 */
		function hasExcessParensNoLineTerminator(token, node) {
			if (token.loc.end.line === node.loc.start.line) {
				return hasExcessParens(node);
			}

			return hasDoubleExcessParens(node);
		}

		/**
		 * Checks whether or not a given node is located at the head of ExpressionStatement.
		 * @param {ASTNode} node - A node to check.
		 * @returns {boolean} `true` if the node is located at the head of ExpressionStatement.
		 */
		function isHeadOfExpressionStatement(node) {
			var parent = node.parent;

			while (parent) {
				switch (parent.type) {
					case "SequenceExpression":
						if (parent.expressions[0] !== node || isParenthesised(node)) {
							return false;
						}
						break;

					case "UnaryExpression":
					case "UpdateExpression":
						if (parent.prefix || isParenthesised(node)) {
							return false;
						}
						break;

					case "BinaryExpression":
					case "LogicalExpression":
						if (parent.left !== node || isParenthesised(node)) {
							return false;
						}
						break;

					case "ConditionalExpression":
						if (parent.test !== node || isParenthesised(node)) {
							return false;
						}
						break;

					case "CallExpression":
						if (parent.callee !== node || isParenthesised(node)) {
							return false;
						}
						break;

					case "MemberExpression":
						if (parent.object !== node || isParenthesised(node)) {
							return false;
						}
						break;

					case "ExpressionStatement":
						return true;

					default:
						return false;
				}

				node = parent;
				parent = parent.parent;
			}

			/* istanbul ignore next */
			throw new Error("unreachable");
		}

		/**
		 * Report the node
		 * @param {ASTNode} node node to evaluate
		 * @returns {void}
		 * @private
		 */
		function report(node) {
			var previousToken = sourceCode.getTokenBefore(node);

			context.report(node, previousToken.loc.start, ProblemMessages.noExtraParens);
		}

		/**
		 * Evaluate Unary update
		 * @param {ASTNode} node node to evaluate
		 * @returns {void}
		 * @private
		 */
		function dryUnaryUpdate(node) {
			if (hasExcessParens(node.argument) && precedence(node.argument) >= precedence(node)) {
				report(node.argument);
			}
		}

		/**
		 * Evaluate a new call
		 * @param {ASTNode} node node to evaluate
		 * @returns {void}
		 * @private
		 */
		function dryCallNew(node) {
			if (hasExcessParens(node.callee) && precedence(node.callee) >= precedence(node) && !(
					node.type === "CallExpression" &&
					node.callee.type === "FunctionExpression" &&

					// One set of parentheses are allowed for a function expression
					!hasDoubleExcessParens(node.callee)
				)) {
				report(node.callee);
			}
			if (node.arguments.length === 1) {
				if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= precedence({
						type: "AssignmentExpression"
					})) {
					report(node.arguments[0]);
				}
			} else {
				[].forEach.call(node.arguments, function(arg) {
					if (hasExcessParens(arg) && precedence(arg) >= precedence({
							type: "AssignmentExpression"
						})) {
						report(arg);
					}
				});
			}
		}

		/**
		 * Evaluate binary logicals
		 * @param {ASTNode} node node to evaluate
		 * @returns {void}
		 * @private
		 */
		function dryBinaryLogical(node) {
			if (!NESTED_BINARY) {
				var prec = precedence(node);

				if (hasExcessParens(node.left) && precedence(node.left) >= prec) {
					report(node.left);
				}
				if (hasExcessParens(node.right) && precedence(node.right) > prec) {
					report(node.right);
				}
			}
		}

		return {
			ArrayExpression: function(node) {
				[].forEach.call(node.elements, function(e) {
					if (e && hasExcessParens(e) && precedence(e) >= precedence({type: "AssignmentExpression"})) {
						report(e);
					}
				});
			},

			ArrowFunctionExpression: function(node) {
				if (isReturnAssignException(node)) {
					return;
				}

				if (node.body.type !== "BlockStatement") {
					if (sourceCode.getFirstToken(node.body).value !== "{"
							&& hasExcessParens(node.body)
							&& precedence(node.body) >= precedence({type: "AssignmentExpression"})) {
						report(node.body);
						return;
					}

					// Object literals *must* be parenthesised
					if (node.body.type === "ObjectExpression" && hasDoubleExcessParens(node.body)) {
						report(node.body);
						return;
					}
				}
			},

			AssignmentExpression: function(node) {
				if (isReturnAssignException(node)) {
					return;
				}

				if (hasExcessParens(node.right) && precedence(node.right) >= precedence(node)) {
					report(node.right);
				}
			},

			BinaryExpression: dryBinaryLogical,
			CallExpression: dryCallNew,

			ConditionalExpression: function(node) {
				if (isReturnAssignException(node)) {
					return;
				}

				if (hasExcessParens(node.test) && precedence(node.test) >= precedence({type: "LogicalExpression", operator: "||"})) {
					report(node.test);
				}

				if (hasExcessParens(node.consequent) && precedence(node.consequent) >= precedence({type: "AssignmentExpression"})) {
					report(node.consequent);
				}

				if (hasExcessParens(node.alternate) && precedence(node.alternate) >= precedence({type: "AssignmentExpression"})) {
					report(node.alternate);
				}
			},

			DoWhileStatement: function(node) {
				if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
					report(node.test);
				}
			},

			ExpressionStatement: function(node) {
				var firstToken, secondToken, firstTokens;

				if (hasExcessParens(node.expression)) {
					firstTokens = sourceCode.getFirstTokens(node.expression, 2);
					firstToken = firstTokens[0];
					secondToken = firstTokens[1];

					if (!firstToken ||
						firstToken.value !== "{" &&
						(firstToken.value !== "function" || (firstToken.value === 'function' && ALL_NODES)) &&
						firstToken.value !== "class" &&
						(
							firstToken.value !== "let" ||
							!secondToken ||
							secondToken.value !== "["
						)
					) {
						report(node.expression);
					}
				}
			},

			ForInStatement: function(node) {
				if (hasExcessParens(node.right)) {
					report(node.right);
				}
			},

			ForOfStatement: function(node) {
				if (hasExcessParens(node.right)) {
					report(node.right);
				}
			},

			ForStatement: function(node) {
				if (node.init && hasExcessParens(node.init)) {
					report(node.init);
				}

				if (node.test && hasExcessParens(node.test) && !isCondAssignException(node)) {
					report(node.test);
				}

				if (node.update && hasExcessParens(node.update)) {
					report(node.update);
				}
			},

			IfStatement: function(node) {
				if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
					report(node.test);
				}
			},

			LogicalExpression: dryBinaryLogical,

			MemberExpression: function(node) {
				if (
					hasExcessParens(node.object) &&
					precedence(node.object) >= precedence(node) &&
					(
						node.computed ||
						!(
							(node.object.type === "Literal" &&
								typeof node.object.value === "number" &&
								/^[0-9]+$/.test(sourceCode.getFirstToken(node.object).value)) ||

							// RegExp literal is allowed to have parens (#1589)
							(node.object.type === "Literal" && node.object.regex)
						)
					) &&
					!(
						(node.object.type === "FunctionExpression" || node.object.type === "ClassExpression") &&
						isHeadOfExpressionStatement(node) &&
						!hasDoubleExcessParens(node.object)
					)
				) {
					report(node.object);
				}
				if (node.computed && hasExcessParens(node.property)) {
					report(node.property);
				}
			},

			NewExpression: dryCallNew,

			ObjectExpression: function(node) {
				[].forEach.call(node.properties, function(e) {
					var v = e.value;

					if (v && hasExcessParens(v) && precedence(v) >= precedence({
							type: "AssignmentExpression"
						})) {
						report(v);
					}
				});
			},

			ReturnStatement: function(node) {
				var returnToken = sourceCode.getFirstToken(node);

				if (isReturnAssignException(node)) {
					return;
				}

				if (node.argument &&
					hasExcessParensNoLineTerminator(returnToken, node.argument) &&

					// RegExp literal is allowed to have parens (#1589)
					!(node.argument.type === "Literal" && node.argument.regex)) {
					report(node.argument);
				}
			},

			SequenceExpression: function(node) {
				[].forEach.call(node.expressions, function(e) {
					if (hasExcessParens(e) && precedence(e) >= precedence(node)) {
						report(e);
					}
				});
			},

			SwitchCase: function(node) {
				if (node.test && hasExcessParens(node.test)) {
					report(node.test);
				}
			},

			SwitchStatement: function(node) {
				if (hasDoubleExcessParens(node.discriminant)) {
					report(node.discriminant);
				}
			},

			ThrowStatement: function(node) {
				var throwToken = sourceCode.getFirstToken(node);

				if (hasExcessParensNoLineTerminator(throwToken, node.argument)) {
					report(node.argument);
				}
			},

			UnaryExpression: dryUnaryUpdate,
			UpdateExpression: dryUnaryUpdate,

			VariableDeclarator: function(node) {
				if (node.init && hasExcessParens(node.init) &&
					precedence(node.init) >= precedence({
						type: "AssignmentExpression"
					}) &&

					// RegExp literal is allowed to have parens (#1589)
					!(node.init.type === "Literal" && node.init.regex)) {
					report(node.init);
				}
			},

			WhileStatement: function(node) {
				if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
					report(node.test);
				}
			},

			WithStatement: function(node) {
				if (hasDoubleExcessParens(node.object)) {
					report(node.object);
				}
			},

			YieldExpression: function(node) {
				var yieldToken;

				if (node.argument) {
					yieldToken = sourceCode.getFirstToken(node);

					if ((precedence(node.argument) >= precedence(node) &&
							hasExcessParensNoLineTerminator(yieldToken, node.argument)) ||
						hasDoubleExcessParens(node.argument)) {
						report(node.argument);
					}
				}
			}
		};

	};

	module.exports.schema = {
		anyOf: [{
			type: "array",
			items: [{
				enum: ["functions"]
			}],
			minItems: 0,
			maxItems: 1
		},
		{
			type: "array",
			items: [{
				enum: ["all"]
			},
			{
				type: "object",
				properties: {
					conditionalAssign: {
						type: "boolean"
					},
					nestedBinaryExpressions: {
						type: "boolean"
					},
					returnAssign: {
						type: "boolean"
					}
				},
				additionalProperties: false
			}],
			minItems: 0,
			maxItems: 2
		}]
	};

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-invalid-regexp',[
 'acorn/dist/acorn',
 'i18n!javascript/nls/problems',
 'module'
], function (parser, ProblemMessages, module) {
/**
 * @fileoverview Validate strings passed to the RegExp constructor
 * @author Michael Ficarra
 * @copyright 2014 Michael Ficarra. All rights reserved.
 */


//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {

	var options = context.options[0],
		allowedFlags = "";

	if (options && options.allowConstructorFlags) {
		allowedFlags = options.allowConstructorFlags.join("");
	}

	/**
	 * Check if node is a string
	 * @param {ASTNode} node node to evaluate
	 * @returns {boolean} True if its a string
	 * @private
	 */
	function isString(node) {
		return node && node.type === "Literal" && typeof node.value === "string";
	}

	/**
	 * Validate strings passed to the RegExp constructor
	 * @param {ASTNode} node node to evaluate
	 * @returns {void}
	 * @private
	 */
	function check(node) {
		if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(node.arguments[0])) {
			var flags = isString(node.arguments[1]) ? node.arguments[1].value : "";

			if (allowedFlags) {
				flags = flags.replace(new RegExp("[" + allowedFlags + "]", "gi"), "");
			}

			try {
				void new RegExp(node.arguments[0].value);
			} catch (e) {
				context.report(node, e.message);
			}
			if (flags) {
				try {
					var result = parser.parse("/./" + flags, context.parserOptions);
					if (result.body.length > 0) {
						var expressionStatement = result.body[0];
						var regexp = expressionStatement.expression;
						if (regexp && regexp.value === null) {
							context.report(node, ProblemMessages.noInvalidRegexp, {
								arg: flags
							});
						}
					}
				} catch (ex) {
					context.report(node, ProblemMessages.noInvalidRegexp, {
						arg: flags
					});
				}
			}
		}
	}

	return {
		CallExpression: check,
		NewExpression: check
	};

};
module.exports.schema = [{
	type: "object",
	properties: {
		allowConstructorFlags: {
			type: "array",
			items: {
				type: "string"
			}
		}
	},
	additionalProperties: false
}];

return module.exports;
});

/*eslint-env amd */
define('eslint/lib/rules/no-negated-in-lhs',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
/**
 * @fileoverview A rule to disallow negated left operands of the `in` operator
 * @author Michael Ficarra
 */



//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {

    return {

        "BinaryExpression": function(node) {
            if (node.operator === "in" && node.left.type === "UnaryExpression" && node.left.operator === "!") {
                context.report(node, ProblemMessages.NoNegatedInLhs);
            }
        }
    };

};

module.exports.schema = [];

return module.exports;
});

/*eslint-env amd */
define('eslint/lib/rules/no-obj-calls',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	/**
	 * @fileoverview Rule to flag use of an object property of the global object (Math and JSON) as a function
	 * @author James Allardice
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {

		return {
			"CallExpression": function(node) {

				if (node.callee.type === "Identifier") {
					var name = node.callee.name;
					if (name === "Math" || name === "JSON") {
						context.report(node, ProblemMessages.noObjCalls, {
							name: name
						});
					}
				}
			}
		};

	};

	module.exports.schema = [];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-self-compare',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	/**
	 * @fileoverview Rule to flag comparison where left part is the same as the right
	 * part.
	 * @author Ilya Volodin
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {

		return {

			"BinaryExpression": function(node) {
				var operators = ["===", "==", "!==", "!=", ">", "<", ">=", "<="];
				if (operators.indexOf(node.operator) > -1 &&
					(node.left.type === "Identifier" && node.right.type === "Identifier" && node.left.name === node.right.name ||
						node.left.type === "Literal" && node.right.type === "Literal" && node.left.value === node.right.value)) {
					context.report(node, ProblemMessages.noSelfCompare);
				}
			}
		};

	};

	module.exports.schema = [];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-irregular-whitespace',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	/**
	 * @fileoverview Rule to disalow whitespace that is not a tab or space, whitespace inside strings and comments are allowed
	 * @author Jonathan Kingston
	 * @copyright 2014 Jonathan Kingston. All rights reserved.
	 */

	"use strict";
	var ALL_IRREGULARS = /[\f\v\u0085\u00A0\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000\u2028\u2029]/;
	var IRREGULAR_WHITESPACE = /[\f\v\u0085\u00A0\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000]+/mg;
	var IRREGULAR_LINE_TERMINATORS = /[\u2028\u2029]/mg;
	var LINE_BREAK = /\r\n|\r|\n|\u2028|\u2029/g;

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {
		// Module store of errors that we have found
		var errors = [];

		// Comment nodes.  We accumulate these as we go, so we can be sure to trigger them after the whole `Program` entity is parsed, even for top-of-file comments.
		var commentNodes = [];

		// Lookup the `skipComments` option, which defaults to `false`.
		var options = context.options[0] || {};
		var skipComments = Boolean(options.skipComments);
		var skipStrings = options.skipStrings !== false;
		var skipRegExps = Boolean(options.skipRegExps);
		var skipTemplates = Boolean(options.skipTemplates);

		var sourceCode = context.getSourceCode();

		/**
		 * Removes errors that occur inside a string node
		 * @param {ASTNode} node to check for matching errors.
		 * @returns {void}
		 * @private
		 */
		function removeWhitespaceError(node) {
			var locStart = node.loc.start;
			var locEnd = node.loc.end;

			errors = errors.filter(function(error) {
				var errorLoc = error[1];

				if (errorLoc.line >= locStart.line && errorLoc.line <= locEnd.line) {
					if (errorLoc.column >= locStart.column && (errorLoc.column <= locEnd.column || errorLoc.line < locEnd.line)) {
						return false;
					}
				}
				return true;
			});
		}

		/**
		 * Checks identifier or literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
		 * @param {ASTNode} node to check for matching errors.
		 * @returns {void}
		 * @private
		 */
		function removeInvalidNodeErrorsInIdentifierOrLiteral(node) {
			var shouldCheckStrings = skipStrings && (typeof node.value === "string");
			var shouldCheckRegExps = skipRegExps && (node.value instanceof RegExp);

			if (shouldCheckStrings || shouldCheckRegExps) {

				// If we have irregular characters remove them from the errors list
				if (ALL_IRREGULARS.test(node.raw)) {
					removeWhitespaceError(node);
				}
			}
		}

		/**
		 * Checks template string literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
		 * @param {ASTNode} node to check for matching errors.
		 * @returns {void}
		 * @private
		 */
		function removeInvalidNodeErrorsInTemplateLiteral(node) {
			if (typeof node.value.raw === "string") {
				if (ALL_IRREGULARS.test(node.value.raw)) {
					removeWhitespaceError(node);
				}
			}
		}

		/**
		 * Checks comment nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
		 * @param {ASTNode} node to check for matching errors.
		 * @returns {void}
		 * @private
		 */
		function removeInvalidNodeErrorsInComment(node) {
			if (ALL_IRREGULARS.test(node.value)) {
				removeWhitespaceError(node);
			}
		}

		/**
		 * Checks the program source for irregular whitespace
		 * @param {ASTNode} node The program node
		 * @returns {void}
		 * @private
		 */
		function checkForIrregularWhitespace(node) {
			var sourceLines = sourceCode.lines;

			sourceLines.forEach(function(sourceLine, lineIndex) {
				var lineNumber = lineIndex + 1,
					location,
					match;

				while ((match = IRREGULAR_WHITESPACE.exec(sourceLine)) !== null) {
					location = {
						line: lineNumber,
						column: match.index
					};

					errors.push([node, location, ProblemMessages.noIrregularWhitespaces]);
				}
			});
		}

		/**
		 * Checks the program source for irregular line terminators
		 * @param {ASTNode} node The program node
		 * @returns {void}
		 * @private
		 */
		function checkForIrregularLineTerminators(node) {
			var source = sourceCode.getText(),
				sourceLines = sourceCode.lines,
				linebreaks = source.match(LINE_BREAK),
				lastLineIndex = -1,
				lineIndex,
				location,
				match;

			while ((match = IRREGULAR_LINE_TERMINATORS.exec(source)) !== null) {
				lineIndex = linebreaks.indexOf(match[0], lastLineIndex + 1) || 0;

				location = {
					line: lineIndex + 1,
					column: sourceLines[lineIndex].length
				};

				errors.push([node, location, ProblemMessages.noIrregularWhitespaces]);
				lastLineIndex = lineIndex;
			}
		}

		/**
		 * Stores a comment node (`LineComment` or `BlockComment`) for later stripping of errors within; a necessary deferring of processing to deal with top-of-file comments.
		 * @param {ASTNode} node The comment node
		 * @returns {void}
		 * @private
		 */
		function rememberCommentNode(node) {
			commentNodes.push(node);
		}

		/**
		 * A no-op function to act as placeholder for comment accumulation when the `skipComments` option is `false`.
		 * @returns {void}
		 * @private
		 */
		function noop() {}

		var nodes = {};

		if (ALL_IRREGULARS.test(sourceCode.getText())) {
			nodes.Program = function(node) {

				/*
				 * As we can easily fire warnings for all white space issues with
				 * all the source its simpler to fire them here.
				 * This means we can check all the application code without having
				 * to worry about issues caused in the parser tokens.
				 * When writing this code also evaluating per node was missing out
				 * connecting tokens in some cases.
				 * We can later filter the errors when they are found to be not an
				 * issue in nodes we don't care about.
				 */

				checkForIrregularWhitespace(node);
				checkForIrregularLineTerminators(node);
			};

			nodes.Identifier = removeInvalidNodeErrorsInIdentifierOrLiteral;
			nodes.Literal = removeInvalidNodeErrorsInIdentifierOrLiteral;
			nodes.TemplateElement = skipTemplates ? removeInvalidNodeErrorsInTemplateLiteral : noop;
			nodes.LineComment = skipComments ? rememberCommentNode : noop;
			nodes.BlockComment = skipComments ? rememberCommentNode : noop;
			nodes["Program:exit"] = function() {

				if (skipComments) {

					// First strip errors occurring in comment nodes.  We have to do this post-`Program` to deal with top-of-file comments.
					commentNodes.forEach(removeInvalidNodeErrorsInComment);
				}

				// If we have any errors remaining report on them
				errors.forEach(function(error) {
					context.report.apply(context, error);
				});
			};
		} else {
			nodes.Program = noop;
		}

		return nodes;
	};

	module.exports.schema = [{
		type: "object",
		properties: {
			skipComments: {
				type: "boolean"
			},
			skipStrings: {
				type: "boolean"
			},
			skipTemplates: {
				type: "boolean"
			},
			skipRegExps: {
				type: "boolean"
			}
		},
		additionalProperties: false
	}];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-const-assign',[
	'i18n!javascript/nls/problems',
	'module'
], function (ProblemMessages, module) {
	/**
	 * @fileoverview Rule to forbid control charactes from regular expressions.
	 * @author Nicholas C. Zakas
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {
		/**
		 * Checks reference if is non initializer and writable.
		 * @param {Reference} reference - A reference to check.
		 * @param {int} index - The index of the reference in the references.
		 * @param {Reference[]} references - The array that the reference belongs to.
		 * @returns {boolean} Success/Failure
		 * @private
		 */
		function isModifyingReference(reference, index, references) {
			var identifier = reference.identifier;

			return identifier &&
				reference.init === false &&
				reference.isWrite() &&
				// Destructuring assignments can have multiple default value,
				// so possibly there are multiple writeable references for the same identifier.
				(index === 0 || references[index - 1].identifier !== identifier
			);
		}

		function getModifyingReferences(references) {
			return references.filter(isModifyingReference);
		}

		/**
		 * Finds and reports references that are non initializer and writable.
		 * @param {Variable} variable - A variable to check.
		 * @returns {void}
		 */
		function checkVariable(variable) {
			getModifyingReferences(variable.references).forEach(function(reference) {
				context.report(
					reference.identifier,
					ProblemMessages.noConstantAssign,
					{
						name: reference.identifier.name
					});
			});
		}

		return {
			VariableDeclaration: function(node) {
				if (node.kind === "const") {
					context.getDeclaredVariables(node).forEach(checkVariable);
				}
			}
		};

	};
	module.exports.schema = [];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-implicit-coercion',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
/**
 * @fileoverview Rule to flag comparisons to null without a type-checking
 * operator.
 * @author Ian Christian Myers
 */



//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {
	var INDEX_OF_PATTERN = /^(?:i|lastI)ndexOf$/;

	/**
	 * Parses and normalizes an option object.
	 * @param {object} options - An option object to parse.
	 * @returns {object} The parsed and normalized option object.
	 */
	function parseOptions(options) {
		options = options || {};
		return {
			boolean: "boolean" in options ? Boolean(options.boolean) : true,
			number: "number" in options ? Boolean(options.number) : true,
			string: "string" in options ? Boolean(options.string) : true
		};
	}

	/**
	 * Checks whether or not a node is a double logical negating.
	 * @param {ASTNode} node - An UnaryExpression node to check.
	 * @returns {boolean} Whether or not the node is a double logical negating.
	 */
	function isDoubleLogicalNegating(node) {
		return node.operator === "!" &&
			node.argument.type === "UnaryExpression" &&
			node.argument.operator === "!";
	}

	/**
	 * Checks whether or not a node is a binary negating of `.indexOf()` method calling.
	 * @param {ASTNode} node - An UnaryExpression node to check.
	 * @returns {boolean} Whether or not the node is a binary negating of `.indexOf()` method calling.
	 */
	function isBinaryNegatingOfIndexOf(node) {
		return node.operator === "~" &&
			node.argument.type === "CallExpression" &&
			node.argument.callee.type === "MemberExpression" &&
			node.argument.callee.property.type === "Identifier" &&
			INDEX_OF_PATTERN.test(node.argument.callee.property.name);
	}

	/**
	 * Checks whether or not a node is a multiplying by one.
	 * @param {BinaryExpression} node - A BinaryExpression node to check.
	 * @returns {boolean} Whether or not the node is a multiplying by one.
	 */
	function isMultiplyByOne(node) {
		return node.operator === "*" && (
			node.left.type === "Literal" && node.left.value === 1 ||
			node.right.type === "Literal" && node.right.value === 1
		);
	}

	/**
	 * Checks whether the result of a node is numeric or not
	 * @param {ASTNode} node The node to test
	 * @returns {boolean} true if the node is a number literal or a `Number()`, `parseInt` or `parseFloat` call
	 */
	function isNumeric(node) {
		return node.type === "Literal"
			&& typeof node.value === "number"
			|| node.type === "CallExpression"
			&& (node.callee.name === "Number" ||
				node.callee.name === "parseInt" ||
				node.callee.name === "parseFloat");
	}

	/**
	 * Returns the first non-numeric operand in a BinaryExpression. Designed to be
	 * used from bottom to up since it walks up the BinaryExpression trees using
	 * node.parent to find the result.
	 * @param {BinaryExpression} node The BinaryExpression node to be walked up on
	 * @returns {ASTNode|
	} The first non-numeric item in the BinaryExpression tree or 

	 */
	function getNonNumericOperand(node) {
		var left = node.left,
			right = node.right;

		if (right.type !== "BinaryExpression" && !isNumeric(right)) {
			return right;
		}

		if (left.type !== "BinaryExpression" && !isNumeric(left)) {
			return left;
		}
	}

	/**
	 * Checks whether or not a node is a concatenating with an empty string.
	 * @param {ASTNode} node - A BinaryExpression node to check.
	 * @returns {boolean} Whether or not the node is a concatenating with an empty string.
	 */
	function isConcatWithEmptyString(node) {
		return node.operator === "+" && (
			(node.left.type === "Literal" && node.left.value === "") ||
			(node.right.type === "Literal" && node.right.value === "")
		);
	}

	/**
	 * Checks whether or not a node is appended with an empty string.
	 * @param {ASTNode} node - An AssignmentExpression node to check.
	 * @returns {boolean} Whether or not the node is appended with an empty string.
	 */
	function isAppendEmptyString(node) {
		return node.operator === "+=" && node.right.type === "Literal" && node.right.value === "";
	}

	/**
	 * Gets a node that is the left or right operand of a node, is not the specified literal.
	 * @param {ASTNode} node - A BinaryExpression node to get.
	 * @param {any} value - A literal value to check.
	 * @returns {ASTNode} A node that is the left or right operand of the node, is not the specified literal.
	 */
	function getOtherOperand(node, value) {
		if (node.left.type === "Literal" && node.left.value === value) {
			return node.right;
		}
		return node.left;
	}
	var options = parseOptions(context.options[0]);

	return {
		"UnaryExpression": function(node) {
			// !!foo
			if (options.boolean && isDoubleLogicalNegating(node)) {
				context.report(
					node,
					ProblemMessages.noImplicitCoercionBoolean,
					{
						code: context.getSource(node.argument.argument)
					});
			}

			// ~foo.indexOf(bar)
			if (options.boolean && isBinaryNegatingOfIndexOf(node)) {
				context.report(
					node,
					ProblemMessages.noImplicitCoercionIndexOf,
					{
						code: context.getSource(node.argument)
					});
			}

			// +foo
			if (options.number && node.operator === "+" && !isNumeric(node.argument)) {
				context.report(
					node,
					ProblemMessages.noImplicitCoercionNumber,
					{
						code: context.getSource(node.argument)
					});
			}
		},

		// Use `:exit` to prevent double reporting
		"BinaryExpression:exit": function(node) {
			// 1 * foo
			var nonNumericOperand = options.number && isMultiplyByOne(node) && getNonNumericOperand(node);
			if (nonNumericOperand) {
				context.report(
					node,
					ProblemMessages.noImplicitCoercionNumber,
					{
						code: context.getSource(nonNumericOperand)
					});
			}

			// "" + foo
			if (options.string && isConcatWithEmptyString(node)) {
				context.report(
					node,
					ProblemMessages.noImplicitCoercionString,
					{
						code: context.getSource(getOtherOperand(node, ""))
					});
			}
		},
		AssignmentExpression: function(node) {
			// foo += ""
			if (options.string && isAppendEmptyString(node)) {
				context.report(
					node,
					ProblemMessages.noImplicitCoercionString2,
					{
						code: context.getSource(getOtherOperand(node, ""))
					});
			}
		}
	};
};

module.exports.schema = [{
	"type": "object",
	"properties": {
		"boolean": {
			"type": "boolean"
		},
		"number": {
			"type": "boolean"
		},
		"string": {
			"type": "boolean"
		}
	},
	"additionalProperties": false
}];

return module.exports;
});

/*eslint-env amd */
define('eslint/lib/rules/no-extra-bind',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	/**
	 * @fileoverview Rule to forbid control charactes from regular expressions.
	 * @author Nicholas C. Zakas
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {
		var scopeInfo = null;

		/**
		 * Reports a given function node.
		 *
		 * @param {ASTNode} node - A node to report. This is a FunctionExpression or
		 *      an ArrowFunctionExpression.
		 * @returns {void}
		 */
		function report(node) {
			var nodeParent = node.parent; // memberexpression
			context.report(
				nodeParent.property,
				ProblemMessages.noExtraBind);
		}

		/**
		 * Gets the property name of a given node.
		 * If the property name is dynamic, this returns an empty string.
		 *
		 * @param {ASTNode} node - A node to check. This is a MemberExpression.
		 * @returns {string} The property name of the node.
		 */
		function getPropertyName(node) {
			if (node.computed) {
				switch (node.property.type) {
					case "Literal":
						return String(node.property.value);
					case "TemplateLiteral":
						if (node.property.expressions.length === 0) {
							return node.property.quasis[0].value.cooked;
						}
						// fallthrough
					default:
						return false;
				}
			}
			return node.property.name;
		}

		/**
		 * Checks whether or not a given function node is the callee of `.bind()`
		 * method.
		 *
		 * e.g. `(function() {}.bind(foo))`
		 *
		 * @param {ASTNode} node - A node to report. This is a FunctionExpression or
		 *      an ArrowFunctionExpression.
		 * @returns {boolean} `true` if the node is the callee of `.bind()` method.
		 */
		function isCalleeOfBindMethod(node) {
			var parent = node.parent;
			var grandparent = parent.parent;

			return grandparent &&
				grandparent.type === "CallExpression" &&
				grandparent.callee === parent &&
				grandparent.arguments.length === 1 &&
				parent.type === "MemberExpression" &&
				parent.object === node &&
				getPropertyName(parent) === "bind";
		}

		/**
		 * Adds a scope information object to the stack.
		 *
		 * @param {ASTNode} node - A node to add. This node is a FunctionExpression
		 *      or a FunctionDeclaration node.
		 * @returns {void}
		 */
		function enterFunction(node) {
			scopeInfo = {
				isBound: isCalleeOfBindMethod(node),
				thisFound: false,
				upper: scopeInfo
			};
		}

		/**
		 * Removes the scope information object from the top of the stack.
		 * At the same time, this reports the function node if the function has
		 * `.bind()` and the `this` keywords found.
		 *
		 * @param {ASTNode} node - A node to remove. This node is a
		 *      FunctionExpression or a FunctionDeclaration node.
		 * @returns {void}
		 */
		function exitFunction(node) {
			if (scopeInfo.isBound && !scopeInfo.thisFound) {
				report(node);
			}

			scopeInfo = scopeInfo.upper;
		}

		/**
		 * Reports a given arrow function if the function is callee of `.bind()`
		 * method.
		 *
		 * @param {ASTNode} node - A node to report. This node is an
		 *      ArrowFunctionExpression.
		 * @returns {void}
		 */
		function exitArrowFunction(node) {
			if (isCalleeOfBindMethod(node)) {
				report(node);
			}
		}

		/**
		 * Set the mark as the `this` keyword was found in this scope.
		 *
		 * @returns {void}
		 */
		function markAsThisFound() {
			if (scopeInfo) {
				scopeInfo.thisFound = true;
			}
		}

		return {
			"ArrowFunctionExpression:exit": exitArrowFunction,
			"FunctionDeclaration": enterFunction,
			"FunctionDeclaration:exit": exitFunction,
			"FunctionExpression": enterFunction,
			"FunctionExpression:exit": exitFunction,
			"ThisExpression": markAsThisFound
		};
	};

	module.exports.schema = [];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-extend-native',[
	'eslint/conf/globals',
	'i18n!javascript/nls/problems',
	'module',
], function(globals, ProblemMessages, module) {
	/**
	 * @fileoverview Rule to flag comparisons to null without a type-checking
	 * operator.
	 * @author Ian Christian Myers
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {

		var config = context.options[0] || {};
		var exceptions = config.exceptions || [];
		var modifiedBuiltins = Object.keys(globals.builtin).filter(function(builtin) {
			return builtin[0].toUpperCase() === builtin[0];
		});

		if (exceptions.length) {
			modifiedBuiltins = modifiedBuiltins.filter(function(builtIn) {
				return exceptions.indexOf(builtIn) === -1;
			});
		}

		return {

			// handle the Array.prototype.extra style case
			"AssignmentExpression": function(node) {
				var lhs = node.left,
					affectsProto;

				if (lhs.type !== "MemberExpression" || lhs.object.type !== "MemberExpression") {
					return;
				}

				affectsProto = lhs.object.computed ?
					lhs.object.property.type === "Literal" && lhs.object.property.value === "prototype" :
					lhs.object.property.name === "prototype";

				if (!affectsProto) {
					return;
				}

				modifiedBuiltins.forEach(function(builtin) {
					if (lhs.object.object.name === builtin) {
						context.report(node, ProblemMessages.noExtendNative, {builtin: builtin});
					}
				});
			},

			// handle the Object.definePropert[y|ies](Array.prototype) case
			"CallExpression": function(node) {

				var callee = node.callee,
					subject,
					object;

				// only worry about Object.definePropert[y|ies]
				if (callee.type === "MemberExpression"
						&& callee.object.name === "Object"
						&& (callee.property.name === "defineProperty" || callee.property.name === "defineProperties")) {

					// verify the object being added to is a native prototype
					subject = node.arguments[0];
					object = subject.object;

					if (object &&
						object.type === "Identifier" &&
						modifiedBuiltins.indexOf(object.name) > -1 &&
						subject.property.name === "prototype") {

						context.report(node, ProblemMessages.noExtendNative, {builtin: object.name});
					}
				}

			}
		};

	};

	module.exports.schema = [{
		"type": "object",
		"properties": {
			"exceptions": {
				"type": "array",
				"items": {
					"type": "string"
				},
				"uniqueItems": true
			}
		},
		"additionalProperties": false
	}];


	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-lone-blocks',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	/**
	 * @fileoverview Rule to forbid control charactes from regular expressions.
	 * @author Nicholas C. Zakas
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {

		// A stack of lone blocks to be checked for block-level bindings
		var loneBlocks = [],
			ruleDef;

		/**
		 * Reports a node as invalid.
		 * @param {ASTNode} node - The node to be reported.
		 * @returns {void}
		 */
		function report(node) {
			var parent = context.getAncestors().pop();

			context.report(node, parent.type === "Program" ?
				ProblemMessages.noLoneBlock :
				ProblemMessages.noLoneNestedBlock
			);
		}

		/**
		 * Checks for any ocurrence of BlockStatement > BlockStatement or Program > BlockStatement
		 * @returns {boolean} True if the current node is a lone block.
		 */
		function isLoneBlock() {
			var parent = context.getAncestors().pop();

			return parent.type === "BlockStatement" || parent.type === "Program";
		}

		/**
		 * Checks the enclosing block of the current node for block-level bindings,
		 * and "marks it" as valid if any.
		 * @returns {void}
		 */
		function markLoneBlock() {
			if (loneBlocks.length === 0) {
				return;
			}

			var block = context.getAncestors().pop();

			if (loneBlocks[loneBlocks.length - 1] === block) {
				loneBlocks.pop();
			}
		}

		// Default rule definition: report all lone blocks
		ruleDef = {
			BlockStatement: function(node) {
				if (isLoneBlock(node)) {
					report(node);
				}
			}
		};

		// ES6: report blocks without block-level bindings
		if (context.parserOptions.ecmaVersion >= 6) {
			ruleDef = {
				BlockStatement: function(node) {
					if (isLoneBlock(node)) {
						loneBlocks.push(node);
					}
				},
				"BlockStatement:exit": function(node) {
					if (loneBlocks.length > 0 && loneBlocks[loneBlocks.length - 1] === node) {
						loneBlocks.pop();
						report(node);
					}
				}
			};

			ruleDef.VariableDeclaration = function(node) {
				if (node.kind === "let" || node.kind === "const") {
					markLoneBlock(node);
				}
			};

			ruleDef.FunctionDeclaration = function(node) {
				if (context.getScope().isStrict) {
					markLoneBlock(node);
				}
			};

			ruleDef.ClassDeclaration = markLoneBlock;
		}

		return ruleDef;
	};

	module.exports.schema = [];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/quotes',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	/**
	 * @fileoverview Rule to flag use of an object property of the global object (Math and JSON) as a function
	 * @author James Allardice
	 */

	"use strict";

	var QUOTE_SETTINGS = {
		double: {
			quote: "\"",
			alternateQuote: "'",
			description: "doublequote",
			nlsDescription: ProblemMessages.doublequote
		},
		single: {
			quote: "'",
			alternateQuote: "\"",
			description: "singlequote",
			nlsDescription: ProblemMessages.singlequote
		},
		backtick: {
			quote: "`",
			alternateQuote: "\"",
			description: "backtick",
			nlsDescription: ProblemMessages.backtick
		}
	};

	var AVOID_ESCAPE = "avoid-escape",
		FUNCTION_TYPE = /^(?:Arrow)?Function(?:Declaration|Expression)$/;

	/**
	 * Validate that a string passed in is surrounded by the specified character
	 * @param  {string} val The text to check.
	 * @param  {string} character The character to see if it's surrounded by.
	 * @returns {boolean} True if the text is surrounded by the character, false if not.
	 * @private
	 */
	function isSurroundedBy(val, character) {
		return val[0] === character && val[val.length - 1] === character;
	}

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {

		var quoteOption = context.options[0],
			settings = QUOTE_SETTINGS[quoteOption || "double"],
			options = context.options[1],
			avoidEscape = options && options.avoidEscape === true,
			allowTemplateLiterals = options && options.allowTemplateLiterals === true;

		// deprecated
		if (options === AVOID_ESCAPE) {
			avoidEscape = true;
		}

		/**
		 * Determines if a given node is part of JSX syntax.
		 * @param {ASTNode} node The node to check.
		 * @returns {boolean} True if the node is a JSX node, false if not.
		 * @private
		 */
		function isJSXElement(node) {
			return node.type.indexOf("JSX") === 0;
		}

		/**
		 * Checks whether or not a given node is a directive.
		 * The directive is a `ExpressionStatement` which has only a string literal.
		 * @param {ASTNode} node - A node to check.
		 * @returns {boolean} Whether or not the node is a directive.
		 * @private
		 */
		function isDirective(node) {
			return node.type === "ExpressionStatement" &&
				node.expression.type === "Literal" &&
				typeof node.expression.value === "string";
		}

		/**
		 * Checks whether or not a given node is a part of directive prologues.
		 * See also: http://www.ecma-international.org/ecma-262/6.0/#sec-directive-prologues-and-the-use-strict-directive
		 * @param {ASTNode} node - A node to check.
		 * @returns {boolean} Whether or not the node is a part of directive prologues.
		 * @private
		 */
		function isPartOfDirectivePrologue(node) {
			var block = node.parent.parent;

			if (block.type !== "Program" && (block.type !== "BlockStatement" || !FUNCTION_TYPE.test(block.parent.type))) {
				return false;
			}

			// Check the node is at a prologue.
			for (var i = 0; i < block.body.length; ++i) {
				var statement = block.body[i];

				if (statement === node.parent) {
					return true;
				}
				if (!isDirective(statement)) {
					break;
				}
			}

			return false;
		}

		/**
		 * Checks whether or not a given node is allowed as non backtick.
		 * @param {ASTNode} node - A node to check.
		 * @returns {boolean} Whether or not the node is allowed as non backtick.
		 * @private
		 */
		function isAllowedAsNonBacktick(node) {
			var parent = node.parent;

			switch (parent.type) {

				// Directive Prologues.
				case "ExpressionStatement":
					return isPartOfDirectivePrologue(node);

					// LiteralPropertyName.
				case "Property":
					return parent.key === node && !parent.computed;

					// ModuleSpecifier.
				case "ImportDeclaration":
				case "ExportNamedDeclaration":
				case "ExportAllDeclaration":
					return parent.source === node;

					// Others don't allow.
				default:
					return false;
			}
		}

		return {

			Literal: function(node) {
				var val = node.value,
					rawVal = node.raw,
					isValid;

				if (settings && typeof val === "string") {
					isValid = (quoteOption === "backtick" && isAllowedAsNonBacktick(node)) ||
						isJSXElement(node.parent) ||
						isSurroundedBy(rawVal, settings.quote);

					if (!isValid && avoidEscape) {
						isValid = isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.indexOf(settings.quote) >= 0;
					}

					if (!isValid) {
						var data = Object.create(null);
						data.quote = QUOTE_SETTINGS[quoteOption].quote;
						context.report(node, settings.nlsDescription, {data: data});
					}
				}
			},

			TemplateLiteral: function(node) {

				// If backticks are expected or it's a tagged template, then this shouldn't throw an errors
				if (allowTemplateLiterals || quoteOption === "backtick" || node.parent.type === "TaggedTemplateExpression") {
					return;
				}

				var shouldWarn = node.quasis.length === 1 && node.quasis[0].value.cooked.indexOf("\n") === -1;

				if (shouldWarn) {
					var data = Object.create(null);
					data.quote = QUOTE_SETTINGS[quoteOption].quote;
					data.oldQuote = '`';
					context.report(node, settings.nlsDescription, {data: data});
				}
			}
		};

	};

	module.exports.schema = [
		{
			"enum": ["single", "double", "backtick"]
		},
		{
			anyOf: [{
				"enum": ["avoid-escape"]
			},
			{
				type: "object",
				properties: {
					avoidEscape: {
						type: "boolean"
					},
					allowTemplateLiterals: {
						type: "boolean"
					}
				},
				additionalProperties: false
			}]
		}
	];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/yoda',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	/**
	 * @fileoverview Rule to flag use of an object property of the global object (Math and JSON) as a function
	 * @author James Allardice
	 */

	"use strict";

	//--------------------------------------------------------------------------
	// Helpers
	//--------------------------------------------------------------------------

	/**
	 * Determines whether an operator is a comparison operator.
	 * @param {String} operator The operator to check.
	 * @returns {boolean} Whether or not it is a comparison operator.
	 */
	function isComparisonOperator(operator) {
		return (/^(==|===|!=|!==|<|>|<=|>=)$/).test(operator);
	}

	/**
	 * Determines whether an operator is an equality operator.
	 * @param {String} operator The operator to check.
	 * @returns {boolean} Whether or not it is an equality operator.
	 */
	function isEqualityOperator(operator) {
		return (/^(==|===)$/).test(operator);
	}

	/**
	 * Determines whether an operator is one used in a range test.
	 * Allowed operators are `<` and `<=`.
	 * @param {String} operator The operator to check.
	 * @returns {boolean} Whether the operator is used in range tests.
	 */
	function isRangeTestOperator(operator) {
		return ["<", "<="].indexOf(operator) >= 0;
	}

	/**
	 * Determines whether a non-Literal node is a negative number that should be
	 * treated as if it were a single Literal node.
	 * @param {ASTNode} node Node to test.
	 * @returns {boolean} True if the node is a negative number that looks like a
	 *                    real literal and should be treated as such.
	 */
	function looksLikeLiteral(node) {
		return node.type === "UnaryExpression" &&
			node.operator === "-" &&
			node.prefix &&
			node.argument.type === "Literal" &&
			typeof node.argument.value === "number";
	}

	/**
	 * Attempts to derive a Literal node from nodes that are treated like literals.
	 * @param {ASTNode} node Node to normalize.
	 * @returns {ASTNode} The original node if the node is already a Literal, or a
	 *                    normalized Literal node with the negative number as the
	 *                    value if the node represents a negative number literal,
	 *                    otherwise null if the node cannot be converted to a
	 *                    normalized literal.
	 */
	function getNormalizedLiteral(node) {
		if (node.type === "Literal") {
			return node;
		}

		if (looksLikeLiteral(node)) {
			return {
				type: "Literal",
				value: -node.argument.value,
				raw: "-" + node.argument.value
			};
		}

		return null;
	}

	/**
	 * Checks whether two expressions reference the same value. For example:
	 *     a = a
	 *     a.b = a.b
	 *     a[0] = a[0]
	 *     a['b'] = a['b']
	 * @param   {ASTNode} a Left side of the comparison.
	 * @param   {ASTNode} b Right side of the comparison.
	 * @returns {boolean}   True if both sides match and reference the same value.
	 */
	function same(a, b) {
		if (a.type !== b.type) {
			return false;
		}

		switch (a.type) {
			case "Identifier":
				return a.name === b.name;

			case "Literal":
				return a.value === b.value;

			case "MemberExpression":

				// x[0] = x[0]
				// x[y] = x[y]
				// x.y = x.y
				return same(a.object, b.object) && same(a.property, b.property);

			case "ThisExpression":
				return true;

			default:
				return false;
		}
	}

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {

		// Default to "never" (!always) if no option
		var always = context.options[0] === "always";
		var exceptRange = context.options[1] && context.options[1].exceptRange;
		var onlyEquality = context.options[1] && context.options[1].onlyEquality;

		var sourceCode = context.getSourceCode();

		/**
		 * Determines whether node represents a range test.
		 * A range test is a "between" test like `(0 <= x && x < 1)` or an "outside"
		 * test like `(x < 0 || 1 <= x)`. It must be wrapped in parentheses, and
		 * both operators must be `<` or `<=`. Finally, the literal on the left side
		 * must be less than or equal to the literal on the right side so that the
		 * test makes any sense.
		 * @param {ASTNode} node LogicalExpression node to test.
		 * @returns {Boolean} Whether node is a range test.
		 */
		function isRangeTest(node) {
			var left = node.left,
				right = node.right;

			/**
			 * Determines whether node is of the form `0 <= x && x < 1`.
			 * @returns {Boolean} Whether node is a "between" range test.
			 */
			function isBetweenTest() {
				var leftLiteral, rightLiteral;

				return node.operator === "&&" &&
					(leftLiteral = getNormalizedLiteral(left.left)) &&
					(rightLiteral = getNormalizedLiteral(right.right)) &&
					leftLiteral.value <= rightLiteral.value &&
					same(left.right, right.left);
			}

			/**
			 * Determines whether node is of the form `x < 0 || 1 <= x`.
			 * @returns {Boolean} Whether node is an "outside" range test.
			 */
			function isOutsideTest() {
				var leftLiteral, rightLiteral;

				return node.operator === "||" &&
					(leftLiteral = getNormalizedLiteral(left.right)) &&
					(rightLiteral = getNormalizedLiteral(right.left)) &&
					leftLiteral.value <= rightLiteral.value &&
					same(left.left, right.right);
			}

			/**
			 * Determines whether node is wrapped in parentheses.
			 * @returns {Boolean} Whether node is preceded immediately by an open
			 *                    paren token and followed immediately by a close
			 *                    paren token.
			 */
			function isParenWrapped() {
				var tokenBefore, tokenAfter;

				return (tokenBefore = sourceCode.getTokenBefore(node)) &&
					tokenBefore.value === "(" &&
					(tokenAfter = sourceCode.getTokenAfter(node)) &&
					tokenAfter.value === ")";
			}

			return node.type === "LogicalExpression" &&
				left.type === "BinaryExpression" &&
				right.type === "BinaryExpression" &&
				isRangeTestOperator(left.operator) &&
				isRangeTestOperator(right.operator) &&
				(isBetweenTest() || isOutsideTest()) &&
				isParenWrapped();
		}

		//--------------------------------------------------------------------------
		// Public
		//--------------------------------------------------------------------------

		return {
			BinaryExpression: always ? function(node) {

				// Comparisons must always be yoda-style: if ("blue" === color)
				if (
					(node.right.type === "Literal" || looksLikeLiteral(node.right)) &&
					!(node.left.type === "Literal" || looksLikeLiteral(node.left)) &&
					!(!isEqualityOperator(node.operator) && onlyEquality) &&
					isComparisonOperator(node.operator) &&
					!(exceptRange && isRangeTest(context.getAncestors().pop()))
				) {
					context.report(node, ProblemMessages.yodaLeft, {operator: node.operator});
				}

			} : function(node) {

				// Comparisons must never be yoda-style (default)
				if (
					(node.left.type === "Literal" || looksLikeLiteral(node.left)) &&
					!(node.right.type === "Literal" || looksLikeLiteral(node.right)) &&
					!(!isEqualityOperator(node.operator) && onlyEquality) &&
					isComparisonOperator(node.operator) &&
					!(exceptRange && isRangeTest(context.getAncestors().pop()))
				) {
					context.report(node, ProblemMessages.yodaRight, {operator: node.operator});
				}

			}
		};

	};

	module.exports.schema = [{
		"enum": ["always", "never"]
	}, {
		type: "object",
		properties: {
			exceptRange: {
				type: "boolean"
			},
			onlyEquality: {
				type: "boolean"
			}
		},
		additionalProperties: false
	}];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-param-reassign',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	/**
	 * @fileoverview Rule to flag use of an object property of the global object (Math and JSON) as a function
	 * @author James Allardice
	 */

	"use strict";
	var stopNodePattern = /(?:Statement|Declaration|Function(?:Expression)?|Program)$/;


	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {
		var props = context.options[0] && Boolean(context.options[0].props);

		/**
		 * Checks whether or not the reference modifies properties of its variable.
		 * @param {Reference} reference - A reference to check.
		 * @returns {boolean} Whether or not the reference modifies properties of its variable.
		 */
		function isModifyingProp(reference) {
			var node = reference.identifier;
			var parent = node.parent;

			while (parent && !stopNodePattern.test(parent.type)) {
				switch (parent.type) {

					// e.g. foo.a = 0;
					case "AssignmentExpression":
						return parent.left === node;

					// e.g. ++foo.a;
					case "UpdateExpression":
						return true;

					// e.g. delete foo.a;
					case "UnaryExpression":
						if (parent.operator === "delete") {
							return true;
						}
						break;

					// EXCLUDES: e.g. cache.get(foo.a).b = 0;
					case "CallExpression":
						if (parent.callee !== node) {
							return false;
						}
						break;

					// EXCLUDES: e.g. cache[foo.a] = 0;
					case "MemberExpression":
						if (parent.property === node) {
							return false;
						}
						break;

					default:
						break;
				}

				node = parent;
				parent = node.parent;
			}

			return false;
		}

		/**
		 * Reports a reference if is non initializer and writable.
		 * @param {Reference} reference - A reference to check.
		 * @param {int} index - The index of the reference in the references.
		 * @param {Reference[]} references - The array that the reference belongs to.
		 * @returns {void}
		 */
		function checkReference(reference, index, references) {
			var identifier = reference.identifier;

			if (identifier &&
				!reference.init &&

				// Destructuring assignments can have multiple default value,
				// so possibly there are multiple writeable references for the same identifier.
				(index === 0 || references[index - 1].identifier !== identifier)
			) {
				if (reference.isWrite()) {
					context.report(
						identifier,
						ProblemMessages.noParamReassign,
						{
							name: identifier.name
						});
				} else if (props && isModifyingProp(reference)) {
					context.report(
						identifier,
						ProblemMessages.noParamPropertyReassign,
						{
							name: identifier.name
						});
				}
			}
		}

		/**
		 * Finds and reports references that are non initializer and writable.
		 * @param {Variable} variable - A variable to check.
		 * @returns {void}
		 */
		function checkVariable(variable) {
			if (variable.defs[0].type === "Parameter") {
				variable.references.forEach(checkReference);
			}
		}

		/**
		 * Checks parameters of a given function node.
		 * @param {ASTNode} node - A function node to check.
		 * @returns {void}
		 */
		function checkForFunction(node) {
			context.getDeclaredVariables(node).forEach(checkVariable);
		}

		return {
			// ':exit' is needed for the 'node.parent' property of identifier nodes.
			"FunctionDeclaration:exit": checkForFunction,
			"FunctionExpression:exit": checkForFunction,
			"ArrowFunctionExpression:exit": checkForFunction
		};

	};

	module.exports.schema = [{
		type: "object",
		properties: {
			props: {
				type: "boolean"
			}
		},
		additionalProperties: false
	}];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-native-reassign',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	/**
	 * @fileoverview Rule to flag use of an object property of the global object (Math and JSON) as a function
	 * @author James Allardice
	 */

	"use strict";


	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {
		var config = context.options[0];
		var exceptions = config && config.exceptions || [];

		/**
		 * Reports write references.
		 * @param {Reference} reference - A reference to check.
		 * @param {int} index - The index of the reference in the references.
		 * @param {Reference[]} references - The array that the reference belongs to.
		 * @returns {void}
		 */
		function checkReference(reference, index, references) {
			var identifier = reference.identifier;

			if (reference.init === false &&
				reference.isWrite() &&
				// Destructuring assignments can have multiple default value,
				// so possibly there are multiple writeable references for the same identifier.
				(index === 0 || references[index - 1].identifier !== identifier)
			) {
				context.report({
					node: identifier,
					message: ProblemMessages.noNativeReassign,
					data: {name: identifier.name}
				});
			}
		}

		/**
		 * Reports write references if a given variable is read-only builtin.
		 * @param {Variable} variable - A variable to check.
		 * @returns {void}
		 */
		function checkVariable(variable) {
			if (variable.writeable === false && exceptions.indexOf(variable.name) === -1) {
				variable.references.forEach(checkReference);
			}
		}

		return {
			Program: function() {
				var globalScope = context.getScope();

				globalScope.variables.forEach(checkVariable);
			}
		};
	};

	module.exports.schema = [{
		type: "object",
		properties: {
			exceptions: {
				type: "array",
				items: {
					type: "string"
				},
				uniqueItems: true
			}
		},
		additionalProperties: false
	}];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-unused-expressions',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {
		var config = context.options[0] || {},
			allowShortCircuit = config.allowShortCircuit || false,
			allowTernary = config.allowTernary || false;
			
		var ignore = false;

		/**
		 * @param {ASTNode} node - any node
		 * @returns {boolean} whether the given node structurally represents a directive
		 */
		function looksLikeDirective(node) {
			return node.type === "ExpressionStatement" &&
				node.expression.type === "Literal" && typeof node.expression.value === "string";
		}

		/**
		 * @param {Function} predicate - ([a] -> Boolean) the function used to make the determination
		 * @param {a[]} list - the input list
		 * @returns {a[]} the leading sequence of members in the given list that pass the given predicate
		 */
		function takeWhile(predicate, list) {
			for (var i = 0, l = list.length; i < l; ++i) {
				if (!predicate(list[i])) {
					break;
				}
			}
			return [].slice.call(list, 0, i);
		}

		/**
		 * @param {ASTNode} node - a Program or BlockStatement node
		 * @returns {ASTNode[]} the leading sequence of directive nodes in the given node's body
		 */
		function directives(node) {
			return takeWhile(looksLikeDirective, node.body);
		}

		/**
		 * @param {ASTNode} node - any node
		 * @param {ASTNode[]} ancestors - the given node's ancestors
		 * @returns {boolean} whether the given node is considered a directive in its current position
		 */
		function isDirective(node, ancestors) {
			var parent = ancestors[ancestors.length - 1],
				grandparent = ancestors[ancestors.length - 2];
			return (parent.type === "Program" || parent.type === "BlockStatement" &&
					(/Function/.test(grandparent.type))) &&
				directives(parent).indexOf(node) >= 0;
		}

		/**
		 * Determines whether or not a given node is a valid expression. Recurses on short circuit eval and ternary nodes if enabled by flags.
		 * @param {ASTNode} node - any node
		 * @returns {boolean} whether the given node is a valid expression
		 */
		function isValidExpression(node) {
			if (allowTernary) {
				// Recursive check for ternary and logical expressions
				if (node.type === "ConditionalExpression") {
					return isValidExpression(node.consequent) && isValidExpression(node.alternate);
				}
			}
			if (allowShortCircuit) {
				if (node.type === "LogicalExpression") {
					return isValidExpression(node.right);
				}
			}

			return /^(?:Assignment|Call|New|Update|Yield)Expression$/.test(node.type) ||
				(node.type === "UnaryExpression" && ["delete", "void"].indexOf(node.operator) >= 0);
		}

		return {
			"Program" : function(node) {
				if (node.errors && node.errors.length !== 0) {
					ignore = true;
					return;
				}
			},
			"ExpressionStatement": function(node) {
				if (ignore) return;
				if (!isValidExpression(node.expression)
						&& !isDirective(node, context.getAncestors())) {
					context.report(node, ProblemMessages.noUnusedExpression);
				}
			}
		};

	};

	module.exports.schema = [{
		"type": "object",
		"properties": {
			"allowShortCircuit": {
				"type": "boolean"
			},
			"allowTernary": {
				"type": "boolean"
			}
		},
		"additionalProperties": false
	}];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-invalid-this',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {

	"use strict";
	
	var thisTagPattern = /^[\s\*]*@this/m;
	var bindOrCallOrApplyPattern = /^(?:bind|call|apply)$/;
	var arrayMethodPattern = /^(?:every|filter|find|findIndex|forEach|map|some)$/;
	var anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/;
	var arrayOrTypedArrayPattern = /Array$/;

	/**
	 * Checks whether or not a node has a '@this' tag in its comments.
	 * @param {ASTNode} node - A node to check.
	 * @param {SourceCode} sourceCode - A SourceCode instance to get comments.
	 * @returns {boolean} Whether or not the node has a '@this' tag in its comments.
	 */
	function hasJSDocThisTag(node, sourceCode) {
		var jsdocComment = sourceCode.getJSDocComment(node);
	
		if (jsdocComment && thisTagPattern.test(jsdocComment.value)) {
			return true;
		}
	
		// Checks '@this' in its leading comments for callbacks,
		// because callbacks don't have its JSDoc comment.
		// e.g.
		//     sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
		return sourceCode.getComments(node).leading.some(function(comment) {
			return thisTagPattern.test(comment.value);
		});
	}
	
	/**
	 * Checks whether or not a node is 'Reclect.apply'.
	 * @param {ASTNode} node - A node to check.
	 * @returns {boolean} Whether or not the node is a 'Reclect.apply'.
	 */
	function isReflectApply(node) {
		return node.type === "MemberExpression" &&
			node.object.type === "Identifier" &&
			node.object.name === "Reflect" &&
			node.property.type === "Identifier" &&
			node.property.name === "apply" &&
			node.computed === false;
	}
	
	/**
	 * Checks whether or not a node is 'Array.from'.
	 * *@param {ASTNode} node - A node to check.
	 * @returns {boolean} Whether or not the node is a 'Array.from'.*/

	function isArrayFromMethod(node) {
		return node.type === "MemberExpression" &&
			node.object.type === "Identifier" &&
			arrayOrTypedArrayPattern.test(node.object.name) &&
			node.property.type === "Identifier" &&
			node.property.name === "from" &&
			node.computed === false;
	}

	/**
	 * Checks whether or not a node is a method which has `thisArg`.
	 * @param {ASTNode} node - A node to check.
	 * @returns {boolean} Whether or not the node is a method which has `thisArg`.
	 */
	function isMethodWhichHasThisArg(node) {
		var currentNode = node;
		while (currentNode) {
			if (currentNode.type === "Identifier") {
				return arrayMethodPattern.test(currentNode.name);
			}
			if (currentNode.type === "MemberExpression" && !currentNode.computed) {
				currentNode = currentNode.property;
				continue;
			}
	
			break;
		}
	
		return false;
	}
	
	/**
	 * Checks whether or not a node is a constructor.
	 * @param {ASTNode} node - A function node to check.
	 * @returns {boolean} Wehether or not a node is a constructor.
	 */
	function isES5Constructor(node) {
		return node.id &&
			node.id.name[0] !== node.id.name[0].toLocaleLowerCase();
	}
	/**
	 * Finds a function node from ancestors of a node.
	 * @param {ASTNode} node - A start node to find.
	 * @returns {Node|null} A found function node.
	 */
	function getUpperFunction(node) {
		var currentNode = node;
		while (currentNode) {
			if (anyFunctionPattern.test(currentNode.type)) {
				return currentNode;
			}
			currentNode = currentNode.parent;
		}
		return null;
	}

	/**
	 * Checks whether or not a node is 'null' or 'undefined'.
	 * 
	 * @param {ASTNode} node - A node to check.
	 * @returns {boolean} Whether or not the node is a 'null' or 'undefined'
	 * @public
	 */
	function isNullOrUndefined(node) {
		return (node.type === "Literal" && node.value === null) ||
			(node.type === "Identifier" && node.name === "undefined") ||
			(node.type === "UnaryExpression" && node.operator === "void");
	}

	/**
	 * Checks whether or not a node is callee.
	 * @param {ASTNode} node - A node to check.
	 * @returns {boolean} Whether or not the node is callee.
	 */
	function isCallee(node) {
		return node.parent.type === "CallExpression" && node.parent.callee === node;
	}

	function isDefaultThisBinding(node, sourceCode) {
		if (isES5Constructor(node) || hasJSDocThisTag(node, sourceCode)) {
			return false;
		}

		var currentNode = node;
		while (currentNode) {
			var parent = currentNode.parent;

			switch (parent.type) {

				/*
				 * Looks up the destination.
				 * e.g., obj.foo = nativeFoo || function foo() { ... };
				 */
				case "LogicalExpression":
				case "ConditionalExpression":
					currentNode = parent;
					break;

					// If the upper function is IIFE, checks the destination of the return value.
					// e.g.
					//   obj.foo = (function() {
					//     // setup...
					//     return function foo() { ... };
					//   })();
				case "ReturnStatement":
					var func = getUpperFunction(parent);

					if (func === null || !isCallee(func)) {
						return true;
					}
					currentNode = func.parent;
					break;

					// e.g.
					//   var obj = { foo() { ... } };
					//   var obj = { foo: function() { ... } };
				case "Property":
					return false;

					// e.g.
					//   obj.foo = foo() { ... };
				case "AssignmentExpression":
					return parent.right !== node ||
						parent.left.type !== "MemberExpression";

					// e.g.
					//   class A { constructor() { ... } }
					//   class A { foo() { ... } }
					//   class A { get foo() { ... } }
					//   class A { set foo() { ... } }
					//   class A { static foo() { ... } }
				case "MethodDefinition":
					return false;

					// e.g.
					//   var foo = function foo() { ... }.bind(obj);
					//   (function foo() { ... }).call(obj);
					//   (function foo() { ... }).apply(obj, []);
				case "MemberExpression":
					return parent.object !== node ||
						parent.property.type !== "Identifier" ||
						!bindOrCallOrApplyPattern.test(parent.property.name) ||
						!isCallee(parent) ||
						parent.parent.arguments.length === 0 ||
						isNullOrUndefined(parent.parent.arguments[0]);

					// e.g.
					//   Reflect.apply(function() {}, obj, []);
					//   Array.from([], function() {}, obj);
					//   list.forEach(function() {}, obj);
				case "CallExpression":
					if (isReflectApply(parent.callee)) {
						return parent.arguments.length !== 3 ||
							parent.arguments[0] !== node ||
							isNullOrUndefined(parent.arguments[1]);
					}
					if (isArrayFromMethod(parent.callee)) {
						return parent.arguments.length !== 3 ||
							parent.arguments[1] !== node ||
							isNullOrUndefined(parent.arguments[2]);
					}
					if (isMethodWhichHasThisArg(parent.callee)) {
						return parent.arguments.length !== 2 ||
							parent.arguments[0] !== node ||
							isNullOrUndefined(parent.arguments[1]);
					}
					return true;

					// Otherwise 'this' is default.
				default:
					return true;
			}
		}

		/* istanbul ignore next */
		return true;
	}

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {
		var stack = [],
			sourceCode = context.getSourceCode();

		/**
		 * Gets the current checking context.
		 *
		 * The return value has a flag that whether or not 'this' keyword is valid.
		 * The flag is initialized when got at the first time.
		 *
		 * @returns {{valid: boolean}}
		 *   an object which has a flag that whether or not 'this' keyword is valid.
		 */
		stack.getCurrent = function() {
			var current = this[this.length - 1];

			if (!current.init) {
				current.init = true;
				current.valid = !isDefaultThisBinding(
					current.node,
					sourceCode);
			}
			return current;
		};

		/**
		 * Pushs new checking context into the stack.
		 *
		 * The checking context is not initialized yet.
		 * Because most functions don't have 'this' keyword.
		 * When 'this' keyword was found, the checking context is initialized.
		 *
		 * @param {ASTNode} node - A function node that was entered.
		 * @returns {void}
		 */
		function enterFunction(node) {

			// 'this' can be invalid only under strict mode.
			stack.push({
				init: !context.getScope().isStrict,
				node: node,
				valid: true
			});
		}

		/**
		 * Pops the current checking context from the stack.
		 * @returns {void}
		 */
		function exitFunction() {
			stack.pop();
		}

		return {

			/*
			 * 'this' is invalid only under strict mode.
			 * Modules is always strict mode.
			 */
			Program: function(node) {
				var scope = context.getScope(),
					features = context.ecmaFeatures || {};

				stack.push({
					init: true,
					node: node,
					valid: !(
						scope.isStrict ||
						node.sourceType === "module" ||
						(features.globalReturn && scope.childScopes[0].isStrict)
					)
				});
			},

			"Program:exit": function() {
				stack.pop();
			},

			"FunctionDeclaration": enterFunction,
			"FunctionDeclaration:exit": exitFunction,
			"FunctionExpression": enterFunction,
			"FunctionExpression:exit": exitFunction,

			// Reports if 'this' of the current context is invalid.
			"ThisExpression": function(node) {
				var current = stack.getCurrent();

				if (current && !current.valid) {
					context.report(node, ProblemMessages.noInvalidThis);
				}
			}
		};
	};

	module.exports.schema = [];

	return module.exports;
});
/*eslint-env amd */
define('eslint/lib/rules/no-trailing-spaces',[
	'i18n!javascript/nls/problems',
	'module'
], function(ProblemMessages, module) {
	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	module.exports = function(context) {
		var sourceCode = context.getSourceCode();

		var BLANK_CLASS = "[ \t\u00a0\u2000-\u200b\u2028\u2029\u3000]",
			SKIP_BLANK = "^" + BLANK_CLASS + "*$",
			NONBLANK = BLANK_CLASS + "+$";

		var options = context.options[0] || {},
			skipBlankLines = options.skipBlankLines || false;

		/**
		 * Report the error message
		 * @param {ASTNode} node node to report
		 * @param {int[]} location range information
		 * @param {int[]} fixRange Range based on the whole program
		 * @returns {void}
		 */
		function report(node, location, fixRange) {

			/*
			 * Passing node is a bit dirty, because message data will contain big
			 * text in `source`. But... who cares :) ?
			 * One more kludge will not make worse the bloody wizardry of this
			 * plugin.
			 */
			context.report({
				node: node,
				loc: location,
				message: ProblemMessages.noTrailingSpaces,
				data: fixRange
			});
		}


		//--------------------------------------------------------------------------
		// Public
		//--------------------------------------------------------------------------

		return {

			Program: function checkTrailingSpaces(node) {

				// Let's hack. Since Espree does not return whitespace nodes,
				// fetch the source code and do matching via regexps.

				var re = new RegExp(NONBLANK),
					skipMatch = new RegExp(SKIP_BLANK),
					matches,
					lines = sourceCode.lines,
					linebreaks = sourceCode.getText().match(/\r\n|\r|\n|\u2028|\u2029/g),
					location,
					totalLength = 0,
					rangeStart,
					rangeEnd,
					containingNode;

				for (var i = 0, ii = lines.length; i < ii; i++) {
					matches = re.exec(lines[i]);

					// Always add linebreak length to line length to accommodate for line break (\n or \r\n)
					// Because during the fix time they also reserve one spot in the array.
					// Usually linebreak length is 2 for \r\n (CRLF) and 1 for \n (LF)
					var linebreakLength = linebreaks && linebreaks[i] ? linebreaks[i].length : 1;
					var lineLength = lines[i].length + linebreakLength;

					try {
						if (matches) {
							location = {
								line: i + 1,
								column: matches.index
							};
	
							rangeStart = totalLength + location.column;
							rangeEnd = totalLength + lineLength - linebreakLength;
							containingNode = sourceCode.getNodeByRangeIndex(rangeStart);
	
							if (containingNode && containingNode.type === "TemplateElement" &&
								rangeStart > containingNode.parent.range[0] &&
								rangeEnd < containingNode.parent.range[1]) {
								continue;
							}
	
							// If the line has only whitespace, and skipBlankLines
							// is true, don't report it
							if (skipBlankLines && skipMatch.test(lines[i])) {
								continue;
							}
							report(node, location, {range: {start: rangeStart, end: rangeEnd }});
						}
					} finally {
						// always adjust the total length
						totalLength += lineLength;
					}
				}
			}

		};
	};

	module.exports.schema = [{
		type: "object",
		properties: {
			skipBlankLines: {
				type: "boolean"
			}
		},
		additionalProperties: false
	}];

	return module.exports;
});
/*******************************************************************************
 * @license
 * Copyright (c) 2013, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
 *
 * Contributors:
 *	 IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd, worker */
/**
 * Implements eslint's load-rules API for AMD. Our rules are loaded as AMD modules.
 */
define('eslint/lib/load-rules-async',[
'./utils/util',
'javascript/finder',
'i18n!javascript/nls/problems',
'estraverse/estraverse',
'orion/editor/stylers/application_javascript/syntax',
'./rules/accessor-pairs',
'./rules/no-control-regex',
'./rules/no-duplicate-case',
'./rules/no-else-return',
'./rules/no-empty-character-class',
'./rules/no-empty-label',
'./rules/no-eq-null',
'./rules/no-extra-boolean-cast',
'./rules/no-extra-parens',
'./rules/no-invalid-regexp',
'./rules/no-negated-in-lhs',
'./rules/no-obj-calls',
'./rules/no-self-compare',
'./rules/no-irregular-whitespace',
'./rules/no-const-assign',
'./rules/no-implicit-coercion',
'./rules/no-extra-bind',
'./rules/no-extend-native',
'./rules/no-lone-blocks',
'./rules/quotes',
'./rules/yoda',
'./rules/no-param-reassign',
'./rules/no-native-reassign',
'./rules/no-unused-expressions',
'./rules/no-invalid-this',
'./rules/no-trailing-spaces'
], function(util, Finder, ProblemMessages, Estraverse, JsSyntax,
		accessorPairs, noControlRegex, noDuplicateCase, noElseReturn, noEmptyCharClasses, 
		noEmptyLabel, noEqNull, noExtraBoolCast, noExtraParens, noInvalidRegExp, noNegatedInLhs,
		noObjCalls, noSelfCompare, noIrregularWhitespace, noConstAssign, noImplicitCoercion,
		noExtraBind, noExtendNative, noLoneBlocks, quotes, yoda, noParamReassign, noNativeReassign,
		noUnusedExpressions, noInvalidThis, noTrailingSpaces) {
	
	var nodeModules = {
		"buffer": true,
		"child_process": true,
		"cluster": true, 
		"crypto": true, 
		"dns": true, 
		"domain": true, 
		"events": true, 
		"fs": true, 
		"http": true, 
		"https": true, 
		"net": true, 
		"os": true, 
		"path": true, 
		"punycode": true, 
		"readline": true, 
		"stream": true, 
		"string_decoder": true, 
		"tls":true, 
		"tty": true, 
		"dgram": true, 
		"url": true, 
		"util": true, 
		"v8": true, 
		"vm": true, 
		"zlib": true
	};
	
    var rules = {
    	/** @callback */
        "check-tern-plugin": function(context) {
        	function checkProject(node) {
        		function getEnvNode(envname) {
    				var comment = Finder.findDirective(node, "eslint-env"); //$NON-NLS-1$
    				if(comment) {
    					var idx = comment.value.indexOf(envname);
    					if(idx > -1) {
    						var start = comment.range[0]+idx+2, //add 2 because the spec says block value starts exclude /*
    							end = start+envname.length;
    						return {type: "EnvName", range: [start, end], loc: comment.loc}; //$NON-NLS-1$
    					}
    				}
        		}
        		// Don't report problems on HTML files that have no script blocks
        		if (node.end === 0){
        			return;
        		}
                if(context.env){
                	var envKeys = Object.keys(context.env);
                	if (envKeys.length > 0){
                		var tern = context.getTern();
                		if (tern){
       						envKeys.forEach(function(key) {
       							// CommonJS define statements have node env key, but are handled in requirejs plugin (see missing-requirejs)
       							if (key === "node" && node.environments && node.environments['simplifiedCommonJS']) {
       								return;
   								}
	   							var pluginName = tern.plugins[key] ? key : tern.optionalPlugins[key];
	   							if (pluginName && !tern.plugins[pluginName]) {
	   								var envnode = getEnvNode(key);
	   								if(envnode) {
	   									context.report(envnode, ProblemMessages['check-tern-plugin'], {0:key, 1:pluginName, data: pluginName});
	   								} else {
	       								context.report(node, ProblemMessages['check-tern-plugin'], {0:key, 1:pluginName, data: pluginName});
	   								}
	   							} else {
	   								var def = tern.getDef(key);
	   								if (!def && tern.optionalDefs[key]){
	   									envnode = getEnvNode(key);
		   								if(envnode) {
		   									context.report(envnode, ProblemMessages['check-tern-lib'], {0:key, 1:key, nls: 'check-tern-lib', data: key}); //$NON-NLS-1$
		   								} else {
		       								context.report(node, ProblemMessages['check-tern-lib'], {0:key, 1:key, nls: 'check-tern-lib', data: key}); //$NON-NLS-1$
		   								}
   									}
   								}
       						});
        				}
       				}
   				}
			}
			return {
				"Program": checkProject
			};
        },
    	/** @callback */
        "curly": function(context) {
	        		/**
	        		 * Checks the following AST element for a BlockStatement
	        		 */
	        		function checkBlock(node) {
        			    switch(node.type) {
        			        case 'IfStatement': {
            					if(node.consequent && node.consequent.type !== 'BlockStatement') {
            						//flag the first token of the statement that should be in the block
            						context.report(node.consequent, ProblemMessages['curly'], null /*, context.getTokens(node.consequent)[0]*/);
            					}
            					if(node.alternate && node.alternate.type !== 'BlockStatement' && node.alternate.type !== 'IfStatement') {
            						//flag the first token of the statement that should be in the block
            						context.report(node.alternate, ProblemMessages['curly'], null /*, context.getTokens(node.alternate)[0]*/);
            					}
            					break;
        				    }
        				    case 'DoWhileStatement':
        				    case 'WhileStatement':
        				    case 'WithStatement':
        				    case 'ForStatement':
                            case 'ForInStatement': 
                            case 'ForOfStatement': {
            					if(node.body && node.body.type !== 'BlockStatement') {
            						//flag the first token of the statement that should be in the block
            						context.report(node.body, ProblemMessages['curly'], null /*, context.getTokens(node.body)[0]*/);
            					}
            					break;
        					}
        				}
	        		}
	
	        		return {
	        			'IfStatement' : checkBlock,
	        			'WhileStatement' : checkBlock,
	        			'ForStatement' : checkBlock,
	        			'ForInStatement' : checkBlock,
	        			'ForOfStatement': checkBlock,
	        			'WithStatement': checkBlock,
	        			'DoWhileStatement': checkBlock
	        		};
        },
        /** @callback */
		"eqeqeq": function(context) {
			        /**
        			 * @description Find the token with the matching operator
        			 * @param {{}}} context The ESLint rule context
        			 * @param {Object} node The AST node
        			 * @returns {Object} The matching token or null
        			 */
        			function getOperatorToken(context, node) {
	            		var tokens = context.getTokens(node),
	            			len = tokens.length, 
	            			operator = node.operator;
	            		// node is a binary expression node so node.left exists
	            		var start = node.left.end; // we need to start to look for the operator at the end of the left node
	            		for (var i=0; i < len; i++) {
	            			var t = tokens[i];
	            			if (t.start >= start && t.value === operator) {
	            				return t;
	            			}
	            		}
	            		return null;
	            	}
	            	/**
	            	 * @description If the given node value is null or undefined
	            	 * @param {Object} node The AST node
	            	 * @returns {Boolean} if the node's value is null or undefined
	            	 */
	            	function isNullness(node) {
	            		if(node && node.type) {
	            			return node.type === 'Literal' && node.value === null || node.type === 'Identifier' && node.name === 'undefined';
	            		}
	            		return false;
	            	}
	        		return {
	        			/* @callback */
	        			"BinaryExpression": function(node) {
        					if(isNullness(node.left) || isNullness(node.right)) {
        						return;
        					}
        					var op = node.operator;
        					var expected = null;
        					if (op === "==") {
        					    expected = '===';
        						context.report(node, ProblemMessages['eqeqeq'], {0: expected, 1:op}, getOperatorToken(context, node));
        					} else if (op === "!=") {
        					    expected = '!==';
        						context.report(node, ProblemMessages['eqeqeq'], {0:expected, 1:op}, getOperatorToken(context, node));
        					}
	        			}
	        		};
        },
        /** @callback */
		"missing-doc": function(context) {
                /**
                 * @description If the comment is a block comment
                 * @param {Array.<Object>} comments The array of AST comment nodes
                 * @returns {Boolean} If the last comment is a block comment (this one that would be immediately preceeding the AST node)
                 */
                function validComment(comments) {
                    if(comments && comments.leading) {
                        var len = comments.leading.length;
                        return len > 0 && comments.leading[len-1].type === 'Block';
                    }
                    return false;
                }
        		/**
		         * @description Checks the attached comments on the node
		         * @param {Object} node The AST node
		         */
		        function checkDoc(node) {
    				var comments;
    				var name;
    				switch(node.type) {
    					case 'Property':
    						if(node.value && node.value.type === 'FunctionExpression') {
    							comments = context.getComments(node);
    							if(!comments || comments.leading.length < 1) {
    							    //TODO see https://github.com/jquery/esprima/issues/1071
							        comments = context.getComments(node.key);
    							}
    							if(!validComment(comments)) {
    								switch(node.key.type) {
    									case 'Identifier':
    										name = node.key.name;
    										break;
    									case 'Literal':
    										name = node.key.value;
    										break;
    								}
    								context.report(node.key, ProblemMessages['missing-doc'], {0:name}, { type: 'expr' }); //$NON-NLS-1$
    							}
    						}
    						break;
    					case 'FunctionDeclaration':
							comments = context.getComments(node);
							if(!comments || comments.leading.length < 1) {
							    //TODO see https://github.com/jquery/esprima/issues/1071
						        comments = context.getComments(node.id);
							}
							if(!validComment(comments) && node.parent && node.parent.type !== "ExportNamedDeclaration") {
								context.report(node.id, ProblemMessages['missing-doc'], {0:node.id.name}, { type: 'decl' });  //$NON-NLS-1$
							}
    						break;
    					case 'ExpressionStatement':
    						if(node.expression && node.expression.type === 'AssignmentExpression') {
    							var anode = node.expression;
    							if(anode.right && anode.right.type === 'FunctionExpression' && anode.left && anode.left.type === 'MemberExpression') {
    								//comments are attached to the enclosing expression statement
    								comments = context.getComments(node);
    								if(!comments || comments.leading.length < 1) {
        							    //TODO see https://github.com/jquery/esprima/issues/1071
    							        comments = context.getComments(anode.left);
        							}
    								if(!validComment(comments)) {
    									name = anode.left.computed === true ? anode.left.property.value : anode.left.property.name;
    									context.report(anode.left.property, ProblemMessages['missing-doc'], {0:name}, { type: 'expr' }); //$NON-NLS-1$
    								}
    							}
    						}
    						break;
    					case 'ExportNamedDeclaration' :
							comments = context.getComments(node);
							if(!validComment(comments) && node.declaration && node.declaration.type === "FunctionDeclaration") {
								context.report(node.declaration.id, ProblemMessages['missing-doc'], {0:node.declaration.id.name}, { type: 'decl' });  //$NON-NLS-1$
							}
					}
        		}

        		return {
        			"Property": checkDoc,
        			"FunctionDeclaration": checkDoc,
        			"ExpressionStatement": checkDoc,
        			"ExportNamedDeclaration": checkDoc
        		};
        },
        /** @callback */
		"new-parens": function(context) {
        		return {
        			/* @callback */
        			'NewExpression' : function(node) {
    					if(node.callee) {
    						var tokens = context.getTokens(node.callee, 0, 1);
    						if(tokens && tokens.length > 0) {
    							var last = tokens[tokens.length-1];
    							if(last.type !== 'Punctuator' || last.value !== '(') {
    								//if there s no opening parenthesis its safe to assume they are missing
    								context.report(node.callee, ProblemMessages['new-parens'], null, tokens[0]);
    							}
    						}
    					}
        			}
        		};
        },
        /** @callback */
        "no-caller": function(context) {
                return {
                	/* @callback */
                    "MemberExpression": function(node) {
                        var func = Finder.findParentFunction(node);
                        if(func) {
                            var object = node.object;
                            if (!object || object.name !== "arguments" || object.type !== "Identifier") {
                                return;
                            }
                            var prop = node.property;
                            var name = prop.name ? prop.name : prop.value;
                            if (name === "callee" || name === "caller") {
                                context.report(prop, ProblemMessages['no-caller'], {0: name});
                            }
                        }
                    }
                };
        },
        /** @callback */
        "no-comma-dangle": function(context) {
                return {
                	/* @callback */
                    'ObjectExpression': function(node) {
                        var token  = context.getLastToken(node, 1);
                        if(token && token.value === ',') {
                            context.report(node, ProblemMessages['no-comma-dangle'], null, token);
                        }
                    }
                };
        },
        /* @callback */
        "no-cond-assign": function(context) {

                var statements = {
                    'IfStatement': true,
                    'DoWhileStatement': true,
                    'WhileStatement': true,
                    'ForStatement': true
                };

                /**
                 * @description If the given node is surrounded by ()
                 * @param {Object} node The AST node
                 * @returns {Boolean} If the node is surrounded by ()
                 */
                function isParenthesised(node) {
                    var type = node.parent.type;
                    if(statements[type]) {
                        //if its direct parent is the control statement, check for double parenthesis
                        if(type !== 'ForStatement') {
                            return context.getTokenBefore(node, 1).value === '(';
                        }
                    }
                    return context.getTokenBefore(node).value === '(';
                }
                /**
                 * @description If we should skip the node
                 * @param {Object} node The AST node
                 * @returns {Boolean} if we should skip checking the given node
                 */
                function skip(node) {
                    switch(node.type) {
                        case 'FunctionExpression':
                        case 'ObjectExpression':
                        case 'CallExpression':
                        case 'ArrayExpression': {
                            return true;
                        }
                        default: return false;
                    }
                }
                /**
                 * @description Checks for assignment expressions
                 * @param {Object} node The AST node
                 */
                function checkForAssignment(node) {
                    var assigns = [];
                    if(node.test === null) {
                        return;
                    }
                    node.test.parent = node;
                    Estraverse.traverse(node.test, {
                    	/* @callback */
                        enter: function(n, parent) {
                            if(n.range[0] > node.test.range[1]) {
                                //once we've left the test object
                                return Estraverse.VisitorOption.Break;
                            }
                            if(skip(n)) {
                                return Estraverse.VisitorOption.Skip;
                            }
                            if(parent) {
                                n.parent = parent;
                            }
                            if(n && n.type === 'AssignmentExpression') {
                                assigns.push(n);
                            }
                        }
                    });
                    var len = assigns.length;
                    if(len > 0) {
                        for(var i = 0; i < len; i++) {
                            var assign = assigns[i];
                            if(!isParenthesised(assign)) {
                                assign.range[0] = assign.left.range[0]; //mark only from the start of first part
                                context.report(assign, ProblemMessages['no-cond-assign']);
                            }
                        }
                    }
                }

                return {
                  'IfStatement': checkForAssignment,
                  'WhileStatement': checkForAssignment,
                  'ForStatement': checkForAssignment,
                  'DoWhileStatement': checkForAssignment
                };
        },
        /** @callback */
        "no-console": function(context) {
                return {
                	/* @callback */
                    'MemberExpression': function(node) {
                        if(node.object.name === 'console') {
                            //are we using the browser env?
                            if(context.env && context.env['browser']) {
                                context.report(node.object, ProblemMessages['no-console']);
                            }
                        }
                    }
                };
        },
        /** @callback */
        "no-constant-condition": function(context) {
                /**
                 * @param {Object} node The AST node
                 * @returns {Boolean} If the given node has a 'truthy' constant value
                 */
                function isConst(node) {
                    switch(node.type) {
                        case 'Literal':
                        case 'ObjectExpression':
                        case 'FunctionExpression':
                        case 'ArrayExpression': {
                            return true;
                        }
                        case 'BinaryExpression':
                        case 'LogicalExpression': {
                            return isConst(node.left) && isConst(node.right);
                        }
                        case 'UnaryExpression': {
                            return isConst(node.argument);
                        }
                        default: return false;
                    }
                }
                /**
                 * @description Check if the condition is a constant
                 * @param {Object} node The AST node
                 */
                function checkCondition(node) {
                    if(node && node.test && isConst(node.test)) {
                        context.report(node.test, ProblemMessages['no-constant-condition']);
                    }
                }

                return {
                    'IfStatement': checkCondition,
                    'WhileStatement': checkCondition,
                    'DoWhileStatement': checkCondition,
                    'ForStatement': checkCondition,
                    'ConditionalExpression': checkCondition
                };
        },
        /** @callback */
		"no-debugger": function(context) {
        		return {
        			/* @callback */
        			"DebuggerStatement": function(node) {
        				context.report(node, ProblemMessages['no-debugger'], null, context.getTokens(node)[0]);
					}
        		};
        },
        /** @callback */
		"no-dupe-keys": function(context) {
        		return {
        			/* @callback */
        			"ObjectExpression": function(node) {
    					var props = node.properties;
    					if(props && props.length > 0) {
    						var len = props.length;
    						var seen = Object.create(null);
    						for(var i = 0; i < len; i++) {
    							var prop = props[i];
    							// Here we're concerned only with duplicate keys having kind == "init". Duplicates among other kinds (get, set)
    							// cause syntax errors, by spec, so don't need to be linted.
    							if(prop.kind !== "init") {
    								continue;
    							}
    							var name = prop.key.name ? prop.key.name : prop.key.value;
    							if(Object.prototype.hasOwnProperty.call(seen, name)) {
    								context.report(prop, ProblemMessages['no-dupe-keys'], {0:name}, context.getTokens(prop)[0]);
    							}
    							else {
    								seen[name] = 1;
    							}
    						}
    					}
        			}
        		};
        },
        /** @callback */
		'no-empty-block': function(context) {
        		var comments;

        		return {
        			/* @callback */
        		    'Program' : function(node) {
        		          comments = node.comments;
        		    },
        		    /* @callback */
        			'BlockStatement' : function(node) {
        			    if(node.body.length < 1) {
        			        for(var i = 0; i < comments.length; i++) {
        			            var range = comments[i].range;
        			            if(range[0] >= node.range[0] && range[1] <= node.range[1]) {
        			                //a commented empty block, ignore
        			                return;
        			            }
        			        }
        			        context.report(node, ProblemMessages['no-empty-block']);
        			    }
        			}
        		};
        },
        /** @callback */
		"no-eval": function(context) {
        		return {
        			/* @callback */
        			"CallExpression": function(node) {
     					var name = node.callee.name;
    					if(!name) {
    						return;
    					}
    					if('eval' === name) {
    						context.report(node.callee, ProblemMessages['no-eval'], {0:'\'eval\''}, context.getTokens(node.callee)[0]); //$NON-NLS-1$
    					}
        			}
        		};
        },
        /** @callback */
		"no-extra-semi": function(context) {
        		return {
        			/* @callback */
        			"EmptyStatement": function(node) {
    					var tokens = context.getTokens(node);
    					var t = tokens[tokens.length - 1];
    					if (t && t.type === "Punctuator" && t.value === ";") {
    						context.report(node, ProblemMessages['no-extra-semi'], null, t /* expose the bad token */);
    					}
        			}
        		};
        },
        /** @callback */
		'no-fallthrough': function(context) {
        		/**
		         * @description Check if the AST node falls through
		         * @param {Object} node The AST node
		         */
		        function fallsthrough(node) {
        		    // cases with no statements or only a single case are implicitly fall-through
        		    if(node.consequent) {
        		        var statements = node.consequent.slice(0);
        		        if(statements.length > 0 && statements[0].type === 'BlockStatement') {
        		            var stmts = statements.shift(); //take the block statement off the list, it is not returnable
        		            if(stmts.body.length > 0) {
        		                statements = [].concat(statements, stmts.body); //remove the block statement
        		            }
        		        }
        		        if(statements.length < 1) {
        					return false;
        				}
        		        var statement = null;
        		        for(var i = 0; i < statements.length; i++) {
        		            statement = statements[i];
        		            if(util.returnableStatement(statement)) {
        		                return false;
        		            }
        		        }
        		        return true;
        		    }
        		    return false;
        		}

        		return {
        			/* @callback */
        			'SwitchStatement' : function(node) {
        			    if(node.cases && node.cases.length > 1) {
        			        //single case is implicitly fallthrough
        			        var caselen  = node.cases.length;
        			       cases: for(var i = 0; i < caselen; i++) {
        			            if(i+1 === caselen) {
        			                //last node is implicitly fall-through
        			                break;
        			            }
        			            if(fallsthrough(node.cases[i])) {
        			                //corect the highlighting to match eclipse
        			                var reportednode = node.cases[i+1];
        			                if(reportednode.test) {
        			                    reportednode.range[1] = reportednode.test.range[1];
        			                } else {
        			                    //default case - tag the token
        			                    var tokens = context.getTokens(reportednode);
        			                    if(tokens && tokens.length > 0) {
        			                        reportednode.range[1] = tokens[0].range[1];
        			                    }
        			                }
        			                var comments = reportednode.leadingComments;
        			                if(!comments && reportednode.test) {
        			                    //TODO see https://github.com/jquery/esprima/issues/1071
        			                    comments = reportednode.test.leadingComments;
        			                }
        			                if(comments) {
                    		            var comment = null;
                    		            for(var c = 0; c < comments.length; c++) {
                    		                comment = comments[c];
                    		                if(/\$?falls?\s?through\$?/i.test(comment.value.toLowerCase())) {
                    		                    continue cases;
                    		                }
                    		            }
                    		        }
        			                context.report(reportednode, ProblemMessages['no-fallthrough']);
        			            }
        			        }
        			    }
        			 }
        		};
        },
        /** @callback */
        "no-implied-eval": function(context) {
        		return {
        			/* @callback */
        			"CallExpression": function(node) {
    					var name = node.callee.name;
    					if(!name) {
    						return;
    					}
    					if('setInterval' === name || 'setTimeout' === name) {
    						if(node.arguments.length > 0) {
    							var arg = node.arguments[0];
    							if(arg.type === 'Literal') {
    								context.report(node.callee, ProblemMessages['no-eval'], {0:'Implicit \'eval\''}, context.getTokens(node.callee)[0]); //$NON-NLS-1$
    							}
    							else if(arg.type === 'Identifier') {
    								//lets see if we can find it's definition
    								var scope = context.getScope();
    								var decl = util.getDeclaration(arg, scope);
    								if (decl && decl.defs && decl.defs.length) {
    									var def = decl.defs[0];
    									var dnode = def.node;
    									if(def.type === 'Variable' && dnode && dnode.type === 'VariableDeclarator' &&
    											dnode.init && dnode.init.type === 'Literal') {
    										context.report(node.callee, ProblemMessages['no-eval'], {0:'Implicit \'eval\''}, context.getTokens(node.callee)[0]); //$NON-NLS-1$
    									}
    								}
    							}
    						}
    					}
        			}
        		};
        },
        /** @callback */
        "no-iterator": function(context) {
                return {
                	/* @callback */
                    'MemberExpression': function(node) {
                        if(node.property !== null) {
                            if(node.computed) {
                                if(node.property.value === '__iterator__') {
                                    context.report(node.property, ProblemMessages['no-iterator']);
                                }
                            } else if(node.property.name === '__iterator__') {
                                context.report(node.property, ProblemMessages['no-iterator']);
                            }
                        }
                    }
                };
        },
        /** @callback */
        "no-proto": function(context) {
                return {
                	/* @callback */
                    'MemberExpression': function(node) {
                        if(node.property !== null) {
                            if(node.computed) {
                                if(node.property.value === '__proto__') {
                                    context.report(node.property, ProblemMessages['no-proto']);
                                }
                            } else if(node.property.name === '__proto__') {
                                context.report(node.property, ProblemMessages['no-proto']);
                            }
                        }
                    }
                };
        },
        /** @callback */
		'no-jslint': function(context) {
        		return {
        			/* @callback */
        			'Program' : function(node) {
        			    var comments = node.comments;
        			    var len;
        			    if(comments && (len = comments.length) && comments.length > 0) {
        			        for(var i = 0; i < len; i++) {
        			            var comment = comments[i];
        			            if(comment.type === 'Block') {
        			                var match = /^\s*(js[l|h]int)(\s+\w+:\w+)+/ig.exec(comment.value);
        			                if(match) {
        			                    var jslint = match[1];
        			                    if(jslint.length < 1) {
        			                        continue;
        			                    }
        			                    var start = 2 + comment.value.indexOf(jslint) + comment.range[0]; 
        			                    var end = start + jslint.length;
        			                    context.report({type:'BlockComment', range:[start, end], loc: comment.loc}, ProblemMessages['no-jslint'], {0:jslint}); //$NON-NLS-1$
        			                }
        			            }
        			        }
        			    }
        			 }
        		};
        },
        /** @callback */
		"no-new-array": function(context) {
		    	/**
	    		 * @description Check the given AST node for new Array(..) violations
	    		 * @param {Object} node The AST node
	    		 */
	    		function checkNode(node) {
		    		var callee = node.callee;
	    			if (callee && callee.name === 'Array') {
						var args = node.arguments;
						if(args.length > 1) {
							context.report(callee, ProblemMessages['no-new-array']);
						} else if(args.length === 1 && (args[0].type === 'Literal' && typeof args[0].value !== 'number')) {
							context.report(callee, ProblemMessages['no-new-array']);
						}
					}
		    	}
        		return {
        			'NewExpression': checkNode,
        			'CallExpression': checkNode
        		};
        },
        /** @callback */
		"no-new-func": function(context) {
        		return {
        			/* @callback */
        			'NewExpression': function(node) {
        				var callee = node.callee;
		    			if (callee && callee.name === 'Function') {
		    				context.report(callee, ProblemMessages['no-new-func']);
	    				}
        			}
        		};
        },
        /** @callback */
		"no-new-object": function(context) {
		   		return {
		   			/* @callback */
        			'NewExpression': function(node) {
        				var callee = node.callee;
		    			if (callee && callee.name === 'Object') {
		    				context.report(callee, ProblemMessages['no-new-object']);
	    				}
        			}
        		};
        },
        /** @callback */
		"no-new-wrappers": function(context) {
        		var wrappers = ["String", "Number", "Math", "Boolean", "JSON"]; //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$ //$NON-NLS-5$
				return {
					/* @callback */
        			'NewExpression': function(node) {
        				var callee = node.callee;
		    			if (callee && wrappers.indexOf(callee.name) > -1) {
		    				context.report(callee, ProblemMessages['no-new-wrappers'], [callee.name]);
	    				}
        			}
        		};
        },
        /** @callback */
        "no-with": function(context) {
        		return {
        			/* @callback */
        			'WithStatement': function(node) {
	        			context.report(node, ProblemMessages['no-with'], null, context.getFirstToken(node));
	        		}
        		};
        },
        /** @callback */
		"missing-nls": function(context){
        		/**
		         * @description Reports missing NLS on the given node and offset
		         * @param {Object} node The AST node
		         * @param {Number} index The NLS index to use
		         */
		        function reportMissingNLS(node, index){
        			var data = Object.create(null);
        			data.indexOnLine = index;
        			context.report(node, ProblemMessages['missing-nls'], {0:node.value, data: data});
        		}
        		
        		return {
        			/* @callback */
                    'Literal': function(node) {
                    	_collectLinesWithStringLiterals(node, context._linesWithStringLiterals);
                    },
                    /**
                     * @callback
                     */
                    'Program': function(node){
                    	context._linesWithStringLiterals = Object.create(null);
                    	context._isMissingNLSActive = true;
                    },
                    /**
                     * @callback
                     */
                    'Program:exit': function(node){
                    	context._isMissingNLSActive = false;
                    	// Read each line in the map and check if there are non-nls statements
                    	if (context._linesWithStringLiterals){
                    		
                    		var comments = node.comments;
	                    	var linesWithComments = {};
	                    	if (Array.isArray(comments)){
	                    		for (var f=0; f<comments.length; f++) {
	                    			var comment = comments[f];
	                    			if (comment.type.toLowerCase() === 'line'){
	                    				var lineNumber = comment.loc.end.line;
								        linesWithComments[lineNumber] = "//" + comment.value; // Add in leading // to simply regex //$NON-NLS-1$
	                    			}
	                    		}
	                    	}
                    		
                    		for (lineNumber in context._linesWithStringLiterals) {
                    			var nodes = context._linesWithStringLiterals[lineNumber];
							    if (nodes) {
							    	
							    	// 0 based line count
									comment = linesWithComments[lineNumber];
							        var nonNlsRegExp = /\/\/\$NON-NLS-([0-9])+\$/g;
							        var match;
							        comments = [];
							    	if (comment){
								        while ((match = nonNlsRegExp.exec(comment)) !== null){
								        	comments.push(match[1]);
								        }
							        }

							        for (var i=0; i<nodes.length; i++) {
							        	match = false;
							        	for (var j=0; j<comments.length; j++) {

							        		// NON-NLS comments start at 1
							        		if (comments[j] === String(i+1)){
							        			comments[j] = null;
							        			match = true;
							        			break;
							        		}
							        		// For now allow NON-NLS-0 comments
							        		if (i===0 && comments[j] === '0'){
							        			comments[j] = null;
							        			match = true;
							        			break;
							        		}
							        	}
							        	if (!match){
							        		reportMissingNLS(nodes[i], i);
							        	}
							        }
							    }
							}
                    	}
                    }
				};
        },
        /** @callback */
        "unnecessary-nls": function(context){
        		/**
		         * @name reportUnusedNLS
		         * @description Reports unnecessary NLS problem, you must set both the range (index array) and the loc {start/end line/col} on the location argument
		         * @param location
		         * @param value
		         * @param nlsCommentValue
		         */
		        function reportUnusedNLS(location, value, nlsCommentValue){
					context.report({range: location.range, loc: location.loc, value: value}, ProblemMessages['unnecessary-nls'], {data: {nlsComment: nlsCommentValue}});
        		}

        		return {
        			/* @callback */
                    'Literal': function(node) {
                    	if (!context._isMissingNLSActive){
                    		_collectLinesWithStringLiterals(node, context._linesWithStringLiterals);
                    	}
                    },
                    /**
                     * @callback
                     */
                    'Program': function(node){
                    	if (!context._isMissingNLSActive){
                    		context._linesWithStringLiterals = Object.create(null);
                		}
                    },
                    /**
                     * @callback
                     */
                    'Program:exit': function(node){
                    	var start, value, lineNumber, match, nlsComments;
                    	var comments = node.comments;
                    	var linesWithComments = {};
                    	if (Array.isArray(comments)){
                    		for (var f=0; f<comments.length; f++) {
                    			var comment = comments[f];
                    			if (comment.type.toLowerCase() === 'line'){
                    				lineNumber = comment.loc.end.line;
							        linesWithComments[lineNumber] = comment;
                    			}
                    		}
                    	}
                    	
                    	// NLS tag must start with // (or be start of line comment) and can be numbered 0 to 9
                    	var nonNlsRegExp = /(?:^|(\/\/))\$NON-NLS-([0-9])+\$/g;

                    	// Read each line in the map and check if there are non-nls statements
                    	if (context._linesWithStringLiterals){
                    		for (lineNumber in context._linesWithStringLiterals) {
                    			var nodes = context._linesWithStringLiterals[lineNumber];
							    if (nodes) {
							    	comment = linesWithComments[lineNumber];
							    	delete linesWithComments[lineNumber];
								    nlsComments = [];
							        if (comment) {
								        while ((match = nonNlsRegExp.exec(comment.value)) !== null){
								        	nlsComments.push(match);
								        }
						        	}
									for (var j=0; j<nlsComments.length; j++) {
										match = nlsComments[j];
							        	var hasMatch = false;
							        	for (var i=0; i<nodes.length; i++) {
							        		// NON-NLS comments start at 1
							        		if (match[2] === String(i+1)){
							        			hasMatch = true;
							        			break;
							        		}
							        		// For now allow NON-NLS-0 comments
							        		if (i===0 && match[2] === '0'){
							        			hasMatch = true;
							        			break;
							        		}
							        	}
							        	if (!hasMatch){
							        		value = match[1] ? match[0] : '//' + match[0]; //$NON-NLS-1$
											start = comment.range[0] + match.index;
											if (match[1]){
												start += 2; // Comment range doesn't include line comment prefix
											}
                    						reportUnusedNLS({range: [start, start+value.length], loc: comment.loc}, value, match[0]);	        		
							        	}
							        }
							    }
							}
                    	}
                    	
                    	// Find comments on lines with no string literals
                    	for (lineNumber in linesWithComments){
                    		comment = linesWithComments[lineNumber];
                    		if (comment){
                    			// See if there is any code on the line before the comment
                    			var index = comment.range[0]-1;
                    			var text = node.sourceFile.text;
                    			var isBlank = index < 0;  //If we happen to be at the start of the file treat as newline
                    			var prevChar;
                    			if (text){
	                    			while (index >= 0 && (prevChar = text.charAt(index)).match(/\s/)){
	                    				if (prevChar === '\n' || index === 0){
	                    					isBlank = true;
	                    					break;
	                    				}
	                    				index--;
	                    			}
                				}
                    			while ((match = nonNlsRegExp.exec(comment.value)) !== null){
                    				if (isBlank && match.index > 0){
                    					break; // We are on a commented out line of code, skip marking non-nls messages
                    				}
                    				value = match[1] ? match[0] : '//' + match[0]; //$NON-NLS-1$
									start = comment.range[0] + match.index;
									if (match[1]){
										start += 2; // Comment range doesn't include line comment prefix
									}
            						reportUnusedNLS({range: [start, start+value.length], loc: comment.loc}, value, match[0]);	
            					}
                			}
                    	}
                    }
				};
        },
        /** @callback */
		"no-redeclare": function(context) {
			var options = {
				builtinGlobals: Boolean(context.options[0] && context.options[0].builtinGlobals)
			};

			/**
			 * Find variables in a given scope and flag redeclared ones.
			 * @param {Scope} scope - An escope scope object.
			 * @returns {void}
			 * @private
			 */
			function findVariablesInScope(scope) {
				scope.variables.forEach(function(variable) {
					var hasBuiltin = options.builtinGlobals && "writeable" in variable;
					var count = (hasBuiltin ? 1 : 0) + variable.identifiers.length;

					if (count >= 2) {
						variable.identifiers.sort(function(a, b) {
							return a.range[1] - b.range[1];
						});

						for (var i = hasBuiltin ? 0 : 1, l = variable.identifiers.length; i < l; i++) {
							context.report(
								variable.identifiers[i],
								ProblemMessages['no-redeclare'], {0:variable.name});
						}
					}
				});

			}

			/**
			 * Find variables in the current scope.
			 * @param {ASTNode} node - The Program node.
			 * @returns {void}
			 * @private
			 */
			function checkForGlobal(node) {
				var scope = context.getScope(),
					parserOptions = context.parserOptions,
					ecmaFeatures = parserOptions.ecmaFeatures || {};

				// Nodejs env or modules has a special scope.
				if (ecmaFeatures.globalReturn || node.sourceType === "module") {
					findVariablesInScope(scope.childScopes[0]);
				} else {
					findVariablesInScope(scope);
				}
			}

			/**
			 * Find variables in the current scope.
			 * @returns {void}
			 * @private
			 */
			function checkForBlock() {
				findVariablesInScope(context.getScope());
			}

			if (context.parserOptions.ecmaVersion >= 6) {
				return {
					Program: checkForGlobal,
					BlockStatement: checkForBlock,
					SwitchStatement: checkForBlock
				};
			}
			return {
				Program: checkForGlobal,
				FunctionDeclaration: checkForBlock,
				FunctionExpression: checkForBlock,
				ArrowFunctionExpression: checkForBlock
			};
		},
        /** @callback */
        "no-regex-spaces": function(context) {
                /**
                 * @description Reports spaces used in the regex node
                 * @param {Object} node The AST node
                 */
                function reportSpaces(node) {
                    var regex = /( {2,})/g;
                    var val = null;
                    while((val = regex.exec(node.raw)) !== null) {
                        var start = node.range[0]+val.index;
                        var len = val[0].length;
                        context.report({type: 'Literal', range:[start, start+len], loc: node.loc}, //$NON-NLS-1$
                                        ProblemMessages['no-regex-spaces'], {0:len});
                    }
                }

                return {
                	/* @callback */
                    'Literal': function(node) {
                        if(node.parent && node.parent.type === 'NewExpression') {
                            if(node.parent.callee.name === 'RegExp') {
                                reportSpaces(node);
                            }
                        }
                        var tok = context.getFirstToken(node);
                        if(tok && tok.type === 'RegularExpression') {
                            reportSpaces(node);
                        }
                    }
                };
        },
        /** @callback */
        "no-reserved-keys": function(context) {
                return {
                	/* @callback */
                    'ObjectExpression': function(node) {
                        if(node.properties) {
                            for(var i = 0; i < node.properties.length; i++) {
                                var prop = node.properties[i];
                                if(prop.key.type === 'Identifier' && JsSyntax.keywords.indexOf(prop.key.name) > -1) {//$NON-NLS-1$
                                    context.report(prop.key, ProblemMessages['no-reserved-keys']);//$NON-NLS-1$
                                }
                            }
                        }
                    },
                    "MemberExpression" : function(node) {
                    	if (node.property) {
                            if(node.property.type === 'Identifier' && JsSyntax.keywords.indexOf(node.property.name) > -1) {//$NON-NLS-1$
                                context.report(node.property, ProblemMessages['no-reserved-keys']);//$NON-NLS-1$
                            }
                    	}
                    }
                };
        },
        /** @callback */
        "no-shadow": function(context) {
                /**
                 * @description Collect all vars from the given scope into the map
                 * @param {Object} map The collector map
                 * @param {Object} scope The backing EScope scope
                 */
                function addVariables(map, scope) {
                    scope.variables.forEach(function(variable) {
                        var name = variable.name;
                        if (!variable.defs.length) { // Ignore the synthetic 'arguments' variable
                            return;
                        } if (!Object.prototype.hasOwnProperty.call(map, name)) {
                            map[variable.name] = scope;
                        }
                    });
                }

                /**
                 * @returns {Object} A map of {String} -> {Scope}. Keys are symbol names, values are the
                 * uppermost scope that binds the name.
                 */
                function createSymbolMap(scope) {
                    var upper = scope.upper;
                    var symbols = Object.create(null);

                    // Hack to walk past upper scope lacking a _namedFunctions map. This happens because escope generates
                    // 2 scopes for a FunctionExpression. The first is never returned by context.getScope() as it is not
                    // the innermost, so this rule never visits it.
                    while (upper && !upper._symbols) { upper = upper.upper; }
                    if (upper) {
                        // Propagate upper scope's named functions to ours
                        util.mixin(symbols, upper._symbols);
                    }
                    addVariables(symbols, scope);
                    scope._symbols = symbols;
                    return symbols;
                }
                /**
                 * @description Check if any of the variable defs are of type Parameter
                 * @param {Object} variable The variable
                 * @returns {Boolean} if the any of the defs are of type Parameter
                 */
                function isParameter(variable) {
                    return variable.defs.some(function(def) {
                        return def.type === "Parameter";
                    });
                }
                /**
                 * @description Check the scope the encloses the given AST node
                 * @param {Object} node The AST node
                 */
                function checkScope(node) {
                    // Build map
                    var scope = context.getScope();
                    if (node.type === "FunctionExpression" && node.id && node.id.name) {
                        scope  = scope.upper;
                        if (scope.type === "global") {
                            return; // No shadowing can occur in the global (Program) scope
                        }
                    }
                    var symbolMap = createSymbolMap(scope);
                    scope.variables.forEach(function(variable) {
                        if (!variable.defs.length) {
                            return; // Skip 'arguments'
                        }
                        // If variable's name was first bound in an upper scope, and the variable is not a parameter,
                        // flag it.
                        var bindingSource;
                        if ((bindingSource = symbolMap[variable.name]) && bindingSource !== scope && !isParameter(variable)) {
                        	context.report(variable.defs[0].name, ProblemMessages['no-shadow'], {0: variable.name});
                        }
                    });
                }
                return {
                    "Program": checkScope,
                    "FunctionDeclaration": checkScope,
                    "FunctionExpression": checkScope,
                    "ArrowFunctionExpression": checkScope
                };
        },
        /** @callback */
        "no-shadow-global": function(context) {
        		function checkIdentifier(node, func) {
        			switch(node.type) {
        				case 'Identifier' :
        				    func(node);
		                    break;
		                case 'Property' :
		                   checkIdentifier(node.value, func);
		                   break;
                		case 'ArrayPattern' :
                			var elements = node.elements;
                			elements.forEach(function(element) {
                				if (element) checkIdentifier(element, func);
                			});
                			break;
                		case 'ObjectPattern' :
                			var properties = node.properties;
                			properties.forEach(function(property) {
                				if (property) checkIdentifier(property, func);
                			});
                			break;
        			}
        		}
                /**
                 * @description Check if the given node is a shadow
                 * @param {Object} node The AST node
                 */
                function checkShadow(node) {
                    var env = context.env ? context.env : {};
                    env.builtin = true;
                    var tern = context.getTern();
                    var isSimplifiedCommonJS = false;
                    if (tern.file.ast && tern.file.ast.environments && tern.file.ast.environments.simplifiedCommonJS){
                    	isSimplifiedCommonJS = true;
                	}
                    switch(node.type) {
                        case 'VariableDeclarator': {
                        	checkIdentifier(node.id, function(node) {
                        		var name = node.name;
								if(name && env[Finder.findESLintEnvForMember(name)]) {
		                        	context.report(node, ProblemMessages['no-shadow-global'], {0: name});
		                    	}
		                    });
                        	break;
                        }
                        case 'FunctionExpression':
                        case 'FunctionDeclaration':
                        case 'ArrowFunctionExpression': {
                            node.params.forEach(function(param) {
                            	if (isSimplifiedCommonJS && (param.name === 'require' || param.name === 'exports' || param.name === 'module')){
                            		return;
                            	}
                                if(param.type === 'Identifier' && env[Finder.findESLintEnvForMember(param.name)]) {
                                    context.report(param, ProblemMessages['no-shadow-global-param'], {0: param.name, nls:'no-shadow-global-param'}); //$NON-NLS-1$
                                }
                            });
                            break;
                        }
                    }
                }

                return {
                    'FunctionExpression': checkShadow,
                    'FunctionDeclaration': checkShadow,
                    'ArrowFunctionExpression': checkShadow,
                    'VariableDeclarator': checkShadow
                };
        },
        /** @callback */
		'no-sparse-arrays': function(context) {
        		return {
        			/* @callback */
        			'ArrayExpression' : function(node){
        			    if(node.elements.indexOf(null) > -1) {
        			        context.report(node, ProblemMessages['no-sparse-arrays']);
        			    }
        			}
        		};
        },
        /** @callback */
        "no-throw-literal": function(context) {
                return {
                	/* @callback */
                    "ThrowStatement": function(node) {
                        var argument = node.argument;
                        // We have no type analysis yet, so to avoid false positives, assume any expr that
                        // *could* generate an Error actually does.
                        switch (argument.type) {
                            case "Identifier":
                                if (argument.name !== "undefined") {
                                    return;
                                }
                            //$FALLTHROUGH$
                            case "Literal":
                            case "ObjectExpression":
                            case "ArrayExpression":
                                context.report(argument, ProblemMessages['no-throw-literal']);
                        }
                    }
                };
        },
        /** @callback */
		"no-undef": function(context) {
				/**
				 * @description Checks if the node is a recovered node
				 * @param {Object} node The AST node
				 * @returns {Boolean} If the node is recovered
				 */
				function isRecoveredNode(node) {
					return node.range && node.range[0] === node.range[1];
				}
                /**
                 * @description If any of the variables defs are ImplicitGlobalVariable
                 * @param {Object} variable The variable
                 * @returns {Boolean} if any of the variable defs are ImplicitGlobalVariable
                 */
                function isImplicitGlobal(variable) {
                    return variable.defs.every(function(def) {
                        return def.type === "ImplicitGlobalVariable";
                    });
                }
                /**
                 * @description description
                 * @param scope
                 * @param ref
                 * @returns returns
                 */
                function getDeclaredGlobalVariable(scope, ref) {
                    var declaredGlobal = null;
                    scope.variables.some(function(variable) {
                        if (variable.name === ref.identifier.name) {
                            // If it's an implicit global, it must have a `writeable` field (indicating it was declared)
                            if (!isImplicitGlobal(variable) || Object.hasOwnProperty.call(variable, "writeable")) {  //$NON-NLS-0$
                                declaredGlobal = variable;
                                return true;
                            }
                        }
                        return false;
                    });
                    return declaredGlobal;
                }

                return {
                	/* @callback */
                    "Program": function(node) {
        	            var globalScope = context.getScope();

        	            globalScope.through.forEach(function(ref) {
    	            	    if (isRecoveredNode(ref.identifier)) {
    	            	    	return;
    	            	    }
        	                var variable = getDeclaredGlobalVariable(globalScope, ref),
        	                    name = ref.identifier.name;
        	                if (!variable) {
        	                	// Check if Tern knows about a definition in another file
        	                	var env = Finder.findESLintEnvForMember(name);
        	                    var tern = context.getTern();
								var query = {end: ref.identifier.start};
								var foundType = null;
								var expr = tern.findQueryExpr(tern.file, query);
								if (!expr) {
									return;
								}
								var type = tern.findExprType(query, tern.file, expr);
								// The origin could be a primitive in the same file (a=1;) which we still want to mark
								if (type && (type.originNode || Array.isArray(type.types) && type.types.length > 0) && type.origin && type.origin !== tern.file.name){
									foundType = type;
								}
            	                if (!foundType){
            	                    var inenv = env ? '-inenv' : ''; //$NON-NLS-1$
            	                    var nls = 'no-undef-defined'; //$NON-NLS-1$
            	                    context.report(ref.identifier, ProblemMessages['no-undef-defined'], {0:name, nls: nls, pid: nls+inenv, data: name});
        	                    }
        	                }
        	            });
                    }
                };
        },
        /** @callback */
        'no-undef-expression': function(context){
			return {
				/* @callback */
				'MemberExpression': function(node){
					if (node.property && node.object && node.object.type !== 'ThisExpression'){
						if (node.parent && node.parent.type === 'CallExpression' && node.parent.callee && node.parent.callee === node){
							var propName = node.property.name ? node.property.name : node.property.value;
							if (!propName) {
								return;
							}
							var query = {start: node.property.start, end: node.property.end};
							var tern = context.getTern();
							var expr = tern.findQueryExpr(tern.file, query);
							if (!expr) {
								// no expression found. No need to look for the type
								return;
							}
							var type = tern.findExprType(query, tern.file, expr);
							if (type && type.propertyOf) {
								if(type.guess) {
									return;
								}
								if(type.propertyOf.props[propName]) {
									//if we found a type and its a direct property, quit
									return;
								}
								if(!type.propertyOf.proto) {
									//this is a stub type - i.e. from a JSDoc type that does not exist
									return;
								}
							}
							query.end = node.object.end;
	                		expr = tern.findQueryExpr(tern.file, query);
							if (!expr) {
								// no expression found so we cannot get the type
								return;
							}
	                		type = tern.findExprType(query, tern.file, expr);
							if (type && type.types && type.types.length > 0 && !type.guess) {
	                			for (var i = 0; i < type.types.length; i++) {
	                				if (type.types[i].props && type.types[i].props[propName]) {
	                					return;
	                				}
	                				if (type.types[i].proto && type.types[i].proto.name !== 'Object.prototype') {
	                					if(type.types[i].proto.props[propName]) {
	                						return;
	                					}
            						}
	                			}
            					var name = type.types[0].name;
            					if (!name && type.originNode){
            						name = type.originNode.name;
        						}
            					var origin = type.types[0].origin;
            					if (!origin && type.origin){
            						origin = type.origin;
            					}
                				if (type.types.length === 1 && name && origin){
                					if(name.indexOf("!known_modules.") === 0) { //$NON-NLS-1$
                						name = name.slice("!known_modules.".length);
                					}
                					if (/\./.test(origin)){
            							var originNode = type.types[0].originNode ? type.types[0].originNode : type.originNode;
            							if (originNode){
                							var index = origin.lastIndexOf('/');
                							if (index >= 0){
                								origin = origin.substring(index+1);
                							}
											context.report(node.property, ProblemMessages['no-undef-expression-defined-object'], {0:propName, 1: name, 2: origin, nls: 'no-undef-expression-defined-object', data: {file: originNode.sourceFile.name, start: originNode.start, end: originNode.end}}); //$NON-NLS-1$
										} else {
											context.report(node.property, ProblemMessages['no-undef-expression-defined'], {0:propName, nls: 'no-undef-expression-defined'}); //$NON-NLS-1$
										}
									} else {
										context.report(node.property, ProblemMessages['no-undef-expression-defined-index'], {0:propName, 1: name, 2: origin, nls: 'no-undef-expression-defined-index'}); //$NON-NLS-1$
									}
								} else {
									context.report(node.property, ProblemMessages['no-undef-expression-defined'], {0:propName, nls: 'no-undef-expression-defined'}); //$NON-NLS-1$
								}
	                		}
                		}
                	}
            	}
        	};
        },
        /* @callback */
        'no-undef-init': function(context) {
        		return {
        			/* @callback */
        			'VariableDeclarator': function(node) {
        				if(node.init && node.init.type === 'Identifier' && node.init.name === 'undefined') {
    						context.report(node.init, ProblemMessages['no-undef-init']);
        				}
        			}
        		};
        },
		/** @callback */
		'no-unreachable': function(context) {
			var currentCodePath = null;
			/**
			 * Checks whether or not a given variable declarator has the initializer.
			 * @param {ASTNode} node - A VariableDeclarator node to check.
			 * @returns {boolean} `true` if the node has the initializer.
			 */
			function isInitialized(node) {
				return Boolean(node.init);
			}

			/**
			 * Checks whether or not a given code path segment is unreachable.
			 * @param {CodePathSegment} segment - A CodePathSegment to check.
			 * @returns {boolean} `true` if the segment is unreachable.
			 */
			function isUnreachable(segment) {
				return !segment.reachable;
			}
			
			/**
			 * Reports a given node if it's unreachable.
			 * @param {ASTNode} node - A statement node to report.
			 * @returns {void}
			 */
			function reportIfUnreachable(node) {
				if (currentCodePath.currentSegments.every(isUnreachable)) {
					context.report(node, ProblemMessages['no-unreachable']);
				}
			}

			return {

				// Manages the current code path.
				onCodePathStart: function(codePath) {
					currentCodePath = codePath;
				},

				onCodePathEnd: function() {
					currentCodePath = currentCodePath.upper;
				},

				// Registers for all statement nodes (excludes FunctionDeclaration).
				BlockStatement: reportIfUnreachable,
				BreakStatement: reportIfUnreachable,
				ClassDeclaration: reportIfUnreachable,
				ContinueStatement: reportIfUnreachable,
				DebuggerStatement: reportIfUnreachable,
				DoWhileStatement: reportIfUnreachable,
				EmptyStatement: reportIfUnreachable,
				ExpressionStatement: reportIfUnreachable,
				ForInStatement: reportIfUnreachable,
				ForOfStatement: reportIfUnreachable,
				ForStatement: reportIfUnreachable,
				IfStatement: reportIfUnreachable,
				ImportDeclaration: reportIfUnreachable,
				LabeledStatement: reportIfUnreachable,
				ReturnStatement: reportIfUnreachable,
				SwitchStatement: reportIfUnreachable,
				ThrowStatement: reportIfUnreachable,
				TryStatement: reportIfUnreachable,

				VariableDeclaration: function(node) {
					if (node.kind !== "var" || node.declarations.some(isInitialized)) {
						// if it has an initialization, this means the variable is not hoisted.
						reportIfUnreachable(node);
					}
				},

				WhileStatement: reportIfUnreachable,
				WithStatement: reportIfUnreachable,
				ExportNamedDeclaration: reportIfUnreachable,
				ExportDefaultDeclaration: reportIfUnreachable,
				ExportAllDeclaration: reportIfUnreachable
			};
		},
		/** @callback */
		"no-unused-params" : function(context) {
                /**
                 * @description If the node has an @callback comment
                 * @param {Object} node The AST node
                 * @returns {Boolean} If the node has an @callback comment
                 */
                function hasCallbackComment(node) {
                    if(node && node.leadingComments) {
                        var len = node.leadingComments.length;
                        for(var i = 0; i < len; i++) {
                            var comment = node.leadingComments[i];
                            if (comment.type === 'Block' && /\s*(?:@(callback|public))\s+/.test(comment.value)) {
                                return true;
                            }
                        }
                    }
                    return false;
                }

        		/**
		         * @description Check the given AST node 
		         * @param {Object} node The AST node
		         */
		        function check(node) {
    				var scope = context.getScope();
    				var kids = scope.childScopes;
    				if(scope.functionExpressionScope && kids && kids.length) {
    					scope = kids[0];
    				}
    				scope.variables.forEach(function(variable) {
    					if (!variable.defs.length || variable.defs[0].type !== "Parameter") { // only care about parameters
    						return;
    					}
    					var defnode = variable.defs[0].name;
    					if (!variable.references.length) {
    					    var pid = 'no-unused-params'; //$NON-NLS-1$
    					    if(node.type === 'FunctionExpression') {
    					        pid += '-expr'; //$NON-NLS-1$
    					        if(hasCallbackComment(node) || node.params && node.params.length > 0 && hasCallbackComment(node.params[0])) {
    					            return;
    					        }
    					        var parent = node.parent;
    					        switch(parent.type) {
    					        	case 'Property': {
    					        		if(hasCallbackComment(parent) || hasCallbackComment(parent.key)) {
    					        			return;
    					        		}
    					        		break;
    					        	}
    					        	case 'MemberExpression': {
        					        	//https://bugs.eclipse.org/bugs/show_bug.cgi?id=457067
        					            // func epxrs part of call expressions, i.e. bind-like calls
        					            //Esprima tags the root containing expression with the doc, not the func expr
        					            parent = parent.parent;
        					            if(parent.type === 'CallExpression' && hasCallbackComment(parent)) {
        					               return;
        					            }
    					        		break;
    					        	}
    					        	case 'AssignmentExpression': {
    					        		var left = parent.left;
        					        	if(left.type === 'MemberExpression') {
        					        		if(hasCallbackComment(left)) {
        					        			return;
        					        		}
        					        	} else if(left.type === 'Identifier') {
        					        		if(hasCallbackComment(left)) {
        					        			return;
        					        		}
        					        	}
        					        	parent = parent.parent;
        					        	if (parent && parent.type === "ExpressionStatement") {
        					        		if (hasCallbackComment(parent)) {
        					        			return;
        					        		}
        					        	}
    					        		break;
    					        	}
    					        	case 'VariableDeclarator': {
    					        		if(hasCallbackComment(parent.id) || hasCallbackComment(parent)) {
    					        			return;
    					        		}
    					        		parent = parent.parent;
    					        		if (parent && parent.type === "VariableDeclaration") {
    					        			if(hasCallbackComment(parent)) {
    					        				return;
    					        			}
    					        		}
    					        		break;
    					        	}
    					        }
    					    } else if(node.type === 'ArrowFunctionExpression') {
    					    	pid += '-arrow'; //$NON-NLS-1$
    					    	/*
					        	//check the parent: () => {a => {}}
					        	//the comment is attached to the ExpressionStatement
					        	if(node.parent.type === 'ExpressionStatement' && hasCallbackComment(node.parent)) {
					        		return;
					        	}
    					        */
    					    }
    						context.report(defnode, ProblemMessages['no-unused-params'], {0:defnode.name, pid: pid}); //$NON-NLS-0
    					}
    				});
        		}

        		return {
        			"FunctionDeclaration": check,
        			"FunctionExpression": check,
        			"ArrowFunctionExpression": check
        		};
        },
        /** @callback */
		"no-unused-vars": function(context) {
			//ORION - this is the rule soure from eslint - but kept in place
	        var config = {
	            vars: "all",
	            args: "none",
	            caughtErrors: "none"
	        };
	        var firstOption = context.options[0];
	        if (firstOption) {
	            if (typeof firstOption === "string") {
	                config.vars = firstOption;
	            } else {
	                config.vars = firstOption.vars || config.vars;
	                config.args = firstOption.args || config.args;
	                config.caughtErrors = firstOption.caughtErrors || config.caughtErrors;
	
	                if (firstOption.varsIgnorePattern) {
	                    config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern);
	                }
	
	                if (firstOption.argsIgnorePattern) {
	                    config.argsIgnorePattern = new RegExp(firstOption.argsIgnorePattern);
	                }
	
	                if (firstOption.caughtErrorsIgnorePattern) {
	                    config.caughtErrorsIgnorePattern = new RegExp(firstOption.caughtErrorsIgnorePattern);
	                }
	            }
	        }
	        var STATEMENT_TYPE = /(?:Statement|Declaration)$/;
	        /**
	         * Determines if a given variable is being exported from a module.
	         * @param {Variable} variable - EScope variable object.
	         * @returns {boolean} True if the variable is exported, false if not.
	         * @private
	         */
	        function isExported(variable) {
	            var definition = variable.defs[0];
	            if (definition) {
	                var node = definition.node;
	                if (node.type === "VariableDeclarator") {
	                    node = node.parent;
	                } else if (definition.type === "Parameter") {
	                    return false;
	                }
	                return node.parent.type.indexOf("Export") === 0;
	            }
	            return false;
	        }
	        /**
	         * Determine if an identifier is referencing an enclosing function name.
	         * @param {Reference} ref - The reference to check.
	         * @param {ASTNode[]} nodes - The candidate function nodes.
	         * @returns {boolean} True if it's a self-reference, false if not.
	         * @private
	         */
	        function isSelfReference(ref, nodes) {
	            var scope = ref.from;
	            while (scope) {
	                if (nodes.indexOf(scope.block) >= 0) {
	                    return true;
	                }
	                scope = scope.upper;
	            }
	            return false;
	        }
	
	        /**
	         * Checks the position of given nodes.
	         *
	         * @param {ASTNode} inner - A node which is expected as inside.
	         * @param {ASTNode} outer - A node which is expected as outside.
	         * @returns {boolean} `true` if the `inner` node exists in the `outer` node.
	         */
	        function isInside(inner, outer) {
	            return (
	                inner.range[0] >= outer.range[0] &&
	                inner.range[1] <= outer.range[1]
	            );
	        }
	        /**
	         * If a given reference is left-hand side of an assignment, this gets
	         * the right-hand side node of the assignment.
	         *
	         * @param {escope.Reference} ref - A reference to check.
	         * @param {ASTNode} prevRhsNode - The previous RHS node.
	         * @returns {ASTNode} The RHS node.
	         */
	        function getRhsNode(ref, prevRhsNode) {
	            var id = ref.identifier;
	            var prnt = id.parent;
	            var granpa = prnt.parent;
	            var refScope = ref.from.variableScope;
	            var varScope = ref.resolved.scope.variableScope;
	            var canBeUsedLater = refScope !== varScope;
	            /*
	             * Inherits the previous node if this reference is in the node.
	             * This is for `a = a + a`-like code.
	             */
	            if (prevRhsNode && isInside(id, prevRhsNode)) {
	                return prevRhsNode;
	            }
	
	            if (prnt.type === "AssignmentExpression" &&
	                granpa.type === "ExpressionStatement" &&
	                id === prnt.left &&
	                !canBeUsedLater
	            ) {
	                return prnt.right;
	            }
	            return null;
	        }
	
	        /**
	         * Checks whether a given function node is stored to somewhere or not.
	         * If the function node is stored, the function can be used later.
	         *
	         * @param {ASTNode} funcNode - A function node to check.
	         * @param {ASTNode} rhsNode - The RHS node of the previous assignment.
	         * @returns {boolean} `true` if under the following conditions:
	         *      - the funcNode is assigned to a variable.
	         *      - the funcNode is bound as an argument of a function call.
	         *      - the function is bound to a property and the object satisfies above conditions.
	         */
	        function isStorableFunction(funcNode, rhsNode) {
	            var node = funcNode;
	            var prnt = funcNode.parent;
	            while (prnt && isInside(prnt, rhsNode)) {
	                switch (prnt.type) {
	                    case "SequenceExpression":
	                        if (prnt.expressions[prnt.expressions.length - 1] !== node) {
	                            return false;
	                        }
	                        break;
	
	                    case "CallExpression":
	                    case "NewExpression":
	                        return prnt.callee !== node;
	
	                    case "AssignmentExpression":
	                    case "TaggedTemplateExpression":
	                    case "YieldExpression":
	                        return true;
	
	                    default:
	                        if (STATEMENT_TYPE.test(prnt.type)) {
	                            /*
	                             * If it encountered statements, this is a complex pattern.
	                             * Since analyzeing complex patterns is hard, this returns `true` to avoid false positive.
	                             */
	                            return true;
	                        }
	                }
	                node = prnt;
	                prnt = prnt.parent;
	            }
	            return false;
	        }
	
			function getUpperFunction(node) {
			    while (node) {
			        if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
			            return node;
			        }
			        node = node.parent;
			    }
			    return null;
			}
	
	        /**
	         * Checks whether a given Identifier node exists inside of a function node which can be used later.
	         *
	         * "can be used later" means:
	         * - the function is assigned to a variable.
	         * - the function is bound to a property and the object can be used later.
	         * - the function is bound as an argument of a function call.
	         *
	         * If a reference exists in a function which can be used later, the reference is read when the function is called.
	         *
	         * @param {ASTNode} id - An Identifier node to check.
	         * @param {ASTNode} rhsNode - The RHS node of the previous assignment.
	         * @returns {boolean} `true` if the `id` node exists inside of a function node which can be used later.
	         */
	        function isInsideOfStorableFunction(id, rhsNode) {
	            var funcNode = getUpperFunction(id);
	            return (
	                funcNode &&
	                isInside(funcNode, rhsNode) &&
	                isStorableFunction(funcNode, rhsNode)
	            );
	        }
	
	        /**
	         * Checks whether a given reference is a read to update itself or not.
	         *
	         * @param {escope.Reference} ref - A reference to check.
	         * @param {ASTNode} rhsNode - The RHS node of the previous assignment.
	         * @returns {boolean} The reference is a read to update itself.
	         */
	        function isReadForItself(ref, rhsNode) {
	            var id = ref.identifier;
	            var prnt = id.parent;
	            var granpa = prnt.parent;
	
	            return ref.isRead() && (
	                // self update. e.g. `a += 1`, `a++`
	                (
	                    prnt.type === "AssignmentExpression" &&
	                    granpa.type === "ExpressionStatement" &&
	                    prnt.left === id
	                ) ||
	                (
	                    prnt.type === "UpdateExpression" &&
	                    granpa.type === "ExpressionStatement"
	                ) ||
	                // in RHS of an assignment for itself. e.g. `a = a + 1`
	                (
	                    rhsNode &&
	                    isInside(id, rhsNode) &&
	                    !isInsideOfStorableFunction(id, rhsNode)
	                )
	            );
	        }
	
	        /**
	         * Determine if an identifier is used either in for-in loops.
	         *
	         * @param {Reference} ref - The reference to check.
	         * @returns {boolean} whether reference is used in the for-in loops
	         * @private
	         */
	        function isForInRef(ref) {
	            var target = ref.identifier.parent;
	            // "for (var ...) { return; }"
	            if (target.type === "VariableDeclarator") {
	                target = target.parent.parent;
	            }
	
	            if (target.type !== "ForInStatement") {
	                return false;
	            }
	
	            // "for (...) { return; }"
	            if (target.body.type === "BlockStatement") {
	                target = target.body.body[0];
	
	            // "for (...) return;"
	            } else {
	                target = target.body;
	            }
	
	            // For empty loop body
	            if (!target) {
	                return false;
	            }
	
	            return target.type === "ReturnStatement";
	        }
	
	        /**
	         * Determines if the variable is used.
	         * @param {Variable} variable - The variable to check.
	         * @returns {boolean} True if the variable is used
	         * @private
	         */
	        function isUsedVariable(variable) {
	            var functionNodes = variable.defs.filter(function(def) {
	                    return def.type === "FunctionName";
	                }).map(function(def) {
	                    return def.node;
	                }),
	                isFunctionDefinition = functionNodes.length > 0,
	                rhsNode = null;
	
	            return variable.references.some(function(ref) {
	                if (isForInRef(ref)) {
	                    return true;
	                }
	
	                var forItself = isReadForItself(ref, rhsNode);
	
	                rhsNode = getRhsNode(ref, rhsNode);
					variable.isread = variable.isread || ref.isRead();
	                return (
	                    ref.isRead() &&
	                    !forItself &&
	                    !(isFunctionDefinition && isSelfReference(ref, functionNodes))
	                ) || (ref.isRead() && forItself);
	            });
	        }
	
	        /**
	         * Gets an array of variables without read references.
	         * @param {Scope} scope - an escope Scope object.
	         * @param {Variable[]} unusedVars - an array that saving result.
	         * @returns {Variable[]} unused variables of the scope and descendant scopes.
	         * @private
	         */
	        function collectUnusedVariables(scope, unusedVars) {
	            var variables = scope.variables;
	            var childScopes = scope.childScopes;
	            var i, l;
	            if (scope.type !== "TDZ" && (scope.type !== "global" || config.vars === "all")) {
	                for (i = 0, l = variables.length; i < l; ++i) {
	                    var variable = variables[i];
	
	                    // skip a variable of class itself name in the class scope
	                    if (scope.type === "class" && scope.block.id === variable.identifiers[0]) {
	                        continue;
	                    }
	
	                    // skip function expression names and variables marked with markVariableAsUsed()
	                    if (scope.functionExpressionScope || variable.eslintUsed) {
	                        continue;
	                    }
	
	                    // skip implicit "arguments" variable
	                    if (scope.type === "function" && variable.name === "arguments" && variable.identifiers.length === 0) {
	                        continue;
	                    }
	
	                    // explicit global variables don't have definitions.
	                    var def = variable.defs[0];
	
	                    if (def) {
	                        var type = def.type;
	                        // skip catch variables
	                        if (type === "CatchClause") {
	                            if (config.caughtErrors === "none") {
	                                continue;
	                            }
	
	                            // skip ignored parameters
	                            if (config.caughtErrorsIgnorePattern && config.caughtErrorsIgnorePattern.test(def.name.name)) {
	                                continue;
	                            }
	                        }
	                        if (type === "Parameter") {
	                            // skip any setter argument
	                            if (def.node.parent.type === "Property" && def.node.parent.kind === "set") {
	                                continue;
	                            }
	
	                            // if "args" option is "none", skip any parameter
	                            if (config.args === "none") {
	                                continue;
	                            }
	
	                            // skip ignored parameters
	                            if (config.argsIgnorePattern && config.argsIgnorePattern.test(def.name.name)) {
	                                continue;
	                            }
	
	                            // if "args" option is "after-used", skip all but the last parameter
	                            if (config.args === "after-used" && def.index < def.node.params.length - 1) {
	                                continue;
	                            }
	                        } else {
	                            // skip ignored variables
	                            if (config.varsIgnorePattern && config.varsIgnorePattern.test(def.name.name)) {
	                                continue;
	                            }
	                        }
	                        if (def.node.type === 'VariableDeclarator') {
				        		// Variables can be marked as 'exported' in a comment if they are used as global variables
								var comments = def.parent.leadingComments;
								var report = true;
								if (Array.isArray(comments)) {
									for (var k = 0, len = comments.length; k < len; k++) {
										if (comments[k].type === "Block" && comments[k].value.toLowerCase().indexOf('exported') >= 0){ //$NON-NLS-1$
											report = false;
											break;
										}
									}
								}
								if (!report) {
									continue;
								}
							}
	                    }
	                    if (!isUsedVariable(variable) && !isExported(variable)) {
	                    	if(def) {
	                    		type = def.type;
	                    		variable.data = {
		                    		0: variable.identifiers[0].name,
		                    		nls: 'no-unused-vars-unused',
		                    		pid: 'no-unused-vars-unused'
		                    	};
		                    	if(type === "Parameter") {
		                    		continue;
		                    	}
		                    	if(type === "ImportBinding") {
		                    		variable.data.pid = 'no-unused-vars-import';
		                    	} else if(def.node.type === "FunctionDeclaration") {
		                    		var tern = context.getTern();
		                    		var refQuery = {
		                    			end: def.node.id.start
		                    		};
		                    		var filename = tern.file.name;
		                    		var refs = tern.findRefs(refQuery, tern.file);
		                    		var result = [];
		                    		if (refs && Array.isArray(refs.refs)) {
		                    			// filtering the refs from the current file - remove the one that matches the current node
		                    			refs.refs.forEach(function(match) {
		                    				if (match.file !== filename) {
		                    					// any match in a different file is a good match
		                    					result.push(match);
		                    				}
		                    			});
		                    		}
		                    		if (result.length > 0) {
		                    			continue;
		                    		}
		                    		variable.data.pid = variable.data.nls = 'no-unused-vars-unused-funcdecl';
		                    	} else if(typeof variable.isread === "boolean" && !variable.isread) {
		                    		variable.data.pid = variable.data.nls = 'no-unused-vars-unread';
		                    	}
	                    	}
	                        unusedVars.push(variable);
	                    }
	                }
	            }
	            for (i = 0, l = childScopes.length; i < l; ++i) {
	                collectUnusedVariables(childScopes[i], unusedVars);
	            }
	            return unusedVars;
	        }
	
	        return {
	            "Program:exit": function() {
	                var unusedVars = collectUnusedVariables(context.getScope(), []);
	
	                for (var i = 0, l = unusedVars.length; i < l; ++i) {
	                    var unusedVar = unusedVars[i];
						if (unusedVar.defs.length > 0) {
	                        context.report({
	                            node: unusedVar.identifiers[0],
	                            message: ProblemMessages[unusedVar.data.nls],
	                            data: unusedVar.data
	                        });
	                    }
	                }
	            }
	        };
        },
        /** @callback */
		"no-use-before-define": function(context) {
                /**
                 * @description Checks the option to make sure its a boolean, if not return the default
                 * @param {Boolean|Any} b The option to check
                 * @param {Boolean} defaultValue The default to return if the option is not of type boolean
                 * @returns {Boolean} The given option or the default, if the option is not a boolean
                 */
                function booleanOption(b, defaultValue) {
            		return typeof b === "boolean" ? b : defaultValue;
            	}

        		var options = context.options,
        		    flag_vars = booleanOption(options[0], true),   // by default, flag vars
        		    flag_funcs = booleanOption(options[1], false), // ... but not funcs
        		    flag_classes = booleanOption(options[2], true); // flag classes
        		
		        function checkScope(scope) {
    				scope.references.forEach(function(ref) {
    					var decl = util.getDeclaration(ref, scope), identifier = ref.identifier, name = identifier.name, defs;
    					if (decl && (defs = decl.defs).length && identifier.range[0] < defs[0].node.range[0]) {
    						var defType = defs[0].type;
    						if ((!flag_funcs && defType === "FunctionName") || (!flag_vars && defType === "Variable") || (!flag_classes && defType === "ClassName")) {
    							return;
    						}
    						context.report(identifier, ProblemMessages['no-use-before-define'], {0:name});
    					}
    				});
        		}
        		
        		/*
		         * @description Check the current scope for use
		         */
		        function check(node) {
    				var scope = context.getScope();
    				checkScope(scope);
    				// If using ES6 modules check the child 'module' scope
    				if (node.type === "Program" && node.sourceType === "module"){
    					checkScope(scope.childScopes[0]);
    				}
        		}
        		
        		if (context.parserOptions.ecmaVersion >= 6){
					return {
						"Program": check,
						"BlockStatement": check,
						"SwitchStatement": check,
						"ArrowFunctionExpression": function(node){
							if (node.body.type !== "BlockStatement") {
			                    check(node);
			                }	
						}
					};
				}
				// ECMA 5
				return {
					"Program": check,
					"FunctionExpression": check,
					"FunctionDeclaration": check,
					"ArrowFunctionExpression": check
				};

        },
        /** @callback */
        "radix": function(context) {
                return {
                	/* @callback */
                    "CallExpression": function(call) {
	                    var callee = call.callee;
	                    if (callee.name === "parseInt" && callee.type === "Identifier" && call.arguments.length < 2) {
	                        // Ensure callee actually resolves to the global `parseInt`
	                        var shadowed = false;
	                        for (var scope = context.getScope(); scope; scope = scope.upper) {
	                            shadowed = scope.variables.some(function(variable) {
	                                // Found a `parseInt` that is not the builtin
	                                return variable.name === "parseInt" && variable.defs.length;
	                            });
	                            if (shadowed) {
	                                break;
	                            }
	                        }
	                        if (!shadowed) {
	                            context.report(callee, ProblemMessages['radix'], null);
	                        }
	                    }
	                }
                };
        },
        /** @callback */
		"no-mixed-spaces-and-tabs": function(context) {
				var ignoredLocations = [];
				// we define a regular expression that matches any line that starts with spaces after tabs before any other character
				// any space after a tab \            (not a problem with smart-tabs)
				//                        after any number of tabs or space at the beginning of the line (^)
				// any tab after a space /
				var lineStart = /^(?=[\t ]* \t)/; // smart-tabs enabled
				//var lineStart = /^(?=[\t ]*( \t|\t ))/; // smart-tabs disabled
				//var lineStart = /^(?:<script>)*(?=[\t ]* \t)/; // include <script> at start of line
				/**
				 * @description Check the Program node in the AST (the whole AST)
				 * @param {Object} node The AST node
				 */
				function checkProgram(node) {
					var lines = context.getSourceLines();
					var allComments = context.getAllComments();
					
					// add all comments to the ignored elements
					allComments.forEach(function(node) {
						ignoredLocations.push(node.loc);
					});
					
					// now we check if the lines starts with a mix of tabs and spaces
					lines.forEach(function(line, index) {
						var match = lineStart.exec(line);
						if (match !== null) {
							// we got a match on the corresponding line
							// we need to see if the match is within an existing comment or a literal
							var currentLine = index + 1; // index is 0-based
							var currentColumn = match.index + 1; // column is 1-based
							if (searchInsideComments(ignoredLocations, { line: currentLine, column: currentColumn}) !== null) {
								// the position is inside a comment so we ignore it - move to the next one
								return;
							}
							context.report(node, { line: currentLine, column: currentColumn}, ProblemMessages['no-mixed-spaces-and-tabs']);
						}
					});
				}
				/**
				 * @description Look inside comment locations
				 * @param {Array.<Object>} locations The locations
				 * @param {Object} loc The location to check
				 * @returns {object} If a location was found
				 */
				function searchInsideComments(locations, loc) {
					var min = 0;
					var max = locations.length - 1;
					var guess;
				
					while (min <= max) {
						guess = Math.floor(min + (max - min) / 2);
				
						var currentLocation = locations[guess];
						if (isLocationInside(loc, currentLocation)) {
							return currentLocation;
						}
						else if (isBefore(loc, currentLocation)) {
							max = guess - 1;
						} else {
							min = guess + 1;
						}
					}
					return null;
				}
				/**
				 * @description If the given location is inside one of the locations
				 * @param {Object} givenLocation The location to check
				 * @param {Array.<Object>} locations The array of locations
				 * @returns {Boolean} If the given location is in one of the locations in the array
				 */
				function isLocationInside(givenLocation, locations) {
					/**
					 * Return true if the given location is inside the locations, false otherwise 
					 */
					var start = locations.start;
					var end = locations.end;
					var line = givenLocation.line;
					var column = givenLocation.column;
					
					if (start.line < line) {
						if (end.line > line) {
							return true;
						} else if (end.line === line) {
							return end.column > column;
						}
					} else if (start.line === line) {
						if (start.column < column) {
							if (end.line > line) {
								return true;
							} else if (end.line === line) {
								return end.column > column;
							}
						}
					}
					return false;
				}
				/**
				 * @description If the given location is before ny in the array
				 * @param {Object} givenLocation The location to check
				 * @param {Array.<Object>} locations The array of locations
				 * @returns {Boolean} If the given location is before any in the array
				 */
				function isBefore(givenLocation, locations) {
					/**
					 * Return true if the given location is before locations
					 */
					var start = locations.start;
					var line = givenLocation.line;
					var column = givenLocation.column;
					
					if (line < start.line) {
						return true;
					} else if (line === start.line) {
						return column < start.column;
					}
					return false;
				}

				return {
					"Program:exit": checkProgram
				};
		},
		/** @callback */
		"semi": function(context) {

			var OPT_OUT_PATTERN = /[\[\(\/\+\-]/; // One of [(/+-
			var options = context.options[1];
			var never = context.options[0] === "never",
				exceptOneLine = options && options.omitLastInOneLineBlock === true,
				sourceCode = context.getSourceCode();

			//--------------------------------------------------------------------------
			// Helpers
			//--------------------------------------------------------------------------

			/**
			 * Reports a semicolon error with appropriate location and message.
			 * @param {ASTNode} node The node with an extra or missing semicolon.
			 * @param {boolean} missing True if the semicolon is missing.
			 * @returns {void}
			 */
			function report(node, missing) {
				var message,
					lastToken = sourceCode.getLastToken(node),
					data = Object.create(null);

				if (!missing) {
					message = ProblemMessages["semi-missing"];
					data.kind = "missing"; //$NON-NLS-1$
				} else {
					message = ProblemMessages["semi-extra"];
					data.kind = "extra"; //$NON-NLS-1$
				}

				context.report(node, message, {data: data}, lastToken);
			}
			/**
			 * Checks whether a token is a semicolon punctuator.
			 * @param {Token} token The token.
			 * @returns {boolean} True if token is a semicolon punctuator.
			 */
			function isSemicolon(token) {
				return token.type === "Punctuator" && token.value === ";";
			}

			/**
			 * Check if a semicolon is unnecessary, only true if:
			 *   - next token is on a new line and is not one of the opt-out tokens
			 *   - next token is a valid statement divider
			 * @param {Token} lastToken last token of current node.
			 * @returns {boolean} whether the semicolon is unnecessary.
			 */
			function isUnnecessarySemicolon(lastToken) {
				var isDivider, isOptOutToken, lastTokenLine, nextToken, nextTokenLine;

				if (!isSemicolon(lastToken)) {
					return false;
				}

				nextToken = sourceCode.getTokenAfter(lastToken);

				if (!nextToken) {
					return true;
				}

				lastTokenLine = lastToken.loc.end.line;
				nextTokenLine = nextToken.loc.start.line;
				isOptOutToken = OPT_OUT_PATTERN.test(nextToken.value);
				isDivider = nextToken.value === "}" || nextToken.value === ";";

				return (lastTokenLine !== nextTokenLine && !isOptOutToken) || isDivider;
			}

			/**
			 * Checks a node to see if it's in a one-liner block statement.
			 * @param {ASTNode} node The node to check.
			 * @returns {boolean} whether the node is in a one-liner block statement.
			 */
			function isOneLinerBlock(node) {
				var nextToken = sourceCode.getTokenAfter(node);

				if (!nextToken || nextToken.value !== "}") {
					return false;
				}

				var parent = node.parent;

				return parent && parent.type === "BlockStatement" &&
					parent.loc.start.line === parent.loc.end.line;
			}

			/**
			 * Checks a node to see if it's followed by a semicolon.
			 * @param {ASTNode} node The node to check.
			 * @returns {void}
			 */
			function checkForSemicolon(node) {
				var lastToken = sourceCode.getLastToken(node);

				if (never) {
					if (isUnnecessarySemicolon(lastToken)) {
						report(node, true);
					}
				} else {
					if (!isSemicolon(lastToken)) {
						if (!exceptOneLine || !isOneLinerBlock(node)) {
							report(node);
						}
					} else {
						if (exceptOneLine && isOneLinerBlock(node)) {
							report(node, true);
						}
					}
				}
			}

			/**
			 * Checks to see if there's a semicolon after a variable declaration.
			 * @param {ASTNode} node The node to check.
			 * @returns {void}
			 */
			function checkForSemicolonForVariableDeclaration(node) {
				var ancestors = context.getAncestors(),
					parentIndex = ancestors.length - 1,
					parent = ancestors[parentIndex];

				if ((parent.type !== "ForStatement" || parent.init !== node) &&
					(!/^For(?:In|Of)Statement/.test(parent.type) || parent.left !== node)
				) {
					checkForSemicolon(node);
				}
			}

			//--------------------------------------------------------------------------
			// Public API
			//--------------------------------------------------------------------------

			return {
				VariableDeclaration: checkForSemicolonForVariableDeclaration,
				ExpressionStatement: checkForSemicolon,
				ReturnStatement: checkForSemicolon,
				ThrowStatement: checkForSemicolon,
				DoWhileStatement: checkForSemicolon,
				DebuggerStatement: checkForSemicolon,
				BreakStatement: checkForSemicolon,
				ContinueStatement: checkForSemicolon,
				ImportDeclaration: checkForSemicolon,
				ExportAllDeclaration: checkForSemicolon,
				ExportNamedDeclaration: function(node) {
					if (!node.declaration) {
						checkForSemicolon(node);
					}
				},
				ExportDefaultDeclaration: function(node) {
					if (!/(?:Class|Function)Declaration/.test(node.declaration.type)) {
						checkForSemicolon(node);
					}
				}
			};

		},
		/** @callback */
        "unknown-require": function(context) {
			function checkImportExport(node) {
				var tern = context.getTern();
				if(!tern.pluginRunning("es_modules")) { //$NON-NLS-1$
					// create a location object to flag only the 'import' keyword
					var token = context.getFirstToken(node);
					context.report(node, ProblemMessages['esmodules-not-running'], {pid: 'unknown-require-not-running', nls: 'esmodules-not-running', data: 'es_modules'}, token); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
				}
			}
        	return {
				"ImportDeclaration" : checkImportExport,
				"ExportAllDeclaration" : checkImportExport,
				"ExportDefaultDeclaration" : checkImportExport,
				"ExportNamedDeclaration" : checkImportExport,
				"CallExpression": function(node) {
      				if(node.callee.name === "require") {
        				var args = node.arguments;
        				if(args.length === 1) {
        					var lib = args[0];
        					if(lib.type === "Literal") { //we don't check relative libs
        						if(typeof lib.value !== 'string' || (typeof lib.value == 'string' && !lib.value.trim())) {
        							//don't mark empty strings or non-strings
        							return;
        						}
        						if(lib.value.charAt(0) !== '.') {
	        						var tern = context.getTern();
	        						if(tern.file.ast && tern.file.ast.environments) {
	        							var envs = tern.file.ast.environments;
	        							if(envs.node && !envs.simplifiedCommonJS) {
	        								if(!tern.pluginRunning('node') && !tern.pluginRunning('commonjs')) { //$NON-NLS-1$ //$NON-NLS-2$
	        									context.report(lib, ProblemMessages['unknown-require-not-running'], {0: 'node', pid: 'unknown-require-not-running', nls: 'unknown-require-not-running', data: 'node'}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
	        									return;
	        								}
	        								if(!tern.pluginRunning('commonjs')) { //$NON-NLS-1$
	        									context.report(lib, ProblemMessages['unknown-require-not-running'], {0: 'commonjs', pid: 'unknown-require-not-running', nls: 'unknown-require-not-running', data: 'commonjs'}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
	        									return;
	        								}
	        							}
	        						}
	        						if(tern.plugins[lib.value] || tern.getDef(lib.value) || tern.libKnown(lib.value)) {
	        							return;
	        						}
									//it might be a node built-in, this also confirms its in the running node def
									var nodejs = tern.getDef('node'); //$NON-NLS-1$
									if(nodejs && (nodejs[lib.value] || (nodejs['!define'] && nodejs['!define'][lib.value]))) {
										return;
									}
									//TODO check for the module having been loaded via the graph
									if(tern.optionalPlugins[lib.value]) {
										//we known about it
										context.report(lib, ProblemMessages['unknown-require-plugin'], {pid: 'unknown-require-plugin', nls: 'unknown-require-plugin', data: lib.value}); //$NON-NLS-2$ //$NON-NLS-1$
										return;
									}
									if(nodeModules[lib.value]) {
										context.report(lib, ProblemMessages['unknown-require-plugin'], {pid: 'unknown-require-plugin', nls: 'unknown-require-plugin', data: 'node'}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
										return;
									}
									var env = tern.getEnvFromDep(lib.value);
									if (env === 'commonjs' && envs.simplifiedCommonJS){
										context.report(lib, ProblemMessages['unknown-require'], {data: lib.value});
										return;
									}
									if(!tern.pluginRunning(env)) {
										context.report(lib, ProblemMessages['unknown-require-not-running'], {0: env, pid: 'unknown-require-not-running', nls: 'unknown-require-not-running', data: env}); //$NON-NLS-1$ //$NON-NLS-2$
									} else {
										context.report(lib, ProblemMessages['unknown-require'], {data: lib.value});
									}
	        					} else {
	        						//relative path, if Tern has it in the fileMap its known, otherwise mark it
	        						tern = context.getTern();
	        						if(!tern.hasFile(lib.value, tern.file)) {
	        							context.report(lib, ProblemMessages['unknown-require'], {data: lib.value});
	        						}
	        					}
	    					}
        				}
        			}
        		}
        	};
        },
        /** @callback */
		"use-isnan": function(context) {
        		return {
        			/* @callback */
        			'BinaryExpression' : function(node) {
        					if(node.left.type === 'Identifier' && node.left.name === 'NaN') {
        						context.report(node.left, ProblemMessages['use-isnan'], null, node.left);
        					} else if(node.right.type === 'Identifier' && node.right.name === 'NaN') {
        						context.report(node.right, ProblemMessages['use-isnan'], null, node.right);
        					}
        			}
        		};
        },
        /** @callback */
		'valid-typeof': function(context) {
        		//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
        		var symbols = ['undefined', 'object', 'function', 'boolean', 'number', 'string', 'symbol']; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
        		var ops = ['==', '===', '!=', '!=='];

        		return {
        			/* @callback */
        			'UnaryExpression' : function(node){
        			    if(node.operator === 'typeof') {
        			        var parent = node.parent;
        			        if(parent && parent.type === 'BinaryExpression' &&
        			            ops.indexOf(parent.operator) > -1) {
           			            var val = parent.left === node ? parent.right : parent.left;
        			            if (val.type !== 'Literal' || symbols.indexOf(val.value) < 0) {
        			                context.report(val, ProblemMessages['valid-typeof']);
        			            }
        			        }
        			    }
        			}
        		};
        },
        /**
         * @callback
         */
        'missing-requirejs': function(context) {
        	return {
        		'CallExpression': function(node) {
        			if(node.callee.name === "define") {
        				if(node.arguments.length === 1 && (node.arguments[0].type === 'FunctionExpression' || node.arguments[0].type === 'ObjectExpression')) {
        					if(!context.getTern().pluginRunning('requirejs')) { //$NON-NLS-1$
        						context.report(node.callee, ProblemMessages['missing-requirejs'], {data: 'requirejs'}); //$NON-NLS-1$
        					}
        				} else if(node.arguments.length === 3 && node.arguments[0].type === 'Literal' && typeof node.arguments[0].value === 'string' 
        							&& node.arguments[1] && node.arguments[1].type === 'ArrayExpression' && node.arguments[2].type === 'FunctionExpression') {
        					if(!context.getTern().pluginRunning('requirejs')) { //$NON-NLS-1$
        						context.report(node.callee, ProblemMessages['missing-requirejs'], {data: 'requirejs'}); //$NON-NLS-1$
        					}
        				} else if(node.arguments.length === 2 && node.arguments[0].type === 'ArrayExpression' && node.arguments[1] && node.arguments[1].type === 'FunctionExpression') {
        					if(!context.getTern().pluginRunning('requirejs')) { //$NON-NLS-1$
        						context.report(node.callee, ProblemMessages['missing-requirejs'], {data: 'requirejs'}); //$NON-NLS-1$
        					}
        				}
    				}
        		}
        	};	
        },
        // Rules consumed from ESLint 3rd party library
		'accessor-pairs': accessorPairs,
		'no-control-regex': noControlRegex,
		'no-duplicate-case': noDuplicateCase,
		'no-empty-character-class': noEmptyCharClasses,
		'no-extra-boolean-cast': noExtraBoolCast,
		'no-extra-parens': noExtraParens,
		'no-invalid-regexp': noInvalidRegExp,
		'no-negated-in-lhs': noNegatedInLhs,
		'no-obj-calls': noObjCalls,
		'no-eq-null' : noEqNull,
		'no-else-return': noElseReturn,
		'no-empty-label': noEmptyLabel,
		'no-self-compare': noSelfCompare,
		'no-irregular-whitespace': noIrregularWhitespace,
		'no-const-assign' : noConstAssign,
		/** @callback */
		'no-self-assign': function(context) {
				/**
				 * @description Check the variable declarator node for self-assignment
				 * @param {Object} variableDeclarator The AST node
				 */
				function checkVariableDeclarator(variableDeclarator) {
					var init = variableDeclarator.init;
					var id = variableDeclarator.id;
					if (init
							&& init.type === 'Identifier'
							&& id.type === 'Identifier'
							&& id.name === init.name) {
						context.report(variableDeclarator, ProblemMessages['no-self-assign'], {0: id.name, pid: 'no-self-assign', nls: 'no-self-assign'}); //$NON-NLS-1$ //$NON-NLS-2$
					}
				}
				/**
				 * @description Check the assingment expression node for self-assignment
				 * @param {Object} assignment The AST node
				 */
				function checkAssignmentExpression(assignment) {
					if (assignment.operator === '='){
						var left = assignment.left;
						var right = assignment.right;
						if (left.type === 'Identifier'
								&& right.type === 'Identifier'
								&& left.name === right.name) {
							context.report(assignment, ProblemMessages['no-self-assign'], {0: left.name, pid: 'no-self-assign', nls: 'no-self-assign'}); //$NON-NLS-1$ //$NON-NLS-2$
						}
					}
				}
				return {
					"AssignmentExpression" : checkAssignmentExpression,
					"VariableDeclarator" : checkVariableDeclarator
				};
			},
		/** @callback */
		'type-checked-consistent-return' : function(context) {
				var functions = [];
			
				//--------------------------------------------------------------------------
				// Helpers
				//--------------------------------------------------------------------------
			
				/**
				 * Marks entrance into a function by pushing a new object onto the functions
				 * stack.
				 * @returns {void}
				 * @private
				 */
				function enterFunction() {
					functions.push({});
				}
			
				/**
				 * Marks exit of a function by popping off the functions stack.
				 * @returns {void}
				 * @private
				 */
				function exitFunction() {
					functions.pop();
				}
				/**
				 * @description Try to fetch the backing type value from Tern
				 * @param {Object} node The AST node
				 * @returns {String} The name of the type of the value
				 */
				function getValue(node) {
					if (node.argument) {
						var tern = context.getTern();
						var query = {end: node.argument.start};
						var foundType = null;
						var expr = tern.findQueryExpr(tern.file, query);
						if (!expr) {
							return "undefined"; //$NON-NLS-1$
						}
						var type = tern.findExprType(query, tern.file, expr);
						if (type) {
							foundType = type;
						}
						if (foundType) {
							var typeString = foundType.toString();
							switch(typeString) {
								case "bool" :
									return "boolean"; //$NON-NLS-1$
								case "{}" :
									return "object"; //$NON-NLS-1$
								case "?" :
									return "null"; //$NON-NLS-1$
								default :
									return typeString;
							}
						}
						return "object"; //$NON-NLS-1$
					}
					return "undefined"; //$NON-NLS-1$
				}
			
				//--------------------------------------------------------------------------
				// Public
				//--------------------------------------------------------------------------
			
				return {
			
					"Program": enterFunction,
					"FunctionDeclaration": enterFunction,
					"FunctionExpression": enterFunction,
					"ArrowFunctionExpression": enterFunction,
			
					"Program:exit": exitFunction,
					"FunctionDeclaration:exit": exitFunction,
					"FunctionExpression:exit": exitFunction,
					"ArrowFunctionExpression:exit": exitFunction,
					/* @callback */
					"ReturnStatement": function(node) {
						var returnInfo = functions[functions.length - 1];
						var returnTypeDefined = "type" in returnInfo;
			
						if (returnTypeDefined) {
							var typeOfReturnStatement = getValue(node);
							var storeType = returnInfo.type;
							if (storeType !== typeOfReturnStatement) {
								// "null" and "object", "string" or String" are compatible
								switch(storeType) {
									case "null" :
										if (typeOfReturnStatement !== "object" && typeOfReturnStatement !== "String" && typeOfReturnStatement !== "string") {
											context.report(node, ProblemMessages['inconsistent-return'], {type1: storeType, type2: typeOfReturnStatement});
										}
										break;
									case "String" :
									case "string" :
										if (typeOfReturnStatement !== "null") {
											context.report(node, ProblemMessages['inconsistent-return'], {type1: storeType, type2: typeOfReturnStatement});
										}
										break;
									case "object" :
										if (typeOfReturnStatement !== "null") {
											context.report(node, ProblemMessages['inconsistent-return'], {type1: storeType, type2: typeOfReturnStatement});
										}
										break;
									default:
										context.report(node, ProblemMessages['inconsistent-return'], {type1: storeType, type2: typeOfReturnStatement});
								}
							}
						} else {
							returnInfo.type = getValue(node);
						}
					}
				};
			},
		'no-void': function(context) {
			return {
				"UnaryExpression": function(node) {
					if (node.operator === "void") {
						context.report(node, ProblemMessages['no-void']);
					}
				}
			};
		},
		'no-extra-bind': noExtraBind,
		'no-implicit-coercion': noImplicitCoercion,
		'no-extend-native': noExtendNative,
		'no-lone-blocks' : noLoneBlocks,
		'quotes' : quotes,
		'yoda' : yoda,
		'no-param-reassign' : noParamReassign,
		'no-native-reassign' : noNativeReassign,
		'no-unused-expressions' : noUnusedExpressions,
		'no-invalid-this' : noInvalidThis,
		'no-trailing-spaces' : noTrailingSpaces
	};

	/**
	 * @description Map all of the callees in the given array into the obj map
	 * @private
	 * @param {Array.<Object>} arr The array of callees
	 * @param {Object} obj The map
	 */
	function _mapCallees(arr, obj) {
		for(var i = 0; i < arr.length; i++) {
			obj[arr[i]] = true;
		}
	}

	var _callees = Object.create(null);
	_mapCallees(['require', 'requirejs', 'importScripts', 'define', 'Worker', 'SharedWorker', 'addEventListener', 'RegExp', //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
	'removeEventListener'], _callees);  //$NON-NLS-1$
    var _documentCallees = Object.create(null);
    _mapCallees(['createElement'], _documentCallees); //$NON-NLS-1$
    
    /**
     * @description Collects all the string literals and their location infos into the line mapping
     * @private
     * @param {Object} node The AST node to check
     * @param {Object} lineMap The mapping of literals and line infos
     */
    function _collectLinesWithStringLiterals(node, lineMap){
    	
    	// Create a map of line numbers to a list of literal nodes
    	if (typeof node.value === 'string' && node.value.length > 0){
    		if (node.value.toLowerCase() === 'use strict'){
    			return;
    		}
    		if(/^(?:[\.,-\/#!$%\^&\*;:{}=\-_`~()@\+\?><\[\]])$/.test(node.value)) {
    			return; //don't nag about punctuation
    		} else if(/^(?:==|!=|===|!==|=>)$/.test(node.value)) {
    			return; //don't nag about operators
    		}
    		if (node.parent){
    			switch(node.parent.type) {
    				case 'ImportDeclaration':
    				case 'MemberExpression':
    				case 'SwitchCase':
        			case 'ExportAllDeclaration' :
    				case 'UnaryExpression': {
    					return;
    				}
    				case 'BinaryExpression': {
    					if(node.parent.operator !== '+') {
    						return;
    					}
    					break;
    				}
    				case 'Property': {
    					if(node.parent.key === node) {
    						return;
    					}
						var _p = node.parent.parent.parent;
						if(_p && _p.type === 'CallExpression' && _p.callee && _p.callee.name === 'define') {
							return;
						}
    					break;
    				}
    				case 'NewExpression':
    				case 'CallExpression': {
    					var callee = node.parent.callee;
    					if(callee) {
    						if(callee.type === 'MemberExpression' && callee.property) {
    							if(callee.object && callee.object.type === "Identifier" && callee.object.name === "document" && _documentCallees[callee.property.name]) {
    								return;
    							} else if(_callees[callee.property.name]) {
	    							return;
								}
    						} else if(_callees[callee.name]) {
    							return;
    						}
    					}
    					break;
    				}
    				case 'ArrayExpression': {
    					_p = node.parent.parent;
    					if(_p.type === 'CallExpression' && (_p.callee.name === 'define' || _p.callee.name === 'require' || _p.callee.name === 'requirejs')) {
    						return;
    					}
    					break;
    				}
    			}
    		}
    		var lineNum = node.loc.end.line;
    		if (!lineMap[lineNum]){
    			lineMap[lineNum] = [];
    		}
    		lineMap[lineNum].push(node);
    	}
    }

	return {
		rules: rules
	};
});

/* eslint-env amd */
define('eslint/lib/config/rules',[
	'../load-rules-async',
	'exports',
],
function(Rules, exports) {
	/**
	 * @fileoverview Defines a storage for rules.
	 * @author Nicholas C. Zakas
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Privates
	//------------------------------------------------------------------------------

	var rules = Object.create(null);

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	/**
	 * Registers a rule module for rule id in storage.
	 * @param {String} ruleId Rule id (file name).
	 * @param {Function} ruleModule Rule handler.
	 * @returns {void}
	 */
	function define(ruleId, ruleModule) {
		rules[ruleId] = ruleModule;
	}

	/**
	 * Loads and registers all rules from passed rules directory.
	 * @param {String} [rulesDir] Path to rules directory, may be relative. Defaults to `lib/rules`.
	 * @returns {void}
	 */
	function load() {
		var newRules = Rules.rules;
		Object.keys(newRules).forEach(function(ruleId) {
			define(ruleId, newRules[ruleId]);
		});
	}

	/**
	 * Registers all given rules of a plugin.
	 * @param {Object} pluginRules A key/value map of rule definitions.
	 * @param {string} pluginName The name of the plugin without prefix (`eslint-plugin-`).
	 * @returns {void}
	 */
	function importPlugin(pluginRules, pluginName) {
		Object.keys(pluginRules).forEach(function(ruleId) {
			var qualifiedRuleId = pluginName + "/" + ruleId,
				rule = pluginRules[ruleId];

			define(qualifiedRuleId, rule);
		});
	}

	/**
	 * Access rule handler by id (file name).
	 * @param {String} ruleId Rule id (file name).
	 * @returns {Function} Rule handler.
	 */
	function get(ruleId) {
		return rules[ruleId];
	}

	function testClear() {
		rules = Object.create(null);
	}

	exports = {
		define: define,
		load: load,
		import: importPlugin,
		get: get,
		testClear: testClear,

		/**
		 * Resets rules to its starting state. Use for tests only.
		 * @returns {void}
		 */
		testReset: function() {
			testClear();
			load();
		}
	};

	//------------------------------------------------------------------------------
	// Initialization
	//------------------------------------------------------------------------------

	// loads built-in rules
	load();


	return exports;
});
/* eslint-env amd */
define('eslint/lib/rule-fixer',[
], function() {
/**
 * @fileoverview An object that creates fix commands for rules.
 * @author Nicholas C. Zakas
 * @copyright 2015 Nicholas C. Zakas. All rights reserved.
 * See LICENSE file in root directory for full license.
 */

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Creates a fix command that inserts text at the specified index in the source text.
 * @param {int} index The 0-based index at which to insert the new text.
 * @param {string} text The text to insert.
 * @returns {Object} The fix command.
 * @private
 */
function insertTextAt(index, text) {
    return {
        range: [index, index],
        text: text
    };
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

/**
 * Creates code fixing commands for rules.
 * @constructor
 */
function RuleFixer() {
    Object.freeze(this);
}

RuleFixer.prototype = {
    constructor: RuleFixer,

    /**
     * Creates a fix command that inserts text after the given node or token.
     * The fix is not applied until applyFixes() is called.
     * @param {ASTNode|Token} nodeOrToken The node or token to insert after.
     * @param {string} text The text to insert.
     * @returns {Object} The fix command.
     */
    insertTextAfter: function(nodeOrToken, text) {
        return this.insertTextAfterRange(nodeOrToken.range, text);
    },

    /**
     * Creates a fix command that inserts text after the specified range in the source text.
     * The fix is not applied until applyFixes() is called.
     * @param {int[]} range The range to replace, first item is start of range, second
     *      is end of range.
     * @param {string} text The text to insert.
     * @returns {Object} The fix command.
     */
    insertTextAfterRange: function(range, text) {
        return insertTextAt(range[1], text);
    },

    /**
     * Creates a fix command that inserts text before the given node or token.
     * The fix is not applied until applyFixes() is called.
     * @param {ASTNode|Token} nodeOrToken The node or token to insert before.
     * @param {string} text The text to insert.
     * @returns {Object} The fix command.
     */
    insertTextBefore: function(nodeOrToken, text) {
        return this.insertTextBeforeRange(nodeOrToken.range, text);
    },

    /**
     * Creates a fix command that inserts text before the specified range in the source text.
     * The fix is not applied until applyFixes() is called.
     * @param {int[]} range The range to replace, first item is start of range, second
     *      is end of range.
     * @param {string} text The text to insert.
     * @returns {Object} The fix command.
     */
    insertTextBeforeRange: function(range, text) {
        return insertTextAt(range[0], text);
    },

    /**
     * Creates a fix command that replaces text at the node or token.
     * The fix is not applied until applyFixes() is called.
     * @param {ASTNode|Token} nodeOrToken The node or token to remove.
     * @param {string} text The text to insert.
     * @returns {Object} The fix command.
     */
    replaceText: function(nodeOrToken, text) {
        return this.replaceTextRange(nodeOrToken.range, text);
    },

    /**
     * Creates a fix command that replaces text at the specified range in the source text.
     * The fix is not applied until applyFixes() is called.
     * @param {int[]} range The range to replace, first item is start of range, second
     *      is end of range.
     * @param {string} text The text to insert.
     * @returns {Object} The fix command.
     */
    replaceTextRange: function(range, text) {
        return {
            range: range,
            text: text
        };
    },

    /**
     * Creates a fix command that removes the node or token from the source.
     * The fix is not applied until applyFixes() is called.
     * @param {ASTNode|Token} nodeOrToken The node or token to remove.
     * @returns {Object} The fix command.
     */
    remove: function(nodeOrToken) {
        return this.removeRange(nodeOrToken.range);
    },

    /**
     * Creates a fix command that removes the specified range of text from the source.
     * The fix is not applied until applyFixes() is called.
     * @param {int[]} range The range to remove, first item is start of range, second
     *      is end of range.
     * @returns {Object} The fix command.
     */
    removeRange: function(range) {
        return {
            range: range,
            text: ""
        };
    }

};

return RuleFixer;

});
/* eslint-env amd */
define('eslint/lib/rule-context',[
	'./rule-fixer',
], function(RuleFixer) {
	/**
	 * @fileoverview RuleContext utility for rules
	 * @author Nicholas C. Zakas
	 * @copyright 2013 Nicholas C. Zakas. All rights reserved.
	 * See LICENSE file in root directory for full license.
	 */
	"use strict";
	//------------------------------------------------------------------------------
	// Constants
	//------------------------------------------------------------------------------

	var PASSTHROUGHS = [
		"getAncestors",
		"getDeclaredVariables",
		"getFilename",
		"getScope",
		"markVariableAsUsed",

		// DEPRECATED
		"getAllComments",
		"getComments",
		"getFirstToken",
		"getFirstTokens",
		"getJSDocComment",
		"getLastToken",
		"getLastTokens",
		"getNodeByRangeIndex",
		"getSource",
		"getSourceLines",
		"getTokenAfter",
		"getTokenBefore",
		"getTokenByRangeStart",
		"getTokens",
		"getTokensAfter",
		"getTokensBefore",
		"getTokensBetween"
	];

	//------------------------------------------------------------------------------
	// Typedefs
	//------------------------------------------------------------------------------

	/**
	 * An error message description
	 * @typedef {Object} MessageDescriptor
	 * @property {string} nodeType The type of node.
	 * @property {Location} loc The location of the problem.
	 * @property {string} message The problem message.
	 * @property {Object} [data] Optional data to use to fill in placeholders in the
	 *      message.
	 * @property {Function} fix The function to call that creates a fix command.
	 */

	//------------------------------------------------------------------------------
	// Rule Definition
	//------------------------------------------------------------------------------

	/**
	 * Acts as an abstraction layer between rules and the main eslint object.
	 * @constructor
	 * @param {string} ruleId The ID of the rule using this object.
	 * @param {eslint} eslint The eslint object.
	 * @param {number} severity The configured severity level of the rule.
	 * @param {Array} options The configuration information to be added to the rule.
	 * @param {Object} settings The configuration settings passed from the config file.
	 * @param {Object} parserOptions The parserOptions settings passed from the config file.
	 * @param {Object} parserPath The parser setting passed from the config file.
	 * @param {Object} meta The metadata of the rule
	 */
	function RuleContext(ruleId, eslint, severity, options, settings, parserOptions, parserPath, meta, env) {

		// public.
		this.id = ruleId;
		this.options = options;
		this.settings = settings;
		this.parserOptions = parserOptions;
		this.parserPath = parserPath;
		this.meta = meta;

		// private.
		this.eslint = eslint;
		this.severity = severity;

		// ORION
		this.env = env;
		//Object.freeze(this); ORION we use the context to store the tern object and store temporary lines for nls checks.
	}

	RuleContext.prototype = {
		constructor: RuleContext,

		/**
		 * Passthrough to eslint.getSourceCode().
		 * @returns {SourceCode} The SourceCode object for the code.
		 */
		getSourceCode: function() {
			return this.eslint.getSourceCode();
		},

		/**
		 * Passthrough to eslint.report() that automatically assigns the rule ID and severity.
		 * @param {ASTNode|MessageDescriptor} nodeOrDescriptor The AST node related to the message or a message
		 *      descriptor.
		 * @param {Object=} location The location of the error.
		 * @param {string} message The message to display to the user.
		 * @param {Object} opts Optional template data which produces a formatted message
		 *     with symbols being replaced by this object's values.
		 * @returns {void}
		 */
		report: function(nodeOrDescriptor, location, message, opts) {
			var descriptor,
				fix = null;

			// check to see if it's a new style call
			if (arguments.length === 1) {
				descriptor = nodeOrDescriptor;

				// if there's a fix specified, get it
				if (typeof descriptor.fix === "function") {
					fix = descriptor.fix(new RuleFixer());
				}

				this.eslint.report(
					this.id,
					this.severity,
					descriptor.node,
					descriptor.loc || descriptor.node.loc.start,
					descriptor.message,
					descriptor.data,
					fix,
					this.meta
				);

				return;
			}

			// old style call
			this.eslint.report(
				this.id,
				this.severity,
				nodeOrDescriptor,
				location,
				message,
				opts,
				this.meta
			);
		},
		
		getTern : function() {
			return this.settings.tern;
		}
	};

	// Copy over passthrough methods. All functions will have 5 or fewer parameters.
	PASSTHROUGHS.forEach(function(name) {
		this[name] = function(a, b, c, d, e) {
			return this.eslint[name](a, b, c, d, e);
		};
	}, RuleContext.prototype);

	return RuleContext;
});
/*******************************************************************************
 * @license
 * Copyright (c) 2012 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env browser, amd*/
define('orion/EventTarget',[],function() {
	/**
	 * Creates an Event Target
	 *
	 * @name orion.EventTarget
	 * @class Base for creating an Orion event target
	 */
	function EventTarget() {
		this._namedListeners = {};
	}

	EventTarget.prototype = /** @lends orion.EventTarget.prototype */
	{
		/**
		 * Dispatches a named event along with an arbitrary set of arguments. Any arguments after <code>eventName</code>
		 * will be passed to the event listener(s).
		 * @param {Object} event The event to dispatch. The event object MUST have a type field
		 * @returns {boolean} false if the event has been canceled and any associated default action should not be performed
		 * listeners (if any) have resolved.
		 */
		dispatchEvent: function(event) {
			if (!event.type) {
				throw new Error("unspecified type");
			}
			var listeners = this._namedListeners[event.type];
			if (listeners) {
				listeners.forEach(function(listener) {
					try {
						if (typeof listener === "function") {
							listener(event);
						} else {
							listener.handleEvent(event);
						}
					} catch (e) {
						if (typeof console !== 'undefined') {
							console.log(e); // for now, probably should dispatch an ("error", e)
						}
					}			
				});
			}
			return !event.defaultPrevented;
		},

		/**
		 * Adds an event listener for a named event
		 * @param {String} eventName The event name
		 * @param {Function} listener The function called when an event occurs
		 */
		addEventListener: function(eventName, listener) {
			if (typeof listener === "function" || listener.handleEvent) {
				this._namedListeners[eventName] = this._namedListeners[eventName] || [];
				this._namedListeners[eventName].push(listener);
			}
		},

		/**
		 * Removes an event listener for a named event
		 * @param {String} eventName The event name
		 * @param {Function} listener The function called when an event occurs
		 */
		removeEventListener: function(eventName, listener) {
			var listeners = this._namedListeners[eventName];
			if (listeners) {
				for (var i = 0; i < listeners.length; i++) {
					if (listeners[i] === listener) {
						if (listeners.length === 1) {
							delete this._namedListeners[eventName];
						} else {
							listeners.splice(i, 1);
						}
						break;
					}
				}
			}
		}
	};
	EventTarget.prototype.constructor = EventTarget;
	
	EventTarget.attach = function(obj) {
		var eventTarget = new EventTarget();
		obj.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget);
		obj.addEventListener = eventTarget.addEventListener.bind(eventTarget);
		obj.removeEventListener = eventTarget.removeEventListener.bind(eventTarget);
	};
	
	return EventTarget;
});
/*******************************************************************************
 * @license
 * Copyright (c) 2013, 2015 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*
 * Shim for Node events API
 * http://nodejs.org/api/events.html
 */
/*eslint-env amd */
/*global console*/
define('eslint/lib/events',[
	"orion/EventTarget",
	"orion/objects"
], function(EventTarget, objects) {
	var DEFAULT_MAX_LISTENERS = 10;

	function EventEmitter() {
		this._eventTarget = new EventTarget();
	}

	function addListener(eventName, listener) {
		if (typeof listener !== "function") {
			throw new Error("addListener only takes instances of Function");
		}
		var max = typeof this._maxListeners !== "undefined" ? this._maxListeners : DEFAULT_MAX_LISTENERS;
		var count;
		if (max !== 0 && (count = EventEmitter.listenerCount(this, eventName) >= max)) {
			if (typeof console !== "undefined") {
				console.error("Possible EventEmitter memory leak: " + count + " listeners added.");
			}
		}
		this.emit("newListener", listener);
		this._eventTarget.addEventListener(eventName, listener);
		return this;
	}

	EventEmitter.prototype.constructor = EventEmitter;
	objects.mixin(EventEmitter.prototype, {
		_maxListeners: 10,
		addListener: addListener,
		on: addListener,
		once: function(eventName, listener) {
			var emitter = this;
			var oneTimeListener = /* @callback */ function(event) {
				try {
					listener.apply(this, Array.prototype.slice.call(arguments));
				} finally {
					emitter.removeListener(eventName, oneTimeListener);
				}
			};
			this.addListener(eventName, oneTimeListener);
			return this;
		},
		removeListener: function(eventName, listener) {
			if (typeof listener !== "function") {
				throw new Error("removeListener only takes instances of Function");
			}
			this._eventTarget.removeEventListener(eventName, listener);
			this.emit("removeListener", listener);
			return this;
		},
		removeAllListeners: function(eventName) {
			var namedListeners = this._eventTarget._namedListeners;
			var emitter = this;
			var removeAllListenersFor = function(eventName) {
				var listeners = namedListeners[eventName];
				if (!listeners) {
					return;
				}
				listeners.forEach(emitter.emit.bind(emitter, "removeListener"));
				delete namedListeners[eventName];
			};
			if (typeof eventName === "undefined") {
				Object.keys(namedListeners).forEach(removeAllListenersFor);
			} else {
				removeAllListenersFor(eventName);
			}
			return this;
		},
		setMaxListeners: function(n) {
			if (typeof n !== "number") {
				throw new Error("setMaxListeners only takes a number");
			}
			this._maxListeners = n;
		},
		listeners: function(eventName) {
			var listeners = this._eventTarget._namedListeners[eventName];
			return listeners ? listeners.slice() : [];
		},
		emit: function emit(eventName /*, arg1, arg2, ...*/) {
			var listeners = this._eventTarget._namedListeners[eventName];
			if (!listeners) {
				if (eventName === "error") {
					throw new Error("Uncaught, unspecified 'error' event.");
				}
				return false;
			}
			var args = Array.prototype.slice.call(arguments, 1);
			var emitter = this;
			listeners.forEach(function(listener) {
				// To match Node's behavior we intentionally allow an exception thrown by listener to blow up the stack.
				listener.apply(emitter, args);
			});
			return true;
		}
	});
	EventEmitter.listenerCount = function(emitter, eventName) {
		var listeners = emitter._eventTarget._namedListeners[eventName];
		return listeners ? listeners.length : 0;
	};

	return {
		EventEmitter: EventEmitter
	};
});
/* eslint-env amd */
define('eslint/lib/utils/token-store',[],
function() {
	/**
	 * @fileoverview Object to handle access and retrieval of tokens.
	 * @author Brandon Mills
	 * @copyright 2014 Nicholas C. Zakas. All rights reserved.
	 * @copyright 2014 Brandon Mills. All rights reserved.
	 */
	//------------------------------------------------------------------------------
	// Implementation
	//------------------------------------------------------------------------------

	return function(tokens) {
		var api = {},
			starts = Object.create(null),
			ends = Object.create(null),
			index, length, range;

		/**
		 * Gets tokens in a given interval.
		 * @param {int} start Inclusive index of the first token. 0 if negative.
		 * @param {int} end Exclusive index of the last token.
		 * @returns {Token[]} Tokens in the interval.
		 */
		function get(start, end) {
			var result = [],
				i;

			for (i = Math.max(0, start); i < end && i < length; i++) {
				result.push(tokens[i]);
			}

			return result;
		}

		/**
		 * Gets the index in the tokens array of the last token belonging to a node.
		 * Usually a node ends exactly at a token, but due to ASI, sometimes a
		 * node's range extends beyond its last token.
		 * @param {ASTNode} node The node for which to find the last token's index.
		 * @returns {int} Index in the tokens array of the node's last token.
		 */
		function lastTokenIndex(node) {
			var end = node.range[1],
				cursor = ends[end];

			// If the node extends beyond its last token, get the token before the
			// next token
			if (typeof cursor === "undefined") {
				cursor = starts[end] - 1;
			}

			// If there isn't a next token, the desired token is the last one in the
			// array
			if (isNaN(cursor)) {
				cursor = length - 1;
			}

			return cursor;
		}

		// Map tokens' start and end range to the index in the tokens array
		for (index = 0, length = tokens.length; index < length; index++) {
			range = tokens[index].range;
			starts[range[0]] = index;
			ends[range[1]] = index;
		}

		/**
		 * Gets a number of tokens that precede a given node or token in the token
		 * stream.
		 * @param {(ASTNode|Token)} node The AST node or token.
		 * @param {int} [beforeCount=0] The number of tokens before the node or
		 *     token to retrieve.
		 * @returns {Token[]} Array of objects representing tokens.
		 */
		api.getTokensBefore = function(node, beforeCount) {
			var first = starts[node.range[0]];
			return get(first - (beforeCount || 0), first);
		};

		/**
		 * Gets the token that precedes a given node or token in the token stream.
		 * @param {(ASTNode|Token)} node The AST node or token.
		 * @param {int} [skip=0] A number of tokens to skip before the given node or
		 *     token.
		 * @returns {Token} An object representing the token.
		 */
		api.getTokenBefore = function(node, skip) {
			return tokens[starts[node.range[0]] - (skip || 0) - 1];
		};

		/**
		 * Gets a number of tokens that follow a given node or token in the token
		 * stream.
		 * @param {(ASTNode|Token)} node The AST node or token.
		 * @param {int} [afterCount=0] The number of tokens after the node or token
		 *     to retrieve.
		 * @returns {Token[]} Array of objects representing tokens.
		 */
		api.getTokensAfter = function(node, afterCount) {
			var start = lastTokenIndex(node) + 1;
			return get(start, start + (afterCount || 0));
		};

		/**
		 * Gets the token that follows a given node or token in the token stream.
		 * @param {(ASTNode|Token)} node The AST node or token.
		 * @param {int} [skip=0] A number of tokens to skip after the given node or
		 *     token.
		 * @returns {Token} An object representing the token.
		 */
		api.getTokenAfter = function(node, skip) {
			return tokens[lastTokenIndex(node) + (skip || 0) + 1];
		};

		/**
		 * Gets all tokens that are related to the given node.
		 * @param {ASTNode} node The AST node.
		 * @param {int} [beforeCount=0] The number of tokens before the node to retrieve.
		 * @param {int} [afterCount=0] The number of tokens after the node to retrieve.
		 * @returns {Token[]} Array of objects representing tokens.
		 */
		api.getTokens = function(node, beforeCount, afterCount) {
			return get(
				starts[node.range[0]] - (beforeCount || 0),
				lastTokenIndex(node) + (afterCount || 0) + 1
			);
		};

		/**
		 * Gets the first `count` tokens of the given node's token stream.
		 * @param {ASTNode} node The AST node.
		 * @param {int} [count=0] The number of tokens of the node to retrieve.
		 * @returns {Token[]} Array of objects representing tokens.
		 */
		api.getFirstTokens = function(node, count) {
			var first = starts[node.range[0]];
			return get(
				first,
				Math.min(lastTokenIndex(node) + 1, first + (count || 0))
			);
		};

		/**
		 * Gets the first token of the given node's token stream.
		 * @param {ASTNode} node The AST node.
		 * @param {int} [skip=0] A number of tokens to skip.
		 * @returns {Token} An object representing the token.
		 */
		api.getFirstToken = function(node, skip) {
			return tokens[starts[node.range[0]] + (skip || 0)];
		};

		/**
		 * Gets the last `count` tokens of the given node.
		 * @param {ASTNode} node The AST node.
		 * @param {int} [count=0] The number of tokens of the node to retrieve.
		 * @returns {Token[]} Array of objects representing tokens.
		 */
		api.getLastTokens = function(node, count) {
			var last = lastTokenIndex(node) + 1;
			return get(Math.max(starts[node.range[0]], last - (count || 0)), last);
		};

		/**
		 * Gets the last token of the given node's token stream.
		 * @param {ASTNode} node The AST node.
		 * @param {int} [skip=0] A number of tokens to skip.
		 * @returns {Token} An object representing the token.
		 */
		api.getLastToken = function(node, skip) {
			return tokens[lastTokenIndex(node) - (skip || 0)];
		};

		/**
		 * Gets all of the tokens between two non-overlapping nodes.
		 * @param {ASTNode} left Node before the desired token range.
		 * @param {ASTNode} right Node after the desired token range.
		 * @param {int} [padding=0] Number of extra tokens on either side of center.
		 * @returns {Token[]} Tokens between left and right plus padding.
		 */
		api.getTokensBetween = function(left, right, padding) {
			var currentPadding = padding || 0;
			return get(
				lastTokenIndex(left) + 1 - currentPadding,
				starts[right.range[0]] + currentPadding
			);
		};

		/**
		 * Gets the token starting at the specified index.
		 * @param {int} startIndex Index of the start of the token's range.
		 * @returns {Token} The token starting at index, or null if no such token.
		 */
		api.getTokenByRangeStart = function(startIndex) {
			return tokens[starts[startIndex]] || null;
		};

		return api;
	};
});
/* eslint-env amd */
define('eslint/lib/utils/source-code',[
	'./token-store',
	'./traverser',
	'./util'
], function(createTokenStore, Traverser, utils) {
	/**
 * @fileoverview Abstraction of JavaScript source code.
 * @author Nicholas C. Zakas
 * @copyright 2015 Nicholas C. Zakas. All rights reserved.
 * See LICENSE file in root directory for full license.
 */

/* eslint no-underscore-dangle: 0*/
//------------------------------------------------------------------------------
// Private
//------------------------------------------------------------------------------

/**
 * Validates that the given AST has the required information.
 * @param {ASTNode} ast The Program node of the AST to check.
 * @throws {Error} If the AST doesn't contain the correct information.
 * @returns {void}
 * @private
 */
function validate(ast) {

    if (!ast.tokens) {
        throw new Error("AST is missing the tokens array.");
    }

    if (!ast.comments) {
        throw new Error("AST is missing the comments array.");
    }

    if (!ast.loc) {
        throw new Error("AST is missing location information.");
    }

    if (!ast.range) {
        throw new Error("AST is missing range information");
    }
}

/**
 * Finds a JSDoc comment node in an array of comment nodes.
 * @param {ASTNode[]} comments The array of comment nodes to search.
 * @param {int} line Line number to look around
 * @returns {ASTNode} The node if found, null if not.
 * @private
 */
function findJSDocComment(comments, line) {

    if (comments) {
        for (var i = comments.length - 1; i >= 0; i--) {
            if (comments[i].type === "Block" && comments[i].value.charAt(0) === "*") {

                if (line - comments[i].loc.end.line <= 1) {
                    return comments[i];
                }
                break;
            }
        }
    }

    return null;
}

/**
 * Check to see if its a ES6 export declaration
 * @param {ASTNode} astNode - any node
 * @returns {boolean} whether the given node represents a export declaration
 * @private
 */
function looksLikeExport(astNode) {
    return astNode.type === "ExportDefaultDeclaration" || astNode.type === "ExportNamedDeclaration" ||
        astNode.type === "ExportAllDeclaration" || astNode.type === "ExportSpecifier";
}


//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

/**
 * Represents parsed source code.
 * @param {string} text The source code text.
 * @param {ASTNode} ast The Program node of the AST representing the code.
 * @constructor
 */
function SourceCode(text, ast) {

    validate(ast);

    /**
     * The flag to indicate that the source code has Unicode BOM.
     * @type boolean
     */
    this.hasBOM = text.charCodeAt(0) === 0xFEFF;

   /**
     * The original text source code.
     * BOM was stripped from this text.
     * @type string
     */
    this.text = this.hasBOM ? text.slice(1) : text;

    /**
     * The parsed AST for the source code.
     * @type ASTNode
     */
    this.ast = ast;

    /**
     * The source code split into lines according to ECMA-262 specification.
     * This is done to avoid each rule needing to do so separately.
     * @type string[]
     */
    this.lines = SourceCode.splitLines(this.text);

    this.tokensAndComments = ast.tokens.concat(ast.comments).sort(function(left, right) {
        return left.range[0] - right.range[0];
    });

    // create token store methods
    var tokenStore = createTokenStore(ast.tokens);
    Object.keys(tokenStore).forEach(function(methodName) {
        this[methodName] = tokenStore[methodName];
    }, this);

    var tokensAndCommentsStore = createTokenStore(this.tokensAndComments);

    this.getTokenOrCommentBefore = tokensAndCommentsStore.getTokenBefore;
    this.getTokenOrCommentAfter = tokensAndCommentsStore.getTokenAfter;

    // don't allow modification of this object
    Object.freeze(this);
    Object.freeze(this.lines);
}

/**
 * Split the source code into multiple lines based on the line delimiters
 * @param {string} text Source code as a string
 * @returns {string[]} Array of source code lines
 * @public
 */
SourceCode.splitLines = function(text) {
	return text.split(/\r\n|\r|\n|\u2028|\u2029/g);
};

SourceCode.prototype = {
    constructor: SourceCode,

    /**
     * Gets the source code for the given node.
     * @param {ASTNode=} node The AST node to get the text for.
     * @param {int=} beforeCount The number of characters before the node to retrieve.
     * @param {int=} afterCount The number of characters after the node to retrieve.
     * @returns {string} The text representing the AST node.
     */
    getText: function(node, beforeCount, afterCount) {
        if (node) {
            return (this.text !== null) ? this.text.slice(Math.max(node.range[0] - (beforeCount || 0), 0),
                node.range[1] + (afterCount || 0)) : null;
        }
        return this.text;

    },

    /**
     * Gets the entire source text split into an array of lines.
     * @returns {Array} The source text as an array of lines.
     */
    getLines: function() {
        return this.lines;
    },

    /**
     * Retrieves an array containing all comments in the source code.
     * @returns {ASTNode[]} An array of comment nodes.
     */
    getAllComments: function() {
        return this.ast.comments;
    },

        /**
     * Gets all comments for the given node.
     * @param {ASTNode} node The AST node to get the comments for.
     * @returns {Object} The list of comments indexed by their position.
     * @public
     */
    getComments: function(node) {

        var leadingComments = node.leadingComments || [],
            trailingComments = node.trailingComments || [];

        /*
         * espree adds a "comments" array on Program nodes rather than
         * leadingComments/trailingComments. Comments are only left in the
         * Program node comments array if there is no executable code.
         */
        if (node.type === "Program") {
            if (node.body.length === 0) {
                leadingComments = node.comments;
            }
        }

        return {
            leading: leadingComments,
            trailing: trailingComments
        };
    },

    /**
     * Retrieves the JSDoc comment for a given node.
     * @param {ASTNode} node The AST node to get the comment for.
     * @returns {ASTNode} The BlockComment node containing the JSDoc for the
     *      given node or null if not found.
     * @public
     */
    getJSDocComment: function(node) {

        var parent = node.parent;

        switch (node.type) {
            case "ClassDeclaration":
            case "FunctionDeclaration":
                if (looksLikeExport(parent)) {
                    return findJSDocComment(parent.leadingComments, parent.loc.start.line);
                }
                return findJSDocComment(node.leadingComments, node.loc.start.line);

            case "ClassExpression":
                return findJSDocComment(parent.parent.leadingComments, parent.parent.loc.start.line);

            case "ArrowFunctionExpression":
            case "FunctionExpression":

                if (parent.type !== "CallExpression" && parent.type !== "NewExpression") {
                    while (parent && !parent.leadingComments && !/Function/.test(parent.type) && parent.type !== "MethodDefinition" && parent.type !== "Property") {
                        parent = parent.parent;
                    }

                    return parent && parent.type !== "FunctionDeclaration" ? findJSDocComment(parent.leadingComments, parent.loc.start.line) : null;
                } else if (node.leadingComments) {
                    return findJSDocComment(node.leadingComments, node.loc.start.line);
                }

            // falls through

            default:
                return null;
        }
    },

    /**
     * Gets the deepest node containing a range index.
     * @param {int} index Range index of the desired node.
     * @returns {ASTNode} The node if found or null if not found.
     */
    getNodeByRangeIndex: function(index) {
        var result = null,
            resultParent = null,
            traverser = new Traverser();

        traverser.traverse(this.ast, {
            enter: function(node, parent) {
                if (node.range[0] <= index && index < node.range[1]) {
                    result = node;
                    resultParent = parent;
                } else {
                    this.skip();
                }
            },
            leave: function(node) {
                if (node === result) {
                    this.break();
                }
            }
        });

        return result ? utils.mixin({parent: resultParent}, result) : null; // ORION
    },

    /**
     * Determines if two tokens have at least one whitespace character
     * between them. This completely disregards comments in making the
     * determination, so comments count as zero-length substrings.
     * @param {Token} first The token to check after.
     * @param {Token} second The token to check before.
     * @returns {boolean} True if there is only space between tokens, false
     *  if there is anything other than whitespace between tokens.
     */
    isSpaceBetweenTokens: function(first, second) {
        var text = this.text.slice(first.range[1], second.range[0]);
        return /\s/.test(text.replace(/\/\*.*?\*\//g, ""));
    }
};
	return SourceCode;
});

/* eslint-env amd */
define('eslint/lib/utils/node-event-generator',[], function() {
	/**
	 * @fileoverview The event generator for AST nodes.
	 * @author Toru Nagashima
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	/**
	 * The event generator for AST nodes.
	 * This implements below interface.
	 *
	 * ```ts
	 * interface EventGenerator {
	 *     emitter: EventEmitter;
	 *     enterNode(node: ASTNode): void;
	 *     leaveNode(node: ASTNode): void;
	 * }
	 * ```
	 *
	 * @param {EventEmitter} emitter - An event emitter which is the destination of events.
	 * @returns {NodeEventGenerator} new instance.
	 */
	function NodeEventGenerator(emitter) {
		this.emitter = emitter;
	}

	NodeEventGenerator.prototype = {
		constructor: NodeEventGenerator,

		/**
		 * Emits an event of entering AST node.
		 * @param {ASTNode} node - A node which was entered.
		 * @returns {void}
		 */
		enterNode: function enterNode(node) {
			this.emitter.emit(node.type, node);
		},

		/**
		 * Emits an event of leaving AST node.
		 * @param {ASTNode} node - A node which was left.
		 * @returns {void}
		 */
		leaveNode: function leaveNode(node) {
			this.emitter.emit(node.type + ":exit", node);
		}
	};

	return NodeEventGenerator;

});
/* eslint-env amd */
define('eslint/lib/utils/comment-event-generator',[],
function() {
	/**
	 * @fileoverview The event generator for comments.
	 * @author Toru Nagashima
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Helpers
	//------------------------------------------------------------------------------

	/**
	 * Check collection of comments to prevent double event for comment as
	 * leading and trailing, then emit event if passing
	 * @param {ASTNode[]} comments - Collection of comment nodes
	 * @param {EventEmitter} emitter - The event emitter which is the destination of events.
	 * @param {Object[]} locs - List of locations of previous comment nodes
	 * @param {string} eventName - Event name postfix
	 * @returns {void}
	 */
	function emitComments(comments, emitter, locs, eventName) {
		if (comments.length > 0) {
			comments.forEach(function(node) {
				var index = locs.indexOf(node.loc);

				if (index >= 0) {
					locs.splice(index, 1);
				} else {
					locs.push(node.loc);
					emitter.emit(node.type + eventName, node);
				}
			});
		}
	}

	/**
	 * Shortcut to check and emit enter of comment nodes
	 * @param {CommentEventGenerator} generator - A generator to emit.
	 * @param {ASTNode[]} comments - Collection of comment nodes
	 * @returns {void}
	 */
	function emitCommentsEnter(generator, comments) {
		emitComments(
			comments,
			generator.emitter,
			generator.commentLocsEnter,
			"Comment");
	}

	/**
	 * Shortcut to check and emit exit of comment nodes
	 * @param {CommentEventGenerator} generator - A generator to emit.
	 * @param {ASTNode[]} comments Collection of comment nodes
	 * @returns {void}
	 */
	function emitCommentsExit(generator, comments) {
		emitComments(
			comments,
			generator.emitter,
			generator.commentLocsExit,
			"Comment:exit");
	}

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	/**
	 * The event generator for comments.
	 * This is the decorator pattern.
	 * This generates events of comments before/after events which are generated the original generator.
	 *
	 * @param {EventGenerator} originalEventGenerator - An event generator which is the decoration target.
	 * @param {SourceCode} sourceCode - A source code which has comments.
	 * @returns {CommentEventGenerator} new instance.
	 */
	function CommentEventGenerator(originalEventGenerator, sourceCode) {
		this.original = originalEventGenerator;
		this.emitter = originalEventGenerator.emitter;
		this.sourceCode = sourceCode;
		this.commentLocsEnter = [];
		this.commentLocsExit = [];
	}

	CommentEventGenerator.prototype = {
		constructor: CommentEventGenerator,

		/**
		 * Emits an event of entering comments.
		 * @param {ASTNode} node - A node which was entered.
		 * @returns {void}
		 */
		enterNode: function enterNode(node) {
			var comments = this.sourceCode.getComments(node);

			emitCommentsEnter(this, comments.leading);
			this.original.enterNode(node);
			emitCommentsEnter(this, comments.trailing);
		},

		/**
		 * Emits an event of leaving comments.
		 * @param {ASTNode} node - A node which was left.
		 * @returns {void}
		 */
		leaveNode: function leaveNode(node) {
			var comments = this.sourceCode.getComments(node);

			emitCommentsExit(this, comments.trailing);
			this.original.leaveNode(node);
			emitCommentsExit(this, comments.leading);
		}
	};

	return CommentEventGenerator;

});
/* eslint-env amd */
define('eslint/lib/config/config-validator',[
	'eslint/conf/environments',
	'./rules',
	'module',
], function(Environments, rules, module) {
	/**
	 * @fileoverview Validates configs.
	 * @author Brandon Mills
	 * @copyright 2015 Brandon Mills
	 */

	"use strict";
	var validators = {
		rules: Object.create(null)
	};

	//------------------------------------------------------------------------------
	// Private
	//------------------------------------------------------------------------------

	/**
	 * Gets a complete options schema for a rule.
	 * @param {string} id The rule's unique name.
	 * @returns {object} JSON Schema for the rule's options.
	 */
	function getRuleOptionsSchema(id) {
		var rule = rules.get(id),
			schema = rule && rule.schema || rule && rule.meta && rule.meta.schema;

		// Given a tuple of schemas, insert warning level at the beginning
		if (Array.isArray(schema)) {
			if (schema.length) {
				return {
					type: "array",
					items: schema,
					minItems: 0,
					maxItems: schema.length
				};
			}
			return {
				type: "array",
				minItems: 0,
				maxItems: 0
			};
		}

		// Given a full schema, leave it alone
		return schema || null;
	}

	/**
	 * Validates a rule's options against its schema.
	 * @param {string} id The rule's unique name.
	 * @param {array|number} options The given options for the rule.
	 * @param {string} source The name of the configuration source.
	 * @returns {void}
	 */
	function validateRuleOptions(id, options, source) {
		var validateRule = validators.rules[id],
			message,
			severity,
			localOptions,
			schema = getRuleOptionsSchema(id),
			validSeverity = true;

		/* ORION no CQ for is-my-json-valid library
		 if (!validateRule && schema) {
			validateRule = schemaValidator(schema, {
				verbose: true
			});
			validators.rules[id] = validateRule;
		}*/

		// if it's not an array, it should be just a severity
		if (Array.isArray(options)) {
			localOptions = options.concat(); // clone
			severity = localOptions.shift();
		} else {
			severity = options;
			localOptions = [];
		}

		validSeverity = (
			severity === 0 || severity === 1 || severity === 2 ||
			(typeof severity === "string" && /^(?:off|warn|error)$/i.test(severity))
		);

		if (validateRule) {
			validateRule(localOptions);
		}

		if ((validateRule && validateRule.errors) || !validSeverity) {
			message = [
				source, ":\n",
				"\tConfiguration for rule \"", id, "\" is invalid:\n"
			];

			if (!validSeverity) {
				message.push(
					"\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '",
					util.inspect(severity).replace(/'/g, "\"").replace(/\n/g, ""),
					"').\n"
				);
			}

			if (validateRule && validateRule.errors) {
				validateRule.errors.forEach(function(error) {
					message.push(
						"\tValue \"", error.value, "\" ", error.message, ".\n"
					);
				});
			}

			throw new Error(message.join(""));
		}
	}

	/**
	 * Validates an environment object
	 * @param {object} environment The environment config object to validate.
	 * @param {string} source The location to report with any errors.
	 * @returns {void}
	 */
	function validateEnvironment(environment, source) {

		// not having an environment is ok
		if (!environment) {
			return;
		}

		if (Array.isArray(environment)) {
			throw new Error("Environment must not be an array");
		}

		if (typeof environment === "object") {
			Object.keys(environment).forEach(function(env) {
				if (!Environments.env) {
					var message = [
						source, ":\n",
						"\tEnvironment key \"", env, "\" is unknown\n"
					];

					throw new Error(message.join(""));
				}
			});
		} else {
			throw new Error("Environment must be an object");
		}
	}

	/**
	 * Validates an entire config object.
	 * @param {object} config The config object to validate.
	 * @param {string} source The location to report with any errors.
	 * @returns {void}
	 */
	function validate(config, source) {

		if (typeof config.rules === "object") {
			Object.keys(config.rules).forEach(function(id) {
				validateRuleOptions(id, config.rules[id], source);
			});
		}

		validateEnvironment(config.env, source);
	}

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	module.exports = {
		getRuleOptionsSchema: getRuleOptionsSchema,
		validate: validate,
		validateRuleOptions: validateRuleOptions
	};
});

define("json!eslint/conf/replacements.json", function(){ return {
	"rules": {
		"generator-star": ["generator-star-spacing"],
		"global-strict": ["strict"],
		"no-arrow-condition": ["no-confusing-arrow", "no-constant-condition"],
		"no-comma-dangle": ["comma-dangle"],
		"no-empty-class": ["no-empty-character-class"],
		"no-empty-label": ["no-labels"],
		"no-extra-strict": ["strict"],
		"no-reserved-keys": ["quote-props"],
		"no-space-before-semi": ["semi-spacing"],
		"no-wrap-func": ["no-extra-parens"],
		"space-after-function-name": ["space-before-function-paren"],
		"space-after-keywords": ["keyword-spacing"],
		"space-before-function-parentheses": ["space-before-function-paren"],
		"space-before-keywords": ["keyword-spacing"],
		"space-in-brackets": ["object-curly-spacing", "array-bracket-spacing", "computed-property-spacing"],
		"space-return-throw-case": ["keyword-spacing"],
		"space-unary-word-ops": ["space-unary-ops"],
		"spaced-line-comment": ["spaced-comment"]
	}
};});

/* eslint-env amd */
define('eslint/lib/config/config-ops',[
	'eslint/conf/environments',
	'../utils/util',
	'module'
],
function(Environments, util, module) {
/**
 * @fileoverview Config file operations. This file must be usable in the browser,
 * so no Node-specific code can be here.
 * @author Nicholas C. Zakas
 */
	"use strict";

	//------------------------------------------------------------------------------
	// Private
	//------------------------------------------------------------------------------
	var RULE_SEVERITY_STRINGS = ["off", "warn", "error"],
		RULE_SEVERITY = RULE_SEVERITY_STRINGS.reduce(function(map, value, index) {
			map[value] = index;
			return map;
		}, {}),
		VALID_SEVERITIES = [0, 1, 2, "off", "warn", "error"];
	var assign = util.mixin;

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	module.exports = {

		/**
		 * Creates an empty configuration object suitable for merging as a base.
		 * @returns {Object} A configuration object.
		 */
		createEmptyConfig: function() {
			return {
				globals: {},
				env: {},
				rules: {},
				parserOptions: {}
			};
		},

		/**
		 * Creates an environment config based on the specified environments.
		 * @param {Object<string,boolean>} env The environment settings.
		 * @returns {Object} A configuration object with the appropriate rules and globals
		 *      set.
		 */
		createEnvironmentConfig: function(env) {

			var envConfig = this.createEmptyConfig();

			if (env) {

				envConfig.env = env;

				Object.keys(env).filter(function(name) {
					return env[name];
				}).forEach(function(name) {
					var environment = Environments.name;

					if (environment) {
						if (environment.globals) {
							assign(envConfig.globals, environment.globals);
						}

						if (environment.parserOptions) {
							assign(envConfig.parserOptions, environment.parserOptions);
						}
					}
				});
			}

			return envConfig;
		},

		/**
		 * Given a config with environment settings, applies the globals and
		 * ecmaFeatures to the configuration and returns the result.
		 * @param {Object} config The configuration information.
		 * @returns {Object} The updated configuration information.
		 */
		applyEnvironments: function(config) {
			if (config.env && typeof config.env === "object") {
				return this.merge(this.createEnvironmentConfig(config.env), config);
			}

			return config;
		},

		/**
		 * Merges two config objects. This will not only add missing keys, but will also modify values to match.
		 * @param {Object} target config object
		 * @param {Object} src config object. Overrides in this config object will take priority over base.
		 * @param {boolean} [combine] Whether to combine arrays or not
		 * @param {boolean} [isRule] Whether its a rule
		 * @returns {Object} merged config object.
		 */
		merge: function deepmerge(target, src, combine, isRule) {

			/*
			 The MIT License (MIT)

			 Copyright (c) 2012 Nicholas Fisher

			 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.
			 */

			/*
			 * This code is taken from deepmerge repo
			 * (https://github.com/KyleAMathews/deepmerge)
			 * and modified to meet our needs.
			 */
			var array = Array.isArray(src) || Array.isArray(target);
			var dst = array && [] || {};

			combine = !!combine;
			isRule = !!isRule;
			if (array) {
				target = target || [];

				// src could be a string, so check for array
				if (isRule && Array.isArray(src) && src.length > 1) {
					dst = dst.concat(src);
				} else {
					dst = dst.concat(target);
				}
				if (typeof src !== "object" && !Array.isArray(src)) {
					src = [src];
				}
				Object.keys(src).forEach(function(e, i) {
					e = src[i];
					if (typeof dst[i] === "undefined") {
						dst[i] = e;
					} else if (typeof e === "object") {
						if (isRule) {
							dst[i] = e;
						} else {
							dst[i] = deepmerge(target[i], e, combine, isRule);
						}
					} else {
						if (!combine) {
							dst[i] = e;
						} else {
							if (dst.indexOf(e) === -1) {
								dst.push(e);
							}
						}
					}
				});
			} else {
				if (target && typeof target === "object") {
					Object.keys(target).forEach(function(key) {
						dst[key] = target[key];
					});
				}
				Object.keys(src).forEach(function(key) {
					if (Array.isArray(src[key]) || Array.isArray(target[key])) {
						dst[key] = deepmerge(target[key], src[key], key === "plugins", isRule);
					} else if (typeof src[key] !== "object" || !src[key] || key === "exported" || key === "astGlobals") {
						dst[key] = src[key];
					} else {
						dst[key] = deepmerge(target[key] || {}, src[key], combine, key === "rules");
					}
				});
			}

			return dst;
		},

		/**
		 * Converts new-style severity settings (off, warn, error) into old-style
		 * severity settings (0, 1, 2) for all rules. Assumption is that severity
		 * values have already been validated as correct.
		 * @param {Object} config The config object to normalize.
		 * @returns {void}
		 */
		normalize: function(config) {

			if (config.rules) {
				Object.keys(config.rules).forEach(function(ruleId) {
					var ruleConfig = config.rules[ruleId];

					if (typeof ruleConfig === "string") {
						config.rules[ruleId] = RULE_SEVERITY[ruleConfig.toLowerCase()] || 0;
					} else if (Array.isArray(ruleConfig) && typeof ruleConfig[0] === "string") {
						ruleConfig[0] = RULE_SEVERITY[ruleConfig[0].toLowerCase()] || 0;
					}
				});
			}
		},

		/**
		 * Converts old-style severity settings (0, 1, 2) into new-style
		 * severity settings (off, warn, error) for all rules. Assumption is that severity
		 * values have already been validated as correct.
		 * @param {Object} config The config object to normalize.
		 * @returns {void}
		 */
		normalizeToStrings: function(config) {

			if (config.rules) {
				Object.keys(config.rules).forEach(function(ruleId) {
					var ruleConfig = config.rules[ruleId];

					if (typeof ruleConfig === "number") {
						config.rules[ruleId] = RULE_SEVERITY_STRINGS[ruleConfig] || RULE_SEVERITY_STRINGS[0];
					} else if (Array.isArray(ruleConfig) && typeof ruleConfig[0] === "number") {
						ruleConfig[0] = RULE_SEVERITY_STRINGS[ruleConfig[0]] || RULE_SEVERITY_STRINGS[0];
					}
				});
			}
		},

		/**
		 * Determines if the severity for the given rule configuration represents an error.
		 * @param {int|string|Array} ruleConfig The configuration for an individual rule.
		 * @returns {boolean} True if the rule represents an error, false if not.
		 */
		isErrorSeverity: function(ruleConfig) {

			var severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;

			if (typeof severity === "string") {
				severity = RULE_SEVERITY[severity.toLowerCase()] || 0;
			}

			return typeof severity === "number" && severity === 2;
		},

		/**
		 * Checks whether a given config has valid severity or not.
		 * @param {number|string|Array} ruleConfig - The configuration for an individual rule.
		 * @returns {boolean} `true` if the configuration has valid severity.
		 */
		isValidSeverity: function(ruleConfig) {
			var severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;

			if (typeof severity === "string") {
				severity = severity.toLowerCase();
			}
			return VALID_SEVERITIES.indexOf(severity) !== -1;
		},

		/**
		 * Checks whether every rule of a given config has valid severity or not.
		 * @param {object} config - The configuration for rules.
		 * @returns {boolean} `true` if the configuration has valid severity.
		 */
		isEverySeverityValid: function(config) {
			return Object.keys(config).every(function(ruleId) {
				return this.isValidSeverity(config[ruleId]);
			}, this);
		}
	};

	return module.exports;
});
/* eslint-env amd */
define('eslint/lib/code-path-analysis/code-path-segment',[],
function() {
	/**
	 * @fileoverview A class of the code path segment.
	 * @author Toru Nagashima
	 */

	"use strict";
	//------------------------------------------------------------------------------
	// Helpers
	//------------------------------------------------------------------------------

	/**
	 * Replaces unused segments with the previous segments of each unused segment.
	 *
	 * @param {CodePathSegment[]} segments - An array of segments to replace.
	 * @returns {CodePathSegment[]} The replaced array.
	 */
	function flattenUnusedSegments(segments) {
		var done = Object.create(null);
		var retv = [];

		for (var i = 0; i < segments.length; ++i) {
			var segment = segments[i];

			// Ignores duplicated.
			if (done[segment.id]) {
				continue;
			}

			// Use previous segments if unused.
			if (!segment.internal.used) {
				for (var j = 0; j < segment.allPrevSegments.length; ++j) {
					var prevSegment = segment.allPrevSegments[j];

					if (!done[prevSegment.id]) {
						done[prevSegment.id] = true;
						retv.push(prevSegment);
					}
				}
			} else {
				done[segment.id] = true;
				retv.push(segment);
			}
		}

		return retv;
	}

	/**
	 * Checks whether or not a given segment is reachable.
	 *
	 * @param {CodePathSegment} segment - A segment to check.
	 * @returns {boolean} `true` if the segment is reachable.
	 */
	function isReachable(segment) {
		return segment.reachable;
	}

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	/**
	 * A code path segment.
	 *
	 * @constructor
	 * @param {string} id - An identifier.
	 * @param {CodePathSegment[]} allPrevSegments - An array of the previous segments.
	 *   This array includes unreachable segments.
	 * @param {boolean} reachable - A flag which shows this is reachable.
	 */
	function CodePathSegment(id, allPrevSegments, reachable) {

		/**
		 * The identifier of this code path.
		 * Rules use it to store additional information of each rule.
		 * @type {string}
		 */
		this.id = id;

		/**
		 * An array of the next segments.
		 * @type {CodePathSegment[]}
		 */
		this.nextSegments = [];

		/**
		 * An array of the previous segments.
		 * @type {CodePathSegment[]}
		 */
		this.prevSegments = allPrevSegments.filter(isReachable);

		/**
		 * An array of the next segments.
		 * This array includes unreachable segments.
		 * @type {CodePathSegment[]}
		 */
		this.allNextSegments = [];

		/**
		 * An array of the previous segments.
		 * This array includes unreachable segments.
		 * @type {CodePathSegment[]}
		 */
		this.allPrevSegments = allPrevSegments;

		/**
		 * A flag which shows this is reachable.
		 * @type {boolean}
		 */
		this.reachable = reachable;

		// Internal data.
		Object.defineProperty(this, "internal", {
			value: {
				used: false,
				loopedPrevSegments: []
			}
		});
	}

	CodePathSegment.prototype = {
		constructor: CodePathSegment,

		/**
		 * Checks a given previous segment is coming from the end of a loop.
		 *
		 * @param {CodePathSegment} segment - A previous segment to check.
		 * @returns {boolean} `true` if the segment is coming from the end of a loop.
		 */
		isLoopedPrevSegment: function(segment) {
			return this.internal.loopedPrevSegments.indexOf(segment) !== -1;
		}
	};

	/**
	 * Creates the root segment.
	 *
	 * @param {string} id - An identifier.
	 * @returns {CodePathSegment} The created segment.
	 */
	CodePathSegment.newRoot = function(id) {
		return new CodePathSegment(id, [], true);
	};

	/**
	 * Creates a segment that follows given segments.
	 *
	 * @param {string} id - An identifier.
	 * @param {CodePathSegment[]} allPrevSegments - An array of the previous segments.
	 * @returns {CodePathSegment} The created segment.
	 */
	CodePathSegment.newNext = function(id, allPrevSegments) {
		return new CodePathSegment(
			id,
			flattenUnusedSegments(allPrevSegments),
			allPrevSegments.some(isReachable));
	};

	/**
	 * Creates an unreachable segment that follows given segments.
	 *
	 * @param {string} id - An identifier.
	 * @param {CodePathSegment[]} allPrevSegments - An array of the previous segments.
	 * @returns {CodePathSegment} The created segment.
	 */
	CodePathSegment.newUnreachable = function(id, allPrevSegments) {
		var segment = new CodePathSegment(id, flattenUnusedSegments(allPrevSegments), false);

		// In `if (a) return a; foo();` case, the unreachable segment preceded by
		// the return statement is not used but must not be remove.
		CodePathSegment.markUsed(segment);

		return segment;
	};

	/**
	 * Creates a segment that follows given segments.
	 * This factory method does not connect with `allPrevSegments`.
	 * But this inherits `reachable` flag.
	 *
	 * @param {string} id - An identifier.
	 * @param {CodePathSegment[]} allPrevSegments - An array of the previous segments.
	 * @returns {CodePathSegment} The created segment.
	 */
	CodePathSegment.newDisconnected = function(id, allPrevSegments) {
		return new CodePathSegment(id, [], allPrevSegments.some(isReachable));
	};

	/**
	 * Makes a given segment being used.
	 *
	 * And this function registers the segment into the previous segments as a next.
	 *
	 * @param {CodePathSegment} segment - A segment to mark.
	 * @returns {void}
	 */
	CodePathSegment.markUsed = function(segment) {
		if (segment.internal.used) {
			return;
		}
		segment.internal.used = true;

		var i;

		if (segment.reachable) {
			for (i = 0; i < segment.allPrevSegments.length; ++i) {
				var prevSegment = segment.allPrevSegments[i];

				prevSegment.allNextSegments.push(segment);
				prevSegment.nextSegments.push(segment);
			}
		} else {
			for (i = 0; i < segment.allPrevSegments.length; ++i) {
				segment.allPrevSegments[i].allNextSegments.push(segment);
			}
		}
	};

	/**
	 * Marks a previous segment as looped.
	 *
	 * @param {CodePathSegment} segment - A segment.
	 * @param {CodePathSegment} prevSegment - A previous segment to mark.
	 * @returns {void}
	 */
	CodePathSegment.markPrevSegmentAsLooped = function(segment, prevSegment) {
		segment.internal.loopedPrevSegments.push(prevSegment);
	};

	return CodePathSegment;
});
/* eslint-env amd */
define('eslint/lib/code-path-analysis/fork-context',[
	'./code-path-segment'
],
function(CodePathSegment) {
	/**
	 * @fileoverview A class to operate forking.
	 *
	 * This is state of forking.
	 * This has a fork list and manages it.
	 *
	 * @author Toru Nagashima
	 */

	"use strict";
	//------------------------------------------------------------------------------
	// Helpers
	//------------------------------------------------------------------------------

	/**
	 * Gets whether or not a given segment is reachable.
	 *
	 * @param {CodePathSegment} segment - A segment to get.
	 * @returns {boolean} `true` if the segment is reachable.
	 */
	function isReachable(segment) {
		return segment.reachable;
	}

	/**
	 * Creates new segments from the specific range of `context.segmentsList`.
	 *
	 * When `context.segmentsList` is `[[a, b], [c, d], [e, f]]`, `begin` is `0`, and
	 * `end` is `-1`, this creates `[g, h]`. This `g` is from `a`, `c`, and `e`.
	 * This `h` is from `b`, `d`, and `f`.
	 *
	 * @param {ForkContext} context - An instance.
	 * @param {number} begin - The first index of the previous segments.
	 * @param {number} end - The last index of the previous segments.
	 * @param {function} create - A factory function of new segments.
	 * @returns {CodePathSegment[]} New segments.
	 */
	function makeSegments(context, begin, end, create) {
		var list = context.segmentsList;

		if (begin < 0) {
			begin = list.length + begin;
		}
		if (end < 0) {
			end = list.length + end;
		}

		var segments = [];

		for (var i = 0; i < context.count; ++i) {
			var allPrevSegments = [];

			for (var j = begin; j <= end; ++j) {
				allPrevSegments.push(list[j][i]);
			}

			segments.push(create(context.idGenerator.next(), allPrevSegments));
		}

		return segments;
	}

	/**
	 * `segments` becomes doubly in a `finally` block. Then if a code path exits by a
	 * control statement (such as `break`, `continue`) from the `finally` block, the
	 * destination's segments may be half of the source segments. In that case, this
	 * merges segments.
	 *
	 * @param {ForkContext} context - An instance.
	 * @param {CodePathSegment[]} segments - Segments to merge.
	 * @returns {CodePathSegment[]} The merged segments.
	 */
	function mergeExtraSegments(context, segments) {
		while (segments.length > context.count) {
			var merged = [];

			for (var i = 0, length = segments.length / 2 | 0; i < length; ++i) {
				merged.push(CodePathSegment.newNext(
					context.idGenerator.next(), [segments[i], segments[i + length]]
				));
			}
			segments = merged;
		}
		return segments;
	}

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	/**
	 * A class to manage forking.
	 *
	 * @constructor
	 * @param {IdGenerator} idGenerator - An identifier generator for segments.
	 * @param {ForkContext|null} upper - An upper fork context.
	 * @param {number} count - A number of parallel segments.
	 */
	function ForkContext(idGenerator, upper, count) {
		this.idGenerator = idGenerator;
		this.upper = upper;
		this.count = count;
		this.segmentsList = [];
	}

	ForkContext.prototype = {
		constructor: ForkContext,

		/**
		 * The head segments.
		 * @type {CodePathSegment[]}
		 */
		get head() {
			var list = this.segmentsList;

			return list.length === 0 ? [] : list[list.length - 1];
		},

		/**
		 * A flag which shows empty.
		 * @type {boolean}
		 */
		get empty() {
			return this.segmentsList.length === 0;
		},

		/**
		 * A flag which shows reachable.
		 * @type {boolean}
		 */
		get reachable() {
			var segments = this.head;

			return segments.length > 0 && segments.some(isReachable);
		},

		/**
		 * Creates new segments from this context.
		 *
		 * @param {number} begin - The first index of previous segments.
		 * @param {number} end - The last index of previous segments.
		 * @returns {CodePathSegment[]} New segments.
		 */
		makeNext: function(begin, end) {
			return makeSegments(this, begin, end, CodePathSegment.newNext);
		},

		/**
		 * Creates new segments from this context.
		 * The new segments is always unreachable.
		 *
		 * @param {number} begin - The first index of previous segments.
		 * @param {number} end - The last index of previous segments.
		 * @returns {CodePathSegment[]} New segments.
		 */
		makeUnreachable: function(begin, end) {
			return makeSegments(this, begin, end, CodePathSegment.newUnreachable);
		},

		/**
		 * Creates new segments from this context.
		 * The new segments don't have connections for previous segments.
		 * But these inherit the reachable flag from this context.
		 *
		 * @param {number} begin - The first index of previous segments.
		 * @param {number} end - The last index of previous segments.
		 * @returns {CodePathSegment[]} New segments.
		 */
		makeDisconnected: function(begin, end) {
			return makeSegments(this, begin, end, CodePathSegment.newDisconnected);
		},

		/**
		 * Adds segments into this context.
		 * The added segments become the head.
		 *
		 * @param {CodePathSegment[]} segments - Segments to add.
		 * @returns {void}
		 */
		add: function(segments) {
			this.segmentsList.push(mergeExtraSegments(this, segments));
		},

		/**
		 * Replaces the head segments with given segments.
		 * The current head segments are removed.
		 *
		 * @param {CodePathSegment[]} segments - Segments to add.
		 * @returns {void}
		 */
		replaceHead: function(segments) {
			this.segmentsList.splice(-1, 1, mergeExtraSegments(this, segments));
		},

		/**
		 * Adds all segments of a given fork context into this context.
		 *
		 * @param {ForkContext} context - A fork context to add.
		 * @returns {void}
		 */
		addAll: function(context) {
			var source = context.segmentsList;

			for (var i = 0; i < source.length; ++i) {
				this.segmentsList.push(source[i]);
			}
		},

		/**
		 * Clears all secments in this context.
		 *
		 * @returns {void}
		 */
		clear: function() {
			this.segmentsList = [];
		}
	};

	/**
	 * Creates the root fork context.
	 *
	 * @param {IdGenerator} idGenerator - An identifier generator for segments.
	 * @returns {ForkContext} New fork context.
	 */
	ForkContext.newRoot = function(idGenerator) {
		var context = new ForkContext(idGenerator, null, 1);

		context.add([CodePathSegment.newRoot(idGenerator.next())]);

		return context;
	};

	/**
	 * Creates an empty fork context preceded by a given context.
	 *
	 * @param {ForkContext} parentContext - The parent fork context.
	 * @param {boolean} forkLeavingPath - A flag which shows inside of `finally` block.
	 * @returns {ForkContext} New fork context.
	 */
	ForkContext.newEmpty = function(parentContext, forkLeavingPath) {
		return new ForkContext(
			parentContext.idGenerator,
			parentContext,
			(forkLeavingPath ? 2 : 1) * parentContext.count);
	};

	return ForkContext;
});
/* eslint-env amd */
define('eslint/lib/code-path-analysis/code-path-state',[
	'./code-path-segment',
	'./fork-context'
],
function(CodePathSegment, ForkContext) {
	/**
	 * @fileoverview A class to manage state of generating a code path.
	 * @author Toru Nagashima
	 */

	"use strict";
	//------------------------------------------------------------------------------
	// Helpers
	//------------------------------------------------------------------------------

	/**
	 * Adds given segments into the `dest` array.
	 * If the `others` array does not includes the given segments, adds to the `all`
	 * array as well.
	 *
	 * This adds only reachable and used segments.
	 *
	 * @param {CodePathSegment[]} dest - A destination array (`returnedSegments` or `thrownSegments`).
	 * @param {CodePathSegment[]} others - Another destination array (`returnedSegments` or `thrownSegments`).
	 * @param {CodePathSegment[]} all - The unified destination array (`finalSegments`).
	 * @param {CodePathSegment[]} segments - Segments to add.
	 * @returns {void}
	 */
	function addToReturnedOrThrown(dest, others, all, segments) {
		for (var i = 0; i < segments.length; ++i) {
			var segment = segments[i];

			dest.push(segment);
			if (others.indexOf(segment) === -1) {
				all.push(segment);
			}
		}
	}

	/**
	 * Gets a loop-context for a `continue` statement.
	 *
	 * @param {CodePathState} state - A state to get.
	 * @param {string} label - The label of a `continue` statement.
	 * @returns {LoopContext} A loop-context for a `continue` statement.
	 */
	function getContinueContext(state, label) {
		if (!label) {
			return state.loopContext;
		}

		var context = state.loopContext;

		while (context) {
			if (context.label === label) {
				return context;
			}
			context = context.upper;
		}

		/* istanbul ignore next: foolproof (syntax error) */
		return null;
	}

	/**
	 * Gets a context for a `break` statement.
	 *
	 * @param {CodePathState} state - A state to get.
	 * @param {string} label - The label of a `break` statement.
	 * @returns {LoopContext|SwitchContext} A context for a `break` statement.
	 */
	function getBreakContext(state, label) {
		var context = state.breakContext;

		while (context) {
			if (label ? context.label === label : context.breakable) {
				return context;
			}
			context = context.upper;
		}

		/* istanbul ignore next: foolproof (syntax error) */
		return null;
	}

	/**
	 * Gets a context for a `return` statement.
	 *
	 * @param {CodePathState} state - A state to get.
	 * @returns {TryContext|CodePathState} A context for a `return` statement.
	 */
	function getReturnContext(state) {
		var context = state.tryContext;

		while (context) {
			if (context.hasFinalizer && context.position !== "finally") {
				return context;
			}
			context = context.upper;
		}

		return state;
	}

	/**
	 * Gets a context for a `throw` statement.
	 *
	 * @param {CodePathState} state - A state to get.
	 * @returns {TryContext|CodePathState} A context for a `throw` statement.
	 */
	function getThrowContext(state) {
		var context = state.tryContext;

		while (context) {
			if (context.position === "try" ||
					(context.hasFinalizer && context.position === "catch")) {
				return context;
			}
			context = context.upper;
		}

		return state;
	}

	/**
	 * Removes a given element from a given array.
	 *
	 * @param {any[]} xs - An array to remove the specific element.
	 * @param {any} x - An element to be removed.
	 * @returns {void}
	 */
	function remove(xs, x) {
		xs.splice(xs.indexOf(x), 1);
	}

	/**
	 * Disconnect given segments.
	 *
	 * This is used in a process for switch statements.
	 * If there is the "default" chunk before other cases, the order is different
	 * between node's and running's.
	 *
	 * @param {CodePathSegment[]} prevSegments - Forward segments to disconnect.
	 * @param {CodePathSegment[]} nextSegments - Backward segments to disconnect.
	 * @returns {void}
	 */
	function removeConnection(prevSegments, nextSegments) {
		for (var i = 0; i < prevSegments.length; ++i) {
			var prevSegment = prevSegments[i];
			var nextSegment = nextSegments[i];

			remove(prevSegment.nextSegments, nextSegment);
			remove(prevSegment.allNextSegments, nextSegment);
			remove(nextSegment.prevSegments, prevSegment);
			remove(nextSegment.allPrevSegments, prevSegment);
		}
	}

	/**
	 * Creates looping path.
	 *
	 * @param {CodePathState} state - The instance.
	 * @param {CodePathSegment[]} fromSegments - Segments which are source.
	 * @param {CodePathSegment[]} toSegments - Segments which are destination.
	 * @returns {void}
	 */
	function makeLooped(state, fromSegments, toSegments) {
		var end = Math.min(fromSegments.length, toSegments.length);

		for (var i = 0; i < end; ++i) {
			var fromSegment = fromSegments[i];
			var toSegment = toSegments[i];

			if (toSegment.reachable) {
				fromSegment.nextSegments.push(toSegment);
			}
			if (fromSegment.reachable) {
				toSegment.prevSegments.push(fromSegment);
			}
			fromSegment.allNextSegments.push(toSegment);
			toSegment.allPrevSegments.push(fromSegment);

			if (toSegment.allPrevSegments.length >= 2) {
				CodePathSegment.markPrevSegmentAsLooped(toSegment, fromSegment);
			}

			state.notifyLooped(fromSegment, toSegment);
		}
	}

	/**
	 * Finalizes segments of `test` chunk of a ForStatement.
	 *
	 * - Adds `false` paths to paths which are leaving from the loop.
	 * - Sets `true` paths to paths which go to the body.
	 *
	 * @param {LoopContext} context - A loop context to modify.
	 * @param {ChoiceContext} choiceContext - A choice context of this loop.
	 * @param {CodePathSegment[]} head - The current head paths.
	 * @returns {void}
	 */
	function finalizeTestSegmentsOfFor(context, choiceContext, head) {
		if (!choiceContext.processed) {
			choiceContext.trueForkContext.add(head);
			choiceContext.falseForkContext.add(head);
		}

		if (context.test !== true) {
			context.brokenForkContext.addAll(choiceContext.falseForkContext);
		}
		context.endOfTestSegments = choiceContext.trueForkContext.makeNext(0, -1);
	}

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	/**
	 * A class which manages state to analyze code paths.
	 *
	 * @constructor
	 * @param {IdGenerator} idGenerator - An id generator to generate id for code
	 *   path segments.
	 * @param {function} onLooped - A callback function to notify looping.
	 */
	function CodePathState(idGenerator, onLooped) {
		this.idGenerator = idGenerator;
		this.notifyLooped = onLooped;
		this.forkContext = ForkContext.newRoot(idGenerator);
		this.choiceContext = null;
		this.switchContext = null;
		this.tryContext = null;
		this.loopContext = null;
		this.breakContext = null;

		this.currentSegments = [];
		this.initialSegment = this.forkContext.head[0];

		// returnedSegments and thrownSegments push elements into finalSegments also.
		var final = this.finalSegments = [];
		var returned = this.returnedForkContext = [];
		var thrown = this.thrownForkContext = [];

		returned.add = addToReturnedOrThrown.bind(null, returned, thrown, final);
		thrown.add = addToReturnedOrThrown.bind(null, thrown, returned, final);
	}

	CodePathState.prototype = {
		constructor: CodePathState,

		/**
		 * The head segments.
		 * @type {CodePathSegment[]}
		 */
		get headSegments() {
			return this.forkContext.head;
		},

		/**
		 * The parent forking context.
		 * This is used for the root of new forks.
		 * @type {ForkContext}
		 */
		get parentForkContext() {
			var current = this.forkContext;

			return current && current.upper;
		},

		/**
		 * Creates and stacks new forking context.
		 *
		 * @param {boolean} forkLeavingPath - A flag which shows being in a
		 *   "finally" block.
		 * @returns {ForkContext} The created context.
		 */
		pushForkContext: function(forkLeavingPath) {
			this.forkContext = ForkContext.newEmpty(
				this.forkContext,
				forkLeavingPath
			);

			return this.forkContext;
		},

		/**
		 * Pops and merges the last forking context.
		 * @returns {ForkContext} The last context.
		 */
		popForkContext: function() {
			var lastContext = this.forkContext;

			this.forkContext = lastContext.upper;
			this.forkContext.replaceHead(lastContext.makeNext(0, -1));

			return lastContext;
		},

		/**
		 * Creates a new path.
		 * @returns {void}
		 */
		forkPath: function() {
			this.forkContext.add(this.parentForkContext.makeNext(-1, -1));
		},

		/**
		 * Creates a bypass path.
		 * This is used for such as IfStatement which does not have "else" chunk.
		 *
		 * @returns {void}
		 */
		forkBypassPath: function() {
			this.forkContext.add(this.parentForkContext.head);
		},

		//--------------------------------------------------------------------------
		// ConditionalExpression, LogicalExpression, IfStatement
		//--------------------------------------------------------------------------

		/**
		 * Creates a context for ConditionalExpression, LogicalExpression,
		 * IfStatement, WhileStatement, DoWhileStatement, or ForStatement.
		 *
		 * LogicalExpressions have cases that it goes different paths between the
		 * `true` case and the `false` case.
		 *
		 * For Example:
		 *
		 *     if (a || b) {
		 *         foo();
		 *     } else {
		 *         bar();
		 *     }
		 *
		 * In this case, `b` is evaluated always in the code path of the `else`
		 * block, but it's not so in the code path of the `if` block.
		 * So there are 3 paths.
		 *
		 *     a -> foo();
		 *     a -> b -> foo();
		 *     a -> b -> bar();
		 *
		 * @param {string} kind - A kind string.
		 *   If the new context is LogicalExpression's, this is `"&&"` or `"||"`.
		 *   If it's IfStatement's or ConditionalExpression's, this is `"test"`.
		 *   Otherwise, this is `"loop"`.
		 * @param {boolean} isForkingAsResult - A flag that shows that goes different
		 *   paths between `true` and `false`.
		 * @returns {void}
		 */
		pushChoiceContext: function(kind, isForkingAsResult) {
			this.choiceContext = {
				upper: this.choiceContext,
				kind: kind,
				isForkingAsResult: isForkingAsResult,
				trueForkContext: ForkContext.newEmpty(this.forkContext),
				falseForkContext: ForkContext.newEmpty(this.forkContext),
				processed: false
			};
		},

		/**
		 * Pops the last choice context and finalizes it.
		 *
		 * @returns {ChoiceContext} The popped context.
		 */
		popChoiceContext: function() {
			var context = this.choiceContext;

			this.choiceContext = context.upper;

			var forkContext = this.forkContext;
			var headSegments = forkContext.head;

			switch (context.kind) {
				case "&&":
				case "||":

					/*
					 * If any result were not transferred from child contexts,
					 * this sets the head segments to both cases.
					 * The head segments are the path of the right-hand operand.
					 */
					if (!context.processed) {
						context.trueForkContext.add(headSegments);
						context.falseForkContext.add(headSegments);
					}

					/*
					 * Transfers results to upper context if this context is in
					 * test chunk.
					 */
					if (context.isForkingAsResult) {
						var parentContext = this.choiceContext;

						parentContext.trueForkContext.addAll(context.trueForkContext);
						parentContext.falseForkContext.addAll(context.falseForkContext);
						parentContext.processed = true;

						return context;
					}

					break;

				case "test":
					if (!context.processed) {

						/*
						 * The head segments are the path of the `if` block here.
						 * Updates the `true` path with the end of the `if` block.
						 */
						context.trueForkContext.clear();
						context.trueForkContext.add(headSegments);
					} else {

						/*
						 * The head segments are the path of the `else` block here.
						 * Updates the `false` path with the end of the `else`
						 * block.
						 */
						context.falseForkContext.clear();
						context.falseForkContext.add(headSegments);
					}

					break;

				case "loop":

					/*
					 * Loops are addressed in popLoopContext().
					 * This is called from popLoopContext().
					 */
					return context;

					/* istanbul ignore next */
				default:
					throw new Error("unreachable");
			}

			// Merges all paths.
			var prevForkContext = context.trueForkContext;

			prevForkContext.addAll(context.falseForkContext);
			forkContext.replaceHead(prevForkContext.makeNext(0, -1));

			return context;
		},

		/**
		 * Makes a code path segment of the right-hand operand of a logical
		 * expression.
		 *
		 * @returns {void}
		 */
		makeLogicalRight: function() {
			var context = this.choiceContext;
			var forkContext = this.forkContext;

			if (context.processed) {

				/*
				 * This got segments already from the child choice context.
				 * Creates the next path from own true/false fork context.
				 */
				var prevForkContext =
					context.kind === "&&" ? context.trueForkContext :
					/* kind === "||" */
					context.falseForkContext;

				forkContext.replaceHead(prevForkContext.makeNext(0, -1));
				prevForkContext.clear();

				context.processed = false;
			} else {

				/*
				 * This did not get segments from the child choice context.
				 * So addresses the head segments.
				 * The head segments are the path of the left-hand operand.
				 */
				if (context.kind === "&&") {

					// The path does short-circuit if false.
					context.falseForkContext.add(forkContext.head);
				} else {

					// The path does short-circuit if true.
					context.trueForkContext.add(forkContext.head);
				}

				forkContext.replaceHead(forkContext.makeNext(-1, -1));
			}
		},

		/**
		 * Makes a code path segment of the `if` block.
		 *
		 * @returns {void}
		 */
		makeIfConsequent: function() {
			var context = this.choiceContext;
			var forkContext = this.forkContext;

			/*
			 * If any result were not transferred from child contexts,
			 * this sets the head segments to both cases.
			 * The head segments are the path of the test expression.
			 */
			if (!context.processed) {
				context.trueForkContext.add(forkContext.head);
				context.falseForkContext.add(forkContext.head);
			}

			context.processed = false;

			// Creates new path from the `true` case.
			forkContext.replaceHead(
				context.trueForkContext.makeNext(0, -1)
			);
		},

		/**
		 * Makes a code path segment of the `else` block.
		 *
		 * @returns {void}
		 */
		makeIfAlternate: function() {
			var context = this.choiceContext;
			var forkContext = this.forkContext;

			/*
			 * The head segments are the path of the `if` block.
			 * Updates the `true` path with the end of the `if` block.
			 */
			context.trueForkContext.clear();
			context.trueForkContext.add(forkContext.head);
			context.processed = true;

			// Creates new path from the `false` case.
			forkContext.replaceHead(
				context.falseForkContext.makeNext(0, -1)
			);
		},

		//--------------------------------------------------------------------------
		// SwitchStatement
		//--------------------------------------------------------------------------

		/**
		 * Creates a context object of SwitchStatement and stacks it.
		 *
		 * @param {boolean} hasCase - `true` if the switch statement has one or more
		 *   case parts.
		 * @param {string|null} label - The label text.
		 * @returns {void}
		 */
		pushSwitchContext: function(hasCase, label) {
			this.switchContext = {
				upper: this.switchContext,
				hasCase: hasCase,
				defaultSegments: null,
				defaultBodySegments: null,
				foundDefault: false,
				lastIsDefault: false,
				countForks: 0
			};

			this.pushBreakContext(true, label);
		},

		/**
		 * Pops the last context of SwitchStatement and finalizes it.
		 *
		 * - Disposes all forking stack for `case` and `default`.
		 * - Creates the next code path segment from `context.brokenForkContext`.
		 * - If the last `SwitchCase` node is not a `default` part, creates a path
		 *   to the `default` body.
		 *
		 * @returns {void}
		 */
		popSwitchContext: function() {
			var context = this.switchContext;

			this.switchContext = context.upper;

			var forkContext = this.forkContext;
			var brokenForkContext = this.popBreakContext().brokenForkContext;

			if (context.countForks === 0) {

				/*
				 * When there is only one `default` chunk and there is one or more
				 * `break` statements, even if forks are nothing, it needs to merge
				 * those.
				 */
				if (!brokenForkContext.empty) {
					brokenForkContext.add(forkContext.makeNext(-1, -1));
					forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
				}

				return;
			}

			var lastSegments = forkContext.head;

			this.forkBypassPath();
			var lastCaseSegments = forkContext.head;

			/*
			 * `brokenForkContext` is used to make the next segment.
			 * It must add the last segment into `brokenForkContext`.
			 */
			brokenForkContext.add(lastSegments);

			/*
			 * A path which is failed in all case test should be connected to path
			 * of `default` chunk.
			 */
			if (!context.lastIsDefault) {
				if (context.defaultBodySegments) {

					/*
					 * Remove a link from `default` label to its chunk.
					 * It's false route.
					 */
					removeConnection(context.defaultSegments, context.defaultBodySegments);
					makeLooped(this, lastCaseSegments, context.defaultBodySegments);
				} else {

					/*
					 * It handles the last case body as broken if `default` chunk
					 * does not exist.
					 */
					brokenForkContext.add(lastCaseSegments);
				}
			}

			// Pops the segment context stack until the entry segment.
			for (var i = 0; i < context.countForks; ++i) {
				this.forkContext = this.forkContext.upper;
			}

			/*
			 * Creates a path from all brokenForkContext paths.
			 * This is a path after switch statement.
			 */
			this.forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
		},

		/**
		 * Makes a code path segment for a `SwitchCase` node.
		 *
		 * @param {boolean} isEmpty - `true` if the body is empty.
		 * @param {boolean} isDefault - `true` if the body is the default case.
		 * @returns {void}
		 */
		makeSwitchCaseBody: function(isEmpty, isDefault) {
			var context = this.switchContext;

			if (!context.hasCase) {
				return;
			}

			/*
			 * Merge forks.
			 * The parent fork context has two segments.
			 * Those are from the current case and the body of the previous case.
			 */
			var parentForkContext = this.forkContext;
			var forkContext = this.pushForkContext();

			forkContext.add(parentForkContext.makeNext(0, -1));

			/*
			 * Save `default` chunk info.
			 * If the `default` label is not at the last, we must make a path from
			 * the last `case` to the `default` chunk.
			 */
			if (isDefault) {
				context.defaultSegments = parentForkContext.head;
				if (isEmpty) {
					context.foundDefault = true;
				} else {
					context.defaultBodySegments = forkContext.head;
				}
			} else {
				if (!isEmpty && context.foundDefault) {
					context.foundDefault = false;
					context.defaultBodySegments = forkContext.head;
				}
			}

			context.lastIsDefault = isDefault;
			context.countForks += 1;
		},

		//--------------------------------------------------------------------------
		// TryStatement
		//--------------------------------------------------------------------------

		/**
		 * Creates a context object of TryStatement and stacks it.
		 *
		 * @param {boolean} hasFinalizer - `true` if the try statement has a
		 *   `finally` block.
		 * @returns {void}
		 */
		pushTryContext: function(hasFinalizer) {
			this.tryContext = {
				upper: this.tryContext,
				position: "try",
				hasFinalizer: hasFinalizer,

				returnedForkContext: hasFinalizer ?
					ForkContext.newEmpty(this.forkContext) :
					null,

				thrownForkContext: ForkContext.newEmpty(this.forkContext),
				lastOfTryIsReachable: false,
				lastOfCatchIsReachable: false
			};
		},

		/**
		 * Pops the last context of TryStatement and finalizes it.
		 *
		 * @returns {void}
		 */
		popTryContext: function() {
			var context = this.tryContext;

			this.tryContext = context.upper;

			if (context.position === "catch") {

				// Merges two paths from the `try` block and `catch` block merely.
				this.popForkContext();
				return;
			}

			/*
			 * The following process is executed only when there is the `finally`
			 * block.
			 */

			var returned = context.returnedForkContext;
			var thrown = context.thrownForkContext;

			if (returned.empty && thrown.empty) {
				return;
			}

			// Separate head to normal paths and leaving paths.
			var headSegments = this.forkContext.head;

			this.forkContext = this.forkContext.upper;
			var normalSegments = headSegments.slice(0, headSegments.length / 2 | 0);
			var leavingSegments = headSegments.slice(headSegments.length / 2 | 0);

			// Forwards the leaving path to upper contexts.
			if (!returned.empty) {
				getReturnContext(this).returnedForkContext.add(leavingSegments);
			}
			if (!thrown.empty) {
				getThrowContext(this).thrownForkContext.add(leavingSegments);
			}

			// Sets the normal path as the next.
			this.forkContext.replaceHead(normalSegments);

			// If both paths of the `try` block and the `catch` block are
			// unreachable, the next path becomes unreachable as well.
			if (!context.lastOfTryIsReachable && !context.lastOfCatchIsReachable) {
				this.forkContext.makeUnreachable();
			}
		},

		/**
		 * Makes a code path segment for a `catch` block.
		 *
		 * @returns {void}
		 */
		makeCatchBlock: function() {
			var context = this.tryContext;
			var forkContext = this.forkContext;
			var thrown = context.thrownForkContext;

			// Update state.
			context.position = "catch";
			context.thrownForkContext = ForkContext.newEmpty(forkContext);
			context.lastOfTryIsReachable = forkContext.reachable;

			// Merge thrown paths.
			thrown.add(forkContext.head);
			var thrownSegments = thrown.makeNext(0, -1);

			// Fork to a bypass and the merged thrown path.
			this.pushForkContext();
			this.forkBypassPath();
			this.forkContext.add(thrownSegments);
		},

		/**
		 * Makes a code path segment for a `finally` block.
		 *
		 * In the `finally` block, parallel paths are created. The parallel paths
		 * are used as leaving-paths. The leaving-paths are paths from `return`
		 * statements and `throw` statements in a `try` block or a `catch` block.
		 *
		 * @returns {void}
		 */
		makeFinallyBlock: function() {
			var context = this.tryContext;
			var forkContext = this.forkContext;
			var returned = context.returnedForkContext;
			var thrown = context.thrownForkContext;
			var headOfLeavingSegments = forkContext.head;

			// Update state.
			if (context.position === "catch") {

				// Merges two paths from the `try` block and `catch` block.
				this.popForkContext();
				forkContext = this.forkContext;

				context.lastOfCatchIsReachable = forkContext.reachable;
			} else {
				context.lastOfTryIsReachable = forkContext.reachable;
			}
			context.position = "finally";

			if (returned.empty && thrown.empty) {

				// This path does not leave.
				return;
			}

			/*
			 * Create a parallel segment from merging returned and thrown.
			 * This segment will leave at the end of this finally block.
			 */
			var segments = forkContext.makeNext(-1, -1);
			var j;

			for (var i = 0; i < forkContext.count; ++i) {
				var prevSegsOfLeavingSegment = [headOfLeavingSegments[i]];

				for (j = 0; j < returned.segmentsList.length; ++j) {
					prevSegsOfLeavingSegment.push(returned.segmentsList[j][i]);
				}
				for (j = 0; j < thrown.segmentsList.length; ++j) {
					prevSegsOfLeavingSegment.push(thrown.segmentsList[j][i]);
				}

				segments.push(CodePathSegment.newNext(
					this.idGenerator.next(),
					prevSegsOfLeavingSegment));
			}

			this.pushForkContext(true);
			this.forkContext.add(segments);
		},

		/**
		 * Makes a code path segment from the first throwable node to the `catch`
		 * block or the `finally` block.
		 *
		 * @returns {void}
		 */
		makeFirstThrowablePathInTryBlock: function() {
			var forkContext = this.forkContext;

			if (!forkContext.reachable) {
				return;
			}

			var context = getThrowContext(this);

			if (context === this ||
				context.position !== "try" ||
				!context.thrownForkContext.empty
			) {
				return;
			}

			context.thrownForkContext.add(forkContext.head);
			forkContext.replaceHead(forkContext.makeNext(-1, -1));
		},

		//--------------------------------------------------------------------------
		// Loop Statements
		//--------------------------------------------------------------------------

		/**
		 * Creates a context object of a loop statement and stacks it.
		 *
		 * @param {string} type - The type of the node which was triggered. One of
		 *   `WhileStatement`, `DoWhileStatement`, `ForStatement`, `ForInStatement`,
		 *   and `ForStatement`.
		 * @param {string|null} label - A label of the node which was triggered.
		 * @returns {void}
		 */
		pushLoopContext: function(type, label) {
			var forkContext = this.forkContext;
			var breakContext = this.pushBreakContext(true, label);

			switch (type) {
				case "WhileStatement":
					this.pushChoiceContext("loop", false);
					this.loopContext = {
						upper: this.loopContext,
						type: type,
						label: label,
						continueDestSegments: null,
						brokenForkContext: breakContext.brokenForkContext
					};
					break;

				case "DoWhileStatement":
					this.pushChoiceContext("loop", false);
					this.loopContext = {
						upper: this.loopContext,
						type: type,
						label: label,
						entrySegments: null,
						continueForkContext: ForkContext.newEmpty(forkContext),
						brokenForkContext: breakContext.brokenForkContext
					};
					break;

				case "ForStatement":
					this.pushChoiceContext("loop", false);
					this.loopContext = {
						upper: this.loopContext,
						type: type,
						label: label,
						endOfInitSegments: null,
						testSegments: null,
						endOfTestSegments: null,
						updateSegments: null,
						endOfUpdateSegments: null,
						continueDestSegments: null,
						brokenForkContext: breakContext.brokenForkContext
					};
					break;

				case "ForInStatement":
				case "ForOfStatement":
					this.loopContext = {
						upper: this.loopContext,
						type: type,
						label: label,
						prevSegments: null,
						leftSegments: null,
						endOfLeftSegments: null,
						continueDestSegments: null,
						brokenForkContext: breakContext.brokenForkContext
					};
					break;

					/* istanbul ignore next */
				default:
					throw new Error("unknown type: \"" + type + "\"");
			}
		},

		/**
		 * Pops the last context of a loop statement and finalizes it.
		 *
		 * @returns {void}
		 */
		popLoopContext: function() {
			var context = this.loopContext;

			this.loopContext = context.upper;

			var forkContext = this.forkContext;
			var brokenForkContext = this.popBreakContext().brokenForkContext;
			var choiceContext;

			// Creates a looped path.
			switch (context.type) {
				case "WhileStatement":
				case "ForStatement":
					choiceContext = this.popChoiceContext();
					makeLooped(
						this,
						forkContext.head,
						context.continueDestSegments);
					break;

				case "DoWhileStatement":
					choiceContext = this.popChoiceContext();

					if (!choiceContext.processed) {
						choiceContext.trueForkContext.add(forkContext.head);
						choiceContext.falseForkContext.add(forkContext.head);
					}
					if (context.test !== true) {
						brokenForkContext.addAll(choiceContext.falseForkContext);
					}

					// `true` paths go to looping.
					var segmentsList = choiceContext.trueForkContext.segmentsList;

					for (var i = 0; i < segmentsList.length; ++i) {
						makeLooped(
							this,
							segmentsList[i],
							context.entrySegments);
					}
					break;

				case "ForInStatement":
				case "ForOfStatement":
					brokenForkContext.add(forkContext.head);
					makeLooped(
						this,
						forkContext.head,
						context.leftSegments);
					break;

					/* istanbul ignore next */
				default:
					throw new Error("unreachable");
			}

			// Go next.
			if (brokenForkContext.empty) {
				forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
			} else {
				forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
			}
		},

		/**
		 * Makes a code path segment for the test part of a WhileStatement.
		 *
		 * @param {boolean|} test - The test value (only when constant).
		 * @returns {void}
		 */
		makeWhileTest: function(test) {
			var context = this.loopContext;
			var forkContext = this.forkContext;
			var testSegments = forkContext.makeNext(0, -1);

			// Update state.
			context.test = test;
			context.continueDestSegments = testSegments;
			forkContext.replaceHead(testSegments);
		},

		/**
		 * Makes a code path segment for the body part of a WhileStatement.
		 *
		 * @returns {void}
		 */
		makeWhileBody: function() {
			var context = this.loopContext;
			var choiceContext = this.choiceContext;
			var forkContext = this.forkContext;

			if (!choiceContext.processed) {
				choiceContext.trueForkContext.add(forkContext.head);
				choiceContext.falseForkContext.add(forkContext.head);
			}

			// Update state.
			if (context.test !== true) {
				context.brokenForkContext.addAll(choiceContext.falseForkContext);
			}
			forkContext.replaceHead(choiceContext.trueForkContext.makeNext(0, -1));
		},

		/**
		 * Makes a code path segment for the body part of a DoWhileStatement.
		 *
		 * @returns {void}
		 */
		makeDoWhileBody: function() {
			var context = this.loopContext;
			var forkContext = this.forkContext;
			var bodySegments = forkContext.makeNext(-1, -1);

			// Update state.
			context.entrySegments = bodySegments;
			forkContext.replaceHead(bodySegments);
		},

		/**
		 * Makes a code path segment for the test part of a DoWhileStatement.
		 *
		 * @param {boolean|} test - The test value (only when constant).
		 * @returns {void}
		 */
		makeDoWhileTest: function(test) {
			var context = this.loopContext;
			var forkContext = this.forkContext;

			context.test = test;

			// Creates paths of `continue` statements.
			if (!context.continueForkContext.empty) {
				context.continueForkContext.add(forkContext.head);
				var testSegments = context.continueForkContext.makeNext(0, -1);

				forkContext.replaceHead(testSegments);
			}
		},

		/**
		 * Makes a code path segment for the test part of a ForStatement.
		 *
		 * @param {boolean|} test - The test value (only when constant).
		 * @returns {void}
		 */
		makeForTest: function(test) {
			var context = this.loopContext;
			var forkContext = this.forkContext;
			var endOfInitSegments = forkContext.head;
			var testSegments = forkContext.makeNext(-1, -1);

			// Update state.
			context.test = test;
			context.endOfInitSegments = endOfInitSegments;
			context.continueDestSegments = context.testSegments = testSegments;
			forkContext.replaceHead(testSegments);
		},

		/**
		 * Makes a code path segment for the update part of a ForStatement.
		 *
		 * @returns {void}
		 */
		makeForUpdate: function() {
			var context = this.loopContext;
			var choiceContext = this.choiceContext;
			var forkContext = this.forkContext;

			// Make the next paths of the test.
			if (context.testSegments) {
				finalizeTestSegmentsOfFor(
					context,
					choiceContext,
					forkContext.head);
			} else {
				context.endOfInitSegments = forkContext.head;
			}

			// Update state.
			var updateSegments = forkContext.makeDisconnected(-1, -1);

			context.continueDestSegments = context.updateSegments = updateSegments;
			forkContext.replaceHead(updateSegments);
		},

		/**
		 * Makes a code path segment for the body part of a ForStatement.
		 *
		 * @returns {void}
		 */
		makeForBody: function() {
			var context = this.loopContext;
			var choiceContext = this.choiceContext;
			var forkContext = this.forkContext;

			// Update state.
			if (context.updateSegments) {
				context.endOfUpdateSegments = forkContext.head;

				// `update` -> `test`
				if (context.testSegments) {
					makeLooped(
						this,
						context.endOfUpdateSegments,
						context.testSegments);
				}
			} else if (context.testSegments) {
				finalizeTestSegmentsOfFor(
					context,
					choiceContext,
					forkContext.head);
			} else {
				context.endOfInitSegments = forkContext.head;
			}

			var bodySegments = context.endOfTestSegments;

			if (!bodySegments) {

				/*
				 * If there is not the `test` part, the `body` path comes from the
				 * `init` part and the `update` part.
				 */
				var prevForkContext = ForkContext.newEmpty(forkContext);

				prevForkContext.add(context.endOfInitSegments);
				if (context.endOfUpdateSegments) {
					prevForkContext.add(context.endOfUpdateSegments);
				}

				bodySegments = prevForkContext.makeNext(0, -1);
			}
			context.continueDestSegments = context.continueDestSegments || bodySegments;
			forkContext.replaceHead(bodySegments);
		},

		/**
		 * Makes a code path segment for the left part of a ForInStatement and a
		 * ForOfStatement.
		 *
		 * @returns {void}
		 */
		makeForInOfLeft: function() {
			var context = this.loopContext;
			var forkContext = this.forkContext;
			var leftSegments = forkContext.makeDisconnected(-1, -1);

			// Update state.
			context.prevSegments = forkContext.head;
			context.leftSegments = context.continueDestSegments = leftSegments;
			forkContext.replaceHead(leftSegments);
		},

		/**
		 * Makes a code path segment for the right part of a ForInStatement and a
		 * ForOfStatement.
		 *
		 * @returns {void}
		 */
		makeForInOfRight: function() {
			var context = this.loopContext;
			var forkContext = this.forkContext;
			var temp = ForkContext.newEmpty(forkContext);

			temp.add(context.prevSegments);
			var rightSegments = temp.makeNext(-1, -1);

			// Update state.
			context.endOfLeftSegments = forkContext.head;
			forkContext.replaceHead(rightSegments);
		},

		/**
		 * Makes a code path segment for the body part of a ForInStatement and a
		 * ForOfStatement.
		 *
		 * @returns {void}
		 */
		makeForInOfBody: function() {
			var context = this.loopContext;
			var forkContext = this.forkContext;
			var temp = ForkContext.newEmpty(forkContext);

			temp.add(context.endOfLeftSegments);
			var bodySegments = temp.makeNext(-1, -1);

			// Make a path: `right` -> `left`.
			makeLooped(this, forkContext.head, context.leftSegments);

			// Update state.
			context.brokenForkContext.add(forkContext.head);
			forkContext.replaceHead(bodySegments);
		},

		//--------------------------------------------------------------------------
		// Control Statements
		//--------------------------------------------------------------------------

		/**
		 * Creates new context for BreakStatement.
		 *
		 * @param {boolean} breakable - The flag to indicate it can break by
		 *      an unlabeled BreakStatement.
		 * @param {string|null} label - The label of this context.
		 * @returns {object} The new context.
		 */
		pushBreakContext: function(breakable, label) {
			this.breakContext = {
				upper: this.breakContext,
				breakable: breakable,
				label: label,
				brokenForkContext: ForkContext.newEmpty(this.forkContext)
			};
			return this.breakContext;
		},

		/**
		 * Removes the top item of the break context stack.
		 *
		 * @returns {object} The removed context.
		 */
		popBreakContext: function() {
			var context = this.breakContext;
			var forkContext = this.forkContext;

			this.breakContext = context.upper;

			// Process this context here for other than switches and loops.
			if (!context.breakable) {
				var brokenForkContext = context.brokenForkContext;

				if (!brokenForkContext.empty) {
					brokenForkContext.add(forkContext.head);
					forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
				}
			}

			return context;
		},

		/**
		 * Makes a path for a `break` statement.
		 *
		 * It registers the head segment to a context of `break`.
		 * It makes new unreachable segment, then it set the head with the segment.
		 *
		 * @param {string} label - A label of the break statement.
		 * @returns {void}
		 */
		makeBreak: function(label) {
			var forkContext = this.forkContext;

			if (!forkContext.reachable) {
				return;
			}

			var context = getBreakContext(this, label);

			/* istanbul ignore else: foolproof (syntax error) */
			if (context) {
				context.brokenForkContext.add(forkContext.head);
			}

			forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
		},

		/**
		 * Makes a path for a `continue` statement.
		 *
		 * It makes a looping path.
		 * It makes new unreachable segment, then it set the head with the segment.
		 *
		 * @param {string} label - A label of the continue statement.
		 * @returns {void}
		 */
		makeContinue: function(label) {
			var forkContext = this.forkContext;

			if (!forkContext.reachable) {
				return;
			}

			var context = getContinueContext(this, label);

			/* istanbul ignore else: foolproof (syntax error) */
			if (context) {
				if (context.continueDestSegments) {
					makeLooped(this, forkContext.head, context.continueDestSegments);

					// If the context is a for-in/of loop, this effects a break also.
					if (context.type === "ForInStatement" ||
						context.type === "ForOfStatement"
					) {
						context.brokenForkContext.add(forkContext.head);
					}
				} else {
					context.continueForkContext.add(forkContext.head);
				}
			}
			forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
		},

		/**
		 * Makes a path for a `return` statement.
		 *
		 * It registers the head segment to a context of `return`.
		 * It makes new unreachable segment, then it set the head with the segment.
		 *
		 * @returns {void}
		 */
		makeReturn: function() {
			var forkContext = this.forkContext;

			if (forkContext.reachable) {
				getReturnContext(this).returnedForkContext.add(forkContext.head);
				forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
			}
		},

		/**
		 * Makes a path for a `throw` statement.
		 *
		 * It registers the head segment to a context of `throw`.
		 * It makes new unreachable segment, then it set the head with the segment.
		 *
		 * @returns {void}
		 */
		makeThrow: function() {
			var forkContext = this.forkContext;

			if (forkContext.reachable) {
				getThrowContext(this).thrownForkContext.add(forkContext.head);
				forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
			}
		},

		/**
		 * Makes the final path.
		 * @returns {void}
		 */
		makeFinal: function() {
			var segments = this.currentSegments;

			if (segments.length > 0 && segments[0].reachable) {
				this.returnedForkContext.add(segments);
			}
		}
	};

	return CodePathState;
});
/* eslint-env amd */
define('eslint/lib/code-path-analysis/id-generator',[
	'module'
],
function(module) {
	/**
	 * @fileoverview A class of identifiers generator for code path segments.
	 *
	 * Each rule uses the identifier of code path segments to store additional
	 * information of the code path.
	 *
	 * @author Toru Nagashima
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	/**
	 * A generator for unique ids.
	 *
	 * @constructor
	 * @param {string} prefix - Optional. A prefix of generated ids.
	 */
	function IdGenerator(prefix) {
		this.prefix = String(prefix);
		this.n = 0;
	}

	/**
	 * Generates id.
	 *
	 * @returns {string} A generated id.
	 */
	IdGenerator.prototype.next = function() {
		this.n = 1 + this.n | 0;

		/* istanbul ignore if */
		if (this.n < 0) {
			this.n = 1;
		}

		return this.prefix + this.n;
	};

	module.exports = IdGenerator;
	return module.exports;
});
/* eslint-env amd */
define('eslint/lib/code-path-analysis/code-path',[
	'./code-path-state',
	'./id-generator'
],
function(CodePathState, IdGenerator) {
	/**
	 * @fileoverview A class of the code path.
	 * @author Toru Nagashima
	 */

	"use strict";
	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	/**
	 * A code path.
	 *
	 * @constructor
	 * @param {string} id - An identifier.
	 * @param {CodePath|null} upper - The code path of the upper function scope.
	 * @param {function} onLooped - A callback function to notify looping.
	 */
	function CodePath(id, upper, onLooped) {

		/**
		 * The identifier of this code path.
		 * Rules use it to store additional information of each rule.
		 * @type {string}
		 */
		this.id = id;

		/**
		 * The code path of the upper function scope.
		 * @type {CodePath|null}
		 */
		this.upper = upper;

		/**
		 * The code paths of nested function scopes.
		 * @type {CodePath[]}
		 */
		this.childCodePaths = [];

		// Initializes internal state.
		Object.defineProperty(
			this,
			"internal", {
				value: new CodePathState(new IdGenerator(id + "_"), onLooped)
			});

		// Adds this into `childCodePaths` of `upper`.
		if (upper) {
			upper.childCodePaths.push(this);
		}
	}

	CodePath.prototype = {
		constructor: CodePath,

		/**
		 * The initial code path segment.
		 * @type {CodePathSegment}
		 */
		get initialSegment() {
			return this.internal.initialSegment;
		},

		/**
		 * Final code path segments.
		 * This array is a mix of `returnedSegments` and `thrownSegments`.
		 * @type {CodePathSegment[]}
		 */
		get finalSegments() {
			return this.internal.finalSegments;
		},

		/**
		 * Final code path segments which is with `return` statements.
		 * This array contains the last path segment if it's reachable.
		 * Since the reachable last path returns ``.
		 * @type {CodePathSegment[]}
		 */
		get returnedSegments() {
			return this.internal.returnedForkContext;
		},

		/**
		 * Final code path segments which is with `throw` statements.
		 * @type {CodePathSegment[]}
		 */
		get thrownSegments() {
			return this.internal.thrownForkContext;
		},

		/**
		 * Current code path segments.
		 * @type {CodePathSegment[]}
		 */
		get currentSegments() {
			return this.internal.currentSegments;
		},

		/**
		 * Traverses all segments in this code path.
		 *
		 *     codePath.traverseSegments(function(segment, controller) {
		 *         // do something.
		 *     });
		 *
		 * This method enumerates segments in order from the head.
		 *
		 * The `controller` object has two methods.
		 *
		 * - `controller.skip()` - Skip the following segments in this branch.
		 * - `controller.break()` - Skip all following segments.
		 *
		 * @param {object} [options] - Omittable.
		 * @param {CodePathSegment} [options.first] - The first segment to traverse.
		 * @param {CodePathSegment} [options.last] - The last segment to traverse.
		 * @param {function} callback - A callback function.
		 * @returns {void}
		 */
		traverseSegments: function(options, callback) {
			if (typeof options === "function") {
				callback = options;
				options = null;
			}

			options = options || {};
			var startSegment = options.first || this.internal.initialSegment;
			var lastSegment = options.last;

			var item = null;
			var index = 0;
			var end = 0;
			var segment = null;
			var visited = Object.create(null);
			var stack = [
				[startSegment, 0]
			];
			var skippedSegment = null;
			var broken = false;
			var controller = {
				skip: function() {
					if (stack.length <= 1) {
						broken = true;
					} else {
						skippedSegment = stack[stack.length - 2][0];
					}
				},
				break: function() {
					broken = true;
				}
			};

			/**
			 * Checks a given previous segment has been visited.
			 * @param {CodePathSegment} prevSegment - A previous segment to check.
			 * @returns {boolean} `true` if the segment has been visited.
			 */
			function isVisited(prevSegment) {
				return visited[prevSegment.id] ||
					segment.isLoopedPrevSegment(prevSegment);
			}

			while (stack.length > 0) {
				item = stack[stack.length - 1];
				segment = item[0];
				index = item[1];

				if (index === 0) {

					// Skip if this segment has been visited already.
					if (visited[segment.id]) {
						stack.pop();
						continue;
					}

					// Skip if all previous segments have not been visited.
					if (segment !== startSegment &&
						segment.prevSegments.length > 0 &&
						!segment.prevSegments.every(isVisited)
					) {
						stack.pop();
						continue;
					}

					// Reset the flag of skipping if all branches have been skipped.
					if (skippedSegment && segment.prevSegments.indexOf(skippedSegment) !== -1) {
						skippedSegment = null;
					}
					visited[segment.id] = true;

					// Call the callback when the first time.
					if (!skippedSegment) {
						callback.call(this, segment, controller); // eslint-disable-line callback-return
						if (segment === lastSegment) {
							controller.skip();
						}
						if (broken) {
							break;
						}
					}
				}

				// Update the stack.
				end = segment.nextSegments.length - 1;
				if (index < end) {
					item[1] += 1;
					stack.push([segment.nextSegments[index], 0]);
				} else if (index === end) {
					item[0] = segment.nextSegments[index];
					item[1] = 0;
				} else {
					stack.pop();
				}
			}
		}
	};

	/**
	 * Gets the state of a given code path.
	 *
	 * @param {CodePath} codePath - A code path to get.
	 * @returns {CodePathState} The state of the code path.
	 */
	CodePath.getState = function getState(codePath) {
		return codePath.internal;
	};

	return CodePath;
});
/* eslint-env amd */
define('eslint/lib/code-path-analysis/code-path-analyzer',[
	'./code-path',
	'./code-path-segment',
	'./id-generator',
	'../utils/util'
],
function(CodePath, CodePathSegment, IdGenerator, Utils) {
	/**
	 * @fileoverview A class of the code path analyzer.
	 * @author Toru Nagashima
	 */

	"use strict";
	//------------------------------------------------------------------------------
	// Helpers
	//------------------------------------------------------------------------------

	/**
	 * Checks whether or not a given node is a `case` node (not `default` node).
	 *
	 * @param {ASTNode} node - A `SwitchCase` node to check.
	 * @returns {boolean} `true` if the node is a `case` node (not `default` node).
	 */
	function isCaseNode(node) {
		return Boolean(node.test);
	}

	/**
	 * Checks whether or not a given logical expression node goes different path
	 * between the `true` case and the `false` case.
	 *
	 * @param {ASTNode} node - A node to check.
	 * @returns {boolean} `true` if the node is a test of a choice statement.
	 */
	function isForkingByTrueOrFalse(node) {
		var parent = node.parent;

		switch (parent.type) {
			case "ConditionalExpression":
			case "IfStatement":
			case "WhileStatement":
			case "DoWhileStatement":
			case "ForStatement":
				return parent.test === node;

			case "LogicalExpression":
				return true;

			default:
				return false;
		}
	}

	/**
	 * Gets the boolean value of a given literal node.
	 *
	 * This is used to detect infinity loops (e.g. `while (true) {}`).
	 * Statements preceded by an infinity loop are unreachable if the loop didn't
	 * have any `break` statement.
	 *
	 * @param {ASTNode} node - A node to get.
	 * @returns {boolean|} a boolean value if the node is a Literal node, otherwise undefined.
	 */
	function getBooleanValueIfSimpleConstant(node) {
		if (node.type === "Literal") {
			return Boolean(node.value);
		}
		return;
	}

	/**
	 * Checks that a given identifier node is a reference or not.
	 *
	 * This is used to detect the first throwable node in a `try` block.
	 *
	 * @param {ASTNode} node - An Identifier node to check.
	 * @returns {boolean} `true` if the node is a reference.
	 */
	function isIdentifierReference(node) {
		var parent = node.parent;

		switch (parent.type) {
			case "LabeledStatement":
			case "BreakStatement":
			case "ContinueStatement":
			case "ArrayPattern":
			case "RestElement":
			case "ImportSpecifier":
			case "ImportDefaultSpecifier":
			case "ImportNamespaceSpecifier":
			case "CatchClause":
				return false;

			case "FunctionDeclaration":
			case "FunctionExpression":
			case "ArrowFunctionExpression":
			case "ClassDeclaration":
			case "ClassExpression":
			case "VariableDeclarator":
				return parent.id !== node;

			case "Property":
			case "MethodDefinition":
				return parent.key !== node ||
					parent.computed ||
					parent.shorthand;

			case "AssignmentPattern":
				return parent.key !== node;

			default:
				return true;
		}
	}

	/**
	 * Updates the current segment with the head segment.
	 * This is similar to local branches and tracking branches of git.
	 *
	 * To separate the current and the head is in order to not make useless segments.
	 *
	 * In this process, both "onCodePathSegmentStart" and "onCodePathSegmentEnd"
	 * events are fired.
	 *
	 * @param {CodePathAnalyzer} analyzer - The instance.
	 * @param {ASTNode} node - The current AST node.
	 * @returns {void}
	 */
	function forwardCurrentToHead(analyzer, node) {
		var codePath = analyzer.codePath;
		var state = CodePath.getState(codePath);
		var currentSegments = state.currentSegments;
		var headSegments = state.headSegments;
		var end = Math.max(currentSegments.length, headSegments.length);
		var i, currentSegment, headSegment;

		// Fires leaving events.
		for (i = 0; i < end; ++i) {
			currentSegment = currentSegments[i];
			headSegment = headSegments[i];

			if (currentSegment !== headSegment && currentSegment) {
				if (currentSegment.reachable) {
					analyzer.emitter.emit(
						"onCodePathSegmentEnd",
						currentSegment,
						node);
				}
			}
		}

		// Update state.
		state.currentSegments = headSegments;

		// Fires entering events.
		for (i = 0; i < end; ++i) {
			currentSegment = currentSegments[i];
			headSegment = headSegments[i];

			if (currentSegment !== headSegment && headSegment) {
				CodePathSegment.markUsed(headSegment);
				if (headSegment.reachable) {
					analyzer.emitter.emit(
						"onCodePathSegmentStart",
						headSegment,
						node);
				}
			}
		}

	}

	/**
	 * Updates the current segment with empty.
	 * This is called at the last of functions or the program.
	 *
	 * @param {CodePathAnalyzer} analyzer - The instance.
	 * @param {ASTNode} node - The current AST node.
	 * @returns {void}
	 */
	function leaveFromCurrentSegment(analyzer, node) {
		var state = CodePath.getState(analyzer.codePath);
		var currentSegments = state.currentSegments;

		for (var i = 0; i < currentSegments.length; ++i) {
			var currentSegment = currentSegments[i];

			if (currentSegment.reachable) {
				analyzer.emitter.emit(
					"onCodePathSegmentEnd",
					currentSegment,
					node);
			}
		}

		state.currentSegments = [];
	}

	/**
	 * Updates the code path due to the position of a given node in the parent node
	 * thereof.
	 *
	 * For example, if the node is `parent.consequent`, this creates a fork from the
	 * current path.
	 *
	 * @param {CodePathAnalyzer} analyzer - The instance.
	 * @param {ASTNode} node - The current AST node.
	 * @returns {void}
	 */
	function preprocess(analyzer, node) {
		var codePath = analyzer.codePath;
		var state = CodePath.getState(codePath);
		var parent = node.parent;

		switch (parent.type) {
			case "LogicalExpression":
				if (parent.right === node) {
					state.makeLogicalRight();
				}
				break;

			case "ConditionalExpression":
			case "IfStatement":

				/*
				 * Fork if this node is at `consequent`/`alternate`.
				 * `popForkContext()` exists at `IfStatement:exit` and
				 * `ConditionalExpression:exit`.
				 */
				if (parent.consequent === node) {
					state.makeIfConsequent();
				} else if (parent.alternate === node) {
					state.makeIfAlternate();
				}
				break;

			case "SwitchCase":
				if (parent.consequent[0] === node) {
					state.makeSwitchCaseBody(false, !parent.test);
				}
				break;

			case "TryStatement":
				if (parent.handler === node) {
					state.makeCatchBlock();
				} else if (parent.finalizer === node) {
					state.makeFinallyBlock();
				}
				break;

			case "WhileStatement":
				if (parent.test === node) {
					state.makeWhileTest(getBooleanValueIfSimpleConstant(node));
				} else {
					state.makeWhileBody();
				}
				break;

			case "DoWhileStatement":
				if (parent.body === node) {
					state.makeDoWhileBody();
				} else {
					state.makeDoWhileTest(getBooleanValueIfSimpleConstant(node));
				}
				break;

			case "ForStatement":
				if (parent.test === node) {
					state.makeForTest(getBooleanValueIfSimpleConstant(node));
				} else if (parent.update === node) {
					state.makeForUpdate();
				} else if (parent.body === node) {
					state.makeForBody();
				}
				break;

			case "ForInStatement":
			case "ForOfStatement":
				if (parent.left === node) {
					state.makeForInOfLeft();
				} else if (parent.right === node) {
					state.makeForInOfRight();
				} else {
					state.makeForInOfBody();
				}
				break;

			case "AssignmentPattern":

				/*
				 * Fork if this node is at `right`.
				 * `left` is executed always, so it uses the current path.
				 * `popForkContext()` exists at `AssignmentPattern:exit`.
				 */
				if (parent.right === node) {
					state.pushForkContext();
					state.forkBypassPath();
					state.forkPath();
				}
				break;

			default:
				break;
		}
	}

	/**
	 * Updates the code path due to the type of a given node in entering.
	 *
	 * @param {CodePathAnalyzer} analyzer - The instance.
	 * @param {ASTNode} node - The current AST node.
	 * @returns {void}
	 */
	function processCodePathToEnter(analyzer, node) {
		var codePath = analyzer.codePath;
		var state = codePath && CodePath.getState(codePath);
		var parent = node.parent;

		switch (node.type) {
			case "Program":
			case "FunctionDeclaration":
			case "FunctionExpression":
			case "ArrowFunctionExpression":
				if (codePath) {

					// Emits onCodePathSegmentStart events if updated.
					forwardCurrentToHead(analyzer, node);
				}

				// Create the code path of this scope.
				codePath = analyzer.codePath = new CodePath(
					analyzer.idGenerator.next(),
					codePath,
					analyzer.onLooped
				);
				state = CodePath.getState(codePath);

				// Emits onCodePathStart events.
				analyzer.emitter.emit("onCodePathStart", codePath, node);
				break;

			case "LogicalExpression":
				state.pushChoiceContext(node.operator, isForkingByTrueOrFalse(node));
				break;

			case "ConditionalExpression":
			case "IfStatement":
				state.pushChoiceContext("test", false);
				break;

			case "SwitchStatement":
				state.pushSwitchContext(
					node.cases.some(isCaseNode),
					Utils.getLabel(node));
				break;

			case "TryStatement":
				state.pushTryContext(Boolean(node.finalizer));
				break;

			case "SwitchCase":

				/*
				 * Fork if this node is after the 2st node in `cases`.
				 * It's similar to `else` blocks.
				 * The next `test` node is processed in this path.
				 */
				if (parent.discriminant !== node && parent.cases[0] !== node) {
					state.forkPath();
				}
				break;

			case "WhileStatement":
			case "DoWhileStatement":
			case "ForStatement":
			case "ForInStatement":
			case "ForOfStatement":
				state.pushLoopContext(node.type, Utils.getLabel(node));
				break;

			case "LabeledStatement":
				if (!Utils.isBreakableStatement(node.body)) {
					state.pushBreakContext(false, node.label.name);
				}
				break;

			default:
				break;
		}

		// Emits onCodePathSegmentStart events if updated.
		forwardCurrentToHead(analyzer, node);
	}

	/**
	 * Updates the code path due to the type of a given node in leaving.
	 *
	 * @param {CodePathAnalyzer} analyzer - The instance.
	 * @param {ASTNode} node - The current AST node.
	 * @returns {void}
	 */
	function processCodePathToExit(analyzer, node) {
		var codePath = analyzer.codePath;
		var state = CodePath.getState(codePath);
		var dontForward = false;

		switch (node.type) {
			case "IfStatement":
			case "ConditionalExpression":
			case "LogicalExpression":
				state.popChoiceContext();
				break;

			case "SwitchStatement":
				state.popSwitchContext();
				break;

			case "SwitchCase":

				/*
				 * This is the same as the process at the 1st `consequent` node in
				 * `preprocess` function.
				 * Must do if this `consequent` is empty.
				 */
				if (node.consequent.length === 0) {
					state.makeSwitchCaseBody(true, !node.test);
				}
				if (state.forkContext.reachable) {
					dontForward = true;
				}
				break;

			case "TryStatement":
				state.popTryContext();
				break;

			case "BreakStatement":
				forwardCurrentToHead(analyzer, node);
				state.makeBreak(node.label && node.label.name);
				dontForward = true;
				break;

			case "ContinueStatement":
				forwardCurrentToHead(analyzer, node);
				state.makeContinue(node.label && node.label.name);
				dontForward = true;
				break;

			case "ReturnStatement":
				forwardCurrentToHead(analyzer, node);
				state.makeReturn();
				dontForward = true;
				break;

			case "ThrowStatement":
				forwardCurrentToHead(analyzer, node);
				state.makeThrow();
				dontForward = true;
				break;

			case "Identifier":
				if (isIdentifierReference(node)) {
					state.makeFirstThrowablePathInTryBlock();
					dontForward = true;
				}
				break;

			case "CallExpression":
			case "MemberExpression":
			case "NewExpression":
				state.makeFirstThrowablePathInTryBlock();
				break;

			case "WhileStatement":
			case "DoWhileStatement":
			case "ForStatement":
			case "ForInStatement":
			case "ForOfStatement":
				state.popLoopContext();
				break;

			case "AssignmentPattern":
				state.popForkContext();
				break;

			case "LabeledStatement":
				if (!Utils.isBreakableStatement(node.body)) {
					state.popBreakContext();
				}
				break;

			default:
				break;
		}

		/*
		 * Skip updating the current segment to avoid creating useless segments if
		 * the node type is the same as the parent node type.
		 */
		if (!dontForward && (!node.parent || node.type !== node.parent.type)) {

			// Emits onCodePathSegmentStart events if updated.
			forwardCurrentToHead(analyzer, node);
		}
	}

	/**
	 * Updates the code path to finalize the current code path.
	 *
	 * @param {CodePathAnalyzer} analyzer - The instance.
	 * @param {ASTNode} node - The current AST node.
	 * @returns {void}
	 */
	function postprocess(analyzer, node) {
		switch (node.type) {
			case "Program":
			case "FunctionDeclaration":
			case "FunctionExpression":
			case "ArrowFunctionExpression":
				var codePath = analyzer.codePath;

				// Mark the current path as the final node.
				CodePath.getState(codePath).makeFinal();

				// Emits onCodePathSegmentEnd event of the current segments.
				leaveFromCurrentSegment(analyzer, node);

				// Emits onCodePathEnd event of this code path.
				analyzer.emitter.emit("onCodePathEnd", codePath, node);

				codePath = analyzer.codePath = analyzer.codePath.upper;
				break;

			default:
				break;
		}
	}

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	/**
	 * The class to analyze code paths.
	 * This class implements the EventGenerator interface.
	 *
	 * @constructor
	 * @param {EventGenerator} eventGenerator - An event generator to wrap.
	 */
	function CodePathAnalyzer(eventGenerator) {
		this.original = eventGenerator;
		this.emitter = eventGenerator.emitter;
		this.codePath = null;
		this.idGenerator = new IdGenerator("s");
		this.currentNode = null;
		this.onLooped = this.onLooped.bind(this);
	}

	CodePathAnalyzer.prototype = {
		constructor: CodePathAnalyzer,

		/**
		 * Does the process to enter a given AST node.
		 * This updates state of analysis and calls `enterNode` of the wrapped.
		 *
		 * @param {ASTNode} node - A node which is entering.
		 * @returns {void}
		 */
		enterNode: function(node) {
			this.currentNode = node;

			// Updates the code path due to node's position in its parent node.
			if (node.parent) {
				preprocess(this, node);
			}

			// Updates the code path.
			// And emits onCodePathStart/onCodePathSegmentStart events.
			processCodePathToEnter(this, node);

			// Emits node events.
			this.original.enterNode(node);

			this.currentNode = null;
		},

		/**
		 * Does the process to leave a given AST node.
		 * This updates state of analysis and calls `leaveNode` of the wrapped.
		 *
		 * @param {ASTNode} node - A node which is leaving.
		 * @returns {void}
		 */
		leaveNode: function(node) {
			this.currentNode = node;// Updates the code path.
			// And emits onCodePathStart/onCodePathSegmentStart events.
			processCodePathToExit(this, node);

			// Emits node events.
			this.original.leaveNode(node);

			// Emits the last onCodePathStart/onCodePathSegmentStart events.
			postprocess(this, node);

			this.currentNode = null;
		},
		/**
		 * This is called on a code path looped.
		 * Then this raises a looped event.
		 *
		 * @param {CodePathSegment} fromSegment - A segment of prev.
		 * @param {CodePathSegment} toSegment - A segment of next.
		 * @returns {void}
		 */
		onLooped: function(fromSegment, toSegment) {
			if (fromSegment.reachable && toSegment.reachable) {
				this.emitter.emit(
					"onCodePathSegmentLoop",
					fromSegment,
					toSegment,
					this.currentNode
				);
			}
		}
	};

	return CodePathAnalyzer;
});
/* eslint-env amd */
define('eslint/lib/eslint',[
'./utils/traverser',
'escope/escope',
'./config/environments',
'./config/rules',
'./utils/util',
'./rule-context',
'./events',
'require',
'module',
'./utils/source-code',
'./utils/node-event-generator',
'./utils/comment-event-generator',
'./config/config-validator',
'json!eslint/conf/replacements.json',
'./config/config-ops',
'./code-path-analysis/code-path-analyzer'
], function(Traverser, escope, Environments, rules, util, RuleContext, events, require, module, SourceCode, NodeEventGenerator, CommentEventGenerator, validator, Replacements, ConfigOps, CodePathAnalyzer) {

	/**
	 * @fileoverview Main ESLint object.
	 * @author Nicholas C. Zakas
	 */
	"use strict";
	var EventEmitter = events.EventEmitter;
	var assign = util.mixin;
	var replacements = Replacements;
	var DEFAULT_PARSER = ""; // ORION we don't need a default parser

	//------------------------------------------------------------------------------
	// Helpers
	//------------------------------------------------------------------------------

	/**
	 * Parses a list of "name:boolean_value" or/and "name" options divided by comma or
	 * whitespace.
	 * @param {string} string The string to parse.
	 * @param {Comment} comment The comment node which has the string.
	 * @returns {Object} Result map object of names and boolean values
	 */
	function parseBooleanConfig(string, comment) {
		var items = {};

		// Collapse whitespace around : to make parsing easier
		string = string.replace(/\s*:\s*/g, ":");

		// Collapse whitespace around ,
		string = string.replace(/\s*,\s*/g, ",");

		string.split(/\s|,+/).forEach(function(name) {
			if (!name) {
				return;
			}
			var pos = name.indexOf(":"),
				value;

			if (pos !== -1) {
				value = name.substring(pos + 1, name.length);
				name = name.substring(0, pos);
			}

			items[name] = {
				value: (value === "true"),
				comment: comment
			};

		});
		return items;
	}

	/**
	 * Parses a JSON-like config.
	 * @param {string} string The string to parse.
	 * @param {Object} location Start line and column of comments for potential error message.
	 * @param {Object[]} messages The messages queue for potential error message.
	 * @returns {Object} Result map object
	 */
	function parseJsonConfig(string, location, messages) {
		var items = {};

		// Parses a JSON-like comment by the same way as parsing CLI option.
		/*try {
			items = levn.parse("Object", string) || {};

			// Some tests say that it should ignore invalid comments such as `/*eslint no-alert:abc* /`.
			// Also, commaless notations have invalid severity:
			//     "no-alert: 2 no-console: 2" --> {"no-alert": "2 no-console: 2"}
			// Should ignore that case as well.
			if (ConfigOps.isEverySeverityValid(items)) {
				return items;
			}
		} catch (ex) {

			// ignore to parse the string by a fallback.
		}*/

		// Optionator cannot parse commaless notations.
		// But we are supporting that. So this is a fallback for that.
		items = {};
		string = string.replace(/([a-zA-Z0-9\-\/]+):/g, "\"$1\":").replace(/(\]|[0-9])\s+(?=")/, "$1,");
		try {
			items = JSON.parse("{" + string + "}");
		} catch (ex) {

			messages.push({
				ruleId: null,
				fatal: true,
				severity: 2,
				source: null,
				message: "Failed to parse JSON from '" + string + "': " + ex.message,
				line: location.start.line,
				column: location.start.column + 1
			});

		}

		return items;
	}

	/**
	 * Parses a config of values separated by comma.
	 * @param {string} string The string to parse.
	 * @returns {Object} Result map of values and true values
	 */
	function parseListConfig(string) {
		var items = {};

		// Collapse whitespace around ,
		string = string.replace(/\s*,\s*/g, ",");

		string.split(/,+/).forEach(function(name) {
			name = name.trim();
			if (!name) {
				return;
			}
			items[name] = true;
		});
		return items;
	}

	/**
	 * Ensures that variables representing built-in properties of the Global Object,
	 * and any globals declared by special block comments, are present in the global
	 * scope.
	 * @param {ASTNode} program The top node of the AST.
	 * @param {Scope} globalScope The global scope.
	 * @param {Object} config The existing configuration data.
	 * @returns {void}
	 */
	function addDeclaredGlobals(program, globalScope, config) {
		var declaredGlobals = {},
			exportedGlobals = {},
			explicitGlobals = {},
			builtin = Environments.get("builtin");

		assign(declaredGlobals, builtin);

		Object.keys(config.env).forEach(function(name) {
			if (config.env[name]) {
				var env = Environments.get(name),
					environmentGlobals = env && env.globals;

				if (environmentGlobals) {
					assign(declaredGlobals, environmentGlobals);
				}
			}
		});

		assign(exportedGlobals, config.exported);
		assign(declaredGlobals, config.globals);
		assign(explicitGlobals, config.astGlobals);

		Object.keys(declaredGlobals).forEach(function(name) {
			var variable = globalScope.set.get(name);

			if (!variable) {
				variable = new escope.Variable(name, globalScope);
				variable.eslintExplicitGlobal = false;
				globalScope.variables.push(variable);
				globalScope.set.set(name, variable);
			}
			variable.writeable = declaredGlobals[name];
		});

		Object.keys(explicitGlobals).forEach(function(name) {
			var variable = globalScope.set.get(name);

			if (!variable) {
				variable = new escope.Variable(name, globalScope);
				variable.eslintExplicitGlobal = true;
				variable.eslintExplicitGlobalComment = explicitGlobals[name].comment;
				globalScope.variables.push(variable);
				globalScope.set.set(name, variable);
			}
			variable.writeable = explicitGlobals[name].value;
		});

		// mark all exported variables as such
		Object.keys(exportedGlobals).forEach(function(name) {
			var variable = globalScope.set.get(name);

			if (variable) {
				variable.eslintUsed = true;
			}
		});

		/*
		 * "through" contains all references which definitions cannot be found.
		 * Since we augment the global scope using configuration, we need to update
		 * references and remove the ones that were added by configuration.
		 */
		globalScope.through = globalScope.through.filter(function(reference) {
			var name = reference.identifier.name;
			var variable = globalScope.set.get(name);

			if (variable) {

				/*
				 * Links the variable and the reference.
				 * And this reference is removed from `Scope#through`.
				 */
				reference.resolved = variable;
				variable.references.push(reference);

				return false;
			}

			return true;
		});
	}

	/**
	 * Add data to reporting configuration to disable reporting for list of rules
	 * starting from start location
	 * @param  {Object[]} reportingConfig Current reporting configuration
	 * @param  {Object} start Position to start
	 * @param  {string[]} rulesToDisable List of rules
	 * @returns {void}
	 */
	function disableReporting(reportingConfig, start, rulesToDisable) {

		if (rulesToDisable.length) {
			rulesToDisable.forEach(function(rule) {
				reportingConfig.push({
					start: start,
					end: null,
					rule: rule
				});
			});
		} else {
			reportingConfig.push({
				start: start,
				end: null,
				rule: null
			});
		}
	}

	/**
	 * Add data to reporting configuration to enable reporting for list of rules
	 * starting from start location
	 * @param  {Object[]} reportingConfig Current reporting configuration
	 * @param  {Object} start Position to start
	 * @param  {string[]} rulesToEnable List of rules
	 * @returns {void}
	 */
	function enableReporting(reportingConfig, start, rulesToEnable) {
		var i;

		if (rulesToEnable.length) {
			rulesToEnable.forEach(function(rule) {
				for (i = reportingConfig.length - 1; i >= 0; i--) {
					if (!reportingConfig[i].end && reportingConfig[i].rule === rule) {
						reportingConfig[i].end = start;
						break;
					}
				}
			});
		} else {

			// find all previous disabled locations if they was started as list of rules
			var prevStart;

			for (i = reportingConfig.length - 1; i >= 0; i--) {
				if (prevStart && prevStart !== reportingConfig[i].start) {
					break;
				}

				if (!reportingConfig[i].end) {
					reportingConfig[i].end = start;
					prevStart = reportingConfig[i].start;
				}
			}
		}
	}

	/**
	 * Parses comments in file to extract file-specific config of rules, globals
	 * and environments and merges them with global config; also code blocks
	 * where reporting is disabled or enabled and merges them with reporting config.
	 * @param {string} filename The file being checked.
	 * @param {ASTNode} ast The top node of the AST.
	 * @param {Object} config The existing configuration data.
	 * @param {Object[]} reportingConfig The existing reporting configuration data.
	 * @param {Object[]} messages The messages queue.
	 * @returns {object} Modified config object
	 */
	function modifyConfigsFromComments(filename, ast, config, reportingConfig, messages) {

		var commentConfig = {
			exported: {},
			astGlobals: {},
			rules: {},
			env: {}
		};
		var commentRules = {};

		ast.comments.forEach(function(comment) {

			var value = comment.value.trim();
			var match = /^(eslint(-\w+){0,3}|exported|globals?)(\s|$)/.exec(value);

			if (match) {
				value = value.substring(match.index + match[1].length);

				if (comment.type === "Block") {
					switch (match[1]) {
						case "exported":
							assign(commentConfig.exported, parseBooleanConfig(value, comment));
							break;

						case "globals":
						case "global":
							assign(commentConfig.astGlobals, parseBooleanConfig(value, comment));
							break;

						case "eslint-env":
							assign(commentConfig.env, parseListConfig(value));
							break;

						case "eslint-disable":
							disableReporting(reportingConfig, comment.loc.start, Object.keys(parseListConfig(value)));
							break;

						case "eslint-enable":
							enableReporting(reportingConfig, comment.loc.start, Object.keys(parseListConfig(value)));
							break;

						case "eslint":
							var items = parseJsonConfig(value, comment.loc, messages);

							Object.keys(items).forEach(function(name) {
								var ruleValue = items[name];

								validator.validateRuleOptions(name, ruleValue, filename + " line " + comment.loc.start.line);
								commentRules[name] = ruleValue;
							});
							break;

							// no default
					}
				} else { // comment.type === "Line"
					if (match[1] === "eslint-disable-line") {
						disableReporting(reportingConfig, {
							line: comment.loc.start.line,
							column: 0
						}, Object.keys(parseListConfig(value)));
						enableReporting(reportingConfig, comment.loc.end, Object.keys(parseListConfig(value)));
					} else if (match[1] === "eslint-disable-next-line") {
						disableReporting(reportingConfig, comment.loc.start, Object.keys(parseListConfig(value)));
						enableReporting(reportingConfig, {
							line: comment.loc.start.line + 2
						}, Object.keys(parseListConfig(value)));
					}
				}
			}
		});

		// apply environment configs
		Object.keys(commentConfig.env).forEach(function(name) {
			var env = Environments.get(name);

			if (env) {
				commentConfig = ConfigOps.merge(commentConfig, env);
			}
		});
		assign(commentConfig.rules, commentRules);

		return ConfigOps.merge(config, commentConfig);
	}

	/**
	 * Check if message of rule with ruleId should be ignored in location
	 * @param  {Object[]} reportingConfig  Collection of ignore records
	 * @param  {string} ruleId   Id of rule
	 * @param  {Object} location Location of message
	 * @returns {boolean}          True if message should be ignored, false otherwise
	 */
	function isDisabledByReportingConfig(reportingConfig, ruleId, location) {

		for (var i = 0, c = reportingConfig.length; i < c; i++) {

			var ignore = reportingConfig[i];

			if ((!ignore.rule || ignore.rule === ruleId) &&
				(location.line > ignore.start.line || (location.line === ignore.start.line && location.column >= ignore.start.column)) &&
				(!ignore.end || (location.line < ignore.end.line || (location.line === ignore.end.line && location.column <= ignore.end.column)))) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Process initial config to make it safe to extend by file comment config
	 * @param  {Object} config Initial config
	 * @returns {Object}        Processed config
	 */
	function prepareConfig(config) {

		config.globals = config.globals || config.global || {};
		delete config.global;

		var copiedRules = {},
			parserOptions = {},
			preparedConfig;

		if (typeof config.rules === "object") {
			Object.keys(config.rules).forEach(function(k) {
				var rule = config.rules[k];

				if (rule === null) {
					throw new Error("Invalid config for rule '" + k + "'\.");
				}
				if (Array.isArray(rule)) {
					copiedRules[k] = rule.slice();
				} else {
					copiedRules[k] = rule;
				}
			});
		}

		// merge in environment parserOptions
		if (typeof config.env === "object") {
			Object.keys(config.env).forEach(function(envName) {
				var env = Environments.get(envName);

				if (config.env[envName] && env && env.parserOptions) {
					parserOptions = ConfigOps.merge(parserOptions, env.parserOptions);
				}
			});
		}

		preparedConfig = {
			rules: copiedRules,
			parser: config.parser || DEFAULT_PARSER,

			globals: ConfigOps.merge({}, config.globals),
			env: ConfigOps.merge({}, config.env || {}),
			settings: ConfigOps.merge({}, config.settings || {}),
			parserOptions: ConfigOps.merge(parserOptions, config.parserOptions || {}),
			tern: config.tern // ORION
		};

		if (preparedConfig.parserOptions.sourceType === "module") {
			if (!preparedConfig.parserOptions.ecmaFeatures) {
				preparedConfig.parserOptions.ecmaFeatures = {};
			}

			// can't have global return inside of modules
			preparedConfig.parserOptions.ecmaFeatures.globalReturn = false;

			// also need at least ES6 for modules
			if (!preparedConfig.parserOptions.ecmaVersion || preparedConfig.parserOptions.ecmaVersion < 6) {
				preparedConfig.parserOptions.ecmaVersion = 6;
			}
		}

		return preparedConfig;
	}

	/**
	 * Provide a stub rule with a given message
	 * @param  {string} message The message to be displayed for the rule
	 * @returns {Function}      Stub rule function
	 */
	function createStubRule(message) {

		/**
		 * Creates a fake rule object
		 * @param {object} context context object for each rule
		 * @returns {object} collection of node to listen on
		 */
		function createRuleModule(context) {
			return {
				Program: function(node) {
					context.report(node, message);
				}
			};
		}

		if (message) {
			return createRuleModule;
		} else {
			throw new Error("No message passed to stub rule");
		}
	}

	/**
	 * Provide a rule replacement message
	 * @param  {string} ruleId Name of the rule
	 * @returns {string}       Message detailing rule replacement
	 */
	function getRuleReplacementMessage(ruleId) {
		if (ruleId in replacements.rules) {
			var newRules = replacements.rules[ruleId];

			return "Rule \'" + ruleId + "\' was removed and replaced by: " + newRules.join(", ");
		}

		return null;
	}

	var eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)\*\//g;

	/**
	 * Checks whether or not there is a comment which has "eslint-env *" in a given text.
	 * @param {string} text - A source code text to check.
	 * @returns {object|null} A result of parseListConfig() with "eslint-env *" comment.
	 */
	function findEslintEnv(text) {
		var match, retv;

		eslintEnvPattern.lastIndex = 0;

		while ((match = eslintEnvPattern.exec(text))) {
			retv = assign(retv || {}, parseListConfig(match[1]));
		}

		return retv;
	}

	/**
	 * Strips Unicode BOM from a given text.
	 *
	 * @param {string} text - A text to strip.
	 * @returns {string} The stripped text.
	 */
	function stripUnicodeBOM(text) {

		/*
		 * Check Unicode BOM.
		 * In JavaScript, string data is stored as UTF-16, so BOM is 0xFEFF.
		 * http://www.ecma-international.org/ecma-262/6.0/#sec-unicode-format-control-characters
		 */
		if (text.charCodeAt(0) === 0xFEFF) {
			return text.slice(1);
		}
		return text;
	}

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	/**
	 * Object that is responsible for verifying JavaScript text
	 * @name eslint
	 */
	module.exports = (function() {

		var api = Object.create(new EventEmitter()),
			messages = [],
			currentConfig = null,
			currentScopes = null,
			scopeMap = null,
			scopeManager = null,
			currentFilename = null,
			traverser = null,
			reportingConfig = [],
			sourceCode = null;

		/**
		 * Parses text into an AST. Moved out here because the try-catch prevents
		 * optimization of functions, so it's best to keep the try-catch as isolated
		 * as possible
		 * @param {string} text The text to parse.
		 * @param {Object} config The ESLint configuration object.
		 * @returns {ASTNode} The AST if successful or null if not.
		 * @private
		 */
		function parse(text, config) {

			var parser,
				parserOptions = {
					loc: true,
					range: true,
					raw: true,
					tokens: true,
					comment: true,
					attachComment: true
				};

			try {
				parser = require(config.parser);
			} catch (ex) {
				messages.push({
					ruleId: null,
					fatal: true,
					severity: 2,
					source: null,
					message: ex.message,
					line: 0,
					column: 0
				});

				return null;
			}

			// merge in any additional parser options
			if (config.parserOptions) {
				parserOptions = assign({}, config.parserOptions, parserOptions);
			}

			/*
			 * Check for parsing errors first. If there's a parsing error, nothing
			 * else can happen. However, a parsing error does not throw an error
			 * from this method - it's just considered a fatal error message, a
			 * problem that ESLint identified just like any other.
			 */
			try {
				return parser.parse(text, parserOptions);
			} catch (ex) {

				// If the message includes a leading line number, strip it:
				var message = ex.message.replace(/^line \d+:/i, "").trim();
				var source = (ex.lineNumber) ? SourceCode.splitLines(text)[ex.lineNumber - 1] : null;

				messages.push({
					ruleId: null,
					fatal: true,
					severity: 2,
					source: source,
					message: "Parsing error: " + message,

					line: ex.lineNumber,
					column: ex.column
				});

				return null;
			}
		}

		/**
		 * Get the severity level of a rule (0 - none, 1 - warning, 2 - error)
		 * Returns 0 if the rule config is not valid (an Array or a number)
		 * @param {Array|number} ruleConfig rule configuration
		 * @returns {number} 0, 1, or 2, indicating rule severity
		 */
		function getRuleSeverity(ruleConfig) {
			if (typeof ruleConfig === "number") {
				return ruleConfig;
			} else if (Array.isArray(ruleConfig)) {
				return ruleConfig[0];
			} else {
				return 0;
			}
		}

		/**
		 * Get the options for a rule (not including severity), if any
		 * @param {Array|number} ruleConfig rule configuration
		 * @returns {Array} of rule options, empty Array if none
		 */
		function getRuleOptions(ruleConfig) {
			if (Array.isArray(ruleConfig)) {
				return ruleConfig.slice(1);
			} else {
				return [];
			}
		}

		// set unlimited listeners (see https://github.com/eslint/eslint/issues/524)
		api.setMaxListeners(0);

		/**
		 * Resets the internal state of the object.
		 * @returns {void}
		 */
		api.reset = function() {
			this.removeAllListeners();
			messages = [];
			currentConfig = null;
			currentScopes = null;
			scopeMap = null;
			scopeManager = null;
			traverser = null;
			reportingConfig = [];
			sourceCode = null;
		};

		/**
		 * Configuration object for the `verify` API. A JS representation of the eslintrc files.
		 * @typedef {Object} ESLintConfig
		 * @property {Object} rules The rule configuration to verify against.
		 * @property {string} [parser] Parser to use when generatig the AST.
		 * @property {Object} [parserOptions] Options for the parsed used.
		 * @property {Object} [settings] Global settings passed to each rule.
		 * @property {Object} [env] The environment to verify in.
		 * @property {Object} [globals] Available globalsto the code.
		 */

		/**
		 * Verifies the text against the rules specified by the second argument.
		 * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
		 * @param {ESLintConfig} config An ESLintConfig instance to configure everything.
		 * @param {(string|Object)} [filenameOrOptions] The optional filename of the file being checked.
		 *      If this is not set, the filename will default to '<input>' in the rule context. If
		 *      an object, then it has "filename", "saveState", and "allowInlineConfig" properties.
		 * @param {boolean} [saveState] Indicates if the state from the last run should be saved.
		 *      Mostly useful for testing purposes.
		 * @param {boolean} [filenameOrOptions.allowInlineConfig] Allow/disallow inline comments' ability to change config once it is set. Defaults to true if not supplied.
		 *      Useful if you want to validate JS without comments overriding rules.
		 * @returns {Object[]} The results as an array of messages or null if no messages.
		 */
		api.verify = function(textOrSourceCode, config, filenameOrOptions, saveState) {

			var ast,
				shebang,
				ecmaFeatures,
				ecmaVersion,
				allowInlineConfig,
				text = typeof textOrSourceCode === "string" ? textOrSourceCode : null;

			// evaluate arguments
			if (typeof filenameOrOptions === "object") {
				currentFilename = filenameOrOptions.filename;
				allowInlineConfig = filenameOrOptions.allowInlineConfig;
				saveState = filenameOrOptions.saveState;
			} else {
				currentFilename = filenameOrOptions;
			}

			if (!saveState) {
				this.reset();
			}

			// search and apply "eslint-env *".
			var envInFile = findEslintEnv(text || textOrSourceCode.text);

			if (envInFile) {
				if (!config || !config.env) {
					config = assign({}, config || {}, {
						env: envInFile
					});
				} else {
					config = assign({}, config);
					config.env = assign({}, config.env, envInFile);
				}
			}

			// process initial config to make it safe to extend
			config = prepareConfig(config || {});

			// only do this for text
			if (text !== null) {

				// there's no input, just exit here
				if (text.trim().length === 0) {
					//sourceCode = new SourceCode(text, blankScriptAST); ORION this code is never used as we always get an ast from tern
					return messages;
				}

				ast = parse(
					stripUnicodeBOM(text).replace(/^#!([^\r\n]+)/, function(match, captured) {
						shebang = captured;
						return "//" + captured;
					}),
					config
				);

				if (ast) {
					sourceCode = new SourceCode(text, ast);
				}

			} else {
				sourceCode = textOrSourceCode;
				ast = sourceCode.ast;
			}

			// if espree failed to parse the file, there's no sense in setting up rules
			if (ast) {

				// parse global comments and modify config
				if (allowInlineConfig !== false) {
					config = modifyConfigsFromComments(currentFilename, ast, config, reportingConfig, messages);
				}

				// ensure that severities are normalized in the config
				ConfigOps.normalize(config);

				// enable appropriate rules
				Object.keys(config.rules).filter(function(key) {
					return getRuleSeverity(config.rules[key]) > 0;
				}).forEach(function(key) {
					var ruleCreator,
						severity,
						options,
						rule;

					ruleCreator = rules.get(key);

					if (!ruleCreator) {
						var replacementMsg = getRuleReplacementMessage(key);

						if (replacementMsg) {
							ruleCreator = createStubRule(replacementMsg);
						} else {
							ruleCreator = createStubRule("Definition for rule '" + key + "' was not found");
						}
						rules.define(key, ruleCreator);
					}

					severity = getRuleSeverity(config.rules[key]);
					options = getRuleOptions(config.rules[key]);

					// ORION pass in config.tern
					var settings = config.settings;
					if (!settings) {
						settings = Object.create(null);
					}
					settings.tern = config.tern;

					try {
						var ruleContext = new RuleContext(
							key, api, severity, options,
							settings, config.parserOptions,
							config.parser, ruleCreator.meta,
							config.env); // ORION

						rule = ruleCreator.create ? ruleCreator.create(ruleContext) :
							ruleCreator(ruleContext);

						// add all the node types as listeners
						Object.keys(rule).forEach(function(nodeType) {
							api.on(nodeType, rule[nodeType]);  // ORION remove timing usage as timing was always disabled
						});
					} catch (ex) {
						ex.message = "Error while loading rule '" + key + "': " + ex.message;
						throw ex;
					}
				});

				// save config so rules can access as necessary
				currentConfig = config;
				traverser = new Traverser();

				ecmaFeatures = currentConfig.parserOptions.ecmaFeatures || {};
				ecmaVersion = currentConfig.parserOptions.ecmaVersion || 5;

				// gather scope data that may be needed by the rules
				scopeManager = escope.analyze(ast, {
					ignoreEval: true,
					nodejsScope: ecmaFeatures.globalReturn,
					impliedStrict: ecmaFeatures.impliedStrict,
					ecmaVersion: ecmaVersion,
					sourceType: currentConfig.parserOptions.sourceType || "script",
					fallback: Traverser.getKeys
				});

				currentScopes = scopeManager.scopes;

				/*
				 * Index the scopes by the start range of their block for efficient
				 * lookup in getScope.
				 */
				scopeMap = [];

				currentScopes.forEach(function(scope, index) {
					var range = scope.block.range[0];

					/*
					 * Sometimes two scopes are returned for a given node. This is
					 * handled later in a known way, so just don't overwrite here.
					 */
					if (!scopeMap[range]) {
						scopeMap[range] = index;
					}
				});

				// augment global scope with declared global variables
				addDeclaredGlobals(ast, currentScopes[0], currentConfig);

				// remove shebang comments
				if (shebang && ast.comments.length && ast.comments[0].value === shebang) {
					ast.comments.splice(0, 1);

					if (ast.body.length && ast.body[0].leadingComments && ast.body[0].leadingComments[0].value === shebang) {
						ast.body[0].leadingComments.splice(0, 1);
					}
				}

				var eventGenerator = new NodeEventGenerator(api);

				eventGenerator = new CodePathAnalyzer(eventGenerator);
				eventGenerator = new CommentEventGenerator(eventGenerator, sourceCode);

				/*
				 * Each node has a type property. Whenever a particular type of
				 * node is found, an event is fired. This allows any listeners to
				 * automatically be informed that this type of node has been found
				 * and react accordingly.
				 */
				traverser.traverse(ast, {
					enter: function(node, parent) {
						node.parent = parent;
						eventGenerator.enterNode(node);
					},
					leave: function(node) {
						eventGenerator.leaveNode(node);
					}
				});
			}

			// sort by line and column
			messages.sort(function(a, b) {
				var lineDiff = a.line - b.line;

				if (lineDiff === 0) {
					return a.column - b.column;
				}
				return lineDiff;
			});

			return messages;
		};

		/**
		 * Reports a message from one of the rules.
		 * @param {string} ruleId The ID of the rule causing the message.
		 * @param {number} severity The severity level of the rule as configured.
		 * @param {ASTNode} node The AST node that the message relates to.
		 * @param {Object=} location An object containing the error line and column
		 *      numbers. If location is not provided the node's start location will
		 *      be used.
		 * @param {string} message The actual message.
		 * @param {Object} opts Optional template data which produces a formatted message
		 *     with symbols being replaced by this object's values.
		 * @param {Object} fix A fix command description.
		 * @param {Object} meta Metadata of the rule
		 * @returns {void}
		 */
		api.report = function(ruleId, severity, node, location, message, opts, fix, meta, related) { // ORION add related
			if (typeof location === "string") {
				meta = fix;
				fix = opts;
				related = opts; // ORION
				opts = message;
				message = location;
				location = node.loc.start;
			}

			// else, assume location was provided, so node may be omitted

			if (isDisabledByReportingConfig(reportingConfig, ruleId, location)) {
				return;
			}

			if (opts) {
				message = message.replace(/\{\{\s*(.+?)\s*\}\}/g, function(fullMatch, term) {
					if (term in opts) {
						return opts[term];
					}

					// Preserve old behavior: If parameter name not provided, don't replace it.
					return fullMatch;
				});
			}

			var problem = {
				ruleId: ruleId,
				severity: severity,
				node: node, // ORION
				args: opts, // ORION
				message: message,
				line: location.line,
				column: location.column, // switch to 1-base instead of 0-base ORION
				nodeType: node && node.type,
				source: sourceCode.lines[location.line - 1] || "",
				related: typeof related !== "undefined" ? related : null // ORION
			};

			// ensure there's range and text properties, otherwise it's not a valid fix
			if (fix && Array.isArray(fix.range) && typeof fix.text === "string") {

				// If rule uses fix, has metadata, but has no metadata.fixable, we should throw
				if (meta && !meta.fixable) {
					throw new Error("Fixable rules should export a `meta.fixable` property.");
				}

				problem.fix = fix;
			}

			messages.push(problem);
		};

		/**
		 * Gets the SourceCode object representing the parsed source.
		 * @returns {SourceCode} The SourceCode object.
		 */
		api.getSourceCode = function() {
			return sourceCode;
		};

		// methods that exist on SourceCode object
		var externalMethods = {
			getSource: "getText",
			getSourceLines: "getLines",
			getAllComments: "getAllComments",
			getNodeByRangeIndex: "getNodeByRangeIndex",
			getComments: "getComments",
			getJSDocComment: "getJSDocComment",
			getFirstToken: "getFirstToken",
			getFirstTokens: "getFirstTokens",
			getLastToken: "getLastToken",
			getLastTokens: "getLastTokens",
			getTokenAfter: "getTokenAfter",
			getTokenBefore: "getTokenBefore",
			getTokenByRangeStart: "getTokenByRangeStart",
			getTokens: "getTokens",
			getTokensAfter: "getTokensAfter",
			getTokensBefore: "getTokensBefore",
			getTokensBetween: "getTokensBetween"
		};

		// copy over methods
		Object.keys(externalMethods).forEach(function(methodName) {
			var exMethodName = externalMethods[methodName];

			// All functions expected to have less arguments than 5.
			api[methodName] = function(a, b, c, d, e) {
				if (sourceCode) {
					return sourceCode[exMethodName](a, b, c, d, e);
				}
				return null;
			};
		});

		/**
		 * Gets nodes that are ancestors of current node.
		 * @returns {ASTNode[]} Array of objects representing ancestors.
		 */
		api.getAncestors = function() {
			return traverser.parents();
		};

		/**
		 * Gets the scope for the current node.
		 * @returns {Object} An object representing the current node's scope.
		 */
		api.getScope = function() {
			var parents = traverser.parents(),
				scope = currentScopes[0];

			// Don't do this for Program nodes - they have no parents
			if (parents.length) {

				// if current node introduces a scope, add it to the list
				var current = traverser.current();

				if (currentConfig.parserOptions.ecmaVersion >= 6) {
					if (["BlockStatement", "SwitchStatement", "CatchClause", "FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].indexOf(current.type) >= 0) {
						parents.push(current);
					}
				} else {
					if (["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].indexOf(current.type) >= 0) {
						parents.push(current);
					}
				}

				// Ascend the current node's parents
				for (var i = parents.length - 1; i >= 0; --i) {

					// Get the innermost scope
					scope = scopeManager.acquire(parents[i], true);
					if (scope) {
						if (scope.type === "function-expression-name") {
							return scope.childScopes[0];
						}
						return scope;
					}

				}

			}

			return currentScopes[0];
		};

		/**
		 * Record that a particular variable has been used in code
		 * @param {string} name The name of the variable to mark as used
		 * @returns {boolean} True if the variable was found and marked as used,
		 *      false if not.
		 */
		api.markVariableAsUsed = function(name) {
			var scope = this.getScope(),
				hasGlobalReturn = currentConfig.parserOptions.ecmaFeatures && currentConfig.parserOptions.ecmaFeatures.globalReturn,
				specialScope = hasGlobalReturn || currentConfig.parserOptions.sourceType === "module",
				variables,
				i,
				len;

			// Special Node.js scope means we need to start one level deeper
			if (scope.type === "global" && specialScope) {
				scope = scope.childScopes[0];
			}

			do {
				variables = scope.variables;
				for (i = 0, len = variables.length; i < len; i++) {
					if (variables[i].name === name) {
						variables[i].eslintUsed = true;
						return true;
					}
				}
			} while ((scope = scope.upper));

			return false;
		};

		/**
		 * Gets the filename for the currently parsed source.
		 * @returns {string} The filename associated with the source being parsed.
		 *     Defaults to "<input>" if no filename info is present.
		 */
		api.getFilename = function() {
			if (typeof currentFilename === "string") {
				return currentFilename;
			}
			return "<input>";
		};

		/**
		 * Defines a new linting rule.
		 * @param {string} ruleId A unique rule identifier
		 * @param {Function} ruleModule Function from context to object mapping AST node types to event handlers
		 * @returns {void}
		 */
		var defineRule = api.defineRule = function(ruleId, ruleModule) {
			rules.define(ruleId, ruleModule);
		};

		/**
		 * Defines many new linting rules.
		 * @param {object} rulesToDefine map from unique rule identifier to rule
		 * @returns {void}
		 */
		api.defineRules = function(rulesToDefine) {
			Object.getOwnPropertyNames(rulesToDefine).forEach(function(ruleId) {
				defineRule(ruleId, rulesToDefine[ruleId]);
			});
		};

		/**
		 * Gets the default eslint configuration.
		 * @returns {Object} Object mapping rule IDs to their default configurations
		 */
		api.defaults = function() {
			return {}; // ORION defaults
		};

		/**
		 * Gets variables that are declared by a specified node.
		 *
		 * The variables are its `defs[].node` or `defs[].parent` is same as the specified node.
		 * Specifically, below:
		 *
		 * - `VariableDeclaration` - variables of its all declarators.
		 * - `VariableDeclarator` - variables.
		 * - `FunctionDeclaration`/`FunctionExpression` - its function name and parameters.
		 * - `ArrowFunctionExpression` - its parameters.
		 * - `ClassDeclaration`/`ClassExpression` - its class name.
		 * - `CatchClause` - variables of its exception.
		 * - `ImportDeclaration` - variables of  its all specifiers.
		 * - `ImportSpecifier`/`ImportDefaultSpecifier`/`ImportNamespaceSpecifier` - a variable.
		 * - others - always an empty array.
		 *
		 * @param {ASTNode} node A node to get.
		 * @returns {escope.Variable[]} Variables that are declared by the node.
		 */
		api.getDeclaredVariables = function(node) {
			return scopeManager && scopeManager.getDeclaredVariables(node) || [];
		};

		return api;

	}());

	return module.exports;
});
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *	 IBM Corporation - Allow original requirejs plugin to find files in Orion workspace
 *******************************************************************************/
/* eslint-disable missing-nls */
/*eslint-env node, amd*/
/*globals tern tern */
define('javascript/ternPlugins/eslint',[
	"tern/lib/tern",
	"javascript/finder",
	"eslint/lib/eslint",
	"eslint/lib/utils/source-code",
	"i18n!javascript/nls/problems"
], function(tern, Finder, Eslint, SourceCode, ProblemMessages) {

	tern.registerPlugin("eslint", /* @callback */ function(server, options) {
		return {
			//don't need any passes yet
		};
	});

	/**
	 * @description Extracts any errors captured by the tolerant Esprima parser and returns them
	 * @function
	 * @private
	 * @param {esprima.AST} ast The AST
	 * @returns {esprima.Error[]} The array of AST errors (if any)
	 */
	function extractParseErrors(ast) {
		var errors = [], errorMap = Object.create(null);
		var asterrors = ast.errors;
		if(asterrors) {
			var len = asterrors.length;
			for(var i = 0; i < len; i++) {
				var error = asterrors[i];
				var msg = error.message;
				if(errorMap[error.index] === msg) {
					continue;
				}
				errorMap[error.index] = msg;
				var start = error.start;
				var message = msg;
				var index = msg.lastIndexOf(" (");
				if (index !== -1) {
					message = msg.substr(0, index);
				}
				if(ast.tokens.length > 0) {
					//error object did not contain the token infos, try to find it
					// it is possible that there is no token at the given location because the parser didn't tokenize it with the syntax error.
					// so we need to check for null after the lookup
					var token = Finder.findToken(error.index, ast.tokens);
					if (token) {
						if (start === error.end && token.range && token.range[1] === start) {
							error.start = token.range[0];
						}
						switch(token.type) {
							case "Keyword" :
								if (token.value === "export" || token.value === "import") {
									// convert to a problem with a different ruleId
									var problem = {
										severity: "error",
										start: token.start,
										end: token.end,
										message: message,
										ruleId: "forbiddenExportImport"
									};
									errors.push(problem);
									error = null;
								}
						}
					}
				}
				if (error) {
					error.message = message;
					errors.push(error);
				}
			}
		}
		return errors;
	}
	
	/**
	 * @description Post-processes the ESLint generated problems to determine if there are any linting issues reported for the same 
	 * nodes as parse errors. If there are we discard those problems.
	 * @function
	 * @private
	 * @param {Array} parseErrors The array of parse errors, never <code>null</code>
	 * @param {Array} eslintErrors The array of eslint computed errors, never <code>null</code>
	 * @returns {Array} The filtered list of errors to report to the editor
	 * @since 6.0
	 */
	function filterProblems(parseErrors, eslintErrors) {
		var len = parseErrors.length;
		if(len < 1) {
			return eslintErrors;
		}
		var filtered = [].concat(parseErrors);
		var len2 = eslintErrors.length;
		filter: for(var i = 0; i < len2; i++) {
			var ee = eslintErrors[i];
			for(var j = 0; j < len; j++) {
				var pe = parseErrors[j];
				var node = ee.node;
				if(node && node.range && node.range[0] >= pe.index && node.range[0] <= pe.end) {
					continue filter;
				}
			}
			filtered.push(ee);
		}
		return filtered;
	}

	/**
	 * @name appendPath
	 * @description Appends the given paths together, ensuring no extra slashes appear in the paths
	 * @param {String} path The path
	 * @param {String} addition The addition to the path
	 * @returns {String} The combined path
	 * @since 14.0
	 */
	function appendPath(path, addition) {
		if (typeof path === 'string' && typeof addition === 'string') {
			var newpath = path;
			if (newpath.charAt(newpath.length - 1) !== '/') {
				newpath += '/';
			}
			if (addition.charAt(0) === '/') {
				newpath += addition.slice(1);
			} else {
				newpath += addition;
			}
			return newpath;
		}
		return null;
	}

	/**
	 * @name resolvePath
	 * @description Resolves the 
	 * @param {String} path The path to resolve, assumed to be relative
	 * @param {File} file The Tern file object to resolve against
	 * @returns {Boolean} The resolved path or null
	 * @since 14.0
	 */
	function resolvePath(path, file) {
		var _p = path,
			filepath = file.name.slice(0, file.name.lastIndexOf('/'));
		var rel = /^\.\.\//.exec(_p);
		if (rel) {
			while (rel !== null) {
				filepath = filepath.slice(0, filepath.lastIndexOf('/'));
				_p = _p.slice(3);
				rel = /^\.\.\//.exec(_p);
			}
			return appendPath(filepath, _p);
		} 
		rel = /^\.\//.test(_p);
		if(rel) {
			while (rel) {
				_p = _p.slice(2);
				rel = /^\.\//.test(_p);
			}
			return appendPath(filepath, _p);
		}
		return null;
	}

	/**
	 * The Tern delegate
	 * @since 13.0
	 */
	var _tern = {
		init: function init(server, file, config) {
			this.server = server;
			this.plugins = server.options.plugins;
			this.optionalPlugins = server.options.optionalPlugins;
			this.optionalDefs = server.options.optionalDefs;
			this.file = file;
			this.config = config;
		},
		/**
		 * @name hasFile
		 * @description Returns if Tern knows about the file 
		 * @function
		 * @param {String} path The full or relative path to the file
		 * @param {File} file the original file we are trying to find the relative path to
		 * @returns {Boolean} True if Tern knows about the file, false otherwise
		 * @since 14.0
		 */
		hasFile: function hasFile(path, file) {
			if(path && this.server && this.server.fileMap) {
				if(this.server.fileMap[path]) {
					return true;
				}
				if(path.indexOf('.') === 0 && file) {
					var p = resolvePath(path, file);
					if(p) {
						if(p.lastIndexOf('.js') < 0) {
							p += ".js";
						}
						return this.server.fileMap[p];
					}
				}
			}
			return false;
		},
		findRefs: function findRefs(query, file) {
			try {
				return tern.findRefs(this.server, query, file);
			} catch(e) {
				if (!e.name || e.name !== "TernError") {
					throw e;
				}
			}
		},
		findRefsToVariable: function findRefsToVariable(query, file, expr, checkShadowing) {
			try {
				return tern.findRefsToVariable(this.server, query, file, expr, checkShadowing);
			} catch(e) {
				if (!e.name || e.name !== "TernError") {
					throw e;
				}
			}
		},
		findRefsToProperty: function findRefsToProperty(query, expr, prop) {
			try {
				return tern.findRefsToProperty(this.server, query, expr, prop);
			} catch(e) {
				if (!e.name || e.name !== "TernError") {
					throw e;
				}
			}
		},
		ternError: function ternError(msg) {
			return tern.ternError(msg);
		},
		findQueryExpr: function findQueryExpr(file, query, wide) {
			try {
				return tern.findQueryExpr(file, query, wide);
			} catch(e) {
				if (!e.name || e.name !== "TernError") {
					throw e;
				}
			}
		},
		findExprType: function findExprType(query, file, expr) {
			try {
				return tern.findExprType(this.server, query, file, expr);
			} catch(e) {
				if (!e.name || e.name !== "TernError") {
					throw e;
				}
			}
		},
		getDef: function getDef(defName) {
			for(var i = 0, len = this.server.defs.length; i < len; i++) {
				var def = this.server.defs[i];
				if(def && (def['!name'] === defName || def[defName])) {
					return def;
				}
			}
		},
		/**
		 * @name libKnown
		 * @description Checks if the library with the given name is known to Tern or the computed environment
		 * @function
		 * @param {string} libName The name of the library
		 * @returns {bool} True if the library name if known, false otherwise
		 */
		libKnown: function libKnown(libName) {
			if(this.server.mod && this.server.mod.modules) {
				if(this.server.mod.modules.knownModules && this.server.mod.modules.knownModules[libName]) {
					return true;
				}
				var keys = Object.keys(this.server.mod.modules.modules);
				for(var i = 0, len = keys.length; i < len; i++) {
					var mod = this.server.mod.modules.modules[keys[i]];
					if(mod && mod.modName === libName) {
						return true;
					}
				}
			}
			if(this.server.mod && this.server.mod.requireJS && this.server.mod.requireJS.interfaces) {
				keys = Object.keys(this.server.mod.requireJS.interfaces);
				for(i = 0, len = keys.length; i < len; i++) {
					mod = this.server.mod.requireJS.interfaces[keys[i]];
					if(mod && mod.reqName === libName) {
						return true;
					}
				}
			}
			//@see https://bugs.eclipse.org/bugs/show_bug.cgi?id=512833
			//if the computed environment includes the lib, don't nag about it
			if(this.config && this.config.env) {
				if(this.config.env[libName]) {
					return true;
				}
			}
			return false;
		},
		getEnvFromDep: function getEnvFromDep(depName) {
			var deps = this.file.ast.dependencies;
			for(var i = 0, len = deps.length; i< len; i++) {
				if(deps[i].value === depName) {
					return deps[i].env;
				}
			}
		},
		pluginRunning: function pluginRunning(pluginName) {
			return this.server.plugins[pluginName];
		},
		isLoadEagerly: function isLoadEagerly(fileName) {
			if(this.server.options && Array.isArray(this.server.options.loadEagerly)) {
				return this.server.options.loadEagerly.length > 0 && this.server.options.loadEagerly.indexOf(fileName) > -1;
			}
			return false;
		}
	};

	tern.defineQueryType("lint", {
		takesFile: true,
		/**
		 * @callback
		 */
		run: function(server, query, file) {
			var config = query.config;
			config.tern = _tern;
			_tern.init(server, file, config);
			if (!config.parserOptions) {
				var parserOptions = Object.create(null);
				parserOptions.ecmaVersion = server.options.ecmaVersion;
				var ecmaVersion = server.options.ecmaVersion ? server.options.ecmaVersion : 5;
				var features = Object.create(null);
				if (ecmaVersion === 6) {
					features.arrowFunctions = true;
					features.binaryLiterals = true;
					features.blockBindings = true;
					features.classes = true;
					features.defaultParams = true;
					features.destructuring = true;
					features.forOf = true;
					features.generators = true;
					features.objectLiteralComputedProperties = true;
					features.objectLiteralDuplicateProperties = true;
					features.objectLiteralShorthandMethods = true;
					features.objectLiteralShorthandProperties = true;
					features.octalLiterals = true;
					features.regexUFlag = true;
					features.regexYFlag = true;
					features.restParams = true;
					features.spread = true;
					features.superInFunctions = true;
					features.templateStrings = true;
					features.unicodeCodePointEscapes = true;
				}
				parserOptions.ecmaFeatures = features;
				parserOptions.sourceType = server.options.sourceType;
				config.parserOptions = parserOptions;
			}
			
			var strippedMessages = [];
			var error = null;
			try {
				var messages = Eslint.verify(new SourceCode(file.text, file.ast), config, file.name);
				messages.forEach(function(element) {
					var strippedMessage =
						{
							args: element.args,
							severity: element.severity,
							column: element.column,
							line: element.line,
							message: element.message,
							nodeType: element.nodeType,
							ruleId: element.ruleId,
							source: element.source
						};
					if (element.node && element.node.range) {
						strippedMessage.node = {
							range: element.node.range
						};
					}
					if (element.related) {
						strippedMessage.related = {
							range: element.related.range
						};
					}
					strippedMessages.push(strippedMessage);
				});
			} catch(e) {
				error =
					{
						severity: "error",
						column: 1,
						line: 0,
						args: { nls: 'eslintValidationFailure', 0: e.message && e.message.length !== 0 ? e.message : ProblemMessages['noErrorDetailed']},
						start: 0,
						end: 1,
						ruleId: 'forbiddenExportImport'
					};
			}
			var parseErrors = extractParseErrors(file.ast);
			if (error) {
				parseErrors.push(error);
			}
			return filterProblems(parseErrors, strippedMessages);
		}
	});
});

/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (https://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (https://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env node, amd*/
/*globals infer tern resolver*/
/**
 * Tern type index and templates for ExpressJS node support
 */
define('javascript/ternPlugins/express',[
	"tern/lib/tern",
	"javascript/finder",
	"i18n!javascript/nls/messages"
], function(tern, Finder, Messages) {

	var templates = [
	/* eslint-disable missing-nls */
		{
			name: "express require",
			nodes: {top:true, member:false, prop:false},
			template: "var ${app} = require('express');",
			doc: Messages['expressrequire'],
			url: "https://expressjs.com/en/4x/api.html"
			
		},
		{
			name: "express app",
			nodes: {top:true, member:false, prop:false},
			template: "var express = require('express');\n" +
					  "var ${app} = express();\n" + 
					  "${cursor}\n"+ 
					  "app.listen(${timeout});\n",
			doc: Messages['expressInstance'],
			url: "https://expressjs.com/en/4x/api.html#app"
		},
		{
			name: "express app get",
			nodes: {top:true, member:false, prop:false},
			template: "var value = app.get(${id}, function(request, result){\n" +
					  "\t${cursor}\n});\n",
			doc: Messages['expressGet'],
			url: "https://expressjs.com/en/4x/api.html#app.get"
		},
		{
			name: "express app set",
			nodes: {top:true, member:false, prop:false},
			template: "app.set(${id}, ${value});\n",
			doc: Messages['expressSet'],
			url: "https://expressjs.com/en/4x/api.html#app.set"
		},
		{
			name: "express app use",
			nodes: {top:true, member:false, prop:false},
			template: "app.use(${fnOrObject});\n",
			doc: Messages['expressUse'],
			url: "https://expressjs.com/en/4x/api.html#app.use"
		},
		{
			name: "express app engine",
			nodes: {top:true, member:false, prop:false},
			template: "app.engine(${fnOrObject});\n",
			doc: Messages['expressEngine'],
			url: "https://expressjs.com/en/4x/api.html#app.engine"
		},
		{
			name: "express app param",
			nodes: {top:true, member:false, prop:false},
			template: "app.param(${id}, ${value});\n",
			doc: Messages['expressParam'],
			url: "https://expressjs.com/en/4x/api.html#app.param"
		},
		{
			name: "express app error use",
			nodes: {top:true, member:false, prop:false},
			template: "app.use(function(error, request, result, next) {\n" + 
  					  "\tresult.send(${code}, ${message});\n" + 
					  "});\n",
			doc: Messages['expressUseError'],
			url: "https://expressjs.com/en/4x/api.html#app.use"
		}
		/* eslint-enable missing-nls */
	];
	
	var cachedQuery;
	
	/* eslint-enable missing-nls */
	tern.registerPlugin("express", /* @callback */ function(server, options) { //$NON-NLS-1$
	 	server.addDefs(defs);
	    return {
	      defs : defs,
	      passes: {
	      	/**
	      	 * @callback
	      	 */
	      	completion: function(file, query) {
	      		cachedQuery = query;
	      	},
	      	/**
	      	 * @callback
	      	 */
	      	variableCompletion: function(file, start, end, gather) {
	      		if(cachedQuery.includeTemplates || cachedQuery.includeTemplates === undefined) {
		      		var kind = Finder.findCompletionKind(file.ast, end);
		      		if(kind && kind.kind) {
			      		var tmpls = Finder.findTemplatesForKind(templates, kind.kind, cachedQuery.ecma ? cachedQuery.ecma : 6);
			      		tmpls.forEach(function(template) {
							gather(template.name, null, 0, function(c) {
								c.template = template.template;
								c.description = template.description;
								c.doc = template.doc;
								c.url = template.url;
								c.type = 'template'; //$NON-NLS-1$
								c.ecma = template.ecma;
								c.origin = 'express';
								c.overwrite = true;
							});
						});
			      	}
		      	}
	      	}
	      }
	    };
	});
	
	/* eslint-disable missing-nls */
	var defs = {
  "!name": "express",
  "!define": {
  	"!known_modules": {
	      "express": {
	      	  "static" : {
		    		"!type": "fn(name: string)",
		    		"!doc": "Built-in middleware function.  Pass the name of the directory that contains the static assets."
		    	},
	          "!type": "fn() -> app",
	          "!url": "https://expressjs.com",
	          "!doc": "Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications."
          }
      },
	"express.static.!0": "string",
    "View.prototype.lookup.!0": "string",
    "View.prototype.resolve.!0": "string",
    "View.prototype.resolve.!1": "string",
    "View.!0": "string",
    "slice.!0": "number",
    "slice.!1": "number",
    "app.use.!0": "string",
    "app.engine.!0": "string",
    "app.set.!0": "string",
    "app.path.!ret": "string",
    "app.enabled.!0": "string",
    "app.enabled.!ret": "bool",
    "app.disabled.!0": "string",
    "app.disabled.!ret": "bool",
    "app.enable.!0": "string",
    "app.disable.!0": "string",
    "app.all.!0": "string",
    "app.render.!0": "string",
    "app.etag.!1": "string",
    "app.etag.!ret": "string",
    "app.wetag.!1": "string",
    "app.wetag.!ret": "string",
    "app.isAbsolute.!0": "string",
    "app.isAbsolute.!ret": "bool",
    "app.normalizeType.!0": "string",
    "app.normalizeType.!ret": {
      "value": "string",
      "quality": "number",
      "params": {
        "<i>": "string"
      }
    },
    "app.normalizeTypes.!0": "[?]",
    "app.normalizeTypes.!ret": "[acceptParams.!ret]",
    "app.setCharset.!0": "string",
    "app.setCharset.!1": "string",
    "app.setCharset.!ret": "string",
    "app.application.param.!0": "string",
    "app.application.handle.!0": "string",
    "app.application.handle.!2": "string",
    "app.application.use.!0": "string",
    "app.application.route.!0": "string",
    "app.request.range.!0": "number",
    "app.request.param.!0": "string",
    "app.request.param.!ret": "string",
    "app.request.response.status.!0": "number",
    "app.request.response.sendStatus.!0": "number",
    "app.request.response.sendFile.!0": "string",
    "app.request.response.attachment.!0": "string",
    "app.request.response.append.!0": "string",
    "app.request.response.get.!0": "string",
    "app.request.response.get.!ret": "string",
    "app.request.response.clearCookie.!0": "string",
    "app.request.response.cookie.!0": "string",
    "app.request.response.location.!0": "string",
    "app.request.Route.prototype._handles_method.!ret": "bool",
    "app.request.Route.!0": "string",
    "app.response.status.!0": "number",
    "app.response.sendStatus.!0": "number",
    "app.response.sendFile.!0": "string",
    "app.response.attachment.!0": "string",
    "app.response.append.!0": "string",
    "app.response.get.!0": "string",
    "app.response.get.!ret": "string",
    "app.response.clearCookie.!0": "string",
    "app.response.cookie.!0": "string",
    "app.response.location.!0": "string",
    "app.Route.prototype._handles_method.!ret": "bool",
    "app.Route.!0": "string",
    "tryRender.!1": {},
    "req.range.!0": "number",
    "req.range.!ret": "[?]",
    "req.param.!0": "string",
    "req.param.!ret": "string",
    "req.is.!0": "[?]",
    "defineGetter.!1": "string",
    "res.status.!0": "number",
    "res.sendStatus.!0": "number",
    "res.sendFile.!0": "string",
    "res.attachment.!0": "string",
    "res.append.!0": "string",
    "res.get.!0": "string",
    "res.get.!ret": "string",
    "res.clearCookie.!0": "string",
    "res.cookie.!0": "string",
    "res.location.!0": "string",
    "res.render.!1": {
      "!type": "fn(err: ?, str: ?)",
      "!doc": "default callback to respond"
    },
    "sendfile.!3": "fn(err: +Error)",
    "acceptParams.!0": "string",
    "acceptParams.!ret": {
      "value": "string",
      "quality": "number",
      "params": {
        "<i>": "string"
      }
    },
    "tryStat.!0": "string",
    "proto.param.!0": "string",
    "proto.handle.!0": "string",
    "proto.handle.!2": "string",
    "proto.use.!0": "string",
    "proto.route.!0": "string",
    "proto.!ret": {
      "_params": "[?]",
      "stack": "[?]",
      "!type": "fn(req: ?, res: ?, next: ?)"
    },
    "Route.prototype._handles_method.!ret": "bool",
    "Route.prototype.all.!ret": "+Route",
    "Route.!0": "string",
    "Layer.prototype.match.!0": "string",
    "Layer.prototype.match.!ret": "bool",
    "Layer.!0": "string",
    "appendMethods.!0": {
      "!type": "[?]",
      "!doc": "store options for OPTIONS request\nonly used if OPTIONS request"
    },
    "gettype.!ret": "string",
    "matchLayer.!1": "string",
    "restore.!ret": "fn(err: ?)",
    "wrap.!1": "fn(old: ?, err: ?)",
    "wrap.!ret": "fn()",
    "decode_param.!0": "string",
    "decode_param.!ret": "string",
    "Route.stack.<i>": "+Layer",
    "app.application.!ret": {
      "_params": "proto.!ret._params",
      "stack": "proto.!ret.stack"
    }
  },
  "View": {
    "prototype": {
      "lookup": {
        "!type": "fn(name: string)",
        "!doc": "Lookup view by the given `name`\n\n@param {string} name\n@private"
      },
      "render": {
        "!type": "fn(options: ?, callback: ?)",
        "!doc": "Render with the given options.\n\n@param {object} options\n@param {function} callback\n@private"
      },
      "resolve": {
        "!type": "fn(dir: string, file: string)",
        "!doc": "Resolve the file within the given directory.\n\n@param {string} dir\n@param {string} file\n@private"
      }
    },
    "!type": "fn(name: string, options: ?)",
    "!doc": "Initialize a new `View` with the given `name`.\n\nOptions:\n\n  - `defaultEngine` the default template engine name\n  - `engines` template engine require() cache\n  - `root` root path for view lookup\n\n@param {string} name\n@param {object} options\n@public"
  },
  "slice": {
    "!type": "fn(from: number, to?: number) -> !this",
    "!doc": "Module variables.\n@private"
  },
  "app": {
  	"delete": {
  		"!type": "fn()",
  	},
  	"get": {
  		"!type": "fn()",
  	},
  	"METHOD": {
  		"!type": "fn()",
  	},
  	"post": {
  		"!type": "fn()",
  	},
  	"put": {
  		"!type": "fn()",
  	},
    "init": {
      "!type": "fn(app: +Function) -> fn(req: ?, res: ?, next: ?)|+Function",
      "!doc": "Initialize the server.\n\n  - setup default configuration\n  - setup default middleware\n  - setup route reflection methods\n\n@private"
    },
    "defaultConfiguration": {
      "!type": "fn()",
      "!doc": "Initialize application configuration.\n@private"
    },
    "lazyrouter": {
      "!type": "fn()",
      "!doc": "lazily adds the base router if it has not yet been added.\n\nWe cannot add the base router in the defaultConfiguration because\nit reads app settings which might be set after that has run.\n\n@private"
    },
    "handle": {
      "!type": "fn(req: ?, res: ?, callback: ?)",
      "!doc": "Dispatch a req, res pair into the application. Starts pipeline processing.\n\nIf no callback is provided, then default error handlers will respond\nin the event of an error bubbling through the stack.\n\n@private"
    },
    "use": {
      "!type": "fn(fn: ?) -> !this",
      "!doc": "Proxy `Router#use()` to add middleware to the app router.\nSee Router#use() documentation for details.\n\nIf the _fn_ parameter is an express app, then it will be\nmounted at the _route_ specified.\n\n@public"
    },
    "route": {
      "!type": "fn(path: ?)",
      "!doc": "Proxy to the app `Router#route()`\nReturns a new `Route` instance for the _path_.\n\nRoutes are isolated middleware stacks for specific paths.\nSee the Route api docs for details.\n\n@public"
    },
    "engine": {
      "!type": "fn(ext: string, fn: +Function) -> !this",
      "!doc": "Register the given template engine callback `fn`\nas `ext`.\n\nBy default will `require()` the engine based on the\nfile extension. For example if you try to render\na \"foo.jade\" file Express will invoke the following internally:\n\n    app.engine('jade', require('jade').__express);\n\nFor engines that do not provide `.__express` out of the box,\nor if you wish to \"map\" a different extension to the template engine\nyou may use this method. For example mapping the EJS template engine to\n\".html\" files:\n\n    app.engine('html', require('ejs').renderFile);\n\nIn this case EJS provides a `.renderFile()` method with\nthe same signature that Express expects: `(path, options, callback)`,\nthough note that it aliases this method as `ejs.__express` internally\nso if you're using \".ejs\" extensions you dont need to do anything.\n\nSome template engines do not follow this convention, the\n[Consolidate.js](https://github.com/tj/consolidate.js)\nlibrary was created to map all of node's popular template\nengines to follow this convention, thus allowing them to\nwork seamlessly within Express.\n\n@param {String} ext\n@param {Function} fn\n@return {app} for chaining\n@public"
    },
    "param": {
      "!type": "fn(name: string|[?], fn: +Function) -> !this",
      "!doc": "Proxy to `Router#param()` with one added api feature. The _name_ parameter\ncan be an array of names.\n\nSee the Router#param() docs for more details.\n\n@param {String|Array} name\n@param {Function} fn\n@return {app} for chaining\n@public"
    },
    "set": {
      "!type": "fn(setting: string, val: ?) -> !this.settings.<i>",
      "!doc": "Assign `setting` to `val`, or return `setting`'s value.\n\n   app.set('foo', 'bar');\n   app.get('foo');\n   // => \"bar\"\n\nMounted servers inherit their parent server's settings.\n\n@param {String} setting\n@param {*} [val]\n@return {Server} for chaining\n@public"
    },
    "path": {
      "!type": "fn() -> string",
      "!doc": "Return the app's absolute pathname\nbased on the parent(s) that have\nmounted it.\n\nFor example if the application was\nmounted as \"/admin\", which itself\nwas mounted as \"/blog\" then the\nreturn value would be \"/blog/admin\".\n\n@return {String}\n@private"
    },
    "enabled": {
      "!type": "fn(setting: string) -> bool",
      "!doc": "Check if `setting` is enabled (truthy).\n\n   app.enabled('foo')\n   // => false\n\n   app.enable('foo')\n   app.enabled('foo')\n   // => true\n\n@param {String} setting\n@return {Boolean}\n@public"
    },
    "disabled": {
      "!type": "fn(setting: string) -> bool",
      "!doc": "Check if `setting` is disabled.\n\n   app.disabled('foo')\n   // => true\n\n   app.enable('foo')\n   app.disabled('foo')\n   // => false\n\n@param {String} setting\n@return {Boolean}\n@public"
    },
    "enable": {
      "!type": "fn(setting: string) -> app",
      "!doc": "Enable `setting`.\n\n@param {String} setting\n@return {app} for chaining\n@public"
    },
    "disable": {
      "!type": "fn(setting: string) -> app",
      "!doc": "Disable `setting`.\n\n@param {String} setting\n@return {app} for chaining\n@public"
    },
    "<i>": "fn(path: ?) -> !this",
    "all": {
      "!type": "fn(path: string) -> !this",
      "!doc": "Special-cased \"all\" method, applying the given route `path`,\nmiddleware, and callback to _every_ HTTP method.\n\n@param {String} path\n@param {Function} ...\n@return {app} for chaining\n@public"
    },
    "render": {
      "!type": "fn(name: string, options: ?, callback: +Function) -> !1.!ret",
      "!doc": "Render the given view `name` name with `options`\nand a callback accepting an error and the\nrendered template string.\n\nExample:\n\n   app.render('email', { name: 'Tobi' }, function(err, html){\n     // ...\n   })\n\n@param {String} name\n@param {Object|Function} options or fn\n@param {Function} callback\n@public"
    },
    "listen": {
      "!type": "fn() -> ?",
      "!doc": "Listen for connections.\n\nA node `http.Server` is returned, with this\napplication (which is a `Function`) as its\ncallback. If you wish to create both an HTTP\nand HTTPS server you may do so with the \"http\"\nand \"https\" modules as shown here:\n\n   var http = require('http')\n     , https = require('https')\n     , express = require('express')\n     , app = express();\n\n   http.createServer(app).listen(80);\n   https.createServer({ ... }, app).listen(443);\n\n@return {http.Server}\n@public"
    },
    "etag": {
      "!type": "fn(body: string|?, encoding?: string) -> string",
      "!doc": "Return strong ETag for `body`.\n\n@param {String|Buffer} body\n@param {String} [encoding]\n@return {String}\n@api private"
    },
    "wetag": {
      "!type": "fn(body: string|?, encoding?: string) -> string",
      "!doc": "Return weak ETag for `body`.\n\n@param {String|Buffer} body\n@param {String} [encoding]\n@return {String}\n@api private"
    },
    "isAbsolute": {
      "!type": "fn(path: string) -> bool",
      "!doc": "Check if `path` looks absolute.\n\n@param {String} path\n@return {Boolean}\n@api private"
    },
    "normalizeType": {
      "!type": "fn(type: string) -> acceptParams.!ret",
      "!doc": "Normalize the given `type`, for example \"html\" becomes \"text/html\".\n\n@param {String} type\n@return {Object}\n@api private"
    },
    "normalizeTypes": {
      "!type": "fn(types: [?]) -> [acceptParams.!ret]",
      "!doc": "Normalize `types`, for example \"html\" becomes \"text/html\".\n\n@param {Array} types\n@return {Array}\n@api private"
    },
    "compileETag": {
      "!type": "fn(val: ?) -> !0",
      "!doc": "Compile \"etag\" value to function.\n\n@param  {Boolean|String|Function} val\n@return {Function}\n@api private"
    },
    "compileQueryParser": {
      "!type": "fn(val: string|+Function) -> !0",
      "!doc": "Compile \"query parser\" value to function.\n\n@param  {String|Function} val\n@return {Function}\n@api private"
    },
    "compileTrust": {
      "!type": "fn(val: ?) -> !0",
      "!doc": "Compile \"proxy trust\" value to function.\n\n@param  {Boolean|String|Number|Array|Function} val\n@return {Function}\n@api private"
    },
    "setCharset": {
      "!type": "fn(type: string, charset: string) -> !0",
      "!doc": "Set the charset in a given Content-Type string.\n\n@param {String} type\n@param {String} charset\n@return {String}\n@api private"
    },
    "application": {
      "param": {},
      "handle": {},
      "use": {},
      "route": {},
      "process_params": "proto.process_params",
      "<i>": "proto.<i>"
    },
    "request": {
      "range": {},
      "param": {},
      "response": {
        "status": {},
        "sendStatus": {},
        "sendFile": {},
        "attachment": {},
        "append": {},
        "get": {},
        "clearCookie": {},
        "cookie": {},
        "location": {},
        "contentType": "res.type",
        "set": "res.header",
        "links": "res.links",
        "send": "res.send",
        "json": "res.json",
        "jsonp": "res.jsonp",
        "sendfile": "res.sendfile",
        "download": "res.download",
        "type": "res.type",
        "format": "res.format",
        "header": "res.header",
        "redirect": "res.redirect",
        "vary": "res.vary",
        "render": "res.render"
      },
      "Route": {
        "prototype": {
          "_handles_method": {},
          "_options": "Route.prototype._options",
          "dispatch": "Route.prototype.dispatch",
          "all": "Route.prototype.all",
          "<i>": "Route.prototype.<i>"
        }
      },
      "init": "app.init",
      "get": "req.header",
      "etag": "app.etag",
      "wetag": "app.wetag",
      "isAbsolute": "app.isAbsolute",
      "normalizeType": "app.normalizeType",
      "normalizeTypes": "app.normalizeTypes",
      "compileETag": "app.compileETag",
      "compileQueryParser": "app.compileQueryParser",
      "compileTrust": "app.compileTrust",
      "setCharset": "app.setCharset",
      "application": "proto",
      "request": "req",
      "header": "req.header",
      "accepts": "req.accepts",
      "acceptsEncodings": "req.acceptsEncodings",
      "acceptsCharsets": "req.acceptsCharsets",
      "acceptsLanguages": "req.acceptsLanguages",
      "is": "req.is"
    },
    "response": {
      "status": {},
      "sendStatus": {},
      "sendFile": {},
      "attachment": {},
      "append": {},
      "get": {},
      "clearCookie": {},
      "cookie": {},
      "location": {},
      "contentType": "res.type",
      "set": "res.header",
      "links": "res.links",
      "send": "res.send",
      "json": "res.json",
      "jsonp": "res.jsonp",
      "sendfile": "res.sendfile",
      "download": "res.download",
      "type": "res.type",
      "format": "res.format",
      "header": "res.header",
      "redirect": "res.redirect",
      "vary": "res.vary",
      "render": "res.render"
    },
    "Route": {
      "prototype": {
        "_handles_method": {},
        "_options": "Route.prototype._options",
        "dispatch": "Route.prototype.dispatch",
        "all": "Route.prototype.all",
        "<i>": "Route.prototype.<i>"
      }
    },
    "!doc": "Application prototype."
  },
  "trustProxyDefaultSymbol": {
    "!type": "string",
    "!doc": "Variable for trust proxy inheritance back-compat\n@private"
  },
  "logerror": {
    "!type": "fn(err: +Error)",
    "!doc": "Log error using console.error.\n\n@param {Error} err\n@private"
  },
  "tryRender": {
    "!type": "fn(view: ?, options: tryRender.!1, callback: ?)",
    "!doc": "Try rendering a view.\n@private"
  },
  "req": {
    "header": {
      "!type": "fn(name: ?) -> !this.headers.referrer",
      "!doc": "Return request header.\n\nThe `Referrer` header field is special-cased,\nboth `Referrer` and `Referer` are interchangeable.\n\nExamples:\n\n    req.get('Content-Type');\n    // => \"text/plain\"\n\n    req.get('content-type');\n    // => \"text/plain\"\n\n    req.get('Something');\n    // => undefined\n\nAliased as `req.header()`.\n\n@param {String} name\n@return {String}\n@public"
    },
    "accepts": "fn()",
    "acceptsEncodings": {
      "!type": "fn() -> string|[?]",
      "!doc": "Check if the given `encoding`s are accepted.\n\n@param {String} ...encoding\n@return {String|Array}\n@public"
    },
    "acceptsCharsets": {
      "!type": "fn() -> string|[?]",
      "!doc": "Check if the given `charset`s are acceptable,\notherwise you should respond with 406 \"Not Acceptable\".\n\n@param {String} ...charset\n@return {String|Array}\n@public"
    },
    "acceptsLanguages": {
      "!type": "fn() -> string|[?]",
      "!doc": "Check if the given `lang`s are acceptable,\notherwise you should respond with 406 \"Not Acceptable\".\n\n@param {String} ...lang\n@return {String|Array}\n@public"
    },
    "range": {
      "!type": "fn(size: number) -> [?]",
      "!doc": "Parse Range header field,\ncapping to the given `size`.\n\nUnspecified ranges such as \"0-\" require\nknowledge of your resource length. In\nthe case of a byte range this is of course\nthe total number of bytes. If the Range\nheader field is not given `null` is returned,\n`-1` when unsatisfiable, `-2` when syntactically invalid.\n\nNOTE: remember that ranges are inclusive, so\nfor example \"Range: users=0-3\" should respond\nwith 4 users when available, not 3.\n\n@param {Number} size\n@return {Array}\n@public"
    },
    "param": {
      "!type": "fn(name: string, defaultValue?: ?) -> !this.params.<i>",
      "!doc": "Return the value of param `name` when present or `defaultValue`.\n\n - Checks route placeholders, ex: _/user/:id_\n - Checks body params, ex: id=12, {\"id\":12}\n - Checks query string params, ex: ?id=12\n\nTo utilize request bodies, `req.body`\nshould be an object. This can be done by using\nthe `bodyParser()` middleware.\n\n@param {String} name\n@param {Mixed} [defaultValue]\n@return {String}\n@public"
    },
    "is": "fn(types: ?)",
    "!doc": "Request prototype.",
    "init": "app.init",
    "get": "req.header",
    "etag": "app.etag",
    "wetag": "app.wetag",
    "isAbsolute": "app.isAbsolute",
    "normalizeType": "app.normalizeType",
    "normalizeTypes": "app.normalizeTypes",
    "compileETag": "app.compileETag",
    "compileQueryParser": "app.compileQueryParser",
    "compileTrust": "app.compileTrust",
    "setCharset": "app.setCharset",
    "application": "proto",
    "request": "req",
    "response": "res",
    "Route": "Route"
  },
  "defineGetter": {
    "!type": "fn(obj: req, name: string, getter: ?)",
    "!doc": "Helper function for creating a getter on an object.\n\n@param {Object} obj\n@param {String} name\n@param {Function} getter\n@private"
  },
  "res": {
    "status": {
      "!type": "fn(code: number) -> !this",
      "!doc": "Set status `code`.\n\n@param {Number} code\n@return {ServerResponse}\n@public"
    },
    "links": {
      "!type": "fn(links: ?) -> ?",
      "!doc": "Set Link header field with the given `links`.\n\nExamples:\n\n   res.links({\n     next: 'https://api.example.com/users?page=2',\n     last: 'https://api.example.com/users?page=5'\n   });\n\n@param {Object} links\n@return {ServerResponse}\n@public"
    },
    "send": {
      "!type": "fn(body: ?) -> !this",
      "!doc": "Send a response.\n\nExamples:\n\n    res.send(new Buffer('wahoo'));\n    res.send({ some: 'json' });\n    res.send('<p>some html</p>');\n\n@param {string|number|boolean|object|Buffer} body\n@public"
    },
    "json": {
      "!type": "fn(obj: ?)",
      "!doc": "Send JSON response.\n\nExamples:\n\n    res.json(null);\n    res.json({ user: 'tj' });\n\n@param {string|number|boolean|object} obj\n@public"
    },
    "jsonp": {
      "!type": "fn(obj: ?)",
      "!doc": "Send JSON response with JSONP callback support.\n\nExamples:\n\n    res.jsonp(null);\n    res.jsonp({ user: 'tj' });\n\n@param {string|number|boolean|object} obj\n@public"
    },
    "sendStatus": {
      "!type": "fn(statusCode: number)",
      "!doc": "Send given HTTP status code.\n\nSets the response status to `statusCode` and the body of the\nresponse to the standard description from node's http.STATUS_CODES\nor the statusCode number if no description.\n\nExamples:\n\n    res.sendStatus(200);\n\n@param {number} statusCode\n@public"
    },
    "sendFile": {
      "!type": "fn(path: ?, options: ?, callback: ?)",
      "!doc": "Transfer the file at the given `path`.\n\nAutomatically sets the _Content-Type_ response header field.\nThe callback `callback(err)` is invoked when the transfer is complete\nor when an error occurs. Be sure to check `res.sentHeader`\nif you wish to attempt responding, as the header and some data\nmay have already been transferred.\n\nOptions:\n\n  - `maxAge`   defaulting to 0 (can be string converted by `ms`)\n  - `root`     root directory for relative filenames\n  - `headers`  object of headers to serve with file\n  - `dotfiles` serve dotfiles, defaulting to false; can be `\"allow\"` to send them\n\nOther options are passed along to `send`.\n\nExamples:\n\n The following example illustrates how `res.sendFile()` may\n be used as an alternative for the `static()` middleware for\n dynamic situations. The code backing `res.sendFile()` is actually\n the same code, so HTTP cache support etc is identical.\n\n    app.get('/user/:uid/photos/:file', function(req, res){\n      var uid = req.params.uid\n        , file = req.params.file;\n\n      req.user.mayViewFilesFrom(uid, function(yes){\n        if (yes) {\n          res.sendFile('/uploads/' + uid + '/' + file);\n        } else {\n          res.send(403, 'Sorry! you cant see that.');\n        }\n      });\n    });\n\n@public"
    },
    "sendfile": {
      "!type": "fn(path: ?, options: ?, callback: ?)",
      "!doc": "Transfer the file at the given `path`.\n\nAutomatically sets the _Content-Type_ response header field.\nThe callback `callback(err)` is invoked when the transfer is complete\nor when an error occurs. Be sure to check `res.sentHeader`\nif you wish to attempt responding, as the header and some data\nmay have already been transferred.\n\nOptions:\n\n  - `maxAge`   defaulting to 0 (can be string converted by `ms`)\n  - `root`     root directory for relative filenames\n  - `headers`  object of headers to serve with file\n  - `dotfiles` serve dotfiles, defaulting to false; can be `\"allow\"` to send them\n\nOther options are passed along to `send`.\n\nExamples:\n\n The following example illustrates how `res.sendfile()` may\n be used as an alternative for the `static()` middleware for\n dynamic situations. The code backing `res.sendfile()` is actually\n the same code, so HTTP cache support etc is identical.\n\n    app.get('/user/:uid/photos/:file', function(req, res){\n      var uid = req.params.uid\n        , file = req.params.file;\n\n      req.user.mayViewFilesFrom(uid, function(yes){\n        if (yes) {\n          res.sendfile('/uploads/' + uid + '/' + file);\n        } else {\n          res.send(403, 'Sorry! you cant see that.');\n        }\n      });\n    });\n\n@public"
    },
    "download": {
      "!type": "fn(path: ?, filename: ?, callback: ?)",
      "!doc": "Transfer the file at the given `path` as an attachment.\n\nOptionally providing an alternate attachment `filename`,\nand optional callback `callback(err)`. The callback is invoked\nwhen the data transfer is complete, or when an error has\nocurred. Be sure to check `res.headersSent` if you plan to respond.\n\nThis method uses `res.sendfile()`.\n\n@public"
    },
    "type": {
      "!type": "fn(type: ?)",
      "!doc": "Set _Content-Type_ response header with `type` through `mime.lookup()`\nwhen it does not contain \"/\", or set the Content-Type to `type` otherwise.\n\nExamples:\n\n    res.type('.html');\n    res.type('html');\n    res.type('json');\n    res.type('application/json');\n    res.type('png');\n\n@param {String} type\n@return {ServerResponse} for chaining\n@public"
    },
    "format": {
      "!type": "fn(obj: ?) -> !this",
      "!doc": "Respond to the Acceptable formats using an `obj`\nof mime-type callbacks.\n\nThis method uses `req.accepted`, an array of\nacceptable types ordered by their quality values.\nWhen \"Accept\" is not present the _first_ callback\nis invoked, otherwise the first match is used. When\nno match is performed the server responds with\n406 \"Not Acceptable\".\n\nContent-Type is set for you, however if you choose\nyou may alter this within the callback using `res.type()`\nor `res.set('Content-Type', ...)`.\n\n   res.format({\n     'text/plain': function(){\n       res.send('hey');\n     },\n\n     'text/html': function(){\n       res.send('<p>hey</p>');\n     },\n\n     'appliation/json': function(){\n       res.send({ message: 'hey' });\n     }\n   });\n\nIn addition to canonicalized MIME types you may\nalso use extnames mapped to these types:\n\n   res.format({\n     text: function(){\n       res.send('hey');\n     },\n\n     html: function(){\n       res.send('<p>hey</p>');\n     },\n\n     json: function(){\n       res.send({ message: 'hey' });\n     }\n   });\n\nBy default Express passes an `Error`\nwith a `.status` of 406 to `next(err)`\nif a match is not made. If you provide\na `.default` callback it will be invoked\ninstead.\n\n@param {Object} obj\n@return {ServerResponse} for chaining\n@public"
    },
    "attachment": {
      "!type": "fn(filename: string) -> !this",
      "!doc": "Set _Content-Disposition_ header to _attachment_ with optional `filename`.\n\n@param {String} filename\n@return {ServerResponse}\n@public"
    },
    "append": {
      "!type": "fn(field: string, val: string|[?]) -> ?",
      "!doc": "Append additional header `field` with value `val`.\n\nExample:\n\n   res.append('Link', ['<https://localhost/>', '<https://localhost:3000/>']);\n   res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');\n   res.append('Warning', '199 Miscellaneous warning');\n\n@param {String} field\n@param {String|Array} val\n@return {ServerResponse} for chaining\n@public"
    },
    "header": {
      "!type": "fn(field: ?, val: ?) -> !this",
      "!doc": "Set header `field` to `val`, or pass\nan object of header fields.\n\nExamples:\n\n   res.set('Foo', ['bar', 'baz']);\n   res.set('Accept', 'application/json');\n   res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });\n\nAliased as `res.header()`.\n\n@param {String|Object} field\n@param {String|Array} val\n@return {ServerResponse} for chaining\n@public"
    },
    "get": {
      "!type": "fn(field: string) -> string",
      "!doc": "Get value for header `field`.\n\n@param {String} field\n@return {String}\n@public"
    },
    "clearCookie": {
      "!type": "fn(name: string, options: ?) -> ?",
      "!doc": "Clear cookie `name`.\n\n@param {String} name\n@param {Object} options\n@return {ServerResponse} for chaining\n@public"
    },
    "cookie": {
      "!type": "fn(name: string, value: string|?, options: ?) -> !this",
      "!doc": "Set cookie `name` to `value`, with the given `options`.\n\nOptions:\n\n   - `maxAge`   max-age in milliseconds, converted to `expires`\n   - `signed`   sign the cookie\n   - `path`     defaults to \"/\"\n\nExamples:\n\n   // \"Remember Me\" for 15 minutes\n   res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });\n\n   // save as above\n   res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })\n\n@param {String} name\n@param {String|Object} value\n@param {Options} options\n@return {ServerResponse} for chaining\n@public"
    },
    "location": {
      "!type": "fn(url: string) -> !this",
      "!doc": "Set the location header to `url`.\n\nThe given `url` can also be \"back\", which redirects\nto the _Referrer_ or _Referer_ headers or \"/\".\n\nExamples:\n\n   res.location('/foo/bar').;\n   res.location('https://example.com');\n   res.location('../login');\n\n@param {String} url\n@return {ServerResponse} for chaining\n@public"
    },
    "redirect": {
      "!type": "fn(url: ?)",
      "!doc": "Redirect to the given `url` with optional response `status`\ndefaulting to 302.\n\nThe resulting `url` is determined by `res.location()`, so\nit will play nicely with mounted apps, relative paths,\n`\"back\"` etc.\n\nExamples:\n\n   res.redirect('/foo/bar');\n   res.redirect('https://example.com');\n   res.redirect(301, 'https://example.com');\n   res.redirect('../login'); // /blog/post/1 -> /blog/login\n\n@public"
    },
    "vary": {
      "!type": "fn(field: [?]|string) -> !this",
      "!doc": "Add `field` to Vary. If already present in the Vary set, then\nthis call is simply ignored.\n\n@param {Array|String} field\n@return {ServerResponse} for chaining\n@public"
    },
    "render": {
      "!type": "fn(view: ?, options: ?, callback: ?)",
      "!doc": "Render `view` with the given `options` and optional callback `fn`.\nWhen a callback function is given a response will _not_ be made\nautomatically, otherwise a response of _200_ and _text/html_ is given.\n\nOptions:\n\n - `cache`     boolean hinting to the engine it should cache\n - `filename`  filename of the view being rendered\n\n@public"
    },
    "!doc": "Response prototype.",
    "contentType": "res.type",
    "set": "res.header"
  },
  "charsetRegExp": {
    "!type": "+RegExp",
    "!doc": "Module variables.\n@private"
  },
  "sendfile": {
    "!type": "fn(res: ?, file: ?, options: ?, callback: fn(err: +Error))",
    "!doc": "pipe the send file stream"
  },
  "acceptParams": {
    "!type": "fn(str: string, index: ?) -> acceptParams.!ret",
    "!doc": "Parse accept params `str` returning an\nobject with `.value`, `.quality` and `.params`.\nalso includes `.originalIndex` for stable sorting\n\n@param {String} str\n@return {Object}\n@api private"
  },
  "parseExtendedQueryString": {
    "!type": "fn(str: ?) -> ?",
    "!doc": "Parse an extended query string with qs.\n\n@return {Object}\n@private"
  },
  "newObject": {
    "!type": "fn() -> ?",
    "!doc": "Return new empty object.\n\n@return {Object}\n@api private"
  },
  "tryStat": {
    "!type": "fn(path: string) -> ?",
    "!doc": "Return a stat, maybe.\n\n@param {string} path\n@return {fs.Stats}\n@private"
  },
  "proto": {
    "param": {
      "!type": "fn(name: string, fn: +Function) -> !this",
      "!doc": "Map the given param placeholder `name`(s) to the given callback.\n\nParameter mapping is used to provide pre-conditions to routes\nwhich use normalized placeholders. For example a _:user_id_ parameter\ncould automatically load a user's information from the database without\nany additional code,\n\nThe callback uses the same signature as middleware, the only difference\nbeing that the value of the placeholder is passed, in this case the _id_\nof the user. Once the `next()` function is invoked, just like middleware\nit will continue on to execute the route, or subsequent parameter functions.\n\nJust like in middleware, you must either respond to the request or call next\nto avoid stalling the request.\n\n app.param('user_id', function(req, res, next, id){\n   User.find(id, function(err, user){\n     if (err) {\n       return next(err);\n     } else if (!user) {\n       return next(new Error('failed to load user'));\n     }\n     req.user = user;\n     next();\n   });\n });\n\n@param {String} name\n@param {Function} fn\n@return {app} for chaining\n@public"
    },
    "handle": {
      "!type": "fn(req: ?, res: ?, out: ?)",
      "!doc": "Dispatch a req, res into the router.\n@private"
    },
    "process_params": {
      "!type": "fn(layer: ?, called: ?, req: ?, res: ?, done: ?)",
      "!doc": "Process any parameters for the layer.\n@private"
    },
    "use": {
      "!type": "fn(fn: ?) -> !this",
      "!doc": "Use the given middleware function, with optional path, defaulting to \"/\".\n\nUse (like `.all`) will run for any http METHOD, but it will not add\nhandlers for those methods so OPTIONS requests will not consider `.use`\nfunctions even if they could respond.\n\nThe other difference is that _route_ path is stripped and not visible\nto the handler function. The main effect of this feature is that mounted\nhandlers can operate without any code changes regardless of the \"prefix\"\npathname.\n\n@public"
    },
    "route": {
      "!type": "fn(path: string) -> proto.route",
      "!doc": "Create a new Route for the given path.\n\nEach route contains a separate middleware stack and VERB handlers.\n\nSee the Route api documentation for details on adding handlers\nand middleware to routes.\n\n@param {String} path\n@return {Route}\n@public"
    },
    "<i>": "fn(path: ?) -> !this",
    "!type": "fn(options: ?) -> fn(req: ?, res: ?, next: ?)",
    "!doc": "Initialize a new `Router` with the given `options`.\n\n@param {Object} options\n@return {Router} which is an callable function\n@public"
  },
  "Route": {
    "prototype": {
      "_handles_method": {
        "!type": "fn(method: ?) -> bool",
        "!doc": "Determine if the route handles a given method.\n@private"
      },
      "_options": {
        "!type": "fn() -> [string]",
        "!doc": "@return {Array} supported HTTP methods\n@private"
      },
      "dispatch": {
        "!type": "fn(req: ?, res: ?, done: ?)",
        "!doc": "dispatch req, res into this route\n@private"
      },
      "all": {
        "!type": "fn() -> !this",
        "!doc": "Add a handler for all HTTP verbs to this route.\n\nBehaves just like middleware and can respond or call `next`\nto continue processing.\n\nYou can use multiple `.all` call to add multiple handlers.\n\n  function check_something(req, res, next){\n    next();\n  };\n\n  function validate_user(req, res, next){\n    next();\n  };\n\n  route\n  .all(validate_user)\n  .all(check_something)\n  .get(function(req, res, next){\n    res.send('hello world');\n  });\n\n@param {function} handler\n@return {Route} for chaining\n@api public"
      },
      "<i>": "fn() -> !this"
    },
    "!type": "fn(path: string)",
    "!doc": "Initialize `Route` with the given `path`,\n\n@param {String} path\n@public",
    "path": "string",
    "stack": "[+Layer]",
    "methods": {
      "_all": "bool",
      "<i>": "bool",
      "!doc": "route handlers for various http methods"
    }
  },
  "createApplication": {
    "!type": "fn() -> fn(req: ?, res: ?, next: ?)|+Function",
    "!doc": "Create an express application.\n\n@return {Function}\n@api public"
  },
  "Layer": {
    "prototype": {
      "handle_error": {
        "!type": "fn(error: +Error, req: ?, res: ?, next: ?)",
        "!doc": "Handle the error for the layer.\n\n@param {Error} error\n@param {Request} req\n@param {Response} res\n@param {function} next\n@api private"
      },
      "handle_request": {
        "!type": "fn(req: ?, res: ?, next: ?)",
        "!doc": "Handle the request for the layer.\n\n@param {Request} req\n@param {Response} res\n@param {function} next\n@api private"
      },
      "match": {
        "!type": "fn(path: string) -> bool",
        "!doc": "Check if this route matches `path`, if so\npopulate `.params`.\n\n@param {String} path\n@return {Boolean}\n@api private"
      }
    },
    "!type": "fn(path: string, options: ?, fn: ?) -> +Layer",
    "name": "string",
    "params": {
      "!doc": "store values"
    },
    "path": "string",
    "keys": "[?]"
  },
  "appendMethods": {
    "!type": "fn(list: [?], addition: ?)",
    "!doc": "append methods to a list of methods"
  },
  "getPathname": {
    "!type": "fn(req: ?)",
    "!doc": "get pathname of request"
  },
  "gettype": {
    "!type": "fn(obj: ?) -> string",
    "!doc": "get type for error message"
  },
  "matchLayer": {
    "!type": "fn(layer: ?, path: string) -> +Error",
    "!doc": "Match path to a layer.\n\n@param {Layer} layer\n@param {string} path\n@private"
  },
  "mergeParams": {
    "!type": "fn(params: ?, parent: ?) -> !0",
    "!doc": "merge params with parent params"
  },
  "restore": {
    "!type": "fn(fn: ?, obj: ?) -> fn(err: ?)",
    "!doc": "restore obj props after function"
  },
  "sendOptionsResponse": {
    "!type": "fn(res: ?, options: [?], next: ?)",
    "!doc": "send an OPTIONS response"
  },
  "wrap": {
    "!type": "fn(old: fn(err: ?)|fn(), fn: fn(old: ?, err: ?)) -> fn()",
    "!doc": "wrap a function"
  },
  "decode_param": {
    "!type": "fn(val: string) -> !0",
    "!doc": "Decode param value.\n\n@param {string} val\n@return {string}\n@private"
  },
  "Error": {
    "statusCode": {
      "!type": "number",
      "!doc": "Respond"
    },
    "status": "number",
    "code": "string",
    "message": "string"
  },
  "objectRegExp": "+RegExp"
	};
});
/*******************************************************************************
 * @license
 * Copyright (c) 2012 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 * 
 * Contributors: IBM Corporation - initial API and implementation
 ******************************************************************************/
/*eslint-env browser, amd, node*/
(function(root, factory) { // UMD
    if (typeof define === "function" && define.amd) { //$NON-NLS-0$
        define('orion/Deferred',factory);
    } else if (typeof exports === "object") { //$NON-NLS-0$
        module.exports = factory();
    } else {
        root.orion = root.orion || {};
        root.orion.Deferred = factory();
    }
}(this, function() {
    var queue = [],
        running = false;

    function run() {
        var fn;
        while ((fn = queue.shift())) {
            fn();
        }
        running = false;
    }

	var runAsync = (function() {
		if (typeof process !== "undefined" && typeof process.nextTick === "function") {
			var nextTick = process.nextTick;
    		return function() {
    			nextTick(run);
    		};
		} else if (typeof MutationObserver === "function") {
			var div = document.createElement("div");
			var observer = new MutationObserver(run);
			observer.observe(div, {
            	attributes: true
        	});
        	return function() {
        		div.setAttribute("class", "_tick");
        	};
		}
		return function() {
			setTimeout(run, 0);
		};
	})();

    function enqueue(fn) {
        queue.push(fn);
        if (!running) {
            running = true;
            runAsync();
        }
    }

    function noReturn(fn) {
        return function(result) {
            fn(result);
        };
    }
    
    function settleDeferred(fn, result, deferred) {
    	try {
    		var listenerResult = fn(result);
    		var listenerThen = listenerResult && (typeof listenerResult === "object" || typeof listenerResult === "function") && listenerResult.then;
    		if (typeof listenerThen === "function") {
    			if (listenerResult === deferred.promise) {
    				deferred.reject(new TypeError());
    			} else {
    				var listenerResultCancel = listenerResult.cancel;
    				if (typeof listenerResultCancel === "function") {
    					deferred._parentCancel = listenerResultCancel.bind(listenerResult);
    				} else {
    					delete deferred._parentCancel;
    				}
    				listenerThen.call(listenerResult, noReturn(deferred.resolve), noReturn(deferred.reject), noReturn(deferred.progress));
    			}
    		} else {
    			deferred.resolve(listenerResult);
    		}
    	} catch (e) {
    		deferred.reject(e);
    	}
    }


    /**
     * @name orion.Promise
     * @class Interface representing an eventual value.
     * @description Promise is an interface that represents an eventual value returned from the single completion of an operation.
     *
     * <p>For a concrete class that implements Promise and provides additional API, see {@link orion.Deferred}.</p>
     * @see orion.Deferred
     * @see orion.Deferred#promise
     */
    /**
     * @name then
     * @function
     * @memberOf orion.Promise.prototype
     * @description Adds handlers to be called on fulfillment or progress of this promise.
     * @param {Function} [onResolve] Called when this promise is resolved.
     * @param {Function} [onReject] Called when this promise is rejected.
     * @param {Function} [onProgress] May be called to report progress events on this promise.
     * @returns {orion.Promise} A new promise that is fulfilled when the given <code>onResolve</code> or <code>onReject</code>
     * callback is finished. The callback's return value gives the fulfillment value of the returned promise.
     */
    /**
     * Cancels this promise.
     * @name cancel
     * @function
     * @memberOf orion.Promise.prototype
     * @param {Object} reason The reason for canceling this promise.
     * @param {Boolean} [strict]
     */

    /**
     * @name orion.Deferred
     * @borrows orion.Promise#then as #then
     * @borrows orion.Promise#cancel as #cancel
     * @class Provides abstraction over asynchronous operations.
     * @description Deferred provides abstraction over asynchronous operations.
     *
     * <p>Because Deferred implements the {@link orion.Promise} interface, a Deferred may be used anywhere a Promise is called for.
     * However, in most such cases it is recommended to use the Deferred's {@link #promise} field instead, which exposes a 
     * simplified, minimally <a href="https://github.com/promises-aplus/promises-spec">Promises/A+</a>-compliant interface to callers.</p>
     */
    function Deferred() {
        var result, state, listeners = [],
            _this = this;

        function notify() {
            var listener;
            while ((listener = listeners.shift())) {
                var deferred = listener.deferred;
                var methodName = state === "fulfilled" ? "resolve" : "reject"; //$NON-NLS-0$ //$NON-NLS-1$ //$NON-NLS-2$
                var fn = listener[methodName];
                if (typeof fn === "function") { //$NON-NLS-0$
                	settleDeferred(fn, result, deferred);
                } else {
                    deferred[methodName](result);
                }
            }
        }

        function _reject(error) {
            delete _this._parentCancel;
            state = "rejected";
            result = error;
            if (listeners.length) {
                enqueue(notify);
            }
        }

        function _resolve(value) {
            function once(fn) {
                return function(result) {
                    if (!state || state === "assumed") {
                          fn(result);
                    }
                };
            }
            delete _this._parentCancel;
            try {
                var valueThen = value && (typeof value === "object" || typeof value === "function") && value.then;
                if (typeof valueThen === "function") {
                    if (value === _this) {
                        _reject(new TypeError());
                    } else {
                        state = "assumed";
                        var valueCancel = value && value.cancel;
                        if (typeof valueCancel !== "function") {
                            var deferred = new Deferred();
                            value = deferred.promise;
                            try {
                                valueThen(deferred.resolve, deferred.reject, deferred.progress);
                            } catch (thenError) {
                                deferred.reject(thenError);
                            }
                            valueCancel = value.cancel;
                            valueThen = value.then;
                        }
                        result = value;
                        valueThen.call(value, once(_resolve), once(_reject));
                        _this._parentCancel = valueCancel.bind(value);
                    }
                } else {
                    state = "fulfilled";
                    result = value;
                    if (listeners.length) {
                        enqueue(notify);
                    }
                }
            } catch (error) {
                once(_reject)(error);
            }
        }

        function cancel() {
            var parentCancel = _this._parentCancel;
            if (parentCancel) {
                delete _this._parentCancel;
                parentCancel();
            } else if (!state) {
                var cancelError = new Error("Cancel");
                cancelError.name = "Cancel";
                _reject(cancelError);
            }
        }


        /**
         * Resolves this Deferred.
         * @name resolve
         * @function
         * @memberOf orion.Deferred.prototype
         * @param {Object} value
         * @returns {orion.Promise}
         */
        this.resolve = function(value) {
            if (!state) {
                _resolve(value);
            }
            return _this;
        };

        /**
         * Rejects this Deferred.
         * @name reject
         * @function
         * @memberOf orion.Deferred.prototype
         * @param {Object} error
         * @param {Boolean} [strict]
         * @returns {orion.Promise}
         */
        this.reject = function(error) {
            if (!state) {
                _reject(error);
            }
            return _this;
        };

        /**
         * Notifies listeners of progress on this Deferred.
         * @name progress
         * @function
         * @memberOf orion.Deferred.prototype
         * @param {Object} update The progress update.
         * @returns {orion.Promise}
         */
        this.progress = function(update) {
            if (!state) {
                listeners.forEach(function(listener) {
                    if (listener.progress) {
                        try {
                            listener.progress(update);
                        } catch (ignore) {
                            // ignore
                        }
                    }
                });
            }
            return _this.promise;
        };

        this.cancel = function() {
            if (_this._parentCancel) {
                setTimeout(cancel, 0);
            } else {
                cancel();
            }
            return _this;
        };

        // Note: "then" ALWAYS returns before having onResolve or onReject called as per http://promises-aplus.github.com/promises-spec/
        this.then = function(onFulfill, onReject, onProgress) {
        	var deferred = new Deferred();
            deferred._parentCancel = _this.promise.cancel;
            listeners.push({
                resolve: onFulfill,
                reject: onReject,
                progress: onProgress,
                deferred: deferred
            });
            if (state === "fulfilled" || state === "rejected") {
                enqueue(notify);
            }
            return deferred.promise;
        };

        /**
         * The promise exposed by this Deferred.
         * @name promise
         * @field
         * @memberOf orion.Deferred.prototype
         * @type orion.Promise
         */
        this.promise = {
            then: _this.then,
            cancel: _this.cancel
        };
    }

    /**
     * Returns a promise that represents the outcome of all the input promises.
     * <p>When <code>all</code> is called with a single parameter, the returned promise has <dfn>eager</dfn> semantics,
     * meaning that if any input promise rejects, the returned promise immediately rejects, without waiting for the rest of the
     * input promises to fulfill.</p>
     *
     * To obtain <dfn>lazy</dfn> semantics (meaning the returned promise waits for every input promise to fulfill), pass the
     * optional parameter <code>optOnError</code>.
     * @name all
     * @function
     * @memberOf orion.Deferred
     * @static
     * @param {orion.Promise[]} promises The input promises.
     * @param {Function} [optOnError] Handles a rejected input promise. <code>optOnError</code> is invoked for every rejected
     * input promise, and is passed the reason the input promise was rejected. <p><code>optOnError</code> can return a value, which
     * allows it to act as a transformer: the return value serves as the final fulfillment value of the rejected promise in the 
     * results array generated by <code>all</code>.
     * @returns {orion.Promise} A new promise. The returned promise is generally fulfilled to an <code>Array</code> whose elements
     * give the fulfillment values of the input promises. <p>However, if an input promise rejects and eager semantics is used, the 
     * returned promise will instead be fulfilled to a single error value.</p>
     */
    Deferred.all = function(promises, optOnError) {
        var count = promises.length,
            result = [],
            rejected = false,
            deferred = new Deferred();

        deferred.then(undefined, function() {
            rejected = true;
            promises.forEach(function(promise) {
                if (promise.cancel) {
                    promise.cancel();
                }
            });
        });

        function onResolve(i, value) {
            if (!rejected) {
                result[i] = value;
                if (--count === 0) {
                    deferred.resolve(result);
                }
            }
        }

        function onReject(i, error) {
            if (!rejected) {
                if (optOnError) {
                    try {
                        onResolve(i, optOnError(error));
                        return;
                    } catch (e) {
                        error = e;
                    }
                }
                deferred.reject(error);
            }
        }

        if (count === 0) {
            deferred.resolve(result);
        } else {
            promises.forEach(function(promise, i) {
                promise.then(onResolve.bind(undefined, i), onReject.bind(undefined, i));
            });
        }
        return deferred.promise;
    };

    /**
     * Applies callbacks to a promise or to a regular object.
     * @name when
     * @function
     * @memberOf orion.Deferred
     * @static
     * @param {Object|orion.Promise} value Either a {@link orion.Promise}, or a normal value.
     * @param {Function} onResolve Called when the <code>value</code> promise is resolved. If <code>value</code> is not a promise,
     * this function is called immediately.
     * @param {Function} onReject Called when the <code>value</code> promise is rejected. If <code>value</code> is not a promise, 
     * this function is never called.
     * @param {Function} onProgress Called when the <code>value</code> promise provides a progress update. If <code>value</code> is
     * not a promise, this function is never called.
     * @returns {orion.Promise} A new promise.
     */
    Deferred.when = function(value, onResolve, onReject, onProgress) {
        var promise, deferred;
        if (value && typeof value.then === "function") { //$NON-NLS-0$
            promise = value;
        } else {
            deferred = new Deferred();
            deferred.resolve(value);
            promise = deferred.promise;
        }
        return promise.then(onResolve, onReject, onProgress);
    };

    return Deferred;
}));
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/* eslint-env amd */
define('javascript/compilationUnit',[
'orion/Deferred'
], function(Deferred) {

    /**
     * @name CompilationUnit
     * @description Creates a new CompilationUint instance. These instances should not be cached as they do
     * not respond to model change events.
     * @constructor
     * @param {Array.<String>} sourceblocks The blocks of source to combine into one unit
     * @param {Object} metadata The metadata describing the file this unit represents
     * @param {Object} editorContext Optional editor context for the source file. Delegated to for setText and to get line information
     * @returns {CompilationUnit} The new CompiationUnit instance
     * @since 8.0
     */
    function CompilationUnit(sourceblocks, metadata, editorContext) {
        this._blocks = sourceblocks;
        this._metadata = metadata;
        if (metadata){
        	// The context returned by getEditorContext only contains javascript text so set the content type to match
        	this._metadata.contentType = {id: 'application/javascript'}; //$NON-NLS-1$
        }
        this._ec = editorContext;
        this._deps = [];
    }

    /**
     * @description Builds the backing source for the compilation unit
     * @function
     * @private
     */
    CompilationUnit.prototype._init = function _init() {
		var wrappedFunctionCallPrefix = "";  // Previously to avoid ESLint warnings we prefixed function calls with 'this.' See Bug 481137
        var _cursor = 0;
        this._source = '';
        this._blocks.sort(function(a, b){
        	var _a = a.offset ? a.offset : 0;
        	var _b = b.offset ? b.offset : 0;
        	return _a - _b;
        });
        for(var i = 0; i < this._blocks.length; i++) {
            var block = this._blocks[i];
            if(block.dependencies) {
            	this._deps.push(block.dependencies);
            }
            var pad = block.offset - _cursor;
            if(block.isWrappedFunctionCall){
				if (pad < wrappedFunctionCallPrefix.length){
					continue;
				}
				pad -= wrappedFunctionCallPrefix.length;
				while(pad > 0) {
                	this._source += ' '; //$NON-NLS-1$
                	pad--;
            	}
            	this._source += wrappedFunctionCallPrefix;
            	this._source += block.text;
            	if (block.text && block.text.charAt(block.text.length-1) !== ';'){
            		this._source += ';';
            	}
            } else {
	            while(pad > 0) {
	                this._source += ' '; //$NON-NLS-1$
	                pad--;
	            }
	            this._source += block.text;
            }

            _cursor = this._source.length;
        }
    };

    /**
     * @description Returns the source of this compilation unit
     * @function
     * @returns {String} The source of the compilation unit
     */
    CompilationUnit.prototype.getSource = function getSource() {
    	if(!this._source) {
            this._init();
        }
        return this._source;
    };

    /**
     * @description Returns if the given offset is valid compared to the blocks of code
     * that make up this unit
     * @function
     * @param {Number} offset The offset to check
     * @returns {Boolean} If the given offset is within any of the backing code blocks
     */
    CompilationUnit.prototype.validOffset = function validOffset(offset) {
        if(!this._blocks || this._blocks.length < 1 || offset < 0) {
	        return false;
	    }
	    for(var i = 0; i < this._blocks.length; i++) {
	        var block = this._blocks[i];
	        var idx = block.offset;
	        if(offset >= idx && offset <= idx+block.text.length) {
	            return true;
	        }
	    }
	    return false;
    };

    /**
     * @description Returns an EditorContext-like object that can resolve promises for <code>getText</code> and <code>getFileMetadata</code>
     * @function
     * @returns {Object} The EditorContext object to use when parsing
     */
   CompilationUnit.prototype.getEditorContext = function getEditorContext() {
        var proxy = Object.create(null);
        var that = this;
        proxy.getText = function() {
            return new Deferred().resolve(that.getSource());
        };
        proxy.getFileMetadata = function() {
            return new Deferred().resolve(that._metadata);
        };
        proxy.setText = function(text, start, end) {
            if(that._ec) {
                return that._ec.setText(text, start, end);
            }
            return new Deferred().resolve(null);
        };
        // Our tooling needs access to other functions on the editorContext so copy them here
        if (that._ec){
	        	proxy.getSelections = that._ec.getSelections;
	        	proxy.setSelection = that._ec.setSelection;
	        	proxy.syntaxCheck = that._ec.syntaxCheck;
	        	proxy.setCaretOffset = that._ec.setCaretOffset;
	    	}
        return proxy;
    };

    /**
     * @description Returns the computed list of dependencies
     * @function
     * @returns {Array.<string>} Returns the array of dependencies, or an empty array, never null
     * @since 9.0
     */
    CompilationUnit.prototype.getDependencies = function getDependencies() {
    	return this._deps;
    };

    return CompilationUnit;
});
/*******************************************************************************
 * @license
 * Copyright (c) 2015 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - Allow original requirejs plugin to find files in Orion workspace
 *******************************************************************************/
/* eslint-disable missing-nls */
/*eslint-env node, amd*/
/*globals tern tern*/
define('javascript/ternPlugins/html',[
	"tern/lib/infer", 
	"tern/lib/tern", 
	"./resolver", 
	"javascript/compilationUnit", 
	"javascript/finder"
], function(infer, tern, resolver, CU, Finder) {

  tern.registerPlugin("html", /* @callback */ function(server, options) {
	server._htmlDeps = Object.create(null);
	server._htmlDeps.map = Object.create(null);
	
	server.on("beforeLoad", function(file) {
      this._htmlDeps.file  = file.name;
    });
    server.on("reset", function() {
      	server._htmlDeps = Object.create(null);
		server._htmlDeps.map = Object.create(null);
    });
	
	function isHTML(name) {
		return /(?:html|htm|xhtml)$/g.test(name);
	}
	
    return {
      passes: {
        /**
		 * @callback
		 */
		postParse: function postParse(ast, text) {
			if(isHTML(ast.sourceFile.name)) {
				var deps = server._htmlDeps.map[server._htmlDeps.file];
				if(Array.isArray(deps)) {
					ast.dependencies = deps.slice(0);
				}
				resolver.doPostParse(server, ast, infer.cx().definitions);
			}
		},
		/**
		 * @callback
		 */
		preInfer: function preInfer(ast, scope) {
			if(isHTML(ast.sourceFile.name)) {
				resolver.doPreInfer(server);
				//should all be resolved by now, add them to the file mapping
				if(Array.isArray(ast.dependencies)) {
					for(var i = 0; i < ast.dependencies.length; i++) {
						var dep = ast.dependencies[i];
						var _f = resolver.getResolved(dep);
						if(_f && _f.file) {
							server.addFile(_f.file, _f.contents, server._htmlDeps.file);
						}
					}
				}
			}
		},
		/**
		 * @callback
		 */
		preParse: function preParse(text, options) {
			var f = options.directSourceFile.name;
			if(isHTML(f)) {
				// TODO We want to use a cuProvider to cache the CU, but there are no onModelChange events in the Tern server to recognize that the file contents have changed.
				var blocks = Finder.findScriptBlocks(text);
				if(Array.isArray(blocks)) {
					var cu = new CU(blocks, {location: f});
					var source = cu.getSource(); // this also sets the dependencies from the blocks
					server._htmlDeps.file = f;
					server._htmlDeps.map[f] = cu.getDependencies(); 
					return source;
				}
			}
		}
      }
    };
  });
});

/*******************************************************************************
 * @license
 * Copyright (c) 2013, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd*/
define('javascript/signatures',[
	"javascript/finder",
], function(Finder) {

	var Signatures = {
		/**
		 * @name FUNCTION_IMAGE
		 * @description The CSS class name for the function icon
		 * @type String
		 * @since 14.0
		 */
		FUNCTION_IMAGE: "iconFunction",
		/**
		 * @name CLASS_IMAGE
		 * @description The CSS class name for the class icon
		 * @type String
		 * @since 14.0
		 */
		CLASS_IMAGE: "iconClass",
		/**
		 * @name OBJECT_IMAGE
		 * @description The CSS class name for the object icon
		 * @type String
		 * @since 14.0
		 */
		OBJECT_IMAGE: "iconObject",
	
		/**
		 * @name computeSignature
		 * @description Computes a signature object from the given AST node. The object holds two properties:
		 * <code>sig</code> - the human readable signature and <code>range</code> 
		 * @function
		 * @public
		 * @memberof javascript.Signatures.prototype
		 * @param {Object} astnode The AST node to parse and compute the signature from
		 * @returns {Object} The computed signature object or <code>null</code> if the computation fails
		 */
		computeSignature: function(astnode, kind) {
			if(astnode) {
				if(astnode.sig) {
					return astnode.sig;
				}
				var val = this.getNameFrom(astnode, kind);
				return {
					sig: val.name,
					details: val.details,
					range: this.getSignatureSourceRangeFrom(astnode)
				};
			}
			return null;
		},
		
		/**
		 * @name getCalleeSignature
		 * @description Returns a signature string describing the parent call expression of the given node. astnode must be
		 * a function expression or arrow expression.  astnode.parent must be available. Will return <code>null</code> otherwise.
		 * If call expression's first argument is a string under 30 chars, it will be included in the signature.
		 * @function
		 * @public
		 * @param {Object} astnode The AST node to parse and compute the signature from
		 * @returns {String} returns a signature string or <code>null</code>
		 */
		getCalleeSignature: function getCalleeSignature(astnode){
			if (astnode && astnode.type === 'FunctionExpression' || astnode.type === 'ArrowFunctionExpression'){
				var callExpr = astnode.parent;
				if (!callExpr){
					var foundNode = Finder.findNode(astnode.start, astnode.sourceFile.ast, {parents: true});
					if (foundNode){
						callExpr = foundNode.parents[foundNode.parents.length-1];
					}
				}
				if (callExpr && callExpr.type === 'CallExpression'){
					if (callExpr.callee && callExpr.callee.name){
						var sig = callExpr.callee.name;
						sig += this.getArgumentsFrom(callExpr);
						return sig;
					}
				}
			}
			return null;
		},
		
		/**
		 * @name getParamsFrom
		 * @description Retrieves the parameters from the given AST node iff it a function declaration. If there is an attached doc node
		 * it will be consulted to help compute the types of the parameters
		 * @function
		 * @public
		 * @memberof javascript.Signatures.prototype
		 * @param {Object} astnode The AST node to compute the parameters from
		 * @returns {Array} An array of parameter names suitable for display, in the order they are defined in source. If no parameters
		 * can be computed an empty array is returned, never <code>null</code>
		 */
		getParamsFrom: function(astnode) {
			if(astnode) {
				var params = astnode.params;
				//TODO with the attached doc node we can augment this infos
				if(params && params.length > 0) {
					var length = params.length;
					var value = '';
					for(var i = 0; i < length; i++) {
						if(params[i].name) {
							value += params[i].name;
						}
						else {
							value += 'Object';  //$NON-NLS-0$
						}
						if(i < length -1) {
							value += ', ';  //$NON-NLS-0$
						}
					}
					return value;
				} 
			}
		},
		
		/**
		 * @name getPropertyListFrom
		 * @description Retrieves the properties from the given AST node iff it is a object declaration.
		 * @function
		 * @public
		 * @memberof javascript.Signatures.prototype
		 * @param {Object} astnode The AST node to compute the parameters from
		 * @param {Integer} maxLength maximum length of string to return,  defaults to 50
		 * @returns {String} A list of named properties, comma separated in source defined order, surrounded by {}. 
		 * 			Ellipsis will be added if no properties are available or max length reached.
		 */
		getPropertyListFrom: function(astnode, maxLength) {
			if (!maxLength){
				maxLength = 50;
			}
			if (maxLength < 0){
				maxLength = 0;
			}
			if(astnode) {
				var props = astnode.properties;
				if(props && props.length > 0) {
					var length = props.length;
					var name;
					var value = '{';
					for(var i = 0; i < length; i++) {
						if(props[i].key && props[i].key.name) {
							name = props[i].key.name;
						} else {
							name = 'Object';  //$NON-NLS-0$
						}
						
						if ((value.length + name.length) > (maxLength+1)){
							value += '...';   //$NON-NLS-0$
							break;
						} else {
							value += name;
							if(i < length -1) {
								value += ', ';  //$NON-NLS-0$
							}
						}
					}
					value += '}';
					return value;
				}
			}
			return '{...}';  //$NON-NLS-0$
		},
		
		/**
		 * @name getPropertyListFrom
		 * @description Retrieves the arguments from the given astnode if it is a CallExpression.
		 * @function
		 * @public
		 * @memberof javascript.Signatures.prototype
		 * @param {Object} astnode The AST node to compute the arguments from
		 * @returns {String} A list of arguments, comma separated in source defined order, surrounded by () 
		 * 			Ellipsis will be added if no properties are available or max length reached.
		 */
		getArgumentsFrom: function(astnode) {
			var maxLength = 50;
			if(astnode) {
				var callArgs = astnode.arguments;
				if(callArgs && callArgs.length > 0) {
					var len = callArgs.length;
					var argName;
					var value = '(';
					for(var i = 0; i < len; i++) {
						var callArg = callArgs[i];
						if (callArg.type === 'ArrayExpression'){
							argName = '[]'; //$NON-NLS-1$
						} else if (callArg.type === 'ObjectExpression'){
							argName = '{}'; //$NON-NLS-1$
						} else if (callArg.type === 'Literal' && typeof callArg.raw === 'string'){
							argName = callArg.raw;
						} else if (callArg.type === 'FunctionExpression'){
							argName = 'function(){...}'; //$NON-NLS-1$
						} else if (callArg.type === 'ArrowFunctionExpression'){
							argName = '() => {...}'; //$NON-NLS-1$
						}
						if ((value.length + argName.length) > (maxLength+1)){
							value += '...';   //$NON-NLS-0$
							break;
						} else {
							value += argName;
							if(i < len -1) {
								value += ', ';  //$NON-NLS-0$
							}
						}
					}
					value += ')';
					return value;
				}
			}
			return '()';  //$NON-NLS-0$
		},
		
		/**
		 * @name getNameFrom
		 * @description Returns an object describing what to display for the given AST node. If there is an attached doc node it
		 * will be consulted to help compute the name to display
		 * @function
		 * @public
		 * @memberof javascript.Signatures.prototype
		 * @param {Object} astnode The AST node to compute the name from
		 * @returns {String} An object containing 'name', the computed name to display for the node or <code>null</code> if one could not be 
		 * 					computed and possibly 'details' if optional display information is computed
		 */
		getNameFrom: function(astnode, kind) {
			var name = "Anonymous " + astnode.type;  //$NON-NLS-0$
			var details;
			if(astnode) {
				switch(astnode.type) {
					case 'ClassDeclaration' :
						name = 'class ';
						if (astnode.id && astnode.id.start !== astnode.id.end) {
							name += astnode.id.name;
						} else {
							name += '<anonymous>';
						}
						break;
					case 'ClassExpression' :
						name = 'class ';
						if (astnode.id && astnode.id.start !== astnode.id.end) {
							name += astnode.id.name;
						} else {
							name += '<anonymous>';
						}
						break;
					case 'FunctionDeclaration' :
						//TODO with the attached doc node we can augment this infos
						if(astnode.id && astnode.id.name) {
							name = astnode.id.name+'(';
							var fdparams = this.getParamsFrom(astnode);
							if(fdparams) {
								name += fdparams;
							}
							name += ')';
						}
						break;
					case 'MethodDefinition' :
						if(astnode.key && astnode.key.name) {
							name = astnode.key.name+'(';
							var mdParams = this.getParamsFrom(astnode.value);
							if(mdParams) {
								name += mdParams;
							}
							name += ')';
						}
						break;
					case 'ArrowFunctionExpression' :
						name = 'arrow function(';  //$NON-NLS-0$
						var afeparams = this.getParamsFrom(astnode);
						if(afeparams) {
							name += afeparams;
						}
						name += ')';
						break;
					case 'FunctionExpression' :
						name = 'function(';  //$NON-NLS-0$
						// If the function has a non-empty label use that name
						if (astnode.id && astnode.id.type === 'Identifier' && astnode.id.name){
							name = astnode.id.name + '(';
						}
						var feparams = this.getParamsFrom(astnode);
						if(feparams) {
							name += feparams;
						}
						name += ')';
						break;
					case 'ObjectExpression' :
						name = 'closure ';  //$NON-NLS-0$
						details = this.getPropertyListFrom(astnode);
						break;
					case 'Property' :
						if(astnode.value) {
							if(astnode.value.type === 'FunctionExpression') {
								if(astnode.key) {
									if(astnode.key.name) {
										name = astnode.key.name + '(';
									}
									else if(astnode.key.value) {
										name = astnode.key.value + '(';
									}
								}
								else {
									name = 'function(';  //$NON-NLS-0$
								}
								var pparams = this.getParamsFrom(astnode.value);
								if(pparams) {
									name += pparams;
								}
								name += ')';
							}
							else if(astnode.value.type === 'ObjectExpression') {
								if(astnode.key) {
									if(astnode.key.name) {
										name = astnode.key.name + ' ';  //$NON-NLS-0$
									}
									else if(astnode.key.value) {
										name = astnode.key.value + ' ';  //$NON-NLS-0$
									}
									details = this.getPropertyListFrom(astnode.value);
								}
							}
							else if(astnode.key) {
								if(astnode.key.name) {
									name = astnode.key.name;
								}
								else if(astnode.key.value) {
									name = astnode.key.value;
								}
							}
						}
						break;
					case 'VariableDeclarator' :
						if(astnode.init) {
							if(astnode.init.type === 'ObjectExpression') {
								switch(kind) {
									case 'let' :
										name = 'let ';
										break;
									case 'const' :
										name = 'const ';
										break;
									default:
										name = 'var ';
								}
								if(astnode.id && astnode.id.name) {
									name += astnode.id.name+ ' = ';  //$NON-NLS-1$ //$NON-NLS-2$
									details = this.getPropertyListFrom(astnode.init);
								}
							}
							else if(astnode.init.type === 'FunctionExpression') {
								if(astnode.id && astnode.id.name) {
									name = astnode.id.name + '(';
									var vparams = this.getParamsFrom(astnode.init);
									if(vparams) {
										name += vparams;
									}
									name += ')';
								}
								else {
									name = this.getNameFrom(astnode.init);
								}
							}
						}
						break;
					case 'AssignmentExpression' :
						if(astnode.left && astnode.right) {
							var isobject = astnode.right.type === 'ObjectExpression';
							if(isobject || astnode.right.type === 'FunctionExpression') {
								if(astnode.left.name) {
									name = astnode.left.name;
								}
								else if(astnode.left.type === 'MemberExpression') {
									name = this.expandMemberExpression(astnode.left, '');
								}
								if(name) {
									//append the right stuff
									if(isobject) {
										name += ' ';  //$NON-NLS-0$
										details = this.getPropertyListFrom(astnode.right); 
									}
									else {
										name += '(';
										var aparams = this.getParamsFrom(astnode.right);
										if(aparams) {
											name += aparams;
										}
										name += ')';
									}
								}
								else {
									name = this.getNameFrom(astnode.right);
								}
							}
						}
						break;
					case 'ReturnStatement' :
						if(astnode.argument) {
							if(astnode.argument.type === 'ObjectExpression'
								|| astnode.argument.type === 'FunctionExpression'
								|| astnode.argument.type === 'ArrowFunctionExpression') {
									name = 'return ';  //$NON-NLS-0$
									details = this.getPropertyListFrom(astnode.argument);
							}
						}
						break;
				}
			}
			return {name: name, details: details};
		},
		
		/**
		 * @name expandMemberExpression
		 * @description Given a MemberExpression node this function will recursively compute the complete name from the node
		 * by visiting all of the child MemberExpressions, if any
		 * @function
		 * @private
		 * @memberof javascript.Signatures.prototype
		 * @param {Object} astnode The MemberExpression AST node
		 * @returns {String} The name to use for the node
		 */
		expandMemberExpression: function(astnode, name) {
			if(astnode.type === 'MemberExpression') {
				if(astnode.property) {
				    var propname = astnode.property.name;
				    if(astnode.property.type === 'Literal') {
				        propname = astnode.property.value;
				    }
				    if(propname) {
    					if(name && name.length > 0) {
    						name = propname+'.' + name;
    					}
    					else {
    						name = propname;
    					}
					}
				}
				if(astnode.object && astnode.object.name) {
					name = astnode.object.name +'.'+ name;
				}
				//TODO recursion
				return this.expandMemberExpression(astnode.object, name);
			}
			return name;
		},
		
		/**
		 * @name getSignatureSourceRangeFrom
		 * @description Computes the signature source range (start, end) for the given node 
		 * @function
		 * @ppublic
		 * @memberof javascript.Signatures.prototype
		 * @param {Object} astnode The AST node to compute the range from
		 * @returns {Array} The computed signature source range as an array [start, end] or <code>[-1, -1]</code> if it could not
		 * be computed
		 */
		getSignatureSourceRangeFrom: function(astnode) {
			var range = [0, 0];
			if(astnode) {
				if(astnode.type === 'AssignmentExpression') {
					if(astnode.left && astnode.left.range) {
						range = astnode.left.range;
					}
				}
				else if(astnode.type === 'Property') {
					if(astnode.key && astnode.key.range) {
						range = astnode.key.range;
					}
				}
				else if(astnode.type === 'ReturnStatement') {
					range[0] = astnode.range[0];
					range[1] = range[0] + 6;
				}
				else if(astnode.type === 'ArrowFunctionExpression') {
					range = astnode.range;
				}
				else if(astnode.type === 'ClassExpression'
							|| astnode.type === 'ClassDeclaration') {
					if(astnode.id && astnode.id.range) {
						range = astnode.id.range;
					} else {
						// just highlight 'class' for anynomous class
						range = astnode.range;
						range[1] = range[0] + 5;
					}
				}
				else if(astnode.id && astnode.id.range) {
					range = astnode.id.range;
				}
				else if(astnode.range) {
					range = astnode.range;
					if(astnode.type === 'FunctionExpression') {
						range[1] = range[0]+8;
					}
				}
			}
			return range;
		}
		
	};
	
	return Signatures;
});

/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0
 * (https://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
 * License v1.0 (https://www.eclipse.org/org/documents/edl-v10.html).
 *
 * Contributors:
 *	 IBM Corporation - initial API and implementation
 *******************************************************************************/
/* eslint-env amd */
/* eslint-disable missing-nls */
define('javascript/ruleData',[
	"i18n!javascript/nls/problems"
], function(Messages) {

	var rules = {
		defaults: {
			"accessor-pairs" : 3,
			"check-tern-plugin" : 3,
			"curly" : 0,
			"eqeqeq": 3,
			"missing-doc" : 0,
			"missing-nls" : 0,
			"missing-requirejs": 3,
			"new-parens" : 1,
			"no-caller": 1,
			"no-comma-dangle" : 0,
			"no-cond-assign" : 1,
			"no-console" : 0,
			"no-const-assign" : 1,
			"no-constant-condition" : 1,
			"no-control-regex" : 1,
			"no-debugger" : 3,
			"no-dupe-keys" : 1,
			"no-duplicate-case": 1,
			"no-else-return" : 3,
			"no-empty-block" : 0,
			"no-empty-character-class" : 1,
			"no-empty-label" : 1,
			"no-eq-null" : 1,
			"no-eval" : 0,
			"no-extend-native": [1, {"exceptions" : ""}],
			"no-extra-bind" : 3,
			"no-extra-boolean-cast" : 1,
			"no-extra-parens" : [3, "all", {"conditionalAssign": false, "returnAssign": false, "nestedBinaryExpressions": false}],
			"no-extra-semi": 3,
			"no-fallthrough" : 1,
			"no-implicit-coercion" : [3, { "boolean": true, "number" : true, "string" : true }],
			"no-implied-eval" : 0,
			"no-invalid-regexp": [1, {"allowConstructorFlags" : ""}],
			"no-invalid-this" : 3,
			"no-irregular-whitespace" : [0, {"skipComments": false, "skipRegExps": false, "skipTemplates": false}],
			"no-iterator": 1,
			"no-jslint" : 3,
			"no-lone-blocks" : 0,
			"no-mixed-spaces-and-tabs" : 0,
			"no-native-reassign" : [1, {"exceptions" : ""}],
			"no-negated-in-lhs" : 1,
			"no-new-array": 3,
			"no-new-func" : 3,
			"no-new-object" : 3,
			"no-new-wrappers" : 3,
			"no-obj-calls" : 1,
			"no-param-reassign" : [3, {props: false}],
			"no-proto" : 1,
			"no-redeclare" : 3,
			"no-regex-spaces" : 1,
			"no-reserved-keys" : 0,
			"no-self-assign" : 1,
			"no-self-compare" : 1,
			"no-shadow" : 3,
			"no-shadow-global" : 0,
			"no-sparse-arrays" : 3,
			"no-throw-literal" : 3,
			"no-trailing-spaces" : [0, { "skipBlankLines": true }],
			"no-undef" : 1,
			"no-undef-expression": 3,
			"no-undef-init" : 3,
			"no-unreachable" : 3,
			"no-unused-expressions" : [0, {allowShortCircuit: false, allowTernary: false}],
			"no-unused-params" : 3,
			"no-unused-vars" : 3,
			"no-use-before-define" : 3,
			"no-void" : 0,
			"no-with" : 3,
			"quotes" : [0, "double", {avoidEscape: false, allowTemplateLiterals: false}],
			"radix" : 3,
			"semi" : [3, "always", {omitLastInOneLineBlock: false}],
			"type-checked-consistent-return" : 0,
			"unknown-require": 3,
			"unnecessary-nls" : 0,
			"use-isnan" : 1,
			"valid-typeof" : 1,
			"yoda" : [0, "never", {exceptRange: false, onlyEquality: false}]
		},

		metadata: {
			"accessor-pairs" : {
				description: Messages['accessor-pairs-description'],
				url: 'https://eslint.org/docs/rules/accessor-pairs'
			},
			"check-tern-plugin" : {
				description: Messages['check-tern-plugin-description']
			},
			"curly" : {
				description: Messages['curly-description'],
				url: 'https://eslint.org/docs/rules/curly'
			},
			"eqeqeq": {
				description: Messages['eqeqeq-description'],
				url: "https://eslint.org/docs/rules/eqeqeq"
			},
			"missing-doc" : {
				description: Messages['missing-doc-description'],
				url: 'https://eslint.org/docs/rules/valid-jsdoc'
			},
			"missing-nls" : {
				description: Messages['missing-nls-description']
			},
			"missing-requirejs" : {
				description: Messages['missing-requirejs-description']
			},
			"new-parens" : {
				description: Messages['new-parens-description'],
				url: 'https://eslint.org/docs/rules/new-parens'
			},
			"no-caller": {
				description: Messages['no-caller-description'],
				url: 'https://eslint.org/docs/rules/no-caller'
			},
			"no-comma-dangle" : {
				description: Messages['no-comma-dangle-description'],
				url: 'https://eslint.org/docs/rules/no-comma-dangle'
			},
			"no-cond-assign" : {
				description: Messages['no-cond-assign-description'],
				url: 'https://eslint.org/docs/rules/no-cond-assign'
			},
			"no-console" : {
				description: Messages['no-console-description'],
				url: 'https://eslint.org/docs/rules/no-console'
			},
			"no-constant-condition" : {
				description: Messages['no-constant-condition-description'],
				url: 'https://eslint.org/docs/rules/no-constant-condition'
			},
			"no-const-assign" : {
				description: Messages['no-const-assign-description'],
				url: 'https://eslint.org/docs/rules/no-const-assign'
			},
			"no-control-regex" : {
				description: Messages['no-control-regex-description'],
				url: 'https://eslint.org/docs/rules/no-control-regex'
			},
			"no-debugger" : {
				description: Messages['no-debugger-description'],
				url: 'https://eslint.org/docs/rules/no-debugger'
			},
			"no-dupe-keys" : {
				description: Messages['no-dupe-keys-description'],
				url: 'https://eslint.org/docs/rules/no-dupe-keys'
			},
			"no-duplicate-case": {
				description: Messages['no-duplicate-case-description'],
				url: 'https://eslint.org/docs/rules/no-duplicate-case'
			},
			"no-else-return" : {
				description: Messages['no-else-return-description'],
				url: 'https://eslint.org/docs/rules/no-else-return'
			},
			"no-empty-block" : {
				description: Messages['no-empty-block-description'],
				url: 'https://eslint.org/docs/rules/no-empty'
			},
			"no-empty-character-class" : {
				description: Messages['no-empty-character-class-description'],
				url: 'https://eslint.org/docs/rules/no-empty-character-class'
			},
			"no-empty-label" : {
				description: Messages['no-empty-label-description'],
				url: 'https://eslint.org/docs/rules/no-empty-label'
			},
			"no-eq-null" : {
				description: Messages['no-eq-null-description'],
				url: 'https://eslint.org/docs/rules/no-eq-null'
			},
			"no-eval" : {
				description: Messages['no-eval-description'],
				url: 'https://eslint.org/docs/rules/no-eval'
			},
			"no-extend-native" : {
				description: Messages['no-extend-native-description'],
				url: 'https://eslint.org/docs/rules/no-extend-native'
			},
			"no-extra-bind" : {
				description: Messages['no-extra-bind-description'],
				url: 'https://eslint.org/docs/rules/no-extra-bind'
			},
			"no-extra-boolean-cast" : {
				description: Messages['no-extra-boolean-cast-description'],
				url: 'https://eslint.org/docs/rules/no-extra-boolean-cast'
			},
			"no-extra-parens" : {
				description: Messages['no-extra-parens-description'],
				url: 'https://eslint.org/docs/rules/no-extra-parens'
			},
			"no-extra-semi": {
				description: Messages['no-extra-semi-description'],
				url: 'https://eslint.org/docs/rules/no-extra-semi'
			},
			"no-fallthrough" : {
				description: Messages['no-fallthrough-description'],
				url: 'https://eslint.org/docs/rules/no-fallthrough'
			},
			"no-implicit-coercion" : {
				description: Messages['no-implicit-coercion-description'],
				url: 'https://eslint.org/docs/rules/no-implicit-coercion'
			},
			"no-implied-eval" : {
				description: Messages['no-implied-eval-description'],
				url: 'https://eslint.org/docs/rules/no-implied-eval'
			},
			"no-invalid-regexp": {
				description: Messages['no-invalid-regexp-description'],
				url: 'https://eslint.org/docs/rules/no-invalid-regexp'
			},
			"no-invalid-this": {
				description: Messages['no-invalid-this-description'],
				url: 'https://eslint.org/docs/rules/no-invalid-this'
			},
			"no-irregular-whitespace" : {
				description: Messages['no-irregular-whitespace-description'],
				url: 'https://eslint.org/docs/rules/no-irregular-whitespace'
			},
			"no-iterator": {
				description: Messages['no-iterator-description'],
				url: 'https://eslint.org/docs/rules/no-iterator'
			},
			"no-jslint" : {
				description: Messages['no-jslint-description']
			},
			"no-native-reassign" : {
				description: Messages['no-native-reassign-description'],
				url: 'https://eslint.org/docs/rules/no-native-reassign'
			},
			"no-lone-blocks" : {
				description: Messages['no-lone-blocks-description'],
				url: 'https://eslint.org/docs/rules/no-lone-blocks'
			},
			"no-mixed-spaces-and-tabs" : {
				description: Messages['no-mixed-spaces-and-tabs-description'],
				url: 'https://eslint.org/docs/rules/no-mixed-spaces-and-tabs'
			},
			"no-negated-in-lhs" : {
				description: Messages['no-negated-in-lhs-description'],
				url: 'https://eslint.org/docs/rules/no-negated-in-lhs'
			},
			"no-new-array": {
				description: Messages['no-new-array-description'],
				url: 'https://eslint.org/docs/rules/no-array-constructor'
			},
			"no-new-func" : {
				description: Messages['no-new-func-description'],
				url: 'https://eslint.org/docs/rules/no-new-func'
			},
			"no-new-object" : {
				description: Messages['no-new-object-description'],
				url: 'https://eslint.org/docs/rules/no-new-object'
			},
			"no-new-wrappers" : {
				description: Messages['no-new-wrappers-description'],
				url: 'https://eslint.org/docs/rules/no-new-wrappers'
			},
			"no-obj-calls" : {
				description: Messages['no-obj-calls-description'],
				url: 'https://eslint.org/docs/rules/no-obj-calls'
			},
			"no-param-reassign" : {
				description: Messages['no-param-reassign-description'],
				url: 'https://eslint.org/docs/rules/no-param-reassign'
			},
			"no-proto" : {
				description: Messages['no-proto-description'],
				url: 'https://eslint.org/docs/rules/no-proto'
			},
			"no-redeclare" : {
				description: Messages['no-redeclare-description'],
				url: 'https://eslint.org/docs/rules/no-redeclare'
			},
			"no-regex-spaces" : {
				description: Messages['no-regex-spaces-description'],
				url: 'https://eslint.org/docs/rules/no-regex-spaces'
			},
			"no-reserved-keys" : {
				description: Messages['no-reserved-keys-description'],
				url: 'https://eslint.org/docs/rules/no-reserved-keys'
			},
			"no-self-compare" : {
				description: Messages['no-self-compare-description'],
				url: 'https://eslint.org/docs/rules/no-self-compare'
			},
			"no-self-assign" : {
				description: Messages['no-self-assign-description'],
				url: 'https://eslint.org/docs/rules/no-self-assign'
			},
			"no-shadow" : {
				description: Messages['no-shadow-description'],
				url: 'https://eslint.org/docs/rules/no-shadow'
			},
			"no-shadow-global" : {
				description: Messages['no-shadow-global-description']
			},
			"no-sparse-arrays" : {
				description: Messages['no-sparse-arrays-description'],
				url: 'https://eslint.org/docs/rules/no-sparse-arrays'
			},
			"no-throw-literal" : {
				description: Messages['no-throw-literal-description'],
				url: 'https://eslint.org/docs/rules/no-throw-literal'
			},
			"no-trailing-spaces" : {
				description: Messages['no-trailing-spaces-description'],
				url: 'https://eslint.org/docs/rules/no-trailing-spaces'
			},
			"no-undef" : {
				description: Messages['no-undef-description'],
				url: 'https://eslint.org/docs/rules/no-undef'
			},
			"no-undef-expression" : {
				description: Messages['no-undef-expression-description']
			},
			"no-undef-init" : {
				description: Messages['no-undef-init-description'],
				url: 'https://eslint.org/docs/rules/no-undef-init'
			},
			"no-unreachable" : {
				description: Messages['no-unreachable-description'],
				url: 'https://eslint.org/docs/rules/no-unreachable'
			},
			"no-unused-expressions" : {
				description: Messages['no-unused-expressions-description'],
				url: 'https://eslint.org/docs/rules/no-unused-expressions'
			},
			"no-unused-params" : {
				description: Messages['no-unused-params-description']
			},
			"no-unused-vars" : {
				description: Messages['no-unused-vars-description'],
				url: 'https://eslint.org/docs/rules/no-unused-vars'
			},
			"no-use-before-define" : {
				description: Messages['no-use-before-define-description'],
				url: 'https://eslint.org/docs/rules/no-use-before-define'
			},
			"no-void" : {
				description: Messages['no-void-description'],
				url: 'https://eslint.org/docs/rules/no-void'
			},
			"no-with" : {
				description: Messages['no-with-description'],
				url: 'https://eslint.org/docs/rules/no-with'
			},
			"quotes" : {
				description: Messages['quotes-description'],
				url: 'https://eslint.org/docs/rules/quotes'
			},
			"radix" : {
				description: Messages['radix-description'],
				url: 'https://eslint.org/docs/rules/radix'
			},
			"semi" : {
				description: Messages['semi-description'],
				url: 'https://eslint.org/docs/rules/semi'
			},
			"unknown-require" : {
				description: Messages['unknown-require-description']
			},
			"unnecessary-nls" : {
				description: Messages['unnecessary-nls-description']
			},
			"use-isnan" : {
				description: Messages['use-isnan-description'],
				url: 'https://eslint.org/docs/rules/use-isnan'
			},
			"valid-typeof" : {
				description: Messages['valid-typeof-description'],
				url: 'https://eslint.org/docs/rules/valid-typeof'
			},
			"type-checked-consistent-return" : {
				description: Messages['type-checked-consistent-return-description']
			},
			"yoda" : {
				description: Messages['yoda-description'],
				url: 'https://eslint.org/docs/rules/yoda'
			}
		}
	};
	return Object.freeze(rules);
});

/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (https://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (https://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - Allow original requirejs plugin to find files in Orion workspace
 *******************************************************************************/
/* eslint-disable missing-nls */
/*eslint-env node, amd*/
/*globals tern tern */
define('javascript/ternPlugins/jsdoc',[
	"tern/lib/tern",
	"orion/objects",
	"javascript/finder",
	"javascript/signatures",
	"javascript/util",
	"javascript/ruleData",
	"eslint/conf/environments",
	"i18n!javascript/nls/messages"
], function(tern, objects, Finder, Signatures, Util, Rules, ESLintEnvs, Messages) {
	
	var allEnvs = {};
	var envNames = {
		browser: Messages['browser'],
		node: Messages['node'],
		commonjs: Messages['commonjs'],
		worker: Messages['worker'],
		amd: Messages['amd'],
		mocha: Messages['mocha'],
		jasmine: Messages['jasmine'],
		jest: Messages['jest'],
		phantomjs: Messages['phantomjs'],
		protractor: Messages['protractor'],
		qunit: Messages['qunit'],
		jquery: Messages['jquery'],
		prototypejs: Messages['prototypejs'],
		shelljs: Messages['shelljs'],
		meteor: Messages['meteor'],
		mongo: Messages['mongo'],
		applescript: Messages['applescript'],
		nashorn: Messages['nashorn'],
		serviceworker: Messages['serviceworker'],
		embertest: Messages['embertest'],
		webextensions: Messages['webextension'],
		es6: Messages['es6']
	};
	
	tern.registerPlugin("jsdoc", /* @callback */ function(server, options) {
		return {
     		passes: {
		      	/**
		      	 * @callback
		      	 */
		      	completion: function(file, options, expr, type) {
		      		var comment = Finder.findComment(options.end, file.ast);
		      		if(comment) {
		      			objects.mixin(allEnvs, ESLintEnvs);
		      			fetchPluginEnvs(server);
		      			if(comment.type === 'Line') {
		      				return findLineCompletions(comment, options, file);
		      			}
		      			return findBlockCompletions(comment, options, file);
		      		}
		      		return null;
		      	}
      		}
    	};
	});
	
	function fetchPluginEnvs(server) {
		if(server.options && server.options.plugins) {
			var keys = Object.keys(server.options.plugins);
			for(var i = 0, len = keys.length; i < len; i++) {
				var env = server.options.plugins[keys[i]].env;
				if(env) {
					allEnvs[env] = true;
				}			
			}
		}
	}
	
	/**
	 * @description Finds completions for line comments: //-prefixed
	 * @param {Object} comment The comment AST node
	 * @param {Object} options The options object
	 * @param {File} file The Tern file object
	 * @returns {Array.<Object>} The array of completion objects
	 * @since 11.0
	 */
	function findLineCompletions(comment, options, file) {
		var proposals = [];
		var prefix = getPrefix(options.end, file.text), char = file.text.charAt(options.end-prefix.length-1);
		if(char === '{') {
			proposals = computeTypeCompletions(prefix, comment, file);
		}
		return {completions: proposals};

	}
	
	/**
	 * @description Finds the completions for block comments: /**- or /*-prefixed 
	 * @param {Object} comment The comment AST node
	 * @param {Object} options The options object
	 * @param {File} file The Tern file object
	 * @returns {Array.<Object>} The array of completion objects
	 * @since 11.0
	 */
	function findBlockCompletions(comment, options, file) {
		var proposals = [];
		var prefix = getPrefix(options.end, file.text), char = file.text.charAt(options.end-prefix.length-1);
		if(char === '{') {
			// TODO When to use @link vs {}
			proposals = computeTypeCompletions(prefix, comment, file);
		} else if(char === '.') {
			proposals = computeMemberCompletions(prefix, comment, options);
		} else if(char === '*' || char === ' ') {
			proposals = computeBlockCompletions(prefix, comment, options, file);
		}
		return {completions: proposals};
	}
	
	/**
     * @description Get the prefix to use for the proposal, handles @-based prefixes
     * @param {Number} pos The activation context
     * @param {String} buffer The text
     * @returns {String} The prefix to use
     */
    function getPrefix(pos, buffer) {
        var index = pos-1;
        var word = '', char = buffer.charAt(index);
        if('{*,'.indexOf(char) > -1) {
            return word;
        }
        if(char === '@') {
            return char;
        } else if(char === '/' && buffer.charAt(index-1) === '/') {
        	return word;
    	}
        while(index >= 0 && /\S/.test(char)) {
            word = char+word;
            if(char === '@' || char === '*' || char === '{') {
                //we want the prefix to include the '@'
	            return word;
	        } else if(char === '/' && buffer.charAt(index-1) === '/') {
	        	return word;
	        }
            index--;
            char = buffer.charAt(index);
            if('{*,'.indexOf(char) > -1) {
                // we don't want the prefix to include the '*'
                return word;
            }
        }
        return word;
    }
	/**
	 * @description Computes the reachable type names from the given scope
	 * @param {String} prefix The prefix
	 * @returns {Array.<Object>} The array of completion objects
	 */
	function computeTypeCompletions(prefix, comment, file) {
		var theType, proposal, key;
		var proposals = [];
		
		var defaultEnvs = {ecma5: true, ecma6: true, ecma7: true};
		if (isHTML(file.name)){
			defaultEnvs.browser = true;
		}
		var envs = getActiveEnvironments(file.ast, defaultEnvs);
		var existingCompletions = Object.create(null); // Tern separates proto and non-proto props into two entries, we only want to see one

		var node = Finder.findNodeAfterComment(comment, file.ast);
		var scope = file.scope;
		if (node){
			if (node.scope){
				scope = node.scope;
			} else if (node.parents) {
				for (var i=node.parents.length-1; i>=0; i--) {
					if (node.parents[i] && node.parents[i].scope){
						scope = node.parents[i].scope;
						break;
					}
				}
			}
		}
		while (scope){
			for (key in scope.props){
				theType = scope.props[key];
				// TODO How to handle union types?
				if (theType && theType.types && theType.types.length > 0){
					proposal = getProposalForType(theType.types[0], envs, existingCompletions, prefix);
					if (proposal){
						proposals.push(proposal);
					}
				}
	
			}
			scope = scope.prev;
		}
		
		// Add in additional types that Tern can understand
		var _p = createProposal('{}', Messages['jsDocEmptyObjDesc'], prefix);
		_p.origin = "ecma5";
		_p.doc = Messages['jsDocEmptyObjDoc'];
		proposals.push(_p);
		_p = createProposal('{prop: propType}', Messages['jsDocObjPropDesc'], prefix);
		_p.origin = "ecma5";
		_p.doc = Messages['jsDocObjPropDoc'];
		proposals.push(_p);
		_p = createProposal('?', Messages['jsDocAnyTypeDesc'], prefix);
		_p.origin = "ecma5";
		_p.doc = Messages['jsDocAnyTypeDoc'];
		proposals.push(_p);
		
		return proposals;
	}
	
	/**
	 * Return whether the given file name is an html file
	 * @param name file name to look at
	 * @returns returns whether the file name is html
	 */
	function isHTML(name) {
		return /(?:html|htm|xhtml)$/g.test(name);
	}
	
	/**
	 * @name getProposalForType
	 * @description Returns a completion proposal for the given type object or <code>null<code> if no proposal should be added
	 * @param theType {Object} type to make a proposal for
	 * @param envs {Object} list of active environments
	 * @param existingCompletions {Object} list of existing completion names
	 * @param prefix {String} preceding characters to the completion
	 * @returns returns a proposal or null
	 */
	function getProposalForType(theType, envs, existingCompletions, prefix){
		if (theType && typeof theType === 'object' && theType.name && theType.origin){
			if (envs[theType.origin] || theType.origin.indexOf('/') >= 0){
				var name = theType.name;
				
				// Top level scope can't be a return type in Browser or Node
				if (name === '<top>'){
					return null;
				}
				// We only want type completions not functions returning primitives, but include the actual primitive types
				var prims = ['bool', 'boolean', 'string', 'number', 'regexp'];
				if (theType.retval && prims.indexOf(theType.retval.name) >= 0 && prims.indexOf(theType.name.toLowerCase()) < 0){
					return null;
				}
				// Different scopes may have same properties
				if (existingCompletions[name]){
					return null;
				}
				
				existingCompletions[name] = true;
				var _p = createProposal(name, '', prefix);
				_p.origin = theType.origin;
				_p.doc = theType.doc;
				_p.url = theType.url;
				return _p;
			}
		}
		return null;
	}
	
	/**
	 * @name getActiveEnvironments
	 * @description Return an {Object} containing all active environments in the given ast based on eslint env and global settings
	 * @param ast File AST to get environments for
	 * @param defenvs {Object} containing environments that should be included in the active list by default
	 * @returns returns {Object} will all active environments set to <code>true</code>
	 */
	function getActiveEnvironments(ast, defenvs) {
		var env = Object.create(null);
		objects.mixin(env, defenvs);
		if(ast.comments) {
			for(var i = 0; i < ast.comments.length; i++) {
				var comment = ast.comments[i];
				if (comment.type === "Block") {
		            var value = comment.value.trim();
		            var match = /^(eslint-\w+|eslint|globals?)(\s|$)/.exec(value);
					if (match) {
		                value = value.substring(match.index + match[1].length);
		                if(match[1] === 'eslint-env') {
		                	// Collapse whitespace around ,
						    var string = value.replace(/\s*,\s*/g, ",");
						    string.split(/,+/).forEach(function(_name) {
						        _name = _name.trim();
						        if (!_name) {
						            return;
						        }
						        env[_name] = true;
						    });
		                }
		            }
		        }
			}
		}
	    return env;
	}
	
	/**
	 * @description Computes the reachable type member names for completions
	 * @param {String} prefix The prefix
	 * @returns {Array.<Object>} The array of completion objects
	 */
	function computeMemberCompletions(prefix) {
		//TODO
		return [];
	}
	/**
	 * @description Computes the proposals for the block
	 * @param {String} prefix The prefix
	 * @param {Object} comment The AST node for the comment
	 * @param {Object} options The activation options
	 * @param {File} file The mapped file from Tern
	 * @returns {Array.<Object>} The array of completion objects
	 */
	function computeBlockCompletions(prefix, comment, options, file) {
		var proposals = [];
		var keys, val, _p;
		var line = getLine(options.end, file);
		var preamble = line.line.slice(0, options.end-line.start-prefix.length-1);
		
		if(/^\/\*$/.test(preamble.trim())) {
			// Provide a eslint-env template with set list of environments for the user to choose from
			if (Util.looselyMatches(prefix, "eslint-env")){
				var envsListTemplate = getEnvsListForTemplate();
				var template = "eslint-env ${library:" + envsListTemplate +"}";
				_p = createProposal("eslint-env", "", prefix, template);
				_p.doc = Messages['eslintEnvDirective'];
				_p.url = "https://eslint.org/docs/user-guide/configuring.html#specifying-environments";
				proposals.push(_p);
			}
			keys = Object.keys(block);
			for(var len = keys.length, i = 0; i < len; i++) {
				var tag = block[keys[i]];
				if(Util.looselyMatches(prefix, tag.name)) {
					_p = createProposal(tag.name, '', prefix, tag.template);
					_p.url = tag.url;
					_p.doc = tag.desc;
					proposals.push(_p);
				}
			}
		} else if((val = /\s*\*\s*\@name\s*(\w*)/ig.exec(line.line)) !== null) {
			if(val[1] === prefix) {
				var node = Finder.findNodeAfterComment(comment, file.ast);
				var _name = getFunctionName(node);
				if(_name) {
					proposals.push(createProposal(_name, Messages['funcProposalDescription'], prefix));
				}
			}
		} else if((val = /\s*\*\s*\@param\s*(?:\{.*\})?\s*(\w*)/ig.exec(line.line)) !== null) {
			if(val[1] === prefix) {
				node = Finder.findNodeAfterComment(comment, file.ast);
				if(node) {
					var prms = getFunctionParams(node);
					if(Array.isArray(prms)) {
						for(i = 0; i < prms.length; i++) {
							_name = prms[i].name;
							if(Util.looselyMatches(prefix, _name)) {
								proposals.push(createProposal(_name, Messages['funcParamProposalDescription'], prefix));
							}
						}
					}
				}
			}
		} else if(/^\s*(?:\/\*)?\s*eslint(?:-enable|-disable)?\s+/gi.test(line.line)) {
			//eslint eslint-enable eslint-disable
			var rules = Rules.metadata;
			var rulekeys = Object.keys(rules).sort();
			for(i = 0; i < rulekeys.length; i++) {
				var rulekey = rulekeys[i];
				if(Util.looselyMatches(prefix, rulekey)) {
					var rule = rules[rulekey];
					_p = createProposal(rulekey, '', prefix);
					_p.doc = rule.description ? rule.description : Messages['eslintRuleProposalDescripton'];
					if(rule.url) {
						_p.url = rule.url;
					}
					proposals.push(_p);
				}
			}
		} else if(/^\s*(?:\/\*)?\s*eslint-env\s+/gi.test(line.line)) {
			//eslint-env (comma-separated list)
			keys = Object.keys(allEnvs).sort();
			for(i = 0; i < keys.length; i++) {
				var key = keys[i];
				if(key !== 'builtin' && Util.looselyMatches(prefix, key)) {
					_p = createProposal(key, "", prefix);
					_p.doc = envNames[key];
					if(!_p.doc) {
						_p.doc = Messages['eslintEnvProposalDescription'];
					}
					_p.url = "https://eslint.org/docs/user-guide/configuring.html#specifying-environments";
					proposals.push(_p);
				}
			}
		} else {
				keys = Object.keys(tags);
				for(len = keys.length, i = 0; i < len; i++) {
					tag = tags[keys[i]];
					if(Util.looselyMatches(prefix, tag.name)) {
						if(tag.template) {
							_p = createProposal(tag.name, "", prefix, tag.template);
						} else {
							_p = createProposal(tag.name, "", prefix);
						}
						_p.url = tag.url;
						_p.doc = tag.desc;
						proposals.push(_p);
					}
				}
		}
		return proposals;
	}
	
	/**
	 * @description Finds the line of text the completion is happening on - this mimics 
	 * an option we used to get from the client
	 * @param {Number} offset The offset in text the completion is happening at
	 * @param {File} file The file obejct from Tern
	 * @returns {String} The line of text
	 * @since 11.0
	 */
	function getLine(offset, file) {
		var line = '', idx = offset;
		if(idx > 0 && idx <= file.text.length) {
			var c = file.text.charAt(idx);
			if(c === '\n') {
				//started at the end, walk back to the next end
				idx--;
				c = file.text.charAt(idx);
			}
			while(c !== '\n' && idx > -1) {
				line = c+line;
				idx--;
				c = file.text.charAt(idx);
			}
		}
		return {line: line, start: idx};
	}
	
	/**
	 * @description Returns the function name from the given node if it relates to a function in some way
	 * @param {Object} node The AST node
	 * @returns {String} The name of he related function or null
	 * @since 10.0
	 */
	function getFunctionName(node) {
		switch(node.type) {
			case 'FunctionDeclaration': {
				return node.id.name;
			}
			case 'Property': {
				if(node.value.type === 'FunctionExpression') {
					return node.value.id ? node.value.id.name : node.key.name;
				}
				break;
			}
			case 'ExpressionStatement': {
				var _n = node.expression;
				if(_n && _n.type === 'AssignmentExpression' && _n.right.type === 'FunctionExpression') {
					if(_n.right.id) {
						return _n.right.id.name;
					}
					if(_n.left.type === 'Identifier') {
						return _n.left.name;
					}
					if(_n.left.type === 'MemberExpression') {
						return Signatures.expandMemberExpression(_n.left, '');
					}
				}
				break;
			}
			case 'VariableDeclaration': {
				if(node.declarations.length > 0) {
					//always pick the first one to attach the comment to
					var decl = node.declarations[0];
					if(decl.init && decl.init.type === 'FunctionExpression') {
						if(decl.init.id) {
							return decl.init.id.name;
						}
						return decl.id.name;
					}
				}
				break;
			}
		}
		return null;
	}

	/**
	 * @description Returns the parameters from the related function
	 * @param {Object} node The AST node
	 * @returns {Array.<Object>} The parameters from the related function or an empty array
	 * @since 10.0
	 */
	function getFunctionParams(node) {
		switch(node.type) {
			case 'FunctionDeclaration': {
				return node.params;
			}
			case 'Property': {
				if(node.value.type === 'FunctionExpression') {
					return node.value.params;
				}
				break;
			}
			case 'ExpressionStatement': {
				var _n = node.expression;
				if(_n && _n.type === 'AssignmentExpression' && _n.right.type === 'FunctionExpression') {
					return _n.right.params;
				}
				break;
			}
			case 'VariableDeclaration': {
				if(node.declarations.length > 0) {
					//always pick the first one to attach the comment to
					var decl = node.declarations[0];
					if(decl.init && decl.init.type === 'FunctionExpression') {
						return decl.init.params;
					}
				}
				break;
			}
		}
		return [];
	}
	
	/**
	 * Takes the allEnvs object, extracts the envs list and formats it into a JSON string that the template
	 * computer will accept.
	 * @returns {String} A string list of eslint environment directives that the template computer will accept
	 */
	function getEnvsListForTemplate(){
		var envsList = [];
		var keys = Object.keys(allEnvs).sort();
		for(var j = 0; j < keys.length; j++) {
			var key = keys[j];
			if(key !== 'builtin'){
				envsList.push(key);
			}
		}
		var templateList = {
			type: "link", //$NON-NLS-0$
			values: envsList,
			title: 'ESLint Environments',
			style: 'no_emphasis' //$NON-NLS-1$
		};
		return JSON.stringify(templateList).replace("}", "\\}");
	}
	
	/**
	 * @description Helper function to create a new proposal object
	 * @param {String} name The name of the proposal
	 * @param {String} description The description
	 * @param {String} prefix The optional prefix to pass along
	 * @param {String} template The optional code template for the proposal
	 * @returns {Object} A new proposal object
	 */
	function createProposal(name, description, prefix, template) {
		var p = Object.create(null);
		if(typeof template === 'string') {
			p.type = 'jsdoc_template';
			p.template = template;
		} else {
			p.type = 'doc';
		}
		p.name = name;
		p.proposal = name.slice(prefix.length);
		p.description = description;
		p.overwrite = true;
		if(typeof prefix === 'string') {
			p.prefix = prefix;
		}
		return p;
	}
	
	/**
	 * These are templates / entries that apply to blocks starting with /* vs. /**
	 */
	var block = {
		"eslint": {
			name: "eslint",  //$NON-NLS-0$
			desc: Messages['eslintRuleEnableDisable'],
			template: "eslint ${rule-id}:${0/1} ${cursor}", //$NON-NLS-0$  
			url: "https://eslint.org/docs/user-guide/configuring.html#configuring-rules"
	    },
	    "eslint-enable": {
			name: "eslint-enable",  //$NON-NLS-0$
			desc: Messages['eslintRuleEnable'],
			template: "eslint-enable ${rule-id} ${cursor}", //$NON-NLS-0$  
			url: "https://eslint.org/docs/user-guide/configuring.html#configuring-rules"
	    },
	    "eslint-disable": {
			name: "eslint-disable",  //$NON-NLS-0$
			desc: Messages['eslintRuleDisable'],
			template: "eslint-disable ${rule-id} ${cursor}", //$NON-NLS-0$
			url: "https://eslint.org/docs/user-guide/configuring.html#configuring-rules"
	    }
	};
	
	var tags = {
		"abstract": {
			"name": "@abstract",
			"url": "https://usejsdoc.org/tags-abstract.html",
			"desc": "This member must be implemented (or overridden) by the inheritor."
		},
		"access": {
			"name": "@access",
			"url": "https://usejsdoc.org/tags-access.html",
			"desc": "Specify the access level of this member (private, public, or protected)."
		},
		"alias": {
			"name": "@alias",
			"url": "https://usejsdoc.org/tags-alias.html",
			"desc": "Treat a member as if it had a different name."
		},
		"augments": {
			"name": "@augments",
			"url": "https://usejsdoc.org/tags-augments.html",
			"desc": "Indicate that a symbol inherits from, ands adds to, a parent symbol."
		},
		"author": {
			"name": "@author",
			"url": "https://usejsdoc.org/tags-author.html",
			"desc": "Identify the author of an item.",
			"template": "@author ${cursor}"
		},
		"borrows": {
			"name": "@borrows",
			"url": "https://usejsdoc.org/tags-borrows.html",
			"desc": "This object uses something from another object."
		},
		"callback": {
			"name": "@callback",
			"url": "https://usejsdoc.org/tags-callback.html",
			"desc": "Document a callback function.",
			"template": "@callback ${cursor}"
		},
		"class": {
			"name": "@class",
			"url": "https://usejsdoc.org/tags-class.html",
			"desc": "This function is intended to be called with the \"new\" keyword.",
			"template": "@class ${cursor}"
		},
		"classdesc": {
			"name": "@classdesc",
			"url": "https://usejsdoc.org/tags-classdesc.html",
			"desc": "Use the following text to describe the entire class."
		},
		"constant": {
			"name": "@constant",
			"url": "https://usejsdoc.org/tags-constant.html",
			"desc": "Document an object as a constant."
		},
		"constructs": {
			"name": "@constructs",
			"url": "https://usejsdoc.org/tags-constructs.html",
			"desc": "This function member will be the constructor for the previous class."
		},
		"copyright": {
			"name": "@copyright",
			"url": "https://usejsdoc.org/tags-copyright.html",
			"desc": "Document some copyright information."
		},
		"default": {
			"name": "@default",
			"url": "https://usejsdoc.org/tags-default.html",
			"desc": "Document the default value."
		},
		"deprecated": {
			"name": "@deprecated",
			"url": "https://usejsdoc.org/tags-deprecated.html",
			"desc": "Document that this is no longer the preferred way.",
			"template": "@deprecated ${cursor}"
		},
		"description": {
			"name": "@description",
			"url": "https://usejsdoc.org/tags-description.html",
			"desc": "Describe a symbol.",
			"template": "@description ${cursor}"
		},
		"enum": {
			"name": "@enum",
			"url": "https://usejsdoc.org/tags-enum.html",
			"desc": "Document a collection of related properties."
		},
		"event": {
			"name": "@event",
			"url": "https://usejsdoc.org/tags-event.html",
			"desc": "Document an event."
		},
		"example": {
			"name": "@example",
			"url": "https://usejsdoc.org/tags-example.html",
			"desc": "Provide an example of how to use a documented item."
		},
		"exports": {
			"name": "@exports",
			"url": "https://usejsdoc.org/tags-exports.html",
			"desc": "Identify the member that is exported by a JavaScript module."
		},
		"external": {
			"name": "@external",
			"url": "https://usejsdoc.org/tags-external.html",
			"desc": "Identifies an external class, namespace, or module."
		},
		"file": {
			"name": "@file",
			"url": "https://usejsdoc.org/tags-file.html",
			"desc": "Describe a file."
		},
		"fires": {
			"name": "@fires",
			"url": "https://usejsdoc.org/tags-fires.html",
			"desc": "Describe the events this method may fire."
		},
		"function": {
			"name": "@function",
			"url": "https://usejsdoc.org/tags-function.html",
			"desc": "Describe a function or method.",
			"template": "@function ${cursor}"
		},
		"global": {
			"name": "@global",
			"url": "https://usejsdoc.org/tags-global.html",
			"desc": "Document a global object."
		},
		"ignore": {
			"name": "@ignore",
			"url": "https://usejsdoc.org/tags-ignore.html",
			"desc": "Omit a symbol from the documentation."
		},
		"implements": {
			"name": "@implements",
			"url": "https://usejsdoc.org/tags-implements.html",
			"desc": "This symbol implements an interface."
		},
		"inheritdoc": {
			"name": "@inheritdoc",
			"url": "https://usejsdoc.org/tags-inheritdoc.html",
			"desc": "Indicate that a symbol should inherit its parent's documentation."
		},
		"inner": {
			"name": "@inner",
			"url": "https://usejsdoc.org/tags-inner.html",
			"desc": "Document an inner object."
		},
		"instance": {
			"name": "@instance",
			"url": "https://usejsdoc.org/tags-instance.html",
			"desc": "Document an instance member."
		},
		"interface": {
			"name": "@interface",
			"url": "https://usejsdoc.org/tags-interface.html",
			"desc": "This symbol is an interface that others can implement."
		},
		"kind": {
			"name": "@kind",
			"url": "https://usejsdoc.org/tags-kind.html",
			"desc": "What kind of symbol is this?"
		},
		"lends": {
			"name": "@lends",
			"url": "https://usejsdoc.org/tags-lends.html",
			"desc": "Document properties on an object literal as if they belonged to a symbol with a given name.",
			"template": "@lends ${cursor}"
		},
		"license": {
			"name": "@license",
			"url": "https://usejsdoc.org/tags-license.html",
			"desc": "Identify the license that applies to this code.",
			"template": "@license ${cursor}"
		},
		"listens": {
			"name": "@listens",
			"url": "https://usejsdoc.org/tags-listens.html",
			"desc": "List the events that a symbol listens for."
		},
		"member": {
			"name": "@member",
			"url": "https://usejsdoc.org/tags-member.html",
			"desc": "Document a member."
		},
		"memberof": {
			"name": "@memberof",
			"url": "https://usejsdoc.org/tags-memberof.html",
			"desc": "This symbol belongs to a parent symbol."
		},
		"mixes": {
			"name": "@mixes",
			"url": "https://usejsdoc.org/tags-mixes.html",
			"desc": "This object mixes in all the members from another object."
		},
		"mixin": {
			"name": "@mixin",
			"url": "https://usejsdoc.org/tags-mixin.html",
			"desc": "Document a mixin object."
		},
		"module": {
			"name": "@module",
			"url": "https://usejsdoc.org/tags-module.html",
			"desc": "Document a JavaScript module."
		},
		"name": {
			"name": "@name",
			"url": "https://usejsdoc.org/tags-name.html",
			"desc": "Document the name of an object.",
			"template": "@name ${cursor}"
		},
		"namespace": {
			"name": "@namespace",
			"url": "https://usejsdoc.org/tags-namespace.html",
			"desc": "Document a namespace object."
		},
		"override": {
			"name": "@override",
			"url": "https://usejsdoc.org/tags-override.html",
			"desc": "Indicate that a symbol overrides its parent."
		},
		"param": {
			"name": "@param",
			"url": "https://usejsdoc.org/tags-param.html",
			"desc": "Document the parameter to a function.",
			"template": "@param {${type}} ${cursor}"
		},
		"private": {
			"name": "@private",
			"url": "https://usejsdoc.org/tags-private.html",
			"desc": "This symbol is meant to be private.",
			"template": "@private ${cursor}"
		},
		"property": {
			"name": "@property",
			"url": "https://usejsdoc.org/tags-property.html",
			"desc": "Document a property of an object."
		},
		"protected": {
			"name": "@protected",
			"url": "https://usejsdoc.org/tags-protected.html",
			"desc": "This symbol is meant to be protected."
		},
		"public": {
			"name": "@public",
			"url": "https://usejsdoc.org/tags-public.html",
			"desc": "This symbol is meant to be public.",
			"template": "@public ${cursor}"
		},
		"readonly": {
			"name": "@readonly",
			"url": "https://usejsdoc.org/tags-readonly.html",
			"desc": "This symbol is meant to be read-only."
		},
		"requires": {
			"name": "@requires",
			"url": "https://usejsdoc.org/tags-requires.html",
			"desc": "This file requires a JavaScript module."
		},
		"returns": {
			"name": "@returns",
			"url": "https://usejsdoc.org/tags-returns.html",
			"desc": "Document the return value of a function.",
			"template": "@returns {${type}} ${cursor}"
		},
		"see": {
			"name": "@see",
			"url": "https://usejsdoc.org/tags-see.html",
			"desc": "Refer to some other documentation for more information.",
			"template": "@see ${cursor}"
		},
		"since": {
			"name": "@since",
			"url": "https://usejsdoc.org/tags-since.html",
			"desc": "When was this feature added?",
			"template": "@since ${cursor}"
		},
		"static": {
			"name": "@static",
			"url": "https://usejsdoc.org/tags-static.html",
			"desc": "Document a static member."
		},
		"summary": {
			"name": "@summary",
			"url": "https://usejsdoc.org/tags-summary.html",
			"desc": "A shorter version of the full description."
		},
		"this": {
			"name": "@this",
			"url": "https://usejsdoc.org/tags-this.html",
			"desc": "What does the 'this' keyword refer to here?"
		},
		"throws": {
			"name": "@throws",
			"url": "https://usejsdoc.org/tags-throws.html",
			"desc": "Describe what errors could be thrown.",
			"template": "@throws {${type}} ${cursor}"
		},
		"todo": {
			"name": "@todo",
			"url": "https://usejsdoc.org/tags-todo.html",
			"desc": "Document tasks to be completed."
		},
		"tutorial": {
			"name": "@tutorial",
			"url": "https://usejsdoc.org/tags-tutorial.html",
			"desc": "Insert a link to an included tutorial file."
		},
		"type": {
			"name": "@type",
			"url": "https://usejsdoc.org/tags-type.html",
			"desc": "Document the type of an object."
		},
		"typedef": {
			"name": "@typedef",
			"url": "https://usejsdoc.org/tags-typedef.html",
			"desc": "Document a custom type."
		},
		"variation": {
			"name": "@variation",
			"url": "https://usejsdoc.org/tags-variation.html",
			"desc": "Distinguish different objects with the same name."
		},
		"version": {
			"name": "@version",
			"url": "https://usejsdoc.org/tags-version.html",
			"desc": "Documents the version number of an item."
		},
		"link": {
			"name": "{@link}",
			"url": "https://usejsdoc.org/tags-inline-link.html",
			"desc": "Link to another item in the documentation."
		},
		"inline-tutorial": {
			"name": "{@tutorial}",
			"url": "https://usejsdoc.org/tags-inline-tutorial.html",
			"desc": "Link to a tutorial."
		}
	};
	
});
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env node, amd*/
/*globals infer tern resolver*/
/**
 * Tern type index and templates for MongoDB node support
 */
define('javascript/ternPlugins/mongodb',[
	"tern/lib/tern",
	"javascript/finder",
	"i18n!javascript/nls/messages"
], function(tern, Finder, Messages) {

	var templates = [
	/* eslint-disable missing-nls */
		{
			name: "mongodb",
			nodes: {top:true, member:false, prop:false},
			template: "var ${name} = require('mongodb');\n",
			doc: Messages['mongodbRequire'],
			url: "https://docs.mongodb.com/manual/reference/"
		},
		{
			name: "mongodb client",
			nodes: {top:true, member:false, prop:false},
			template: "var MongoClient = require('mongodb').MongoClient;\n" +
					  "var Server = require('mongodb').Server;\n${cursor}",
			doc: Messages['mongodbClient'],
			url: "https://docs.mongodb.com/manual/reference/"
		},
		{
			name: "mongodb open",
			nodes: {top:true, member:false, prop:false},
			template: "var MongoClient = require('mongodb').MongoClient;\n" +
					  "var Server = require('mongodb').Server;\n"+ 
					  "var ${client} = new MongoClient(new Server(${host}, ${port}));\n"+
					  "try {\n" +
					  "\t${client}.open(function(error, ${client}) {\n" +
  					  "\t\tvar ${db} = ${client}.db(${name});\n" +
  					  "\t\t${cursor}\n" +
  					  "\t});\n" + 
  					  "} finally {\n" +
  					  "\t${client}.close();\n" +
  					  "};",
			doc: Messages['mongodbOpen'],
			url: "https://docs.mongodb.com/manual/reference/"
		},
		{
			name: "mongodb connect",
			nodes: {top:true, member:false, prop:false},
			template: "var MongoClient = require('mongodb').MongoClient;\n" +
					  "MongoClient.connect(${url}, function(error, db) {\n"+ 
					  "\t${cursor}\n"+
  					  "});\n",
			doc: Messages['mongodbConnect'],
			url: "https://docs.mongodb.com/manual/reference/"
		},
		{
			name: "mongodb connect (Cloud Foundry)",
			nodes: {top:true, member:false, prop:false},
			template: "if (${process}.env.VCAP_SERVICES) {\n" + 
   					  "\tvar env = JSON.parse(${process}.env.VCAP_SERVICES);\n" + 
   					  "\tvar mongo = env[\'${mongo-version}\'][0].credentials;\n" + 
					  "} else {\n" + 
					  "\tvar mongo = {\n" + 
					  "\t\tusername : \'username\',\n" + 
					  "\t\tpassword : \'password\',\n" + 
					  "\t\turl : \'mongodb://username:password@localhost:27017/database\'\n" + 
					  "\t};\n}\n" + 
					  "var MongoClient = require('mongodb').MongoClient;\n" +
					  "MongoClient.connect(mongo.url, function(error, db) {\n"+ 
					  "\t${cursor}\n"+
  					  "});\n",
			doc: Messages['mongodbConnectCF'],
			url: "https://docs.mongodb.com/manual/reference/"
		},
		{
			name: "mongodb collection",
			nodes: {top:true, member:false, prop:false},
			template: "${db}.collection(${id}, function(${error}, collection) {\n"+
					  "\t${cursor}\n" + 
				  "});",
			doc: Messages['mongodbCollection'],
			url: "https://docs.mongodb.com/manual/reference/"
		},
		{
			name: "mongodb strict collection",
			nodes: {top:true, member:false, prop:false},
			template: "${db}.collection(${id}, {strict:true}, function(${error}, collection) {\n"+
					  "\t${cursor}\n" + 
					  "});",
			doc: Messages['mongodbStrictCollection'],
			url: "https://docs.mongodb.com/manual/reference/"
		}
		/* eslint-enable missing-nls */
	];
	
	var cachedQuery;
	
	/* eslint-enable missing-nls */
	tern.registerPlugin("mongodb", /* @callback */ function(server, options) { //$NON-NLS-1$
	    return {
	      defs : defs,
	      passes: {
		      	/**
		      	 * @callback
		      	 */
		      	completion: function(file, query) {
		      		cachedQuery = query;
		      	},
		      	/**
		      	 * @callback
		      	 */
		      	variableCompletion: function(file, start, end, gather) {
		      		if(cachedQuery.includeTemplates || cachedQuery.includeTemplates === undefined) {
			      		var kind = Finder.findCompletionKind(file.ast, end);
			      		if(kind && kind.kind) {
				      		var tmpls = Finder.findTemplatesForKind(templates, kind.kind, cachedQuery.ecma ? cachedQuery.ecma : 6);
				      		tmpls.forEach(function(template) {
								gather(template.name, null, 0, function(c) {
									c.template = template.template;
									c.description = template.description;
									c.doc = template.doc;
									c.url = template.url;
									c.type = 'template'; //$NON-NLS-1$
									c.ecma = template.ecma;
									c.origin = 'mongodb';
									c.overwrite = true;
								});
							});
				      	}
			      	}
		      	}
	      	}
	    };
	});
	
	/* eslint-disable missing-nls */
	var defs = {
		"mongodb": {
		    "MongoClient": {
		      "!type": "fn(serverConfig: ?, options: ?)",
		      "connect": "fn(uri: string, options: ?, callback: fn(err: Error, db: Db))",
		      "prototype": {}
		    },
		    "Server": {
		      "!type": "fn(host: string, port: number, opts?: ServerOptions)",
		      "prototype": {
		        "connect": "fn() -> ?"
		      }
		    },
		    "Db": {
		      "!type": "fn(databaseName: string, serverConfig: Server, dbOptions?: DbCreateOptions)",
		      "prototype": {
		        "db": "fn(dbName: string) -> Db",
		        "open": "fn(callback: fn(err: Error, db: Db))",
		        "close": "fn(forceClose?: bool, callback?: fn(err: Error, result: ?))",
		        "admin": "fn(callback: fn(err: Error, result: ?)) -> ?",
		        "collectionsInfo": "fn(collectionName: string, callback?: fn(err: Error, result: ?))",
		        "collectionNames": "fn(collectionName: string, options: ?, callback?: fn(err: Error, result: ?))",
		        "collection": "fn(collectionName: string) -> Collection",
		        "collections": "fn(callback: fn(err: Error, collections: [Collection]))",
		        "eval": "fn(code: ?, parameters: [?], options?: ?, callback?: fn(err: Error, result: ?))",
		        "logout": "fn(options: ?, callback?: fn(err: Error, result: ?))",
		        "authenticate": "fn(userName: string, password: string, callback?: fn(err: Error, result: ?))",
		        "addUser": "fn(username: string, password: string, callback?: fn(err: Error, result: ?))",
		        "removeUser": "fn(username: string, callback?: fn(err: Error, result: ?))",
		        "createCollection": "fn(collectionName: string, callback?: fn(err: Error, result: Collection))",
		        "command": "fn(selector: Object, callback?: fn(err: Error, result: ?))",
		        "dropCollection": "fn(collectionName: string, callback?: fn(err: Error, result: ?))",
		        "renameCollection": "fn(fromCollection: string, toCollection: string, callback?: fn(err: Error, result: ?))",
		        "lastError": "fn(options: Object, connectionOptions: ?, callback: fn(err: Error, result: ?))",
		        "previousError": "fn(options: Object, callback: fn(err: Error, result: ?))",
		        "executeDbCommand": "fn(command_hash: ?, callback?: fn(err: Error, result: ?))",
		        "executeDbAdminCommand": "fn(command_hash: ?, callback?: fn(err: Error, result: ?))",
		        "resetErrorHistory": "fn(callback?: fn(err: Error, result: ?))",
		        "createIndex": "fn(collectionName: ?, fieldOrSpec: ?, options: IndexOptions, callback: Function)",
		        "ensureIndex": "fn(collectionName: ?, fieldOrSpec: ?, options: IndexOptions, callback: Function)",
		        "cursorInfo": "fn(options: ?, callback: Function)",
		        "dropIndex": "fn(collectionName: string, indexName: string, callback: Function)",
		        "reIndex": "fn(collectionName: string, callback: Function)",
		        "indexInformation": "fn(collectionName: string, options: ?, callback: Function)",
		        "dropDatabase": "fn(callback: fn(err: Error, result: ?))",
		        "stats": "fn(options: ?, callback: Function)",
		        "_registerHandler": "fn(db_command: ?, raw: ?, connection: ?, exhaust: ?, callback: Function)",
		        "_reRegisterHandler": "fn(newId: ?, object: ?, callback: Function)",
		        "_callHandler": "fn(id: ?, document: ?, err: ?) -> ?",
		        "_hasHandler": "fn(id: ?) -> ?",
		        "_removeHandler": "fn(id: ?) -> ?",
		        "_findHandler": "fn(id: ?) -> ret",
		        "__executeQueryCommand": "fn(self: ?, db_command: ?, options: ?, callback: ?)",
		        "DEFAULT_URL": "string",
		        "connect": "fn(url: string, options: Object, callback: fn(err: Error, result: ?))",
		        "addListener": "fn(event: string, handler: fn(param: ?)) -> ?"
		      }
		    },
		    "SocketOptions": {
		      "timeout": "number",
		      "noDelay": "bool",
		      "keepAlive": "number",
		      "encoding": "string"
		    },
		    "ServerOptions": {
		      "auto_reconnect": "bool",
		      "poolSize": "number",
		      "socketOptions": "?"
		    },
		    "PKFactory": {
		      "counter": "number",
		      "createPk": "fn()"
		    },
		    "DbCreateOptions": {
		      "w": "?",
		      "wtimeout": "number",
		      "fsync": "bool",
		      "journal": "bool",
		      "readPreference": "string",
		      "native_parser": "bool",
		      "forceServerObjectId": "bool",
		      "pkFactory": "PKFactory",
		      "serializeFunctions": "bool",
		      "raw": "bool",
		      "recordQueryStats": "bool",
		      "retryMiliSeconds": "number",
		      "numberOfRetries": "number",
		      "logger": "Object",
		      "slaveOk": "number",
		      "promoteLongs": "bool"
		    },
		    "ReadPreference": {
		      "PRIMARY": "string",
		      "PRIMARY_PREFERRED": "string",
		      "SECONDARY": "string",
		      "SECONDARY_PREFERRED": "string",
		      "NEAREST": "string",
		      "prototype": {}
		    },
		    "CollectionCreateOptions": {
		      "readPreference": "string",
		      "slaveOk": "bool",
		      "serializeFunctions": "bool",
		      "raw": "bool",
		      "pkFactory": "PKFactory"
		    },
		    "CollStats": {
		      "ns": "string",
		      "count": "number",
		      "size": "number",
		      "avgObjSize": "number",
		      "storageSize": "number",
		      "numExtents": "number",
		      "nindexes": "number",
		      "lastExtentSize": "number",
		      "paddingFactor": "number",
		      "flags": "number",
		      "totalIndexSize": "number",
		      "indexSizes": {
		        "_id_": "number",
		        "username": "number"
		      }
		    },
		    "Collection": {
		      "insert": "fn(query: ?, callback: fn(err: Error, result: ?))",
		      "remove": "fn(selector: Object, callback?: fn(err: Error, result: ?))",
		      "rename": "fn(newName: String, callback?: fn(err: Error, result: ?))",
		      "save": "fn(doc: ?, callback: fn(err: Error, result: ?))",
		      "update": "fn(selector: Object, document: ?, callback?: fn(err: Error, result: ?))",
		      "distinct": "fn(key: string, query: Object, callback: fn(err: Error, result: ?))",
		      "count": "fn(callback: fn(err: Error, result: ?))",
		      "drop": "fn(callback?: fn(err: Error, result: ?))",
		      "findAndModify": "fn(query: Object, sort: [?], doc: Object, callback: fn(err: Error, result: ?))",
		      "findAndRemove": "fn(query: Object, sort?: [?], callback?: fn(err: Error, result: ?))",
		      "find": "fn(callback?: fn(err: Error, result: Cursor)) -> Cursor",
		      "findOne": "fn(callback?: fn(err: Error, result: ?)) -> Cursor",
		      "createIndex": "fn(fieldOrSpec: ?, callback: fn(err: Error, indexName: string))",
		      "ensureIndex": "fn(fieldOrSpec: ?, callback: fn(err: Error, indexName: string))",
		      "indexInformation": "fn(options: ?, callback: Function)",
		      "dropIndex": "fn(name: string, callback: Function)",
		      "dropAllIndexes": "fn(callback: Function)",
		      "reIndex": "fn(callback: Function)",
		      "mapReduce": "fn(map: Function, reduce: Function, options: MapReduceOptions, callback: Function)",
		      "group": "fn(keys: Object, condition: Object, initial: Object, reduce: Function, finalize: Function, command: bool, options: Object, callback: Function)",
		      "options": "fn(callback: Function)",
		      "isCapped": "fn(callback: Function)",
		      "indexExists": "fn(indexes: string, callback: Function)",
		      "geoNear": "fn(x: number, y: number, callback: Function)",
		      "geoHaystackSearch": "fn(x: number, y: number, callback: Function)",
		      "indexes": "fn(callback: Function)",
		      "aggregate": "fn(pipeline: [?], callback: fn(err: Error, results: ?))",
		      "stats": "fn(options: Object, callback: fn(err: Error, results: CollStats))",
		      "hint": "?"
		    },
		    "MapReduceOptions": {
		      "out": "Object",
		      "query": "Object",
		      "sort": "Object",
		      "limit": "number",
		      "keeptemp": "bool",
		      "finalize": "?",
		      "scope": "Object",
		      "jsMode": "bool",
		      "verbose": "bool",
		      "readPreference": "string"
		    },
		    "IndexOptions": {
		      "w": "?",
		      "wtimeout": "number",
		      "fsync": "bool",
		      "journal": "bool",
		      "unique": "bool",
		      "sparse": "bool",
		      "background": "bool",
		      "dropDups": "bool",
		      "min": "number",
		      "max": "number",
		      "v": "number",
		      "expireAfterSeconds": "number",
		      "name": "string"
		    },
		    "Cursor": {
		      "INIT": "number",
		      "OPEN": "number",
		      "CLOSED": "number",
		      "GET_MORE": "number",
		      "prototype": {
		        "rewind": "fn() -> Cursor",
		        "toArray": "fn(callback: fn(err: Error, results: [?]))",
		        "each": "fn(callback: fn(err: Error, item: ?))",
		        "count": "fn(applySkipLimit: bool, callback: fn(err: Error, count: number))",
		        "sort": "fn(keyOrList: ?, callback?: fn(err: Error, result: ?)) -> Cursor",
		        "limit": "fn(limit: number, callback?: fn(err: Error, result: ?)) -> Cursor",
		        "setReadPreference": "fn(preference: string, callback?: Function) -> Cursor",
		        "skip": "fn(skip: number, callback?: fn(err: Error, result: ?)) -> Cursor",
		        "batchSize": "fn(batchSize: number, callback?: fn(err: Error, result: ?)) -> Cursor",
		        "nextObject": "fn(callback: fn(err: Error, doc: ?))",
		        "explain": "fn(callback: fn(err: Error, result: ?))",
		        "stream": "fn() -> CursorStream",
		        "close": "fn(callback: fn(err: Error, result: ?))",
		        "isClosed": "fn() -> bool"
		      }
		    },
		    "CursorStream": {
		      "!type": "fn(cursor: Cursor)",
		      "prototype": {
		        "pause": "fn() -> ?",
		        "resume": "fn() -> ?",
		        "destroy": "fn() -> ?"
		      }
		    },
		    "CollectionFindOptions": {
		      "limit": "number",
		      "sort": "?",
		      "fields": "Object",
		      "skip": "number",
		      "hint": "Object",
		      "explain": "bool",
		      "snapshot": "bool",
		      "timeout": "bool",
		      "tailtable": "bool",
		      "tailableRetryInterval": "number",
		      "numberOfRetries": "number",
		      "awaitdata": "bool",
		      "oplogReplay": "bool",
		      "exhaust": "bool",
		      "batchSize": "number",
		      "returnKey": "bool",
		      "maxScan": "number",
		      "min": "number",
		      "max": "number",
		      "showDiskLoc": "bool",
		      "comment": "String",
		      "raw": "bool",
		      "readPreference": "String",
		      "partial": "bool"
		    },
		    "MongoCollectionOptions": {
		      "safe": "?",
		      "serializeFunctions": "?",
		      "raw": "bool",
		      "pkFactory": "?",
		      "readPreference": "string"
		    }
	  },
	  "!name": "mongodb",
	  "!define": {
	  	"!known_modules": {
		   "mongodb": {
		   	"!doc": "MongoDB",
		   	"!url": "https://www.mongodb.org/",
		    "MongoClient": "mongodb.MongoClient",
		    "Db": "mongodb.Db",
		    "Server": "mongodb.Server",
		    "SocketOptions": "mongodb.SocketOptions",
		    "ServerOptions": "mongodb.ServerOptions",
		    "CollectionFindOptions": "mongodb.CollectionFindOptions",
		    "MongoCollectionOptions": "mongodb.MongoCollectionOptions",
		    "IndexOptions": "mongodb.IndexOptions",
		    "CollectionCreateOptions": "mongodb.CollectionCreateOptions",
		    "DbCreateOptions": "mongodb.DbCreateOptions",
		    "MapReduceOptions": "mongodb.MapReduceOptions",
		    "CollStats": "mongodb.CollStats",
		    "ReadPreference": "mongodb.ReadPreference",
		    "Collection": "mongodb.Collection",
		    "Cursor": "mongodb.Cursor",
		    "PKFactory": "mongodb.PKFactory"
		   }
		 }
		}
	};
});
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env node, amd*/
/*globals infer tern resolver*/
/**
 * Tern type index and templates for AMQP node support
 */
define('javascript/ternPlugins/mysql',[
	"tern/lib/tern",
	"javascript/finder",
	"i18n!javascript/nls/messages"
], function(tern, Finder, Messages) {

	var templates = [
	/* eslint-disable missing-nls */
		{
			name: "mysql", 
			nodes: {top:true, member:false, prop:false},
			template: "var mysql = require('mysql');\n",
			doc: Messages[''],
			url: "https://github.com/redblaze/node-mysql#apis"
		},
		{
			name: "mysql connection", 
			nodes: {top:true, member:false, prop:false},
			template: "var mysql = require('mysql');\n" + 
					  "var ${connection} = mysql.createConnection({\n" +  
  					  "\thost : ${host},\n" +  
  					  "\tuser : ${username},\n" +  
  					  "\tpassword : ${password}\n" +  
					  "});\n" + 
					  "try {\n" +  
					  "\t${connection}.connect();\n" +  
					  "\t${cursor}\n" +  
					  "} finally {\n" +  
					  "\t${connection}.end();\n" +  
					  "}",
			doc: Messages['mysqlConnection'],
			url: "https://github.com/redblaze/node-mysql#apis"
		},
		{
			name: "mysql query", 
			nodes: {top:true, member:false, prop:false},
			template: "${connection}.query(${sql}, function(error, rows, fields) {\n" + 
					  "\t${cursor}\n" +  
					  "});\n",
			doc: Messages['mysqlQuery'],
			url: "https://github.com/redblaze/node-mysql#apis"
		}
		/* eslint-enable missing-nls */
	];
	
	var cachedQuery;
	
	/* eslint-enable missing-nls */
	tern.registerPlugin("mysql", /* @callback */ function(server, options) { //$NON-NLS-1$
	    return {
	      defs : defs,
	      passes: {
	      	/**
	      	 * @callback
	      	 */
	      	completion: function(file, query) {
	      		cachedQuery = query;
	      	},
	      	/**
	      	 * @callback
	      	 */
	      	variableCompletion: function(file, start, end, gather) {
	      		if(cachedQuery.includeTemplates || cachedQuery.includeTemplates === undefined) {
		      		var kind = Finder.findCompletionKind(file.ast, end);
		      		if(kind && kind.kind) {
			      		var tmpls = Finder.findTemplatesForKind(templates, kind.kind, cachedQuery.ecma ? cachedQuery.ecma : 6);
			      		tmpls.forEach(function(template) {
							gather(template.name, null, 0, function(c) {
								c.template = template.template;
								c.description = template.description;
								c.doc = template.doc;
								c.url = template.url;
								c.type = 'template'; //$NON-NLS-1$
								c.ecma = template.ecma;
								c.origin = 'mysql';
								c.overwrite = true;
							});
						});
			      	}
		      	}
	      	}
	      }
	    };
	});
	
	/* eslint-disable missing-nls */
	var defs = {
		
			  "mysql": {
			    "createConnection": "fn(connectionUri: string) -> Connection",
			    "createPool": "fn(config: PoolConfig) -> Pool",
			    "createPoolCluster": "fn(config?: PoolClusterConfig) -> PoolCluster",
			    "escape": "fn(value: ?) -> string",
			    "format": "fn(sql: string) -> string",
			    "MySql": {
			      "createConnection": "fn(connectionUri: string) -> Connection",
			      "createPool": "fn(config: PoolConfig) -> Pool",
			      "createPoolCluster": "fn(config?: PoolClusterConfig) -> PoolCluster",
			      "escape": "fn(value: ?) -> string",
			      "format": "fn(sql: string) -> string"
			    },
			    "ConnectionStatic": {
			      "createQuery": "fn(sql: string) -> Query"
			    },
			    "Connection": {
			      "config": "ConnectionConfig",
			      "threadId": "number",
			      "beginTransaction": "fn(callback: fn(err: Error))",
			      "connect": "fn()",
			      "commit": "fn(callback: fn(err: Error))",
			      "changeUser": "fn(options: ConnectionOptions)",
			      "query": "QueryFunction",
			      "end": "fn()",
			      "destroy": "fn()",
			      "pause": "fn()",
			      "release": "fn()",
			      "resume": "fn()",
			      "escape": "fn(value: ?) -> string",
			      "escapeId": "fn(value: string) -> string",
			      "format": "fn(sql: string) -> string",
			      "on": "fn(ev: string, callback: fn(args: [?])) -> Connection",
			      "rollback": "fn(callback: fn())"
			    },
			    "Pool": {
			      "config": "PoolConfig",
			      "getConnection": "fn(callback: fn(err: Error, connection: Connection))",
			      "query": "QueryFunction",
			      "end": "fn()",
			      "on": "fn(ev: string, callback: fn(args: [?])) -> Pool"
			    },
			    "PoolCluster": {
			      "config": "PoolClusterConfig",
			      "add": "fn(config: PoolConfig)",
			      "end": "fn()",
			      "getConnection": "fn(callback: fn(err: Error, connection: Connection))",
			      "of": "fn(pattern: string) -> Pool",
			      "on": "fn(ev: string, callback: fn(args: [?])) -> PoolCluster"
			    },
			    "Query": {
			      "sql": "string",
			      "start": "fn()",
			      "determinePacket": "fn(firstByte: number, parser: ?) -> ?",
			      "stream": "fn(options: StreamOptions) -> stream.Readable",
			      "pipe": "fn(callback: fn(args: [?])) -> Query",
			      "on": "fn(ev: string, callback: fn(args: [?])) -> Query"
			    },
			    "QueryFunction": {},
			    "QueryOptions": {
			      "sql": "string",
			      "timeout": "number",
			      "nestTables": "?",
			      "typeCast": "?"
			    },
			    "StreamOptions": {
			      "highWaterMark": "number",
			      "objectMode": "?"
			    },
			    "ConnectionOptions": {
			      "user": "string",
			      "password": "string",
			      "database": "string",
			      "charset": "string"
			    },
			    "ConnectionConfig": {
			      "host": "string",
			      "port": "number",
			      "localAddress": "string",
			      "socketPath": "string",
			      "timezone": "string",
			      "connectTimeout": "number",
			      "stringifyObjects": "bool",
			      "insecureAuth": "bool",
			      "typeCast": "?",
			      "queryFormat": "fn(query: string, values: ?)",
			      "supportBigNumbers": "bool",
			      "bigNumberStrings": "bool",
			      "dateStrings": "bool",
			      "debug": "?",
			      "trace": "bool",
			      "multipleStatements": "bool",
			      "flags": "?",
			      "ssl": "?"
			    },
			    "PoolConfig": {
			      "acquireTimeout": "number",
			      "waitForConnections": "bool",
			      "connectionLimit": "number",
			      "queueLimit": "number"
			    },
			    "PoolClusterConfig": {
			      "canRetry": "bool",
			      "removeNodeErrorCount": "number",
			      "defaultSelector": "string"
			    },
			    "SslCredentials": {
			      "pfx": "string",
			      "key": "string",
			      "passphrase": "string",
			      "cert": "string",
			      "ca": "?",
			      "crl": "?",
			      "ciphers": "string"
			    },
			    "Error": {
			      "code": "string",
			      "errno": "number",
			      "sqlStateMarker": "string",
			      "sqlState": "string",
			      "fieldCount": "number",
			      "stack": "string",
			      "fatal": "bool"
			    }
	  },
	  "!name": "mysql",
	  "!define": {
			"!known_modules": {
				"mysql": {
					"createConnection": "fn(connectionUri: string) -> mysql.Connection",
				    "createPool": "fn(config: mysql.PoolConfig) -> mysql.Pool",
				    "createPoolCluster": "fn(config?: mysql.PoolClusterConfig) -> mysql.PoolCluster",
				    "escape": "fn(value: ?) -> string",
				    "format": "fn(sql: string) -> string"
			    }
			}
		}
	};
});
/*******************************************************************************
 * @license
 * Copyright (c) 2016, 2018 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd*/
define('javascript/ternPlugins/occurrences',[
	"tern/lib/tern", 
	"estraverse/estraverse",
	"javascript/finder"
], function(tern, Estraverse, Finder) {

	tern.registerPlugin("occurrences", /* @callback */ function(server, options) { //$NON-NLS-1$
		return {}; //no phases
	});
	
	tern.defineQueryType("occurrences", { //$NON-NLS-1$
		takesFile: true,
		/**
		 * @callback
		 */
		run: function(server, query, file) {
			if(file.ast) {
				var start = typeof query.start === 'number' ? query.start : query.end;
				var end = query.end;
				var token = _getToken(start, file.ast);
				if (token) {
					var node = Finder.findNode(start, file.ast, {parents: true});
					if(!_skip(node)) {
						if (token.range[0] >= node.range[0] && token.range[1] <= node.range[1]){
							if (node.type === Estraverse.Syntax.Literal){
								var amdNode = checkNodeDefineStatement(node, file.ast);
								if (amdNode){
									node = amdNode;
									start = node.range[0];
									end = node.range[1];
								} else {
									// No other literals can have occurrences so bail
									return [];
								}
							}
							reset({
								start: start,
								end: end,
								word: _nameFromNode(node),
								token: node
							});
							Estraverse.traverse(file.ast, visitor);
							return occurrences;
						}
					}
				}
			}
			return [];
		}
	});
	
	var occurrences = [],
		scopes = [],
		context = null,
		defscope = null,
		skipScope = null,
		thisCheck = false,
		objectPropCheck = false,
		labeledStatementCheck = false;
	
	var visitor = {
		
		/**
		 * @name enter
		 * @description Callback from estraverse when a node is starting to be visited
		 * @function
		 * @private
		 * @memberof javascript.Visitor.prototype
		 * @param {Object} node The AST node currently being visited
		 * @param {Object} parent The last node we visited
		 * @returns The status if we should continue visiting
		 */
		enter: function(node, parent) {
			var len, idx;
			node.parent = parent;
			switch(node.type) {
				case Estraverse.Syntax.Program:
					scopes.push({range: node.range, occurrences: [], kind:'p'}); //$NON-NLS-1$
					break;
				case Estraverse.Syntax.BlockStatement:
					var scope = scopes[scopes.length-1];
					if (scope.isLet)
					var blocks = scopes[scopes.length-1].blocks;
					if (!blocks){
						blocks = [];
					}
					blocks.push({range: node.range, occurrences: [], kind:'b'}); //$NON-NLS-1$
					scopes[scopes.length-1].blocks = blocks;
					break;
				case Estraverse.Syntax.FunctionDeclaration:
					checkId(node.id, node, true);
					_enterScope(node);
					if (skipScope){
						// If the function decl was a redefine, checkId may set skipScope and we can skip processing the contents
						return Estraverse.VisitorOption.Skip;
					}
					if (node.params) {
						len = node.params.length;
						for (idx = 0; idx < len; idx++) {
							var identifier = node.params[idx];
							if (identifier.type === Estraverse.Syntax.AssignmentPattern && identifier.left){
								identifier = identifier.left;
							}
							if(checkId(identifier, node, true)) {
								return Estraverse.VisitorOption.Skip;
							}
						}
					}
					break;
				case Estraverse.Syntax.FunctionExpression:
				case Estraverse.Syntax.ArrowFunctionExpression:
					if(_enterScope(node)) {
						return Estraverse.VisitorOption.Skip;
					}
					checkId(node.id, node, true); // Function expressions can be named expressions
					if (node.params) {
						len = node.params.length;
						for (idx = 0; idx < len; idx++) {
							identifier = node.params[idx];
							if (identifier.type === Estraverse.Syntax.AssignmentPattern && identifier.left){
								identifier = identifier.left;
							}
							if(identifier.type === Estraverse.Syntax.ObjectPattern) {
								identifier.properties.forEach(function(prop) {
									checkId(prop.key, node, true, false);
									checkId(prop.value, node);
								});
							} else
							if(checkId(identifier, node, true)) {
								return Estraverse.VisitorOption.Skip;
							}
						}
					}
					break;
				case Estraverse.Syntax.ClassDeclaration:
					checkId(node.id, node, true);
					checkId(node.superClass, node);
					if(_enterScope(node)) {
						return Estraverse.VisitorOption.Skip;
					}
					break;
				case Estraverse.Syntax.ClassExpression:
					if(_enterScope(node)) {
						return Estraverse.VisitorOption.Skip;
					}
					checkId(node.id, node, true);
					checkId(node.superClass, node);
					break;
				case Estraverse.Syntax.AssignmentExpression:
					if(node.left.type === Estraverse.Syntax.ArrayPattern) {
						//Destructuring assignment
						node.left.elements.forEach(function(element) {
							checkId(element, node.left);
						});
					} else if(node.left.type === Estraverse.Syntax.ObjectPattern) {
						//Destructuring object assignment
						node.left.properties.forEach(function(prop) {
							checkId(prop.key, node.left);
						});
					} else {
						//Basic identifier check
						checkId(node.left, node);
					}
					checkId(node.right, node);
					break;
				case Estraverse.Syntax.ExpressionStatement:
					checkId(node.expression, node);
					break;
				case Estraverse.Syntax.ArrayExpression: 
					if (node.elements) {
						len = node.elements.length;
						for (idx = 0; idx < len; idx++) {
							checkId(node.elements[idx], node);
						}
					}
					break;
				case Estraverse.Syntax.MemberExpression:
					checkId(node.object, node);
					if (node.computed) { //computed = true for [], false for . notation
						checkId(node.property, node);
					} else {
						checkId(node.property, node, false, true);
					}
					break;
				case Estraverse.Syntax.BinaryExpression:
					checkId(node.left, node);
					checkId(node.right, node);
					break;
				case Estraverse.Syntax.UnaryExpression:
					checkId(node.argument, node);
					break;
				case Estraverse.Syntax.SwitchStatement:
					checkId(node.discriminant, node);
					break;
				case Estraverse.Syntax.UpdateExpression:
					checkId(node.argument, node);
					break;
				case Estraverse.Syntax.ConditionalExpression:
					checkId(node.test, node);
					checkId(node.consequent, node);
					checkId(node.alternate, node);
					break;
				case Estraverse.Syntax.CallExpression:
					checkId(node.callee, node, false);
					if (node.arguments) {
						len = node.arguments.length;
						for (idx = 0; idx < len; idx++) {
							checkId(node.arguments[idx], node);
						}
					}
					break;
				case Estraverse.Syntax.ReturnStatement:
					checkId(node.argument, node);
					break;
				case Estraverse.Syntax.ObjectExpression:
					if(_enterScope(node)) {
						return Estraverse.VisitorOption.Skip;
					}
					if(node.properties) {
						len = node.properties.length;
						for (idx = 0; idx < len; idx++) {
							var prop = node.properties[idx];
							if (prop.value && prop.value.type === Estraverse.Syntax.FunctionExpression){
								if(thisCheck) {
									//tag it 
									prop.value.isprop = true;
								} else {
									checkId(prop.value.id, node, false, true);
								}
							}
							checkId(prop.key, node, true, true);
							checkId(prop.value, node);
						}
					}
					break;
				case Estraverse.Syntax.VariableDeclarator:
					if(node.id.type === Estraverse.Syntax.ArrayPattern) {
						//Destructuring array assignment
						node.id.elements.forEach(function(element) {
							if(element.type === Estraverse.Syntax.AssignmentPattern) {
								//Destructured default values
								checkId(element.left, element, true);
								checkId(element.right, element);
							} else {
								checkId(element, node.id, true);
							}
						});
					} else if(node.id.type === Estraverse.Syntax.ObjectPattern) {
						//Destructuring object assignment
						node.id.properties.forEach(function(prop) {
							checkId(prop.key, node.id, true, false);
							if(prop.key !== prop.value) {
								checkId(prop.value, node.id);
							}
						});
					} else {
						checkId(node.id, node, true);
					}
					checkId(node.init, node);
					break;
				case Estraverse.Syntax.NewExpression:
					checkId(node.callee, node, false);
					if(node.arguments) {
						len = node.arguments.length;
						for(idx = 0; idx < len; idx++) {
							checkId(node.arguments[idx], node);
						}
					}
					break;
				case Estraverse.Syntax.LogicalExpression:
					checkId(node.left, node);
					checkId(node.right, node);
					break;
				case Estraverse.Syntax.ThisExpression:
					if(thisCheck) {
						var scope = scopes[scopes.length-1];
						scope.occurrences.push({
							start: node.range[0],
							end: node.range[1]
						});
						// if this node is the selected this we are in the right scope
						if (node.range[0] === context.token.range[0]){
							defscope = scope;
						}
					}
					break;
				case Estraverse.Syntax.IfStatement:
				case Estraverse.Syntax.DoWhileStatement:
				case Estraverse.Syntax.WhileStatement:
					checkId(node.test, node);
					break;
				case Estraverse.Syntax.ForStatement:
					checkId(node.init, node);
					break;
				case Estraverse.Syntax.ForInStatement:
                    checkId(node.left, node);
                    checkId(node.right, node);
                    break;
                case Estraverse.Syntax.ForOfStatement: {
                	checkId(node.left, node);
                	checkId(node.right, node);
                	break;
                }
				case Estraverse.Syntax.WithStatement:
                    checkId(node.object, node);
                    break;
                case Estraverse.Syntax.ThrowStatement:
                    checkId(node.argument, node);
                    break;
                case Estraverse.Syntax.LabeledStatement:
               		_enterScope(node);
                    checkId(node.label, node, true, false, true);
                    break;
                case Estraverse.Syntax.ContinueStatement :
                    checkId(node.label, node, false, false, true);
                    break;
                case Estraverse.Syntax.BreakStatement:
                    checkId(node.label, node, false, false, true);
                    break;
                // ES6 constructs (Class expressions and declarations are done above)
                case Estraverse.Syntax.AssignmentPattern:
                	checkId(node.right, node);
                	break;   
                case Estraverse.Syntax.ExportDefaultDeclaration:
                	checkId(node.declaration, node);
                	break;   
                case Estraverse.Syntax.ExportSpecifier:
                	checkId(node.local, node);
                	break;
                case Estraverse.Syntax.ImportDefaultSpecifier:
                	checkId(node.local, node, true);
                	break;
                case Estraverse.Syntax.ImportNamespaceSpecifier:
                	checkId(node.local, node, true);
                	break;
                case Estraverse.Syntax.ImportSpecifier:
                	checkId(node.local, node, true);
                	break;
               	case Estraverse.Syntax.MethodDefinition:
               		checkId(node.key, node, true);
               		break;
               	case Estraverse.Syntax.YieldExpression:
               		checkId(node.argument, node);
               		break;
			}
		},
		
		/**
		 * @name leave
		 * @description Callback from estraverse when visitation of a node has completed
		 * @function
		 * @private
		 * @memberof javascript.Visitor.prototype
		 * @param {Object} node The AST node that ended its visitation
		 * @return The status if we should continue visiting
		 */
		leave: function(node) {
			if(thisCheck) {
				switch(node.type) {
					case Estraverse.Syntax.FunctionExpression:
						if(node.isprop) {
							delete node.isprop; //remove the tag
							break;
						}
					//$FALLTHROUGH$
					case Estraverse.Syntax.ObjectExpression:
					case Estraverse.Syntax.Program:
					case Estraverse.Syntax.ClassDeclaration:
					case Estraverse.Syntax.ClassExpression:
						if (!defscope){
							skipScope = scopes[scopes.length-1];
						}
						if(_popScope()) {
							//we left an object closure, end
							return Estraverse.VisitorOption.Break;
						}
						break;
				}
			} else if (objectPropCheck) {
				switch(node.type){
					case Estraverse.Syntax.ClassDeclaration:
					case Estraverse.Syntax.ClassExpression:
					case Estraverse.Syntax.ObjectExpression:
					case Estraverse.Syntax.Program:
						if(_popScope()) {
							return Estraverse.VisitorOption.Break;
						}
						break;
				}
			} else if (labeledStatementCheck) {
				switch(node.type){
					case Estraverse.Syntax.LabeledStatement:
						if(_popScope()) {
							return Estraverse.VisitorOption.Break;
						}
						break;
				}
			} else {
				switch(node.type) {
					case Estraverse.Syntax.FunctionExpression:
					case Estraverse.Syntax.FunctionDeclaration: 
					case Estraverse.Syntax.ArrowFunctionExpression:
					case Estraverse.Syntax.ClassDeclaration:
					case Estraverse.Syntax.ClassExpression:
					{
					    if(_popScope()) {
							return Estraverse.VisitorOption.Break;
						}
						break;
					}
					case Estraverse.Syntax.Program: {
					    _popScope(); // pop the last scope
						break;
					}
					case Estraverse.Syntax.BlockStatement:
						if (_popBlock()){
							return Estraverse.VisitorOption.Break;
						}
						break;
				}
			}
		}
	};

	/**
	 * @name checkId
	 * @description Checks if the given identifier matches the occurrence we are looking for
	 * @function
	 * @private
	 * @memberof javascript.JavaScriptOccurrences.prototype
	 * @param {Object} node The AST node we are inspecting
	 * @param {Object} parent The parent for the node we are currently going to visit 
	 * @param {Boolean} candefine If the given node can define the word we are looking for
	 * @param {Boolean} isObjectProp Whether the given node is only an occurrence if we are searching for object property occurrences
	 * @param {Boolean} isLabeledStatement Whether the given node is only an occurrence if we are searching for labeled statements
	 * @returns {Boolean} <code>true</code> if we should skip the next nodes, <code>false</code> otherwise
	 */
	function checkId(node, parent, candefine, isObjectProp, isLabeledStatement) {
		if (skipScope){
			return true;
		}
		if (thisCheck){
			return false;
		}
		if ((isObjectProp && !objectPropCheck) || (!isObjectProp && objectPropCheck)){
			return false;
		}
		if ((isLabeledStatement && !labeledStatementCheck) || (!isLabeledStatement && labeledStatementCheck)){
			return false;
		}
		if(node) {
			//have to tag the node here since we don't visit these nodes via the estraverse API
			node.parent = parent;
		}
		if (node && node.type === Estraverse.Syntax.Identifier) {
			if (node.name === context.word) {
				var scope = scopes[scopes.length-1]; // Always will have at least the program scope
				if (node.parent.type === Estraverse.Syntax.VariableDeclarator && node.parent.parent && node.parent.parent.type === Estraverse.Syntax.VariableDeclaration && (node.parent.parent.kind === 'let' || node.parent.parent.kind === 'const')){
					scope.isLet = true;
				}
				if (scope.isLet && scope.blocks && scope.blocks.length > 0){
					scope = scope.blocks[scope.blocks.length-1];
				}
				if(candefine) {
					// Check if we are redefining
					if(defscope) {
						if((scope.range[0] <= context.start) && (scope.range[1] >= context.end)) {
							// Selection inside this scope, use this scope as the defining scope
							occurrences = []; // Clear any occurrences in sibling scopes
							defscope = scope;
							scope.occurrences.push({
								start: node.range[0],
								end: node.range[1]
							});
							return false;
						} 
						// Selection belongs to an outside scope so use the outside definition
						scope.occurrences = []; // Clear any occurrences we have found in this scope
						skipScope = scope;  // Skip this scope and all inner scopes
						return true;  // Where possible we short circuit checking this scope
					}
					//does the scope enclose it?
					if((scope.range[0] <= context.start) && (scope.range[1] >= context.end)) {
						defscope = scope;
						
						// If identifier is an argument of a define statement, also mark the matching dependency
						_markDefineStatementOccurrences(node, scope.occurrences);
					} else {
						// Selection belongs to an outside scope so use the outside definition (Bug 447962)
						scope.occurrences = [];
						skipScope = scope;
						return true;
					}
				}
				scope.occurrences.push({
					start: node.range[0],
					end: node.range[1]
				});
			}
		}
		return false;
	}
	
	/**
	 * @description Checks if the given node is a parameter specifying an AMD define dependency.  If so, mark the matching dependency path.
	 * @function
	 * @private
	 * @param {ASTNode} node The AST node we are inspecting
	 * @param {Array} occurrencesList The array of occurrences to add the new occurrence to
	 */
	function _markDefineStatementOccurrences(node, occurrencesList){
		var parent = node.parent;
		if (parent && parent.type === Estraverse.Syntax.FunctionExpression) {
			var parent2 = parent.parent;
			if (parent2 && parent2.type === Estraverse.Syntax.CallExpression && parent2.callee && parent2.callee.name === "define"){
				var funcExpression = parent;
				for (var i=0; i<funcExpression.params.length; i++) {
					if (funcExpression.params[i] === node){
						if (parent2.arguments.length === 2 || parent2.arguments.length === 3){
							var pathsNode = parent2.arguments[parent2.arguments.length-2];
							if (pathsNode.elements && pathsNode.elements.length > i){
								occurrencesList.push({
									start: pathsNode.elements[i].range[0],
									end: pathsNode.elements[i].range[1]
								});
							}
						}
						break;
					}
				}
			}
		}
	}
	
	function reset(cntxt) {
		occurrences = [];
		scopes = [];
		context = cntxt;
		thisCheck = false;
		defscope = null;
		skipScope = null;
		objectPropCheck = false;
		labeledStatementCheck = false;
		if (context.token){
			var parent = context.token.parent ? context.token.parent : (context.token.parents && context.token.parents.length > 0 ? context.token.parents[context.token.parents.length-1] : null);
			
			// See if a 'this' keyword was selected
			thisCheck = context.token.type === Estraverse.Syntax.ThisExpression;
			
			// See if we are doing an object property check
			objectPropCheck = false;
			if (parent && parent.type === Estraverse.Syntax.Property){
				var grandparent = parent.parent;
				if(!grandparent) {
					grandparent = cntxt.token.parents && cntxt.token.parents.length > 1 ? cntxt.token.parents[cntxt.token.parents.length-2] : null;
				}
				if(grandparent) {
					if(grandparent.type !== Estraverse.Syntax.ObjectPattern) {
						// Object property key is selected
						objectPropCheck = context.token === parent.key;
					}
				} else {
					//no grand parent context, assume an object expression
					objectPropCheck = context.token === parent.key;
				}

			} else if (parent && (parent.type === Estraverse.Syntax.MemberExpression)){
				if (parent.object && parent.object.type === Estraverse.Syntax.ThisExpression && !parent.computed){
					// Usage of this within an object
					objectPropCheck = true;
				} else if (!parent.computed && parent.property && context.start >= parent.property.range[0] && context.end <= parent.property.range[1]){
				 	// Selecting the property key of a member expression that is not computed (foo.a vs foo[a])
					objectPropCheck = true;
				}
			} else if (parent && parent.type === Estraverse.Syntax.FunctionExpression && context.token.parents && context.token.parents.length > 1 && context.token.parents[context.token.parents.length-2].type === Estraverse.Syntax.Property){
				// Both the name and the params have the same parent
				if (parent.id && parent.id.range === context.token.range){
					// Named function expresison as the child of a property
					objectPropCheck = true;
				}
			}
			// See if a labeled statement is selected
			labeledStatementCheck = parent && (parent.type === Estraverse.Syntax.LabeledStatement || parent.type === Estraverse.Syntax.ContinueStatement || parent.type === Estraverse.Syntax.BreakStatement);
		}
	}
	
	function checkNodeDefineStatement(node, ast) {
		var parent = node.parent ? node.parent : (node.parents && node.parents.length > 0 ? node.parents[node.parents.length-1] : null);
		if (parent && parent.type === Estraverse.Syntax.ArrayExpression){
			var parent2 = parent.parent ? parent.parent : (node.parents && node.parents.length > 1 ? node.parents[node.parents.length-2] : null);
			if (parent2 && parent2.type === Estraverse.Syntax.CallExpression && parent2.callee && parent2.callee.name === "define"){
				var elements = parent.elements;
				for (var i=0; i<elements.length; i++) {
					if (elements[i] === node){
						var deps = parent2;
						if (deps && deps.arguments && (deps.arguments.length === 2 || deps.arguments.length === 3)){
							deps = deps.arguments[deps.arguments.length-1];
							if (deps.params && deps.params.length > i){
								return Finder.findNode(deps.params[i].range[0], ast, {parents: true});
							}
						}
						break;
					}
				}
			}
		}
		return null;
	}
	
	/**
	 * @description Gets the token from the given offset or the proceeding token if the found token 
	 * is a punctuator
	 * @function
	 * @private
	 * @param {Number} offset The offset into the source
	 * @param {Object} ast The AST
	 * @return {Object} The token for the given offset or null
	 */
	function _getToken(offset, ast) {
		if(ast.tokens && ast.tokens.length > 0) {
			var token = Finder.findToken(offset, ast.tokens);
			if(token) {
				if(token.type === 'Punctuator') {
					var index = token.index;
					//only check back if we are at the start of the punctuator i.e. here -> {
					if(offset === token.range[0] && index !== null && index > 0) {
						var prev = ast.tokens[index-1];
						if(prev.range[1] !== token.range[0]) {
							return null;
						}
						token = prev;
					}
				}
				if(token.type === 'Identifier' || token.type === "String" || (token.type === 'Keyword' && token.value === 'this')) {
					return token;
				}
			}
		}
		return null;
	}
	
	/**
	 * @description Enters and records the current scope onthe scope stack
	 * @function
	 * @private
	 * @param {Object} node The AST node
	 * @returns {Boolean} If we should skip visiting children of the scope node
	 */
	function _enterScope(node) {
		if(thisCheck) {
			switch(node.type) {
				case Estraverse.Syntax.ObjectExpression:
					scopes.push({range: node.range, occurrences: [], kind:'o'});  //$NON-NLS-0$
					if (defscope){
						return true;
					}
					break;
				case Estraverse.Syntax.FunctionExpression:
					if (!node.isprop){
						scopes.push({range: node.body.range, occurrences: [], kind:'fe'});  //$NON-NLS-0$
						// If the outer scope has the selected 'this' we can skip the inner scope
						if (defscope){
							return true;
						}
					}
					break;
				case Estraverse.Syntax.ClassDeclaration:
				case Estraverse.Syntax.ClassExpression:
					scopes.push({range: node.body.range, occurrences: [], kind:'c'});  //$NON-NLS-0$
					if (defscope){
						return true;
					}
					break;
			}
		} else if (objectPropCheck){
			switch(node.type) {
				case Estraverse.Syntax.ClassDeclaration:
				case Estraverse.Syntax.ClassExpression:
					scopes.push({range: node.range, occurrences: [], kind:'c'});  //$NON-NLS-0$
					break;
				case Estraverse.Syntax.ObjectExpression:
					scopes.push({range: node.range, occurrences: [], kind:'o'});  //$NON-NLS-0$
					break;
			}
		} else if (labeledStatementCheck){
			switch(node.type) {
				case Estraverse.Syntax.LabeledStatement:
					scopes.push({range: node.range, occurrences: [], kind:'ls'});  //$NON-NLS-0$
					// Skip labelled loops that don't contain the selection
					if(node.range[0] > context.start || node.range[1] < context.end) {
						return true;
					}						
			}
		} else {
			var kind;
			var rangeStart = node.range[0];
			if (node.body){
				rangeStart = node.body.range[0];
			}
			switch(node.type) {
				case Estraverse.Syntax.FunctionDeclaration:
					kind = 'fd';  //$NON-NLS-0$
					// Include the params and body in the scope, but not the identifier
					if (node.params && (node.params.length > 0)){
						rangeStart = node.params[0].range[0];
					}
					break;
				case Estraverse.Syntax.FunctionExpression:
				case Estraverse.Syntax.ArrowFunctionExpression:
					kind = 'fe';  //$NON-NLS-0$
					// Include the params, body and identifier (if available) See Bug 447413
					if (node.id) {
						rangeStart = node.id.range[0];
					} else if (node.params && (node.params.length > 0)){
						rangeStart = node.params[0].range[0];
					}
					break;
				case Estraverse.Syntax.ClassDeclaration:
					kind = 'c';  //$NON-NLS-0$
					break;
				case Estraverse.Syntax.ClassExpression:
					kind = 'c';  //$NON-NLS-0$
					// Include the body and identifier (if available) See Bug 447413
					if (node.id) {
						rangeStart = node.id.range[0];
					}
					break;
			}
			if (kind){
				scopes.push({range: [rangeStart,node.range[1]], occurrences: [], kind:kind});	
			}
		}
		return false;
	}
	
	/**
	 * @description Pops the tip of the block stack off, adds occurrences (if any) and returns if we should
	 * quit visiting
	 * @function
	 * @private
	 * @returns {Boolean} If we should quit visiting
	 */
	function _popBlock() {
		var scope = scopes[scopes.length-1];
		if (!scope.blocks || scope.blocks.length === 0){
			return false;
		}
		var block = scope.blocks.pop();
		if (skipScope){
			if (skipScope === block){
				skipScope = null;
			}
			return false;
		}
		if(scope.isLet && Array.isArray(block.occurrences)) {
			var i, j;
			var len = block.occurrences.length;
			if (defscope && defscope === block){
				for(i = 0; i < len; i++) {
					occurrences.push(block.occurrences[i]);
				}
				return true;
			}
			// We popped out of a scope but don't know where the define is, treat the occurrences like they belong to the outer scope (Bug 445410)
			if (scope.blocks.length > 0){
				for (j=0; j< len; j++) {
					scope.blocks[scope.blocks.length - 1].occurrences.push(block.occurrences[j]);
				}
			} else {
				for (j=0; j< len; j++) {
					scope.occurrences.push(block.occurrences[j]);
				}
			}
		}
		return false;
	}
	
	/**
	 * @description Pops the tip of the scope stack off, adds occurrences (if any) and returns if we should
	 * quit visiting
	 * @function
	 * @private
	 * @returns {Boolean} If we should quit visiting
	 */
	function _popScope() {
		var scope = scopes.pop();
		if (skipScope){
			if (skipScope === scope){
				skipScope = null;
			}
			return false;
		}
		var len = scope.occurrences.length;
		var i, j;
		// Move all occurrences into the defining scope in case an inner scope redefines (Bug 448535)
		if(defscope && defscope === scope) {
			for(i = 0; i < len; i++) {
				occurrences.push(scope.occurrences[i]);
			}
			if(defscope.range[0] === scope.range[0] && defscope.range[1] === scope.range[1] &&
				defscope.kind === scope.kind) {
				//we just popped out of the scope the node was defined in, we can quit
				return true;
			}
		} else {
			if (scopes.length > 0){
				// We popped out of a scope but don't know where the define is, treat the occurrences like they belong to the outer scope (Bug 445410)
				for (j=0; j< len; j++) {
					scopes[scopes.length - 1].occurrences.push(scope.occurrences[j]);
				}
			} else {
				// We are leaving the AST, add the occurrences if we never found a defining scope
				occurrences = [];
				for (j=0; j< len; j++) {
					occurrences.push(scope.occurrences[j]);
				}
			}
		}
		return false;
	}
	
	/**
	 * @description Computes the node name to use while searching
	 * @function
	 * @private
	 * @param {Object} node The AST token
	 * @returns {String} The node name to use while seraching
	 */
	function _nameFromNode(node) {
		switch(node.type) {
			case Estraverse.Syntax.Identifier: return node.name;
			case Estraverse.Syntax.ThisExpression: return 'this'; //$NON-NLS-0$
		}
	}
	
	/**
	 * @description If we should skip marking occurrences
	 * @function
	 * @private
	 * @param {Object} node The AST node
	 * @returns {Boolean} True if we shoud skip computing occurrences
	 */
	function _skip(node) {
		if(!node) {
			return true;
		}
		if(node.type === Estraverse.Syntax.ThisExpression) {
			return false;
		}
		
		if (node.type === Estraverse.Syntax.Literal){
			return false;
		}
		
		if (node.type === Estraverse.Syntax.Identifier){
			var parent = node.parent;
			if (!parent && node.parents) {
				parent = node.parents[node.parents.length-1];
			}
			if (parent){
				if (parent.type === Estraverse.Syntax.ImportSpecifier){
					return parent.imported === node;
				} else if (parent.type === Estraverse.Syntax.ExportSpecifier){
					return parent.exported === node;
				}
			}
		}
		
		return node.type !== Estraverse.Syntax.Identifier;
	}
});
/*******************************************************************************
 * @license
 * Copyright (c) 2015 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd*/
/*globals infer tern resolver*/
define('javascript/ternPlugins/open_impl',[
	"tern/lib/infer", 
	"tern/lib/tern", 
	"javascript/finder"
], function(infer, tern, Finder) {
	
	tern.registerPlugin("open_impl", /* @callback */ function(server, options) { //$NON-NLS-1$
	    return {};
	});
	
	tern.defineQueryType('implementation', { //$NON-NLS-1$
		/**
		 * @callback
		 */
		run: function run(server, query) {
			if (query.end && !query.start) {
				query.start = query.end;
			}
			var theFile = server.fileMap[query.file];
			var impl = this.findImplRecurse(query.end, theFile, {}, server);
			if(!query.guess && infer.didGuess()) {
				return null;
			}
			if (!impl.implementation){
				impl.implementation = {};
			}
			impl.implementation.guess = infer.didGuess();
			return impl;
		},
		
		/**
		 * Looks at the given offset in the given file and returns the implementation if one can be found.
		 * Otherwise returns the given candidateImpl.  This function will run recursively until it finds a node
		 * that is the implementation or findDef returns the same node.
		 * @param offset offset in the file where the node/caret is
		 * @param serverFile the server file containing the ast and filename
		 * @param candidateImpl the value to return if an implementation can't be found
		 * @param server the server to lookup other files in
		 * @returns {Object} Implementation object containing a single 'implementation' property with start/end/file info
		 */
		findImplRecurse: function findImplRecurse(offset, serverFile, candidateImpl, server){
			var query, typeDef, newServerFile;
			if (serverFile) {
				var node = Finder.findNode(offset, serverFile.ast, {parents: true});
				if (node){
					if (node.type === 'Identifier') {
						var parent = node.parents[node.parents.length-1];
						if (parent){
							if (parent.type === 'MemberExpression' && node.parents.length >= 2) {
								// See if the member expression is an assignment a.b=1 that we can follow, otherwise fallthrough and lookup typeDef for the property node
								parent = node.parents[node.parents.length-2];
							}
							
							var rhs;
							if (parent.type === 'VariableDeclarator' && parent.init){
								rhs = parent.init;
							} else if (parent.type === 'AssignmentExpression' && parent.right){
								rhs = parent.right;
							} else if (parent.type === 'Property' && parent.value){
								rhs = parent.value;
							}
							if (rhs){
								if (rhs.type === 'Literal' || rhs.type === 'FunctionExpression') {
									// Literals count as implementations
									// Function expressions are implementations of a function
									// Short circuit and use the current node
									return {implementation: {start: node.start, end: node.end, file: serverFile.name}};
								}
								// Find the implementation of the RHS identifier
								query = {start: rhs.start, end: rhs.end, file: serverFile.name, guess: true};
								typeDef = tern.findDef(server, query, serverFile);
								if (typeDef && typeof typeDef.start === 'number' && typeof typeDef.end === 'number' && (typeDef.start !== node.start || typeDef.end !== node.end)){
									newServerFile = server.fileMap[typeDef.file];
									return this.findImplRecurse(typeDef.end, newServerFile, {implementation: {start: typeDef.start, end: typeDef.end, file: typeDef.file}}, server);
								}
							}
						}
						// There are many parents of an identifier, rather than list them all, default to look up the typeDef of the identifier
						query = {start: node.start, end: node.end, file: serverFile.name, guess: true};
						typeDef = tern.findDef(server, query, serverFile);
						if (typeDef){
							if (typeof typeDef.start === 'number' && typeof typeDef.end === 'number'){
								if (typeDef.start !== node.start || typeDef.end !== node.end){
									// Found a new node, recurse into the declaring node
									newServerFile = server.fileMap[typeDef.file];
									return this.findImplRecurse(typeDef.end, newServerFile, {implementation: {start: typeDef.start, end: typeDef.end, file: typeDef.file}}, server);
								}
							} else if (typeof typeDef.origin === 'string'){
								// The declaration is in an index file
								return {implementation: {origin: typeDef.origin}};
							} else if (!candidateImpl.implementation){
								// Could not find an implementation, still at the node we started at, tell the user we can't find an implementation	
								return {};
							}
						}
						
						// The typeDef couldn't be found or matches the current node, just return the node
						return {implementation: {start: node.start, end: node.end, file: serverFile.name}};
					}
				}
			}
			// Fall back on the last candidate implementation
			return candidateImpl;
		}
	});
});
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd*/
define('javascript/ternPlugins/outliner',[
	"tern/lib/tern", 
	"estraverse/estraverse",
	"javascript/signatures"
], function(tern, Estraverse, Signatures) {

	tern.registerPlugin("outliner", /* @callback */ function(server, options) { //$NON-NLS-1$
		return {}; //no phases
	});
	
	tern.defineQueryType("outline", { //$NON-NLS-1$
		takesFile: true,
		/**
		 * @callback
		 */
		run: function(server, query, file) {
			if(file.ast) {
				var outline = [], scope = [];
				/**
				 * @description Appends the given signature object to the running outline
				 * @function
				 * @private
				 * @param {?} sig The signature object
				 * @param {String} classPre The name of the CSS class to tag the item with
				 */
				function addElement(sig, classPre) {
					if(sig) {
						var item = {
							label: sig.sig,
							labelPost: sig.details,
							start: sig.range[0],
							end: sig.range[1]
						};
						if(classPre) {
							item.classNamePre = classPre;
						}
						if(scope.length < 1) {
							outline.push(item);
						}
						else {
							var parent = scope[scope.length-1];
							if(!parent.children) {
								parent.children = [];
							}
							parent.children.push(item);
						}
						return item;
					}
				}
				function _isOutlinedExpression(node) {
					return node.type === Estraverse.Syntax.FunctionExpression
						|| node.type === Estraverse.Syntax.ObjectExpression
						|| node.type === Estraverse.Syntax.ClassExpression
						|| node.type === Estraverse.Syntax.ArrowFunctionExpression;
				}
				Estraverse.traverse(file.ast, {
					/**
					 * @description Callback from estraverse when a node is starting to be visited
					 * @function
					 * @private
					 * @param {Object} node The AST node currently being visited
					 */
					enter: function(node) {
						var item;
						switch(node.type) {
							case Estraverse.Syntax.ClassDeclaration : {
								item = addElement(Signatures.computeSignature(node), Signatures.CLASS_IMAGE);
								if(item) {
									scope.push(item);
								}
								delete node.sig;
								break;
							}
							case Estraverse.Syntax.ClassExpression : {
								item = addElement(Signatures.computeSignature(node), Signatures.CLASS_IMAGE);
								if(item) {
									scope.push(item);
								}
								delete node.sig;
								break;
							}
							case Estraverse.Syntax.MethodDefinition: {
								item = addElement(Signatures.computeSignature(node), Signatures.FUNCTION_IMAGE);
								if(item) {
									scope.push(item);
								}
								node.value.md = true; //tag the function expression to know to skip
								delete node.sig;
								break;
							}
							case Estraverse.Syntax.FunctionDeclaration: {
								item = addElement(Signatures.computeSignature(node), Signatures.FUNCTION_IMAGE);
								if(item) {
									scope.push(item);
								}
								break;
							}
							case Estraverse.Syntax.FunctionExpression: {
								if(node.md) {
									delete node.sig;
									break;
								}
								var sig = Signatures.computeSignature(node);
								var details = Signatures.getCalleeSignature(node);
								if (details){
									sig.details = ' - ' + details;
								}
								item = addElement(sig, Signatures.FUNCTION_IMAGE);
								if(item) {
									scope.push(item);
								}
								delete node.sig;
								break;
							}
							case Estraverse.Syntax.ArrowFunctionExpression : {
								sig = Signatures.computeSignature(node);
								details = Signatures.getCalleeSignature(node);
								if (details){
									sig.details = ' - ' + details;
								}
								item = addElement(sig, Signatures.FUNCTION_IMAGE);
								if (item) {
									scope.push(item);
								}
								delete node.sig;
								break;
							}
							case Estraverse.Syntax.ObjectExpression: {
								item = addElement(Signatures.computeSignature(node), Signatures.OBJECT_IMAGE);
								if(item) {
									scope.push(item);
								}
								delete node.sig;
								if(node.properties) {
									node.properties.forEach(function(property) {
										if(property.value) {
											if(_isOutlinedExpression(property.value)) {
												property.value.sig = Signatures.computeSignature(property);
											}
											else {
												addElement(Signatures.computeSignature(property));
											}
										}
									});
								}
								break;
							}
							case Estraverse.Syntax.VariableDeclaration: {
								if(node.declarations) {
									node.declarations.forEach(function(declaration) {
										if(declaration.init) {
											if(declaration.init.type === Estraverse.Syntax.ObjectExpression) {
												declaration.init.sig = Signatures.computeSignature(declaration, node.kind);
											}
										}
									});
								}
								break;
							}
							case Estraverse.Syntax.AssignmentExpression: {
								if(node.left && node.right) {
									if(_isOutlinedExpression(node.right)) {
										node.right.sig = Signatures.computeSignature(node);
									}
								}
								break;
							}
							case Estraverse.Syntax.ReturnStatement: {
								if(node.argument) {
									if(_isOutlinedExpression(node.argument)) {
										node.argument.sig = Signatures.computeSignature(node);
									}
								}
								break;
							}
						}
					},
					
					/**
					 * @description Callback from estraverse when visitation of a node has completed
					 * @function
					 * @private
					 * @param {Object} node The AST node that ended its visitation
					 */
					leave: function(node) {
						switch(node.type) {
							case Estraverse.Syntax.ObjectExpression :
							case Estraverse.Syntax.FunctionDeclaration :
							case Estraverse.Syntax.ClassDeclaration :
							case Estraverse.Syntax.ClassExpression :
							case Estraverse.Syntax.MethodDefinition :
							case Estraverse.Syntax.ArrowFunctionExpression : {
								scope.pop();
								break;
							}
							case Estraverse.Syntax.FunctionExpression : {
								if(node.md) {
									delete node.md;
									break;
								}
								scope.pop();
								break;
							}
						}
					}
				});
			}
			return outline;
		}
	});
});
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd*/
define('javascript/ternPlugins/plugins',[
	"tern/lib/tern"
], function(tern) {
	
	tern.registerPlugin('plugins', /* @callback */ function(server, options) { //$NON-NLS-1$
		return {};
	});
	
	tern.defineQueryType('installed_plugins', { //$NON-NLS-1$
		/**
		 * @callback
		 */
		run: function run(server, query) {
			if(server.options && typeof server.options.plugins === 'object') {
				return server.options.plugins;
			}
			return null;
		}
	});
	
	tern.defineQueryType('installed_defs', { //$NON-NLS-1$
		/**
		 * @callback
		 */
		run: function run(server, query) {
			if(server.options && typeof server.options.defs === 'object') {
				return server.options.defs;
			}
			return null;
		}
	});
	
	tern.defineQueryType('environments', { //$NON-NLS-1$
		/**
		 * @callback
		 */
		run: function run(server, query) {
			if(server.options && typeof server.options.plugins === 'object') {
				var plugins = server.options.plugins;
				var keys = Object.keys(plugins);
				var envs = Object.create(null);
				for(var i = 0; i < keys.length; i++) {
					var key = keys[i];
					var env = plugins[key].env;
					if(env) {
						envs[env] = true;
					} else {
						envs[plugins[key]] = true;
					}
				}
				return envs;
			}
			return null;
		}
	});
}); 
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env node, amd*/
/*globals infer tern resolver*/
/**
 * Tern type index and templates for PostgreSQL node support
 */
define('javascript/ternPlugins/postgres',[
	"tern/lib/tern",
	"javascript/finder",
	"i18n!javascript/nls/messages"
], function(tern, Finder, Messages) {

	var templates = [
	/* eslint-disable missing-nls */
		{
			name: "postgres", 
			nodes: {top:true, member:false, prop:false},
			template: "var pg = require('pg');\n",
			doc: Messages['postgresRequire'],
			url: "https://github.com/brianc/node-postgres/wiki"
		},
		{
			name: "postgres client", 
			nodes: {top:true, member:false, prop:false},
			template: "var pg = require('pg');\n" + 
					  "var url = \"postgres://postgres:${port}@${host}/${database}\";\n" +  
					  "var ${client} = new pg.Client(url);\n",
			doc: Messages['postgresClient'],
			url: "https://github.com/brianc/node-postgres/wiki"
		},
		{
			name: "postgres connect", 
			nodes: {top:true, member:false, prop:false},
			template: "var pg = require('pg');\n" + 
					  "var url = \"postgres://postgres:${port}@${host}/${database}\";\n" +  
					  "var ${client} = new pg.Client(url);\n" + 
					  "${client}.connect(function(error) {\n" +  
					  "\t${cursor}\n" +  
					  "});\n",
			doc: Messages['postgresConnect'],
			url: "https://github.com/brianc/node-postgres/wiki"
		},
		{
			name: "postgres query", 
			nodes: {top:true, member:false, prop:false},
			template: "${client}.query(${sql}, function(error, result) {\n" + 
					  "\t${cursor}\n" +  
					  "});\n",
			doc: Messages['postgresQuery'],
			url: "https://github.com/brianc/node-postgres/wiki"
		}
		/* eslint-enable missing-nls */
	];
	
	var cachedQuery;
	
	/* eslint-enable missing-nls */
	tern.registerPlugin("postgres", /* @callback */ function(server, options) { //$NON-NLS-1$
	    return {
	      defs : defs,
	      passes: {
	      	/**
	      	 * @callback
	      	 */
	      	completion: function(file, query) {
	      		cachedQuery = query;
	      	},
	      	/**
	      	 * @callback
	      	 */
	      	variableCompletion: function(file, start, end, gather) {
	      		if(cachedQuery.includeTemplates || cachedQuery.includeTemplates === undefined) {
		      		var kind = Finder.findCompletionKind(file.ast, end);
		      		if(kind && kind.kind) {
			      		var tmpls = Finder.findTemplatesForKind(templates, kind.kind, cachedQuery.ecma ? cachedQuery.ecma : 6);
			      		tmpls.forEach(function(template) {
							gather(template.name, null, 0, function(c) {
								c.template = template.template;
								c.description = template.description;
								c.doc = template.doc;
								c.url = template.url;
								c.type = 'template'; //$NON-NLS-1$
								c.ecma = template.ecma;
								c.origin = 'pg';
								c.overwrite = true;
							});
						});
			      	}
		      	}
	      	}
	      }
	    };
	});
	
	/* eslint-disable missing-nls */
	var defs = {
	  "pg": {
	    "connect": "fn(connection: string, callback: fn(err: Error, client: Client, done: fn()))",
	    "end": "fn()",
	    "ConnectionConfig": {
	      "user": "string",
	      "database": "string",
	      "password": "string",
	      "port": "number",
	      "host": "string"
	    },
	    "Defaults": {
	      "poolSize": "number",
	      "poolIdleTimeout": "number",
	      "reapIntervalMillis": "number",
	      "binary": "bool",
	      "parseInt8": "bool"
	    },
	    "ClientConfig": {
	      "ssl": "bool"
	    },
	    "QueryConfig": {
	      "name": "string",
	      "text": "string",
	      "values": "[?]"
	    },
	    "QueryResult": {
	      "rows": "[?]"
	    },
	    "ResultBuilder": {
	      "command": "string",
	      "rowCount": "number",
	      "oid": "number",
	      "addRow": "fn(row: ?)"
	    },
	    "Client": {
	      "!type": "fn(connection: string)",
	      "prototype": {
	        "connect": "fn(callback?: fn(err: Error))",
	        "end": "fn()",
	        "query": "fn(queryText: string, callback?: fn(err: Error, result: QueryResult)) -> Query",
	        "copyFrom": "fn(queryText: string) -> stream.Writable",
	        "copyTo": "fn(queryText: string) -> stream.Readable",
	        "pauseDrain": "fn()",
	        "resumeDrain": "fn()",
	        "on": "fn(event: string, listener: fn()) -> Client"
	      }
	    },
	    "Query": {
	      "prototype": {
	        "on": "fn(event: string, listener: fn(row: ?, result?: ResultBuilder)) -> Query"
	      }
	    },
	    "Events": {
	      "prototype": {
	        "on": "fn(event: string, listener: fn(err: Error, client: Client)) -> Events"
	      }
	    }
	  },
	  "!name": "pg",
	  "!define": {
	  	"!known_modules": {
	  		"pg": {
	  			"connect": "fn(connection: string, callback: fn(err: Error, client: Client, done: fn()))",
	    		"end": "fn()",
	    		"Client": "pg.Client"
	  		}
	  	}
	  }
	};
});
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env node, amd*/
/*globals infer tern resolver*/
define('javascript/ternPlugins/redis',[
	"tern/lib/tern",
	"javascript/finder",
	"i18n!javascript/nls/messages"
], function(tern, Finder, Messages) {

	var templates = [
	/* eslint-disable missing-nls */
		{
			name: "redis",
			nodes: {top:true, member:false, prop:false},
			template: "var ${name} = require('redis');\n",
			doc: Messages['redisRequire'],
			url: "https://github.com/NodeRedis/node_redis#api"
		},
		{
			name: "redis client",
			nodes: {top:true, member:false, prop:false},
			template: "var ${name} = require('redis');\n" +
					  "var ${client} = ${name}.createClient(${port}, ${host}, ${options});\n",
			doc: Messages['redisClient'],
			url: "https://github.com/NodeRedis/node_redis#api"
		},
		{
			name: "redis connect",
			nodes: {top:true, member:false, prop:false},
			template: "var ${name} = require('redis');\n" +
					  "var ${client} = ${name}.createClient(${port}, ${host}, ${options});\n" + 
				  "try {\n" + 
					  "\t${cursor}\n"+ 
					  "} finally {\n"+ 
					  "\t${client}.close();\n"+ 
				  "}\n",
			doc: Messages['redisConnect'],
			url: "https://github.com/NodeRedis/node_redis#api"
		},
		{
			name: "redis set",
			nodes: {top:true, member:false, prop:false},
			template: "client.set(${key}, ${value});\n",
			doc: Messages['redisSet'],
			url: "https://github.com/NodeRedis/node_redis#api"
		},
		{
			name: "redis get",
			nodes: {top:true, member:false, prop:false},
			template: "client.get(${key}, function(${error}, ${reply}) {\n"+ 
					  "\t${cursor}\n" + 
					  "});\n",
			doc: Messages['redisGet'],
			url: "https://github.com/NodeRedis/node_redis#api"
		},
		{
			name: "redis on",
			nodes: {top:true, member:false, prop:false},
			template: "client.on(${event}, function(${arg}) {\n"+ 
					  "\t${cursor}" + 
					  "});\n",
			doc: Messages['redisOn'],
			url: "https://github.com/NodeRedis/node_redis#api"
		}
		/* eslint-enable missing-nls */
	];
	
	var cachedQuery;
	
	tern.registerPlugin("redis", /* @callback */ function(server, options) { //$NON-NLS-1$
	    return {
	      defs : defs,
	      passes: {
	      	/**
	      	 * @callback
	      	 */
	      	completion: function(file, query) {
	      		cachedQuery = query;
	      	},
	      	/**
	      	 * @callback
	      	 */
	      	variableCompletion: function(file, start, end, gather) {
	      		if(cachedQuery.includeTemplates || cachedQuery.includeTemplates === undefined) {
		      		var kind = Finder.findCompletionKind(file.ast, end);
		      		if(kind && kind.kind) {
			      		var tmpls = Finder.findTemplatesForKind(templates, kind.kind, cachedQuery.ecma ? cachedQuery.ecma : 6);
			      		tmpls.forEach(function(template) {
							gather(template.name, null, 0, function(c) {
								c.template = template.template;
								c.description = template.description;
								c.doc = template.doc;
								c.url = template.url;
								c.type = 'template'; //$NON-NLS-1$
								c.ecma = template.ecma;
								c.origin = 'redis';
								c.overwrite = true;
							});
						});
			      	}
		      	}
	      	}
	      }
	    };
	});
	
	/* eslint-disable missing-nls */
	var defs = {
		"!name": "redis",
		"!define": {
			"!known_modules": {
				"redis": {
					"createClient": "fn(port_arg: number, host_arg?: string, options?: ClientOpts) -> RedisClient",
					"print": "fn(err: Error, reply: ?)",
					"debug_mode": "bool",
					"ClientOpts": "redis.ClientOpts"
				}
			}
		},
	    "ClientOpts": {
	      "parser": "string",
	      "return_buffers": "bool",
	      "detect_buffers": "bool",
	      "socket_nodelay": "bool",
	      "no_ready_check": "bool",
	      "enable_offline_queue": "bool",
	      "retry_max_delay": "number",
	      "connect_timeout": "number",
	      "max_attempts": "number",
	      "auth_pass": "string"
	    },
	    "RedisClient": {
	      "on": "fn(event: string, action: fn())",
	      "connected": "bool",
	      "retry_delay": "number",
	      "retry_backoff": "number",
	      "command_queue": "[?]",
	      "offline_queue": "[?]",
	      "server_info": "ServerInfo",
	      "end": "fn()",
	      "auth": "fn(password: string, callback?: ?)",
	      "ping": "fn(callback?: ?)",
	      "append": "fn(key: string, value: string, callback?: ?)",
	      "bitcount": "fn(key: string, callback?: ?)",
	      "set": "fn(key: string, value: string, callback?: ?)",
	      "get": "fn(key: string, callback?: ?)",
	      "exists": "fn(key: string, value: string, callback?: ?)",
	      "publish": "fn(channel: string, value: ?)",
	      "subscribe": "fn(channel: string)",
	      "setnx": "fn(args: [?], callback?: ?)",
	      "setex": "fn(args: [?], callback?: ?)",
	      "strlen": "fn(args: [?], callback?: ?)",
	      "del": "fn(args: [?], callback?: ?)",
	      "setbit": "fn(args: [?], callback?: ?)",
	      "getbit": "fn(args: [?], callback?: ?)",
	      "setrange": "fn(args: [?], callback?: ?)",
	      "getrange": "fn(args: [?], callback?: ?)",
	      "substr": "fn(args: [?], callback?: ?)",
	      "incr": "fn(args: [?], callback?: ?)",
	      "decr": "fn(args: [?], callback?: ?)",
	      "mget": "fn(args: [?], callback?: ?)",
	      "rpush": "fn(args: [?])",
	      "lpush": "fn(args: [?], callback?: ?)",
	      "rpushx": "fn(args: [?], callback?: ?)",
	      "lpushx": "fn(args: [?], callback?: ?)",
	      "linsert": "fn(args: [?], callback?: ?)",
	      "rpop": "fn(args: [?], callback?: ?)",
	      "lpop": "fn(args: [?], callback?: ?)",
	      "brpop": "fn(args: [?], callback?: ?)",
	      "brpoplpush": "fn(args: [?], callback?: ?)",
	      "blpop": "fn(args: [?], callback?: ?)",
	      "llen": "fn(args: [?], callback?: ?)",
	      "lindex": "fn(args: [?], callback?: ?)",
	      "lset": "fn(args: [?], callback?: ?)",
	      "lrange": "fn(args: [?], callback?: ?)",
	      "ltrim": "fn(args: [?], callback?: ?)",
	      "lrem": "fn(args: [?], callback?: ?)",
	      "rpoplpush": "fn(args: [?], callback?: ?)",
	      "sadd": "fn(args: [?], callback?: ?)",
	      "srem": "fn(args: [?], callback?: ?)",
	      "smove": "fn(args: [?], callback?: ?)",
	      "sismember": "fn(args: [?], callback?: ?)",
	      "scard": "fn(args: [?], callback?: ?)",
	      "spop": "fn(args: [?], callback?: ?)",
	      "srandmember": "fn(args: [?], callback?: ?)",
	      "sinter": "fn(args: [?], callback?: ?)",
	      "sinterstore": "fn(args: [?], callback?: ?)",
	      "sunion": "fn(args: [?], callback?: ?)",
	      "sunionstore": "fn(args: [?], callback?: ?)",
	      "sdiff": "fn(args: [?], callback?: ?)",
	      "sdiffstore": "fn(args: [?], callback?: ?)",
	      "smembers": "fn(args: [?], callback?: ?)",
	      "zadd": "fn(args: [?], callback?: ?)",
	      "zincrby": "fn(args: [?], callback?: ?)",
	      "zrem": "fn(args: [?], callback?: ?)",
	      "zremrangebyscore": "fn(args: [?], callback?: ?)",
	      "zremrangebyrank": "fn(args: [?], callback?: ?)",
	      "zunionstore": "fn(args: [?], callback?: ?)",
	      "zinterstore": "fn(args: [?], callback?: ?)",
	      "zrange": "fn(args: [?], callback?: ?)",
	      "zrangebyscore": "fn(args: [?], callback?: ?)",
	      "zrevrangebyscore": "fn(args: [?], callback?: ?)",
	      "zcount": "fn(args: [?], callback?: ?)",
	      "zrevrange": "fn(args: [?], callback?: ?)",
	      "zcard": "fn(args: [?], callback?: ?)",
	      "zscore": "fn(args: [?], callback?: ?)",
	      "zrank": "fn(args: [?], callback?: ?)",
	      "zrevrank": "fn(args: [?], callback?: ?)",
	      "hset": "fn(args: [?], callback?: ?)",
	      "hsetnx": "fn(args: [?], callback?: ?)",
	      "hget": "fn(args: [?], callback?: ?)",
	      "hmset": "fn(args: [?], callback?: ?)",
	      "hmget": "fn(args: [?], callback?: ?)",
	      "hincrby": "fn(args: [?], callback?: ?)",
	      "hdel": "fn(args: [?], callback?: ?)",
	      "hlen": "fn(args: [?], callback?: ?)",
	      "hkeys": "fn(args: [?], callback?: ?)",
	      "hvals": "fn(args: [?], callback?: ?)",
	      "hgetall": "fn(args: [?], callback?: ?)",
	      "hexists": "fn(args: [?], callback?: ?)",
	      "incrby": "fn(args: [?], callback?: ?)",
	      "decrby": "fn(args: [?], callback?: ?)",
	      "getset": "fn(args: [?], callback?: ?)",
	      "mset": "fn(args: [?], callback?: ?)",
	      "msetnx": "fn(args: [?], callback?: ?)",
	      "randomkey": "fn(args: [?], callback?: ?)",
	      "select": "fn(args: [?], callback?: ?)",
	      "move": "fn(args: [?], callback?: ?)",
	      "rename": "fn(args: [?], callback?: ?)",
	      "renamenx": "fn(args: [?], callback?: ?)",
	      "expire": "fn(args: [?], callback?: ?)",
	      "expireat": "fn(args: [?], callback?: ?)",
	      "keys": "fn(args: [?], callback?: ?)",
	      "dbsize": "fn(args: [?], callback?: ?)",
	      "echo": "fn(args: [?], callback?: ?)",
	      "save": "fn(args: [?], callback?: ?)",
	      "bgsave": "fn(args: [?], callback?: ?)",
	      "bgrewriteaof": "fn(args: [?], callback?: ?)",
	      "shutdown": "fn(args: [?], callback?: ?)",
	      "lastsave": "fn(args: [?], callback?: ?)",
	      "type": "fn(args: [?], callback?: ?)",
	      "multi": "fn(args: [?], callback?: ?)",
	      "exec": "fn(args: [?], callback?: ?)",
	      "discard": "fn(args: [?], callback?: ?)",
	      "sync": "fn(args: [?], callback?: ?)",
	      "flushdb": "fn(args: [?], callback?: ?)",
	      "flushall": "fn(args: [?], callback?: ?)",
	      "sort": "fn(args: [?], callback?: ?)",
	      "info": "fn(args: [?], callback?: ?)",
	      "monitor": "fn(args: [?], callback?: ?)",
	      "ttl": "fn(args: [?], callback?: ?)",
	      "persist": "fn(args: [?], callback?: ?)",
	      "slaveof": "fn(args: [?], callback?: ?)",
	      "debug": "fn(args: [?], callback?: ?)",
	      "config": "fn(args: [?], callback?: ?)",
	      "unsubscribe": "fn(args: [?], callback?: ?)",
	      "psubscribe": "fn(args: [?], callback?: ?)",
	      "punsubscribe": "fn(args: [?], callback?: ?)",
	      "watch": "fn(args: [?], callback?: ?)",
	      "unwatch": "fn(args: [?], callback?: ?)",
	      "cluster": "fn(args: [?], callback?: ?)",
	      "restore": "fn(args: [?], callback?: ?)",
	      "migrate": "fn(args: [?], callback?: ?)",
	      "dump": "fn(args: [?], callback?: ?)",
	      "object": "fn(args: [?], callback?: ?)",
	      "client": "fn(args: [?], callback?: ?)",
	      "eval": "fn(args: [?], callback?: ?)",
	      "evalsha": "fn(args: [?], callback?: ?)",
	      "quit": "fn(args: [?], callback?: ?)"
	    },
	    "createClient": "fn(port_arg: number, host_arg?: string, options?: ClientOpts) -> RedisClient",
	    "print": "fn(err: Error, reply: ?)",
	    "debug_mode": "bool",
	    "MessageHandler": {},
	    "ServerInfo": {
	      "redis_version": "string",
	      "versions": "[number]"
	    }
		};
});
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd*/
/*globals infer tern walk*/
define('javascript/ternPlugins/refs',[
	"tern/lib/infer", 
	"tern/lib/tern", 
	"javascript/finder"
], function(infer, tern, finder) {
	
	tern.registerPlugin('refs', /* @callback */ function(server, options) { //$NON-NLS-1$
		return {};
	});
		
	tern.defineQueryType('checkRef', { //$NON-NLS-1$
		takesFile: true,
		/**
		 * @callback
		 */
		run: function run(server, query, file) {
			return doCheck(query, file, server);
		}
	});
	
	function doCheck(query, file, server) {
		var comment = finder.findComment(query.end, file.ast), result;
		if(comment) {
			result = {
		    	guess: false,
		        type: undefined,
		        name: undefined,
		        category: comment.type === 'Block' ? 'blockcomments': 'linecomments' //$NON-NLS-1$ //$NON-NLS-2$
		    };
		} else {
			var expr = tern.findQueryExpr(file, query), exprName, type, exprType;
			try {
			    type = tern.findExprType(server, query, file, expr);
			    exprType = type;
			    if (query.preferFunction) {
					type = type.getFunctionType() || type.getType();
				} else {
					type = type.getType();
				}
			    if (expr) {
					if (expr.node.type === "Identifier") {
			        	exprName = expr.node.name;
		        	} else if (expr.node.type === "MemberExpression" && !expr.node.computed) {
			        	exprName = expr.node.property.name;
		        	}
			    }
		    }
			catch(er) {
				//do nothing tag the result later and do a static check
			}
		    result = {
		    	guess: infer.didGuess(),
		        type: infer.toString(exprType),
		        name: type && type.name,
		        exprName: exprName
		    };
		    categorize(query, file, result);
		    if (type) {
		    	tern.storeTypeDocs(query, type, result);
	    	} else {
	    		staticCheck(query, file, result);
	    	}
		    if (!result.doc && exprType && exprType.doc) {
		    	result.doc = tern.parseDoc(query, exprType.doc);
			}
	    }
	    return result;
	}
	
	/**
	 * @description Tags the match with the category is belongs to
	 * @function
	 * @private
	 * @param {Object} node The AST node
	 * @param {Object} result The result
	 */
	function categorize(query, file, result) {
		if(Array.isArray(file.ast.errors) && file.ast.errors.length > 0) {
			result.category = 'parseerrors'; //$NON-NLS-1$
			return;
		}
		var node = finder.findNode(query.end-1, file.ast, {parents:true});
		if(node) {
			if(node.type === 'Identifier') {
				var p = node.parents.pop();
				p.parents = node.parents;
				node = p;
			} 
			switch(node.type) {
				case 'Program': {
					result.category = 'uncategorized'; //$NON-NLS-1$
					break;
				}
				case 'FunctionDeclaration':
				case 'FunctionExpression': {
					for(var i = 0, len = node.params.length; i< len; i++) {
						if(encloses(query.end, node.params[i])) {
							result.category = 'vardecls'; //$NON-NLS-1$
							break;
						}
					}
					if(!result.category) {
						result.category = 'funcdecls'; //$NON-NLS-1$
					}
					break;
				}
				case 'Property': {
					if(encloses(query.end, node.key)) {
						if(node.value && node.value.type === 'FunctionExpression') {
							result.category = 'funcdecl'; //$NON-NLS-1$
						} else {
							result.category = 'propwrite'; //$NON-NLS-1$
						}
					} else if(encloses(query.end, node.value)) {
						if(node.value.type === 'FunctionExpression') {
							result.category = 'funcdecls'; //$NON-NLS-1$
						} else if(node.value.type === 'Identifier') {
							result.category = 'varaccess'; //$NON-NLS-1$
						} else {
							result.category = 'propwrite'; //$NON-NLS-1$
						}
					}
					break;
				}
				case 'CallExpression': {
					if(encloses(query.end, node.callee)) {
						result.category = 'funccalls'; //$NON-NLS-1$
					} 
					if(node.arguments.length > 0) {
						for(i = 0, len = node.arguments.length; i < len; i++) {
							var param = node.arguments[i];
							if(encloses(query.end, param)) {
								if(param.type === 'Identifier') {
									result.category = 'varaccess'; //$NON-NLS-1$
								} else if(param.type === 'MemberExpression') {
									result.category = 'propaccess'; //$NON-NLS-1$
								}
							}
						}
					}
					break;
				}
				case 'AssignmentExpression': {
					if(encloses(query.end, node.left)) {
						//on the left, write
						if(node.left.type === 'Identifier') {
							result.category = 'varwrite'; //$NON-NLS-1$
						} else {
							result.category = 'propwrite'; //$NON-NLS-1$
						}
					} else if(encloses(query.end, node.right)) {
						if(node.right.type === 'Identifier') {
							result.category = 'varaccess'; //$NON-NLS-1$
						} else if(node.right.type === 'MemberExpression') {
							result.category = 'propaccess'; //$NON-NLS-1$
						} 
					}
					break;
				}
				case 'VariableDeclarator': {
					if(encloses(query.end, node.id)) {
						result.category = 'vardecls'; //$NON-NLS-1$
					} else if(encloses(query.end, node.init)) {
						result.category = 'varaccess';						 //$NON-NLS-1$
					}
					break;
				}
				case 'Literal': {
					if(node.regex) {
						result.category = 'regex'; //$NON-NLS-1$
					} else if(typeof(node.value) === "string") {
						result.category = 'strings'; //$NON-NLS-1$
					}
					break;
				}
				case 'NewExpression': {
					if(node.callee && encloses(query.end, node.callee)) {
						result.category = 'funccalls'; //$NON-NLS-1$
					}
					break;
				}
				case 'MemberExpression': {
					//if we are talking about the root object, it will be a var access
					if(node.object && node.object.type === 'Identifier' && encloses(query.end, node.object)) {
						result.category = 'varaccess'; //$NON-NLS-1$
						break;
					}
					var prop;
					//walk up to find first non-member expression
					while(node.type === 'MemberExpression') {
						prop = node.property;
						p = node.parents.pop();
						p.parents = node.parents;
						node = p;
					}
					if(node && (node.type === 'CallExpression' || node.type === 'NewExpression') && encloses(query.end, prop)) {
						if(node.callee && encloses(query.end, node.callee)) {
							result.category = 'funccalls'; //$NON-NLS-1$
						} else if(node.arguments && node.arguments.length > 0) {
							//check args
							for(i = 0, len = node.arguments.length; i < len; i++) {
								if(encloses(query.end, node.arguments[i])) {
									result.category = 'propaccess'; //$NON-NLS-1$
								}
							}
						}
						
					} else if(node && node.type === 'AssignmentExpression') {
						if(encloses(query.end, node.left)) {
							if(node.right && node.right.type === 'FunctionExpression') {
								result.category = 'funcdecls'; //$NON-NLS-1$
							} else if(encloses(query.end, prop)) {
								result.category = 'propwrite'; //$NON-NLS-1$
							} else {
								result.category = 'propaccess'; //$NON-NLS-1$
							}
						} else {
							result.category = 'propaccess'; //$NON-NLS-1$
						}
					} else {
						result.category = 'propaccess'; //$NON-NLS-1$
					}
					break;
				}
				case 'UpdateExpression': {
					if(node.argument.type === 'Identifier') {
						result.category = 'varaccess'; //$NON-NLS-1$
					} else if(node.argument.type === 'MemberExpression') {
						result.category = 'propaccess'; //$NON-NLS-1$
					}
					break;
				}
				case 'BinaryExpression': {
					if(node.left.type === 'Identifier' && encloses(query.end, node.left)) {
						result.category = 'varaccess'; //$NON-NLS-1$
					} else if(node.right.type === 'Identifier' && encloses(query.end, node.right)) {
						result.category = 'varaccess'; //$NON-NLS-1$
					}
					break;
				}
				case 'BreakStatement':
				case 'ConditionalExpression':
				case 'ContinueStatement':
				case 'IfStatement': 
				case 'DoWhileStatement':
				case 'ForInStatement':
				case 'ForStatement':
				case 'LogicalExpression':
				case 'SwitchStatement':
				case 'SwitchCase':
				case 'WithStatement': 
				case 'WhileStatement': {
					result.category = 'varaccess'; //$NON-NLS-1$
					break;
				}
				case 'LetStatement':
				case 'LabeledStatement': {
					result.category = 'varwrite'; //$NON-NLS-1$
					break;
				}
				case 'Block': {
					result.category = 'blockcomments'; //$NON-NLS-1$
					break;
				}
				case 'Line': {
					result.category = 'linecomments'; //$NON-NLS-1$
					break;
				}
				case 'UnaryExpression': {
					if(node.argument && encloses(query.end, node.argument)) {
						result.category = 'varaccess'; //$NON-NLS-1$
					}
					break;
				}
			}
		}
		if(!result.category) {
			result.category = 'uncategorized'; //$NON-NLS-1$
		}
	}
	
	function encloses(offset, node) {
		return node && (node.range[0] <= offset && offset <= node.range[1]);
	}
	
	function staticCheck(query, file, result) {
		var node = finder.findNode(query.end, file.ast, {parents: true});
		if(node) {
			checkNode(query, node, result);
		} else {
			result.staticCheck = {
				confidence: 0
			};
		}
	}
		
	function checkNode(query, node, result) {
		switch(node.type) {
			case 'FunctionDeclaration':
			case 'FunctionExpression':
			case 'VariableDeclarator': 
			case 'Literal': {
				//a re-decl cannot be a reference
				result.staticCheck = {
					confidence: 0
				};
				break;
			}
			case 'Identifier': {
				if(Array.isArray(node.parents)) {
					var p = node.parents.slice(node.parents.length-1)[0];
					checkNode(query, p, result);
				} else {
					result.staticCheck = {
						confidence: 25
					};
				}
				break;
			}
			case 'AssignmentExpression': {
				if(node.left.type === 'Identifier' && node.left.name === query.origin.type.exprName) {
					result.staticCheck = {
						confidence: 25
					};
				} else if(node.right.type === 'Identifier' && node.right.name === query.origin.type.exprName) {
					result.staticCheck = {
						confidence: 25
					};
				} else {
					//TODO catch all
					result.staticCheck = {
						confidence: 5
					};
				}
				break;
			}
			case 'MemberExpression': {
				//if part of the expression, maybe relevant
				result.staticCheck = {
					confidence: 10
				};
				break;
			}
			case 'CallExpression': {
				if(node.callee.name === query.origin.type.exprName) {
					if(query.origin.type.type === 'fn()') {
						result.staticCheck = {
							confidence: 25
						};
					} else {
						result.staticCheck = {
							confidence: 0
						};
					}
				}
				for(var i = 0, l = node.arguments.length; i < l; i++) {
					var arg = node.arguments[i];
					if(arg.type === 'Identifier') {
						if(query.origin.type.type === 'fn()') {
							//orig type is function, this is not relevant
							result.staticCheck = {
								confidence: 0
							};
						} else {
							//with no type infos we have no idea if this is the same one
							result.staticCheck = {
								confidence: 40
							};
						}
					} else if(arg.type === 'FunctionExpression') {
						if(arg.id === query.origin.type.exprName) {
							//redecl, not relevant
							result.staticCheck = {
								confidence: 0
							};
						}
					}
				}
				break;
			}
			default: {
				result.staticCheck = {
					confidence: 0
				};
			}
		}
	}
}); 

/*******************************************************************************
 * @license
 * Copyright (c) 2016, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (https://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (https://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - Allow original requirejs plugin to find files in Orion workspace
 *******************************************************************************/
/* eslint-disable missing-nls */
/*eslint-env node, amd*/
/*globals tern tern */
define('javascript/ternPlugins/templates',[
	"tern/lib/tern",
	"javascript/finder",
	"i18n!javascript/nls/messages"
], function(tern, Finder, Messages) {
	
	var typeofValues = {
		type: "link", //$NON-NLS-1$
		values: [
			"boolean", //$NON-NLS-1$
			"function", //$NON-NLS-1$
			"number", //$NON-NLS-1$
			"object", //$NON-NLS-1$
			"string", //$NON-NLS-1$
			"symbol", //$NON-NLS-1$
			"undefined" //$NON-NLS-1$
		],
		title: Messages['typeofOptions'],
		style: 'emphasis' //$NON-NLS-1$
	};
	
	/**
	 * @description Array of template metadata objects. These get converted into
	 * {orion.editor.Template} objects lazily as they are asked for
	 * @private 
	 */
	var templates = [
		{
			prefix: "async", //$NON-NLS-1$
			name: "async function",  //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "async function ${name}(${param}) {\n"+
   					  "    ${cursor}\n"+
					  "}",
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function", 
			doc: Messages.asyncFunction,
			ecma: 9
		},
		{
			prefix: "async", //$NON-NLS-1$
			name: "async function expression",  //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "var ${name} = async function ${name}(${param}) {\n"+
   					  "    ${cursor}\n"+
					  "};",
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function", 
			doc: Messages.asyncFunctionExpression,
			ecma: 9
		},
		{
			prefix: "await", //$NON-NLS-1$
			name: "await statement",  //$NON-NLS-1$
			nodes: {top:true, assign: true, async:true, member:false, prop:false},
			template: "await ${statement};", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await", 
			doc: Messages.awaitExpression,
			ecma: 9
		},
		{
			prefix: "await", //$NON-NLS-1$
			name: "await return statement",  //$NON-NLS-1$
			nodes: {top:false, async:true, member:false, prop:false},
			template: "return await ${statement};", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await", 
			doc: Messages.awaitExpression,
			ecma: 9
		},
		{
			prefix: "await", //$NON-NLS-1$
			name: "await variable statement",  //$NON-NLS-1$
			nodes: {top:true, async:true, member:false, prop:false},
			template: "var ${variable} = await ${statement};", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await", 
			doc: Messages.awaitExpression,
			ecma: 9
		},
		{
			prefix: "if", //$NON-NLS-1$
			name: "if statement",  //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "if (${condition}) {\n\t${cursor}\n}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/if...else", 
			doc: Messages['ifSimple'],
			ecma: 5
		},
		{
			prefix: "if", //$NON-NLS-1$
			name: "if..else statement", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "if (${condition}) {\n\t${cursor}\n} else {\n\t\n}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/if...else", 
			doc: Messages['ifElseSimple'],
			ecma: 5
		},
		{
			prefix: "for", //$NON-NLS-1$
			name: "for statement", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "for (var ${i} = 0; ${i} < ${array}.length; ${i}++) {\n\t${cursor}\n}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for", 
			doc: Messages['forArray'],
			ecma: 5
		},
		{
			prefix: "for", //$NON-NLS-1$
			name: "for statement (with loop variable)", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "for (var ${i} = 0; ${i} < ${array}.length; ${i}++) {\n\tvar ${value} = ${array}[${i}];\n\t${cursor}\n}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for", 
			doc: Messages['forArrayVar'],
			ecma: 5
		},
		{
			prefix: "for", //$NON-NLS-1$
			name: "for..in statement", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "for (var ${property} in ${object}) {\n\tif (${object}.hasOwnProperty(${property})) {\n\t\t${cursor}\n\t}\n}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in", 
			doc: Messages['forInSimple'],
			ecma: 5
		},
		{
			prefix: "while", //$NON-NLS-1$
			name: "while statement", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "while (${condition}) {\n\t${cursor}\n}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while", 
			doc: Messages['whileSimple'],
			ecma: 5
		},
		{
			prefix: "do", //$NON-NLS-1$
			name: "do..while statement", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "do {\n\t${cursor}\n} while (${condition});", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while", 
			doc: Messages['doSimple'],
			ecma: 5
		},
		{
		    prefix: "eslint", //$NON-NLS-1$
			name: "eslint", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false, doc:false, jsdoc:false},
			template: "/* eslint ${rule-id}:${0/1}*/", //$NON-NLS-1$
			url: "https://eslint.org/docs/user-guide/configuring.html#configuring-rules", //$NON-NLS-1$
			doc: Messages['eslintRuleEnableDisable'],
			ecma: 5
		},
		{
		    prefix: "eslint-env", //$NON-NLS-1$
			name: "eslint-env", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false, doc:false, jsdoc:false},
			template: "/* eslint-env ${library}*/", //$NON-NLS-1$
			url: "https://eslint.org/docs/user-guide/configuring.html#specifying-environments", //$NON-NLS-1$
			doc: Messages['eslintEnvDirective'],
			ecma: 5
		},
		{
		    prefix: "eslint-enable", //$NON-NLS-1$
			name: "eslint-enable", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false, doc:false, jsdoc:false},
			template: "/* eslint-enable ${rule-id} */", //$NON-NLS-1$
			url: "https://eslint.org/docs/user-guide/configuring.html#configuring-rule", //$NON-NLS-1$
			doc: Messages['eslintRuleEnable'],
			ecma: 5
		},
		{
		    prefix: "eslint-disable", //$NON-NLS-1$
			name: "eslint-disable", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false, doc:false, jsdoc:false},
			template: "/* eslint-disable ${rule-id} */", //$NON-NLS-1$
			url: "https://eslint.org/docs/user-guide/configuring.html#configuring-rules", //$NON-NLS-1$
			doc: Messages['eslintRuleDisable'],
			ecma: 5
		},
		{
			prefix: "switch", //$NON-NLS-1$
			name: "switch statement", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "switch (${expression}) {\n\tcase ${value1}:\n\t\t${cursor}\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch", 
			doc: Messages['switchSimple'],
			ecma: 5
		},
		{
			prefix: "case", //$NON-NLS-1$
			name: "case statement", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false, swtch: true},
			template: "case ${value}:\n\t${cursor}\n\tbreak;", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch", 
			doc: Messages['caseSimple'],
			ecma: 5
		},
		{
			prefix: "try", //$NON-NLS-1$
			name: "try..catch statement", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "try {\n\t${cursor}\n} catch (${err}) {\n}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch", 
			doc: Messages['tryCatch'],
			ecma: 5
		},
		{
			prefix: "try", //$NON-NLS-1$
			name: "try..catch..finally statement", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "try {\n\t${cursor}\n} catch (${err}) {\n} \n finally {\n}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch", 
			doc: Messages['tryCatchFinally'],
			ecma: 5
		},
		{
			prefix: "typeof", //$NON-NLS-1$
			name: "typeof expression", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "typeof ${object} === \"${type:" + JSON.stringify(typeofValues).replace("}", "\\}") + "}\"", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof", 
			doc: Messages['typeofSimple'],
			ecma: 5
		},
		{
			prefix: "instanceof", //$NON-NLS-1$
			name: "instanceof expresion", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "${object} instanceof ${type}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof", 
			doc: Messages['instanceofSimple'],
			ecma: 5
		},
		{
			prefix: "with", //$NON-NLS-1$
			name: "with statement", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "with (${object}) {\n\t${cursor}\n}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with", 
			doc: Messages['withSimple'],
			ecma: 5
		},
		{
			prefix: "function", //$NON-NLS-1$
			name: "function declaration", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "/**\n"+  //$NON-NLS-1$
					  " * @param ${parameter}\n"+  //$NON-NLS-1$
					  " */\n"+  //$NON-NLS-1$
					  "function ${name} (${parameter}) {\n\t${cursor}\n}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function", 
			doc: Messages['functionSimple'],
			ecma: 5
		},
		{
			prefix: "function", //$NON-NLS-1$
			name: "function expression (as object property)", //$NON-NLS-1$
			nodes: {top:false, member:false, prop:false, obj:true},
			template: "/**\n"+  //$NON-NLS-1$
					  " * @name ${name}\n"+  //$NON-NLS-1$
					  " * @function\n"+  //$NON-NLS-1$
					  " * @param ${parameter}\n"+  //$NON-NLS-1$
					  " */\n"+  //$NON-NLS-1$
					  "${name}: function(${parameter}) {\n\t${cursor}\n}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function", 
			doc: Messages['functionProp'],
			ecma: 5
		},
		{
			prefix: "function", //$NON-NLS-0$
			name: "function expression (as property value)", //$NON-NLS-0$
			nodes: {top:false, member:false, prop:true, obj:false},
			template: "function(${parameter}) {\n\t${cursor}\n}", //$NON-NLS-0$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function", 
			doc: Messages['functionProp'],
			ecma: 5
		},
		{
			prefix: "define", //$NON-NLS-1$
			name: "define function call", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "/* eslint-env amd */\n"+ //$NON-NLS-1$
					  "define('${name}', [\n"+  //$NON-NLS-1$
					  "'${import}'\n"+  //$NON-NLS-1$
					  "], function(${importname}) {\n"+  //$NON-NLS-1$
					  "\t${cursor}\n"+  //$NON-NLS-1$
					  "});", //$NON-NLS-1$
			url: "https://requirejs.org/docs/api.html#deffunc", 
			doc: Messages['defineSimple'],
			ecma: 5
		},
		{
			prefix: "nls", //$NON-NLS-1$
			name: "nls comment", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "${cursor} //$NON-NLS-${0}$", //$NON-NLS-1$
			doc: Messages['nlsSimple'],
			ecma: 5
		},
		{
			prefix: "log", //$NON-NLS-1$
			name: "log function call", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "console.log(${object});", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en/docs/Web/API/Console/log", 
			doc: Messages['logSimple'],
			ecma: 5
		},
		{
		    prefix: "require", //$NON-NLS-1$
			name: "require function call", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false, doc:false, jsdoc:false},
			template: "var lib = require('${cursor}');", //$NON-NLS-1$
			url: "https://nodejs.org/api/modules.html#modules_modules", 
			doc: Messages['requireSimple'],
			ecma: 5
		},
		{
			prefix: "arrow", //$NON-NLS-1$
			name: "arrow expression",  //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "${param} => {${cursor}}", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions", //$NON-NLS-1$
			doc: Messages['arrowFunc'],
			ecma: 6
		},
		{
			prefix: "arrow", //$NON-NLS-1$
			name: "arrow expression with object",  //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "var ${name} = () => ({ ${prop}: ${val}${cursor} });", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions", //$NON-NLS-1$
			doc: Messages['arrowFuncObj'],
			ecma: 6
		},
		{
			prefix: "class", //$NON-NLS-1$
			name: "class definition", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "class ${name} extends ${superClass}; {\n"+
						"\tconstructor() {\n"+
						"\t\t${cursor}\n"+
						"\t}\n"+
						"}\n",
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes", //$NON-NLS-1$
			doc: Messages['classSimple'],
			ecma: 6
		},
		{
			prefix: "class", //$NON-NLS-1$
			name: "class expression", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "var ${name} = class ${className} extends ${superClass}; {\n"+
						"\tconstructor() {\n"+
						"\t\t${cursor}\n"+
						"\t}\n"+
						"}\n",
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/class", //$NON-NLS-1$
			doc: Messages['classExpr'],
			ecma: 6
		},
		{
			prefix: "const", //$NON-NLS-1$
			name: "const expression", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "const ${name} = ${value};", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const", //$NON-NLS-1$
			doc: Messages['constSimple'],
			ecma: 6
		},
		{
			prefix: "export", //$NON-NLS-1$
			name: "export statement", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "export ${value};", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export", //$NON-NLS-1$
			doc: Messages['exportSimple'],
			ecma: 6
		},
		{
			prefix: "export", //$NON-NLS-1$
			name: "export statement (as default)", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "export default ${value};", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export", //$NON-NLS-1$
			doc: Messages['exportDefault'],
			ecma: 6
		},
		{
			prefix: "for", //$NON-NLS-1$
			name: "for..of statement", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "for (${variable} of ${iterable}) {\n"+ //$NON-NLS-1$
  					  "    ${cursor}\n"+ //$NON-NLS-1$
					  "}",
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of", //$NON-NLS-1$
			doc: Messages['forOf'],
			ecma: 6
		},
		{
			prefix: "function*", //$NON-NLS-1$
			name: "function declaration (generator)", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "function* ${name}(${param}) {\n"+ //$NON-NLS-1$
   					  "    ${cursor}\n"+ //$NON-NLS-1$
					  "}",
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*", //$NON-NLS-1$
			doc: Messages['funcGenerator'],
			ecma: 6
		},
		{
			prefix: "import", //$NON-NLS-1$
			name: "import statement (default member)",  //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "import ${member} from \"${module}\";", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import", //$NON-NLS-1$
			doc: Messages['importSimpleDefault'],
			ecma: 6
		},
		{
			prefix: "import", //$NON-NLS-1$
			name: "import statement (all as star)",  //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "import * as ${name} from \"${module}\";", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import", //$NON-NLS-1$
			doc: Messages['importStarAs'],
			ecma: 6
		},
		{
			prefix: "import", //$NON-NLS-1$
			name: "import statement (multiple members)",  //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "import { ${member1} , ${member2} } from \"${module}\";", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import", //$NON-NLS-1$
			doc: Messages['importMultiMember'],
			ecma: 6
		},
		{
			prefix: "import", //$NON-NLS-1$
			name: "import statement (module only)",  //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "import \"${module}\"", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import", //$NON-NLS-1$
			doc: Messages['importSideEffects'],
			ecma: 6
		},
		{
			prefix: "let", //$NON-NLS-1$
			name: "let expression", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "let ${name} = ${value};", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let", //$NON-NLS-1$
			doc: Messages['letSimple'],
			ecma: 6
		},
		{
			prefix: "yield", //$NON-NLS-1$
			name: "yield expression", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "yield ${expression};", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield", //$NON-NLS-1$
			doc: Messages['yieldSimple'],
			ecma: 6
		},
		{
			prefix: "yield", //$NON-NLS-1$
			name: "yield expression with return", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "${value} = yield ${expression};", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield", //$NON-NLS-1$
			doc: Messages['yieldSimpleReturn'],
			ecma: 6
		},
		{
			prefix: "yield*", //$NON-NLS-1$
			name: "yield expression delegate", //$NON-NLS-1$
			nodes: {top:true, member:false, prop:false},
			template: "yield* ${gennerator};", //$NON-NLS-1$
			url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield*", //$NON-NLS-1$
			doc: Messages['yieldDelegate'],
			ecma: 6
		}
	];

	var cachedQuery;
	
	tern.registerPlugin("templates", /* @callback */ function(server, options) {
		return {
     		passes: {
     			/**
     			 * @callback
     			 */
     			completion: function(file, query) {
     				cachedQuery = query;
     			},
		      	/**
		      	 * @callback
		      	 */
		      	variableCompletion: function(file, start, end, gather) {
		      		if(cachedQuery.includeTemplates || cachedQuery.includeTemplates === undefined) {
			      		var kind = Finder.findCompletionKind(file.ast, end);
			      		if(kind && kind.kind) {
				      		var tmpls = Finder.findTemplatesForKind(templates, kind.kind, cachedQuery.ecma ? cachedQuery.ecma : 6);
				      		tmpls.forEach(function(template) {
				      			if(!kind.isasync && template.nodes.async === true) {
				      				return;
				      			}
								gather(template.name, null, 0, function(c) {
									c.template = template.template;
									c.doc = template.doc;
									c.url = template.url;
									c.type = 'template'; //$NON-NLS-1$
									c.ecma = template.ecma;
									c.overwrite = true;
								});
							});
				      	}
			      	}
		      	}
      		}
    	};
	});
});
/*******************************************************************************
 * @license
 * Copyright (c) 2016, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env amd*/
define('javascript/ternPlugins/quickfixes',[
	"javascript/finder",
	"tern/lib/tern"
], function(Finder, tern) {

	tern.registerPlugin("quickfixes", /* @callback */ function(server, options) { //$NON-NLS-1$
		return {
			//no pass hooks needed
		};
	});

	tern.defineQueryType("fixes", { //$NON-NLS-1$
		takesFile: true,
		/**
		 * @callback
		 */
		run: function(server, query, file) {
			return computeFixes(query, file);
		}
	});

	var availableFixes = {
		/**
		 * @description Fix for 'radix' problem
		 * @function
		 * @callback
		 */
		"radix": function(annotation, annotations, file) {
			return applySingleFixToAll(annotations, function(annot) {
				var node = Finder.findNode(annot.start, file.ast, {parents:true});
				if(node && node.type === 'Identifier') {
					node = node.parents[node.parents.length-1];
					if(node.type === 'CallExpression' && Array.isArray(node.arguments)) {
						var arg = node.arguments[node.arguments.length-1];
						return {text: ", 10", start: arg.range[1], end: arg.range[1]}; //$NON-NLS-1$
					}
				}
			});
		},
		/**
		 * @description Fix for 'curly' problem
		 * @function
		 * @callback
		 */
		"curly": function(annotation, annotations, file) {
			var tok = Finder.findToken(annotation.start, file.ast.tokens);
			if(tok) {
				tok = file.ast.tokens[tok.index-1];
				var idx = tok.range[1],
					start = tok.range[1],
					end = annotation.end,
					lineBreak = false;
				while(idx < annotation.start) {
					if(file.ast.sourceFile.text.charAt(idx) === '\n') {
						lineBreak = true;
						break;
					}
					idx++;
				}
				var text = ' {'+ file.ast.sourceFile.text.slice(start, end); //$NON-NLS-1$
				if(lineBreak) {
					var node = Finder.findNode(annotation.start, file.ast, {parents: true});
					if(node) {
						var p = node.parents[node.parents.length-1];
						var lineStart = getLineStart(file.ast.sourceFile.text, p.range[0]);
						var ctrl = getControlStatementParent(node);
						if(ctrl) {
							//compute the offset based on the control statement start if we can
							lineStart = getLineStart(file.ast.sourceFile.text, ctrl.range[0]);
						}
						//only preserve comments and whitespace at the end of the line
						//erroneous statements should not be enclosed
						var lineEnd = getLineEnd(file.ast.sourceFile.text, end);
						var preserved = '';
						var nodes = Finder.findNodesForRange(file.ast, end, lineEnd);
						if(lineEnd > end && nodes.length < 1) {
							preserved += file.ast.sourceFile.text.slice(end, lineEnd);
							end = lineEnd;
						}
						text += preserved+'\n'+computeIndent(file.ast.sourceFile.text, lineStart, 0)+'}'; //$NON-NLS-1$
					}
				} else {
					text += ' }'; //$NON-NLS-1$
				}
				return {text: text, start: start, end: end};
			}
		},
		/**
		 * @description Fix for 'no-new-wrappers' problem
		 * @function
		 * @callback
		 */
		"no-new-wrappers": function(annotation, annotations, file) {
			var node = Finder.findNode(annotation.start, file.ast, {parents:true});
			if(node) {
				var parent = node.parents[node.parents.length-1];
				if(parent.type === 'NewExpression') {
					var tok = Finder.findToken(parent.range[0], file.ast.tokens);
					if(tok && tok.type === 'Keyword' && tok.value === 'new') {
						var text = '';
						var end = tok.range[1],
							start = tok.range[0],
							prev = file.ast.tokens[tok.index-1];
						if(prev.range[1] < tok.range[0]) {
							end = node.range[0];
							start = prev.range[1]+1;
						} else if(node.range[0] - end > 1) {
							end += node.range[0] - end - 1;
						}
						if(parent.callee.name === 'Math' || parent.callee.name === 'JSON') {
							//also get rid of the params - these two have no functional equivilent
							end = parent.range[1];
							text = parent.callee.name;
						}
						return {text: text, start: start, end: end};
					}
				}
			}
		},
		/**
		 * @description Fix for 'no-new-wrappers-literal' problem
		 * @function
		 * @callback
		 */
		"no-new-wrappers-literal": function(annotation, annotations, file) {
			var node = Finder.findNode(annotation.start, file.ast, {parents:true});
			if(node) {
				var parent = node.parents[node.parents.length-1];
				if(parent.type === 'NewExpression') {
					switch(parent.callee.name) {
						case 'Math':
						case 'JSON': {
							return {text: parent.callee.name, start: parent.range[0], end: parent.range[1]};
						}
						case 'String': {
							var s = '';
							if(parent.arguments.length > 0) {
								var str = parent.arguments[0];
								if(str.type === 'Literal') {
									s = String(str.value);
								} else if(str.type === 'Identifier') {
									if(str.name === 'undefined') {
										s = String(undefined);
									} else if(str.name === 'NaN') {
										s = String(NaN);
									} else {
										s = String(str.name);
									}
								}
							} else {
								s = String();
							}
							return {text: '"'+s.toString()+'"', start: parent.range[0], end: parent.range[1]}; //$NON-NLS-1$ //$NON-NLS-2$
						}
						case 'Number': {
							var nu;
							if(parent.arguments.length > 0) {
								var num = parent.arguments[0];
								if(num.type === 'Literal') {
									nu = Number(num.value);
								} else if(num.type === 'Identifier') {
									if(num.name === 'undefined') {
										nu = Number(undefined);
									} else if(num.name === 'NaN') {
										nu = Number(NaN);
									} else {
										nu = Number(num.name);
									}
								} else {
									nu = Number(num);
								}
							} else {
								nu = Number();
							}
							return {text: nu.toString(), start: parent.range[0], end: parent.range[1]};
						}
						case 'Boolean': {
							var b;
							if(parent.arguments.length > 0) {
								var arg = parent.arguments[0];
								if(arg.type === 'ObjectExpression') {
									b = true;
								} else if(arg.type === 'Literal') {
									b = Boolean(arg.value);
								} else if(arg.type === 'Identifier') {
									if(arg.name === 'undefined') {
										b = Boolean(undefined);
									} else if(arg.name === 'NaN') {
										b = Boolean(NaN);
									} else {
										b = Boolean(arg.name);
									}
								} else if(arg.type === 'UnaryExpression' && arg.operator === '-' &&
									arg.argument.type === 'Literal' && typeof arg.argument.value === 'number') {
									b = false;
								}
							} else {
								b = false;
							}
							return {text: b.toString(), start: parent.range[0], end: parent.range[1]};
						}
					}
				}
			}
		},
		/**
		 * @description Fix for 'no-debugger' problem
		 * @function
		 * @callback
		 */
		"no-debugger" : function(annotation, annotations, file) {
			return applySingleFixToAll(annotations, function(annot) {
				var end = annot.end;
				var tok = Finder.findToken(annot.end, file.ast.tokens);
				if(tok && tok.type === 'Punctuator' && tok.value === ';') {
					end = tok.range[1];
				}
				return {text: '', start: annot.start, end: end};
			});
		},
		/**
		 * @description Fix for 'eqeqeq' problem
		 * @function
		 * @callback
		 */
		"eqeqeq": function(annotation, annotations, file) {
			return applySingleFixToAll(annotations, function(annot){
				var expected = /^.*\'(\!==|===)\'/.exec(annot.title);
				return {text: expected[1], start: annot.start, end: annot.end};
			});
		},
		/**
		 * @description Fix for 'no-eq-null' problem
		 * @function
		 * @callback
		 */
		"no-eq-null": function(annotation, annotations, file) {
			return applySingleFixToAll(annotations, function(annot){
				var expected = /^.*\'(\!==|===)\'/.exec(annot.title);
				return {text: expected[1], start: annot.start, end: annot.end};
			});
		},
		/**
		 * @description Fix for 'no-undef-init' problem
		 * @function
		 * @callback
		 */
		"no-undef-init": function(annotation, annotations, file) {
			return applySingleFixToAll(annotations, function(annot){
				var node = Finder.findNode(annot.start, file.ast, {parents:true});
				if(node) {
					var p = node.parents[node.parents.length-1];
					if(p.type === 'VariableDeclarator') {
						return {text: '', start: p.id.range[1], end: p.range[1]};
					}
				}
			});
		},
		/**
		 * @description Fix for 'no-self-assign' problem
		 * @function
		 * @callback
		 */
		"no-self-assign": function(annotation, annotations, file) {
			return applySingleFixToAll(annotations, function(annot) {
				var node = Finder.findNode(annot.start, file.ast, {parents:true});
				if(node) {
					var p = node.parents[node.parents.length-1];
					switch(p.type) {
						case 'AssignmentExpression' : {
							var end = p.range[1];
							var tokens = file.ast.tokens;
							var tok = Finder.findToken(end, tokens);
							if(tok) {
								//we want the next one, ignoring whitespace
								if((tok.type !== 'Punctuator' || tok.value !== ';') && tokens.length > tok.index + 1) {
									tok = tokens[tok.index+1];
								}
								if(tok &&  tok.type === 'Punctuator' && tok.value === ';') {
									end = tok.range[1]; //clean up trailing semicolons
								}
							}
							return {text: '', start: p.range[0], end: end};
						}
						case 'VariableDeclarator' : {
							// init is the same name as the id
							var variableDeclarator = p;
							return {text: '', start: variableDeclarator.id.range[1], end: variableDeclarator.range[1]};
						}
					}
				}
			});
		},
		/**
		 * @description Fix for 'new-parens' problem
		 * @function
		 * @callback
		 */
		"new-parens": function(annotation, annotations, file) {
			var node = Finder.findNode(annotation.start, file.ast, {parents:true});
			if(node && node.type === 'Identifier') {
				return {text: '()', start: node.range[1], end: node.range[1]}; //$NON-NLS-1$
			}
		},
		/**
		 * @description fix for the missing-nls rule
		 * @function
		 * @callback
		 */
        "missing-nls": function(annotation, annotations, file){
			return applySingleFixToAll(annotations, function(annot) {
                if(annot.data && typeof annot.data.indexOnLine === 'number') {
	                // Insert the correct non nls comment
	                var end = getLineEnd(file.ast.sourceFile.text, annot.end);
	                // indexOnLine starts at 0, non-nls comments start at one
	                var comment = " //$NON-NLS-" + (annot.data.indexOnLine + 1) + "$"; //$NON-NLS-1$
	                return {text: comment,	start: end, end: end};
                }
            });
        },
		/**
		 * @description fix for the 'no-comma-dangle' rule
		 * @function
		 * @callback
		 */
		"no-comma-dangle": function(annotation, annotations, file) {
			return applySingleFixToAll(annotations, function(annot){
				var end = annot.end;
				if(annot.start+1 !== end) {
					end = annot.start+1;
				}
				return {text: '', start: annot.start, end: end};
			});
		},
		/**
		 * @description fix for the 'no-comma-dangle' rule
		 * @function
		 * @callback
		 */
		"no-empty-block": function(annotation, annotations, file) {
            var linestart = getLineStart(file.ast.sourceFile.text, annotation.start);
            var fix = '//TODO empty block'; //$NON-NLS-1$
            var indent = computeIndent(file.ast.sourceFile.text, linestart, true);
            fix = '\n' + indent + fix; //$NON-NLS-1$
            fix += computePostfix(file.ast.sourceFile.text, annotation);
            return {text: fix, start: annotation.start+1, end: annotation.start+1};
        },
		/**
		 * @description fix for the 'no-extra-parens' rule
		 * @function
		 * @callback
		 */
		"no-extra-parens": function(annotation, annotations, file) {
			return applySingleFixToAll(annotations, function(annot) {
				var tokens = file.ast.tokens;
				var token = Finder.findToken(annot.start, tokens);
				var openBracket = tokens[token.index-1];
				if (openBracket.value === '(') {
					var closeBracket = Finder.findToken(annot.end, tokens);
					if (closeBracket.value !== ')') {
						// try the next token if ')' is on the next line
						if (tokens.length > closeBracket.index + 1) {
							closeBracket = tokens[closeBracket.index + 1];
						}
					}
					if (closeBracket.value === ')') {
						var replacementText = "";
						if (token.index >= 2) {
							var previousToken = file.ast.tokens[token.index - 2];
							if (previousToken.range[1] === openBracket.range[0]
									&& (previousToken.type === "Identifier" || previousToken.type === "Keyword")) {
								// now we should also check if there is a space between the '(' and the next token
								if (token.range[0] === openBracket.range[1]) {
									replacementText = " "; //$NON-NLS-1$
								}
							}
						}
						return [
							{text: replacementText, start: openBracket.range[0], end: openBracket.range[1]},
							{text: '', start: closeBracket.range[0], end: closeBracket.range[1]}
						];
					}
				}
			});
		},
		/**
		 * @description fix for the 'no-extra-semi' rule
		 * @function
		 * @callback
		 */
		"no-extra-semi": function(annotation, annotations, file) {
			return applySingleFixToAll(annotations, function(currentAnnotation){
	           return {text: '', start: currentAnnotation.start, end: currentAnnotation.end};
            });
        },
        /**
		 * @description fix for the 'no-fallthrough' rule
		 * @function
		 * @callback
		 */
        "no-fallthrough": function(annotation, annotations, file) {
            var linestart = getLineStart(file.ast.sourceFile.text, annotation.start);
            var fix = '//$FALLTHROUGH$'; //$NON-NLS-1$
            var indent = computeIndent(file.ast.sourceFile.text, linestart);
            fix += computePostfix(file.ast.sourceFile.text, annotation, indent);
            return {text: fix, start: annotation.start, end: annotation.start};
        },
        /**
		 * @description fix for the 'no-fallthrough-break' rule
		 * @function
		 * @callback
		 */
        "no-fallthrough-break": function(annotation, annotations, file) {
            var linestart = getLineStart(file.ast.sourceFile.text, annotation.start);
            var fix = 'break;'; //$NON-NLS-1$
            var indent = computeIndent(file.ast.sourceFile.text, linestart);
            fix += computePostfix(file.ast.sourceFile.text, annotation, indent);
            return {text: fix, start: annotation.start, end: annotation.start};
        },
        /**
		 * @description fix for the 'no-new-array' rule
		 * @function
		 * @callback
		 */
        "no-new-array": function(annotation, annotations, file) {
			var node = Finder.findNode(annotation.start, file.ast, {parents:true});
			if(node && node.parents) {
				var p = node.parents[node.parents.length-1];
				if(p.type === 'CallExpression' || p.type === 'NewExpression') {
					var fix = '';
						if(p.arguments.length > 0) {
						var start = p.arguments[0].range[0], end = p.arguments[p.arguments.length-1].range[1];
						fix += '[' + file.ast.sourceFile.text.substring(start, end) + ']';
					} else {
						fix += '[]'; //$NON-NLS-1$
					}
					return {text: fix, start: p.start, end: p.end};
				}
			}
        },
        /**
		 * @description fix for the 'no-reserved-keys' rule
		 * @function
		 * @callback
		 */
		"no-reserved-keys": function(annotation, annotations, file) {
			return applySingleFixToAll(annotations, function(annot) {
				var node = Finder.findNode(annot.start, file.ast, {parents: true});
				if (node && node.type === 'Identifier') {
					var parent = node.parents.pop();
					if (parent && parent.type === 'MemberExpression') {
						return {
							text: '["' + node.name + '"]', //$NON-NLS-1$ //$NON-NLS-2$
							start: parent.object.range[1],
							end: node.range[1]
						};
					}
					return {
						text: '"' + node.name + '"', //$NON-NLS-1$ //$NON-NLS-2$
						start: node.range[0],
						end: node.range[1]
					};
				}
			});
		},
        /**
		 * @description fix for the 'no-throw-literal' rule
		 * @function
		 * @callback
		 */
        "no-throw-literal": function(annotation, annotations, file) {
            var node = Finder.findNode(annotation.start, file.ast, {parents:true});
            var source = node.raw || file.ast.sourceFile.text.slice(node.range[0], node.range[1]);
            return {text: 'new Error(' + source + ')', start: annotation.start, end: annotation.end}; //$NON-NLS-1$
        },
        /**
		 * @description fix for the 'no-undef-defined' rule
		 * @function
		 * @callback
		 */
        "no-undef-defined": function(annotation, annotations, file) {
            /**
             * @callback
             */
            function assignLike(node) {
                if(node && node.parents && node.parents.length > 0 && node.type === 'Identifier') {
                    var parent = node.parents.pop();
                    return parent && (parent.type === 'AssignmentExpression' || parent.type === 'UpdateExpression');
                }
                return false;
            }
            var name = annotation.data;
            if (name && typeof name === 'object'){
            	name = name.data;
            }
            if(name !== null && typeof name !== 'undefined') {
                var comment = null;
                var start = 0;
                var insert = name;
                var node = Finder.findNode(annotation.start, file.ast, {parents:true});
                if(assignLike(node)) {
                    insert += ':true'; //$NON-NLS-1$
                }
                comment = Finder.findDirective(file.ast, 'globals'); //$NON-NLS-1$
                if(comment) {
                    start = comment.range[0]+2;
                    return {text: updateDirective(comment.value, 'globals', insert), start: start, end: start+comment.value.length}; //$NON-NLS-1$
                }
                var point = getDirectiveInsertionPoint(file.ast);
				var linestart = getLineStart(file.ast.sourceFile.text, point);
				var indent = computeIndent(file.ast.sourceFile.text, linestart, false);
				var fix = '/*globals '+insert+' */\n' + indent; //$NON-NLS-1$ //$NON-NLS-2$
                return {text: fix, start: point, end: point};
            }
        },
        /**
		 * @description fix for the 'no-undef-defined-inenv' rule
		 * @function
		 * @callback
		 */
        "no-undef-defined-inenv": function(annotation, annotations, file) {
            var name = annotation.data;
            if (name && typeof name === 'object'){
            	name = name.data;
            }
            if(name) {
                var comment = null;
                var start = 0;
                /**
                 * @description resolves ambigous names based on environments in use
                 * @param {String} name The name to resolve
                 * @returns {String} The name of the environment
                 * @since 12.0
                 */
                function getEnv(name) {
					switch(name) {
						case 'require': {
							if(file.ast.environments) {
								if(file.ast.environments.node && !file.ast.environments.amd) {
									return 'node';
								}
								if(file.ast.environments.node && file.ast.environments.amd) {
									return 'amd';
								}
								if(file.ast.environments.amd) {
									return 'amd';
								}
							}
							break;
						}
						case 'console': {
							if(file.ast.environments) {
								if(file.ast.environments.node && !file.ast.environments.amd) {
									return 'node';
								}
								return 'browser';
							}
							break;
						}
					}
					return Finder.findESLintEnvForMember(name);
                }
                var env = getEnv(name);
                if(env) {
                    comment = Finder.findDirective(file.ast, 'eslint-env'); //$NON-NLS-1$
                    if(comment) {
                        start = getDocOffset(file.ast.sourceFile.text, comment.range[0]) + comment.range[0];
	                    return {text: updateDirective(comment.value, 'eslint-env', env, true), start: start, end: start+comment.value.length}; //$NON-NLS-1$
                    }
                    var point = getDirectiveInsertionPoint(file.ast);
					var linestart = getLineStart(file.ast.sourceFile.text, point);
					var indent = computeIndent(file.ast.sourceFile.text, linestart, false);
					var fix = '/*eslint-env '+env+' */\n' + indent; //$NON-NLS-1$ //$NON-NLS-2$
                    return {text: fix, start: point, end: point};
                }
            }
        },
        /**
         * @description Fix for the 'unknown-require-missing-env' rule
         * @function
         * @callback
         * @since 12.0
         */
        "unknown-require-missing-env": function(annotation, annotations, file) {
			var name = annotation.data;
			if (name && typeof name === 'object'){
            	name = name.data;
            }
			if(typeof name === 'string') {
				var start,
					comment = Finder.findDirective(file.ast, 'eslint-env'); //$NON-NLS-1$
                if(comment) {
                    start = getDocOffset(file.ast.sourceFile.text, comment.range[0]) + comment.range[0];
                    return {text: updateDirective(comment.value, 'eslint-env', name, true), start: start, end: start+comment.value.length}; //$NON-NLS-1$
                }
                var point = getDirectiveInsertionPoint(file.ast);
				var linestart = getLineStart(file.ast.sourceFile.text, point);
				var indent = computeIndent(file.ast.sourceFile.text, linestart, false);
				var fix = '/*eslint-env '+name+' */\n' + indent; //$NON-NLS-1$ //$NON-NLS-2$
                return {text: fix, start: point, end: point};
			}
        },
        /**
		 * @description fix for the 'no-unreachable' rule
		 * @function
		 * @callback
		 */
		"no-unreachable": function(annotation, annotations, file) {
            return {text: '', start: annotation.start, end: annotation.end};
        },
        /**
		 * @description fix for the 'no-unused-params' rule
		 * @function
		 * @callback
		 */
        "no-unused-params": function(annotation, annotations, file) {
			var analyzed = false;
	        	var analyzeResult = {};
	        	return applySingleFixToAll(annotations, function(annot){
	        		if (!analyzed){
	        			for (var i = annotations.length - 1; i >= 0; i--) {
	        				var element = annotations[i];
	        				var startNode = Finder.findNode(element.start, file.ast, {parents:true});
	        				var startParent = startNode.parents.pop();
	        				if (startParent){
	        					var startParentLocation = startParent.start.toString() + ":" + startParent.end.toString();
	        					if (analyzeResult[startParentLocation] || !(analyzeResult[startParentLocation] = 0)){
	        						analyzeResult[startParentLocation] += 1;
	        					}
	        				}
	        			}
	        			analyzed = true;
	        		}
	                var node = Finder.findNode(annot.start, file.ast, {parents:true});
	                if(node) {
	                    var changes = [];
	                    var parent = node.parents.pop();
	                    var parentLocation = parent.start.toString() + ":" + parent.end.toString();
	                    var paramindex = -1;
	                    var greedy = false;
	                    for(i = 0; i < parent.params.length; i++) {
	                        var p = parent.params[i];
	                        if(node.range[0] === p.range[0] && node.range[1] === p.range[1]) {
	                            paramindex = i;
	                            break;
	                        }
	                    }
	                    if (paramindex === 1 && analyzeResult[parentLocation] === parent.params.length - 1){
	                    	greedy = true;
	                    }
	                    var change = removeIndexedItemChange(parent.params, paramindex, greedy);
	                    if(change) {
	                        changes.push(change);
	                    }
	                    switch(parent.type) {
	                        case 'FunctionExpression': {
	                            var funcparent = node.parents.pop();
	                            if(funcparent.type === 'CallExpression' && (funcparent.callee.name === 'define' || funcparent.callee.name === 'require')) {
	                                var args = funcparent.arguments;
	                                for(i = 0; i < args.length; i++) {
	                                    var arg = args[i];
	                                    if(arg.type === 'ArrayExpression') {
	                                    	if (arg.elements.length === 1) {
	                                    		// we remove the last element in the array
	                                    		change = { "start" : arg.range[0], "end" : arg.range[1], "text" : "[]" };
	                                    	} else {
	                                        	change = removeIndexedItemChange(arg.elements, paramindex, false, arg);
	                                        }
	                                        if(change) {
	                                            changes.push(change);
	                                        }
	                                        break;
	                                    }
	                                }
	                            } else if(funcparent.type === 'Property' && funcparent.key.leadingComments && funcparent.key.leadingComments.length > 0) {
	                                change = updateDoc(funcparent.key, file.ast.sourceFile.text, parent.params[paramindex].name);
	                                if(change) {
	                                    changes.push(change);
	                                }
	                            } else if(funcparent.type === 'Property' && funcparent.leadingComments && funcparent.leadingComments.length > 0) {
	                                change = updateDoc(funcparent, file.ast.sourceFile.text, parent.params[paramindex].name);
	                                if(change) {
	                                    changes.push(change);
	                                }
	                            }
	                            break;
	                        }
	                        case 'FunctionDeclaration': {
	                           change = updateDoc(parent, file.ast.sourceFile.text, parent.params[paramindex].name);
	                           if(change) {
	                               changes.push(change);
	                           }
	                           break;
	                        }
	                    }
	                    return changes;
	                }
	            });
	        },
	        /**
			 * @description fix for the 'no-unused-params-expr' rule
			 * @function
			 * @callback
			 */
	        "no-unused-params-expr": function(annotation, annotations, file) {
		        	/**
		        	 * @callback
		        	 */
		        	function updateCallback(node, ast, comments) {
		                if(Array.isArray(comments)) {
		                    //attach it to the last one
		                    var comment = comments[comments.length-1];
		                    if(comment.type === 'Block') {
		                        var valueend = comment.range[0]+comment.value.length+getDocOffset(ast.sourceFile.text, comment.range[0]);
		                        var start = getLineStart(ast.sourceFile.text, valueend);
		                        var indent = computeIndent(ast.sourceFile.text, start);
		                        var fix = "* @callback\n"+indent; //$NON-NLS-1$
		                        /*if(comment.value.charAt(valueend) !== '\n') {
		                            fix = '\n' + fix;
		                        }*/
		                        return {text: fix, start: valueend-1, end: valueend-1};
		                    }
		                }
		                start = getLineStart(ast.sourceFile.text, node.range[0]);
		                indent = computeIndent(ast.sourceFile.text, start);
		                return {text: "/**\n"+indent+" * @callback\n"+indent+" */\n"+indent, start: node.range[0], end: node.range[0]}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		        	}
	                var node = Finder.findNode(annotation.start, file.ast, {parents:true});
	                if(node && node.parents && node.parents.length > 0) {
	                    var func = node.parents.pop();
	                    var p = node.parents.pop();
	                    var change;
	                    switch(p.type) {
	                    	case 'Property': {
	                    		if(!hasDocTag(['@callback', '@public'], p) && !hasDocTag(['@callback', '@public'], p.key)) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
	                    			change = updateCallback(p, file.ast, p.leadingComments ? p.leadingComments : p.key.leadingComments);
	                			}
	                    		break;
	                    	}
	                    	case 'AssignmentExpression': {
	                    		var left = p.left;
	                    		var parent = null;
	                    		if (node.parents.length > 0) {
	                    			parent = node.parents.pop();
	                    		}
	                    		if (parent === null) {
	                    			parent = left;
	                    		}
	                    		if(left.type === 'MemberExpression' && !hasDocTag(['@callback', '@public'], parent)) { //$NON-NLS-1$ //$NON-NLS-2$
	                    			change = updateCallback(left, file.ast, parent.leadingComments);
					        	} else if(left.type === 'Identifier' && !hasDocTag(['@callback', '@public'], parent)) { //$NON-NLS-1$ //$NON-NLS-2$
					        		change = updateCallback(p.left, file.ast, parent.leadingComments);
					        	}
	                			break;
	                    	}
	                    	case 'VariableDeclarator': {
	                    		var oldp = p;
	                			p = node.parents.pop();
	                			if(p.declarations[0].range[0] === oldp.range[0] && p.declarations[0].range[1] === oldp.range[1]) {
	                				//insert at the var keyword level to not mess up the code
	             				var leadingComments = oldp.leadingComments;
		            				if (!leadingComments) {
		            					leadingComments = p.leadingComments;
		            				}
	               				change = updateCallback(p, file.ast, leadingComments);
	                			} else if(!hasDocTag(['@callback', '@public'], oldp.id)) { //$NON-NLS-1$ //$NON-NLS-2$
	                    			change = updateCallback(oldp, file.ast, oldp.leadingComments);
	                			}
	                    		break;
	                    	}
	                    }
	                    if(!change && !hasDocTag(['@callback', '@public'], func)) { //$NON-NLS-1$ //$NON-NLS-2$
	                        return {text: "/* @callback */ ", start: func.range[0], end: func.range[0]}; //$NON-NLS-1$
	                    }
	                }
	                return change;
		        },
		    /**
			 * @description fix for the 'no-unused-vars-unused' rule
			 * @function
			 * @callback
			 */
	        "no-unused-vars-unused": function(annotation, annotations, file) {
	            var node = Finder.findNode(annotation.start, file.ast, {parents:true});
	            if(node && node.parents && node.parents.length > 0) {
	                var declr = node.parents.pop();
	                if(declr.type === 'VariableDeclarator') {
	                    var decl = node.parents.pop();
	                    if(decl.type === 'VariableDeclaration') {
	                        if(decl.declarations.length === 1) {
	                            return {text: '', start: decl.range[0], end: decl.range[1]};
	                        }
	                        var idx = indexOf(decl.declarations, declr);
	                        if(idx > -1) {
	                        	var change = removeIndexedItemChange(decl.declarations, idx);
	                        	if(change) {
	                        		return {text: change.text, start: change.start, end: change.end};
	                        	}
	                        }
	                    }
	                } else if(declr.type === 'ClassDeclaration') {
	                	return {text: '', start: declr.range[0], end: declr.range[1]};
	                }
	            }
	        },
	        /**
			 * @description fix for the 'no-unused-vars-unread' rule
			 * @function
			 * @callback
			 */
	        "no-unused-vars-unread": function(annotation, annotations, file) {
	        	function getQuickfix(node, child, parents) {
	        		if (parents.length === 0) {
	        			return null;
	        		}
	                var idx, change, properties, propertyIndex;
	                switch(node.type) {
	                	case 'VariableDeclarator' : {
		                    return getQuickfix(parents.pop(), node, parents);
		                }
		                case 'VariableDeclaration' : {
	                        if(node.declarations.length === 1) {
	                            return {text: '', start: node.range[0], end: node.range[1]};
	                        }
	                        idx = indexOf(node.declarations, child);
	                        if(idx > -1) {
	                        	change = removeIndexedItemChange(node.declarations, idx);
	                        	if(change) {
	                        		return {text: change.text, start: change.start, end: change.end};
	                        	}
	                        }
	                		break;
	                	}
	                	case 'ArrayPattern' : {
	                		var elements = node.elements;
	                		if (elements.length === 1) {
	                			// only one element - remove the whole variable declarator
	                			return getQuickfix(parents.pop(), node, parents);
	                 		}
							// remove only the corresponding elements - might also need to remove elements on the right hand side
							// look for the index inside the elements array
							var index = indexOf(elements, child);
							if (index > -1) {
								var chosenElement = elements[index];
								if(chosenElement) {
									return {text: "", start: chosenElement.range[0], end: chosenElement.range[1]};
								}
							}
							break;
						}
						case 'Identifier' : {
							return getQuickfix(parents.pop(), node, parents);
						}
						case 'Property' : {
							return getQuickfix(parents.pop(), node, parents);
						}
						case 'ObjectPattern' : {
							properties = node.properties;
							if (properties.length === 1) {
								// only one property - we can remove the whole declarator
								return getQuickfix(parents.pop(), node, parents);
							}
							propertyIndex = indexOf(properties, child);
	                        if(propertyIndex > -1) {
								change = removeIndexedItemChange(properties, propertyIndex);
								if(change) {
									return {text: change.text, start: change.start, end: change.end};
								}
	                        }
							break;
						}
	                }
	                return null;
				}
	            var found = Finder.findNode(annotation.start, file.ast, {parents:true});
	            if(found && found.parents && found.parents.length > 0) {
	                var quickfix = getQuickfix(found, null, found.parents);
	                if (quickfix) { return quickfix; }
	            }
	        },
	        /**
			 * @description fix for the 'no-unused-vars-unused-funcdecl' rule
			 * @function
			 * @callback
			 */
	        "no-unused-vars-unused-funcdecl": function(annotation, annotations, file) {
	                var node = Finder.findNode(annotation.start, file.ast, {parents:true});
	                if(node && node.parents && node.parents.length > 0) {
	                    var decl = node.parents.pop();
	                    if(decl.type === 'FunctionDeclaration') {
	                        return {text: '', start: decl.range[0], end: decl.range[1]};
	                    }
	                }
	        },
	        /**
			 * @description fix for the 'use-isnan' rule
			 * @function
			 * @callback
			 */
	       "use-isnan": function(annotation, annotations, file) {
				return applySingleFixToAll(annotations, function(annot){
					var node = Finder.findNode(annot.start, file.ast, {parents:true});
	                if(node && node.parents && node.parents.length > 0) {
	                    var bin = node.parents.pop();
	                    if(bin.type === 'BinaryExpression') {
							var tomove;
							if(bin.left.type === 'Identifier' && bin.left.name === 'NaN') {
								tomove = bin.right;
							} else if(bin.right.type === 'Identifier' && bin.right.name === 'NaN') {
								tomove = bin.left;
							}
							if(tomove) {
								var src = file.ast.sourceFile.text.slice(tomove.range[0], tomove.range[1]);
								var pre = "";
								if(bin.operator.charAt(0) === '!') {
									pre = "!";
								}
								return {
									text: pre+'isNaN('+src+')', //$NON-NLS-1$
									start: bin.range[0],
									end: bin.range[1]
								};
							}
	                    }
	                }
				});
	       },
	        /**
			 * @description fix for the 'semi' rule
			 * @function
			 * @callback
			 */
			"semi": function(annotation, annotations, file) {
				return applySingleFixToAll(annotations, function(annot) {
					if (annot.data) {
						switch (annot.data.kind) {
							case 'missing':
								return {
									text: ';',
									start: annot.end,
									end: annot.end
								};
							case 'extra':
								return {
									text: '',
									start: annot.start,
									end: annot.end
								};
						}
					}
				});
			},
			/**
			 * @description fix for the 'unnecessary-nl' rule
			 * @function
			 * @callback
			 */
	        "unnecessary-nls": function(annotation, annotations, file){
				return applySingleFixToAll(annotations, function(annot){
					var comment = Finder.findComment(annot.start + 2, file.ast); // Adjust for leading //
					var nlsTag = annot.data.nlsComment; // We store the nls tag in the annotation
					if (comment && comment.type.toLowerCase() === 'line' && nlsTag){
						var index = comment.value.indexOf(nlsTag);
						// Check if we can delete the whole comment
						if (index > 0){
							var start = annot.start;
							while (file.ast.sourceFile.text.charAt(start-1) === ' ' || file.ast.sourceFile.text.charAt(start-1) === '\t'){
								start--;
							}
							return {
								text: '',
								start: start,
								end: annot.end
							};
						} else if (index === 0){
							var newComment = comment.value.substring(index+nlsTag.length);
							start = annot.start;
							var end = annot.end;
							if (!newComment.match(/^(\s*|\s*\/\/.*)$/)){
								start += 2; // Only remove leading // if additional comments start with another //
							} else {
								while (file.ast.sourceFile.text.charAt(start-1) === ' ' || file.ast.sourceFile.text.charAt(start-1) === '\t'){
									start--;
								}
							}
							if (newComment.match(/^\s*$/)){
								end = comment.range[1]; // If there is only whitespace left in the comment, delete it entirely
								while (file.ast.sourceFile.text.charAt(start-1) === ' ' || file.ast.sourceFile.text.charAt(start-1) === '\t'){
									start--;
								}
							}
							return {
								text: '',
								start: start,
								end: end
							};
						}
					}
				});
			},
			/**
			 * @callback
			 */
			"no-else-return": function(annotation, annotations, file) {
				return applySingleFixToAll(annotations, function(annot) {
					var node = Finder.findNode(annot.start, file.ast, {parents:true});
					// looking for the if statement
					while (node && node.type !== 'IfStatement') {
						node = node.parents.pop();
					}
					var thenNode = node.consequent;
					var elseNode = node.alternate;
					var parent = node.parent || {};
					// Replace else statement with newline.
					var replacement = '\n';
					if (parent.type === 'BlockStatement' || parent.type === 'IfStatement') {
						// Preserve whitespace if inside of a block.
						replacement = node.sourceFile.text.substring(parent.start + 1, parent.body[0].start);
						if (replacement.charAt(0) !== '\n') {
							replacement = '\n' + replacement;
						}
					}
					if (elseNode.type === 'BlockStatement') {
						// need to remove the else { and the closing brace }
						// check the first statement inside the block for trailing comment
						var firstStatement = elseNode.body[0];
						var start = firstStatement.start;
						if (firstStatement.leadingComments) {
							start = firstStatement.leadingComments[0].start;
						}
						var lastStatement = elseNode.body[elseNode.body.length - 1];
						var closingStart = lastStatement.end;
						var trailingComments = lastStatement.trailingComments;
						if (trailingComments) {
							closingStart = trailingComments[trailingComments.length - 1].end;
						}
						return [
							{ text: replacement, start: thenNode.end, end: start },
							{ text: '', start:  closingStart, end: elseNode.end },
						];
					}
					// need to remove the else keyword
					start = elseNode.start;
					if (elseNode.leadingComments) {
						start = elseNode.leadingComments[0].start;
					}
					return {
						text: replacement,
						start: thenNode.end + 1,
						end: start
					};
				});
			},
			/**
			 * @callback
			 */
			"no-cond-assign": function(annotation, annotations, file) {
				return applySingleFixToAll(annotations, function(annot) {
					var node = Finder.findNode(annot.start, file.ast, {parents:true});
					var assignmentExpression = node.parents.pop();
					if (assignmentExpression && assignmentExpression.type === 'AssignmentExpression') {
						var start = assignmentExpression.range[0];
						var end = assignmentExpression.range[1];
						return [
							{text: '(', start: start, end:  start},
							{text: ')', start: end, end:  end},
						];
					}
				});
			},
			/**
			 * @callback
			 */
			"no-extra-bind": function(annotation, annotations, file) {
				return applySingleFixToAll(annotations, function(annot) {
					var node = Finder.findNode(annot.start, file.ast, {parents:true});
					var memberExpression  = node.parents.pop();
					var callExpression = node.parents.pop();
					if (memberExpression && callExpression) {
						var object = memberExpression.object;
						if (object.type === 'ArrowFunctionExpression') {
							return [
								{text: '', start: callExpression.range[0], end: object.range[0]},
								{text: '', start: object.range[1], end: callExpression.range[1]},
							];
						}
						return {
							text: '',
							start: memberExpression.object.range[1],
							end: callExpression.range[1]
						};
					}
				});
			},
			"quotes" : function(annotation, annotations, file) {
				function convertQuotes(str, newQuote) {
					var oldQuote = str[0];
					if (newQuote === oldQuote) {
						return str;
					}
					return newQuote + str.slice(1, -1).replace(/\\(\${|\r\n?|\n|.)|["'`]|\${|(\r\n?|\n)/g, function(match, escaped, newline) {
						if (escaped === oldQuote || oldQuote === "`" && escaped === "${") {
							return escaped; // unescape
						}
						if (match === newQuote || newQuote === "`" && match === "${") {
							return "\\" + match; // escape
						}
						if (newline && oldQuote === "`") {
							return "\\n"; // escape newlines
						}
						return match;
					}) + newQuote;
				}
				return applySingleFixToAll(annotations, function(annot) {
					var node = Finder.findNode(annot.start, file.ast, {parents:true});
					if (node) {
						switch(node.type) {
							case 'Literal' :
								return {
									text: convertQuotes(node.raw, annotation.data.quote),
									start: node.range[0],
									end: node.range[1]
								};
							case 'TemplateLiteral' :
								return {
									text: convertQuotes('`' + node.quasis[0].value.raw + '`', annotation.data.quote),
									start: node.range[0],
									end: node.range[1]
								};
						}
					}
				});
			},
			/**
			 * @callback
			 */
			"no-unused-expressions": function(annotation, annotations, file) {
				return applySingleFixToAll(annotations, function(annot) {
					return {
						text: '',
						start: annot.start,
						end: annot.end
					};
				});
			},
			/**
			 * @callback
			 */
			"no-implicit-coercion" : function(annotation, annotations, file) {
				var INDEX_OF_PATTERN = /^(?:i|lastI)ndexOf$/;
				/**
				 * Checks whether or not a node is a double logical negating.
				 * @param {ASTNode} node - An UnaryExpression node to check.
				 * @returns {boolean} Whether or not the node is a double logical negating.
				 */
				function isDoubleLogicalNegating(node) {
					return node.operator === "!" &&
						node.argument.type === "UnaryExpression" &&
						node.argument.operator === "!";
				}
				/**
				 * Checks whether or not a node is a binary negating of `.indexOf()` method calling.
				 * @param {ASTNode} node - An UnaryExpression node to check.
				 * @returns {boolean} Whether or not the node is a binary negating of `.indexOf()` method calling.
				 */
				function isBinaryNegatingOfIndexOf(node) {
					return node.operator === "~" &&
						node.argument.type === "CallExpression" &&
						node.argument.callee.type === "MemberExpression" &&
						node.argument.callee.property.type === "Identifier" &&
						INDEX_OF_PATTERN.test(node.argument.callee.property.name);
				}

				/**
				 * Checks whether or not a node is a multiplying by one.
				 * @param {BinaryExpression} node - A BinaryExpression node to check.
				 * @returns {boolean} Whether or not the node is a multiplying by one.
				 */
				function isMultiplyByOne(node) {
					return node.operator === "*" && (
						node.left.type === "Literal" && node.left.value === 1 ||
						node.right.type === "Literal" && node.right.value === 1
					);
				}

				/**
				 * Checks whether the result of a node is numeric or not
				 * @param {ASTNode} node The node to test
				 * @returns {boolean} true if the node is a number literal or a `Number()`, `parseInt` or `parseFloat` call
				 */
				function isNumeric(node) {
					return node.type === "Literal"
						&& typeof node.value === "number"
						|| node.type === "CallExpression"
						&& (node.callee.name === "Number" ||
							node.callee.name === "parseInt" ||
							node.callee.name === "parseFloat");
				}

				/**
				 * Returns the first non-numeric operand in a BinaryExpression. Designed to be
				 * used from bottom to up since it walks up the BinaryExpression trees using
				 * node.parent to find the result.
				 * @param {BinaryExpression} node The BinaryExpression node to be walked up on
				 * @returns {ASTNode} The first non-numeric item in the BinaryExpression tree or
				 */
				function getNonNumericOperand(node) {
					var left = node.left,
						right = node.right;
					if (right.type !== "BinaryExpression" && !isNumeric(right)) {
						return right;
					}
					if (left.type !== "BinaryExpression" && !isNumeric(left)) {
						return left;
					}
				}

				/**
				 * Gets a node that is the left or right operand of a node, is not the specified literal.
				 * @param {ASTNode} node - A BinaryExpression node to get.
				 * @param {any} value - A literal value to check.
				 * @returns {ASTNode} A node that is the left or right operand of the node, is not the specified literal.
				 */
				function getOtherOperand(node, value) {
					if (node.left.type === "Literal" && node.left.value === value) {
						return node.right;
					}
					return node.left;
				}
				function getSource(text, node) {
					return text.slice(node.range[0], node.range[1]);
				}
				function checkBinaryExpression(node, text, annot) {
					if (isMultiplyByOne(node)) {
						var operandNode = getNonNumericOperand(node);
						if (operandNode) {
							return {
								text: 'Number(' + getSource(text, operandNode) + ')',
								start: annot.start,
								end: annot.end
							};
						}
					}
					// "" + foo
					return {
						text: 'String(' + getSource(text, getOtherOperand(node, "")) + ')',
						start: annot.start,
						end: annot.end
					};
				}
				function checkAssignmentExpression(node, text, annot) {
					var operandNode = getOtherOperand(node, "");
					var source = getSource(text, operandNode);
					return {
						text: source + ' = String(' + source + ')',
						start: annot.start,
						end: annot.end
					};
				}
				var text = file.ast.sourceFile.text;
				return applySingleFixToAll(annotations, function(annot) {
					var node = Finder.findNode(annot.start, file.ast, {parents:true});
					if (node) {
						switch(node.type) {
							case "UnaryExpression" : {
								if (isDoubleLogicalNegating(node)) {
									return {
										text: 'Boolean(' + getSource(text, node.argument.argument) + ')',
										start: annot.start,
										end: annot.end
									};
								} else if (isBinaryNegatingOfIndexOf(node)) {
									return {
										text: getSource(text, node.argument) + ' !== -1',
										start: annot.start,
										end: annot.end
									};
								}
								// +foo
								return {
									text: 'Number(' + getSource(text, node.argument) + ')',
									start: annot.start,
									end: annot.end
								};
							}
							case "Literal" : {
								// check the parent - should be a binary expression
								var parent = node.parents && node.parents.pop();
								if (parent) {
									return checkBinaryExpression(parent, text, annot);
								}
								return null;
							}
							case "BinaryExpression" : {
								return checkBinaryExpression(node, text, annot);
							}
							case "Identifier" : {
								parent = node.parents && node.parents.pop();
								if (parent) {
									return checkAssignmentExpression(parent, text, annot);
								}
								return null;
							}
							case "AssignmentExpression" : {
								return checkAssignmentExpression(node, text, annot);
							}
						}
					}
				});
			},
			/**
			 * @callback
			 */
			"remove-duplicate-case" :  function(annotation, annotations, file) {
				function getFallthroughComment(node) {
					var comments = node.leadingComments;
					if (comments) {
						var comment = comments[0];
						if (comment.type === 'Line' && comment.value === '$FALLTHROUGH$') {
							// if there is only one comment remove it
							return comment;
						}
					}
					return null;
				}
				return applySingleFixToAll(annotations, function(annot) {
					var node = Finder.findNode(annot.start, file.ast, {parents:true});
					if (node) {
						// get the switch case
						if (node.parents.length > 2) {
							var switchCase = node.parents.pop();
							// get the switch statement
							var statement = node.parents.pop();
							if (statement) {
								// get the index of the switch case
								var index = 0;
								loop: for (var i = 0, max = statement.cases.length; i < max; i++) {
									var currentCase = statement.cases[i];
									if (currentCase.range[0] === switchCase.range[0] && currentCase.range[1] === switchCase.range[1]) {
										index = i;
										break loop;
									}
								}
							}
							var start = switchCase.start;
							var end = switchCase.end;
							// check the next case
							var nextCase = statement.cases[index + 1];
							var comment;
							if (nextCase) {
								// check leading comments if it is a fallthrough comment
								comment = getFallthroughComment(nextCase);
								if (comment) {
									end = comment.end;
								}
							}
							if (index !== 0) {
								// check if there is a fallthrough comment for the current case statement we want to remove
								// if yes, we need to preserve it unless there is no other case statement after the one we remove
								comment = getFallthroughComment(switchCase);
								if (comment && nextCase) {
									start = comment.end;
								} else {
									start = statement.cases[index - 1].end;
								}
							}
							return {
								text: '',
								start: start,
								end: end
							};
						}
					}
				});
			},
			/**
			 * @callback
			 */
			"no-trailing-spaces" : function(annotation, annotations, file) {
				return applySingleFixToAll(annotations, function(annot) {
					return {
						text: '',
						start: annot.start,
						end: annot.end
					};
				});
			},
			/**
			 * @callback
			 */
			"ignore-in-file": function(annotation, annotations, file) {
	            var id = annotation.data ? annotation.data.ruleId : null;
	            if(id) {
	            	var result = [];
	                var disable = Finder.findDirective(file.ast, 'eslint-disable'); //$NON-NLS-1$
	                var programStart = getProgramStart(file.ast);
	                programStart = Math.min(annotation.start, programStart);
	                // If there is disable directive already there, add the rule to it
	                if (disable && disable.range[0] <= programStart){
	                    var start = getDocOffset(file.ast.sourceFile.text, disable.range[0]) + disable.range[0];
	                    result.push({text: updateDirective(disable.value, 'eslint-disable', id, true), start: start, end: start+disable.value.length}); //$NON-NLS-1$
	                } else {
	                	// Otherwise insert at first appropriate insertion point
	                	var insertion = getDirectiveInsertionPoint(file.ast);
	                	if (annotation.start < insertion){
	                		insertion = getCodeStart(file.ast);
	                	}
	                	var linestart = getLineStart(file.ast.sourceFile.text, insertion);
						var indent = computeIndent(file.ast.sourceFile.text, linestart, false);
						var fix = '/*eslint-disable '+id+' */\n' + indent; //$NON-NLS-1$ //$NON-NLS-2$
		                result.push({text: fix, start: insertion, end: insertion});
	                }
	                // If there is are enable directives, remove the rule from them
	                var enable = Finder.findDirectives(file.ast, 'eslint-enable'); //$NON-NLS-1$
	                for (var i = 0; i < enable.length; i++) {
	                	// TODO We do not handle there being a general eslint-enable statement in the file
	                	var removal = removeDirective(enable[i].value, 'eslint-enable', id);
	                	if (removal){
	                		if (removal.all){
	                			// TODO Remove trailing whitespace and newline from the removed comment
	                    		result.push({text: '', start: enable[i].range[0], end: enable[i].range[1]});
	                		} else {
	                			var enableStart = enable[i].range[0] + getDocOffset(file.ast.sourceFile.text, enable[i].range[0]) + removal.start;
	                			var enableEnd = enable[i].range[0] + getDocOffset(file.ast.sourceFile.text, enable[i].range[0]) + removal.end;
	                    		result.push({text: '', start: enableStart, end: enableEnd});
	                		}
	                	}
	                }
	                // Add a no-op edit to the annotation range so the selection/scroll remains on the annotation
	                result.push({text: file.ast.sourceFile.text.slice(annotation.start, annotation.end), start: annotation.start, end: annotation.end});
	                // Limitation of the editor, edits must be in order or they will stomp on each other
	                result = result.sort(function(a, b){
	                	return a.start - b.start;
	                });
					return result;
	            }
            },
		};

		/**
		 * @description Compute the fixes
		 * @param {Object} query The original Tern query object
		 * @param {Object} file The file object from Tern
		 */
		function computeFixes(query, file) {
			var func = availableFixes[query.problemId];
			if(typeof func === 'function') {
				var fixes = func.call(this, query.annotation, query.annotations, file);
				if(Array.isArray(fixes)) {
					return fixes;
				} else if(fixes) {
					return [fixes];
				}
			}
			return [];
		}

	   /**
	    * @description Finds the start of the line in the given text starting at the given offset
	    * @param {String} text The text
	    * @param {Number} offset The offset
	    * @returns {Number} The offset in the text of the new line
	    */
	   function getLineStart(text, offset) {
	       if(!text) {
	           return 0;
	       }
	       if(offset < 0) {
	           return 0;
	       }
	       var off = offset;
	       var char = text[off];
	       while(off > -1 && !/[\r\n]/.test(char)) {
	           char = text[--off];
	       }
	       return off+1; //last char inspected will be @ -1 or the new line char
		}

		/**
	    * @description Finds the end of the line in the given text starting at the given offset
	    * @param {String} text The text
	    * @param {Number} offset The offset
	    * @returns {Number} The offset in the text before the new line or end of file
	    */
	   function getLineEnd(text, offset) {
	       if(!text) {
	           return 0;
	       }
	       if(offset < 0) {
	           return 0;
	       }
	        var off = offset;
	       var char = text[off];
	       while(off < text.length && !/[\r\n]/.test(char)) {
	           char = text[++off];
	       }
	       return off;
		}

		/**
		 * @description Computes the indent to use in the editor
		 * @param {String} text The editor text
		 * @param {Number} linestart The start of the line
		 * @param {Boolean} extraIndent If we should add an extra indent
		 * @returns {String} The ammount of indent / formatting for the start of the string
		 */
		function computeIndent(text, linestart, extraIndent) {
		    if(!text || linestart < 0) {
		        return '';
		    }
		    var off = linestart;
		    var char = text[off];
		    var preamble = extraIndent ? '\t' : ''; //$NON-NLS-1$
		    //walk the proceeding whitespace so we will insert formatted at the same level
		    while(char === ' ' || char === '\t') {
		       preamble += char;
		       char = text[++off];
		    }
		    return preamble;
		}

	    /**
	     * @description Computes the formatting for the trailing part of the fix
	     * @param {String} text The editor text
	     * @param {Object} annotation The annotation object
	     * @param {String} indent Additional formatting to apply after the fix
	     * @returns {String} The formatting to apply after the fix
	     */
	    function computePostfix(text, annotation, indent) {
	        if(!text || !annotation) {
	            return '';
	        }
	        var off = annotation.start;
	        var char = text[off];
		    var val = '';
		    var newline = false;
		    //walk the trailing whitespace so we can see if we need axtra whitespace
		    while(off >= annotation.start && off <= annotation.end) {
			    if(char === '\n') {
			        newline = true;
			        break;
			    }
			    char = text[off++];
		    }
		    if(!newline) {
			    val += '\n'; //$NON-NLS-1$
		    }
		    if(typeof indent !== 'undefined') {
			    val += indent;
		    }
		    return val;
	    }

	    /**
	     * @description Computes the offset for the block comment. i.e. 2 if the block starts with /*, 3 if it starts with /**
	     * @param {String} text The file text
	     * @param {Number} offset The doc node offset
	     * @returns {Number} 2 or 3 depending on the start of the comment block
	     */
	    function getDocOffset(text, offset) {
	        if(text.charAt(offset+1) === '*') {
	            if(text.charAt(offset+2) === '*') {
	                return 3;
	            }
	            return 2;
	        }
	        return 0;
	    }

		/**
		 * @description Updates the eslint directive
		 * @param {String}] text The text of the comment
		 * @param {String} directive The directive name
		 * @param {String} name The name to add to the directive
		 * @param {Boolean} usecommas If we should separate the directive entries with commas or not
		 * @returns {String} The new directive text
		 */
		function updateDirective(text, directive, name, usecommas) {
	        if(usecommas) {
		        if(text.slice(directive.length).trim() !== '') {
		            return text.trim() + ', '+name; //$NON-NLS-1$
		        }
		        return text.trim() + ' '+name;  //$NON-NLS-1$
	        }
		    return text.trim() + ' '+name;  //$NON-NLS-1$
	    }
	    
	    /**
		 * @description Returns the offsets to delete to remove an entry from an eslint directive
		 * @param {String} text The text of the comment
		 * @param {String} directive The directive name
		 * @param {String} name The entry name to remove
		 * @returns {Object} Object with start and end properties for the offset to remove, will also have all: true if the directive can be removed
		 */
		function removeDirective(text, directive, name) {
			var offset = text.indexOf(name, directive.length);
			if (offset >= 0){
				var end = offset+name.length;
				if (text[end] === ','){
					end++;
				}
				if (text.slice(0,offset).trim() === directive && text.slice(end).trim() === ""){
					return {all:true, start: offset, end: end};
				}
				return {start: offset, end: end};
			}
			return null;
	    }

		/**
		 * @description Finds the index of the given item in the given list
		 * @param {Array.<Object>} list The array of AST nodes
		 * @param {Object} item The AST node
		 * @returns {Number} The index of the node in the list or -1
		 */
		function indexOf(list, item) {
		    if(list && list.length) {
	            for(var i = 0; i < list.length; i++) {
	                var p = list[i];
	                if(p && item.range[0] === p.range[0] && item.range[1] === p.range[1]) {
	                    return i;
	                }
	            }
	        }
	        return -1;
		}

		/**
		 * @description Remove the item from the list and return it as a change object
		 * @param {Array.<Object>} list The list of items to remove from
		 * @param {Number} index The index to remove
		 * @returns {Object} A change object containg the properties text, start and end
		 */
		function removeIndexedItemChange(list, index, greedy, parent) {
			if (index < 0 || index > list.length) {
				return;
			}
			var node = list[index];
			if (list.length === 1) {
				return {
					"start": node.range[0],
					"end": parent ? parent.range[1] - 1 : node.range[1],
					"text": ""
				};
			} else if (index === list.length - 1) {
				return {
					"start": list[index - 1].range[1],
					"end": parent ? parent.range[1] - 1 : node.range[1],
					"text": ""
				};
			} else if (node) {
				if (index === 0) {
					if (list.length === 1) {
						return {
							"start": node.range[0],
							"end": node.range[1],
							"text": ""
						};
					}
					return {
						"start": node.range[0],
						"end": list[index + 1].range[0],
						"text": ""
					};
				}
				return greedy ? {
					"start": list[index - 1].range[1],
					"end": list[index + 1].range[0],
					"text": ""
				} : {
					"start":  list[index - 1].range[1],
					"end": node.range[1],
					"text": ""
				};
			}
			return null;
		}
	    /**
	     * @description Updates the JSDoc attached to the given AST node
	     * @param {Object} node The AST node to update
	     * @param {String} source The AST source
	     * @param {String} name The name of the doc element to remove
	     * @returns {Object} The change object or null
	     */
	    function updateDoc(node, source, name) {
	        if(node.leadingComments && node.leadingComments.length > 0) {
	            for(var i = node.leadingComments.length-1; i > -1; i--) {
	                var comment = node.leadingComments[i];
	                var edit = new RegExp("(\\s*[*]+\\s*(?:@param)\\s*(?:\\{.*\\})?\\s*(?:"+name+")+.*)").exec(comment.value); //$NON-NLS-1$ //$NON-NLS-2$
	                if(edit) {
	                    var start = comment.range[0] + edit.index + getDocOffset(source, comment.range[0]);
	                    return {"start" : start, "end" :start+edit[1].length, "text" : ''};
	                }
	            }
	        }
	        return null;
	    }

		/**
		 * @description Returns if the JSDoc attached to the given node has any of the given tags
		 * @param {Array.<String>} tags The array of tags
		 * @param {Object} node The AST node
		 * @returns {Boolean} If the doc attached to the given node has one of the given tags
		 */
		function hasDocTag(tags, node) {
			// tags contains all tags that have to be checked
		    if(node.leadingComments) {
		        for(var i = 0; i < node.leadingComments.length; i++) {
		            var comment = node.leadingComments[i];
		            for (var j = 0, len = tags.length; j < len; j++) {
						var tag = tags[j];
			            if(comment.value.indexOf(tag) > -1) {
			                return true;
			            }
			        }
		        }
		    }
		    return false;
		}

		/**
		 * @description Computes where the eslint directive should be inserted relative to the given node
		 * @param {Object} node The AST node
		 * @returns {Number} The insertion point
		 */
		function getDirectiveInsertionPoint(node) {
		    if(node.type === 'Program' && node.body && node.body.length > 0) {
	            var n = node.body[0];
	            var val = -1;
	            switch(n.type) {
	                case 'FunctionDeclaration': {
	                    val = getCommentStart(n);
	                    if(val > -1) {
	                        return val;
	                    }
	                    //TODO see https://github.com/jquery/esprima/issues/1071
	                    val = getCommentStart(n.id);
	                    if(val > -1) {
	                        return val;
	                    }
	                    return n.range[0];
	                }
	                case 'ExpressionStatement': {
	                    if(n.expression && n.expression.right && n.expression.right.type === 'FunctionExpression') {
	                        val = getCommentStart(n);
	                        if(val > -1) {
	                            return val;
	                        }
	                        //TODO see https://github.com/jquery/esprima/issues/1071
	                        val = getCommentStart(n.expression.left);
	                        if(val > -1) {
	                            return val;
	                        }
	                        return n.range[0];
	                    }
	                    return n.range[0];
	                }
	            }
	            return n.range[0]; // Start of first node, as script blocks in HTML will have leading whitespace
		    }
		    return node.range[0];
		}
		
		/**
		 * @description Returns the start offset of the first body node
		 * @param {Object} ast The AST
		 * @returns {Number} The start offset of first node
		 */
		function getProgramStart(ast) {
		    if(ast.type === 'Program'){
		    	if (ast.body && ast.body.length > 0) {
	        		return ast.body[0].start;
        		}
        		return ast.end; // Don't use 0 in case this is inside an HTML script block
            }
            return 0;
		}
		
		/**
		 * @description Returns the start offset of the first body node or the first comment, whichever is first.
		 * @param {Object} ast The AST
		 * @returns {Number} The start offset of first code content
		 */
		function getCodeStart(ast) {
			var programStart = getProgramStart(ast);
			var commentStart = programStart;
			if (ast.comments && ast.comments.length > 0){
				commentStart = ast.comments[0].range[0];
			}
			return Math.min(programStart, commentStart);
		}

		/**
		 * @description Returns the offset to use when inserting a comment directive
		 * @param {Object} node The node to check for comments
		 * @returns {Number} The offset to insert the comment
		 */
		function getCommentStart(node) {
		    if(node.leadingComments && node.leadingComments.length > 0) {
	            var comment = node.leadingComments[node.leadingComments.length-1];
	            if(/(?:@param|@return|@returns|@type|@constructor|@name|@description)/ig.test(comment.value)) {
	                //if the immediate comment has any of the tags we use for inferencing, add the directive before it instead of after
	                return comment.range[0];
	            }
	        }
	        return -1;
		}

		var controlStatements = ['IfStatement', 'WhileStatement', 'ForStatement', 'ForInStatement', 'WithStatement', 'DoWhileStatement', 'ForOfStatement']; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$

		/**
		 * @description Walks the parents array and checks to see if there is a control statement as a direct parent
		 * @param {Object} node The AST node to check
		 * @returns {Object} The AST node that is a direct control statement parent of the given node, or null
		 */
		function getControlStatementParent(node) {
			if(node && node.parents) {
				var i = node.parents.length-1,
					p = node.parents[i];
				while(p && i > -1) {
					if(controlStatements.indexOf(p.type) > -1) {
						return p;
					}
					p = node.parents[--i];
				}
			}
			else {
				return null;
			}
		}

		/**
		 * Takes a quickfix implementation that can be applied to all fixes in a file and applies it to either all annotations (if multiple annotations provided) or
		 * just to the single annotation.  Handles applying all edits in a single UNDO step as well as setting the caret to the single selected annotation afterward.
		 * @param {Array.<Object>} annotations Array of annotations to apply the fix to
		 * @param {Function} createTextChange function to create a text edit object (text, start, end) for a given annotation
		 */
		function applySingleFixToAll(annotations, createTextChange) {
			var edits = [];
			annotations.forEach(function(current) {
				var change = createTextChange(current);
				if(change) {
					if(Array.isArray(change)) {
						change.forEach(function(fix) {
							edits.push(fix);
						});
					} else {
						edits.push(change);
					}
				}
			});
			// To use setText() with multiple selections they must be in range order
			edits = edits.sort(function(a, b){
				return a.start - b.start;
			});
			var mergedEdits = [];
			var intersectedRange = false;
			for (var i = 0; i < edits.length ; i++) {
			    if(i === edits.length - 1 && !intersectedRange){
			        mergedEdits.push(edits[i]);
			        break;
			    } else if (i === edits.length - 1) {
			        break;
			    }
			    var element = intersectedRange ? mergedEdits.pop() : edits[i];
			    var nextElement = edits[i+1];
			    if (element.end >= nextElement.start){
			        mergedEdits.push({
			            start: element.start,
			            end: nextElement.end,
			            text: element.text + nextElement.text
			        });
			        intersectedRange = true;
			    } else {
			        mergedEdits.push(element);
			        intersectedRange = false;
			    }
			}
			return mergedEdits;
		}
	});

/*eslint-env amd, node*/
/*

  The MIT License (MIT)

  Copyright (c) 2007-2013 Einar Lielmanis and contributors.

  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.

 JS Beautifier
---------------


  Written by Einar Lielmanis, <einar@jsbeautifier.org>
      http://jsbeautifier.org/

  Originally converted to javascript by Vital, <vital76@gmail.com>
  "End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com>
  Parsing improvements for brace-less statements by Liam Newman <bitwiseman@gmail.com>


  Usage:
    js_beautify(js_source_text);
    js_beautify(js_source_text, options);

  The options are:
    indent_size (default 4)          - indentation size,
    indent_char (default space)      - character to indent with,
    preserve_newlines (default true) - whether existing line breaks should be preserved,
    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk,

    jslint_happy (default false) - if true, then jslint-stricter mode is enforced.

            jslint_happy        !jslint_happy
            ---------------------------------
            function ()         function()

            switch () {         switch() {
            case 1:               case 1:
              break;                break;
            }                   }

    space_after_anon_function (default false) - should the space before an anonymous function's parens be added, "function()" vs "function ()",
          NOTE: This option is overriden by jslint_happy (i.e. if jslint_happy is true, space_after_anon_function is true by design)

    brace_style (default "collapse") - "collapse-preserve-inline" | "collapse" | "expand" | "end-expand" | "none"
            put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line, or attempt to keep them where they are.

    space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)",

    unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65"

    wrap_line_length (default unlimited) - lines should wrap at next opportunity after this number of characters.
          NOTE: This is not a hard limit. Lines will continue until a point where a newline would
                be preserved if it were present.

    end_with_newline (default false)  - end output with a newline


    e.g

    js_beautify(js_source_text, {
      'indent_size': 1,
      'indent_char': '\t'
    });

*/
define('beautifier/beautify-js',[
	"acorn/dist/acorn"
], function(acorn) {


	// Object.values polyfill found here:
	// http://tokenposts.blogspot.com.au/2012/04/javascript-objectkeys-browser.html
	if (!Object.values) {
	    Object.values = function(o) {
	        if (o !== Object(o)) {
	            throw new TypeError('Object.values called on a non-object');
	        }
	        var k = [],
	            p;
	        for (p in o) {
	            if (Object.prototype.hasOwnProperty.call(o, p)) {
	                k.push(o[p]);
	            }
	        }
	        return k;
	    };
	}

	function js_beautify(js_source_text, options) {

        function in_array(what, arr) {
            for (var i = 0; i < arr.length; i += 1) {
                if (arr[i] === what) {
                    return true;
                }
            }
            return false;
        }

        function trim(s) {
            return s.replace(/^\s+|\s+$/g, '');
        }

        function ltrim(s) {
            return s.replace(/^\s+/g, '');
        }

        // function rtrim(s) {
        //     return s.replace(/\s+$/g, '');
        // }

        function sanitizeOperatorPosition(opPosition) {
            opPosition = opPosition || OPERATOR_POSITION.before_newline;

            var validPositionValues = Object.values(OPERATOR_POSITION);

            if (!in_array(opPosition, validPositionValues)) {
                throw new Error("Invalid Option Value: The option 'operator_position' must be one of the following values\n" +
                    validPositionValues +
                    "\nYou passed in: '" + opPosition + "'");
            }

            return opPosition;
        }

        var OPERATOR_POSITION = {
            before_newline: 'before-newline',
            after_newline: 'after-newline',
            preserve_newline: 'preserve-newline',
        };

        var OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION.before_newline, OPERATOR_POSITION.preserve_newline];

        var MODE = {
            BlockStatement: 'BlockStatement', // 'BLOCK'
            Statement: 'Statement', // 'STATEMENT'
            ObjectLiteral: 'ObjectLiteral', // 'OBJECT',
            ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]',
            ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)',
            Conditional: 'Conditional', //'(COND-EXPRESSION)',
            Expression: 'Expression' //'(EXPRESSION)'
        };

        function Beautifier(js_source_text, options) {
            "use strict";
            var output;
            var tokens = [],
                token_pos;
            var Tokenizer;
            var current_token;
            var last_type, last_last_text, indent_string;
            var flags, previous_flags, flag_store;
            var prefix;

            var handlers, opt;
            var baseIndentString = '';

            handlers = {
                'TK_START_EXPR': handle_start_expr,
                'TK_END_EXPR': handle_end_expr,
                'TK_START_BLOCK': handle_start_block,
                'TK_END_BLOCK': handle_end_block,
                'TK_WORD': handle_word,
                'TK_RESERVED': handle_word,
                'TK_SEMICOLON': handle_semicolon,
                'TK_STRING': handle_string,
                'TK_EQUALS': handle_equals,
                'TK_OPERATOR': handle_operator,
                'TK_COMMA': handle_comma,
                'TK_BLOCK_COMMENT': handle_block_comment,
                'TK_COMMENT': handle_comment,
                'TK_DOT': handle_dot,
                'TK_UNKNOWN': handle_unknown,
                'TK_EOF': handle_eof
            };

            function create_flags(flags_base, mode) {
                var next_indent_level = 0;
                if (flags_base) {
                    next_indent_level = flags_base.indentation_level;
                    if (!output.just_added_newline() &&
                        flags_base.line_indent_level > next_indent_level) {
                        next_indent_level = flags_base.line_indent_level;
                    }
                }

                var next_flags = {
                    mode: mode,
                    parent: flags_base,
                    last_text: flags_base ? flags_base.last_text : '', // last token text
                    last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed
                    declaration_statement: false,
                    declaration_assignment: false,
                    multiline_frame: false,
                    inline_frame: false,
                    if_block: false,
                    else_block: false,
                    do_block: false,
                    do_while: false,
                    import_block: false,
                    in_case_statement: false, // switch(..){ INSIDE HERE }
                    in_case: false, // we're on the exact line with "case 0:"
                    case_body: false, // the indented case-action block
                    indentation_level: next_indent_level,
                    line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level,
                    start_line_index: output.get_line_number(),
                    ternary_depth: 0
                };
                return next_flags;
            }

            // Some interpreters have unexpected results with foo = baz || bar;
            options = options ? options : {};
            opt = {};

            // compatibility
            if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
                opt.brace_style = options.braces_on_own_line ? "expand" : "collapse";
            }
            opt.brace_style = options.brace_style ? options.brace_style : (opt.brace_style ? opt.brace_style : "collapse");

            // graceful handling of deprecated option
            if (opt.brace_style === "expand-strict") {
                opt.brace_style = "expand";
            }

            opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4;
            opt.indent_char = options.indent_char ? options.indent_char : ' ';
            opt.eol = options.eol ? options.eol : 'auto';
            opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
            opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods;
            opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10);
            opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren;
            opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren;
            opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy;
            opt.space_after_anon_function = (options.space_after_anon_function === undefined) ? false : options.space_after_anon_function;
            opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation;
            opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional;
            opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings;
            opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10);
            opt.e4x = (options.e4x === undefined) ? false : options.e4x;
            opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
            opt.comma_first = (options.comma_first === undefined) ? false : options.comma_first;
            opt.operator_position = sanitizeOperatorPosition(options.operator_position);

            // For testing of beautify ignore:start directive
            opt.test_output_raw = (options.test_output_raw === undefined) ? false : options.test_output_raw;

            // force opt.space_after_anon_function to true if opt.jslint_happy
            if (opt.jslint_happy) {
                opt.space_after_anon_function = true;
            }

            if (options.indent_with_tabs) {
                opt.indent_char = '\t';
                opt.indent_size = 1;
            }

            if (opt.eol === 'auto') {
                opt.eol = '\n';
                if (js_source_text && acorn.lineBreak.test(js_source_text || '')) {
                    opt.eol = js_source_text.match(acorn.lineBreak)[0];
                }
            }

            opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n');

            //----------------------------------
            indent_string = '';
            while (opt.indent_size > 0) {
                indent_string += opt.indent_char;
                opt.indent_size -= 1;
            }

            var preindent_index = 0;
            if (js_source_text && js_source_text.length) {
                while ((js_source_text.charAt(preindent_index) === ' ' ||
                        js_source_text.charAt(preindent_index) === '\t')) {
                    baseIndentString += js_source_text.charAt(preindent_index);
                    preindent_index += 1;
                }
                js_source_text = js_source_text.substring(preindent_index);
            }

            last_type = 'TK_START_BLOCK'; // last token type
            last_last_text = ''; // pre-last token text
            output = new Output(indent_string, baseIndentString);

            // If testing the ignore directive, start with output disable set to true
            output.raw = opt.test_output_raw;


            // Stack of parsing/formatting states, including MODE.
            // We tokenize, parse, and output in an almost purely a forward-only stream of token input
            // and formatted output.  This makes the beautifier less accurate than full parsers
            // but also far more tolerant of syntax errors.
            //
            // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type
            // MODE.BlockStatement on the the stack, even though it could be object literal.  If we later
            // encounter a ":", we'll switch to to MODE.ObjectLiteral.  If we then see a ";",
            // most full parsers would die, but the beautifier gracefully falls back to
            // MODE.BlockStatement and continues on.
            flag_store = [];
            set_mode(MODE.BlockStatement);

            this.beautify = function() {

                /*jshint onevar:true */
                var local_token, sweet_code;
                Tokenizer = new tokenizer(js_source_text, opt, indent_string);
                tokens = Tokenizer.tokenize();
                token_pos = 0;

                function get_local_token() {
                    local_token = get_token();
                    return local_token;
                }

                while (get_local_token()) {
                    for (var i = 0; i < local_token.comments_before.length; i++) {
                        // The cleanest handling of inline comments is to treat them as though they aren't there.
                        // Just continue formatting and the behavior should be logical.
                        // Also ignore unknown tokens.  Again, this should result in better behavior.
                        handle_token(local_token.comments_before[i]);
                    }
                    handle_token(local_token);

                    last_last_text = flags.last_text;
                    last_type = local_token.type;
                    flags.last_text = local_token.text;

                    token_pos += 1;
                }

                sweet_code = output.get_code();
                if (opt.end_with_newline) {
                    sweet_code += '\n';
                }

                if (opt.eol !== '\n') {
                    sweet_code = sweet_code.replace(/[\n]/g, opt.eol);
                }

                return sweet_code;
            };

            function handle_token(local_token) {
                var newlines = local_token.newlines;
                var keep_whitespace = opt.keep_array_indentation && is_array(flags.mode);

                if (keep_whitespace) {
                    for (var i = 0; i < newlines; i += 1) {
                        print_newline(i > 0);
                    }
                } else {
                    if (opt.max_preserve_newlines && newlines > opt.max_preserve_newlines) {
                        newlines = opt.max_preserve_newlines;
                    }

                    if (opt.preserve_newlines) {
                        if (local_token.newlines > 1) {
                            print_newline();
                            for (var j = 1; j < newlines; j += 1) {
                                print_newline(true);
                            }
                        }
                    }
                }

                current_token = local_token;
                handlers[current_token.type]();
            }

            // we could use just string.split, but
            // IE doesn't like returning empty strings
            function split_linebreaks(s) {
                //return s.split(/\x0d\x0a|\x0a/);

                s = s.replace(acorn.allLineBreaks, '\n');
                var out = [],
                    idx = s.indexOf("\n");
                while (idx !== -1) {
                    out.push(s.substring(0, idx));
                    s = s.substring(idx + 1);
                    idx = s.indexOf("\n");
                }
                if (s.length) {
                    out.push(s);
                }
                return out;
            }

            var newline_restricted_tokens = ['break', 'contiue', 'return', 'throw'];

            function allow_wrap_or_preserved_newline(force_linewrap) {
                force_linewrap = (force_linewrap === undefined) ? false : force_linewrap;

                // Never wrap the first token on a line
                if (output.just_added_newline()) {
                    return;
                }

                var shouldPreserveOrForce = (opt.preserve_newlines && current_token.wanted_newline) || force_linewrap;
                var operatorLogicApplies = in_array(flags.last_text, Tokenizer.positionable_operators) || in_array(current_token.text, Tokenizer.positionable_operators);

                if (operatorLogicApplies) {
                    var shouldPrintOperatorNewline = (
                            in_array(flags.last_text, Tokenizer.positionable_operators) &&
                            in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)
                        ) ||
                        in_array(current_token.text, Tokenizer.positionable_operators);
                    shouldPreserveOrForce = shouldPreserveOrForce && shouldPrintOperatorNewline;
                }

                if (shouldPreserveOrForce) {
                    print_newline(false, true);
                } else if (opt.wrap_line_length) {
                    if (last_type === 'TK_RESERVED' && in_array(flags.last_text, newline_restricted_tokens)) {
                        // These tokens should never have a newline inserted
                        // between them and the following expression.
                        return;
                    }
                    var proposed_line_length = output.current_line.get_character_count() + current_token.text.length +
                        (output.space_before_token ? 1 : 0);
                    if (proposed_line_length >= opt.wrap_line_length) {
                        print_newline(false, true);
                    }
                }
            }

            function print_newline(force_newline, preserve_statement_flags) {
                if (!preserve_statement_flags) {
                    if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') {
                        while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
                            restore_mode();
                        }
                    }
                }

                if (output.add_new_line(force_newline)) {
                    flags.multiline_frame = true;
                }
            }

            function print_token_line_indentation() {
                if (output.just_added_newline()) {
                    if (opt.keep_array_indentation && is_array(flags.mode) && current_token.wanted_newline) {
                        output.current_line.push(current_token.whitespace_before);
                        output.space_before_token = false;
                    } else if (output.set_indent(flags.indentation_level)) {
                        flags.line_indent_level = flags.indentation_level;
                    }
                }
            }

            function print_token(printable_token) {
                if (output.raw) {
                    output.add_raw_token(current_token);
                    return;
                }

                if (opt.comma_first && last_type === 'TK_COMMA' &&
                    output.just_added_newline()) {
                    if (output.previous_line.last() === ',') {
                        var popped = output.previous_line.pop();
                        // if the comma was already at the start of the line,
                        // pull back onto that line and reprint the indentation
                        if (output.previous_line.is_empty()) {
                            output.previous_line.push(popped);
                            output.trim(true);
                            output.current_line.pop();
                            output.trim();
                        }

                        // add the comma in front of the next token
                        print_token_line_indentation();
                        output.add_token(',');
                        output.space_before_token = true;
                    }
                }

                printable_token = printable_token || current_token.text;
                print_token_line_indentation();
                output.add_token(printable_token);
            }

            function indent() {
                flags.indentation_level += 1;
            }

            function deindent() {
                if (flags.indentation_level > 0 &&
                    ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level)) {
                    flags.indentation_level -= 1;

                }
            }

            function set_mode(mode) {
                if (flags) {
                    flag_store.push(flags);
                    previous_flags = flags;
                } else {
                    previous_flags = create_flags(null, mode);
                }

                flags = create_flags(previous_flags, mode);
            }

            function is_array(mode) {
                return mode === MODE.ArrayLiteral;
            }

            function is_expression(mode) {
                return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]);
            }

            function restore_mode() {
                if (flag_store.length > 0) {
                    previous_flags = flags;
                    flags = flag_store.pop();
                    if (previous_flags.mode === MODE.Statement) {
                        output.remove_redundant_indentation(previous_flags);
                    }
                }
            }

            function start_of_object_property() {
                return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && (
                    (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set'])));
            }

            function start_of_statement() {
                if (
                    (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') ||
                    (last_type === 'TK_RESERVED' && flags.last_text === 'do') ||
                    (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw']) && !current_token.wanted_newline) ||
                    (last_type === 'TK_RESERVED' && flags.last_text === 'else' && !(current_token.type === 'TK_RESERVED' && current_token.text === 'if')) ||
                    (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) ||
                    (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement &&
                        !flags.in_case &&
                        !(current_token.text === '--' || current_token.text === '++') &&
                        last_last_text !== 'function' &&
                        current_token.type !== 'TK_WORD' && current_token.type !== 'TK_RESERVED') ||
                    (flags.mode === MODE.ObjectLiteral && (
                        (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set']))))
                ) {

                    set_mode(MODE.Statement);
                    indent();

                    if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') {
                        flags.declaration_statement = true;
                    }

                    // Issue #276:
                    // If starting a new statement with [if, for, while, do], push to a new line.
                    // if (a) if (b) if(c) d(); else e(); else f();
                    if (!start_of_object_property()) {
                        allow_wrap_or_preserved_newline(
                            current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['do', 'for', 'if', 'while']));
                    }

                    return true;
                }
                return false;
            }

            function all_lines_start_with(lines, c) {
                for (var i = 0; i < lines.length; i++) {
                    var line = trim(lines[i]);
                    if (line.charAt(0) !== c) {
                        return false;
                    }
                }
                return true;
            }

            function each_line_matches_indent(lines, indent) {
                var i = 0,
                    len = lines.length,
                    line;
                for (; i < len; i++) {
                    line = lines[i];
                    // allow empty lines to pass through
                    if (line && line.indexOf(indent) !== 0) {
                        return false;
                    }
                }
                return true;
            }

            function is_special_word(word) {
                return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
            }

            function get_token(offset) {
                var index = token_pos + (offset || 0);
                return (index < 0 || index >= tokens.length) ? null : tokens[index];
            }

            function handle_start_expr() {
                if (start_of_statement()) {
                    // The conditional starts the statement if appropriate.
                }

                var next_mode = MODE.Expression;
                if (current_token.text === '[') {

                    if (last_type === 'TK_WORD' || flags.last_text === ')') {
                        // this is array index specifier, break immediately
                        // a[x], fn()[x]
                        if (last_type === 'TK_RESERVED' && in_array(flags.last_text, Tokenizer.line_starters)) {
                            output.space_before_token = true;
                        }
                        set_mode(next_mode);
                        print_token();
                        indent();
                        if (opt.space_in_paren) {
                            output.space_before_token = true;
                        }
                        return;
                    }

                    next_mode = MODE.ArrayLiteral;
                    if (is_array(flags.mode)) {
                        if (flags.last_text === '[' ||
                            (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) {
                            // ], [ goes to new line
                            // }, [ goes to new line
                            if (!opt.keep_array_indentation) {
                                print_newline();
                            }
                        }
                    }

                } else {
                    if (last_type === 'TK_RESERVED' && flags.last_text === 'for') {
                        next_mode = MODE.ForInitializer;
                    } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) {
                        next_mode = MODE.Conditional;
                    } else {
                        // next_mode = MODE.Expression;
                    }
                }

                if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') {
                    print_newline();
                } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') {
                    // TODO: Consider whether forcing this is required.  Review failing tests when removed.
                    allow_wrap_or_preserved_newline(current_token.wanted_newline);
                    // do nothing on (( and )( and ][ and ]( and .(
                } else if (!(last_type === 'TK_RESERVED' && current_token.text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
                    output.space_before_token = true;
                } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) ||
                    (flags.last_text === '*' && last_last_text === 'function')) {
                    // function() vs function ()
                    if (opt.space_after_anon_function) {
                        output.space_before_token = true;
                    }
                } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, Tokenizer.line_starters) || flags.last_text === 'catch')) {
                    if (opt.space_before_conditional) {
                        output.space_before_token = true;
                    }
                }

                // Should be a space between await and an IIFE
                if (current_token.text === '(' && last_type === 'TK_RESERVED' && flags.last_word === 'await') {
                    output.space_before_token = true;
                }

                // Support of this kind of newline preservation.
                // a = (b &&
                //     (c || d));
                if (current_token.text === '(') {
                    if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
                        if (!start_of_object_property()) {
                            allow_wrap_or_preserved_newline();
                        }
                    }
                }

                // Support preserving wrapped arrow function expressions
                // a.b('c',
                //     () => d.e
                // )
                if (current_token.text === '(' && last_type !== 'TK_WORD' && last_type !== 'TK_RESERVED') {
                    allow_wrap_or_preserved_newline();
                }

                set_mode(next_mode);
                print_token();
                if (opt.space_in_paren) {
                    output.space_before_token = true;
                }

                // In all cases, if we newline while inside an expression it should be indented.
                indent();
            }

            function handle_end_expr() {
                // statements inside expressions are not valid syntax, but...
                // statements must all be closed when their container closes
                while (flags.mode === MODE.Statement) {
                    restore_mode();
                }

                if (flags.multiline_frame) {
                    allow_wrap_or_preserved_newline(current_token.text === ']' && is_array(flags.mode) && !opt.keep_array_indentation);
                }

                if (opt.space_in_paren) {
                    if (last_type === 'TK_START_EXPR' && !opt.space_in_empty_paren) {
                        // () [] no inner space in empty parens like these, ever, ref #320
                        output.trim();
                        output.space_before_token = false;
                    } else {
                        output.space_before_token = true;
                    }
                }
                if (current_token.text === ']' && opt.keep_array_indentation) {
                    print_token();
                    restore_mode();
                } else {
                    restore_mode();
                    print_token();
                }
                output.remove_redundant_indentation(previous_flags);

                // do {} while () // no statement required after
                if (flags.do_while && previous_flags.mode === MODE.Conditional) {
                    previous_flags.mode = MODE.Expression;
                    flags.do_block = false;
                    flags.do_while = false;

                }
            }

            function handle_start_block() {
                // Check if this is should be treated as a ObjectLiteral
                var next_token = get_token(1);
                var second_token = get_token(2);
                if (second_token && (
                        (in_array(second_token.text, [':', ',']) && in_array(next_token.type, ['TK_STRING', 'TK_WORD', 'TK_RESERVED'])) ||
                        (in_array(next_token.text, ['get', 'set']) && in_array(second_token.type, ['TK_WORD', 'TK_RESERVED']))
                    )) {
                    // We don't support TypeScript,but we didn't break it for a very long time.
                    // We'll try to keep not breaking it.
                    if (!in_array(last_last_text, ['class', 'interface'])) {
                        set_mode(MODE.ObjectLiteral);
                    } else {
                        set_mode(MODE.BlockStatement);
                    }
                } else if (last_type === 'TK_OPERATOR' && flags.last_text === '=>') {
                    // arrow function: (param1, paramN) => { statements }
                    set_mode(MODE.BlockStatement);
                } else if (in_array(last_type, ['TK_EQUALS', 'TK_START_EXPR', 'TK_COMMA', 'TK_OPERATOR']) ||
                    (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw', 'import']))
                ) {
                    // Detecting shorthand function syntax is difficult by scanning forward,
                    //     so check the surrounding context.
                    // If the block is being returned, imported, passed as arg,
                    //     assigned with = or assigned in a nested object, treat as an ObjectLiteral.
                    set_mode(MODE.ObjectLiteral);
                } else {
                    set_mode(MODE.BlockStatement);
                }

                var empty_braces = !next_token.comments_before.length && next_token.text === '}';
                var empty_anonymous_function = empty_braces && flags.last_word === 'function' &&
                    last_type === 'TK_END_EXPR';


                if (opt.brace_style === "expand" ||
                    (opt.brace_style === "none" && current_token.wanted_newline)) {
                    if (last_type !== 'TK_OPERATOR' &&
                        (empty_anonymous_function ||
                            last_type === 'TK_EQUALS' ||
                            (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) {
                        output.space_before_token = true;
                    } else {
                        print_newline(false, true);
                    }
                } else { // collapse
                    if (opt.brace_style === 'collapse-preserve-inline') {
                        // search forward for a newline wanted inside this block
                        var index = 0;
                        var check_token = null;
                        flags.inline_frame = true;
                        do {
                            index += 1;
                            check_token = get_token(index);
                            if (check_token.wanted_newline) {
                                flags.inline_frame = false;
                                break;
                            }
                        } while (check_token.type !== 'TK_EOF' &&
                            !(check_token.type === 'TK_END_BLOCK' && check_token.opened === current_token));
                    }

                    if (is_array(previous_flags.mode) && (last_type === 'TK_START_EXPR' || last_type === 'TK_COMMA')) {
                        // if we're preserving inline,
                        // allow newline between comma and next brace.
                        if (last_type === 'TK_COMMA' || opt.space_in_paren) {
                            output.space_before_token = true;
                        }

                        if (opt.brace_style === 'collapse-preserve-inline' &&
                            (last_type === 'TK_COMMA' || (last_type === 'TK_START_EXPR' && flags.inline_frame))) {
                            allow_wrap_or_preserved_newline();
                            previous_flags.multiline_frame = previous_flags.multiline_frame || flags.multiline_frame;
                            flags.multiline_frame = false;
                        }
                    } else if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
                        if (last_type === 'TK_START_BLOCK') {
                            print_newline();
                        } else {
                            output.space_before_token = true;
                        }
                    }
                }
                print_token();
                indent();
            }

            function handle_end_block() {
                // statements must all be closed when their container closes
                while (flags.mode === MODE.Statement) {
                    restore_mode();
                }
                var empty_braces = last_type === 'TK_START_BLOCK';

                if (opt.brace_style === "expand") {
                    if (!empty_braces) {
                        print_newline();
                    }
                } else {
                    // skip {}
                    if (!empty_braces) {
                        if (flags.inline_frame) {
                            output.space_before_token = true;
                        } else if (is_array(flags.mode) && opt.keep_array_indentation) {
                            // we REALLY need a newline here, but newliner would skip that
                            opt.keep_array_indentation = false;
                            print_newline();
                            opt.keep_array_indentation = true;

                        } else {
                            print_newline();
                        }
                    }
                }
                restore_mode();
                print_token();
            }

            function handle_word() {
                if (current_token.type === 'TK_RESERVED') {
                    if (in_array(current_token.text, ['set', 'get']) && flags.mode !== MODE.ObjectLiteral) {
                        current_token.type = 'TK_WORD';
                    } else if (in_array(current_token.text, ['as', 'from']) && !flags.import_block) {
                        current_token.type = 'TK_WORD';
                    } else if (flags.mode === MODE.ObjectLiteral) {
                        var next_token = get_token(1);
                        if (next_token.text === ':') {
                            current_token.type = 'TK_WORD';
                        }
                    }
                }

                if (start_of_statement()) {
                    // The conditional starts the statement if appropriate.
                } else if (current_token.wanted_newline && !is_expression(flags.mode) &&
                    (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) &&
                    last_type !== 'TK_EQUALS' &&
                    (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const', 'set', 'get'])))) {

                    print_newline();
                }

                if (flags.do_block && !flags.do_while) {
                    if (current_token.type === 'TK_RESERVED' && current_token.text === 'while') {
                        // do {} ## while ()
                        output.space_before_token = true;
                        print_token();
                        output.space_before_token = true;
                        flags.do_while = true;
                        return;
                    } else {
                        // do {} should always have while as the next word.
                        // if we don't see the expected while, recover
                        print_newline();
                        flags.do_block = false;
                    }
                }

                // if may be followed by else, or not
                // Bare/inline ifs are tricky
                // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
                if (flags.if_block) {
                    if (!flags.else_block && (current_token.type === 'TK_RESERVED' && current_token.text === 'else')) {
                        flags.else_block = true;
                    } else {
                        while (flags.mode === MODE.Statement) {
                            restore_mode();
                        }
                        flags.if_block = false;
                        flags.else_block = false;
                    }
                }

                if (current_token.type === 'TK_RESERVED' && (current_token.text === 'case' || (current_token.text === 'default' && flags.in_case_statement))) {
                    print_newline();
                    if (flags.case_body || opt.jslint_happy) {
                        // switch cases following one another
                        deindent();
                        flags.case_body = false;
                    }
                    print_token();
                    flags.in_case = true;
                    flags.in_case_statement = true;
                    return;
                }

                if (current_token.type === 'TK_RESERVED' && current_token.text === 'function') {
                    if (in_array(flags.last_text, ['}', ';']) || (output.just_added_newline() && !in_array(flags.last_text, ['[', '{', ':', '=', ',']))) {
                        // make sure there is a nice clean space of at least one blank line
                        // before a new function definition
                        if (!output.just_added_blankline() && !current_token.comments_before.length) {
                            print_newline();
                            print_newline(true);
                        }
                    }
                    if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
                        if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return', 'export', 'async'])) {
                            output.space_before_token = true;
                        } else if (last_type === 'TK_RESERVED' && flags.last_text === 'default' && last_last_text === 'export') {
                            output.space_before_token = true;
                        } else {
                            print_newline();
                        }
                    } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') {
                        // foo = function
                        output.space_before_token = true;
                    } else if (!flags.multiline_frame && (is_expression(flags.mode) || is_array(flags.mode))) {
                        // (function
                    } else {
                        print_newline();
                    }
                }

                if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
                    if (!start_of_object_property()) {
                        allow_wrap_or_preserved_newline();
                    }
                }

                if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['function', 'get', 'set'])) {
                    print_token();
                    flags.last_word = current_token.text;
                    return;
                }

                prefix = 'NONE';

                if (last_type === 'TK_END_BLOCK') {

                    if (!(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally', 'from']))) {
                        prefix = 'NEWLINE';
                    } else {
                        if (opt.brace_style === "expand" ||
                            opt.brace_style === "end-expand" ||
                            (opt.brace_style === "none" && current_token.wanted_newline)) {
                            prefix = 'NEWLINE';
                        } else {
                            prefix = 'SPACE';
                            output.space_before_token = true;
                        }
                    }
                } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) {
                    // TODO: Should this be for STATEMENT as well?
                    prefix = 'NEWLINE';
                } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
                    prefix = 'SPACE';
                } else if (last_type === 'TK_STRING') {
                    prefix = 'NEWLINE';
                } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' ||
                    (flags.last_text === '*' && last_last_text === 'function')) {
                    prefix = 'SPACE';
                } else if (last_type === 'TK_START_BLOCK') {
                    if (flags.inline_frame) {
                        prefix = 'SPACE';
                    } else {
                        prefix = 'NEWLINE';
                    }
                } else if (last_type === 'TK_END_EXPR') {
                    output.space_before_token = true;
                    prefix = 'NEWLINE';
                }

                if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
                    if (flags.last_text === 'else' || flags.last_text === 'export') {
                        prefix = 'SPACE';
                    } else {
                        prefix = 'NEWLINE';
                    }

                }

                if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally'])) {
                    if (!(last_type === 'TK_END_BLOCK' && previous_flags.mode === MODE.BlockStatement) ||
                        opt.brace_style === "expand" ||
                        opt.brace_style === "end-expand" ||
                        (opt.brace_style === "none" && current_token.wanted_newline)) {
                        print_newline();
                    } else {
                        output.trim(true);
                        var line = output.current_line;
                        // If we trimmed and there's something other than a close block before us
                        // put a newline back in.  Handles '} // comment' scenario.
                        if (line.last() !== '}') {
                            print_newline();
                        }
                        output.space_before_token = true;
                    }
                } else if (prefix === 'NEWLINE') {
                    if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
                        // no newline between 'return nnn'
                        output.space_before_token = true;
                    } else if (last_type !== 'TK_END_EXPR') {
                        if ((last_type !== 'TK_START_EXPR' || !(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['var', 'let', 'const']))) && flags.last_text !== ':') {
                            // no need to force newline on 'var': for (var x = 0...)
                            if (current_token.type === 'TK_RESERVED' && current_token.text === 'if' && flags.last_text === 'else') {
                                // no newline for } else if {
                                output.space_before_token = true;
                            } else {
                                print_newline();
                            }
                        }
                    } else if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
                        print_newline();
                    }
                } else if (flags.multiline_frame && is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') {
                    print_newline(); // }, in lists get a newline treatment
                } else if (prefix === 'SPACE') {
                    output.space_before_token = true;
                }
                print_token();
                flags.last_word = current_token.text;

                if (current_token.type === 'TK_RESERVED') {
                    if (current_token.text === 'do') {
                        flags.do_block = true;
                    } else if (current_token.text === 'if') {
                        flags.if_block = true;
                    } else if (current_token.text === 'import') {
                        flags.import_block = true;
                    } else if (flags.import_block && current_token.type === 'TK_RESERVED' && current_token.text === 'from') {
                        flags.import_block = false;
                    }
                }
            }

            function handle_semicolon() {
                if (start_of_statement()) {
                    // The conditional starts the statement if appropriate.
                    // Semicolon can be the start (and end) of a statement
                    output.space_before_token = false;
                }
                while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
                    restore_mode();
                }

                // hacky but effective for the moment
                if (flags.import_block) {
                    flags.import_block = false;
                }
                print_token();
            }

            function handle_string() {
                if (start_of_statement()) {
                    // The conditional starts the statement if appropriate.
                    // One difference - strings want at least a space before
                    output.space_before_token = true;
                } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' || flags.inline_frame) {
                    output.space_before_token = true;
                } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
                    if (!start_of_object_property()) {
                        allow_wrap_or_preserved_newline();
                    }
                } else {
                    print_newline();
                }
                print_token();
            }

            function handle_equals() {
                if (start_of_statement()) {
                    // The conditional starts the statement if appropriate.
                }

                if (flags.declaration_statement) {
                    // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
                    flags.declaration_assignment = true;
                }
                output.space_before_token = true;
                print_token();
                output.space_before_token = true;
            }

            function handle_comma() {
                print_token();
                output.space_before_token = true;
                if (flags.declaration_statement) {
                    if (is_expression(flags.parent.mode)) {
                        // do not break on comma, for(var a = 1, b = 2)
                        flags.declaration_assignment = false;
                    }

                    if (flags.declaration_assignment) {
                        flags.declaration_assignment = false;
                        print_newline(false, true);
                    } else if (opt.comma_first) {
                        // for comma-first, we want to allow a newline before the comma
                        // to turn into a newline after the comma, which we will fixup later
                        allow_wrap_or_preserved_newline();
                    }
                } else if (flags.mode === MODE.ObjectLiteral ||
                    (flags.mode === MODE.Statement && flags.parent.mode === MODE.ObjectLiteral)) {
                    if (flags.mode === MODE.Statement) {
                        restore_mode();
                    }

                    if (!flags.inline_frame) {
                        print_newline();
                    }
                } else if (opt.comma_first) {
                    // EXPR or DO_BLOCK
                    // for comma-first, we want to allow a newline before the comma
                    // to turn into a newline after the comma, which we will fixup later
                    allow_wrap_or_preserved_newline();
                }
            }

            function handle_operator() {
                if (start_of_statement()) {
                    // The conditional starts the statement if appropriate.
                }

                if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
                    // "return" had a special handling in TK_WORD. Now we need to return the favor
                    output.space_before_token = true;
                    print_token();
                    return;
                }

                // hack for actionscript's import .*;
                if (current_token.text === '*' && last_type === 'TK_DOT') {
                    print_token();
                    return;
                }

                if (current_token.text === '::') {
                    // no spaces around exotic namespacing syntax operator
                    print_token();
                    return;
                }

                // Allow line wrapping between operators when operator_position is
                //   set to before or preserve
                if (last_type === 'TK_OPERATOR' && in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)) {
                    allow_wrap_or_preserved_newline();
                }

                if (current_token.text === ':' && flags.in_case) {
                    flags.case_body = true;
                    indent();
                    print_token();
                    print_newline();
                    flags.in_case = false;
                    return;
                }

                var space_before = true;
                var space_after = true;
                var in_ternary = false;
                var isGeneratorAsterisk = current_token.text === '*' && last_type === 'TK_RESERVED' && flags.last_text === 'function';
                var isUnary = in_array(current_token.text, ['-', '+']) && (
                    in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) ||
                    in_array(flags.last_text, Tokenizer.line_starters) ||
                    flags.last_text === ','
                );

                if (current_token.text === ':') {
                    if (flags.ternary_depth === 0) {
                        // Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
                        space_before = false;
                    } else {
                        flags.ternary_depth -= 1;
                        in_ternary = true;
                    }
                } else if (current_token.text === '?') {
                    flags.ternary_depth += 1;
                }

                // let's handle the operator_position option prior to any conflicting logic
                if (!isUnary && !isGeneratorAsterisk && opt.preserve_newlines && in_array(current_token.text, Tokenizer.positionable_operators)) {
                    var isColon = current_token.text === ':';
                    var isTernaryColon = (isColon && in_ternary);
                    var isOtherColon = (isColon && !in_ternary);

                    switch (opt.operator_position) {
                        case OPERATOR_POSITION.before_newline:
                            // if the current token is : and it's not a ternary statement then we set space_before to false
                            output.space_before_token = !isOtherColon;

                            print_token();

                            if (!isColon || isTernaryColon) {
                                allow_wrap_or_preserved_newline();
                            }

                            output.space_before_token = true;
                            return;

                        case OPERATOR_POSITION.after_newline:
                            // if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement,
                            //   then print a newline.

                            output.space_before_token = true;

                            if (!isColon || isTernaryColon) {
                                if (get_token(1).wanted_newline) {
                                    print_newline(false, true);
                                } else {
                                    allow_wrap_or_preserved_newline();
                                }
                            } else {
                                output.space_before_token = false;
                            }

                            print_token();

                            output.space_before_token = true;
                            return;

                        case OPERATOR_POSITION.preserve_newline:
                            if (!isOtherColon) {
                                allow_wrap_or_preserved_newline();
                            }

                            // if we just added a newline, or the current token is : and it's not a ternary statement,
                            //   then we set space_before to false
                            space_before = !(output.just_added_newline() || isOtherColon);

                            output.space_before_token = space_before;
                            print_token();
                            output.space_before_token = true;
                            return;
                    }
                }

                if (in_array(current_token.text, ['--', '++', '!', '~']) || isUnary) {
                    // unary operators (and binary +/- pretending to be unary) special cases

                    space_before = false;
                    space_after = false;

                    // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
                    // if there is a newline between -- or ++ and anything else we should preserve it.
                    if (current_token.wanted_newline && (current_token.text === '--' || current_token.text === '++')) {
                        print_newline(false, true);
                    }

                    if (flags.last_text === ';' && is_expression(flags.mode)) {
                        // for (;; ++i)
                        //        ^^^
                        space_before = true;
                    }

                    if (last_type === 'TK_RESERVED') {
                        space_before = true;
                    } else if (last_type === 'TK_END_EXPR') {
                        space_before = !(flags.last_text === ']' && (current_token.text === '--' || current_token.text === '++'));
                    } else if (last_type === 'TK_OPERATOR') {
                        // a++ + ++b;
                        // a - -b
                        space_before = in_array(current_token.text, ['--', '-', '++', '+']) && in_array(flags.last_text, ['--', '-', '++', '+']);
                        // + and - are not unary when preceeded by -- or ++ operator
                        // a-- + b
                        // a * +b
                        // a - -b
                        if (in_array(current_token.text, ['+', '-']) && in_array(flags.last_text, ['--', '++'])) {
                            space_after = true;
                        }
                    }


                    if (((flags.mode === MODE.BlockStatement && !flags.inline_frame) || flags.mode === MODE.Statement) &&
                        (flags.last_text === '{' || flags.last_text === ';')) {
                        // { foo; --i }
                        // foo(); --bar;
                        print_newline();
                    }
                } else if (isGeneratorAsterisk) {
                    space_before = false;
                    space_after = false;
                }
                output.space_before_token = output.space_before_token || space_before;
                print_token();
                output.space_before_token = space_after;
            }

            function handle_block_comment() {
                if (output.raw) {
                    output.add_raw_token(current_token);
                    if (current_token.directives && current_token.directives.preserve === 'end') {
                        // If we're testing the raw output behavior, do not allow a directive to turn it off.
                        output.raw = opt.test_output_raw;
                    }
                    return;
                }

                if (current_token.directives) {
                    print_newline(false, true);
                    print_token();
                    if (current_token.directives.preserve === 'start') {
                        output.raw = true;
                    }
                    print_newline(false, true);
                    return;
                }

                // inline block
                if (!acorn.isNewLine(current_token.text.charCodeAt(0)) && !current_token.wanted_newline) {
                    output.space_before_token = true;
                    print_token();
                    output.space_before_token = true;
                    return;
                }

                var lines = split_linebreaks(current_token.text);
                var j; // iterator for this case
                var javadoc = false;
                var starless = false;
                var lastIndent = current_token.whitespace_before;
                var lastIndentLength = lastIndent.length;

                // block comment starts with a new line
                print_newline(false, true);
                if (lines.length > 1) {
                    javadoc = all_lines_start_with(lines.slice(1), '*');
                    starless = each_line_matches_indent(lines.slice(1), lastIndent);
                }

                // first line always indented
                print_token(lines[0]);
                for (j = 1; j < lines.length; j++) {
                    print_newline(false, true);
                    if (javadoc) {
                        // javadoc: reformat and re-indent
                        print_token(' ' + ltrim(lines[j]));
                    } else if (starless && lines[j].length > lastIndentLength) {
                        // starless: re-indent non-empty content, avoiding trim
                        print_token(lines[j].substring(lastIndentLength));
                    } else {
                        // normal comments output raw
                        output.add_token(lines[j]);
                    }
                }

                // for comments of more than one line, make sure there's a new line after
                print_newline(false, true);
            }

            function handle_comment() {
                if (current_token.wanted_newline) {
                    print_newline(false, true);
                } else {
                    output.trim(true);
                }

                output.space_before_token = true;
                print_token();
                print_newline(false, true);
            }

            function handle_dot() {
                if (start_of_statement()) {
                    // The conditional starts the statement if appropriate.
                }

                if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
                    output.space_before_token = true;
                } else {
                    // allow preserved newlines before dots in general
                    // force newlines on dots after close paren when break_chained - for bar().baz()
                    allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods);
                }

                print_token();
            }

            function handle_unknown() {
                print_token();

                if (current_token.text[current_token.text.length - 1] === '\n') {
                    print_newline();
                }
            }

            function handle_eof() {
                // Unwind any open statements
                while (flags.mode === MODE.Statement) {
                    restore_mode();
                }
            }
        }


        function OutputLine(parent) {
            var _character_count = 0;
            // use indent_count as a marker for lines that have preserved indentation
            var _indent_count = -1;

            var _items = [];
            var _empty = true;

            this.set_indent = function(level) {
                _character_count = parent.baseIndentLength + level * parent.indent_length;
                _indent_count = level;
            };

            this.get_character_count = function() {
                return _character_count;
            };

            this.is_empty = function() {
                return _empty;
            };

            this.last = function() {
                if (!this._empty) {
                    return _items[_items.length - 1];
                } else {
                    return null;
                }
            };

            this.push = function(input) {
                _items.push(input);
                _character_count += input.length;
                _empty = false;
            };

            this.pop = function() {
                var item = null;
                if (!_empty) {
                    item = _items.pop();
                    _character_count -= item.length;
                    _empty = _items.length === 0;
                }
                return item;
            };

            this.remove_indent = function() {
                if (_indent_count > 0) {
                    _indent_count -= 1;
                    _character_count -= parent.indent_length;
                }
            };

            this.trim = function() {
                while (this.last() === ' ') {
                    _items.pop();
                    _character_count -= 1;
                }
                _empty = _items.length === 0;
            };

            this.toString = function() {
                var result = '';
                if (!this._empty) {
                    if (_indent_count >= 0) {
                        result = parent.indent_cache[_indent_count];
                    }
                    result += _items.join('');
                }
                return result;
            };
        }

        function Output(indent_string, baseIndentString) {
            baseIndentString = baseIndentString || '';
            this.indent_cache = [baseIndentString];
            this.baseIndentLength = baseIndentString.length;
            this.indent_length = indent_string.length;
            this.raw = false;

            var lines = [];
            this.baseIndentString = baseIndentString;
            this.indent_string = indent_string;
            this.previous_line = null;
            this.current_line = null;
            this.space_before_token = false;

            this.add_outputline = function() {
                this.previous_line = this.current_line;
                this.current_line = new OutputLine(this);
                lines.push(this.current_line);
            };

            // initialize
            this.add_outputline();


            this.get_line_number = function() {
                return lines.length;
            };

            // Using object instead of string to allow for later expansion of info about each line
            this.add_new_line = function(force_newline) {
                if (this.get_line_number() === 1 && this.just_added_newline()) {
                    return false; // no newline on start of file
                }

                if (force_newline || !this.just_added_newline()) {
                    if (!this.raw) {
                        this.add_outputline();
                    }
                    return true;
                }

                return false;
            };

            this.get_code = function() {
                var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, '');
                return sweet_code;
            };

            this.set_indent = function(level) {
                // Never indent your first output indent at the start of the file
                if (lines.length > 1) {
                    while (level >= this.indent_cache.length) {
                        this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string);
                    }

                    this.current_line.set_indent(level);
                    return true;
                }
                this.current_line.set_indent(0);
                return false;
            };

            this.add_raw_token = function(token) {
                for (var x = 0; x < token.newlines; x++) {
                    this.add_outputline();
                }
                this.current_line.push(token.whitespace_before);
                this.current_line.push(token.text);
                this.space_before_token = false;
            };

            this.add_token = function(printable_token) {
                this.add_space_before_token();
                this.current_line.push(printable_token);
            };

            this.add_space_before_token = function() {
                if (this.space_before_token && !this.just_added_newline()) {
                    this.current_line.push(' ');
                }
                this.space_before_token = false;
            };

            this.remove_redundant_indentation = function(frame) {
                // This implementation is effective but has some issues:
                //     - can cause line wrap to happen too soon due to indent removal
                //           after wrap points are calculated
                // These issues are minor compared to ugly indentation.

                if (frame.multiline_frame ||
                    frame.mode === MODE.ForInitializer ||
                    frame.mode === MODE.Conditional) {
                    return;
                }

                // remove one indent from each line inside this section
                var index = frame.start_line_index;

                var output_length = lines.length;
                while (index < output_length) {
                    lines[index].remove_indent();
                    index++;
                }
            };

            this.trim = function(eat_newlines) {
                eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;

                this.current_line.trim(indent_string, baseIndentString);

                while (eat_newlines && lines.length > 1 &&
                    this.current_line.is_empty()) {
                    lines.pop();
                    this.current_line = lines[lines.length - 1];
                    this.current_line.trim();
                }

                this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null;
            };

            this.just_added_newline = function() {
                return this.current_line.is_empty();
            };

            this.just_added_blankline = function() {
                if (this.just_added_newline()) {
                    if (lines.length === 1) {
                        return true; // start of the file and newline = blank
                    }

                    var line = lines[lines.length - 2];
                    return line.is_empty();
                }
                return false;
            };
        }


        var Token = function(type, text, newlines, whitespace_before, parent) {
            this.type = type;
            this.text = text;
            this.comments_before = [];
            this.newlines = newlines || 0;
            this.wanted_newline = newlines > 0;
            this.whitespace_before = whitespace_before || '';
            this.parent = parent || null;
            this.opened = null;
            this.directives = null;
        };

        function tokenizer(input, opts) {

            var whitespace = "\n\r\t ".split('');
            var digit = /[0-9]/;
            var digit_bin = /[01]/;
            var digit_oct = /[01234567]/;
            var digit_hex = /[0123456789abcdefABCDEF]/;

            this.positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ');
            var punct = this.positionable_operators.concat(
                // non-positionable operators - these do not follow operator position settings
                '! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~'.split(' '));

            // words which should always start on new line.
            this.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',');
            var reserved_words = this.line_starters.concat(['do', 'in', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as']);

            //  /* ... */ comment ends with nearest */ or end of file
            var block_comment_pattern = /([\s\S]*?)((?:\*\/)|$)/g;

            // comment ends just before nearest linefeed or end of file
            var comment_pattern = /([^\n\r\u2028\u2029]*)/g;

            var directives_block_pattern = /\/\* beautify( \w+[:]\w+)+ \*\//g;
            var directive_pattern = / (\w+)[:](\w+)/g;
            var directives_end_ignore_pattern = /([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)/g;

            var template_pattern = /((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)/g;

            var n_newlines, whitespace_before_token, in_html_comment, tokens, parser_pos;
            var input_length;

            this.tokenize = function() {
                // cache the source's length.
                input_length = input.length;
                parser_pos = 0;
                in_html_comment = false;
                tokens = [];

                var next, last;
                var token_values;
                var open = null;
                var open_stack = [];
                var comments = [];

                while (!(last && last.type === 'TK_EOF')) {
                    token_values = tokenize_next();
                    next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
                    while (next.type === 'TK_COMMENT' || next.type === 'TK_BLOCK_COMMENT' || next.type === 'TK_UNKNOWN') {
                        if (next.type === 'TK_BLOCK_COMMENT') {
                            next.directives = token_values[2];
                        }
                        comments.push(next);
                        token_values = tokenize_next();
                        next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
                    }

                    if (comments.length) {
                        next.comments_before = comments;
                        comments = [];
                    }

                    if (next.type === 'TK_START_BLOCK' || next.type === 'TK_START_EXPR') {
                        next.parent = last;
                        open_stack.push(open);
                        open = next;
                    } else if ((next.type === 'TK_END_BLOCK' || next.type === 'TK_END_EXPR') &&
                        (open && (
                            (next.text === ']' && open.text === '[') ||
                            (next.text === ')' && open.text === '(') ||
                            (next.text === '}' && open.text === '{')))) {
                        next.parent = open.parent;
                        next.opened = open;

                        open = open_stack.pop();
                    }

                    tokens.push(next);
                    last = next;
                }

                return tokens;
            };

            function get_directives(text) {
                if (!text.match(directives_block_pattern)) {
                    return null;
                }

                var directives = {};
                directive_pattern.lastIndex = 0;
                var directive_match = directive_pattern.exec(text);

                while (directive_match) {
                    directives[directive_match[1]] = directive_match[2];
                    directive_match = directive_pattern.exec(text);
                }

                return directives;
            }

            function tokenize_next() {
                var resulting_string;
                var whitespace_on_this_line = [];

                n_newlines = 0;
                whitespace_before_token = '';

                if (parser_pos >= input_length) {
                    return ['', 'TK_EOF'];
                }

                var last_token;
                if (tokens.length) {
                    last_token = tokens[tokens.length - 1];
                } else {
                    // For the sake of tokenizing we can pretend that there was on open brace to start
                    last_token = new Token('TK_START_BLOCK', '{');
                }


                var c = input.charAt(parser_pos);
                parser_pos += 1;

                while (in_array(c, whitespace)) {

                    if (acorn.isNewLine(c.charCodeAt(0))) {
                        if (!(c === '\n' && input.charAt(parser_pos - 2) === '\r')) {
                            n_newlines += 1;
                            whitespace_on_this_line = [];
                        }
                    } else {
                        whitespace_on_this_line.push(c);
                    }

                    if (parser_pos >= input_length) {
                        return ['', 'TK_EOF'];
                    }

                    c = input.charAt(parser_pos);
                    parser_pos += 1;
                }

                if (whitespace_on_this_line.length) {
                    whitespace_before_token = whitespace_on_this_line.join('');
                }

                if (digit.test(c) || (c === '.' && digit.test(input.charAt(parser_pos)))) {
                    var allow_decimal = true;
                    var allow_e = true;
                    var local_digit = digit;

                    if (c === '0' && parser_pos < input_length && /[XxOoBb]/.test(input.charAt(parser_pos))) {
                        // switch to hex/oct/bin number, no decimal or e, just hex/oct/bin digits
                        allow_decimal = false;
                        allow_e = false;
                        if (/[Bb]/.test(input.charAt(parser_pos))) {
                            local_digit = digit_bin;
                        } else if (/[Oo]/.test(input.charAt(parser_pos))) {
                            local_digit = digit_oct;
                        } else {
                            local_digit = digit_hex;
                        }
                        c += input.charAt(parser_pos);
                        parser_pos += 1;
                    } else if (c === '.') {
                        // Already have a decimal for this literal, don't allow another
                        allow_decimal = false;
                    } else {
                        // we know this first loop will run.  It keeps the logic simpler.
                        c = '';
                        parser_pos -= 1;
                    }

                    // Add the digits
                    while (parser_pos < input_length && local_digit.test(input.charAt(parser_pos))) {
                        c += input.charAt(parser_pos);
                        parser_pos += 1;

                        if (allow_decimal && parser_pos < input_length && input.charAt(parser_pos) === '.') {
                            c += input.charAt(parser_pos);
                            parser_pos += 1;
                            allow_decimal = false;
                        } else if (allow_e && parser_pos < input_length && /[Ee]/.test(input.charAt(parser_pos))) {
                            c += input.charAt(parser_pos);
                            parser_pos += 1;

                            if (parser_pos < input_length && /[+-]/.test(input.charAt(parser_pos))) {
                                c += input.charAt(parser_pos);
                                parser_pos += 1;
                            }

                            allow_e = false;
                            allow_decimal = false;
                        }
                    }

                    return [c, 'TK_WORD'];
                }

                if (acorn.isIdentifierStart(input.charCodeAt(parser_pos - 1))) {
                    if (parser_pos < input_length) {
                        while (acorn.isIdentifierChar(input.charCodeAt(parser_pos))) {
                            c += input.charAt(parser_pos);
                            parser_pos += 1;
                            if (parser_pos === input_length) {
                                break;
                            }
                        }
                    }

                    if (!(last_token.type === 'TK_DOT' ||
                            (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get']))) &&
                        in_array(c, reserved_words)) {
                        if (c === 'in') { // hack for 'in' operator
                            return [c, 'TK_OPERATOR'];
                        }
                        return [c, 'TK_RESERVED'];
                    }

                    return [c, 'TK_WORD'];
                }

                if (c === '(' || c === '[') {
                    return [c, 'TK_START_EXPR'];
                }

                if (c === ')' || c === ']') {
                    return [c, 'TK_END_EXPR'];
                }

                if (c === '{') {
                    return [c, 'TK_START_BLOCK'];
                }

                if (c === '}') {
                    return [c, 'TK_END_BLOCK'];
                }

                if (c === ';') {
                    return [c, 'TK_SEMICOLON'];
                }

                if (c === '/') {
                    var comment = '';
                    var comment_match;
                    // peek for comment /* ... */
                    if (input.charAt(parser_pos) === '*') {
                        parser_pos += 1;
                        block_comment_pattern.lastIndex = parser_pos;
                        comment_match = block_comment_pattern.exec(input);
                        comment = '/*' + comment_match[0];
                        parser_pos += comment_match[0].length;
                        var directives = get_directives(comment);
                        if (directives && directives.ignore === 'start') {
                            directives_end_ignore_pattern.lastIndex = parser_pos;
                            comment_match = directives_end_ignore_pattern.exec(input);
                            comment += comment_match[0];
                            parser_pos += comment_match[0].length;
                        }
                        comment = comment.replace(acorn.allLineBreaks, '\n');
                        return [comment, 'TK_BLOCK_COMMENT', directives];
                    }
                    // peek for comment // ...
                    if (input.charAt(parser_pos) === '/') {
                        parser_pos += 1;
                        comment_pattern.lastIndex = parser_pos;
                        comment_match = comment_pattern.exec(input);
                        comment = '//' + comment_match[0];
                        parser_pos += comment_match[0].length;
                        return [comment, 'TK_COMMENT'];
                    }

                }

                var startXmlRegExp = /^<([-a-zA-Z:0-9_.]+|{.+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{.+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{.+?}))*\s*(\/?)\s*>/;

                if (c === '`' || c === "'" || c === '"' || // string
                    (
                        (c === '/') || // regexp
                        (opts.e4x && c === "<" && input.slice(parser_pos - 1).match(startXmlRegExp)) // xml
                    ) && ( // regex and xml can only appear in specific locations during parsing
                        (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) ||
                        (last_token.type === 'TK_END_EXPR' && last_token.text === ')' &&
                            last_token.parent && last_token.parent.type === 'TK_RESERVED' && in_array(last_token.parent.text, ['if', 'while', 'for'])) ||
                        (in_array(last_token.type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK',
                            'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'
                        ]))
                    )) {

                    var sep = c,
                        esc = false,
                        has_char_escapes = false;

                    resulting_string = c;

                    if (sep === '/') {
                        //
                        // handle regexp
                        //
                        var in_char_class = false;
                        while (parser_pos < input_length &&
                            ((esc || in_char_class || input.charAt(parser_pos) !== sep) &&
                                !acorn.isNewLine(input.charCodeAt(parser_pos)))) {
                            resulting_string += input.charAt(parser_pos);
                            if (!esc) {
                                esc = input.charAt(parser_pos) === '\\';
                                if (input.charAt(parser_pos) === '[') {
                                    in_char_class = true;
                                } else if (input.charAt(parser_pos) === ']') {
                                    in_char_class = false;
                                }
                            } else {
                                esc = false;
                            }
                            parser_pos += 1;
                        }
                    } else if (opts.e4x && sep === '<') {
                        //
                        // handle e4x xml literals
                        //

                        var xmlRegExp = /<(\/?)([-a-zA-Z:0-9_.]+|{.+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{.+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{.+?}))*\s*(\/?)\s*>/g;
                        var xmlStr = input.slice(parser_pos - 1);
                        var match = xmlRegExp.exec(xmlStr);
                        if (match && match.index === 0) {
                            var rootTag = match[2];
                            var depth = 0;
                            while (match) {
                                var isEndTag = !!match[1];
                                var tagName = match[2];
                                var isSingletonTag = (!!match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA[");
                                if (tagName === rootTag && !isSingletonTag) {
                                    if (isEndTag) {
                                        --depth;
                                    } else {
                                        ++depth;
                                    }
                                }
                                if (depth <= 0) {
                                    break;
                                }
                                match = xmlRegExp.exec(xmlStr);
                            }
                            var xmlLength = match ? match.index + match[0].length : xmlStr.length;
                            xmlStr = xmlStr.slice(0, xmlLength);
                            parser_pos += xmlLength - 1;
                            xmlStr = xmlStr.replace(acorn.allLineBreaks, '\n');
                            return [xmlStr, "TK_STRING"];
                        }
                    } else {
                        //
                        // handle string
                        //
                        var parse_string = function(delimiter, allow_unescaped_newlines, start_sub) {
                            // Template strings can travers lines without escape characters.
                            // Other strings cannot
                            var current_char;
                            while (parser_pos < input_length) {
                                current_char = input.charAt(parser_pos);
                                if (!(esc || (current_char !== delimiter &&
                                        (allow_unescaped_newlines || !acorn.isNewLine(current_char.charCodeAt(0)))))) {
                                    break;
                                }

                                // Handle \r\n linebreaks after escapes or in template strings
                                if ((esc || allow_unescaped_newlines) && acorn.isNewLine(current_char.charCodeAt(0))) {
                                    if (current_char === '\r' && input.charAt(parser_pos + 1) === '\n') {
                                        parser_pos += 1;
                                        current_char = input.charAt(parser_pos);
                                    }
                                    resulting_string += '\n';
                                } else {
                                    resulting_string += current_char;
                                }
                                if (esc) {
                                    if (current_char === 'x' || current_char === 'u') {
                                        has_char_escapes = true;
                                    }
                                    esc = false;
                                } else {
                                    esc = current_char === '\\';
                                }

                                parser_pos += 1;

                                if (start_sub && resulting_string.indexOf(start_sub, resulting_string.length - start_sub.length) !== -1) {
                                    if (delimiter === '`') {
                                        parse_string('}', allow_unescaped_newlines, '`');
                                    } else {
                                        parse_string('`', allow_unescaped_newlines, '${');
                                    }
                                }
                            }
                        };

                        if (sep === '`') {
                            parse_string('`', true, '${');
                        } else {
                            parse_string(sep);
                        }
                    }

                    if (has_char_escapes && opts.unescape_strings) {
                        resulting_string = unescape_string(resulting_string);
                    }

                    if (parser_pos < input_length && input.charAt(parser_pos) === sep) {
                        resulting_string += sep;
                        parser_pos += 1;

                        if (sep === '/') {
                            // regexps may have modifiers /regexp/MOD , so fetch those, too
                            // Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
                            while (parser_pos < input_length && acorn.isIdentifierStart(input.charCodeAt(parser_pos))) {
                                resulting_string += input.charAt(parser_pos);
                                parser_pos += 1;
                            }
                        }
                    }
                    return [resulting_string, 'TK_STRING'];
                }

                if (c === '#') {

                    if (tokens.length === 0 && input.charAt(parser_pos) === '!') {
                        // shebang
                        resulting_string = c;
                        while (parser_pos < input_length && c !== '\n') {
                            c = input.charAt(parser_pos);
                            resulting_string += c;
                            parser_pos += 1;
                        }
                        return [trim(resulting_string) + '\n', 'TK_UNKNOWN'];
                    }



                    // Spidermonkey-specific sharp variables for circular references
                    // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
                    // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
                    var sharp = '#';
                    if (parser_pos < input_length && digit.test(input.charAt(parser_pos))) {
                        do {
                            c = input.charAt(parser_pos);
                            sharp += c;
                            parser_pos += 1;
                        } while (parser_pos < input_length && c !== '#' && c !== '=');
                        if (c === '#') {
                            //
                        } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') {
                            sharp += '[]';
                            parser_pos += 2;
                        } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') {
                            sharp += '{}';
                            parser_pos += 2;
                        }
                        return [sharp, 'TK_WORD'];
                    }
                }

                if (c === '<' && (input.charAt(parser_pos) === '?' || input.charAt(parser_pos) === '%')) {
                    template_pattern.lastIndex = parser_pos - 1;
                    var template_match = template_pattern.exec(input);
                    if (template_match) {
                        c = template_match[0];
                        parser_pos += c.length - 1;
                        c = c.replace(acorn.allLineBreaks, '\n');
                        return [c, 'TK_STRING'];
                    }
                }

                if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') {
                    parser_pos += 3;
                    c = '<!--';
                    while (!acorn.isNewLine(input.charCodeAt(parser_pos)) && parser_pos < input_length) {
                        c += input.charAt(parser_pos);
                        parser_pos++;
                    }
                    in_html_comment = true;
                    return [c, 'TK_COMMENT'];
                }

                if (c === '-' && in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') {
                    in_html_comment = false;
                    parser_pos += 2;
                    return ['-->', 'TK_COMMENT'];
                }

                if (c === '.') {
                    return [c, 'TK_DOT'];
                }

                if (in_array(c, punct)) {
                    while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) {
                        c += input.charAt(parser_pos);
                        parser_pos += 1;
                        if (parser_pos >= input_length) {
                            break;
                        }
                    }

                    if (c === ',') {
                        return [c, 'TK_COMMA'];
                    } else if (c === '=') {
                        return [c, 'TK_EQUALS'];
                    } else {
                        return [c, 'TK_OPERATOR'];
                    }
                }

                return [c, 'TK_UNKNOWN'];
            }


            function unescape_string(s) {
                var esc = false,
                    out = '',
                    pos = 0,
                    s_hex = '',
                    escaped = 0,
                    c;

                while (esc || pos < s.length) {

                    c = s.charAt(pos);
                    pos++;

                    if (esc) {
                        esc = false;
                        if (c === 'x') {
                            // simple hex-escape \x24
                            s_hex = s.substr(pos, 2);
                            pos += 2;
                        } else if (c === 'u') {
                            // unicode-escape, \u2134
                            s_hex = s.substr(pos, 4);
                            pos += 4;
                        } else {
                            // some common escape, e.g \n
                            out += '\\' + c;
                            continue;
                        }
                        if (!s_hex.match(/^[0123456789abcdefABCDEF]+$/)) {
                            // some weird escaping, bail out,
                            // leaving whole string intact
                            return s;
                        }

                        escaped = parseInt(s_hex, 16);

                        if (escaped >= 0x00 && escaped < 0x20) {
                            // leave 0x00...0x1f escaped
                            if (c === 'x') {
                                out += '\\x' + s_hex;
                            } else {
                                out += '\\u' + s_hex;
                            }
                            continue;
                        } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) {
                            // single-quote, apostrophe, backslash - escape these
                            out += '\\' + String.fromCharCode(escaped);
                        } else if (c === 'x' && escaped > 0x7e && escaped <= 0xff) {
                            // we bail out on \x7f..\xff,
                            // leaving whole string escaped,
                            // as it's probably completely binary
                            return s;
                        } else {
                            out += String.fromCharCode(escaped);
                        }
                    } else if (c === '\\') {
                        esc = true;
                    } else {
                        out += c;
                    }
                }
                return out;
            }
        }

        var beautifier = new Beautifier(js_source_text, options);
        return beautifier.beautify();

    }

	return {
		js_beautify: js_beautify
	};
});

/*******************************************************************************
 * @license
 * Copyright (c) 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *	 IBM Corporation - Allow original requirejs plugin to find files in Orion workspace
 *******************************************************************************/
/* eslint-disable missing-nls */
/*eslint-env node, amd*/
/*globals tern tern */
define('javascript/ternPlugins/beautifier',[
	"tern/lib/tern",
	"beautifier/beautify-js"
], function(tern, Beautifier) {

	tern.registerPlugin("beautifier", /* @callback */ function(server, options) {
		return {
			//don't need any passes yet
		};
	});

	tern.defineQueryType("beautify", {
		takesFile: true,
		/**
		 * @callback
		 */
		run: function(server, query, file) {
			return format(query, file);
		}
	});

	/**
	 * @description Format the code using the right beautifier
	 * @param {Object} query The original Tern query object
	 * @param {Object} file The file object from Tern 
	 */
	function format(query, file) {
		var args = query.args;
		var text = file.text;
		if (args) {
			if (args.start && args.end) {
				text = file.text.substring(args.start, args.end);
			}
			return Beautifier.js_beautify(text, args.config);
		}
		return text;
	}
});

/*******************************************************************************
 * @license
 * Copyright (c) 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors:
 *     IBM Corporation - Allow original requirejs plugin to find files in Orion workspace
 *******************************************************************************/
/* eslint-disable missing-nls */
/*eslint-env node, amd*/
/*globals tern tern*/
define('javascript/ternPlugins/async_await',[
	"tern/lib/tern",
	"javascript/finder"
], function(tern, Finder) {

	tern.registerPlugin("async_await", /* @callback */ function(server, options) {
		var cachedQuery;
		
		return {
			passes: {
     			/**
     			 * @callback
     			 */
     			completion: function(file, query) {
     				cachedQuery = query;
     			},
		      	/**
		      	 * @callback
		      	 */
		      	variableCompletion: function(file, start, end, gather) {
		      		if(cachedQuery.ecma >= 9 && (cachedQuery.includeKeywords || cachedQuery.includeKeywords === undefined)) {
		      			//always add async
		      			gather('async', null, 0, function(c) {
	      					c.isKeyword = true;
	      				});
	      				//optionally add await, iff we are inside an async closure
		      			var node = Finder.findNode(start, file.ast, {parents: true});
		      			if(node && Finder.inAsync(node)) {
		      				gather('await', null, 0, function(c) {
		      					c.isKeyword = true;
		      				});
		      			}
					}
				}
			}
		};
	});

});
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/* eslint-env amd*/
/* eslint-disable missing-nls */
define('javascript/plugins/ternDefaults',[
	'javascript/plugins/ternMetadata',
	'json!tern/defs/ecma5.json',
	'json!tern/defs/ecma6.json',
	'json!tern/defs/ecma7.json',
	'json!tern/defs/browser.json',
	'json!tern/defs/chai.json',
	
	//tern defaults
	"tern/plugin/angular",
	"tern/plugin/doc_comment",
	"tern/plugin/es_modules",
	'tern/plugin/node',
	'tern/plugin/commonjs',
	'tern/plugin/requirejs',
	
	//orion defaults
	"javascript/ternPlugins/amqp",
	"javascript/ternPlugins/ast",
	"javascript/ternPlugins/eslint",
	"javascript/ternPlugins/express",
	"javascript/ternPlugins/html",
	"javascript/ternPlugins/jsdoc",
	"javascript/ternPlugins/mongodb",
	"javascript/ternPlugins/mysql",
	"javascript/ternPlugins/occurrences",
	"javascript/ternPlugins/open_impl",
	"javascript/ternPlugins/outliner",
	"javascript/ternPlugins/plugins",
	"javascript/ternPlugins/postgres",
	"javascript/ternPlugins/redis",
	"javascript/ternPlugins/refs",
	"javascript/ternPlugins/templates",
	"javascript/ternPlugins/quickfixes",
	"javascript/ternPlugins/beautifier",
	"javascript/ternPlugins/resolver",
	"javascript/ternPlugins/async_await" //TODO remove once Tern provides built-in support
], function(TernMetadata, ecma5, ecma6, ecma7, browser, chai) {
	var defs = [ecma5, ecma6, ecma7, browser, chai];
	
	var defNames = TernMetadata.defNames;
	var plugins = TernMetadata.plugins;
	
	var serverOptions = {
		async: true,
        debug: false,
        projectDir: 'orionFakeProjectDir', // Tern strips the project dir from any file paths it finds and defaults to '/' so this must be set
        defs: defs,
        ecmaVersion: 7,
        optionalDefs: {
        	"ecma5": true,
        	"ecma6": true,
        	"ecma7": true,
        	"browser": true,
        	"chai": true,
        },
        optionalPlugins: {
        	'amqp': 'amqp', 
        	'angular': 'angular', 
        	'express': 'express', 
        	'es_modules': 'es_modules',
        	'mongodb': 'mongodb',
        	'mongo': 'mongodb', //ESlint provides mongo - map it to Tern
        	'mysql': 'mysql', 
        	'node': 'node',
        	'commonjs': 'commonjs',
        	'pg': 'postgres', 
        	'redis': 'redis', 
        	'amd': 'requirejs'
        }
	};
	
	/**
	 * @description Returns a clone of the deafult server options
	 * @private
	 * @returns {Object} A clone the default server options
	 */
	function _serverOptions() {
		var opts = {};
		Object.keys(serverOptions).forEach(function(key) {
			opts[key] = serverOptions[key];
		});
		opts.plugins = {};
		Object.keys(plugins.required).forEach(function(key) {
			opts.plugins[key] = plugins.required[key];
		});
		return opts;
	}
	
	return {
		plugins: plugins,
		defs: defs,
		defNames: defNames,
		serverOptions: _serverOptions
	};
});

/*******************************************************************************
 * @license
 * Copyright (c) 2013 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 * 
 * Contributors: IBM Corporation - initial API and implementation
 ******************************************************************************/
/*eslint-env browser, amd*/
define('orion/serialize',[], function() {

	/**
	 * Converts an <code>Error</code> into a regular Object.
	 * @memberof module:orion/serialize
	 * @param {Error|Object} error
	 * @returns {Object}
	 */
	function serializeError(error) {
		var result = error ? JSON.parse(JSON.stringify(error)) : error; // sanitizing Error object
		if (error instanceof Error) {
			result.__isError = true;
			result.lineNumber = typeof(result.lineNumber) === 'number' ? result.lineNumber : error.lineNumber; //FF fails to include the line number from JSON.stringify
			result.message = result.message || error.message;
			result.name = result.name || error.name;
			result.stack = result.stack || error.stack;
		}
		return result;
	}

	/**
	 * @exports orion/serialize
	 */
	return {
		serializeError: serializeError
	};
});
/*******************************************************************************
 * @license
 * Copyright (c) 2012, 2015 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 *
 * Contributors: IBM Corporation - initial API and implementation
 *******************************************************************************/
/*eslint-env browser, amd*/
/*global requirejs*/
define('orion/i18nUtil',[], function() {
	/**
	 * Performs string substitution. Can be invoked in 2 ways:
	 *
	 * i) vargs giving numbered substition values:
	 *   formatMessage("${0} is ${1}", "foo", "bar")  // "foo is bar"
	 *
	 * ii) a map giving the substitutions:
	 *   formatMessage("${thing} is ${1}", {1: "bar", thing: "foo"})  // "foo is bar"
	 */
	function formatMessage(msg) {
		var pattern = /\$\{([^\}]+)\}/g, args = arguments;
		if (args.length === 2 && args[1] && typeof args[1] === "object") {
			return msg.replace(pattern, function(str, key) {
				return args[1][key];
			});
		}
		return msg.replace(pattern, function(str, index) {
			return args[(index << 0) + 1];
		});
	}
	return {
		formatMessage: formatMessage
	};
});
/*******************************************************************************
 * @license
 * Copyright (c) 2015, 2017 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*globals onmessage:true onconnect:true requirejs*/
/*eslint-env node, browser*/
var lang = 'en'; //$NON-NLS-1$
var sear = self.location.search;
if (sear) {
  var langs = sear.split('worker-language'); //$NON-NLS-1$
  if (Array.isArray(langs) && langs.length === 2) {
    lang = langs[1].slice(1);
    if (lang) {
      lang = lang.toLocaleLowerCase();
    }
  }
}
requirejs.config({locale: lang});
require([
    'tern/lib/tern',
    'javascript/plugins/ternDefaults',
    'orion/Deferred',
    "orion/objects",
    'orion/serialize',
    'i18n!javascript/nls/messages',
    'orion/i18nUtil'
  ],
  function (Tern, defaultOptions, Deferred, Objects, Serialize, Messages, i18nUtil) {

    var ternserver = null;

    function mergeArray(target, source) {
      if (Array.isArray(target) && Array.isArray(source)) {
        if (target.length < 1) {
          for (var i = 0, len = source.length; i < len; i++) {
            target.push(source[i]);
          }
        } else {
          for (i = 0, len = source.length; i < len; i++) {
            if (target.indexOf(source[i]) < 0) {
              target.push(source[i]);
            }
          }
        }
      }
    }

    function removeEntry(target, item) {
      if (Array.isArray(target)) {
        var idx = target.indexOf(item);
        if (idx > -1) {
          target.slice(idx, idx + 1);
        }
      }
    }

    /**
     * @param {Object} jsonOptions The optional map of JSON options to start the server with
     * @param {Function} callback The optional function to callback to
     * @description Start up the Tern server, send a message after trying
     */
    function startServer(jsonOptions, callback) {
      if (ternserver) {
        ternserver.reset();
        ternserver = null;
      }
      var options = defaultOptions.serverOptions();
      options.getFile = _getFile;

      var defNames = [], plugins, projectLoc;
      if (jsonOptions) {
        projectLoc = jsonOptions.projectLoc;
        plugins = jsonOptions.plugins;
        if (jsonOptions.libs) {
          mergeArray(defNames, jsonOptions.libs);
        }
        if (jsonOptions.defs) {
          mergeArray(defNames, jsonOptions.defs);
        }
        if (Array.isArray(jsonOptions.loadEagerly) && jsonOptions.loadEagerly.length > 0) {
          options.loadEagerly = jsonOptions.loadEagerly;
        }
        if (typeof jsonOptions.ecmaVersion === 'number') {
          options.ecmaVersion = jsonOptions.ecmaVersion;
          if (options.ecmaVersion === 5) {
            mergeArray(defNames, ['ecma5']);
            removeEntry(defNames, 'ecma6');
            removeEntry(defNames, 'ecma7');
          } else if (options.ecmaVersion === 6) {
            mergeArray(defNames, ["ecma5", "ecma6"]);
            removeEntry(defNames, 'ecma7');
          } else if (options.ecmaVersion === 7) {
            mergeArray(defNames, ["ecma5", "ecma6", "ecma7"]);
          }
        } else {
          mergeArray(defNames, ["ecma5", "ecma6", "ecma7"]);
        }
        if (typeof jsonOptions.sourceType === 'string') {
          options.sourceType = jsonOptions.sourceType;
        }
        if (typeof jsonOptions.dependencyBudget === 'number') {
          options.dependencyBudget = jsonOptions.dependencyBudget;
        }
        if (Array.isArray(jsonOptions.dontLoad)) {
          var valid = true;
          for (var i = 0, len = jsonOptions.dontLoad.length; i < len; i++) {
            if (typeof jsonOptions.dontLoad[i] !== 'string') {
              valid = false;
              break;
            }
          }
          if (valid) {
            options.dontLoad = jsonOptions.dontLoad;
          }
        }
      }
      //plugins
      if (plugins && typeof plugins === 'object') {
        Objects.mixin(options.plugins, plugins);
      }
      //definitions
      if (!Array.isArray(defNames) || defNames.length < 1) {
        defNames = [];
      } else {
        defNames = defNames.sort();
      }

      /**
       * @description Start the server with the default options in the event a problem occurrs
       * @param {Error} err The error object from the failed deferred
       */
      function fallback(err) {
        options = defaultOptions.serverOptions();
        options.getFile = _getFile;
        try {
          startAndMessage(options);
          if(err) {
            post(Serialize.serializeError(err));
          }
        }
        catch (err) {
          post(Serialize.serializeError(err));
        }
      }

      /**
       * @description Starts the tern server wit the given options
       * @param {Object} options The options to start the server with
       */
      function startAndMessage(options) {
        ternserver = new Tern.Server(options);
        if (Array.isArray(options.loadEagerly) && options.loadEagerly.length > 0) {
          options.loadEagerly.forEach(function (file) {
            ternserver.addFile(file);
          });
        }
        callback({request: 'start_server', state: "server_ready"}); //$NON-NLS-1$ //$NON-NLS-2$
      }

      Deferred.all(loadPlugins(options.plugins, projectLoc)).then(function () {
        return Deferred.all(loadDefs(defNames, projectLoc));
      })
        .then(function (json) {
        	var arr = json;
        	if(!Array.isArray(arr)) {
        		arr = [arr];
        	}
        	options.defs = [];
        	arr.forEach(function(def) {
        		if(Object.keys(def).length < 1) {
        			return;
        		}
        		options.defs.push(def);
        	});
          startAndMessage(options);
        }, fallback)
        .then(undefined, fallback);
    }

    post({request: "worker_ready"}); //$NON-NLS-1$

    var handlers = {
      /* start_server message handler */
      'start_server': function (args, callback) {
        startServer(args.options, callback);
      },
      /* addFile message handler */
      'addFile': function (args, callback) {
        ternserver.addFile(args.file, args.source);
        callback({request: 'addFile'}); //$NON-NLS-1$
      },
      /* completions message handler */
      'completions': function (args, callback) {
        if (ternserver) {
          ternserver.request({
              query: {
                type: "completions",  //$NON-NLS-1$
                file: args.meta.location,
                types: true,
                origins: true,
                urls: true,
                docs: true,
                end: args.params.offset,
                sort: true,
                filter: false,
                ecma: args.params.ecma,
                expandWordForward: false,
                omitObjectPrototype: false,
                includeKeywords: args.params.keywords,
                includeTemplates: args.params.template,
                caseInsensitive: true,
                docFormat: "full" //$NON-NLS-1$
              },
              files: args.files
            },
            function (error, comps) {
              if (error) {
                callback({
                  request: 'completions',
                  proposals: [],
                  error: error.message,
                  message: Messages['failedToComputeProposals']
                }); //$NON-NLS-1$
              } else if (comps && comps.completions) {
                callback({request: 'completions', proposals: comps.completions}); //$NON-NLS-1$
              } else {
                callback({request: 'completions', proposals: []}); //$NON-NLS-1$
              }
            });

        } else {
          callback({request: 'completions', message: Messages['failedToComputeProposalsNoServer']}); //$NON-NLS-1$
        }
      },
      /* definition message handler */
      'definition': function (args, callback) {
        if (ternserver) {
          ternserver.request({
              query: {
                type: "definition",  //$NON-NLS-1$
                file: args.meta.location,
                end: args.params.offset,
                guess: args.guess
              },
              files: args.files
            },
            function (error, decl) {
              if (error) {
                callback({request: 'definition', error: error.message, message: Messages['failedToComputeDecl']}); //$NON-NLS-1$
              }
              if (decl && typeof decl.start === 'number' && typeof decl.end === "number") {
                callback({request: 'definition', declaration: decl}); //$NON-NLS-1$
              } else if (decl && decl.origin) {
                //tern guessed at it when there is no line infos
                var idx = decl.origin.lastIndexOf(".");
                if (idx > -1 && decl.origin.slice(idx) === ".js") {
                  decl.guess = true;
                } else {
                  delete decl.guess;  //sometimes Tern will set the guess flag, we only want the index flag
                  decl.index = true;
                }
                callback({request: 'definition', declaration: decl}); //$NON-NLS-1$
              } else {
                callback({request: 'definition', declaration: null}); //$NON-NLS-1$
              }
            });
        } else {
          callback({request: 'definition', message: Messages['failedToComputeDeclNoServer']}); //$NON-NLS-1$
        }
      },
      /* delFile message handler */
      'delFile': function (args, callback) {
        if (ternserver && typeof args.file === 'string') {
          ternserver.delFile(args.file);
          callback({request: 'delFile'}); //$NON-NLS-1$
        } else {
          callback({request: 'delFile', message: i18nUtil.formatMessage(Messages['failedDeleteRequest'], args.file)}); //$NON-NLS-1$
        }
      },
      /* documentation message handler */
      'documentation': function (args, callback) {
        if (ternserver) {
          ternserver.request({
              query: {
                type: "documentation",  //$NON-NLS-1$
                file: args.meta.location,
                end: args.params.offset,
                docFormat: args.params.docFormat
              },
              files: args.files
            },
            function (error, doc) {
              if (error) {
                callback({request: 'documentation', error: error.message, message: Messages['failedToComputeDoc']}); //$NON-NLS-1$
              } else if (doc && doc.doc) {
                callback({request: 'documentation', doc: doc}); //$NON-NLS-1$
              } else {
                callback({request: 'documentation', doc: null}); //$NON-NLS-1$
              }
            });
        } else {
          callback({request: 'documentation', message: Messages['failedToComputeDocNoServer']}); //$NON-NLS-1$
        }
      },
      /* environments message handler */
      'environments': function (args, callback) {
        if (ternserver) {
          ternserver.request({
              query: {
                type: 'environments' //$NON-NLS-1$
              }
            },
            function (error, envs) {
              if (error) {
                callback({request: 'environments', error: error.message, message: Messages['failedGetEnvs']}); //$NON-NLS-1$
              }
              if (typeof envs === 'object') {
                callback({request: 'environments', envs: envs}); //$NON-NLS-1$
              } else {
                callback({request: 'environments', envs: null}); //$NON-NLS-1$
              }
            });
        } else {
          callback({request: 'environments', message: Messages['failedGetEnvsNoServer']}); //$NON-NLS-1$
        }
      },
      /* implementation message handler */
      'implementation': function (args, callback) {
        if (ternserver) {
          ternserver.request({
              query: {
                type: "implementation",  //$NON-NLS-1$
                file: args.meta.location,
                end: args.params.offset,
                guess: args.guess
              },
              files: args.files
            },
            function (error, impl) {
              if (error) {
                callback({request: 'implementation', error: error.message, message: Messages['failedToComputeImpl']}); //$NON-NLS-1$
              }
              if (impl && impl.implementation && typeof impl.implementation.start === 'number' && typeof impl.implementation.end === "number") {
                callback({request: 'implementation', implementation: impl.implementation}); //$NON-NLS-1$
              } else if (impl && impl.implementation && impl.implementation.origin) {
                callback({request: 'implementation', implementation: impl.implementation}); //$NON-NLS-1$
              } else {
                callback({request: 'implementation', implementation: null}); //$NON-NLS-1$
              }
            });
        } else {
          callback({request: 'implementation', message: Messages['failedToComputeImplNoServer']}); //$NON-NLS-1$
        }
      },
      /* installed_plugins message handler */
      'installed_plugins': function (args, callback) {
        if (ternserver) {
          ternserver.request({
              query: {
                type: 'installed_plugins' //$NON-NLS-1$
              }
            },
            function (error, plugins) {
              if (error) {
                callback({
                  request: 'installed_plugins',
                  error: error.message,
                  message: Messages['failedGetInstalledPlugins']
                }); //$NON-NLS-1$
              }
              if (typeof plugins === 'object') {
                callback({request: 'installed_plugins', plugins: plugins}); //$NON-NLS-1$
              } else {
                callback({request: 'installed_plugins', plugins: null}); //$NON-NLS-1$
              }
            });
        } else {
          callback({request: 'installed_plugins', message: Messages['failedGetInstalledPluginsNoServer']}); //$NON-NLS-1$
        }
      },
      /* installed_defs message handler */
      'installed_defs': function (args, callback) {
        if (ternserver) {
          ternserver.request({
              query: {
                type: 'installed_defs' //$NON-NLS-1$
              }
            },
            function (error, defs) {
              if (error) {
                callback({
                  request: 'installed_defs',
                  error: error.message,
                  message: Messages['failedGetInstalledDefs']
                }); //$NON-NLS-1$
              }
              if (typeof defs === 'object') {
                callback({request: 'installed_defs', defs: defs}); //$NON-NLS-1$
              } else {
                callback({request: 'installed_defs', defs: null}); //$NON-NLS-1$
              }
            });
        } else {
          callback({request: 'installed_defs', message: Messages['failedGetInstalledDefsNoServer']}); //$NON-NLS-1$
        }
      },
      /* rename message handler */
      'rename': function (args, callback) {
        if (ternserver) {
          ternserver.request({
              query: {
                type: "rename",  //$NON-NLS-1$
                file: args.meta.location,
                end: args.params.offset,
                newName: args.newname
              },
              files: args.files
            },
            function (error, changes) {
              if (error) {
                callback({
                  request: 'rename',
                  error: typeof error === 'string' ? error : error.message,
                  message: Messages['failedRenameTern']
                }); //$NON-NLS-1$
              } else if (changes && Array.isArray(changes.changes)) {
                callback({request: 'rename', changes: changes}); //$NON-NLS-1$
              } else {
                callback({request: 'rename', changes: []}); //$NON-NLS-1$
              }
            });
        } else {
          callback({request: 'rename', message: Messages['failedRenameNoServer']}); //$NON-NLS-1$
        }
      },
      /* type message handler */
      'type': function (args, callback) {
        ternserver.request({
            query: {
              type: "type",  //$NON-NLS-1$
              file: args.meta.location,
              end: args.params.offset
            }
          },
          function (error, type) {
            if (error) {
              callback({
                request: 'type',
                error: typeof error === 'string' ? error : error.message,
                message: Messages['failedType']
              }); //$NON-NLS-1$
            } else {
              callback({request: 'type', type: type}); //$NON-NLS-1$
            }
          });
      },
      /* checkRef message handler */
      'checkRef': function (args, callback) {
        ternserver.request({
            query: {
              type: "checkRef",  //$NON-NLS-1$
              file: args.meta.location,
              end: args.params.offset,
              origin: args.origin
            },
            files: args.files
          },
          function (error, type) {
            if (error) {
              callback({
                request: 'checkRef',
                error: typeof error === 'string' ? error : error.message,
                message: Messages['failedType']
              }); //$NON-NLS-1$
            } else {
              callback({request: 'checkRef', type: type}); //$NON-NLS-1$
            }
          });
      },
      /* lint message handler */
      'lint': function (args, callback) {
        var query =
          {
            type: "lint",  //$NON-NLS-1$
            file: args.meta.location,
            config: args.config
          };
        ternserver.request(
          {
            query: query,
            files: args.files
          },
          function (error, problems) {
            if (error) {
              callback({request: 'lint', error: error.message, message: Messages['failedToComputeProblems']}); //$NON-NLS-1$
            } else if (problems && Array.isArray(problems)) {
              callback({request: 'lint', problems: problems}); //$NON-NLS-1$
            } else {
              callback({request: 'lint', problems: []}); //$NON-NLS-1$
            }
          });
      },
      /* outline message handler */
      'outline': function (args, callback) {
        ternserver.request({
            query: {
              type: "outline", //$NON-NLS-1$
              file: args.meta.location,
              ast: args.ast
            },
            files: args.files
          },
          function (error, outline) {
            if (error) {
              callback({request: 'outline', error: error.message, message: Messages['failedToComputeOutline']}); //$NON-NLS-1$
            } else if (outline && Array.isArray(outline)) {
              callback({request: 'outline', outline: outline}); //$NON-NLS-1$
            } else {
              callback({request: 'outline', outline: []}); //$NON-NLS-1$
            }
          }
        );
      },
      /* fixes message handler */
      'fixes': function (args, callback) {
        ternserver.request({
            query: {
              type: "fixes", //$NON-NLS-1$
              file: args.meta.location,
              problemId: args.problemId,
              annotation: args.annotation,
              annotations: args.annotations
            },
            files: args.files
          },
          function (error, fixes) {
            if (error) {
              callback({request: 'fixes', error: error.message, message: Messages['failedToComputeFixes']}); //$NON-NLS-1$
            } else if (fixes && Array.isArray(fixes)) {
              callback({request: 'fixes', fixes: fixes}); //$NON-NLS-1$
            } else {
              callback({request: 'fixes', fixes: []}); //$NON-NLS-1$
            }
          }
        );
      },
      'occurrences': function (args, callback) {
        if (ternserver) {
          ternserver.request({
              query: {
                type: "occurrences", //$NON-NLS-1$
                file: args.meta.location,
                end: args.params.offset
              },
              files: Array.isArray(args.files) ? args.files : []
            },
            function (error, occurrences) {
              if (error) {
                callback(null, {error: error.message, message: Messages['failedToComputeOccurrences']});
              } else if (Array.isArray(occurrences)) {
                callback({request: 'outline', occurrences: occurrences}); //$NON-NLS-1$
              } else {
                callback({request: 'outline', occurrences: []}); //$NON-NLS-1$
              }
            }
          );
        } else {
          callback(null, {message: Messages['failedToComputeOccurrencesNoServer']});
        }
      },
      /* lint message handler */
      'beautify': function (args, callback) {
        if (ternserver) {
          var query =
            {
              type: "beautify",  //$NON-NLS-1$
              file: args.meta.location,
              args: {
                config: args.config,
                start: args.start,
                end: args.end,
                contentType: args.contentType
              }
            };
          ternserver.request(
            {
              query: query,
              files: args.files
            },
            function (error, text) {
              if (error) {
                callback({request: 'beautify', error: error.message, message: Messages['failedToFormat']}); //$NON-NLS-1$
              } else if (text) {
                callback({request: 'beautify', text: text}); //$NON-NLS-1$
              } else {
                callback({request: 'beautify', text: ""}); //$NON-NLS-1$
              }
            }
          );
        } else {
          callback(null, {message: Messages['failedToFormatNoServer']});
        }
      },
    };

    var ternID = 0;
    var reads = Object.create(null);
    var resolverReads = Object.create(null);

    /**
     * @description Worker callback when a message is sent to the worker
     * @callback
     */
    onmessage = function (evnt) {
      if (typeof evnt.data === 'object') {
        var _d = evnt.data;
        var _handler = handlers[_d.request];
        if (typeof _handler === 'function') {
          if (!ternserver && _d.request !== 'start_server') {
            serverNotReady(_d);
            return;
          }
          _handler(_d.args, function (response) {
            if (typeof _d.messageID === 'number') {
              response.messageID = _d.messageID;
            } else if (typeof _d.ternID === 'number') {
              response.ternID = _d.ternID;
            }
            post(response);
            return;
          });
        } else if (_d.request === 'read') {
          var _read = reads[_d.ternID];
          if (typeof _read === 'function') {
            var text = '';
            if (_d.args && _d.args.contents) {
              text = _d.args.contents;
            }
            _read(_d.args.error, text);//{contents: _d.args.contents ? _d.args.contents : '', file:_d.args.file, logical: _d.args.logical});
            delete reads[_d.ternID];
          }
          _read = resolverReads[_d.ternID];
          if (typeof _read === 'function') {
            _read(_d.args.error, {
              contents: _d.args.contents ? _d.args.contents : '',
              file: _d.args.file,
              logical: _d.args.logical
            });
            delete reads[_d.ternID];
          }
          return;
        } else {
          //no one handled the request, report back an error
          unknownRequest(_d);
        }
      }
    };

    /**
     * @description Respond back that the request is unknown
     * @param {Object} data The original request data
     */
    function unknownRequest(data) {
      var response = Object.create(null);
      response.request = data.request;
      if (data.messageID) {
        response.messageID = data.messageID;
      } else if (data.ternID) {
        response.ternID = data.ternID;
      }
      response.error = i18nUtil.formatMessage(Messages['unknownRequest'], response.request);
      post(response);
    }

    /**
     * @description Respond back that the tern server has not been started
     * @param {Object} data The original request data
     */
    function serverNotReady(data) {
      var response = Object.create(null);
      response.request = data.request;
      if (data.messageID) {
        response.messageID = data.messageID;
      } else if (data.ternID) {
        response.ternID = data.ternID;
      }
      response.error = i18nUtil.formatMessage(Messages['serverNotStarted'], response.request);
      post(response);
    }

    /**
     * @description Worker callback when an error occurs
     * @callback
     */
    onerror = function (evnt) {
      post(evnt);
    };

    /**
     * @description Worker callback when a shared worker starts up
     * @callback
     */
    onconnect = function (evnt) {
      this.port = evnt.ports[0];
      this.port.onmessage = onmessage;
      this.port.start();
    };

    /**
     * @description Sends the given message back to the client. If the msg is null, send an Error
     * object with the optional given error message
     * @param {Object} msg The message to send back to the client
     * @param {String} errormsg The optional error message to send back to the client if the main message is null
     */
    function post(msg, errormsg) {
      if (!msg) {
        msg = new Error(errormsg ? errormsg : Messages['unknownError']);
      }
      if (this.port) {
        this.port.postMessage(msg);
      } else {
        postMessage(msg);
      }
    }

    /**
     * @description Read a file from the workspace into Tern
     * @private
     * @param {String} file The full path of the file
     * @param {Function} callback The callback once the file has been read or failed to read
     */
    function _getFile(file, callback) {
      var request = {request: 'read', ternID: ternID++, args: {file: file}}; //$NON-NLS-1$
      if (file !== null && typeof file === 'object') {
        resolverReads[request.ternID] = callback;
      } else {
        reads[request.ternID] = callback;
      }
      post(request, null);
    }

    /**
     * @description Loads the plugins listed in the given plugins object
     * @param {Object} plugins The object of plugins
     * @param {String} projectDir The base directory of the project
     * @returns {Promise} The promise to resolve all of the plugin loads
     * @since 11.0
     */
    function loadPlugins(plugins, projectDir) {
      var promises = [];
      if (plugins) {
        Object.keys(plugins).forEach(function (key) {
          if (defaultOptions.plugins.required[key] || defaultOptions.plugins.optional[key]) {
            //default plugins are statically loaded
            return;
          }
          var plugin = plugins[key];
          if (!plugin || typeof plugin !== 'object') {
            return;
          }
          if (typeof plugin.location === 'string' && typeof key === 'string') {
            var deferred = new Deferred(),
              loc = /^.tern-plugins/.test(plugin.location) ? projectDir + plugin.location : plugin.location;
            promises.push(deferred);
            _getFile({file: loc, tourl: true}, function (err, meta) {
              var config = {};
              loc = !err && meta && meta.file && meta.file.url ? meta.file.url : plugin.location;
              config[key] = loc;
              requirejs.config({
                paths: config
              });
              requirejs([loc], /* @callback */ function (p) {
                return deferred.resolve(key); //doesn't matter what we resolve, just that the plugin loaded into require
              }, function (err) {
                delete plugins[key];
                promises.pop();
                post("Tern plugin '" + key + "' from '" + loc + "'failed to load.\n\nReason:\n" + Serialize.serializeError(err));
                return deferred.resolve(err);
              });
            });
          }
        });
      }
      return promises;
    }


    /**
     * @description Load any defs from the .tern-project file
     * @param {Array.<String>|Object} defs The definitions, either from an array of names or an object of names with additional metadata
     * @param {String} projectLoc The location of the project we are reading configs from
     * @returns {Promise} Returns a promise to resolve all def loads
     * @since 11.0
     */
    function loadDefs(defs, projectLoc) {
      var _defs = [];
      if (Array.isArray(defs)) {
        defs.forEach(function (_def) {
          if (/^\.definitions/.test(_def)) {
            if (typeof _def === 'string') {
              var deferred = _loadDef(_def, projectLoc);
              if (deferred) {
                _defs.push(deferred);
              }
            }
          } else {
            var idx = defaultOptions.defNames.indexOf(_def);
            if (idx > -1) {
              //a default def, get it
              _defs.push(new Deferred().resolve(defaultOptions.defs[idx]));
            }
          }
        });
      }
      return _defs;
    }

    /**
     * @description Delegate to actually load a definition
     * @private
     * @param {String} def The name of the definition load try and load
     * @param {String} projectLoc The location of the project we are reading configs from
     * @since 11.0
     */
    function _loadDef(def, projectLoc) {
      var loc = def;
      if (projectLoc) {
        loc = projectLoc + loc;
      }
      if (!/\.json$/i.test(def)) {
        loc = loc + '.json'; //$NON-NLS-1$
      }
      var deferred = new Deferred();
      _getFile(loc, /* @callback */ function (err, contents) {
        if (typeof contents === 'string') {
          deferred.resolve(contents.length > 0 ? JSON.parse(contents) : Object.create(null));
        } else {
          deferred.reject();
        }
      });
      return deferred;
    }
  });
define("javascript/plugins/ternWorkerCore", function(){});

