搜索引擎去广告

谷歌百度搜狗神马360必应头条搜索去广告,适配电脑和手机,谷歌搜索拦截部分内容广场

// ==UserScript==
// @name             搜索引擎去广告
// @description        谷歌百度搜狗神马360必应头条搜索去广告,适配电脑和手机,谷歌搜索拦截部分内容广场
// @author             Lemon399
// @version            48
// @match             *://www.google.co.jp/*
// @match             *://www.google.com.hk/*
// @match             *://www.google.com/*
// @match             *://m.baidu.com/*
// @match             *://www.baidu.com/*
// @match             *://m.sm.cn/*
// @match             *://yz.m.sm.cn/*
// @match             *://wap.sogou.com/*
// @match             *://m.sogou.com/*
// @match             *://www.sogou.com/*
// @match             *://www.so.com/*
// @match             *://m.so.com/*
// @match             *://s.cn.bing.net/*
// @match             *://cn.bing.com/*
// @match             *://www.bing.com/*
// @match             *://so.toutiao.com/*
// @grant              GM_addStyle
// @grant              unsafeWindow
// @run-at             document-start
// @namespace        https://lemon399-bitbucket-io.vercel.app/
// @source            https://gitee.com/lemon399/tampermonkey-cli/tree/master/projects/abp_parse
// @copyright          GPL-3.0
// @license            GPL-3.0
// ==/UserScript==

/* eslint-disable no-undef */

(function (cat) {
  "use strict";

  function __awaiter(thisArg, _arguments, P, generator) {
    function adopt(value) {
      return value instanceof P
        ? value
        : new P(function (resolve) {
            resolve(value);
          });
    }
    return new (P || (P = Promise))(function (resolve, reject) {
      function fulfilled(value) {
        try {
          step(generator.next(value));
        } catch (e) {
          reject(e);
        }
      }
      function rejected(value) {
        try {
          step(generator["throw"](value));
        } catch (e) {
          reject(e);
        }
      }
      function step(result) {
        result.done
          ? resolve(result.value)
          : adopt(result.value).then(fulfilled, rejected);
      }
      step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
  }

  const userConfig = {
    css: " {display: none !important;width: 0 !important;height: 0 !important;} ",
    timeout: 10000,
    tryCount: 5,
    tryTimeout: 500,
  };
  const defaultRules = `
! 不支持的规则和开头为 ! 的行会忽略
!
! 由于语法限制,此处规则中
! 一个反斜杠需要改成两个,像这样 \\
www.google.com,www.google.com.hk,www.google.co.jp##div.MjjYud:has([href*='%E2%9C%94%EF%B8%8F%E2%96%9B']),div.MjjYud:has([href*='%E3%80%90%E2%9C%94%EF%B8%8F']),div.MjjYud:has([href*='%E2%9E%BF%E3%80%91']),div.MjjYud:has([href*='%E2%8F%AA%29']),div.MjjYud:has([href*='%E2%9A%BD%E3%80%91']),div.MjjYud:has([href*='-%E3%80%90']),div.MjjYud:has([href*='%E2%9C%94%EF%B8%8F'][href*='%E2%98%80%EF%B8%8F']),div.MjjYud:has([href*='-('][href*='%E2%9C%94%EF%B8%8F']),div.MjjYud:has([href*='%E2%9C%94%EF%B8%8F%29']),div.MjjYud:has([href*='-%E2%9C%94%EF%B8%8F']),div.MjjYud:has([href*='%E2%9C%94%EF%B8%8F']),div.MjjYud:has([href*='%E2%9C%85%EF%B8%8F']),div.MjjYud:has([href*='%E2%9E%A1%EF%B8%8F']),div.MjjYud:has([href*='%E2%AD%90']),div.MjjYud:has([href*='%E3%8A%99%EF%B8%8F'])

so.com##.res-mediav
so.com##.e_result
so.com##.c-title-tag
so.com##DIV.res-mediav-right
so.com##DIV.inner_left
so.com###so-activity-entry
so.com##DIV.tg-wrap
baidu.com##.ad-wrapper
baidu.com#?#.ec_wise_ad
sogou.com##.qb-download-banner-non-share
##DIV[data-text-ad]
##.ad-block
www.baidu.com##.result-op[tpl="right_game_recommend"]
www.baidu.com##div[id$="_canvas"]
www.baidu.com##style[id*="s-m"] + div[id^="m"]
www.baidu.com#?##content_left > div:not([class]) > div[data-placeid]
www.baidu.com#?##content_right > table > tbody > tr > td > div:not(#con-ar):not([class])
www.baidu.com#?#div:not([id]) > style[id^="s-"] + div[id]
www.baidu.com#?##content_left > [style*="important"]
www.baidu.com#?#div[id$="_canvas"]
www.baidu.com#?#.c-container:-abp-has(.t > a[data-landurl])
baidu.com##[class='result c-container new-pmd'][id='1'][tpl='se_com_default'][data-click='{']
baidu.com###content_right > table > tbody > tr > td > div:not(#con-ar):not([class])
baidu.com##.result-op[tpl='sp_hot_sale']
m.baidu.com##DIV#relativewords.se-relativewords.c-container.se-relativewords-new.c-bg-color-white
m.sm.cn##DIV.ad-alert-info
##.se-recommend-word-list-container
###se-recommend-word-list-container
##[class*="ball-wrapper"]
baidu.com##DIV#page-copyright.se-page-copyright[style='margin-bottom: 50px;']
baidu.com##DIV[style^='position: fixed; bottom: 0px; left: 0px; z-index: 300; width: 100%; height: 52px; background: rgb(255, 255, 255);']
##[ad_dot_url*="http"]
##.dl-banner-without-logo
##.ad_result
##[class="result c-container new-pmd"][id="1"][tpl="se_com_default"][data-click="{"]
##.biz_sponsor
##.b_algospacing
##[onmousedown*="ad"][h*="Ads"]
bing.com,cn.bing.net##DIV#bnp_container
bing.com,cn.bing.net##li.b_ad
bing.com,cn.bing.net##.ad_sc
bing.com,cn.bing.net#?#li[class="b_algo"]:-abp-has(.b_attribution[data-partnertag]+p[class])
bing.com,cn.bing.net#?##b_results > li:-abp-has(.b_adProvider)
so.toutiao.com##DIV[id^='ad_']
##[href^='http://yz.m.sm.cn/adclick']
`;

  const CRRE =
      /^(\[\$domain=)?(~?[\w-]+(?:\.[\w-]+)*(?:\.[\w-]+|\.\*)(?:(?:,|\|)~?[\w-]+(?:\.[\w-]+)*(?:\.[\w-]+|\.\*))*)?(?:])?(#@?\$?\??#)([^\s^+].*)/,
    CRFlags = ["##", "#@#", "#?#", "#@?#", "#$#", "#@$#", "#$?#", "#@$?#"],
    styleBoxes = ["genHideCss", "genExtraCss", "spcHideCss", "spcExtraCss"],
    dataBoxes = ["selectors", "extSelectors", "styles", "extStyles"];
  function makeRuleBox() {
    return {
      black: [],
      white: [],
    };
  }
  function domainChecker(domains) {
    const results = [],
      invResults = [],
      currDomain = location.hostname,
      urlSuffix = /\.+?[\w-]+$/.exec(currDomain);
    let totalResult = [0, false],
      black = false,
      white = false,
      match = false;
    domains.forEach((domain) => {
      const invert = domain[0] === "~";
      if (invert) domain = domain.slice(1);
      if (domain.endsWith(".*") && Array.isArray(urlSuffix)) {
        domain = domain.replace(".*", urlSuffix[0]);
      }
      const result = currDomain.endsWith(domain);
      if (invert) {
        if (result) white = true;
        invResults.push([domain.length, !result]);
      } else {
        if (result) black = true;
        results.push([domain.length, result]);
      }
    });
    if (results.length > 0 && !black) {
      match = false;
    } else if (invResults.length > 0 && !white) {
      match = true;
    } else {
      results.forEach((r) => {
        if (r[0] >= totalResult[0] && r[1]) {
          totalResult = r;
        }
      });
      invResults.forEach((r) => {
        if (r[0] >= totalResult[0] && !r[1]) {
          totalResult = r;
        }
      });
      match = totalResult[1];
    }
    return [match, results.length === 0];
  }
  function hasSome(str, arr) {
    return arr.some((word) => str.includes(word));
  }
  function ruleSpliter(rule) {
    const group = rule.match(CRRE);
    if (group) {
      const [, isDomain, place = "*", flag, sel] = group,
        type = CRFlags.indexOf(flag),
        matchResult =
          place === "*"
            ? [true, true]
            : domainChecker(place.split(isDomain ? "|" : ","));
      if (sel && matchResult[0]) {
        return {
          black: type % 2 ? "white" : "black",
          type: Math.floor(type / 2),
          place: (isDomain ? "|" : "") + place,
          generic: matchResult[1],
          sel,
        };
      }
    }
  }
  function ruleLoader(rule) {
    if (
      hasSome(rule, [
        ":matches-path(",
        ":min-text-length(",
        ":watch-attr(",
        ":-abp-properties(",
        ":matches-property(",
      ])
    )
      return;
    // 去掉开头末尾空格
    rule = rule.trim();
    // 如果 #$# 不包含 {} 就排除
    // 可以尽量排除 Snippet Filters
    if (
      /(?:\w|\*|]|^)#\$#/.test(rule) &&
      !/{\s*[a-zA-Z-]+\s*:\s*.+}\s*$/.test(rule)
    )
      return;
    // ## -> #?#
    if (
      /(?:\w|\*|]|^)#@?\$?#/.test(rule) &&
      hasSome(rule, [
        ":has(",
        ":-abp-has(",
        "[-ext-has=",
        ":has-text(",
        ":contains(",
        ":-abp-contains(",
        "[-ext-contains=",
        ":matches-css(",
        "[-ext-matches-css=",
        ":matches-css-before(",
        "[-ext-matches-css-before=",
        ":matches-css-after(",
        "[-ext-matches-css-after=",
        ":matches-attr(",
        ":nth-ancestor(",
        ":upward(",
        ":xpath(",
        ":remove()",
        ":not(",
      ])
    ) {
      rule = rule.replace(/(\w|\*|]|^)#(@?\$?)#/, "$1#$2?#");
    }
    // :style(...) 转换
    // example.com#?##id:style(color: red)
    // example.com#$?##id { color: red }
    if (rule.includes(":style(")) {
      rule = rule
        .replace(/(\w|\*|]|^)#(@?)(\??)#/, "$1#$2$$$3#")
        .replace(/:style\(\s*/, " { ")
        .replace(/\s*\)$/, " }");
    }
    return ruleSpliter(rule);
  }
  function ruleToCss(rule, preset) {
    var _a, _b;
    const isStyle = /}\s*$/.test(rule.sel);
    return [
      `/* ${rule.type}${rule.place} */ ${
        rule.sel + (!isStyle ? preset : "")
      } \n`,
      isStyle
        ? (_b =
            (_a = rule.sel.match(/^(.+?)\s*{\s*[a-zA-Z-]+\s*:\s*.+}\s*$/)) ===
              null || _a === void 0
              ? void 0
              : _a[1]) !== null && _b !== void 0
          ? _b
          : rule.sel
        : rule.sel,
    ];
  }

  const data = {
    disabled: false,
    saved: false,
    update: true,
    updating: false,
    receivedRules: "",
    customRules: defaultRules,
    allRules: "",
    genHideCss: "",
    genExtraCss: "",
    spcHideCss: "",
    spcExtraCss: "",
    selectors: makeRuleBox(),
    extSelectors: makeRuleBox(),
    styles: makeRuleBox(),
    extStyles: makeRuleBox(),
    bRules: [],
    appliedLevel: 0,
    appliedCount: 0,
    isFrame: cat.unsafeWindow.self !== cat.unsafeWindow.top,
    isClean: false,
    mutex: "__lemon__abp__parser__$__",
    preset: getUserConfig("css"),
    timeout: getUserConfig("timeout"),
    xTimeout: 1000,
    tryCount: getUserConfig("tryCount"),
    tryTimeout: getUserConfig("tryTimeout"),
  };
  function getUserConfig(prop) {
    {
      return userConfig[prop];
    }
  }
  function addStyle(css, pass = 0) {
    let el;
    if (pass >= data.tryCount) return;
    if (typeof cat.GM_addStyle == "function") {
      el = cat.GM_addStyle(css);
    } else {
      el = document.createElement("style");
      el.textContent = css;
      document.documentElement.appendChild(el);
    }
    if (typeof el == "object") {
    if (!el || !document.documentElement.contains(el)) {
      setTimeout(() => {
        addStyle(css, pass + 1);
      }, data.tryTimeout);
    }
  }
  }

  function _defineProperty(obj, key, value) {
    if (key in obj) {
      Object.defineProperty(obj, key, {
        value: value,
        enumerable: true,
        configurable: true,
        writable: true,
      });
    } else {
      obj[key] = value;
    }
    return obj;
  }
  const NODE = {
    SELECTOR_LIST: "SelectorList",
    SELECTOR: "Selector",
    REGULAR_SELECTOR: "RegularSelector",
    EXTENDED_SELECTOR: "ExtendedSelector",
    ABSOLUTE_PSEUDO_CLASS: "AbsolutePseudoClass",
    RELATIVE_PSEUDO_CLASS: "RelativePseudoClass",
  };
  class AnySelectorNode {
    constructor(type) {
      _defineProperty(this, "children", []);
      this.type = type;
    }
    addChild(child) {
      this.children.push(child);
    }
  }
  class RegularSelectorNode extends AnySelectorNode {
    constructor(value) {
      super(NODE.REGULAR_SELECTOR);
      this.value = value;
    }
  }
  class RelativePseudoClassNode extends AnySelectorNode {
    constructor(name) {
      super(NODE.RELATIVE_PSEUDO_CLASS);
      this.name = name;
    }
  }
  class AbsolutePseudoClassNode extends AnySelectorNode {
    constructor(name) {
      super(NODE.ABSOLUTE_PSEUDO_CLASS);
      _defineProperty(this, "value", "");
      this.name = name;
    }
  }
  const LEFT_SQUARE_BRACKET = "[";
  const RIGHT_SQUARE_BRACKET = "]";
  const LEFT_PARENTHESIS = "(";
  const RIGHT_PARENTHESIS = ")";
  const LEFT_CURLY_BRACKET = "{";
  const RIGHT_CURLY_BRACKET = "}";
  const BRACKET = {
    SQUARE: {
      LEFT: LEFT_SQUARE_BRACKET,
      RIGHT: RIGHT_SQUARE_BRACKET,
    },
    PARENTHESES: {
      LEFT: LEFT_PARENTHESIS,
      RIGHT: RIGHT_PARENTHESIS,
    },
    CURLY: {
      LEFT: LEFT_CURLY_BRACKET,
      RIGHT: RIGHT_CURLY_BRACKET,
    },
  };
  const SLASH = "/";
  const BACKSLASH = "\\";
  const SPACE = " ";
  const COMMA = ",";
  const DOT = ".";
  const SEMICOLON = ";";
  const COLON = ":";
  const SINGLE_QUOTE = "'";
  const DOUBLE_QUOTE = '"';
  const CARET = "^";
  const DOLLAR_SIGN = "$";
  const EQUAL_SIGN = "=";
  const TAB = "\t";
  const CARRIAGE_RETURN = "\r";
  const LINE_FEED = "\n";
  const FORM_FEED = "\f";
  const WHITE_SPACE_CHARACTERS = [
    SPACE,
    TAB,
    CARRIAGE_RETURN,
    LINE_FEED,
    FORM_FEED,
  ];
  const ASTERISK = "*";
  const ID_MARKER = "#";
  const CLASS_MARKER = DOT;
  const DESCENDANT_COMBINATOR = SPACE;
  const CHILD_COMBINATOR = ">";
  const NEXT_SIBLING_COMBINATOR = "+";
  const SUBSEQUENT_SIBLING_COMBINATOR = "~";
  const COMBINATORS = [
    DESCENDANT_COMBINATOR,
    CHILD_COMBINATOR,
    NEXT_SIBLING_COMBINATOR,
    SUBSEQUENT_SIBLING_COMBINATOR,
  ];
  const SUPPORTED_SELECTOR_MARKS = [
    LEFT_SQUARE_BRACKET,
    RIGHT_SQUARE_BRACKET,
    LEFT_PARENTHESIS,
    RIGHT_PARENTHESIS,
    LEFT_CURLY_BRACKET,
    RIGHT_CURLY_BRACKET,
    SLASH,
    BACKSLASH,
    SEMICOLON,
    COLON,
    COMMA,
    SINGLE_QUOTE,
    DOUBLE_QUOTE,
    CARET,
    DOLLAR_SIGN,
    ASTERISK,
    ID_MARKER,
    CLASS_MARKER,
    DESCENDANT_COMBINATOR,
    CHILD_COMBINATOR,
    NEXT_SIBLING_COMBINATOR,
    SUBSEQUENT_SIBLING_COMBINATOR,
    TAB,
    CARRIAGE_RETURN,
    LINE_FEED,
    FORM_FEED,
  ];
  const SUPPORTED_STYLE_DECLARATION_MARKS = [
    COLON,
    SEMICOLON,
    SINGLE_QUOTE,
    DOUBLE_QUOTE,
    BACKSLASH,
    SPACE,
    TAB,
    CARRIAGE_RETURN,
    LINE_FEED,
    FORM_FEED,
  ];
  const CONTAINS_PSEUDO = "contains";
  const HAS_TEXT_PSEUDO = "has-text";
  const ABP_CONTAINS_PSEUDO = "-abp-contains";
  const MATCHES_CSS_PSEUDO = "matches-css";
  const MATCHES_CSS_BEFORE_PSEUDO = "matches-css-before";
  const MATCHES_CSS_AFTER_PSEUDO = "matches-css-after";
  const MATCHES_ATTR_PSEUDO_CLASS_MARKER = "matches-attr";
  const MATCHES_PROPERTY_PSEUDO_CLASS_MARKER = "matches-property";
  const XPATH_PSEUDO_CLASS_MARKER = "xpath";
  const NTH_ANCESTOR_PSEUDO_CLASS_MARKER = "nth-ancestor";
  const CONTAINS_PSEUDO_NAMES = [
    CONTAINS_PSEUDO,
    HAS_TEXT_PSEUDO,
    ABP_CONTAINS_PSEUDO,
  ];
  const UPWARD_PSEUDO_CLASS_MARKER = "upward";
  const REMOVE_PSEUDO_MARKER = "remove";
  const HAS_PSEUDO_CLASS_MARKER = "has";
  const ABP_HAS_PSEUDO_CLASS_MARKER = "-abp-has";
  const HAS_PSEUDO_CLASS_MARKERS = [
    HAS_PSEUDO_CLASS_MARKER,
    ABP_HAS_PSEUDO_CLASS_MARKER,
  ];
  const IS_PSEUDO_CLASS_MARKER = "is";
  const NOT_PSEUDO_CLASS_MARKER = "not";
  const ABSOLUTE_PSEUDO_CLASSES = [
    CONTAINS_PSEUDO,
    HAS_TEXT_PSEUDO,
    ABP_CONTAINS_PSEUDO,
    MATCHES_CSS_PSEUDO,
    MATCHES_CSS_BEFORE_PSEUDO,
    MATCHES_CSS_AFTER_PSEUDO,
    MATCHES_ATTR_PSEUDO_CLASS_MARKER,
    MATCHES_PROPERTY_PSEUDO_CLASS_MARKER,
    XPATH_PSEUDO_CLASS_MARKER,
    NTH_ANCESTOR_PSEUDO_CLASS_MARKER,
    UPWARD_PSEUDO_CLASS_MARKER,
  ];
  const RELATIVE_PSEUDO_CLASSES = [
    ...HAS_PSEUDO_CLASS_MARKERS,
    IS_PSEUDO_CLASS_MARKER,
    NOT_PSEUDO_CLASS_MARKER,
  ];
  const SUPPORTED_PSEUDO_CLASSES = [
    ...ABSOLUTE_PSEUDO_CLASSES,
    ...RELATIVE_PSEUDO_CLASSES,
  ];
  const OPTIMIZATION_PSEUDO_CLASSES = [
    NOT_PSEUDO_CLASS_MARKER,
    IS_PSEUDO_CLASS_MARKER,
  ];
  const SCOPE_CSS_PSEUDO_CLASS = ":scope";
  const REGULAR_PSEUDO_ELEMENTS = {
    AFTER: "after",
    BACKDROP: "backdrop",
    BEFORE: "before",
    CUE: "cue",
    CUE_REGION: "cue-region",
    FIRST_LETTER: "first-letter",
    FIRST_LINE: "first-line",
    FILE_SELECTION_BUTTON: "file-selector-button",
    GRAMMAR_ERROR: "grammar-error",
    MARKER: "marker",
    PART: "part",
    PLACEHOLDER: "placeholder",
    SELECTION: "selection",
    SLOTTED: "slotted",
    SPELLING_ERROR: "spelling-error",
    TARGET_TEXT: "target-text",
  };
  const AT_RULE_MARKER = "@";
  const CONTENT_CSS_PROPERTY = "content";
  const PSEUDO_PROPERTY_POSITIVE_VALUE = "true";
  const DEBUG_PSEUDO_PROPERTY_GLOBAL_VALUE = "global";
  const NO_SELECTOR_ERROR_PREFIX = "Selector should be defined";
  const STYLE_ERROR_PREFIX = {
    NO_STYLE: "No style declaration found",
    NO_SELECTOR: `${NO_SELECTOR_ERROR_PREFIX} before style declaration in stylesheet`,
    INVALID_STYLE: "Invalid style declaration",
    UNCLOSED_STYLE: "Unclosed style declaration",
    NO_PROPERTY: "Missing style property in declaration",
    NO_VALUE: "Missing style value in declaration",
    NO_STYLE_OR_REMOVE:
      "Style should be declared or :remove() pseudo-class should used",
    NO_COMMENT: "Comments are not supported",
  };
  const NO_AT_RULE_ERROR_PREFIX = "At-rules are not supported";
  const REMOVE_ERROR_PREFIX = {
    INVALID_REMOVE: "Invalid :remove() pseudo-class in selector",
    NO_TARGET_SELECTOR: `${NO_SELECTOR_ERROR_PREFIX} before :remove() pseudo-class`,
    MULTIPLE_USAGE: "Pseudo-class :remove() appears more than once in selector",
    INVALID_POSITION: "Pseudo-class :remove() should be at the end of selector",
  };
  const MATCHING_ELEMENT_ERROR_PREFIX = "Error while matching element";
  const MAX_STYLE_PROTECTION_COUNT = 50;
  const REGEXP_VALID_OLD_SYNTAX =
    /\[-(?:ext)-([a-z-_]+)=(["'])((?:(?=(\\?))\4.)*?)\2\]/g;
  const INVALID_OLD_SYNTAX_MARKER = "[-ext-";
  const evaluateMatch = (match, name, quoteChar, rawValue) => {
    const re = new RegExp(`([^\\\\]|^)\\\\${quoteChar}`, "g");
    const value = rawValue.replace(re, `$1${quoteChar}`);
    return `:${name}(${value})`;
  };
  const SCOPE_MARKER_REGEXP = /\(:scope >/g;
  const SCOPE_REPLACER = "(>";
  const MATCHES_CSS_PSEUDO_ELEMENT_REGEXP = /(:matches-css)-(before|after)\(/g;
  const convertMatchesCss = (
    match,
    extendedPseudoClass,
    regularPseudoElement
  ) => {
    return `${extendedPseudoClass}${BRACKET.PARENTHESES.LEFT}${regularPseudoElement}${COMMA}`;
  };
  const normalize = (selector) => {
    const normalizedSelector = selector
      .replace(REGEXP_VALID_OLD_SYNTAX, evaluateMatch)
      .replace(SCOPE_MARKER_REGEXP, SCOPE_REPLACER)
      .replace(MATCHES_CSS_PSEUDO_ELEMENT_REGEXP, convertMatchesCss);
    if (normalizedSelector.includes(INVALID_OLD_SYNTAX_MARKER)) {
      throw new Error(
        `Invalid extended-css old syntax selector: '${selector}'`
      );
    }
    return normalizedSelector;
  };
  const convert = (rawSelector) => {
    const trimmedSelector = rawSelector.trim();
    return normalize(trimmedSelector);
  };
  const TOKEN_TYPE = {
    MARK: "mark",
    WORD: "word",
  };
  const tokenize = (input, supportedMarks) => {
    let wordBuffer = "";
    const tokens = [];
    const selectorSymbols = input.split("");
    selectorSymbols.forEach((symbol) => {
      if (supportedMarks.includes(symbol)) {
        if (wordBuffer.length > 0) {
          tokens.push({
            type: TOKEN_TYPE.WORD,
            value: wordBuffer,
          });
          wordBuffer = "";
        }
        tokens.push({
          type: TOKEN_TYPE.MARK,
          value: symbol,
        });
        return;
      }
      wordBuffer += symbol;
    });
    if (wordBuffer.length > 0) {
      tokens.push({
        type: TOKEN_TYPE.WORD,
        value: wordBuffer,
      });
    }
    return tokens;
  };
  const tokenizeSelector = (rawSelector) => {
    const selector = convert(rawSelector);
    return tokenize(selector, SUPPORTED_SELECTOR_MARKS);
  };
  const tokenizeAttribute = (attribute) => {
    return tokenize(attribute, [...SUPPORTED_SELECTOR_MARKS, EQUAL_SIGN]);
  };
  const flatten = (input) => {
    const stack = [];
    input.forEach((el) => stack.push(el));
    const res = [];
    while (stack.length) {
      const next = stack.pop();
      if (!next) {
        throw new Error("Unable to make array flat");
      }
      if (Array.isArray(next)) {
        next.forEach((el) => stack.push(el));
      } else {
        res.push(next);
      }
    }
    return res.reverse();
  };
  const getFirst = (array) => {
    return array[0];
  };
  const getLast = (array) => {
    return array[array.length - 1];
  };
  const getPrevToLast = (array) => {
    return array[array.length - 2];
  };
  const getItemByIndex = (array, index, errorMessage) => {
    const indexChild = array[index];
    if (!indexChild) {
      throw new Error(errorMessage || `No array item found by index ${index}`);
    }
    return indexChild;
  };
  const NO_REGULAR_SELECTOR_ERROR =
    "At least one of Selector node children should be RegularSelector";
  const isSelectorListNode = (astNode) => {
    return (
      (astNode === null || astNode === void 0 ? void 0 : astNode.type) ===
      NODE.SELECTOR_LIST
    );
  };
  const isSelectorNode = (astNode) => {
    return (
      (astNode === null || astNode === void 0 ? void 0 : astNode.type) ===
      NODE.SELECTOR
    );
  };
  const isRegularSelectorNode = (astNode) => {
    return (
      (astNode === null || astNode === void 0 ? void 0 : astNode.type) ===
      NODE.REGULAR_SELECTOR
    );
  };
  const isExtendedSelectorNode = (astNode) => {
    return astNode.type === NODE.EXTENDED_SELECTOR;
  };
  const isAbsolutePseudoClassNode = (astNode) => {
    return (
      (astNode === null || astNode === void 0 ? void 0 : astNode.type) ===
      NODE.ABSOLUTE_PSEUDO_CLASS
    );
  };
  const isRelativePseudoClassNode = (astNode) => {
    return (
      (astNode === null || astNode === void 0 ? void 0 : astNode.type) ===
      NODE.RELATIVE_PSEUDO_CLASS
    );
  };
  const getNodeName = (astNode) => {
    if (astNode === null) {
      throw new Error("Ast node should be defined");
    }
    if (
      !isAbsolutePseudoClassNode(astNode) &&
      !isRelativePseudoClassNode(astNode)
    ) {
      throw new Error(
        "Only AbsolutePseudoClass or RelativePseudoClass ast node can have a name"
      );
    }
    if (!astNode.name) {
      throw new Error("Extended pseudo-class should have a name");
    }
    return astNode.name;
  };
  const getNodeValue = (astNode, errorMessage) => {
    if (astNode === null) {
      throw new Error("Ast node should be defined");
    }
    if (
      !isRegularSelectorNode(astNode) &&
      !isAbsolutePseudoClassNode(astNode)
    ) {
      throw new Error(
        "Only RegularSelector ot AbsolutePseudoClass ast node can have a value"
      );
    }
    if (!astNode.value) {
      throw new Error(
        errorMessage ||
          "Ast RegularSelector ot AbsolutePseudoClass node should have a value"
      );
    }
    return astNode.value;
  };
  const getRegularSelectorNodes = (children) => {
    return children.filter(isRegularSelectorNode);
  };
  const getFirstRegularChild = (children, errorMessage) => {
    const regularSelectorNodes = getRegularSelectorNodes(children);
    const firstRegularSelectorNode = getFirst(regularSelectorNodes);
    if (!firstRegularSelectorNode) {
      throw new Error(errorMessage || NO_REGULAR_SELECTOR_ERROR);
    }
    return firstRegularSelectorNode;
  };
  const getLastRegularChild = (children) => {
    const regularSelectorNodes = getRegularSelectorNodes(children);
    const lastRegularSelectorNode = getLast(regularSelectorNodes);
    if (!lastRegularSelectorNode) {
      throw new Error(NO_REGULAR_SELECTOR_ERROR);
    }
    return lastRegularSelectorNode;
  };
  const getNodeOnlyChild = (node, errorMessage) => {
    if (node.children.length !== 1) {
      throw new Error(errorMessage);
    }
    const onlyChild = getFirst(node.children);
    if (!onlyChild) {
      throw new Error(errorMessage);
    }
    return onlyChild;
  };
  const getPseudoClassNode = (extendedSelectorNode) => {
    return getNodeOnlyChild(
      extendedSelectorNode,
      "Extended selector should be specified"
    );
  };
  const getRelativeSelectorListNode = (pseudoClassNode) => {
    if (!isRelativePseudoClassNode(pseudoClassNode)) {
      throw new Error(
        "Only RelativePseudoClass node can have relative SelectorList node as child"
      );
    }
    return getNodeOnlyChild(
      pseudoClassNode,
      `Missing arg for :${getNodeName(pseudoClassNode)}() pseudo-class`
    );
  };
  const ATTRIBUTE_CASE_INSENSITIVE_FLAG = "i";
  const POSSIBLE_MARKS_BEFORE_REGEXP = {
    COMMON: [
      BRACKET.PARENTHESES.LEFT,
      SINGLE_QUOTE,
      DOUBLE_QUOTE,
      EQUAL_SIGN,
      DOT,
      COLON,
      SPACE,
    ],
    CONTAINS: [BRACKET.PARENTHESES.LEFT, SINGLE_QUOTE, DOUBLE_QUOTE],
  };
  const isSupportedPseudoClass = (tokenValue) => {
    return SUPPORTED_PSEUDO_CLASSES.includes(tokenValue);
  };
  const isOptimizationPseudoClass = (name) => {
    return OPTIMIZATION_PSEUDO_CLASSES.includes(name);
  };
  const doesRegularContinueAfterSpace = (nextTokenType, nextTokenValue) => {
    if (!nextTokenType || !nextTokenValue) {
      return false;
    }
    return (
      COMBINATORS.includes(nextTokenValue) ||
      nextTokenType === TOKEN_TYPE.WORD ||
      nextTokenValue === ASTERISK ||
      nextTokenValue === ID_MARKER ||
      nextTokenValue === CLASS_MARKER ||
      nextTokenValue === COLON ||
      nextTokenValue === SINGLE_QUOTE ||
      nextTokenValue === DOUBLE_QUOTE ||
      nextTokenValue === BRACKET.SQUARE.LEFT
    );
  };
  const isRegexpOpening = (context, prevTokenValue, bufferNodeValue) => {
    const lastExtendedPseudoClassName = getLast(
      context.extendedPseudoNamesStack
    );
    if (!lastExtendedPseudoClassName) {
      throw new Error(
        "Regexp pattern allowed only in arg of extended pseudo-class"
      );
    }
    if (CONTAINS_PSEUDO_NAMES.includes(lastExtendedPseudoClassName)) {
      return POSSIBLE_MARKS_BEFORE_REGEXP.CONTAINS.includes(prevTokenValue);
    }
    if (
      prevTokenValue === SLASH &&
      lastExtendedPseudoClassName !== XPATH_PSEUDO_CLASS_MARKER
    ) {
      const rawArgDesc = bufferNodeValue
        ? `in arg part: '${bufferNodeValue}'`
        : "arg";
      throw new Error(
        `Invalid regexp pattern for :${lastExtendedPseudoClassName}() pseudo-class ${rawArgDesc}`
      );
    }
    return POSSIBLE_MARKS_BEFORE_REGEXP.COMMON.includes(prevTokenValue);
  };
  const isAttributeOpening = (tokenValue, prevTokenValue) => {
    return tokenValue === BRACKET.SQUARE.LEFT && prevTokenValue !== BACKSLASH;
  };
  const isAttributeClosing = (context) => {
    var _getPrevToLast;
    if (!context.isAttributeBracketsOpen) {
      return false;
    }
    const noSpaceAttr = context.attributeBuffer.split(SPACE).join("");
    const attrTokens = tokenizeAttribute(noSpaceAttr);
    const firstAttrToken = getFirst(attrTokens);
    const firstAttrTokenType =
      firstAttrToken === null || firstAttrToken === void 0
        ? void 0
        : firstAttrToken.type;
    const firstAttrTokenValue =
      firstAttrToken === null || firstAttrToken === void 0
        ? void 0
        : firstAttrToken.value;
    if (
      firstAttrTokenType === TOKEN_TYPE.MARK &&
      firstAttrTokenValue !== BACKSLASH
    ) {
      throw new Error(
        `'[${context.attributeBuffer}]' is not a valid attribute due to '${firstAttrTokenValue}' at start of it`
      );
    }
    const lastAttrToken = getLast(attrTokens);
    const lastAttrTokenType =
      lastAttrToken === null || lastAttrToken === void 0
        ? void 0
        : lastAttrToken.type;
    const lastAttrTokenValue =
      lastAttrToken === null || lastAttrToken === void 0
        ? void 0
        : lastAttrToken.value;
    if (lastAttrTokenValue === EQUAL_SIGN) {
      throw new Error(
        `'[${context.attributeBuffer}]' is not a valid attribute due to '${EQUAL_SIGN}'`
      );
    }
    const equalSignIndex = attrTokens.findIndex((token) => {
      return token.type === TOKEN_TYPE.MARK && token.value === EQUAL_SIGN;
    });
    const prevToLastAttrTokenValue =
      (_getPrevToLast = getPrevToLast(attrTokens)) === null ||
      _getPrevToLast === void 0
        ? void 0
        : _getPrevToLast.value;
    if (equalSignIndex === -1) {
      if (lastAttrTokenType === TOKEN_TYPE.WORD) {
        return true;
      }
      return (
        prevToLastAttrTokenValue === BACKSLASH &&
        (lastAttrTokenValue === DOUBLE_QUOTE ||
          lastAttrTokenValue === SINGLE_QUOTE)
      );
    }
    const nextToEqualSignToken = getItemByIndex(attrTokens, equalSignIndex + 1);
    const nextToEqualSignTokenValue = nextToEqualSignToken.value;
    const isAttrValueQuote =
      nextToEqualSignTokenValue === SINGLE_QUOTE ||
      nextToEqualSignTokenValue === DOUBLE_QUOTE;
    if (!isAttrValueQuote) {
      if (lastAttrTokenType === TOKEN_TYPE.WORD) {
        return true;
      }
      throw new Error(
        `'[${context.attributeBuffer}]' is not a valid attribute`
      );
    }
    if (
      lastAttrTokenType === TOKEN_TYPE.WORD &&
      (lastAttrTokenValue === null || lastAttrTokenValue === void 0
        ? void 0
        : lastAttrTokenValue.toLocaleLowerCase()) ===
        ATTRIBUTE_CASE_INSENSITIVE_FLAG
    ) {
      return prevToLastAttrTokenValue === nextToEqualSignTokenValue;
    }
    return lastAttrTokenValue === nextToEqualSignTokenValue;
  };
  const isWhiteSpaceChar = (tokenValue) => {
    if (!tokenValue) {
      return false;
    }
    return WHITE_SPACE_CHARACTERS.includes(tokenValue);
  };
  const isAbsolutePseudoClass = (str) => {
    return ABSOLUTE_PSEUDO_CLASSES.includes(str);
  };
  const isRelativePseudoClass = (str) => {
    return RELATIVE_PSEUDO_CLASSES.includes(str);
  };
  const getBufferNode = (context) => {
    if (context.pathToBufferNode.length === 0) {
      return null;
    }
    return getLast(context.pathToBufferNode) || null;
  };
  const getBufferNodeParent = (context) => {
    if (context.pathToBufferNode.length < 2) {
      return null;
    }
    return getPrevToLast(context.pathToBufferNode) || null;
  };
  const getContextLastRegularSelectorNode = (context) => {
    const bufferNode = getBufferNode(context);
    if (!bufferNode) {
      throw new Error("No bufferNode found");
    }
    if (!isSelectorNode(bufferNode)) {
      throw new Error("Unsupported bufferNode type");
    }
    const lastRegularSelectorNode = getLastRegularChild(bufferNode.children);
    context.pathToBufferNode.push(lastRegularSelectorNode);
    return lastRegularSelectorNode;
  };
  const updateBufferNode = (context, tokenValue) => {
    const bufferNode = getBufferNode(context);
    if (bufferNode === null) {
      throw new Error("No bufferNode to update");
    }
    if (isAbsolutePseudoClassNode(bufferNode)) {
      bufferNode.value += tokenValue;
    } else if (isRegularSelectorNode(bufferNode)) {
      bufferNode.value += tokenValue;
      if (context.isAttributeBracketsOpen) {
        context.attributeBuffer += tokenValue;
      }
    } else {
      throw new Error(
        `${bufferNode.type} node cannot be updated. Only RegularSelector and AbsolutePseudoClass are supported`
      );
    }
  };
  const addSelectorListNode = (context) => {
    const selectorListNode = new AnySelectorNode(NODE.SELECTOR_LIST);
    context.ast = selectorListNode;
    context.pathToBufferNode.push(selectorListNode);
  };
  const addAstNodeByType = function (context, type) {
    let tokenValue =
      arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "";
    const bufferNode = getBufferNode(context);
    if (bufferNode === null) {
      throw new Error("No buffer node");
    }
    let node;
    if (type === NODE.REGULAR_SELECTOR) {
      node = new RegularSelectorNode(tokenValue);
    } else if (type === NODE.ABSOLUTE_PSEUDO_CLASS) {
      node = new AbsolutePseudoClassNode(tokenValue);
    } else if (type === NODE.RELATIVE_PSEUDO_CLASS) {
      node = new RelativePseudoClassNode(tokenValue);
    } else {
      node = new AnySelectorNode(type);
    }
    bufferNode.addChild(node);
    context.pathToBufferNode.push(node);
  };
  const initAst = (context, tokenValue) => {
    addSelectorListNode(context);
    addAstNodeByType(context, NODE.SELECTOR);
    addAstNodeByType(context, NODE.REGULAR_SELECTOR, tokenValue);
  };
  const initRelativeSubtree = function (context) {
    let tokenValue =
      arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
    addAstNodeByType(context, NODE.SELECTOR_LIST);
    addAstNodeByType(context, NODE.SELECTOR);
    addAstNodeByType(context, NODE.REGULAR_SELECTOR, tokenValue);
  };
  const upToClosest = (context, parentType) => {
    for (let i = context.pathToBufferNode.length - 1; i >= 0; i -= 1) {
      var _context$pathToBuffer;
      if (
        ((_context$pathToBuffer = context.pathToBufferNode[i]) === null ||
        _context$pathToBuffer === void 0
          ? void 0
          : _context$pathToBuffer.type) === parentType
      ) {
        context.pathToBufferNode = context.pathToBufferNode.slice(0, i + 1);
        break;
      }
    }
  };
  const getUpdatedBufferNode = (context) => {
    const bufferNode = getBufferNode(context);
    if (
      bufferNode &&
      isSelectorListNode(bufferNode) &&
      isRelativePseudoClassNode(getBufferNodeParent(context))
    ) {
      return bufferNode;
    }
    upToClosest(context, NODE.SELECTOR);
    const selectorNode = getBufferNode(context);
    if (!selectorNode) {
      throw new Error(
        "No SelectorNode, impossible to continue selector parsing by ExtendedCss"
      );
    }
    const lastSelectorNodeChild = getLast(selectorNode.children);
    const hasExtended =
      lastSelectorNodeChild &&
      isExtendedSelectorNode(lastSelectorNodeChild) &&
      context.standardPseudoBracketsStack.length === 0;
    const supposedPseudoClassNode =
      hasExtended && getFirst(lastSelectorNodeChild.children);
    let newNeededBufferNode = selectorNode;
    if (supposedPseudoClassNode) {
      const lastExtendedPseudoName =
        hasExtended && supposedPseudoClassNode.name;
      const isLastExtendedNameRelative =
        lastExtendedPseudoName && isRelativePseudoClass(lastExtendedPseudoName);
      const isLastExtendedNameAbsolute =
        lastExtendedPseudoName && isAbsolutePseudoClass(lastExtendedPseudoName);
      const hasRelativeExtended =
        isLastExtendedNameRelative &&
        context.extendedPseudoBracketsStack.length > 0 &&
        context.extendedPseudoBracketsStack.length ===
          context.extendedPseudoNamesStack.length;
      const hasAbsoluteExtended =
        isLastExtendedNameAbsolute &&
        lastExtendedPseudoName === getLast(context.extendedPseudoNamesStack);
      if (hasRelativeExtended) {
        context.pathToBufferNode.push(lastSelectorNodeChild);
        newNeededBufferNode = supposedPseudoClassNode;
      } else if (hasAbsoluteExtended) {
        context.pathToBufferNode.push(lastSelectorNodeChild);
        newNeededBufferNode = supposedPseudoClassNode;
      }
    } else if (hasExtended) {
      newNeededBufferNode = selectorNode;
    } else {
      newNeededBufferNode = getContextLastRegularSelectorNode(context);
    }
    context.pathToBufferNode.push(newNeededBufferNode);
    return newNeededBufferNode;
  };
  const handleNextTokenOnColon = (
    context,
    selector,
    tokenValue,
    nextTokenValue,
    nextToNextTokenValue
  ) => {
    if (!nextTokenValue) {
      throw new Error(
        `Invalid colon ':' at the end of selector: '${selector}'`
      );
    }
    if (!isSupportedPseudoClass(nextTokenValue.toLowerCase())) {
      if (nextTokenValue.toLowerCase() === REMOVE_PSEUDO_MARKER) {
        throw new Error(`${REMOVE_ERROR_PREFIX.INVALID_REMOVE}: '${selector}'`);
      }
      updateBufferNode(context, tokenValue);
      if (
        nextToNextTokenValue &&
        nextToNextTokenValue === BRACKET.PARENTHESES.LEFT &&
        !context.isAttributeBracketsOpen
      ) {
        context.standardPseudoNamesStack.push(nextTokenValue);
      }
    } else {
      if (
        HAS_PSEUDO_CLASS_MARKERS.includes(nextTokenValue) &&
        context.standardPseudoNamesStack.length > 0
      ) {
        throw new Error(
          `Usage of :${nextTokenValue}() pseudo-class is not allowed inside regular pseudo: '${getLast(
            context.standardPseudoNamesStack
          )}'`
        );
      } else {
        upToClosest(context, NODE.SELECTOR);
        addAstNodeByType(context, NODE.EXTENDED_SELECTOR);
      }
    }
  };
  const IS_OR_NOT_PSEUDO_SELECTING_ROOT = `html ${ASTERISK}`;
  const hasExtendedSelector = (selectorList) => {
    return selectorList.children.some((selectorNode) => {
      return selectorNode.children.some((selectorNodeChild) => {
        return isExtendedSelectorNode(selectorNodeChild);
      });
    });
  };
  const selectorListOfRegularsToString = (selectorList) => {
    const standardCssSelectors = selectorList.children.map((selectorNode) => {
      const selectorOnlyChild = getNodeOnlyChild(
        selectorNode,
        "Ast Selector node should have RegularSelector node"
      );
      return getNodeValue(selectorOnlyChild);
    });
    return standardCssSelectors.join(`${COMMA}${SPACE}`);
  };
  const updateNodeChildren = (node, newChildren) => {
    node.children = newChildren;
    return node;
  };
  const shouldOptimizeExtendedSelector = (currExtendedSelectorNode) => {
    if (currExtendedSelectorNode === null) {
      return false;
    }
    const extendedPseudoClassNode = getPseudoClassNode(
      currExtendedSelectorNode
    );
    const pseudoName = getNodeName(extendedPseudoClassNode);
    if (isAbsolutePseudoClass(pseudoName)) {
      return false;
    }
    const relativeSelectorList = getRelativeSelectorListNode(
      extendedPseudoClassNode
    );
    const innerSelectorNodes = relativeSelectorList.children;
    if (isOptimizationPseudoClass(pseudoName)) {
      const areAllSelectorNodeChildrenRegular = innerSelectorNodes.every(
        (selectorNode) => {
          try {
            const selectorOnlyChild = getNodeOnlyChild(
              selectorNode,
              "Selector node should have RegularSelector"
            );
            return isRegularSelectorNode(selectorOnlyChild);
          } catch (e) {
            return false;
          }
        }
      );
      if (areAllSelectorNodeChildrenRegular) {
        return true;
      }
    }
    return innerSelectorNodes.some((selectorNode) => {
      return selectorNode.children.some((selectorNodeChild) => {
        if (!isExtendedSelectorNode(selectorNodeChild)) {
          return false;
        }
        return shouldOptimizeExtendedSelector(selectorNodeChild);
      });
    });
  };
  const getOptimizedExtendedSelector = (
    currExtendedSelectorNode,
    prevRegularSelectorNode
  ) => {
    if (!currExtendedSelectorNode) {
      return null;
    }
    const extendedPseudoClassNode = getPseudoClassNode(
      currExtendedSelectorNode
    );
    const relativeSelectorList = getRelativeSelectorListNode(
      extendedPseudoClassNode
    );
    const hasInnerExtendedSelector = hasExtendedSelector(relativeSelectorList);
    if (!hasInnerExtendedSelector) {
      const relativeSelectorListStr =
        selectorListOfRegularsToString(relativeSelectorList);
      const pseudoName = getNodeName(extendedPseudoClassNode);
      const optimizedExtendedStr = `${COLON}${pseudoName}${BRACKET.PARENTHESES.LEFT}${relativeSelectorListStr}${BRACKET.PARENTHESES.RIGHT}`;
      prevRegularSelectorNode.value = `${getNodeValue(
        prevRegularSelectorNode
      )}${optimizedExtendedStr}`;
      return null;
    }
    const optimizedRelativeSelectorList =
      optimizeSelectorListNode(relativeSelectorList);
    const optimizedExtendedPseudoClassNode = updateNodeChildren(
      extendedPseudoClassNode,
      [optimizedRelativeSelectorList]
    );
    return updateNodeChildren(currExtendedSelectorNode, [
      optimizedExtendedPseudoClassNode,
    ]);
  };
  const optimizeCurrentRegularSelector = (current, previous) => {
    previous.value = `${getNodeValue(previous)}${SPACE}${getNodeValue(
      current
    )}`;
  };
  const optimizeSelectorNode = (selectorNode) => {
    const rawSelectorNodeChildren = selectorNode.children;
    const optimizedChildrenList = [];
    let currentIndex = 0;
    while (currentIndex < rawSelectorNodeChildren.length) {
      const currentChild = getItemByIndex(
        rawSelectorNodeChildren,
        currentIndex,
        "currentChild should be specified"
      );
      if (currentIndex === 0) {
        optimizedChildrenList.push(currentChild);
      } else {
        const prevRegularChild = getLastRegularChild(optimizedChildrenList);
        if (isExtendedSelectorNode(currentChild)) {
          let optimizedExtendedSelector = null;
          let isOptimizationNeeded =
            shouldOptimizeExtendedSelector(currentChild);
          optimizedExtendedSelector = currentChild;
          while (isOptimizationNeeded) {
            optimizedExtendedSelector = getOptimizedExtendedSelector(
              optimizedExtendedSelector,
              prevRegularChild
            );
            isOptimizationNeeded = shouldOptimizeExtendedSelector(
              optimizedExtendedSelector
            );
          }
          if (optimizedExtendedSelector !== null) {
            optimizedChildrenList.push(optimizedExtendedSelector);
            const optimizedPseudoClass = getPseudoClassNode(
              optimizedExtendedSelector
            );
            const optimizedPseudoName = getNodeName(optimizedPseudoClass);
            if (
              getNodeValue(prevRegularChild) === ASTERISK &&
              isOptimizationPseudoClass(optimizedPseudoName)
            ) {
              prevRegularChild.value = IS_OR_NOT_PSEUDO_SELECTING_ROOT;
            }
          }
        } else if (isRegularSelectorNode(currentChild)) {
          const lastOptimizedChild = getLast(optimizedChildrenList) || null;
          if (isRegularSelectorNode(lastOptimizedChild)) {
            optimizeCurrentRegularSelector(currentChild, prevRegularChild);
          }
        }
      }
      currentIndex += 1;
    }
    return updateNodeChildren(selectorNode, optimizedChildrenList);
  };
  const optimizeSelectorListNode = (selectorListNode) => {
    return updateNodeChildren(
      selectorListNode,
      selectorListNode.children.map((s) => optimizeSelectorNode(s))
    );
  };
  const optimizeAst = (ast) => {
    return optimizeSelectorListNode(ast);
  };
  const XPATH_PSEUDO_SELECTING_ROOT = "body";
  const NO_WHITESPACE_ERROR_PREFIX =
    "No white space is allowed before or after extended pseudo-class name in selector";
  const parse = (selector) => {
    const tokens = tokenizeSelector(selector);
    const context = {
      ast: null,
      pathToBufferNode: [],
      extendedPseudoNamesStack: [],
      extendedPseudoBracketsStack: [],
      standardPseudoNamesStack: [],
      standardPseudoBracketsStack: [],
      isAttributeBracketsOpen: false,
      attributeBuffer: "",
      isRegexpOpen: false,
      shouldOptimize: false,
    };
    let i = 0;
    while (i < tokens.length) {
      const token = tokens[i];
      if (!token) {
        break;
      }
      const { type: tokenType, value: tokenValue } = token;
      const nextToken = tokens[i + 1];
      const nextTokenType =
        nextToken === null || nextToken === void 0 ? void 0 : nextToken.type;
      const nextTokenValue =
        nextToken === null || nextToken === void 0 ? void 0 : nextToken.value;
      const nextToNextToken = tokens[i + 2];
      const nextToNextTokenValue =
        nextToNextToken === null || nextToNextToken === void 0
          ? void 0
          : nextToNextToken.value;
      const previousToken = tokens[i - 1];
      const prevTokenType =
        previousToken === null || previousToken === void 0
          ? void 0
          : previousToken.type;
      const prevTokenValue =
        previousToken === null || previousToken === void 0
          ? void 0
          : previousToken.value;
      const previousToPreviousToken = tokens[i - 2];
      const prevToPrevTokenValue =
        previousToPreviousToken === null || previousToPreviousToken === void 0
          ? void 0
          : previousToPreviousToken.value;
      let bufferNode = getBufferNode(context);
      switch (tokenType) {
        case TOKEN_TYPE.WORD:
          if (bufferNode === null) {
            initAst(context, tokenValue);
          } else if (isSelectorListNode(bufferNode)) {
            addAstNodeByType(context, NODE.SELECTOR);
            addAstNodeByType(context, NODE.REGULAR_SELECTOR, tokenValue);
          } else if (isRegularSelectorNode(bufferNode)) {
            updateBufferNode(context, tokenValue);
          } else if (isExtendedSelectorNode(bufferNode)) {
            if (
              isWhiteSpaceChar(nextTokenValue) &&
              nextToNextTokenValue === BRACKET.PARENTHESES.LEFT
            ) {
              throw new Error(`${NO_WHITESPACE_ERROR_PREFIX}: '${selector}'`);
            }
            const lowerCaseTokenValue = tokenValue.toLowerCase();
            context.extendedPseudoNamesStack.push(lowerCaseTokenValue);
            if (isAbsolutePseudoClass(lowerCaseTokenValue)) {
              addAstNodeByType(
                context,
                NODE.ABSOLUTE_PSEUDO_CLASS,
                lowerCaseTokenValue
              );
            } else {
              addAstNodeByType(
                context,
                NODE.RELATIVE_PSEUDO_CLASS,
                lowerCaseTokenValue
              );
              if (isOptimizationPseudoClass(lowerCaseTokenValue)) {
                context.shouldOptimize = true;
              }
            }
          } else if (isAbsolutePseudoClassNode(bufferNode)) {
            updateBufferNode(context, tokenValue);
          } else if (isRelativePseudoClassNode(bufferNode)) {
            initRelativeSubtree(context, tokenValue);
          }
          break;
        case TOKEN_TYPE.MARK:
          switch (tokenValue) {
            case COMMA:
              if (
                !bufferNode ||
                (typeof bufferNode !== "undefined" && !nextTokenValue)
              ) {
                throw new Error(`'${selector}' is not a valid selector`);
              } else if (isRegularSelectorNode(bufferNode)) {
                if (context.isAttributeBracketsOpen) {
                  updateBufferNode(context, tokenValue);
                } else {
                  upToClosest(context, NODE.SELECTOR_LIST);
                }
              } else if (isAbsolutePseudoClassNode(bufferNode)) {
                updateBufferNode(context, tokenValue);
              } else if (isSelectorNode(bufferNode)) {
                upToClosest(context, NODE.SELECTOR_LIST);
              }
              break;
            case SPACE:
              if (
                isRegularSelectorNode(bufferNode) &&
                !context.isAttributeBracketsOpen
              ) {
                bufferNode = getUpdatedBufferNode(context);
              }
              if (isRegularSelectorNode(bufferNode)) {
                if (
                  !context.isAttributeBracketsOpen &&
                  ((prevTokenValue === COLON &&
                    nextTokenType === TOKEN_TYPE.WORD) ||
                    (prevTokenType === TOKEN_TYPE.WORD &&
                      nextTokenValue === BRACKET.PARENTHESES.LEFT))
                ) {
                  throw new Error(`'${selector}' is not a valid selector`);
                }
                if (
                  !nextTokenValue ||
                  doesRegularContinueAfterSpace(
                    nextTokenType,
                    nextTokenValue
                  ) ||
                  context.isAttributeBracketsOpen
                ) {
                  updateBufferNode(context, tokenValue);
                }
              }
              if (isAbsolutePseudoClassNode(bufferNode)) {
                updateBufferNode(context, tokenValue);
              }
              if (isRelativePseudoClassNode(bufferNode)) {
                initRelativeSubtree(context);
              }
              if (isSelectorNode(bufferNode)) {
                if (
                  doesRegularContinueAfterSpace(nextTokenType, nextTokenValue)
                ) {
                  addAstNodeByType(context, NODE.REGULAR_SELECTOR);
                }
              }
              break;
            case DESCENDANT_COMBINATOR:
            case CHILD_COMBINATOR:
            case NEXT_SIBLING_COMBINATOR:
            case SUBSEQUENT_SIBLING_COMBINATOR:
            case SEMICOLON:
            case SLASH:
            case BACKSLASH:
            case SINGLE_QUOTE:
            case DOUBLE_QUOTE:
            case CARET:
            case DOLLAR_SIGN:
            case BRACKET.CURLY.LEFT:
            case BRACKET.CURLY.RIGHT:
            case ASTERISK:
            case ID_MARKER:
            case CLASS_MARKER:
            case BRACKET.SQUARE.LEFT:
              if (COMBINATORS.includes(tokenValue)) {
                if (bufferNode === null) {
                  throw new Error(`'${selector}' is not a valid selector`);
                }
                bufferNode = getUpdatedBufferNode(context);
              }
              if (bufferNode === null) {
                initAst(context, tokenValue);
                if (isAttributeOpening(tokenValue, prevTokenValue)) {
                  context.isAttributeBracketsOpen = true;
                }
              } else if (isRegularSelectorNode(bufferNode)) {
                if (
                  tokenValue === BRACKET.CURLY.LEFT &&
                  !(context.isAttributeBracketsOpen || context.isRegexpOpen)
                ) {
                  throw new Error(`'${selector}' is not a valid selector`);
                }
                updateBufferNode(context, tokenValue);
                if (isAttributeOpening(tokenValue, prevTokenValue)) {
                  context.isAttributeBracketsOpen = true;
                }
              } else if (isAbsolutePseudoClassNode(bufferNode)) {
                updateBufferNode(context, tokenValue);
                if (
                  tokenValue === SLASH &&
                  context.extendedPseudoNamesStack.length > 0
                ) {
                  if (
                    prevTokenValue === SLASH &&
                    prevToPrevTokenValue === BACKSLASH
                  ) {
                    context.isRegexpOpen = false;
                  } else if (prevTokenValue && prevTokenValue !== BACKSLASH) {
                    if (
                      isRegexpOpening(
                        context,
                        prevTokenValue,
                        getNodeValue(bufferNode)
                      )
                    ) {
                      context.isRegexpOpen = !context.isRegexpOpen;
                    } else {
                      context.isRegexpOpen = false;
                    }
                  }
                }
              } else if (isRelativePseudoClassNode(bufferNode)) {
                initRelativeSubtree(context, tokenValue);
                if (isAttributeOpening(tokenValue, prevTokenValue)) {
                  context.isAttributeBracketsOpen = true;
                }
              } else if (isSelectorNode(bufferNode)) {
                if (COMBINATORS.includes(tokenValue)) {
                  addAstNodeByType(context, NODE.REGULAR_SELECTOR, tokenValue);
                } else if (!context.isRegexpOpen) {
                  bufferNode = getContextLastRegularSelectorNode(context);
                  updateBufferNode(context, tokenValue);
                  if (isAttributeOpening(tokenValue, prevTokenValue)) {
                    context.isAttributeBracketsOpen = true;
                  }
                }
              } else if (isSelectorListNode(bufferNode)) {
                addAstNodeByType(context, NODE.SELECTOR);
                addAstNodeByType(context, NODE.REGULAR_SELECTOR, tokenValue);
                if (isAttributeOpening(tokenValue, prevTokenValue)) {
                  context.isAttributeBracketsOpen = true;
                }
              }
              break;
            case BRACKET.SQUARE.RIGHT:
              if (isRegularSelectorNode(bufferNode)) {
                if (
                  !context.isAttributeBracketsOpen &&
                  prevTokenValue !== BACKSLASH
                ) {
                  throw new Error(
                    `'${selector}' is not a valid selector due to '${tokenValue}' after '${getNodeValue(
                      bufferNode
                    )}'`
                  );
                }
                if (isAttributeClosing(context)) {
                  context.isAttributeBracketsOpen = false;
                  context.attributeBuffer = "";
                }
                updateBufferNode(context, tokenValue);
              }
              if (isAbsolutePseudoClassNode(bufferNode)) {
                updateBufferNode(context, tokenValue);
              }
              break;
            case COLON:
              if (
                isWhiteSpaceChar(nextTokenValue) &&
                nextToNextTokenValue &&
                SUPPORTED_PSEUDO_CLASSES.includes(nextToNextTokenValue)
              ) {
                throw new Error(`${NO_WHITESPACE_ERROR_PREFIX}: '${selector}'`);
              }
              if (bufferNode === null) {
                if (nextTokenValue === XPATH_PSEUDO_CLASS_MARKER) {
                  initAst(context, XPATH_PSEUDO_SELECTING_ROOT);
                } else if (
                  nextTokenValue === UPWARD_PSEUDO_CLASS_MARKER ||
                  nextTokenValue === NTH_ANCESTOR_PSEUDO_CLASS_MARKER
                ) {
                  throw new Error(
                    `${NO_SELECTOR_ERROR_PREFIX} before :${nextTokenValue}() pseudo-class`
                  );
                } else {
                  initAst(context, ASTERISK);
                }
                bufferNode = getBufferNode(context);
              }
              if (isSelectorListNode(bufferNode)) {
                addAstNodeByType(context, NODE.SELECTOR);
                addAstNodeByType(context, NODE.REGULAR_SELECTOR);
                bufferNode = getBufferNode(context);
              }
              if (isRegularSelectorNode(bufferNode)) {
                if (
                  (prevTokenValue && COMBINATORS.includes(prevTokenValue)) ||
                  prevTokenValue === COMMA
                ) {
                  updateBufferNode(context, ASTERISK);
                }
                handleNextTokenOnColon(
                  context,
                  selector,
                  tokenValue,
                  nextTokenValue,
                  nextToNextTokenValue
                );
              }
              if (isSelectorNode(bufferNode)) {
                if (!nextTokenValue) {
                  throw new Error(
                    `Invalid colon ':' at the end of selector: '${selector}'`
                  );
                }
                if (isSupportedPseudoClass(nextTokenValue.toLowerCase())) {
                  addAstNodeByType(context, NODE.EXTENDED_SELECTOR);
                } else if (
                  nextTokenValue.toLowerCase() === REMOVE_PSEUDO_MARKER
                ) {
                  throw new Error(
                    `${REMOVE_ERROR_PREFIX.INVALID_REMOVE}: '${selector}'`
                  );
                } else {
                  bufferNode = getContextLastRegularSelectorNode(context);
                  handleNextTokenOnColon(
                    context,
                    selector,
                    tokenValue,
                    nextTokenType,
                    nextToNextTokenValue
                  );
                }
              }
              if (isAbsolutePseudoClassNode(bufferNode)) {
                if (
                  getNodeName(bufferNode) === XPATH_PSEUDO_CLASS_MARKER &&
                  nextTokenValue &&
                  SUPPORTED_PSEUDO_CLASSES.includes(nextTokenValue) &&
                  nextToNextTokenValue === BRACKET.PARENTHESES.LEFT
                ) {
                  throw new Error(
                    `:xpath() pseudo-class should be the last in selector: '${selector}'`
                  );
                }
                updateBufferNode(context, tokenValue);
              }
              if (isRelativePseudoClassNode(bufferNode)) {
                if (!nextTokenValue) {
                  throw new Error(
                    `Invalid pseudo-class arg at the end of selector: '${selector}'`
                  );
                }
                initRelativeSubtree(context, ASTERISK);
                if (!isSupportedPseudoClass(nextTokenValue.toLowerCase())) {
                  updateBufferNode(context, tokenValue);
                  if (nextToNextTokenValue === BRACKET.PARENTHESES.LEFT) {
                    context.standardPseudoNamesStack.push(nextTokenValue);
                  }
                } else {
                  upToClosest(context, NODE.SELECTOR);
                  addAstNodeByType(context, NODE.EXTENDED_SELECTOR);
                }
              }
              break;
            case BRACKET.PARENTHESES.LEFT:
              if (isAbsolutePseudoClassNode(bufferNode)) {
                if (
                  getNodeName(bufferNode) !== XPATH_PSEUDO_CLASS_MARKER &&
                  context.isRegexpOpen
                ) {
                  updateBufferNode(context, tokenValue);
                } else {
                  context.extendedPseudoBracketsStack.push(tokenValue);
                  if (
                    context.extendedPseudoBracketsStack.length >
                    context.extendedPseudoNamesStack.length
                  ) {
                    updateBufferNode(context, tokenValue);
                  }
                }
              }
              if (isRegularSelectorNode(bufferNode)) {
                if (context.standardPseudoNamesStack.length > 0) {
                  updateBufferNode(context, tokenValue);
                  context.standardPseudoBracketsStack.push(tokenValue);
                }
                if (context.isAttributeBracketsOpen) {
                  updateBufferNode(context, tokenValue);
                }
              }
              if (isRelativePseudoClassNode(bufferNode)) {
                context.extendedPseudoBracketsStack.push(tokenValue);
              }
              break;
            case BRACKET.PARENTHESES.RIGHT:
              if (isAbsolutePseudoClassNode(bufferNode)) {
                if (
                  getNodeName(bufferNode) !== XPATH_PSEUDO_CLASS_MARKER &&
                  context.isRegexpOpen
                ) {
                  updateBufferNode(context, tokenValue);
                } else {
                  context.extendedPseudoBracketsStack.pop();
                  if (getNodeName(bufferNode) !== XPATH_PSEUDO_CLASS_MARKER) {
                    context.extendedPseudoNamesStack.pop();
                    if (
                      context.extendedPseudoBracketsStack.length >
                      context.extendedPseudoNamesStack.length
                    ) {
                      updateBufferNode(context, tokenValue);
                    } else if (
                      context.extendedPseudoBracketsStack.length >= 0 &&
                      context.extendedPseudoNamesStack.length >= 0
                    ) {
                      upToClosest(context, NODE.SELECTOR);
                    }
                  } else {
                    if (
                      context.extendedPseudoBracketsStack.length <
                      context.extendedPseudoNamesStack.length
                    ) {
                      context.extendedPseudoNamesStack.pop();
                    } else {
                      updateBufferNode(context, tokenValue);
                    }
                  }
                }
              }
              if (isRegularSelectorNode(bufferNode)) {
                if (context.isAttributeBracketsOpen) {
                  updateBufferNode(context, tokenValue);
                } else if (
                  context.standardPseudoNamesStack.length > 0 &&
                  context.standardPseudoBracketsStack.length > 0
                ) {
                  updateBufferNode(context, tokenValue);
                  context.standardPseudoBracketsStack.pop();
                  const lastStandardPseudo =
                    context.standardPseudoNamesStack.pop();
                  if (!lastStandardPseudo) {
                    throw new Error(
                      `Parsing error. Invalid selector: ${selector}`
                    );
                  }
                  if (
                    Object.values(REGULAR_PSEUDO_ELEMENTS).includes(
                      lastStandardPseudo
                    ) &&
                    nextTokenValue === COLON &&
                    nextToNextTokenValue &&
                    HAS_PSEUDO_CLASS_MARKERS.includes(nextToNextTokenValue)
                  ) {
                    throw new Error(
                      `Usage of :${nextToNextTokenValue}() pseudo-class is not allowed after any regular pseudo-element: '${lastStandardPseudo}'`
                    );
                  }
                } else {
                  context.extendedPseudoBracketsStack.pop();
                  context.extendedPseudoNamesStack.pop();
                  upToClosest(context, NODE.EXTENDED_SELECTOR);
                  upToClosest(context, NODE.SELECTOR);
                }
              }
              if (isSelectorNode(bufferNode)) {
                context.extendedPseudoBracketsStack.pop();
                context.extendedPseudoNamesStack.pop();
                upToClosest(context, NODE.EXTENDED_SELECTOR);
                upToClosest(context, NODE.SELECTOR);
              }
              if (isRelativePseudoClassNode(bufferNode)) {
                if (
                  context.extendedPseudoNamesStack.length > 0 &&
                  context.extendedPseudoBracketsStack.length > 0
                ) {
                  context.extendedPseudoBracketsStack.pop();
                  context.extendedPseudoNamesStack.pop();
                }
              }
              break;
            case LINE_FEED:
            case FORM_FEED:
            case CARRIAGE_RETURN:
              throw new Error(`'${selector}' is not a valid selector`);
            case TAB:
              if (
                isRegularSelectorNode(bufferNode) &&
                context.isAttributeBracketsOpen
              ) {
                updateBufferNode(context, tokenValue);
              } else {
                throw new Error(`'${selector}' is not a valid selector`);
              }
          }
          break;
        default:
          throw new Error(`Unknown type of token: '${tokenValue}'`);
      }
      i += 1;
    }
    if (context.ast === null) {
      throw new Error(`'${selector}' is not a valid selector`);
    }
    if (
      context.extendedPseudoNamesStack.length > 0 ||
      context.extendedPseudoBracketsStack.length > 0
    ) {
      throw new Error(
        `Unbalanced brackets for extended pseudo-class: '${getLast(
          context.extendedPseudoNamesStack
        )}'`
      );
    }
    if (context.isAttributeBracketsOpen) {
      throw new Error(
        `Unbalanced attribute brackets in selector: '${selector}'`
      );
    }
    return context.shouldOptimize ? optimizeAst(context.ast) : context.ast;
  };
  const natives = {
    MutationObserver: window.MutationObserver || window.WebKitMutationObserver,
  };
  class NativeTextContent {
    constructor() {
      this.nativeNode = window.Node || Node;
    }
    setGetter() {
      var _Object$getOwnPropert;
      this.getter =
        (_Object$getOwnPropert = Object.getOwnPropertyDescriptor(
          this.nativeNode.prototype,
          "textContent"
        )) === null || _Object$getOwnPropert === void 0
          ? void 0
          : _Object$getOwnPropert.get;
    }
  }
  const nativeTextContent = new NativeTextContent();
  const getNodeTextContent = (domElement) => {
    if (nativeTextContent.getter) {
      return nativeTextContent.getter.apply(domElement);
    }
    return domElement.textContent || "";
  };
  const getElementSelectorDesc = (element) => {
    let selectorText = element.tagName.toLowerCase();
    selectorText += Array.from(element.attributes)
      .map((attr) => {
        return `[${attr.name}="${element.getAttribute(attr.name)}"]`;
      })
      .join("");
    return selectorText;
  };
  const getElementSelectorPath = (inputEl) => {
    if (!(inputEl instanceof Element)) {
      throw new Error("Function received argument with wrong type");
    }
    let el;
    el = inputEl;
    const path = [];
    while (!!el && el.nodeType === Node.ELEMENT_NODE) {
      let selector = el.nodeName.toLowerCase();
      if (el.id && typeof el.id === "string") {
        selector += `#${el.id}`;
        path.unshift(selector);
        break;
      }
      let sibling = el;
      let nth = 1;
      while (sibling.previousElementSibling) {
        sibling = sibling.previousElementSibling;
        if (
          sibling.nodeType === Node.ELEMENT_NODE &&
          sibling.nodeName.toLowerCase() === selector
        ) {
          nth += 1;
        }
      }
      if (nth !== 1) {
        selector += `:nth-of-type(${nth})`;
      }
      path.unshift(selector);
      el = el.parentElement;
    }
    return path.join(" > ");
  };
  const isHtmlElement = (element) => {
    return element instanceof HTMLElement;
  };
  const getParent = (element, errorMessage) => {
    const { parentElement } = element;
    if (!parentElement) {
      throw new Error(errorMessage || "Element does no have parent element");
    }
    return parentElement;
  };
  const isErrorWithMessage = (error) => {
    return (
      typeof error === "object" &&
      error !== null &&
      "message" in error &&
      typeof error.message === "string"
    );
  };
  const toErrorWithMessage = (maybeError) => {
    if (isErrorWithMessage(maybeError)) {
      return maybeError;
    }
    try {
      return new Error(JSON.stringify(maybeError));
    } catch {
      return new Error(String(maybeError));
    }
  };
  const getErrorMessage = (error) => {
    return toErrorWithMessage(error).message;
  };
  const logger = {
    error:
      typeof console !== "undefined" && console.error && console.error.bind
        ? console.error.bind(window.console)
        : console.error,
    info:
      typeof console !== "undefined" && console.info && console.info.bind
        ? console.info.bind(window.console)
        : console.info,
  };
  const removeSuffix = (str, suffix) => {
    const index = str.indexOf(suffix, str.length - suffix.length);
    if (index >= 0) {
      return str.substring(0, index);
    }
    return str;
  };
  const replaceAll = (input, pattern, replacement) => {
    if (!input) {
      return input;
    }
    return input.split(pattern).join(replacement);
  };
  const toRegExp = (str) => {
    if (str.startsWith(SLASH) && str.endsWith(SLASH)) {
      return new RegExp(str.slice(1, -1));
    }
    const escaped = str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    return new RegExp(escaped);
  };
  const convertTypeIntoString = (value) => {
    let output;
    switch (value) {
      case undefined:
        output = "undefined";
        break;
      case null:
        output = "null";
        break;
      default:
        output = value.toString();
    }
    return output;
  };
  const convertTypeFromString = (value) => {
    const numValue = Number(value);
    let output;
    if (!Number.isNaN(numValue)) {
      output = numValue;
    } else {
      switch (value) {
        case "undefined":
          output = undefined;
          break;
        case "null":
          output = null;
          break;
        case "true":
          output = true;
          break;
        case "false":
          output = false;
          break;
        default:
          output = value;
      }
    }
    return output;
  };
  const SAFARI_USER_AGENT_REGEXP = /\sVersion\/(\d{2}\.\d)(.+\s|\s)(Safari)\//;
  const isSafariBrowser = SAFARI_USER_AGENT_REGEXP.test(navigator.userAgent);
  const isUserAgentSupported = (userAgent) => {
    if (userAgent.includes("MSIE") || userAgent.includes("Trident/")) {
      return false;
    }
    return true;
  };
  const isBrowserSupported = () => {
    return isUserAgentSupported(navigator.userAgent);
  };
  const CSS_PROPERTY = {
    BACKGROUND: "background",
    BACKGROUND_IMAGE: "background-image",
    CONTENT: "content",
    OPACITY: "opacity",
  };
  const REGEXP_ANY_SYMBOL = ".*";
  const REGEXP_WITH_FLAGS_REGEXP = /^\s*\/.*\/[gmisuy]*\s*$/;
  const removeContentQuotes = (str) => {
    return str.replace(/^(["'])([\s\S]*)\1$/, "$2");
  };
  const addUrlPropertyQuotes = (str) => {
    if (!str.includes('url("')) {
      const re = /url\((.*?)\)/g;
      return str.replace(re, 'url("$1")');
    }
    return str;
  };
  const addUrlQuotesTo = {
    regexpArg: (str) => {
      const re = /(\^)?url(\\)?\\\((\w|\[\w)/g;
      return str.replace(re, '$1url$2\\(\\"?$3');
    },
    noneRegexpArg: addUrlPropertyQuotes,
  };
  const escapeRegExp = (str) => {
    const specials = [
      ".",
      "+",
      "?",
      "$",
      "{",
      "}",
      "(",
      ")",
      "[",
      "]",
      "\\",
      "/",
    ];
    const specialsRegex = new RegExp(`[${specials.join("\\")}]`, "g");
    return str.replace(specialsRegex, "\\$&");
  };
  const convertStyleMatchValueToRegexp = (rawValue) => {
    let value;
    if (rawValue.startsWith(SLASH) && rawValue.endsWith(SLASH)) {
      value = addUrlQuotesTo.regexpArg(rawValue);
      value = value.slice(1, -1);
    } else {
      value = addUrlQuotesTo.noneRegexpArg(rawValue);
      value = value.replace(/\\([\\()[\]"])/g, "$1");
      value = escapeRegExp(value);
      value = replaceAll(value, ASTERISK, REGEXP_ANY_SYMBOL);
    }
    return new RegExp(value, "i");
  };
  const normalizePropertyValue = (propertyName, propertyValue) => {
    let normalized = "";
    switch (propertyName) {
      case CSS_PROPERTY.BACKGROUND:
      case CSS_PROPERTY.BACKGROUND_IMAGE:
        normalized = addUrlPropertyQuotes(propertyValue);
        break;
      case CSS_PROPERTY.CONTENT:
        normalized = removeContentQuotes(propertyValue);
        break;
      case CSS_PROPERTY.OPACITY:
        normalized = isSafariBrowser
          ? (Math.round(parseFloat(propertyValue) * 100) / 100).toString()
          : propertyValue;
        break;
      default:
        normalized = propertyValue;
    }
    return normalized;
  };
  const getComputedStylePropertyValue = (
    domElement,
    propertyName,
    regularPseudoElement
  ) => {
    const style = window.getComputedStyle(domElement, regularPseudoElement);
    const propertyValue = style.getPropertyValue(propertyName);
    return normalizePropertyValue(propertyName, propertyValue);
  };
  const getPseudoArgData = (pseudoArg, separator) => {
    const index = pseudoArg.indexOf(separator);
    let name;
    let value;
    if (index > -1) {
      name = pseudoArg.substring(0, index).trim();
      value = pseudoArg.substring(index + 1).trim();
    } else {
      name = pseudoArg;
    }
    return {
      name,
      value,
    };
  };
  const parseStyleMatchArg = (pseudoName, rawArg) => {
    const { name, value } = getPseudoArgData(rawArg, COMMA);
    let regularPseudoElement = name;
    let styleMatchArg = value;
    if (!Object.values(REGULAR_PSEUDO_ELEMENTS).includes(name)) {
      regularPseudoElement = null;
      styleMatchArg = rawArg;
    }
    if (!styleMatchArg) {
      throw new Error(
        `Required style property argument part is missing in :${pseudoName}() arg: '${rawArg}'`
      );
    }
    if (regularPseudoElement) {
      regularPseudoElement = `${COLON}${COLON}${regularPseudoElement}`;
    }
    return {
      regularPseudoElement,
      styleMatchArg,
    };
  };
  const isStyleMatched = (argsData) => {
    const { pseudoName, pseudoArg, domElement } = argsData;
    const { regularPseudoElement, styleMatchArg } = parseStyleMatchArg(
      pseudoName,
      pseudoArg
    );
    const { name: matchName, value: matchValue } = getPseudoArgData(
      styleMatchArg,
      COLON
    );
    if (!matchName || !matchValue) {
      throw new Error(
        `Required property name or value is missing in :${pseudoName}() arg: '${styleMatchArg}'`
      );
    }
    let valueRegexp;
    try {
      valueRegexp = convertStyleMatchValueToRegexp(matchValue);
    } catch (e) {
      logger.error(getErrorMessage(e));
      throw new Error(
        `Invalid argument of :${pseudoName}() pseudo-class: '${styleMatchArg}'`
      );
    }
    const value = getComputedStylePropertyValue(
      domElement,
      matchName,
      regularPseudoElement
    );
    return valueRegexp && valueRegexp.test(value);
  };
  const validateStrMatcherArg = (arg) => {
    if (arg.includes(SLASH)) {
      return false;
    }
    if (!/^[\w-]+$/.test(arg)) {
      return false;
    }
    return true;
  };
  const getValidMatcherArg = function (rawArg) {
    let isWildcardAllowed =
      arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
    let arg;
    if (
      rawArg.length > 1 &&
      rawArg.startsWith(DOUBLE_QUOTE) &&
      rawArg.endsWith(DOUBLE_QUOTE)
    ) {
      rawArg = rawArg.slice(1, -1);
    }
    if (rawArg === "") {
      throw new Error("Argument should be specified. Empty arg is invalid.");
    }
    if (rawArg.startsWith(SLASH) && rawArg.endsWith(SLASH)) {
      if (rawArg.length > 2) {
        arg = toRegExp(rawArg);
      } else {
        throw new Error(`Invalid regexp: '${rawArg}'`);
      }
    } else if (rawArg.includes(ASTERISK)) {
      if (rawArg === ASTERISK && !isWildcardAllowed) {
        throw new Error(`Argument should be more specific than ${rawArg}`);
      }
      arg = replaceAll(rawArg, ASTERISK, REGEXP_ANY_SYMBOL);
      arg = new RegExp(arg);
    } else {
      if (!validateStrMatcherArg(rawArg)) {
        throw new Error(`Invalid argument: '${rawArg}'`);
      }
      arg = rawArg;
    }
    return arg;
  };
  const getRawMatchingData = (pseudoName, pseudoArg) => {
    const { name: rawName, value: rawValue } = getPseudoArgData(
      pseudoArg,
      EQUAL_SIGN
    );
    if (!rawName) {
      throw new Error(
        `Required attribute name is missing in :${pseudoName} arg: ${pseudoArg}`
      );
    }
    return {
      rawName,
      rawValue,
    };
  };
  const isAttributeMatched = (argsData) => {
    const { pseudoName, pseudoArg, domElement } = argsData;
    const elementAttributes = domElement.attributes;
    if (elementAttributes.length === 0) {
      return false;
    }
    const { rawName: rawAttrName, rawValue: rawAttrValue } = getRawMatchingData(
      pseudoName,
      pseudoArg
    );
    let attrNameMatch;
    try {
      attrNameMatch = getValidMatcherArg(rawAttrName);
    } catch (e) {
      const errorMessage = getErrorMessage(e);
      logger.error(errorMessage);
      throw new SyntaxError(errorMessage);
    }
    let isMatched = false;
    let i = 0;
    while (i < elementAttributes.length && !isMatched) {
      const attr = elementAttributes[i];
      if (!attr) {
        break;
      }
      const isNameMatched =
        attrNameMatch instanceof RegExp
          ? attrNameMatch.test(attr.name)
          : attrNameMatch === attr.name;
      if (!rawAttrValue) {
        isMatched = isNameMatched;
      } else {
        let attrValueMatch;
        try {
          attrValueMatch = getValidMatcherArg(rawAttrValue);
        } catch (e) {
          const errorMessage = getErrorMessage(e);
          logger.error(errorMessage);
          throw new SyntaxError(errorMessage);
        }
        const isValueMatched =
          attrValueMatch instanceof RegExp
            ? attrValueMatch.test(attr.value)
            : attrValueMatch === attr.value;
        isMatched = isNameMatched && isValueMatched;
      }
      i += 1;
    }
    return isMatched;
  };
  const parseRawPropChain = (input) => {
    if (
      input.length > 1 &&
      input.startsWith(DOUBLE_QUOTE) &&
      input.endsWith(DOUBLE_QUOTE)
    ) {
      input = input.slice(1, -1);
    }
    const chainChunks = input.split(DOT);
    const chainPatterns = [];
    let patternBuffer = "";
    let isRegexpPattern = false;
    let i = 0;
    while (i < chainChunks.length) {
      const chunk = getItemByIndex(
        chainChunks,
        i,
        `Invalid pseudo-class arg: '${input}'`
      );
      if (
        chunk.startsWith(SLASH) &&
        chunk.endsWith(SLASH) &&
        chunk.length > 2
      ) {
        chainPatterns.push(chunk);
      } else if (chunk.startsWith(SLASH)) {
        isRegexpPattern = true;
        patternBuffer += chunk;
      } else if (chunk.endsWith(SLASH)) {
        isRegexpPattern = false;
        patternBuffer += `.${chunk}`;
        chainPatterns.push(patternBuffer);
        patternBuffer = "";
      } else {
        if (isRegexpPattern) {
          patternBuffer += chunk;
        } else {
          chainPatterns.push(chunk);
        }
      }
      i += 1;
    }
    if (patternBuffer.length > 0) {
      throw new Error(`Invalid regexp property pattern '${input}'`);
    }
    const chainMatchPatterns = chainPatterns.map((pattern) => {
      if (pattern.length === 0) {
        throw new Error(
          `Empty pattern '${pattern}' is invalid in chain '${input}'`
        );
      }
      let validPattern;
      try {
        validPattern = getValidMatcherArg(pattern, true);
      } catch (e) {
        logger.error(getErrorMessage(e));
        throw new Error(
          `Invalid property pattern '${pattern}' in property chain '${input}'`
        );
      }
      return validPattern;
    });
    return chainMatchPatterns;
  };
  const filterRootsByRegexpChain = function (base, chain) {
    let output =
      arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
    const tempProp = getFirst(chain);
    if (chain.length === 1) {
      let key;
      for (key in base) {
        if (tempProp instanceof RegExp) {
          if (tempProp.test(key)) {
            output.push({
              base,
              prop: key,
              value: base[key],
            });
          }
        } else if (tempProp === key) {
          output.push({
            base,
            prop: tempProp,
            value: base[key],
          });
        }
      }
      return output;
    }
    if (tempProp instanceof RegExp) {
      const nextProp = chain.slice(1);
      const baseKeys = [];
      for (const key in base) {
        if (tempProp.test(key)) {
          baseKeys.push(key);
        }
      }
      baseKeys.forEach((key) => {
        var _Object$getOwnPropert;
        const item =
          (_Object$getOwnPropert = Object.getOwnPropertyDescriptor(
            base,
            key
          )) === null || _Object$getOwnPropert === void 0
            ? void 0
            : _Object$getOwnPropert.value;
        filterRootsByRegexpChain(item, nextProp, output);
      });
    }
    if (base && typeof tempProp === "string") {
      var _Object$getOwnPropert2;
      const nextBase =
        (_Object$getOwnPropert2 = Object.getOwnPropertyDescriptor(
          base,
          tempProp
        )) === null || _Object$getOwnPropert2 === void 0
          ? void 0
          : _Object$getOwnPropert2.value;
      chain = chain.slice(1);
      if (nextBase !== undefined) {
        filterRootsByRegexpChain(nextBase, chain, output);
      }
    }
    return output;
  };
  const isPropertyMatched = (argsData) => {
    const { pseudoName, pseudoArg, domElement } = argsData;
    const { rawName: rawPropertyName, rawValue: rawPropertyValue } =
      getRawMatchingData(pseudoName, pseudoArg);
    if (rawPropertyName.includes("\\/") || rawPropertyName.includes("\\.")) {
      throw new Error(
        `Invalid :${pseudoName} name pattern: ${rawPropertyName}`
      );
    }
    let propChainMatches;
    try {
      propChainMatches = parseRawPropChain(rawPropertyName);
    } catch (e) {
      const errorMessage = getErrorMessage(e);
      logger.error(errorMessage);
      throw new SyntaxError(errorMessage);
    }
    const ownerObjArr = filterRootsByRegexpChain(domElement, propChainMatches);
    if (ownerObjArr.length === 0) {
      return false;
    }
    let isMatched = true;
    if (rawPropertyValue) {
      let propValueMatch;
      try {
        propValueMatch = getValidMatcherArg(rawPropertyValue);
      } catch (e) {
        const errorMessage = getErrorMessage(e);
        logger.error(errorMessage);
        throw new SyntaxError(errorMessage);
      }
      if (propValueMatch) {
        for (let i = 0; i < ownerObjArr.length; i += 1) {
          var _ownerObjArr$i;
          const realValue =
            (_ownerObjArr$i = ownerObjArr[i]) === null ||
            _ownerObjArr$i === void 0
              ? void 0
              : _ownerObjArr$i.value;
          if (propValueMatch instanceof RegExp) {
            isMatched = propValueMatch.test(convertTypeIntoString(realValue));
          } else {
            if (realValue === "null" || realValue === "undefined") {
              isMatched = propValueMatch === realValue;
              break;
            }
            isMatched = convertTypeFromString(propValueMatch) === realValue;
          }
          if (isMatched) {
            break;
          }
        }
      }
    }
    return isMatched;
  };
  const isTextMatched = (argsData) => {
    const { pseudoName, pseudoArg, domElement } = argsData;
    const textContent = getNodeTextContent(domElement);
    let isTextContentMatched;
    let pseudoArgToMatch = pseudoArg;
    if (
      pseudoArgToMatch.startsWith(SLASH) &&
      REGEXP_WITH_FLAGS_REGEXP.test(pseudoArgToMatch)
    ) {
      const flagsIndex = pseudoArgToMatch.lastIndexOf("/");
      const flagsStr = pseudoArgToMatch.substring(flagsIndex + 1);
      pseudoArgToMatch = pseudoArgToMatch
        .substring(0, flagsIndex + 1)
        .slice(1, -1)
        .replace(/\\([\\"])/g, "$1");
      let regex;
      try {
        regex = new RegExp(pseudoArgToMatch, flagsStr);
      } catch (e) {
        throw new Error(
          `Invalid argument of :${pseudoName}() pseudo-class: ${pseudoArg}`
        );
      }
      isTextContentMatched = regex.test(textContent);
    } else {
      pseudoArgToMatch = pseudoArgToMatch.replace(/\\([\\()[\]"])/g, "$1");
      isTextContentMatched = textContent.includes(pseudoArgToMatch);
    }
    return isTextContentMatched;
  };
  const getValidNumberAncestorArg = (rawArg, pseudoName) => {
    const deep = Number(rawArg);
    if (Number.isNaN(deep) || deep < 1 || deep >= 256) {
      throw new Error(
        `Invalid argument of :${pseudoName} pseudo-class: '${rawArg}'`
      );
    }
    return deep;
  };
  const getNthAncestor = (domElement, nth, pseudoName) => {
    let ancestor = null;
    let i = 0;
    while (i < nth) {
      ancestor = domElement.parentElement;
      if (!ancestor) {
        throw new Error(
          `Out of DOM: Argument of :${pseudoName}() pseudo-class is too big — '${nth}'.`
        );
      }
      domElement = ancestor;
      i += 1;
    }
    return ancestor;
  };
  const validateStandardSelector = (selector) => {
    let isValid;
    try {
      document.querySelectorAll(selector);
      isValid = true;
    } catch (e) {
      isValid = false;
    }
    return isValid;
  };
  const matcherWrapper = (callback, argsData, errorMessage) => {
    let isMatched;
    try {
      isMatched = callback(argsData);
    } catch (e) {
      logger.error(getErrorMessage(e));
      throw new Error(errorMessage);
    }
    return isMatched;
  };
  const getAbsolutePseudoError = (propDesc, pseudoName, pseudoArg) => {
    return `${MATCHING_ELEMENT_ERROR_PREFIX} ${propDesc}, may be invalid :${pseudoName}() pseudo-class arg: '${pseudoArg}'`;
  };
  const isMatchedByAbsolutePseudo = (domElement, pseudoName, pseudoArg) => {
    let argsData;
    let errorMessage;
    let callback;
    switch (pseudoName) {
      case CONTAINS_PSEUDO:
      case HAS_TEXT_PSEUDO:
      case ABP_CONTAINS_PSEUDO:
        callback = isTextMatched;
        argsData = {
          pseudoName,
          pseudoArg,
          domElement,
        };
        errorMessage = getAbsolutePseudoError(
          "text content",
          pseudoName,
          pseudoArg
        );
        break;
      case MATCHES_CSS_PSEUDO:
      case MATCHES_CSS_AFTER_PSEUDO:
      case MATCHES_CSS_BEFORE_PSEUDO:
        callback = isStyleMatched;
        argsData = {
          pseudoName,
          pseudoArg,
          domElement,
        };
        errorMessage = getAbsolutePseudoError("style", pseudoName, pseudoArg);
        break;
      case MATCHES_ATTR_PSEUDO_CLASS_MARKER:
        callback = isAttributeMatched;
        argsData = {
          domElement,
          pseudoName,
          pseudoArg,
        };
        errorMessage = getAbsolutePseudoError(
          "attributes",
          pseudoName,
          pseudoArg
        );
        break;
      case MATCHES_PROPERTY_PSEUDO_CLASS_MARKER:
        callback = isPropertyMatched;
        argsData = {
          domElement,
          pseudoName,
          pseudoArg,
        };
        errorMessage = getAbsolutePseudoError(
          "properties",
          pseudoName,
          pseudoArg
        );
        break;
      default:
        throw new Error(`Unknown absolute pseudo-class :${pseudoName}()`);
    }
    return matcherWrapper(callback, argsData, errorMessage);
  };
  const findByAbsolutePseudoPseudo = {
    nthAncestor: (domElements, rawPseudoArg, pseudoName) => {
      const deep = getValidNumberAncestorArg(rawPseudoArg, pseudoName);
      const ancestors = domElements
        .map((domElement) => {
          let ancestor = null;
          try {
            ancestor = getNthAncestor(domElement, deep, pseudoName);
          } catch (e) {
            logger.error(getErrorMessage(e));
          }
          return ancestor;
        })
        .filter(isHtmlElement);
      return ancestors;
    },
    xpath: (domElements, rawPseudoArg) => {
      const foundElements = domElements.map((domElement) => {
        const result = [];
        let xpathResult;
        try {
          xpathResult = document.evaluate(
            rawPseudoArg,
            domElement,
            null,
            window.XPathResult.UNORDERED_NODE_ITERATOR_TYPE,
            null
          );
        } catch (e) {
          logger.error(getErrorMessage(e));
          throw new Error(
            `Invalid argument of :xpath() pseudo-class: '${rawPseudoArg}'`
          );
        }
        let node = xpathResult.iterateNext();
        while (node) {
          if (isHtmlElement(node)) {
            result.push(node);
          }
          node = xpathResult.iterateNext();
        }
        return result;
      });
      return flatten(foundElements);
    },
    upward: (domElements, rawPseudoArg) => {
      if (!validateStandardSelector(rawPseudoArg)) {
        throw new Error(
          `Invalid argument of :upward pseudo-class: '${rawPseudoArg}'`
        );
      }
      const closestAncestors = domElements
        .map((domElement) => {
          const parent = domElement.parentElement;
          if (!parent) {
            return null;
          }
          return parent.closest(rawPseudoArg);
        })
        .filter(isHtmlElement);
      return closestAncestors;
    },
  };
  const scopeDirectChildren = `${SCOPE_CSS_PSEUDO_CLASS}${CHILD_COMBINATOR}`;
  const scopeAnyChildren = `${SCOPE_CSS_PSEUDO_CLASS}${DESCENDANT_COMBINATOR}`;
  const getFirstInnerRegularChild = (selectorNode, pseudoName) => {
    return getFirstRegularChild(
      selectorNode.children,
      `RegularSelector is missing for :${pseudoName}() pseudo-class`
    );
  };
  const hasRelativesBySelectorList = (argsData) => {
    const { element, relativeSelectorList, pseudoName } = argsData;
    return relativeSelectorList.children.every((selectorNode) => {
      const relativeRegularSelector = getFirstInnerRegularChild(
        selectorNode,
        pseudoName
      );
      let specifiedSelector = "";
      let rootElement = null;
      const regularSelector = getNodeValue(relativeRegularSelector);
      if (
        regularSelector.startsWith(NEXT_SIBLING_COMBINATOR) ||
        regularSelector.startsWith(SUBSEQUENT_SIBLING_COMBINATOR)
      ) {
        rootElement = element.parentElement;
        const elementSelectorText = getElementSelectorDesc(element);
        specifiedSelector = `${scopeDirectChildren}${elementSelectorText}${regularSelector}`;
      } else if (regularSelector === ASTERISK) {
        rootElement = element;
        specifiedSelector = `${scopeAnyChildren}${ASTERISK}`;
      } else {
        specifiedSelector = `${scopeAnyChildren}${regularSelector}`;
        rootElement = element;
      }
      if (!rootElement) {
        throw new Error(
          `Selection by :${pseudoName}() pseudo-class is not possible`
        );
      }
      let relativeElements;
      try {
        relativeElements = getElementsForSelectorNode(
          selectorNode,
          rootElement,
          specifiedSelector
        );
      } catch (e) {
        logger.error(getErrorMessage(e));
        throw new Error(
          `Invalid selector for :${pseudoName}() pseudo-class: '${regularSelector}'`
        );
      }
      return relativeElements.length > 0;
    });
  };
  const isAnyElementBySelectorList = (argsData) => {
    const { element, relativeSelectorList, pseudoName } = argsData;
    return relativeSelectorList.children.some((selectorNode) => {
      const relativeRegularSelector = getFirstInnerRegularChild(
        selectorNode,
        pseudoName
      );
      const rootElement = getParent(
        element,
        `Selection by :${pseudoName}() pseudo-class is not possible`
      );
      const specifiedSelector = `${scopeDirectChildren}${getNodeValue(
        relativeRegularSelector
      )}`;
      let anyElements;
      try {
        anyElements = getElementsForSelectorNode(
          selectorNode,
          rootElement,
          specifiedSelector
        );
      } catch (e) {
        return false;
      }
      return anyElements.includes(element);
    });
  };
  const notElementBySelectorList = (argsData) => {
    const { element, relativeSelectorList, pseudoName } = argsData;
    return relativeSelectorList.children.every((selectorNode) => {
      const relativeRegularSelector = getFirstInnerRegularChild(
        selectorNode,
        pseudoName
      );
      const rootElement = getParent(
        element,
        `Selection by :${pseudoName}() pseudo-class is not possible`
      );
      const specifiedSelector = `${scopeDirectChildren}${getNodeValue(
        relativeRegularSelector
      )}`;
      let anyElements;
      try {
        anyElements = getElementsForSelectorNode(
          selectorNode,
          rootElement,
          specifiedSelector
        );
      } catch (e) {
        logger.error(getErrorMessage(e));
        throw new Error(
          `Invalid selector for :${pseudoName}() pseudo-class: '${getNodeValue(
            relativeRegularSelector
          )}'`
        );
      }
      return !anyElements.includes(element);
    });
  };
  const getByRegularSelector = (
    regularSelectorNode,
    root,
    specifiedSelector
  ) => {
    const selectorText = specifiedSelector
      ? specifiedSelector
      : getNodeValue(regularSelectorNode);
    let selectedElements = [];
    try {
      selectedElements = Array.from(root.querySelectorAll(selectorText));
    } catch (e) {
      throw new Error(
        `Error: unable to select by '${selectorText}' — ${getErrorMessage(e)}`
      );
    }
    return selectedElements;
  };
  const getByExtendedSelector = (domElements, extendedSelectorNode) => {
    let foundElements = [];
    const extendedPseudoClassNode = getPseudoClassNode(extendedSelectorNode);
    const pseudoName = getNodeName(extendedPseudoClassNode);
    if (isAbsolutePseudoClass(pseudoName)) {
      const absolutePseudoArg = getNodeValue(
        extendedPseudoClassNode,
        `Missing arg for :${pseudoName}() pseudo-class`
      );
      if (pseudoName === NTH_ANCESTOR_PSEUDO_CLASS_MARKER) {
        foundElements = findByAbsolutePseudoPseudo.nthAncestor(
          domElements,
          absolutePseudoArg,
          pseudoName
        );
      } else if (pseudoName === XPATH_PSEUDO_CLASS_MARKER) {
        try {
          document.createExpression(absolutePseudoArg, null);
        } catch (e) {
          throw new Error(
            `Invalid argument of :${pseudoName}() pseudo-class: '${absolutePseudoArg}'`
          );
        }
        foundElements = findByAbsolutePseudoPseudo.xpath(
          domElements,
          absolutePseudoArg
        );
      } else if (pseudoName === UPWARD_PSEUDO_CLASS_MARKER) {
        if (Number.isNaN(Number(absolutePseudoArg))) {
          foundElements = findByAbsolutePseudoPseudo.upward(
            domElements,
            absolutePseudoArg
          );
        } else {
          foundElements = findByAbsolutePseudoPseudo.nthAncestor(
            domElements,
            absolutePseudoArg,
            pseudoName
          );
        }
      } else {
        foundElements = domElements.filter((element) => {
          return isMatchedByAbsolutePseudo(
            element,
            pseudoName,
            absolutePseudoArg
          );
        });
      }
    } else if (isRelativePseudoClass(pseudoName)) {
      const relativeSelectorList = getRelativeSelectorListNode(
        extendedPseudoClassNode
      );
      let relativePredicate;
      switch (pseudoName) {
        case HAS_PSEUDO_CLASS_MARKER:
        case ABP_HAS_PSEUDO_CLASS_MARKER:
          relativePredicate = (element) =>
            hasRelativesBySelectorList({
              element,
              relativeSelectorList,
              pseudoName,
            });
          break;
        case IS_PSEUDO_CLASS_MARKER:
          relativePredicate = (element) =>
            isAnyElementBySelectorList({
              element,
              relativeSelectorList,
              pseudoName,
            });
          break;
        case NOT_PSEUDO_CLASS_MARKER:
          relativePredicate = (element) =>
            notElementBySelectorList({
              element,
              relativeSelectorList,
              pseudoName,
            });
          break;
        default:
          throw new Error(`Unknown relative pseudo-class: '${pseudoName}'`);
      }
      foundElements = domElements.filter(relativePredicate);
    } else {
      throw new Error(`Unknown extended pseudo-class: '${pseudoName}'`);
    }
    return foundElements;
  };
  const getByFollowingRegularSelector = (domElements, regularSelectorNode) => {
    let foundElements = [];
    const value = getNodeValue(regularSelectorNode);
    if (value.startsWith(CHILD_COMBINATOR)) {
      foundElements = domElements.map((root) => {
        const specifiedSelector = `${SCOPE_CSS_PSEUDO_CLASS}${value}`;
        return getByRegularSelector(
          regularSelectorNode,
          root,
          specifiedSelector
        );
      });
    } else if (
      value.startsWith(NEXT_SIBLING_COMBINATOR) ||
      value.startsWith(SUBSEQUENT_SIBLING_COMBINATOR)
    ) {
      foundElements = domElements.map((element) => {
        const rootElement = element.parentElement;
        if (!rootElement) {
          return [];
        }
        const elementSelectorText = getElementSelectorDesc(element);
        const specifiedSelector = `${scopeDirectChildren}${elementSelectorText}${value}`;
        const selected = getByRegularSelector(
          regularSelectorNode,
          rootElement,
          specifiedSelector
        );
        return selected;
      });
    } else {
      foundElements = domElements.map((root) => {
        const specifiedSelector = `${scopeAnyChildren}${getNodeValue(
          regularSelectorNode
        )}`;
        return getByRegularSelector(
          regularSelectorNode,
          root,
          specifiedSelector
        );
      });
    }
    return flatten(foundElements);
  };
  const getElementsForSelectorNode = (
    selectorNode,
    root,
    specifiedSelector
  ) => {
    let selectedElements = [];
    let i = 0;
    while (i < selectorNode.children.length) {
      const selectorNodeChild = getItemByIndex(
        selectorNode.children,
        i,
        "selectorNodeChild should be specified"
      );
      if (i === 0) {
        selectedElements = getByRegularSelector(
          selectorNodeChild,
          root,
          specifiedSelector
        );
      } else if (isExtendedSelectorNode(selectorNodeChild)) {
        selectedElements = getByExtendedSelector(
          selectedElements,
          selectorNodeChild
        );
      } else if (isRegularSelectorNode(selectorNodeChild)) {
        selectedElements = getByFollowingRegularSelector(
          selectedElements,
          selectorNodeChild
        );
      }
      i += 1;
    }
    return selectedElements;
  };
  const selectElementsByAst = function (ast) {
    let doc =
      arguments.length > 1 && arguments[1] !== undefined
        ? arguments[1]
        : document;
    const selectedElements = [];
    ast.children.forEach((selectorNode) => {
      selectedElements.push(...getElementsForSelectorNode(selectorNode, doc));
    });
    const uniqueElements = [...new Set(flatten(selectedElements))];
    return uniqueElements;
  };
  class ExtCssDocument {
    constructor() {
      this.astCache = new Map();
    }
    saveAstToCache(selector, ast) {
      this.astCache.set(selector, ast);
    }
    getAstFromCache(selector) {
      const cachedAst = this.astCache.get(selector) || null;
      return cachedAst;
    }
    getSelectorAst(selector) {
      let ast = this.getAstFromCache(selector);
      if (!ast) {
        ast = parse(selector);
      }
      this.saveAstToCache(selector, ast);
      return ast;
    }
    querySelectorAll(selector) {
      const ast = this.getSelectorAst(selector);
      return selectElementsByAst(ast);
    }
  }
  const extCssDocument = new ExtCssDocument();
  const getObjectFromEntries = (entries) => {
    const object = {};
    entries.forEach((el) => {
      const [key, value] = el;
      object[key] = value;
    });
    return object;
  };
  const DEBUG_PSEUDO_PROPERTY_KEY = "debug";
  const parseRemoveSelector = (rawSelector) => {
    const VALID_REMOVE_MARKER = `${COLON}${REMOVE_PSEUDO_MARKER}${BRACKET.PARENTHESES.LEFT}${BRACKET.PARENTHESES.RIGHT}`;
    const INVALID_REMOVE_MARKER = `${COLON}${REMOVE_PSEUDO_MARKER}${BRACKET.PARENTHESES.LEFT}`;
    let selector;
    let shouldRemove = false;
    const firstIndex = rawSelector.indexOf(VALID_REMOVE_MARKER);
    if (firstIndex === 0) {
      throw new Error(
        `${REMOVE_ERROR_PREFIX.NO_TARGET_SELECTOR}: '${rawSelector}'`
      );
    } else if (firstIndex > 0) {
      if (firstIndex !== rawSelector.lastIndexOf(VALID_REMOVE_MARKER)) {
        throw new Error(
          `${REMOVE_ERROR_PREFIX.MULTIPLE_USAGE}: '${rawSelector}'`
        );
      } else if (firstIndex + VALID_REMOVE_MARKER.length < rawSelector.length) {
        throw new Error(
          `${REMOVE_ERROR_PREFIX.INVALID_POSITION}: '${rawSelector}'`
        );
      } else {
        selector = rawSelector.substring(0, firstIndex);
        shouldRemove = true;
      }
    } else if (rawSelector.includes(INVALID_REMOVE_MARKER)) {
      throw new Error(
        `${REMOVE_ERROR_PREFIX.INVALID_REMOVE}: '${rawSelector}'`
      );
    } else {
      selector = rawSelector;
    }
    const stylesOfSelector = shouldRemove
      ? [
          {
            property: REMOVE_PSEUDO_MARKER,
            value: PSEUDO_PROPERTY_POSITIVE_VALUE,
          },
        ]
      : [];
    return {
      selector,
      stylesOfSelector,
    };
  };
  const parseSelectorRulePart = (selectorBuffer, extCssDoc) => {
    let selector = selectorBuffer.trim();
    if (selector.startsWith(AT_RULE_MARKER)) {
      throw new Error(`${NO_AT_RULE_ERROR_PREFIX}: '${selector}'.`);
    }
    let removeSelectorData;
    try {
      removeSelectorData = parseRemoveSelector(selector);
    } catch (e) {
      logger.error(getErrorMessage(e));
      throw new Error(`${REMOVE_ERROR_PREFIX.INVALID_REMOVE}: '${selector}'`);
    }
    let stylesOfSelector = [];
    let success = false;
    let ast;
    try {
      selector = removeSelectorData.selector;
      stylesOfSelector = removeSelectorData.stylesOfSelector;
      ast = extCssDoc.getSelectorAst(selector);
      success = true;
    } catch (e) {
      success = false;
    }
    return {
      success,
      selector,
      ast,
      stylesOfSelector,
    };
  };
  const createRawResultsMap = () => {
    return new Map();
  };
  const saveToRawResults = (rawResults, rawRuleData) => {
    const { selector, ast, rawStyles } = rawRuleData;
    if (!rawStyles) {
      throw new Error(`No style declaration for selector: '${selector}'`);
    }
    if (!ast) {
      throw new Error(`No ast parsed for selector: '${selector}'`);
    }
    const storedRuleData = rawResults.get(selector);
    if (!storedRuleData) {
      rawResults.set(selector, {
        ast,
        styles: rawStyles,
      });
    } else {
      storedRuleData.styles.push(...rawStyles);
    }
  };
  const isRemoveSetInStyles = (styles) => {
    return styles.some((s) => {
      return (
        s.property === REMOVE_PSEUDO_MARKER &&
        s.value === PSEUDO_PROPERTY_POSITIVE_VALUE
      );
    });
  };
  const getDebugStyleValue = (styles) => {
    const debugStyle = styles.find((s) => {
      return s.property === DEBUG_PSEUDO_PROPERTY_KEY;
    });
    return debugStyle === null || debugStyle === void 0
      ? void 0
      : debugStyle.value;
  };
  const prepareRuleData = (rawRuleData) => {
    const { selector, ast, rawStyles } = rawRuleData;
    if (!ast) {
      throw new Error(`AST should be parsed for selector: '${selector}'`);
    }
    if (!rawStyles) {
      throw new Error(`Styles should be parsed for selector: '${selector}'`);
    }
    const ruleData = {
      selector,
      ast,
    };
    const debugValue = getDebugStyleValue(rawStyles);
    const shouldRemove = isRemoveSetInStyles(rawStyles);
    let styles = rawStyles;
    if (debugValue) {
      styles = rawStyles.filter(
        (s) => s.property !== DEBUG_PSEUDO_PROPERTY_KEY
      );
      if (
        debugValue === PSEUDO_PROPERTY_POSITIVE_VALUE ||
        debugValue === DEBUG_PSEUDO_PROPERTY_GLOBAL_VALUE
      ) {
        ruleData.debug = debugValue;
      }
    }
    if (shouldRemove) {
      ruleData.style = {
        [REMOVE_PSEUDO_MARKER]: PSEUDO_PROPERTY_POSITIVE_VALUE,
      };
      const contentStyle = styles.find(
        (s) => s.property === CONTENT_CSS_PROPERTY
      );
      if (contentStyle) {
        ruleData.style[CONTENT_CSS_PROPERTY] = contentStyle.value;
      }
    } else {
      if (styles.length > 0) {
        const stylesAsEntries = styles.map((style) => {
          const { property, value } = style;
          return [property, value];
        });
        const preparedStyleData = getObjectFromEntries(stylesAsEntries);
        ruleData.style = preparedStyleData;
      }
    }
    return ruleData;
  };
  const combineRulesData = (rawResults) => {
    const results = [];
    rawResults.forEach((value, key) => {
      const selector = key;
      const { ast, styles: rawStyles } = value;
      results.push(
        prepareRuleData({
          selector,
          ast,
          rawStyles,
        })
      );
    });
    return results;
  };
  const tokenizeStyleBlock = (rawStyle) => {
    const styleDeclaration = rawStyle.trim();
    return tokenize(styleDeclaration, SUPPORTED_STYLE_DECLARATION_MARKS);
  };
  const DECLARATION_PART = {
    PROPERTY: "property",
    VALUE: "value",
  };
  const isValueQuotesOpen = (context) => {
    return context.bufferValue !== "" && context.valueQuoteMark !== null;
  };
  const collectStyle = (context) => {
    context.styles.push({
      property: context.bufferProperty.trim(),
      value: context.bufferValue.trim(),
    });
    context.bufferProperty = "";
    context.bufferValue = "";
  };
  const processPropertyToken = (context, styleBlock, token) => {
    const { value: tokenValue } = token;
    switch (token.type) {
      case TOKEN_TYPE.WORD:
        if (context.bufferProperty.length > 0) {
          throw new Error(
            `Invalid style property in style block: '${styleBlock}'`
          );
        }
        context.bufferProperty += tokenValue;
        break;
      case TOKEN_TYPE.MARK:
        if (tokenValue === COLON) {
          if (context.bufferProperty.trim().length === 0) {
            throw new Error(
              `Missing style property before ':' in style block: '${styleBlock}'`
            );
          }
          context.bufferProperty = context.bufferProperty.trim();
          context.processing = DECLARATION_PART.VALUE;
        } else if (WHITE_SPACE_CHARACTERS.includes(tokenValue));
        else {
          throw new Error(
            `Invalid style declaration in style block: '${styleBlock}'`
          );
        }
        break;
      default:
        throw new Error(
          `Unsupported style property character: '${tokenValue}' in style block: '${styleBlock}'`
        );
    }
  };
  const processValueToken = (context, styleBlock, token) => {
    const { value: tokenValue } = token;
    if (token.type === TOKEN_TYPE.WORD) {
      context.bufferValue += tokenValue;
    } else {
      switch (tokenValue) {
        case COLON:
          if (!isValueQuotesOpen(context)) {
            throw new Error(
              `Invalid style value for property '${context.bufferProperty}' in style block: '${styleBlock}'`
            );
          }
          context.bufferValue += tokenValue;
          break;
        case SEMICOLON:
          if (isValueQuotesOpen(context)) {
            context.bufferValue += tokenValue;
          } else {
            collectStyle(context);
            context.processing = DECLARATION_PART.PROPERTY;
          }
          break;
        case SINGLE_QUOTE:
        case DOUBLE_QUOTE:
          if (context.valueQuoteMark === null) {
            context.valueQuoteMark = tokenValue;
          } else if (
            !context.bufferValue.endsWith(BACKSLASH) &&
            context.valueQuoteMark === tokenValue
          ) {
            context.valueQuoteMark = null;
          }
          context.bufferValue += tokenValue;
          break;
        case BACKSLASH:
          if (!isValueQuotesOpen(context)) {
            throw new Error(
              `Invalid style value for property '${context.bufferProperty}' in style block: '${styleBlock}'`
            );
          }
          context.bufferValue += tokenValue;
          break;
        case SPACE:
        case TAB:
        case CARRIAGE_RETURN:
        case LINE_FEED:
        case FORM_FEED:
          if (context.bufferValue.length > 0) {
            context.bufferValue += tokenValue;
          }
          break;
        default:
          throw new Error(`Unknown style declaration token: '${tokenValue}'`);
      }
    }
  };
  const parseStyleBlock = (rawStyleBlock) => {
    const styleBlock = rawStyleBlock.trim();
    const tokens = tokenizeStyleBlock(styleBlock);
    const context = {
      processing: DECLARATION_PART.PROPERTY,
      styles: [],
      bufferProperty: "",
      bufferValue: "",
      valueQuoteMark: null,
    };
    let i = 0;
    while (i < tokens.length) {
      const token = tokens[i];
      if (!token) {
        break;
      }
      if (context.processing === DECLARATION_PART.PROPERTY) {
        processPropertyToken(context, styleBlock, token);
      } else if (context.processing === DECLARATION_PART.VALUE) {
        processValueToken(context, styleBlock, token);
      } else {
        throw new Error("Style declaration parsing failed");
      }
      i += 1;
    }
    if (isValueQuotesOpen(context)) {
      throw new Error(
        `Unbalanced style declaration quotes in style block: '${styleBlock}'`
      );
    }
    if (context.bufferProperty.length > 0) {
      if (context.bufferValue.length === 0) {
        throw new Error(
          `Missing style value for property '${context.bufferProperty}' in style block '${styleBlock}'`
        );
      }
      collectStyle(context);
    }
    if (context.styles.length === 0) {
      throw new Error(STYLE_ERROR_PREFIX.NO_STYLE);
    }
    return context.styles;
  };
  const getLeftCurlyBracketIndexes = (cssRule) => {
    const indexes = [];
    for (let i = 0; i < cssRule.length; i += 1) {
      if (cssRule[i] === BRACKET.CURLY.LEFT) {
        indexes.push(i);
      }
    }
    return indexes;
  };
  const parseRule = (rawCssRule, extCssDoc) => {
    var _rawRuleData$selector;
    const cssRule = rawCssRule.trim();
    if (
      cssRule.includes(`${SLASH}${ASTERISK}`) &&
      cssRule.includes(`${ASTERISK}${SLASH}`)
    ) {
      throw new Error(STYLE_ERROR_PREFIX.NO_COMMENT);
    }
    const leftCurlyBracketIndexes = getLeftCurlyBracketIndexes(cssRule);
    if (getFirst(leftCurlyBracketIndexes) === 0) {
      throw new Error(NO_SELECTOR_ERROR_PREFIX);
    }
    let selectorData;
    if (
      leftCurlyBracketIndexes.length > 0 &&
      !cssRule.includes(BRACKET.CURLY.RIGHT)
    ) {
      throw new Error(
        `${STYLE_ERROR_PREFIX.NO_STYLE} OR ${STYLE_ERROR_PREFIX.UNCLOSED_STYLE}`
      );
    }
    if (
      leftCurlyBracketIndexes.length === 0 ||
      !cssRule.includes(BRACKET.CURLY.RIGHT)
    ) {
      try {
        selectorData = parseSelectorRulePart(cssRule, extCssDoc);
        if (selectorData.success) {
          var _selectorData$stylesO;
          if (
            ((_selectorData$stylesO = selectorData.stylesOfSelector) === null ||
            _selectorData$stylesO === void 0
              ? void 0
              : _selectorData$stylesO.length) === 0
          ) {
            throw new Error(STYLE_ERROR_PREFIX.NO_STYLE_OR_REMOVE);
          }
          return {
            selector: selectorData.selector.trim(),
            ast: selectorData.ast,
            rawStyles: selectorData.stylesOfSelector,
          };
        } else {
          throw new Error("Invalid selector");
        }
      } catch (e) {
        throw new Error(getErrorMessage(e));
      }
    }
    let selectorBuffer;
    let styleBlockBuffer;
    const rawRuleData = {
      selector: "",
    };
    for (let i = leftCurlyBracketIndexes.length - 1; i > -1; i -= 1) {
      const index = leftCurlyBracketIndexes[i];
      if (!index) {
        throw new Error(
          `Impossible to continue, no '{' to process for rule: '${cssRule}'`
        );
      }
      selectorBuffer = cssRule.slice(0, index);
      styleBlockBuffer = cssRule.slice(index + 1, cssRule.length - 1);
      selectorData = parseSelectorRulePart(selectorBuffer, extCssDoc);
      if (selectorData.success) {
        var _rawRuleData$rawStyle;
        rawRuleData.selector = selectorData.selector.trim();
        rawRuleData.ast = selectorData.ast;
        rawRuleData.rawStyles = selectorData.stylesOfSelector;
        const parsedStyles = parseStyleBlock(styleBlockBuffer);
        (_rawRuleData$rawStyle = rawRuleData.rawStyles) === null ||
        _rawRuleData$rawStyle === void 0
          ? void 0
          : _rawRuleData$rawStyle.push(...parsedStyles);
        break;
      } else {
        continue;
      }
    }
    if (
      ((_rawRuleData$selector = rawRuleData.selector) === null ||
      _rawRuleData$selector === void 0
        ? void 0
        : _rawRuleData$selector.length) === 0
    ) {
      throw new Error("Selector in not valid");
    }
    return rawRuleData;
  };
  const parseRules$1 = (rawCssRules, extCssDoc) => {
    const rawResults = createRawResultsMap();
    const warnings = [];
    const uniqueRules = [...new Set(rawCssRules.map((r) => r.trim()))];
    uniqueRules.forEach((rule) => {
      try {
        saveToRawResults(rawResults, parseRule(rule, extCssDoc));
      } catch (e) {
        const errorMessage = getErrorMessage(e);
        warnings.push(`'${rule}' - error: '${errorMessage}'`);
      }
    });
    if (warnings.length > 0) {
      logger.info(`Invalid rules:\n  ${warnings.join("\n  ")}`);
    }
    return combineRulesData(rawResults);
  };
  const REGEXP_DECLARATION_END = /[;}]/g;
  const REGEXP_DECLARATION_DIVIDER = /[;:}]/g;
  const REGEXP_NON_WHITESPACE = /\S/g;
  const restoreRuleAcc = (context) => {
    context.rawRuleData = {
      selector: "",
    };
  };
  const parseSelectorPart = (context, extCssDoc) => {
    let selector = context.selectorBuffer.trim();
    if (selector.startsWith(AT_RULE_MARKER)) {
      throw new Error(`${NO_AT_RULE_ERROR_PREFIX}: '${selector}'.`);
    }
    let removeSelectorData;
    try {
      removeSelectorData = parseRemoveSelector(selector);
    } catch (e) {
      logger.error(getErrorMessage(e));
      throw new Error(`${REMOVE_ERROR_PREFIX.INVALID_REMOVE}: '${selector}'`);
    }
    if (context.nextIndex === -1) {
      if (selector === removeSelectorData.selector) {
        throw new Error(
          `${STYLE_ERROR_PREFIX.NO_STYLE_OR_REMOVE}: '${context.cssToParse}'`
        );
      }
      context.cssToParse = "";
    }
    let stylesOfSelector = [];
    let success = false;
    let ast;
    try {
      selector = removeSelectorData.selector;
      stylesOfSelector = removeSelectorData.stylesOfSelector;
      ast = extCssDoc.getSelectorAst(selector);
      success = true;
    } catch (e) {
      success = false;
    }
    if (context.nextIndex > 0) {
      context.cssToParse = context.cssToParse.slice(context.nextIndex);
    }
    return {
      success,
      selector,
      ast,
      stylesOfSelector,
    };
  };
  const parseUntilClosingBracket = (context, styles) => {
    REGEXP_DECLARATION_DIVIDER.lastIndex = context.nextIndex;
    let match = REGEXP_DECLARATION_DIVIDER.exec(context.cssToParse);
    if (match === null) {
      throw new Error(
        `${STYLE_ERROR_PREFIX.INVALID_STYLE}: '${context.cssToParse}'`
      );
    }
    let matchPos = match.index;
    let matched = match[0];
    if (matched === BRACKET.CURLY.RIGHT) {
      const declarationChunk = context.cssToParse.slice(
        context.nextIndex,
        matchPos
      );
      if (declarationChunk.trim().length === 0) {
        if (styles.length === 0) {
          throw new Error(
            `${STYLE_ERROR_PREFIX.NO_STYLE}: '${context.cssToParse}'`
          );
        }
      } else {
        throw new Error(
          `${STYLE_ERROR_PREFIX.INVALID_STYLE}: '${context.cssToParse}'`
        );
      }
      return matchPos;
    }
    if (matched === COLON) {
      const colonIndex = matchPos;
      REGEXP_DECLARATION_END.lastIndex = colonIndex;
      match = REGEXP_DECLARATION_END.exec(context.cssToParse);
      if (match === null) {
        throw new Error(
          `${STYLE_ERROR_PREFIX.UNCLOSED_STYLE}: '${context.cssToParse}'`
        );
      }
      matchPos = match.index;
      matched = match[0];
      const property = context.cssToParse
        .slice(context.nextIndex, colonIndex)
        .trim();
      if (property.length === 0) {
        throw new Error(
          `${STYLE_ERROR_PREFIX.NO_PROPERTY}: '${context.cssToParse}'`
        );
      }
      const value = context.cssToParse.slice(colonIndex + 1, matchPos).trim();
      if (value.length === 0) {
        throw new Error(
          `${STYLE_ERROR_PREFIX.NO_VALUE}: '${context.cssToParse}'`
        );
      }
      styles.push({
        property,
        value,
      });
      if (matched === BRACKET.CURLY.RIGHT) {
        return matchPos;
      }
    }
    context.cssToParse = context.cssToParse.slice(matchPos + 1);
    context.nextIndex = 0;
    return parseUntilClosingBracket(context, styles);
  };
  const parseNextStyle = (context) => {
    const styles = [];
    const styleEndPos = parseUntilClosingBracket(context, styles);
    REGEXP_NON_WHITESPACE.lastIndex = styleEndPos + 1;
    const match = REGEXP_NON_WHITESPACE.exec(context.cssToParse);
    if (match === null) {
      context.cssToParse = "";
      return styles;
    }
    const matchPos = match.index;
    context.cssToParse = context.cssToParse.slice(matchPos);
    return styles;
  };
  const parseStylesheet = (rawStylesheet, extCssDoc) => {
    const stylesheet = rawStylesheet.trim();
    if (
      stylesheet.includes(`${SLASH}${ASTERISK}`) &&
      stylesheet.includes(`${ASTERISK}${SLASH}`)
    ) {
      throw new Error(
        `${STYLE_ERROR_PREFIX.NO_COMMENT} in stylesheet: '${stylesheet}'`
      );
    }
    const context = {
      isSelector: true,
      nextIndex: 0,
      cssToParse: stylesheet,
      selectorBuffer: "",
      rawRuleData: {
        selector: "",
      },
    };
    const rawResults = createRawResultsMap();
    let selectorData;
    while (context.cssToParse) {
      if (context.isSelector) {
        context.nextIndex = context.cssToParse.indexOf(BRACKET.CURLY.LEFT);
        if (context.selectorBuffer.length === 0 && context.nextIndex === 0) {
          throw new Error(
            `${STYLE_ERROR_PREFIX.NO_SELECTOR}: '${context.cssToParse}'`
          );
        }
        if (context.nextIndex === -1) {
          context.selectorBuffer = context.cssToParse;
        } else {
          context.selectorBuffer += context.cssToParse.slice(
            0,
            context.nextIndex
          );
        }
        selectorData = parseSelectorPart(context, extCssDoc);
        if (selectorData.success) {
          context.rawRuleData.selector = selectorData.selector.trim();
          context.rawRuleData.ast = selectorData.ast;
          context.rawRuleData.rawStyles = selectorData.stylesOfSelector;
          context.isSelector = false;
          if (context.nextIndex === -1) {
            saveToRawResults(rawResults, context.rawRuleData);
            restoreRuleAcc(context);
          } else {
            context.nextIndex = 1;
            context.selectorBuffer = "";
          }
        } else {
          context.selectorBuffer += BRACKET.CURLY.LEFT;
          context.cssToParse = context.cssToParse.slice(1);
        }
      } else {
        var _context$rawRuleData$;
        const parsedStyles = parseNextStyle(context);
        (_context$rawRuleData$ = context.rawRuleData.rawStyles) === null ||
        _context$rawRuleData$ === void 0
          ? void 0
          : _context$rawRuleData$.push(...parsedStyles);
        saveToRawResults(rawResults, context.rawRuleData);
        context.nextIndex = 0;
        restoreRuleAcc(context);
        context.isSelector = true;
      }
    }
    return combineRulesData(rawResults);
  };
  const isNumber = (arg) => {
    return typeof arg === "number" && !Number.isNaN(arg);
  };
  class ThrottleWrapper {
    constructor(callback) {
      this.callback = callback;
      this.executeCallback = this.executeCallback.bind(this);
    }
    executeCallback() {
      this.lastRunTime = performance.now();
      if (isNumber(this.timerId)) {
        clearTimeout(this.timerId);
        delete this.timerId;
      }
      this.callback();
    }
    run() {
      if (isNumber(this.timerId)) {
        return;
      }
      if (isNumber(this.lastRunTime)) {
        const elapsedTime = performance.now() - this.lastRunTime;
        if (elapsedTime < ThrottleWrapper.THROTTLE_DELAY_MS) {
          this.timerId = window.setTimeout(
            this.executeCallback,
            ThrottleWrapper.THROTTLE_DELAY_MS - elapsedTime
          );
          return;
        }
      }
      this.timerId = window.setTimeout(this.executeCallback);
    }
  }
  _defineProperty(ThrottleWrapper, "THROTTLE_DELAY_MS", 150);
  const LAST_EVENT_TIMEOUT_MS = 10;
  const IGNORED_EVENTS = ["mouseover", "mouseleave", "mouseenter", "mouseout"];
  const SUPPORTED_EVENTS = [
    "keydown",
    "keypress",
    "keyup",
    "auxclick",
    "click",
    "contextmenu",
    "dblclick",
    "mousedown",
    "mouseenter",
    "mouseleave",
    "mousemove",
    "mouseover",
    "mouseout",
    "mouseup",
    "pointerlockchange",
    "pointerlockerror",
    "select",
    "wheel",
  ];
  const SAFARI_PROBLEMATIC_EVENTS = ["wheel"];
  class EventTracker {
    constructor() {
      _defineProperty(this, "getLastEventType", () => this.lastEventType);
      _defineProperty(this, "getTimeSinceLastEvent", () => {
        if (!this.lastEventTime) {
          return null;
        }
        return Date.now() - this.lastEventTime;
      });
      this.trackedEvents = isSafariBrowser
        ? SUPPORTED_EVENTS.filter(
            (event) => !SAFARI_PROBLEMATIC_EVENTS.includes(event)
          )
        : SUPPORTED_EVENTS;
      this.trackedEvents.forEach((eventName) => {
        document.documentElement.addEventListener(
          eventName,
          this.trackEvent,
          true
        );
      });
    }
    trackEvent(event) {
      this.lastEventType = event.type;
      this.lastEventTime = Date.now();
    }
    isIgnoredEventType() {
      const lastEventType = this.getLastEventType();
      const sinceLastEventTime = this.getTimeSinceLastEvent();
      return (
        !!lastEventType &&
        IGNORED_EVENTS.includes(lastEventType) &&
        !!sinceLastEventTime &&
        sinceLastEventTime < LAST_EVENT_TIMEOUT_MS
      );
    }
    stopTracking() {
      this.trackedEvents.forEach((eventName) => {
        document.documentElement.removeEventListener(
          eventName,
          this.trackEvent,
          true
        );
      });
    }
  }
  function shouldIgnoreMutations(mutations) {
    return !mutations.some((m) => m.type !== "attributes");
  }
  function observeDocument(context) {
    if (context.isDomObserved) {
      return;
    }
    context.isDomObserved = true;
    context.domMutationObserver = new natives.MutationObserver((mutations) => {
      if (!mutations || mutations.length === 0) {
        return;
      }
      const eventTracker = new EventTracker();
      if (
        eventTracker.isIgnoredEventType() &&
        shouldIgnoreMutations(mutations)
      ) {
        return;
      }
      context.eventTracker = eventTracker;
      context.scheduler.run();
    });
    context.domMutationObserver.observe(document, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ["id", "class"],
    });
  }
  function disconnectDocument(context) {
    if (!context.isDomObserved) {
      return;
    }
    context.isDomObserved = false;
    if (context.domMutationObserver) {
      context.domMutationObserver.disconnect();
    }
    if (context.eventTracker) {
      context.eventTracker.stopTracking();
    }
  }
  const CONTENT_ATTR_PREFIX_REGEXP = /^("|')adguard.+?/;
  const removeElement = (context, affectedElement) => {
    const { node } = affectedElement;
    affectedElement.removed = true;
    const elementSelector = getElementSelectorPath(node);
    const elementRemovalsCounter =
      context.removalsStatistic[elementSelector] || 0;
    if (elementRemovalsCounter > MAX_STYLE_PROTECTION_COUNT) {
      logger.error(
        `ExtendedCss: infinite loop protection for selector: '${elementSelector}'`
      );
      return;
    }
    if (node.parentElement) {
      node.parentElement.removeChild(node);
      context.removalsStatistic[elementSelector] = elementRemovalsCounter + 1;
    }
  };
  const setStyleToElement = (node, style) => {
    if (!(node instanceof HTMLElement)) {
      return;
    }
    Object.keys(style).forEach((prop) => {
      if (typeof node.style.getPropertyValue(prop.toString()) !== "undefined") {
        let value = style[prop];
        if (!value) {
          return;
        }
        if (
          prop === CONTENT_CSS_PROPERTY &&
          value.match(CONTENT_ATTR_PREFIX_REGEXP)
        ) {
          return;
        }
        value = removeSuffix(value.trim(), "!important").trim();
        node.style.setProperty(prop, value, "important");
      }
    });
  };
  const isIAffectedElement = (affectedElement) => {
    return (
      "node" in affectedElement &&
      "rules" in affectedElement &&
      affectedElement.rules instanceof Array
    );
  };
  const isAffectedElement = (affectedElement) => {
    return (
      "node" in affectedElement &&
      "originalStyle" in affectedElement &&
      "rules" in affectedElement &&
      affectedElement.rules instanceof Array
    );
  };
  const applyStyle = (context, rawAffectedElement) => {
    if (rawAffectedElement.protectionObserver) {
      return;
    }
    let affectedElement;
    if (context.beforeStyleApplied) {
      if (!isIAffectedElement(rawAffectedElement)) {
        throw new Error(
          "Returned IAffectedElement should have 'node' and 'rules' properties"
        );
      }
      affectedElement = context.beforeStyleApplied(rawAffectedElement);
      if (!affectedElement) {
        throw new Error(
          "Callback 'beforeStyleApplied' should return IAffectedElement"
        );
      }
    } else {
      affectedElement = rawAffectedElement;
    }
    if (!isAffectedElement(affectedElement)) {
      throw new Error(
        "Returned IAffectedElement should have 'node' and 'rules' properties"
      );
    }
    const { node, rules } = affectedElement;
    for (let i = 0; i < rules.length; i += 1) {
      const rule = rules[i];
      const selector =
        rule === null || rule === void 0 ? void 0 : rule.selector;
      const style = rule === null || rule === void 0 ? void 0 : rule.style;
      const debug = rule === null || rule === void 0 ? void 0 : rule.debug;
      if (style) {
        if (style[REMOVE_PSEUDO_MARKER] === PSEUDO_PROPERTY_POSITIVE_VALUE) {
          removeElement(context, affectedElement);
          return;
        }
        setStyleToElement(node, style);
      } else if (!debug) {
        throw new Error(
          `No style declaration in rule for selector: '${selector}'`
        );
      }
    }
  };
  const revertStyle = (affectedElement) => {
    if (affectedElement.protectionObserver) {
      affectedElement.protectionObserver.disconnect();
    }
    affectedElement.node.style.cssText = affectedElement.originalStyle;
  };
  class ExtMutationObserver {
    constructor(protectionCallback) {
      this.styleProtectionCount = 0;
      this.observer = new natives.MutationObserver((mutations) => {
        if (!mutations.length) {
          return;
        }
        this.styleProtectionCount += 1;
        protectionCallback(mutations, this);
      });
    }
    observe(target, options) {
      if (this.styleProtectionCount < MAX_STYLE_PROTECTION_COUNT) {
        this.observer.observe(target, options);
      } else {
        logger.error("ExtendedCss: infinite loop protection for style");
      }
    }
    disconnect() {
      this.observer.disconnect();
    }
  }
  const PROTECTION_OBSERVER_OPTIONS = {
    attributes: true,
    attributeOldValue: true,
    attributeFilter: ["style"],
  };
  const createProtectionCallback = (styles) => {
    const protectionCallback = (mutations, extObserver) => {
      if (!mutations[0]) {
        return;
      }
      const { target } = mutations[0];
      extObserver.disconnect();
      styles.forEach((style) => {
        setStyleToElement(target, style);
      });
      extObserver.observe(target, PROTECTION_OBSERVER_OPTIONS);
    };
    return protectionCallback;
  };
  const protectStyleAttribute = (node, rules) => {
    if (!natives.MutationObserver) {
      return null;
    }
    const styles = [];
    rules.forEach((ruleData) => {
      const { style } = ruleData;
      if (style) {
        styles.push(style);
      }
    });
    const protectionObserver = new ExtMutationObserver(
      createProtectionCallback(styles)
    );
    protectionObserver.observe(node, PROTECTION_OBSERVER_OPTIONS);
    return protectionObserver;
  };
  const STATS_DECIMAL_DIGITS_COUNT = 4;
  class TimingStats {
    constructor() {
      this.appliesTimings = [];
      this.appliesCount = 0;
      this.timingsSum = 0;
      this.meanTiming = 0;
      this.squaredSum = 0;
      this.standardDeviation = 0;
    }
    push(elapsedTimeMs) {
      this.appliesTimings.push(elapsedTimeMs);
      this.appliesCount += 1;
      this.timingsSum += elapsedTimeMs;
      this.meanTiming = this.timingsSum / this.appliesCount;
      this.squaredSum += elapsedTimeMs * elapsedTimeMs;
      this.standardDeviation = Math.sqrt(
        this.squaredSum / this.appliesCount - Math.pow(this.meanTiming, 2)
      );
    }
  }
  const beautifyTimingNumber = (timestamp) => {
    return Number(timestamp.toFixed(STATS_DECIMAL_DIGITS_COUNT));
  };
  const beautifyTimings = (rawTimings) => {
    return {
      appliesTimings: rawTimings.appliesTimings.map((t) =>
        beautifyTimingNumber(t)
      ),
      appliesCount: beautifyTimingNumber(rawTimings.appliesCount),
      timingsSum: beautifyTimingNumber(rawTimings.timingsSum),
      meanTiming: beautifyTimingNumber(rawTimings.meanTiming),
      standardDeviation: beautifyTimingNumber(rawTimings.standardDeviation),
    };
  };
  const printTimingInfo = (context) => {
    if (context.areTimingsPrinted) {
      return;
    }
    context.areTimingsPrinted = true;
    const timingsLogData = {};
    context.parsedRules.forEach((ruleData) => {
      if (ruleData.timingStats) {
        const { selector, style, debug, matchedElements } = ruleData;
        if (!style && !debug) {
          throw new Error(
            `Rule should have style declaration for selector: '${selector}'`
          );
        }
        const selectorData = {
          selectorParsed: selector,
          timings: beautifyTimings(ruleData.timingStats),
        };
        if (
          style &&
          style[REMOVE_PSEUDO_MARKER] === PSEUDO_PROPERTY_POSITIVE_VALUE
        ) {
          selectorData.removed = true;
        } else {
          selectorData.styleApplied = style || null;
          selectorData.matchedElements = matchedElements;
        }
        timingsLogData[selector] = selectorData;
      }
    });
    if (Object.keys(timingsLogData).length === 0) {
      return;
    }
    logger.info(
      "[ExtendedCss] Timings in milliseconds for %o:\n%o",
      window.location.href,
      timingsLogData
    );
  };
  const findAffectedElement = (affElements, domNode) => {
    return affElements.find((affEl) => affEl.node === domNode);
  };
  const applyRule = (context, ruleData) => {
    const isDebuggingMode = !!ruleData.debug || context.debug;
    let startTime;
    if (isDebuggingMode) {
      startTime = performance.now();
    }
    const { ast } = ruleData;
    const nodes = [];
    try {
      nodes.push(...selectElementsByAst(ast));
    } catch (e) {
      if (context.debug) {
        logger.error(getErrorMessage(e));
      }
    }
    nodes.forEach((node) => {
      let affectedElement = findAffectedElement(context.affectedElements, node);
      if (affectedElement) {
        affectedElement.rules.push(ruleData);
        applyStyle(context, affectedElement);
      } else {
        const originalStyle = node.style.cssText;
        affectedElement = {
          node,
          rules: [ruleData],
          originalStyle,
          protectionObserver: null,
        };
        applyStyle(context, affectedElement);
        context.affectedElements.push(affectedElement);
      }
    });
    if (isDebuggingMode && startTime) {
      const elapsedTimeMs = performance.now() - startTime;
      if (!ruleData.timingStats) {
        ruleData.timingStats = new TimingStats();
      }
      ruleData.timingStats.push(elapsedTimeMs);
    }
    return nodes;
  };
  const applyRules = (context) => {
    const newSelectedElements = [];
    disconnectDocument(context);
    context.parsedRules.forEach((ruleData) => {
      const nodes = applyRule(context, ruleData);
      Array.prototype.push.apply(newSelectedElements, nodes);
      if (ruleData.debug) {
        ruleData.matchedElements = nodes;
      }
    });
    let affLength = context.affectedElements.length;
    while (affLength) {
      const affectedElement = context.affectedElements[affLength - 1];
      if (!affectedElement) {
        break;
      }
      if (!newSelectedElements.includes(affectedElement.node)) {
        revertStyle(affectedElement);
        context.affectedElements.splice(affLength - 1, 1);
      } else if (!affectedElement.removed) {
        if (!affectedElement.protectionObserver) {
          affectedElement.protectionObserver = protectStyleAttribute(
            affectedElement.node,
            affectedElement.rules
          );
        }
      }
      affLength -= 1;
    }
    observeDocument(context);
    printTimingInfo(context);
  };
  class ExtendedCss {
    constructor(configuration) {
      if (!configuration) {
        throw new Error("ExtendedCss configuration should be provided.");
      }
      this.applyRulesCallbackListener =
        this.applyRulesCallbackListener.bind(this);
      this.context = {
        beforeStyleApplied: configuration.beforeStyleApplied,
        debug: false,
        affectedElements: [],
        isDomObserved: false,
        removalsStatistic: {},
        parsedRules: [],
        scheduler: new ThrottleWrapper(this.applyRulesCallbackListener),
      };
      if (!isBrowserSupported()) {
        logger.error("Browser is not supported by ExtendedCss");
        return;
      }
      if (!configuration.styleSheet && !configuration.cssRules) {
        throw new Error(
          "ExtendedCss configuration should have 'styleSheet' or 'cssRules' defined."
        );
      }
      if (configuration.styleSheet) {
        try {
          this.context.parsedRules.push(
            ...parseStylesheet(configuration.styleSheet, extCssDocument)
          );
        } catch (e) {
          throw new Error(
            `Pass the rules as configuration.cssRules since configuration.styleSheet cannot be parsed because of: '${getErrorMessage(
              e
            )}'`
          );
        }
      }
      if (configuration.cssRules) {
        this.context.parsedRules.push(
          ...parseRules$1(configuration.cssRules, extCssDocument)
        );
      }
      this.context.debug =
        configuration.debug ||
        this.context.parsedRules.some((ruleData) => {
          return ruleData.debug === DEBUG_PSEUDO_PROPERTY_GLOBAL_VALUE;
        });
      if (
        this.context.beforeStyleApplied &&
        typeof this.context.beforeStyleApplied !== "function"
      ) {
        throw new Error(
          `Invalid configuration. Type of 'beforeStyleApplied' should be a function, received: '${typeof this
            .context.beforeStyleApplied}'`
        );
      }
    }
    applyRulesCallbackListener() {
      applyRules(this.context);
    }
    init() {
      nativeTextContent.setGetter();
    }
    apply() {
      applyRules(this.context);
      if (document.readyState !== "complete") {
        document.addEventListener(
          "DOMContentLoaded",
          this.applyRulesCallbackListener,
          false
        );
      }
    }
    dispose() {
      disconnectDocument(this.context);
      this.context.affectedElements.forEach((el) => {
        revertStyle(el);
      });
      document.removeEventListener(
        "DOMContentLoaded",
        this.applyRulesCallbackListener,
        false
      );
    }
    getAffectedElements() {
      return this.context.affectedElements;
    }
    static query(selector) {
      let noTiming =
        arguments.length > 1 && arguments[1] !== undefined
          ? arguments[1]
          : true;
      if (typeof selector !== "string") {
        throw new Error("Selector should be defined as a string.");
      }
      const start = performance.now();
      try {
        return extCssDocument.querySelectorAll(selector);
      } finally {
        const end = performance.now();
        if (!noTiming) {
          logger.info(
            `[ExtendedCss] Elapsed: ${Math.round((end - start) * 1000)} μs.`
          );
        }
      }
    }
    static validate(inputSelector) {
      try {
        const { selector } = parseRemoveSelector(inputSelector);
        ExtendedCss.query(selector);
        return {
          ok: true,
          error: null,
        };
      } catch (e) {
        const error = `Error: Invalid selector: '${inputSelector}' -- ${getErrorMessage(
          e
        )}`;
        return {
          ok: false,
          error,
        };
      }
    }
  }

  function canApplyCss(type) {
    return (
      (data.appliedLevel & (type >= 2 ? 2 : 1)) == 0 &&
      data[styleBoxes[type]].length > 0
    );
  }

  function getCustomRules(saveHash) {
    return __awaiter(this, void 0, void 0, function* () {
      return yield Promise.resolve(String(saveHash));
    });
  }
  function initRules(apply) {
    let abpRules = {};
    data.receivedRules = "";
    getCustomRules(true);
    Object.keys(abpRules).forEach((name) => {
      data.receivedRules += "\n" + abpRules[name];
    });
    data.allRules = data.customRules + data.receivedRules;
    if (apply) splitRules();
    return data.receivedRules.length;
  }
  function styleApplyExec(type) {
    if (canApplyCss(type)) {
      const csss = data[styleBoxes[type]];
      new ExtendedCss({
        styleSheet: csss.replaceAll(/\/\*\s*\d.+?\s*\*\//g, ""),
      }).apply();
      if (!(type % 2 == 1)) addStyle(csss);
    }
  }
  function styleApply() {
    for (let type = 0; type < 4; type++) styleApplyExec(type);
  }
  function parseRules() {
    function addRule(rule, exten) {
      const [full, selector] = ruleToCss(rule, data.preset);
      const index = exten + (rule.generic ? 0 : 2);
      const checkResult = ExtendedCss.validate(selector);
      if (checkResult.ok) {
        data[styleBoxes[index]] += full;
        data.appliedCount++;
      } else {
        console.error("选择器检查错误:", rule, checkResult.error);
      }
    }
    styleBoxes.forEach((box) => {
      data[box] = "";
    });
    [data.styles, data.extStyles, data.selectors, data.extSelectors].forEach(
      (r, t) => {
        const sels = new Set();
        r.white.forEach((obj) => !sels.has(obj.sel) && sels.add(obj.sel));
        r.black
          .filter((obj) => !sels.has(obj.sel) && sels.add(obj.sel))
          .forEach((s) => addRule(s, t % 2));
      }
    );
    if (!data.saved) styleApply();
  }
  function splitRules() {
    dataBoxes.forEach((box) => {
      data[box] = makeRuleBox();
    });
    data.allRules.split("\n").forEach((rule) => {
      {
        const ruleObj = ruleLoader(rule);
        if (typeof ruleObj !== "undefined") {
          if (
            ruleObj.black === "black" &&
            data[dataBoxes[ruleObj.type]].white.includes(ruleObj)
          )
            return;
          data[dataBoxes[ruleObj.type]][ruleObj.black].push(ruleObj);
        }
      }
    });
    parseRules();
  }

  function main() {
    return __awaiter(this, void 0, void 0, function* () {
      yield getCustomRules(false);
      {
        if (initRules(false) === 0) {
          initRules(true);
        }
        splitRules();
      }
    });
  }
  function runOnce(key, func) {
    if (key in cat.unsafeWindow) return Promise.reject();
    cat.unsafeWindow[key] = true;
    return func();
  }
  {
    runOnce(data.mutex, main);
  }
})({
  GM_info: typeof GM_info == "object" ? GM_info : {},
  unsafeWindow: typeof unsafeWindow == "object" ? unsafeWindow : window,
  GM_addStyle: typeof GM_addStyle == "function" ? GM_addStyle : undefined,
});