"use strict";
(() => {
  var __create = Object.create;
  var __defProp = Object.defineProperty;
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  var __getOwnPropNames = Object.getOwnPropertyNames;
  var __getProtoOf = Object.getPrototypeOf;
  var __hasOwnProp = Object.prototype.hasOwnProperty;
  var __commonJS = (cb, mod) => function __require() {
    return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
  };
  var __copyProps = (to, from, except, desc) => {
    if (from && typeof from === "object" || typeof from === "function") {
      for (let key of __getOwnPropNames(from))
        if (!__hasOwnProp.call(to, key) && key !== except)
          __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
    }
    return to;
  };
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
    // If the importer is in node compatibility mode or this is not an ESM
    // file that has been converted to a CommonJS file using a Babel-
    // compatible transform (i.e. "__esModule" has not been set), then set
    // "default" to the CommonJS "module.exports" for node compatibility.
    isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
    mod
  ));

  // node_modules/@mozilla/readability/Readability.js
  var require_Readability = __commonJS({
    "node_modules/@mozilla/readability/Readability.js"(exports, module) {
      function Readability2(doc, options) {
        if (options && options.documentElement) {
          doc = options;
          options = arguments[2];
        } else if (!doc || !doc.documentElement) {
          throw new Error(
            "First argument to Readability constructor should be a document object."
          );
        }
        options = options || {};
        this._doc = doc;
        this._docJSDOMParser = this._doc.firstChild.__JSDOMParser__;
        this._articleTitle = null;
        this._articleByline = null;
        this._articleDir = null;
        this._articleSiteName = null;
        this._attempts = [];
        this._metadata = {};
        this._debug = !!options.debug;
        this._maxElemsToParse = options.maxElemsToParse || this.DEFAULT_MAX_ELEMS_TO_PARSE;
        this._nbTopCandidates = options.nbTopCandidates || this.DEFAULT_N_TOP_CANDIDATES;
        this._charThreshold = options.charThreshold || this.DEFAULT_CHAR_THRESHOLD;
        this._classesToPreserve = this.CLASSES_TO_PRESERVE.concat(
          options.classesToPreserve || []
        );
        this._keepClasses = !!options.keepClasses;
        this._serializer = options.serializer || function(el) {
          return el.innerHTML;
        };
        this._disableJSONLD = !!options.disableJSONLD;
        this._allowedVideoRegex = options.allowedVideoRegex || this.REGEXPS.videos;
        this._linkDensityModifier = options.linkDensityModifier || 0;
        this._flags = this.FLAG_STRIP_UNLIKELYS | this.FLAG_WEIGHT_CLASSES | this.FLAG_CLEAN_CONDITIONALLY;
        if (this._debug) {
          let logNode = function(node) {
            if (node.nodeType == node.TEXT_NODE) {
              return `${node.nodeName} ("${node.textContent}")`;
            }
            let attrPairs = Array.from(node.attributes || [], function(attr) {
              return `${attr.name}="${attr.value}"`;
            }).join(" ");
            return `<${node.localName} ${attrPairs}>`;
          };
          this.log = function() {
            if (typeof console !== "undefined") {
              let args = Array.from(arguments, (arg) => {
                if (arg && arg.nodeType == this.ELEMENT_NODE) {
                  return logNode(arg);
                }
                return arg;
              });
              args.unshift("Reader: (Readability)");
              console.log(...args);
            } else if (typeof dump !== "undefined") {
              var msg = Array.prototype.map.call(arguments, function(x) {
                return x && x.nodeName ? logNode(x) : x;
              }).join(" ");
              dump("Reader: (Readability) " + msg + "\n");
            }
          };
        } else {
          this.log = function() {
          };
        }
      }
      Readability2.prototype = {
        FLAG_STRIP_UNLIKELYS: 1,
        FLAG_WEIGHT_CLASSES: 2,
        FLAG_CLEAN_CONDITIONALLY: 4,
        // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
        ELEMENT_NODE: 1,
        TEXT_NODE: 3,
        // Max number of nodes supported by this parser. Default: 0 (no limit)
        DEFAULT_MAX_ELEMS_TO_PARSE: 0,
        // The number of top candidates to consider when analysing how
        // tight the competition is among candidates.
        DEFAULT_N_TOP_CANDIDATES: 5,
        // Element tags to score by default.
        DEFAULT_TAGS_TO_SCORE: "section,h2,h3,h4,h5,h6,p,td,pre".toUpperCase().split(","),
        // The default number of chars an article must have in order to return a result
        DEFAULT_CHAR_THRESHOLD: 500,
        // All of the regular expressions in use within readability.
        // Defined up here so we don't instantiate them repeatedly in loops.
        REGEXPS: {
          // NOTE: These two regular expressions are duplicated in
          // Readability-readerable.js. Please keep both copies in sync.
          unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i,
          okMaybeItsACandidate: /and|article|body|column|content|main|shadow/i,
          positive: /article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story/i,
          negative: /-ad-|hidden|^hid$| hid$| hid |^hid |banner|combx|comment|com-|contact|footer|gdpr|masthead|media|meta|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|widget/i,
          extraneous: /print|archive|comment|discuss|e[\-]?mail|share|reply|all|login|sign|single|utility/i,
          byline: /byline|author|dateline|writtenby|p-author/i,
          replaceFonts: /<(\/?)font[^>]*>/gi,
          normalize: /\s{2,}/g,
          videos: /\/\/(www\.)?((dailymotion|youtube|youtube-nocookie|player\.vimeo|v\.qq)\.com|(archive|upload\.wikimedia)\.org|player\.twitch\.tv)/i,
          shareElements: /(\b|_)(share|sharedaddy)(\b|_)/i,
          nextLink: /(next|weiter|continue|>([^\|]|$)|»([^\|]|$))/i,
          prevLink: /(prev|earl|old|new|<|«)/i,
          tokenize: /\W+/g,
          whitespace: /^\s*$/,
          hasContent: /\S$/,
          hashUrl: /^#.+/,
          srcsetUrl: /(\S+)(\s+[\d.]+[xw])?(\s*(?:,|$))/g,
          b64DataUrl: /^data:\s*([^\s;,]+)\s*;\s*base64\s*,/i,
          // Commas as used in Latin, Sindhi, Chinese and various other scripts.
          // see: https://en.wikipedia.org/wiki/Comma#Comma_variants
          commas: /\u002C|\u060C|\uFE50|\uFE10|\uFE11|\u2E41|\u2E34|\u2E32|\uFF0C/g,
          // See: https://schema.org/Article
          jsonLdArticleTypes: /^Article|AdvertiserContentArticle|NewsArticle|AnalysisNewsArticle|AskPublicNewsArticle|BackgroundNewsArticle|OpinionNewsArticle|ReportageNewsArticle|ReviewNewsArticle|Report|SatiricalArticle|ScholarlyArticle|MedicalScholarlyArticle|SocialMediaPosting|BlogPosting|LiveBlogPosting|DiscussionForumPosting|TechArticle|APIReference$/,
          // used to see if a node's content matches words commonly used for ad blocks or loading indicators
          adWords: /^(ad(vertising|vertisement)?|pub(licité)?|werb(ung)?|广告|Реклама|Anuncio)$/iu,
          loadingWords: /^((loading|正在加载|Загрузка|chargement|cargando)(…|\.\.\.)?)$/iu
        },
        UNLIKELY_ROLES: [
          "menu",
          "menubar",
          "complementary",
          "navigation",
          "alert",
          "alertdialog",
          "dialog"
        ],
        DIV_TO_P_ELEMS: /* @__PURE__ */ new Set([
          "BLOCKQUOTE",
          "DL",
          "DIV",
          "IMG",
          "OL",
          "P",
          "PRE",
          "TABLE",
          "UL"
        ]),
        ALTER_TO_DIV_EXCEPTIONS: ["DIV", "ARTICLE", "SECTION", "P", "OL", "UL"],
        PRESENTATIONAL_ATTRIBUTES: [
          "align",
          "background",
          "bgcolor",
          "border",
          "cellpadding",
          "cellspacing",
          "frame",
          "hspace",
          "rules",
          "style",
          "valign",
          "vspace"
        ],
        DEPRECATED_SIZE_ATTRIBUTE_ELEMS: ["TABLE", "TH", "TD", "HR", "PRE"],
        // The commented out elements qualify as phrasing content but tend to be
        // removed by readability when put into paragraphs, so we ignore them here.
        PHRASING_ELEMS: [
          // "CANVAS", "IFRAME", "SVG", "VIDEO",
          "ABBR",
          "AUDIO",
          "B",
          "BDO",
          "BR",
          "BUTTON",
          "CITE",
          "CODE",
          "DATA",
          "DATALIST",
          "DFN",
          "EM",
          "EMBED",
          "I",
          "IMG",
          "INPUT",
          "KBD",
          "LABEL",
          "MARK",
          "MATH",
          "METER",
          "NOSCRIPT",
          "OBJECT",
          "OUTPUT",
          "PROGRESS",
          "Q",
          "RUBY",
          "SAMP",
          "SCRIPT",
          "SELECT",
          "SMALL",
          "SPAN",
          "STRONG",
          "SUB",
          "SUP",
          "TEXTAREA",
          "TIME",
          "VAR",
          "WBR"
        ],
        // These are the classes that readability sets itself.
        CLASSES_TO_PRESERVE: ["page"],
        // These are the list of HTML entities that need to be escaped.
        HTML_ESCAPE_MAP: {
          lt: "<",
          gt: ">",
          amp: "&",
          quot: '"',
          apos: "'"
        },
        /**
         * Run any post-process modifications to article content as necessary.
         *
         * @param Element
         * @return void
         **/
        _postProcessContent(articleContent) {
          this._fixRelativeUris(articleContent);
          this._simplifyNestedElements(articleContent);
          if (!this._keepClasses) {
            this._cleanClasses(articleContent);
          }
        },
        /**
         * Iterates over a NodeList, calls `filterFn` for each node and removes node
         * if function returned `true`.
         *
         * If function is not passed, removes all the nodes in node list.
         *
         * @param NodeList nodeList The nodes to operate on
         * @param Function filterFn the function to use as a filter
         * @return void
         */
        _removeNodes(nodeList, filterFn) {
          if (this._docJSDOMParser && nodeList._isLiveNodeList) {
            throw new Error("Do not pass live node lists to _removeNodes");
          }
          for (var i = nodeList.length - 1; i >= 0; i--) {
            var node = nodeList[i];
            var parentNode = node.parentNode;
            if (parentNode) {
              if (!filterFn || filterFn.call(this, node, i, nodeList)) {
                parentNode.removeChild(node);
              }
            }
          }
        },
        /**
         * Iterates over a NodeList, and calls _setNodeTag for each node.
         *
         * @param NodeList nodeList The nodes to operate on
         * @param String newTagName the new tag name to use
         * @return void
         */
        _replaceNodeTags(nodeList, newTagName) {
          if (this._docJSDOMParser && nodeList._isLiveNodeList) {
            throw new Error("Do not pass live node lists to _replaceNodeTags");
          }
          for (const node of nodeList) {
            this._setNodeTag(node, newTagName);
          }
        },
        /**
         * Iterate over a NodeList, which doesn't natively fully implement the Array
         * interface.
         *
         * For convenience, the current object context is applied to the provided
         * iterate function.
         *
         * @param  NodeList nodeList The NodeList.
         * @param  Function fn       The iterate function.
         * @return void
         */
        _forEachNode(nodeList, fn) {
          Array.prototype.forEach.call(nodeList, fn, this);
        },
        /**
         * Iterate over a NodeList, and return the first node that passes
         * the supplied test function
         *
         * For convenience, the current object context is applied to the provided
         * test function.
         *
         * @param  NodeList nodeList The NodeList.
         * @param  Function fn       The test function.
         * @return void
         */
        _findNode(nodeList, fn) {
          return Array.prototype.find.call(nodeList, fn, this);
        },
        /**
         * Iterate over a NodeList, return true if any of the provided iterate
         * function calls returns true, false otherwise.
         *
         * For convenience, the current object context is applied to the
         * provided iterate function.
         *
         * @param  NodeList nodeList The NodeList.
         * @param  Function fn       The iterate function.
         * @return Boolean
         */
        _someNode(nodeList, fn) {
          return Array.prototype.some.call(nodeList, fn, this);
        },
        /**
         * Iterate over a NodeList, return true if all of the provided iterate
         * function calls return true, false otherwise.
         *
         * For convenience, the current object context is applied to the
         * provided iterate function.
         *
         * @param  NodeList nodeList The NodeList.
         * @param  Function fn       The iterate function.
         * @return Boolean
         */
        _everyNode(nodeList, fn) {
          return Array.prototype.every.call(nodeList, fn, this);
        },
        _getAllNodesWithTag(node, tagNames) {
          if (node.querySelectorAll) {
            return node.querySelectorAll(tagNames.join(","));
          }
          return [].concat.apply(
            [],
            tagNames.map(function(tag) {
              var collection = node.getElementsByTagName(tag);
              return Array.isArray(collection) ? collection : Array.from(collection);
            })
          );
        },
        /**
         * Removes the class="" attribute from every element in the given
         * subtree, except those that match CLASSES_TO_PRESERVE and
         * the classesToPreserve array from the options object.
         *
         * @param Element
         * @return void
         */
        _cleanClasses(node) {
          var classesToPreserve = this._classesToPreserve;
          var className = (node.getAttribute("class") || "").split(/\s+/).filter((cls) => classesToPreserve.includes(cls)).join(" ");
          if (className) {
            node.setAttribute("class", className);
          } else {
            node.removeAttribute("class");
          }
          for (node = node.firstElementChild; node; node = node.nextElementSibling) {
            this._cleanClasses(node);
          }
        },
        /**
         * Tests whether a string is a URL or not.
         *
         * @param {string} str The string to test
         * @return {boolean} true if str is a URL, false if not
         */
        _isUrl(str) {
          try {
            new URL(str);
            return true;
          } catch {
            return false;
          }
        },
        /**
         * Converts each <a> and <img> uri in the given element to an absolute URI,
         * ignoring #ref URIs.
         *
         * @param Element
         * @return void
         */
        _fixRelativeUris(articleContent) {
          var baseURI = this._doc.baseURI;
          var documentURI = this._doc.documentURI;
          function toAbsoluteURI(uri) {
            if (baseURI == documentURI && uri.charAt(0) == "#") {
              return uri;
            }
            try {
              return new URL(uri, baseURI).href;
            } catch (ex) {
            }
            return uri;
          }
          var links = this._getAllNodesWithTag(articleContent, ["a"]);
          this._forEachNode(links, function(link) {
            var href = link.getAttribute("href");
            if (href) {
              if (href.indexOf("javascript:") === 0) {
                if (link.childNodes.length === 1 && link.childNodes[0].nodeType === this.TEXT_NODE) {
                  var text = this._doc.createTextNode(link.textContent);
                  link.parentNode.replaceChild(text, link);
                } else {
                  var container = this._doc.createElement("span");
                  while (link.firstChild) {
                    container.appendChild(link.firstChild);
                  }
                  link.parentNode.replaceChild(container, link);
                }
              } else {
                link.setAttribute("href", toAbsoluteURI(href));
              }
            }
          });
          var medias = this._getAllNodesWithTag(articleContent, [
            "img",
            "picture",
            "figure",
            "video",
            "audio",
            "source"
          ]);
          this._forEachNode(medias, function(media) {
            var src = media.getAttribute("src");
            var poster = media.getAttribute("poster");
            var srcset = media.getAttribute("srcset");
            if (src) {
              media.setAttribute("src", toAbsoluteURI(src));
            }
            if (poster) {
              media.setAttribute("poster", toAbsoluteURI(poster));
            }
            if (srcset) {
              var newSrcset = srcset.replace(
                this.REGEXPS.srcsetUrl,
                function(_, p1, p2, p3) {
                  return toAbsoluteURI(p1) + (p2 || "") + p3;
                }
              );
              media.setAttribute("srcset", newSrcset);
            }
          });
        },
        _simplifyNestedElements(articleContent) {
          var node = articleContent;
          while (node) {
            if (node.parentNode && ["DIV", "SECTION"].includes(node.tagName) && !(node.id && node.id.startsWith("readability"))) {
              if (this._isElementWithoutContent(node)) {
                node = this._removeAndGetNext(node);
                continue;
              } else if (this._hasSingleTagInsideElement(node, "DIV") || this._hasSingleTagInsideElement(node, "SECTION")) {
                var child = node.children[0];
                for (var i = 0; i < node.attributes.length; i++) {
                  child.setAttributeNode(node.attributes[i].cloneNode());
                }
                node.parentNode.replaceChild(child, node);
                node = child;
                continue;
              }
            }
            node = this._getNextNode(node);
          }
        },
        /**
         * Get the article title as an H1.
         *
         * @return string
         **/
        _getArticleTitle() {
          var doc = this._doc;
          var curTitle = "";
          var origTitle = "";
          try {
            curTitle = origTitle = doc.title.trim();
            if (typeof curTitle !== "string") {
              curTitle = origTitle = this._getInnerText(
                doc.getElementsByTagName("title")[0]
              );
            }
          } catch (e) {
          }
          var titleHadHierarchicalSeparators = false;
          function wordCount(str) {
            return str.split(/\s+/).length;
          }
          if (/ [\|\-\\\/>»] /.test(curTitle)) {
            titleHadHierarchicalSeparators = / [\\\/>»] /.test(curTitle);
            let allSeparators = Array.from(origTitle.matchAll(/ [\|\-\\\/>»] /gi));
            curTitle = origTitle.substring(0, allSeparators.pop().index);
            if (wordCount(curTitle) < 3) {
              curTitle = origTitle.replace(/^[^\|\-\\\/>»]*[\|\-\\\/>»]/gi, "");
            }
          } else if (curTitle.includes(": ")) {
            var headings = this._getAllNodesWithTag(doc, ["h1", "h2"]);
            var trimmedTitle = curTitle.trim();
            var match = this._someNode(headings, function(heading) {
              return heading.textContent.trim() === trimmedTitle;
            });
            if (!match) {
              curTitle = origTitle.substring(origTitle.lastIndexOf(":") + 1);
              if (wordCount(curTitle) < 3) {
                curTitle = origTitle.substring(origTitle.indexOf(":") + 1);
              } else if (wordCount(origTitle.substr(0, origTitle.indexOf(":"))) > 5) {
                curTitle = origTitle;
              }
            }
          } else if (curTitle.length > 150 || curTitle.length < 15) {
            var hOnes = doc.getElementsByTagName("h1");
            if (hOnes.length === 1) {
              curTitle = this._getInnerText(hOnes[0]);
            }
          }
          curTitle = curTitle.trim().replace(this.REGEXPS.normalize, " ");
          var curTitleWordCount = wordCount(curTitle);
          if (curTitleWordCount <= 4 && (!titleHadHierarchicalSeparators || curTitleWordCount != wordCount(origTitle.replace(/[\|\-\\\/>»]+/g, "")) - 1)) {
            curTitle = origTitle;
          }
          return curTitle;
        },
        /**
         * Prepare the HTML document for readability to scrape it.
         * This includes things like stripping javascript, CSS, and handling terrible markup.
         *
         * @return void
         **/
        _prepDocument() {
          var doc = this._doc;
          this._removeNodes(this._getAllNodesWithTag(doc, ["style"]));
          if (doc.body) {
            this._replaceBrs(doc.body);
          }
          this._replaceNodeTags(this._getAllNodesWithTag(doc, ["font"]), "SPAN");
        },
        /**
         * Finds the next node, starting from the given node, and ignoring
         * whitespace in between. If the given node is an element, the same node is
         * returned.
         */
        _nextNode(node) {
          var next = node;
          while (next && next.nodeType != this.ELEMENT_NODE && this.REGEXPS.whitespace.test(next.textContent)) {
            next = next.nextSibling;
          }
          return next;
        },
        /**
         * Replaces 2 or more successive <br> elements with a single <p>.
         * Whitespace between <br> elements are ignored. For example:
         *   <div>foo<br>bar<br> <br><br>abc</div>
         * will become:
         *   <div>foo<br>bar<p>abc</p></div>
         */
        _replaceBrs(elem) {
          this._forEachNode(this._getAllNodesWithTag(elem, ["br"]), function(br) {
            var next = br.nextSibling;
            var replaced = false;
            while ((next = this._nextNode(next)) && next.tagName == "BR") {
              replaced = true;
              var brSibling = next.nextSibling;
              next.remove();
              next = brSibling;
            }
            if (replaced) {
              var p = this._doc.createElement("p");
              br.parentNode.replaceChild(p, br);
              next = p.nextSibling;
              while (next) {
                if (next.tagName == "BR") {
                  var nextElem = this._nextNode(next.nextSibling);
                  if (nextElem && nextElem.tagName == "BR") {
                    break;
                  }
                }
                if (!this._isPhrasingContent(next)) {
                  break;
                }
                var sibling = next.nextSibling;
                p.appendChild(next);
                next = sibling;
              }
              while (p.lastChild && this._isWhitespace(p.lastChild)) {
                p.lastChild.remove();
              }
              if (p.parentNode.tagName === "P") {
                this._setNodeTag(p.parentNode, "DIV");
              }
            }
          });
        },
        _setNodeTag(node, tag) {
          this.log("_setNodeTag", node, tag);
          if (this._docJSDOMParser) {
            node.localName = tag.toLowerCase();
            node.tagName = tag.toUpperCase();
            return node;
          }
          var replacement = node.ownerDocument.createElement(tag);
          while (node.firstChild) {
            replacement.appendChild(node.firstChild);
          }
          node.parentNode.replaceChild(replacement, node);
          if (node.readability) {
            replacement.readability = node.readability;
          }
          for (var i = 0; i < node.attributes.length; i++) {
            replacement.setAttributeNode(node.attributes[i].cloneNode());
          }
          return replacement;
        },
        /**
         * Prepare the article node for display. Clean out any inline styles,
         * iframes, forms, strip extraneous <p> tags, etc.
         *
         * @param Element
         * @return void
         **/
        _prepArticle(articleContent) {
          this._cleanStyles(articleContent);
          this._markDataTables(articleContent);
          this._fixLazyImages(articleContent);
          this._cleanConditionally(articleContent, "form");
          this._cleanConditionally(articleContent, "fieldset");
          this._clean(articleContent, "object");
          this._clean(articleContent, "embed");
          this._clean(articleContent, "footer");
          this._clean(articleContent, "link");
          this._clean(articleContent, "aside");
          var shareElementThreshold = this.DEFAULT_CHAR_THRESHOLD;
          this._forEachNode(articleContent.children, function(topCandidate) {
            this._cleanMatchedNodes(topCandidate, function(node, matchString) {
              return this.REGEXPS.shareElements.test(matchString) && node.textContent.length < shareElementThreshold;
            });
          });
          this._clean(articleContent, "iframe");
          this._clean(articleContent, "input");
          this._clean(articleContent, "textarea");
          this._clean(articleContent, "select");
          this._clean(articleContent, "button");
          this._cleanHeaders(articleContent);
          this._cleanConditionally(articleContent, "table");
          this._cleanConditionally(articleContent, "ul");
          this._cleanConditionally(articleContent, "div");
          this._replaceNodeTags(
            this._getAllNodesWithTag(articleContent, ["h1"]),
            "h2"
          );
          this._removeNodes(
            this._getAllNodesWithTag(articleContent, ["p"]),
            function(paragraph) {
              var contentElementCount = this._getAllNodesWithTag(paragraph, [
                "img",
                "embed",
                "object",
                "iframe"
              ]).length;
              return contentElementCount === 0 && !this._getInnerText(paragraph, false);
            }
          );
          this._forEachNode(
            this._getAllNodesWithTag(articleContent, ["br"]),
            function(br) {
              var next = this._nextNode(br.nextSibling);
              if (next && next.tagName == "P") {
                br.remove();
              }
            }
          );
          this._forEachNode(
            this._getAllNodesWithTag(articleContent, ["table"]),
            function(table) {
              var tbody = this._hasSingleTagInsideElement(table, "TBODY") ? table.firstElementChild : table;
              if (this._hasSingleTagInsideElement(tbody, "TR")) {
                var row = tbody.firstElementChild;
                if (this._hasSingleTagInsideElement(row, "TD")) {
                  var cell = row.firstElementChild;
                  cell = this._setNodeTag(
                    cell,
                    this._everyNode(cell.childNodes, this._isPhrasingContent) ? "P" : "DIV"
                  );
                  table.parentNode.replaceChild(cell, table);
                }
              }
            }
          );
        },
        /**
         * Initialize a node with the readability object. Also checks the
         * className/id for special names to add to its score.
         *
         * @param Element
         * @return void
         **/
        _initializeNode(node) {
          node.readability = { contentScore: 0 };
          switch (node.tagName) {
            case "DIV":
              node.readability.contentScore += 5;
              break;
            case "PRE":
            case "TD":
            case "BLOCKQUOTE":
              node.readability.contentScore += 3;
              break;
            case "ADDRESS":
            case "OL":
            case "UL":
            case "DL":
            case "DD":
            case "DT":
            case "LI":
            case "FORM":
              node.readability.contentScore -= 3;
              break;
            case "H1":
            case "H2":
            case "H3":
            case "H4":
            case "H5":
            case "H6":
            case "TH":
              node.readability.contentScore -= 5;
              break;
          }
          node.readability.contentScore += this._getClassWeight(node);
        },
        _removeAndGetNext(node) {
          var nextNode = this._getNextNode(node, true);
          node.remove();
          return nextNode;
        },
        /**
         * Traverse the DOM from node to node, starting at the node passed in.
         * Pass true for the second parameter to indicate this node itself
         * (and its kids) are going away, and we want the next node over.
         *
         * Calling this in a loop will traverse the DOM depth-first.
         *
         * @param {Element} node
         * @param {boolean} ignoreSelfAndKids
         * @return {Element}
         */
        _getNextNode(node, ignoreSelfAndKids) {
          if (!ignoreSelfAndKids && node.firstElementChild) {
            return node.firstElementChild;
          }
          if (node.nextElementSibling) {
            return node.nextElementSibling;
          }
          do {
            node = node.parentNode;
          } while (node && !node.nextElementSibling);
          return node && node.nextElementSibling;
        },
        // compares second text to first one
        // 1 = same text, 0 = completely different text
        // works the way that it splits both texts into words and then finds words that are unique in second text
        // the result is given by the lower length of unique parts
        _textSimilarity(textA, textB) {
          var tokensA = textA.toLowerCase().split(this.REGEXPS.tokenize).filter(Boolean);
          var tokensB = textB.toLowerCase().split(this.REGEXPS.tokenize).filter(Boolean);
          if (!tokensA.length || !tokensB.length) {
            return 0;
          }
          var uniqTokensB = tokensB.filter((token) => !tokensA.includes(token));
          var distanceB = uniqTokensB.join(" ").length / tokensB.join(" ").length;
          return 1 - distanceB;
        },
        /**
         * Checks whether an element node contains a valid byline
         *
         * @param node {Element}
         * @param matchString {string}
         * @return boolean
         */
        _isValidByline(node, matchString) {
          var rel = node.getAttribute("rel");
          var itemprop = node.getAttribute("itemprop");
          var bylineLength = node.textContent.trim().length;
          return (rel === "author" || itemprop && itemprop.includes("author") || this.REGEXPS.byline.test(matchString)) && !!bylineLength && bylineLength < 100;
        },
        _getNodeAncestors(node, maxDepth) {
          maxDepth = maxDepth || 0;
          var i = 0, ancestors = [];
          while (node.parentNode) {
            ancestors.push(node.parentNode);
            if (maxDepth && ++i === maxDepth) {
              break;
            }
            node = node.parentNode;
          }
          return ancestors;
        },
        /***
         * grabArticle - Using a variety of metrics (content score, classname, element types), find the content that is
         *         most likely to be the stuff a user wants to read. Then return it wrapped up in a div.
         *
         * @param page a document to run upon. Needs to be a full document, complete with body.
         * @return Element
         **/
        /* eslint-disable-next-line complexity */
        _grabArticle(page) {
          this.log("**** grabArticle ****");
          var doc = this._doc;
          var isPaging = page !== null;
          page = page ? page : this._doc.body;
          if (!page) {
            this.log("No body found in document. Abort.");
            return null;
          }
          var pageCacheHtml = page.innerHTML;
          while (true) {
            this.log("Starting grabArticle loop");
            var stripUnlikelyCandidates = this._flagIsActive(
              this.FLAG_STRIP_UNLIKELYS
            );
            var elementsToScore = [];
            var node = this._doc.documentElement;
            let shouldRemoveTitleHeader = true;
            while (node) {
              if (node.tagName === "HTML") {
                this._articleLang = node.getAttribute("lang");
              }
              var matchString = node.className + " " + node.id;
              if (!this._isProbablyVisible(node)) {
                this.log("Removing hidden node - " + matchString);
                node = this._removeAndGetNext(node);
                continue;
              }
              if (node.getAttribute("aria-modal") == "true" && node.getAttribute("role") == "dialog") {
                node = this._removeAndGetNext(node);
                continue;
              }
              if (!this._articleByline && !this._metadata.byline && this._isValidByline(node, matchString)) {
                var endOfSearchMarkerNode = this._getNextNode(node, true);
                var next = this._getNextNode(node);
                var itemPropNameNode = null;
                while (next && next != endOfSearchMarkerNode) {
                  var itemprop = next.getAttribute("itemprop");
                  if (itemprop && itemprop.includes("name")) {
                    itemPropNameNode = next;
                    break;
                  } else {
                    next = this._getNextNode(next);
                  }
                }
                this._articleByline = (itemPropNameNode ?? node).textContent.trim();
                node = this._removeAndGetNext(node);
                continue;
              }
              if (shouldRemoveTitleHeader && this._headerDuplicatesTitle(node)) {
                this.log(
                  "Removing header: ",
                  node.textContent.trim(),
                  this._articleTitle.trim()
                );
                shouldRemoveTitleHeader = false;
                node = this._removeAndGetNext(node);
                continue;
              }
              if (stripUnlikelyCandidates) {
                if (this.REGEXPS.unlikelyCandidates.test(matchString) && !this.REGEXPS.okMaybeItsACandidate.test(matchString) && !this._hasAncestorTag(node, "table") && !this._hasAncestorTag(node, "code") && node.tagName !== "BODY" && node.tagName !== "A") {
                  this.log("Removing unlikely candidate - " + matchString);
                  node = this._removeAndGetNext(node);
                  continue;
                }
                if (this.UNLIKELY_ROLES.includes(node.getAttribute("role"))) {
                  this.log(
                    "Removing content with role " + node.getAttribute("role") + " - " + matchString
                  );
                  node = this._removeAndGetNext(node);
                  continue;
                }
              }
              if ((node.tagName === "DIV" || node.tagName === "SECTION" || node.tagName === "HEADER" || node.tagName === "H1" || node.tagName === "H2" || node.tagName === "H3" || node.tagName === "H4" || node.tagName === "H5" || node.tagName === "H6") && this._isElementWithoutContent(node)) {
                node = this._removeAndGetNext(node);
                continue;
              }
              if (this.DEFAULT_TAGS_TO_SCORE.includes(node.tagName)) {
                elementsToScore.push(node);
              }
              if (node.tagName === "DIV") {
                var p = null;
                var childNode = node.firstChild;
                while (childNode) {
                  var nextSibling = childNode.nextSibling;
                  if (this._isPhrasingContent(childNode)) {
                    if (p !== null) {
                      p.appendChild(childNode);
                    } else if (!this._isWhitespace(childNode)) {
                      p = doc.createElement("p");
                      node.replaceChild(p, childNode);
                      p.appendChild(childNode);
                    }
                  } else if (p !== null) {
                    while (p.lastChild && this._isWhitespace(p.lastChild)) {
                      p.lastChild.remove();
                    }
                    p = null;
                  }
                  childNode = nextSibling;
                }
                if (this._hasSingleTagInsideElement(node, "P") && this._getLinkDensity(node) < 0.25) {
                  var newNode = node.children[0];
                  node.parentNode.replaceChild(newNode, node);
                  node = newNode;
                  elementsToScore.push(node);
                } else if (!this._hasChildBlockElement(node)) {
                  node = this._setNodeTag(node, "P");
                  elementsToScore.push(node);
                }
              }
              node = this._getNextNode(node);
            }
            var candidates = [];
            this._forEachNode(elementsToScore, function(elementToScore) {
              if (!elementToScore.parentNode || typeof elementToScore.parentNode.tagName === "undefined") {
                return;
              }
              var innerText = this._getInnerText(elementToScore);
              if (innerText.length < 25) {
                return;
              }
              var ancestors2 = this._getNodeAncestors(elementToScore, 5);
              if (ancestors2.length === 0) {
                return;
              }
              var contentScore = 0;
              contentScore += 1;
              contentScore += innerText.split(this.REGEXPS.commas).length;
              contentScore += Math.min(Math.floor(innerText.length / 100), 3);
              this._forEachNode(ancestors2, function(ancestor, level) {
                if (!ancestor.tagName || !ancestor.parentNode || typeof ancestor.parentNode.tagName === "undefined") {
                  return;
                }
                if (typeof ancestor.readability === "undefined") {
                  this._initializeNode(ancestor);
                  candidates.push(ancestor);
                }
                if (level === 0) {
                  var scoreDivider = 1;
                } else if (level === 1) {
                  scoreDivider = 2;
                } else {
                  scoreDivider = level * 3;
                }
                ancestor.readability.contentScore += contentScore / scoreDivider;
              });
            });
            var topCandidates = [];
            for (var c = 0, cl = candidates.length; c < cl; c += 1) {
              var candidate = candidates[c];
              var candidateScore = candidate.readability.contentScore * (1 - this._getLinkDensity(candidate));
              candidate.readability.contentScore = candidateScore;
              this.log("Candidate:", candidate, "with score " + candidateScore);
              for (var t = 0; t < this._nbTopCandidates; t++) {
                var aTopCandidate = topCandidates[t];
                if (!aTopCandidate || candidateScore > aTopCandidate.readability.contentScore) {
                  topCandidates.splice(t, 0, candidate);
                  if (topCandidates.length > this._nbTopCandidates) {
                    topCandidates.pop();
                  }
                  break;
                }
              }
            }
            var topCandidate = topCandidates[0] || null;
            var neededToCreateTopCandidate = false;
            var parentOfTopCandidate;
            if (topCandidate === null || topCandidate.tagName === "BODY") {
              topCandidate = doc.createElement("DIV");
              neededToCreateTopCandidate = true;
              while (page.firstChild) {
                this.log("Moving child out:", page.firstChild);
                topCandidate.appendChild(page.firstChild);
              }
              page.appendChild(topCandidate);
              this._initializeNode(topCandidate);
            } else if (topCandidate) {
              var alternativeCandidateAncestors = [];
              for (var i = 1; i < topCandidates.length; i++) {
                if (topCandidates[i].readability.contentScore / topCandidate.readability.contentScore >= 0.75) {
                  alternativeCandidateAncestors.push(
                    this._getNodeAncestors(topCandidates[i])
                  );
                }
              }
              var MINIMUM_TOPCANDIDATES = 3;
              if (alternativeCandidateAncestors.length >= MINIMUM_TOPCANDIDATES) {
                parentOfTopCandidate = topCandidate.parentNode;
                while (parentOfTopCandidate.tagName !== "BODY") {
                  var listsContainingThisAncestor = 0;
                  for (var ancestorIndex = 0; ancestorIndex < alternativeCandidateAncestors.length && listsContainingThisAncestor < MINIMUM_TOPCANDIDATES; ancestorIndex++) {
                    listsContainingThisAncestor += Number(
                      alternativeCandidateAncestors[ancestorIndex].includes(
                        parentOfTopCandidate
                      )
                    );
                  }
                  if (listsContainingThisAncestor >= MINIMUM_TOPCANDIDATES) {
                    topCandidate = parentOfTopCandidate;
                    break;
                  }
                  parentOfTopCandidate = parentOfTopCandidate.parentNode;
                }
              }
              if (!topCandidate.readability) {
                this._initializeNode(topCandidate);
              }
              parentOfTopCandidate = topCandidate.parentNode;
              var lastScore = topCandidate.readability.contentScore;
              var scoreThreshold = lastScore / 3;
              while (parentOfTopCandidate.tagName !== "BODY") {
                if (!parentOfTopCandidate.readability) {
                  parentOfTopCandidate = parentOfTopCandidate.parentNode;
                  continue;
                }
                var parentScore = parentOfTopCandidate.readability.contentScore;
                if (parentScore < scoreThreshold) {
                  break;
                }
                if (parentScore > lastScore) {
                  topCandidate = parentOfTopCandidate;
                  break;
                }
                lastScore = parentOfTopCandidate.readability.contentScore;
                parentOfTopCandidate = parentOfTopCandidate.parentNode;
              }
              parentOfTopCandidate = topCandidate.parentNode;
              while (parentOfTopCandidate.tagName != "BODY" && parentOfTopCandidate.children.length == 1) {
                topCandidate = parentOfTopCandidate;
                parentOfTopCandidate = topCandidate.parentNode;
              }
              if (!topCandidate.readability) {
                this._initializeNode(topCandidate);
              }
            }
            var articleContent = doc.createElement("DIV");
            if (isPaging) {
              articleContent.id = "readability-content";
            }
            var siblingScoreThreshold = Math.max(
              10,
              topCandidate.readability.contentScore * 0.2
            );
            parentOfTopCandidate = topCandidate.parentNode;
            var siblings = parentOfTopCandidate.children;
            for (var s = 0, sl = siblings.length; s < sl; s++) {
              var sibling = siblings[s];
              var append = false;
              this.log(
                "Looking at sibling node:",
                sibling,
                sibling.readability ? "with score " + sibling.readability.contentScore : ""
              );
              this.log(
                "Sibling has score",
                sibling.readability ? sibling.readability.contentScore : "Unknown"
              );
              if (sibling === topCandidate) {
                append = true;
              } else {
                var contentBonus = 0;
                if (sibling.className === topCandidate.className && topCandidate.className !== "") {
                  contentBonus += topCandidate.readability.contentScore * 0.2;
                }
                if (sibling.readability && sibling.readability.contentScore + contentBonus >= siblingScoreThreshold) {
                  append = true;
                } else if (sibling.nodeName === "P") {
                  var linkDensity = this._getLinkDensity(sibling);
                  var nodeContent = this._getInnerText(sibling);
                  var nodeLength = nodeContent.length;
                  if (nodeLength > 80 && linkDensity < 0.25) {
                    append = true;
                  } else if (nodeLength < 80 && nodeLength > 0 && linkDensity === 0 && nodeContent.search(/\.( |$)/) !== -1) {
                    append = true;
                  }
                }
              }
              if (append) {
                this.log("Appending node:", sibling);
                if (!this.ALTER_TO_DIV_EXCEPTIONS.includes(sibling.nodeName)) {
                  this.log("Altering sibling:", sibling, "to div.");
                  sibling = this._setNodeTag(sibling, "DIV");
                }
                articleContent.appendChild(sibling);
                siblings = parentOfTopCandidate.children;
                s -= 1;
                sl -= 1;
              }
            }
            if (this._debug) {
              this.log("Article content pre-prep: " + articleContent.innerHTML);
            }
            this._prepArticle(articleContent);
            if (this._debug) {
              this.log("Article content post-prep: " + articleContent.innerHTML);
            }
            if (neededToCreateTopCandidate) {
              topCandidate.id = "readability-page-1";
              topCandidate.className = "page";
            } else {
              var div = doc.createElement("DIV");
              div.id = "readability-page-1";
              div.className = "page";
              while (articleContent.firstChild) {
                div.appendChild(articleContent.firstChild);
              }
              articleContent.appendChild(div);
            }
            if (this._debug) {
              this.log("Article content after paging: " + articleContent.innerHTML);
            }
            var parseSuccessful = true;
            var textLength = this._getInnerText(articleContent, true).length;
            if (textLength < this._charThreshold) {
              parseSuccessful = false;
              page.innerHTML = pageCacheHtml;
              this._attempts.push({
                articleContent,
                textLength
              });
              if (this._flagIsActive(this.FLAG_STRIP_UNLIKELYS)) {
                this._removeFlag(this.FLAG_STRIP_UNLIKELYS);
              } else if (this._flagIsActive(this.FLAG_WEIGHT_CLASSES)) {
                this._removeFlag(this.FLAG_WEIGHT_CLASSES);
              } else if (this._flagIsActive(this.FLAG_CLEAN_CONDITIONALLY)) {
                this._removeFlag(this.FLAG_CLEAN_CONDITIONALLY);
              } else {
                this._attempts.sort(function(a, b) {
                  return b.textLength - a.textLength;
                });
                if (!this._attempts[0].textLength) {
                  return null;
                }
                articleContent = this._attempts[0].articleContent;
                parseSuccessful = true;
              }
            }
            if (parseSuccessful) {
              var ancestors = [parentOfTopCandidate, topCandidate].concat(
                this._getNodeAncestors(parentOfTopCandidate)
              );
              this._someNode(ancestors, function(ancestor) {
                if (!ancestor.tagName) {
                  return false;
                }
                var articleDir = ancestor.getAttribute("dir");
                if (articleDir) {
                  this._articleDir = articleDir;
                  return true;
                }
                return false;
              });
              return articleContent;
            }
          }
        },
        /**
         * Converts some of the common HTML entities in string to their corresponding characters.
         *
         * @param str {string} - a string to unescape.
         * @return string without HTML entity.
         */
        _unescapeHtmlEntities(str) {
          if (!str) {
            return str;
          }
          var htmlEscapeMap = this.HTML_ESCAPE_MAP;
          return str.replace(/&(quot|amp|apos|lt|gt);/g, function(_, tag) {
            return htmlEscapeMap[tag];
          }).replace(/&#(?:x([0-9a-f]+)|([0-9]+));/gi, function(_, hex, numStr) {
            var num = parseInt(hex || numStr, hex ? 16 : 10);
            if (num == 0 || num > 1114111 || num >= 55296 && num <= 57343) {
              num = 65533;
            }
            return String.fromCodePoint(num);
          });
        },
        /**
         * Try to extract metadata from JSON-LD object.
         * For now, only Schema.org objects of type Article or its subtypes are supported.
         * @return Object with any metadata that could be extracted (possibly none)
         */
        _getJSONLD(doc) {
          var scripts = this._getAllNodesWithTag(doc, ["script"]);
          var metadata;
          this._forEachNode(scripts, function(jsonLdElement) {
            if (!metadata && jsonLdElement.getAttribute("type") === "application/ld+json") {
              try {
                var content = jsonLdElement.textContent.replace(
                  /^\s*<!\[CDATA\[|\]\]>\s*$/g,
                  ""
                );
                var parsed = JSON.parse(content);
                if (Array.isArray(parsed)) {
                  parsed = parsed.find((it) => {
                    return it["@type"] && it["@type"].match(this.REGEXPS.jsonLdArticleTypes);
                  });
                  if (!parsed) {
                    return;
                  }
                }
                var schemaDotOrgRegex = /^https?\:\/\/schema\.org\/?$/;
                var matches = typeof parsed["@context"] === "string" && parsed["@context"].match(schemaDotOrgRegex) || typeof parsed["@context"] === "object" && typeof parsed["@context"]["@vocab"] == "string" && parsed["@context"]["@vocab"].match(schemaDotOrgRegex);
                if (!matches) {
                  return;
                }
                if (!parsed["@type"] && Array.isArray(parsed["@graph"])) {
                  parsed = parsed["@graph"].find((it) => {
                    return (it["@type"] || "").match(this.REGEXPS.jsonLdArticleTypes);
                  });
                }
                if (!parsed || !parsed["@type"] || !parsed["@type"].match(this.REGEXPS.jsonLdArticleTypes)) {
                  return;
                }
                metadata = {};
                if (typeof parsed.name === "string" && typeof parsed.headline === "string" && parsed.name !== parsed.headline) {
                  var title = this._getArticleTitle();
                  var nameMatches = this._textSimilarity(parsed.name, title) > 0.75;
                  var headlineMatches = this._textSimilarity(parsed.headline, title) > 0.75;
                  if (headlineMatches && !nameMatches) {
                    metadata.title = parsed.headline;
                  } else {
                    metadata.title = parsed.name;
                  }
                } else if (typeof parsed.name === "string") {
                  metadata.title = parsed.name.trim();
                } else if (typeof parsed.headline === "string") {
                  metadata.title = parsed.headline.trim();
                }
                if (parsed.author) {
                  if (typeof parsed.author.name === "string") {
                    metadata.byline = parsed.author.name.trim();
                  } else if (Array.isArray(parsed.author) && parsed.author[0] && typeof parsed.author[0].name === "string") {
                    metadata.byline = parsed.author.filter(function(author) {
                      return author && typeof author.name === "string";
                    }).map(function(author) {
                      return author.name.trim();
                    }).join(", ");
                  }
                }
                if (typeof parsed.description === "string") {
                  metadata.excerpt = parsed.description.trim();
                }
                if (parsed.publisher && typeof parsed.publisher.name === "string") {
                  metadata.siteName = parsed.publisher.name.trim();
                }
                if (typeof parsed.datePublished === "string") {
                  metadata.datePublished = parsed.datePublished.trim();
                }
              } catch (err) {
                this.log(err.message);
              }
            }
          });
          return metadata ? metadata : {};
        },
        /**
         * Attempts to get excerpt and byline metadata for the article.
         *
         * @param {Object} jsonld — object containing any metadata that
         * could be extracted from JSON-LD object.
         *
         * @return Object with optional "excerpt" and "byline" properties
         */
        _getArticleMetadata(jsonld) {
          var metadata = {};
          var values = {};
          var metaElements = this._doc.getElementsByTagName("meta");
          var propertyPattern = /\s*(article|dc|dcterm|og|twitter)\s*:\s*(author|creator|description|published_time|title|site_name)\s*/gi;
          var namePattern = /^\s*(?:(dc|dcterm|og|twitter|parsely|weibo:(article|webpage))\s*[-\.:]\s*)?(author|creator|pub-date|description|title|site_name)\s*$/i;
          this._forEachNode(metaElements, function(element) {
            var elementName = element.getAttribute("name");
            var elementProperty = element.getAttribute("property");
            var content = element.getAttribute("content");
            if (!content) {
              return;
            }
            var matches = null;
            var name = null;
            if (elementProperty) {
              matches = elementProperty.match(propertyPattern);
              if (matches) {
                name = matches[0].toLowerCase().replace(/\s/g, "");
                values[name] = content.trim();
              }
            }
            if (!matches && elementName && namePattern.test(elementName)) {
              name = elementName;
              if (content) {
                name = name.toLowerCase().replace(/\s/g, "").replace(/\./g, ":");
                values[name] = content.trim();
              }
            }
          });
          metadata.title = jsonld.title || values["dc:title"] || values["dcterm:title"] || values["og:title"] || values["weibo:article:title"] || values["weibo:webpage:title"] || values.title || values["twitter:title"] || values["parsely-title"];
          if (!metadata.title) {
            metadata.title = this._getArticleTitle();
          }
          const articleAuthor = typeof values["article:author"] === "string" && !this._isUrl(values["article:author"]) ? values["article:author"] : void 0;
          metadata.byline = jsonld.byline || values["dc:creator"] || values["dcterm:creator"] || values.author || values["parsely-author"] || articleAuthor;
          metadata.excerpt = jsonld.excerpt || values["dc:description"] || values["dcterm:description"] || values["og:description"] || values["weibo:article:description"] || values["weibo:webpage:description"] || values.description || values["twitter:description"];
          metadata.siteName = jsonld.siteName || values["og:site_name"];
          metadata.publishedTime = jsonld.datePublished || values["article:published_time"] || values["parsely-pub-date"] || null;
          metadata.title = this._unescapeHtmlEntities(metadata.title);
          metadata.byline = this._unescapeHtmlEntities(metadata.byline);
          metadata.excerpt = this._unescapeHtmlEntities(metadata.excerpt);
          metadata.siteName = this._unescapeHtmlEntities(metadata.siteName);
          metadata.publishedTime = this._unescapeHtmlEntities(metadata.publishedTime);
          return metadata;
        },
        /**
         * Check if node is image, or if node contains exactly only one image
         * whether as a direct child or as its descendants.
         *
         * @param Element
         **/
        _isSingleImage(node) {
          while (node) {
            if (node.tagName === "IMG") {
              return true;
            }
            if (node.children.length !== 1 || node.textContent.trim() !== "") {
              return false;
            }
            node = node.children[0];
          }
          return false;
        },
        /**
         * Find all <noscript> that are located after <img> nodes, and which contain only one
         * <img> element. Replace the first image with the image from inside the <noscript> tag,
         * and remove the <noscript> tag. This improves the quality of the images we use on
         * some sites (e.g. Medium).
         *
         * @param Element
         **/
        _unwrapNoscriptImages(doc) {
          var imgs = Array.from(doc.getElementsByTagName("img"));
          this._forEachNode(imgs, function(img) {
            for (var i = 0; i < img.attributes.length; i++) {
              var attr = img.attributes[i];
              switch (attr.name) {
                case "src":
                case "srcset":
                case "data-src":
                case "data-srcset":
                  return;
              }
              if (/\.(jpg|jpeg|png|webp)/i.test(attr.value)) {
                return;
              }
            }
            img.remove();
          });
          var noscripts = Array.from(doc.getElementsByTagName("noscript"));
          this._forEachNode(noscripts, function(noscript) {
            if (!this._isSingleImage(noscript)) {
              return;
            }
            var tmp = doc.createElement("div");
            tmp.innerHTML = noscript.innerHTML;
            var prevElement = noscript.previousElementSibling;
            if (prevElement && this._isSingleImage(prevElement)) {
              var prevImg = prevElement;
              if (prevImg.tagName !== "IMG") {
                prevImg = prevElement.getElementsByTagName("img")[0];
              }
              var newImg = tmp.getElementsByTagName("img")[0];
              for (var i = 0; i < prevImg.attributes.length; i++) {
                var attr = prevImg.attributes[i];
                if (attr.value === "") {
                  continue;
                }
                if (attr.name === "src" || attr.name === "srcset" || /\.(jpg|jpeg|png|webp)/i.test(attr.value)) {
                  if (newImg.getAttribute(attr.name) === attr.value) {
                    continue;
                  }
                  var attrName = attr.name;
                  if (newImg.hasAttribute(attrName)) {
                    attrName = "data-old-" + attrName;
                  }
                  newImg.setAttribute(attrName, attr.value);
                }
              }
              noscript.parentNode.replaceChild(tmp.firstElementChild, prevElement);
            }
          });
        },
        /**
         * Removes script tags from the document.
         *
         * @param Element
         **/
        _removeScripts(doc) {
          this._removeNodes(this._getAllNodesWithTag(doc, ["script", "noscript"]));
        },
        /**
         * Check if this node has only whitespace and a single element with given tag
         * Returns false if the DIV node contains non-empty text nodes
         * or if it contains no element with given tag or more than 1 element.
         *
         * @param Element
         * @param string tag of child element
         **/
        _hasSingleTagInsideElement(element, tag) {
          if (element.children.length != 1 || element.children[0].tagName !== tag) {
            return false;
          }
          return !this._someNode(element.childNodes, function(node) {
            return node.nodeType === this.TEXT_NODE && this.REGEXPS.hasContent.test(node.textContent);
          });
        },
        _isElementWithoutContent(node) {
          return node.nodeType === this.ELEMENT_NODE && !node.textContent.trim().length && (!node.children.length || node.children.length == node.getElementsByTagName("br").length + node.getElementsByTagName("hr").length);
        },
        /**
         * Determine whether element has any children block level elements.
         *
         * @param Element
         */
        _hasChildBlockElement(element) {
          return this._someNode(element.childNodes, function(node) {
            return this.DIV_TO_P_ELEMS.has(node.tagName) || this._hasChildBlockElement(node);
          });
        },
        /***
         * Determine if a node qualifies as phrasing content.
         * https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content
         **/
        _isPhrasingContent(node) {
          return node.nodeType === this.TEXT_NODE || this.PHRASING_ELEMS.includes(node.tagName) || (node.tagName === "A" || node.tagName === "DEL" || node.tagName === "INS") && this._everyNode(node.childNodes, this._isPhrasingContent);
        },
        _isWhitespace(node) {
          return node.nodeType === this.TEXT_NODE && node.textContent.trim().length === 0 || node.nodeType === this.ELEMENT_NODE && node.tagName === "BR";
        },
        /**
         * Get the inner text of a node - cross browser compatibly.
         * This also strips out any excess whitespace to be found.
         *
         * @param Element
         * @param Boolean normalizeSpaces (default: true)
         * @return string
         **/
        _getInnerText(e, normalizeSpaces) {
          normalizeSpaces = typeof normalizeSpaces === "undefined" ? true : normalizeSpaces;
          var textContent = e.textContent.trim();
          if (normalizeSpaces) {
            return textContent.replace(this.REGEXPS.normalize, " ");
          }
          return textContent;
        },
        /**
         * Get the number of times a string s appears in the node e.
         *
         * @param Element
         * @param string - what to split on. Default is ","
         * @return number (integer)
         **/
        _getCharCount(e, s) {
          s = s || ",";
          return this._getInnerText(e).split(s).length - 1;
        },
        /**
         * Remove the style attribute on every e and under.
         * TODO: Test if getElementsByTagName(*) is faster.
         *
         * @param Element
         * @return void
         **/
        _cleanStyles(e) {
          if (!e || e.tagName.toLowerCase() === "svg") {
            return;
          }
          for (var i = 0; i < this.PRESENTATIONAL_ATTRIBUTES.length; i++) {
            e.removeAttribute(this.PRESENTATIONAL_ATTRIBUTES[i]);
          }
          if (this.DEPRECATED_SIZE_ATTRIBUTE_ELEMS.includes(e.tagName)) {
            e.removeAttribute("width");
            e.removeAttribute("height");
          }
          var cur = e.firstElementChild;
          while (cur !== null) {
            this._cleanStyles(cur);
            cur = cur.nextElementSibling;
          }
        },
        /**
         * Get the density of links as a percentage of the content
         * This is the amount of text that is inside a link divided by the total text in the node.
         *
         * @param Element
         * @return number (float)
         **/
        _getLinkDensity(element) {
          var textLength = this._getInnerText(element).length;
          if (textLength === 0) {
            return 0;
          }
          var linkLength = 0;
          this._forEachNode(element.getElementsByTagName("a"), function(linkNode) {
            var href = linkNode.getAttribute("href");
            var coefficient = href && this.REGEXPS.hashUrl.test(href) ? 0.3 : 1;
            linkLength += this._getInnerText(linkNode).length * coefficient;
          });
          return linkLength / textLength;
        },
        /**
         * Get an elements class/id weight. Uses regular expressions to tell if this
         * element looks good or bad.
         *
         * @param Element
         * @return number (Integer)
         **/
        _getClassWeight(e) {
          if (!this._flagIsActive(this.FLAG_WEIGHT_CLASSES)) {
            return 0;
          }
          var weight = 0;
          if (typeof e.className === "string" && e.className !== "") {
            if (this.REGEXPS.negative.test(e.className)) {
              weight -= 25;
            }
            if (this.REGEXPS.positive.test(e.className)) {
              weight += 25;
            }
          }
          if (typeof e.id === "string" && e.id !== "") {
            if (this.REGEXPS.negative.test(e.id)) {
              weight -= 25;
            }
            if (this.REGEXPS.positive.test(e.id)) {
              weight += 25;
            }
          }
          return weight;
        },
        /**
         * Clean a node of all elements of type "tag".
         * (Unless it's a youtube/vimeo video. People love movies.)
         *
         * @param Element
         * @param string tag to clean
         * @return void
         **/
        _clean(e, tag) {
          var isEmbed = ["object", "embed", "iframe"].includes(tag);
          this._removeNodes(this._getAllNodesWithTag(e, [tag]), function(element) {
            if (isEmbed) {
              for (var i = 0; i < element.attributes.length; i++) {
                if (this._allowedVideoRegex.test(element.attributes[i].value)) {
                  return false;
                }
              }
              if (element.tagName === "object" && this._allowedVideoRegex.test(element.innerHTML)) {
                return false;
              }
            }
            return true;
          });
        },
        /**
         * Check if a given node has one of its ancestor tag name matching the
         * provided one.
         * @param  HTMLElement node
         * @param  String      tagName
         * @param  Number      maxDepth
         * @param  Function    filterFn a filter to invoke to determine whether this node 'counts'
         * @return Boolean
         */
        _hasAncestorTag(node, tagName, maxDepth, filterFn) {
          maxDepth = maxDepth || 3;
          tagName = tagName.toUpperCase();
          var depth = 0;
          while (node.parentNode) {
            if (maxDepth > 0 && depth > maxDepth) {
              return false;
            }
            if (node.parentNode.tagName === tagName && (!filterFn || filterFn(node.parentNode))) {
              return true;
            }
            node = node.parentNode;
            depth++;
          }
          return false;
        },
        /**
         * Return an object indicating how many rows and columns this table has.
         */
        _getRowAndColumnCount(table) {
          var rows = 0;
          var columns = 0;
          var trs = table.getElementsByTagName("tr");
          for (var i = 0; i < trs.length; i++) {
            var rowspan = trs[i].getAttribute("rowspan") || 0;
            if (rowspan) {
              rowspan = parseInt(rowspan, 10);
            }
            rows += rowspan || 1;
            var columnsInThisRow = 0;
            var cells = trs[i].getElementsByTagName("td");
            for (var j = 0; j < cells.length; j++) {
              var colspan = cells[j].getAttribute("colspan") || 0;
              if (colspan) {
                colspan = parseInt(colspan, 10);
              }
              columnsInThisRow += colspan || 1;
            }
            columns = Math.max(columns, columnsInThisRow);
          }
          return { rows, columns };
        },
        /**
         * Look for 'data' (as opposed to 'layout') tables, for which we use
         * similar checks as
         * https://searchfox.org/mozilla-central/rev/f82d5c549f046cb64ce5602bfd894b7ae807c8f8/accessible/generic/TableAccessible.cpp#19
         */
        _markDataTables(root) {
          var tables = root.getElementsByTagName("table");
          for (var i = 0; i < tables.length; i++) {
            var table = tables[i];
            var role = table.getAttribute("role");
            if (role == "presentation") {
              table._readabilityDataTable = false;
              continue;
            }
            var datatable = table.getAttribute("datatable");
            if (datatable == "0") {
              table._readabilityDataTable = false;
              continue;
            }
            var summary = table.getAttribute("summary");
            if (summary) {
              table._readabilityDataTable = true;
              continue;
            }
            var caption = table.getElementsByTagName("caption")[0];
            if (caption && caption.childNodes.length) {
              table._readabilityDataTable = true;
              continue;
            }
            var dataTableDescendants = ["col", "colgroup", "tfoot", "thead", "th"];
            var descendantExists = function(tag) {
              return !!table.getElementsByTagName(tag)[0];
            };
            if (dataTableDescendants.some(descendantExists)) {
              this.log("Data table because found data-y descendant");
              table._readabilityDataTable = true;
              continue;
            }
            if (table.getElementsByTagName("table")[0]) {
              table._readabilityDataTable = false;
              continue;
            }
            var sizeInfo = this._getRowAndColumnCount(table);
            if (sizeInfo.columns == 1 || sizeInfo.rows == 1) {
              table._readabilityDataTable = false;
              continue;
            }
            if (sizeInfo.rows >= 10 || sizeInfo.columns > 4) {
              table._readabilityDataTable = true;
              continue;
            }
            table._readabilityDataTable = sizeInfo.rows * sizeInfo.columns > 10;
          }
        },
        /* convert images and figures that have properties like data-src into images that can be loaded without JS */
        _fixLazyImages(root) {
          this._forEachNode(
            this._getAllNodesWithTag(root, ["img", "picture", "figure"]),
            function(elem) {
              if (elem.src && this.REGEXPS.b64DataUrl.test(elem.src)) {
                var parts = this.REGEXPS.b64DataUrl.exec(elem.src);
                if (parts[1] === "image/svg+xml") {
                  return;
                }
                var srcCouldBeRemoved = false;
                for (var i = 0; i < elem.attributes.length; i++) {
                  var attr = elem.attributes[i];
                  if (attr.name === "src") {
                    continue;
                  }
                  if (/\.(jpg|jpeg|png|webp)/i.test(attr.value)) {
                    srcCouldBeRemoved = true;
                    break;
                  }
                }
                if (srcCouldBeRemoved) {
                  var b64starts = parts[0].length;
                  var b64length = elem.src.length - b64starts;
                  if (b64length < 133) {
                    elem.removeAttribute("src");
                  }
                }
              }
              if ((elem.src || elem.srcset && elem.srcset != "null") && !elem.className.toLowerCase().includes("lazy")) {
                return;
              }
              for (var j = 0; j < elem.attributes.length; j++) {
                attr = elem.attributes[j];
                if (attr.name === "src" || attr.name === "srcset" || attr.name === "alt") {
                  continue;
                }
                var copyTo = null;
                if (/\.(jpg|jpeg|png|webp)\s+\d/.test(attr.value)) {
                  copyTo = "srcset";
                } else if (/^\s*\S+\.(jpg|jpeg|png|webp)\S*\s*$/.test(attr.value)) {
                  copyTo = "src";
                }
                if (copyTo) {
                  if (elem.tagName === "IMG" || elem.tagName === "PICTURE") {
                    elem.setAttribute(copyTo, attr.value);
                  } else if (elem.tagName === "FIGURE" && !this._getAllNodesWithTag(elem, ["img", "picture"]).length) {
                    var img = this._doc.createElement("img");
                    img.setAttribute(copyTo, attr.value);
                    elem.appendChild(img);
                  }
                }
              }
            }
          );
        },
        _getTextDensity(e, tags) {
          var textLength = this._getInnerText(e, true).length;
          if (textLength === 0) {
            return 0;
          }
          var childrenLength = 0;
          var children = this._getAllNodesWithTag(e, tags);
          this._forEachNode(
            children,
            (child) => childrenLength += this._getInnerText(child, true).length
          );
          return childrenLength / textLength;
        },
        /**
         * Clean an element of all tags of type "tag" if they look fishy.
         * "Fishy" is an algorithm based on content length, classnames, link density, number of images & embeds, etc.
         *
         * @return void
         **/
        _cleanConditionally(e, tag) {
          if (!this._flagIsActive(this.FLAG_CLEAN_CONDITIONALLY)) {
            return;
          }
          this._removeNodes(this._getAllNodesWithTag(e, [tag]), function(node) {
            var isDataTable = function(t) {
              return t._readabilityDataTable;
            };
            var isList = tag === "ul" || tag === "ol";
            if (!isList) {
              var listLength = 0;
              var listNodes = this._getAllNodesWithTag(node, ["ul", "ol"]);
              this._forEachNode(
                listNodes,
                (list) => listLength += this._getInnerText(list).length
              );
              isList = listLength / this._getInnerText(node).length > 0.9;
            }
            if (tag === "table" && isDataTable(node)) {
              return false;
            }
            if (this._hasAncestorTag(node, "table", -1, isDataTable)) {
              return false;
            }
            if (this._hasAncestorTag(node, "code")) {
              return false;
            }
            if ([...node.getElementsByTagName("table")].some(
              (tbl) => tbl._readabilityDataTable
            )) {
              return false;
            }
            var weight = this._getClassWeight(node);
            this.log("Cleaning Conditionally", node);
            var contentScore = 0;
            if (weight + contentScore < 0) {
              return true;
            }
            if (this._getCharCount(node, ",") < 10) {
              var p = node.getElementsByTagName("p").length;
              var img = node.getElementsByTagName("img").length;
              var li = node.getElementsByTagName("li").length - 100;
              var input = node.getElementsByTagName("input").length;
              var headingDensity = this._getTextDensity(node, [
                "h1",
                "h2",
                "h3",
                "h4",
                "h5",
                "h6"
              ]);
              var embedCount = 0;
              var embeds = this._getAllNodesWithTag(node, [
                "object",
                "embed",
                "iframe"
              ]);
              for (var i = 0; i < embeds.length; i++) {
                for (var j = 0; j < embeds[i].attributes.length; j++) {
                  if (this._allowedVideoRegex.test(embeds[i].attributes[j].value)) {
                    return false;
                  }
                }
                if (embeds[i].tagName === "object" && this._allowedVideoRegex.test(embeds[i].innerHTML)) {
                  return false;
                }
                embedCount++;
              }
              var innerText = this._getInnerText(node);
              if (this.REGEXPS.adWords.test(innerText) || this.REGEXPS.loadingWords.test(innerText)) {
                return true;
              }
              var contentLength = innerText.length;
              var linkDensity = this._getLinkDensity(node);
              var textishTags = ["SPAN", "LI", "TD"].concat(
                Array.from(this.DIV_TO_P_ELEMS)
              );
              var textDensity = this._getTextDensity(node, textishTags);
              var isFigureChild = this._hasAncestorTag(node, "figure");
              const shouldRemoveNode = () => {
                const errs = [];
                if (!isFigureChild && img > 1 && p / img < 0.5) {
                  errs.push(`Bad p to img ratio (img=${img}, p=${p})`);
                }
                if (!isList && li > p) {
                  errs.push(`Too many li's outside of a list. (li=${li} > p=${p})`);
                }
                if (input > Math.floor(p / 3)) {
                  errs.push(`Too many inputs per p. (input=${input}, p=${p})`);
                }
                if (!isList && !isFigureChild && headingDensity < 0.9 && contentLength < 25 && (img === 0 || img > 2) && linkDensity > 0) {
                  errs.push(
                    `Suspiciously short. (headingDensity=${headingDensity}, img=${img}, linkDensity=${linkDensity})`
                  );
                }
                if (!isList && weight < 25 && linkDensity > 0.2 + this._linkDensityModifier) {
                  errs.push(
                    `Low weight and a little linky. (linkDensity=${linkDensity})`
                  );
                }
                if (weight >= 25 && linkDensity > 0.5 + this._linkDensityModifier) {
                  errs.push(
                    `High weight and mostly links. (linkDensity=${linkDensity})`
                  );
                }
                if (embedCount === 1 && contentLength < 75 || embedCount > 1) {
                  errs.push(
                    `Suspicious embed. (embedCount=${embedCount}, contentLength=${contentLength})`
                  );
                }
                if (img === 0 && textDensity === 0) {
                  errs.push(
                    `No useful content. (img=${img}, textDensity=${textDensity})`
                  );
                }
                if (errs.length) {
                  this.log("Checks failed", errs);
                  return true;
                }
                return false;
              };
              var haveToRemove = shouldRemoveNode();
              if (isList && haveToRemove) {
                for (var x = 0; x < node.children.length; x++) {
                  let child = node.children[x];
                  if (child.children.length > 1) {
                    return haveToRemove;
                  }
                }
                let li_count = node.getElementsByTagName("li").length;
                if (img == li_count) {
                  return false;
                }
              }
              return haveToRemove;
            }
            return false;
          });
        },
        /**
         * Clean out elements that match the specified conditions
         *
         * @param Element
         * @param Function determines whether a node should be removed
         * @return void
         **/
        _cleanMatchedNodes(e, filter) {
          var endOfSearchMarkerNode = this._getNextNode(e, true);
          var next = this._getNextNode(e);
          while (next && next != endOfSearchMarkerNode) {
            if (filter.call(this, next, next.className + " " + next.id)) {
              next = this._removeAndGetNext(next);
            } else {
              next = this._getNextNode(next);
            }
          }
        },
        /**
         * Clean out spurious headers from an Element.
         *
         * @param Element
         * @return void
         **/
        _cleanHeaders(e) {
          let headingNodes = this._getAllNodesWithTag(e, ["h1", "h2"]);
          this._removeNodes(headingNodes, function(node) {
            let shouldRemove = this._getClassWeight(node) < 0;
            if (shouldRemove) {
              this.log("Removing header with low class weight:", node);
            }
            return shouldRemove;
          });
        },
        /**
         * Check if this node is an H1 or H2 element whose content is mostly
         * the same as the article title.
         *
         * @param Element  the node to check.
         * @return boolean indicating whether this is a title-like header.
         */
        _headerDuplicatesTitle(node) {
          if (node.tagName != "H1" && node.tagName != "H2") {
            return false;
          }
          var heading = this._getInnerText(node, false);
          this.log("Evaluating similarity of header:", heading, this._articleTitle);
          return this._textSimilarity(this._articleTitle, heading) > 0.75;
        },
        _flagIsActive(flag) {
          return (this._flags & flag) > 0;
        },
        _removeFlag(flag) {
          this._flags = this._flags & ~flag;
        },
        _isProbablyVisible(node) {
          return (!node.style || node.style.display != "none") && (!node.style || node.style.visibility != "hidden") && !node.hasAttribute("hidden") && //check for "fallback-image" so that wikimedia math images are displayed
          (!node.hasAttribute("aria-hidden") || node.getAttribute("aria-hidden") != "true" || node.className && node.className.includes && node.className.includes("fallback-image"));
        },
        /**
         * Runs readability.
         *
         * Workflow:
         *  1. Prep the document by removing script tags, css, etc.
         *  2. Build readability's DOM tree.
         *  3. Grab the article content from the current dom tree.
         *  4. Replace the current DOM tree with the new one.
         *  5. Read peacefully.
         *
         * @return void
         **/
        parse() {
          if (this._maxElemsToParse > 0) {
            var numTags = this._doc.getElementsByTagName("*").length;
            if (numTags > this._maxElemsToParse) {
              throw new Error(
                "Aborting parsing document; " + numTags + " elements found"
              );
            }
          }
          this._unwrapNoscriptImages(this._doc);
          var jsonLd = this._disableJSONLD ? {} : this._getJSONLD(this._doc);
          this._removeScripts(this._doc);
          this._prepDocument();
          var metadata = this._getArticleMetadata(jsonLd);
          this._metadata = metadata;
          this._articleTitle = metadata.title;
          var articleContent = this._grabArticle();
          if (!articleContent) {
            return null;
          }
          this.log("Grabbed: " + articleContent.innerHTML);
          this._postProcessContent(articleContent);
          if (!metadata.excerpt) {
            var paragraphs = articleContent.getElementsByTagName("p");
            if (paragraphs.length) {
              metadata.excerpt = paragraphs[0].textContent.trim();
            }
          }
          var textContent = articleContent.textContent;
          return {
            title: this._articleTitle,
            byline: metadata.byline || this._articleByline,
            dir: this._articleDir,
            lang: this._articleLang,
            content: this._serializer(articleContent),
            textContent,
            length: textContent.length,
            excerpt: metadata.excerpt,
            siteName: metadata.siteName || this._articleSiteName,
            publishedTime: metadata.publishedTime
          };
        }
      };
      if (typeof module === "object") {
        module.exports = Readability2;
      }
    }
  });

  // node_modules/@mozilla/readability/Readability-readerable.js
  var require_Readability_readerable = __commonJS({
    "node_modules/@mozilla/readability/Readability-readerable.js"(exports, module) {
      var REGEXPS = {
        // NOTE: These two regular expressions are duplicated in
        // Readability.js. Please keep both copies in sync.
        unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i,
        okMaybeItsACandidate: /and|article|body|column|content|main|shadow/i
      };
      function isNodeVisible(node) {
        return (!node.style || node.style.display != "none") && !node.hasAttribute("hidden") && //check for "fallback-image" so that wikimedia math images are displayed
        (!node.hasAttribute("aria-hidden") || node.getAttribute("aria-hidden") != "true" || node.className && node.className.includes && node.className.includes("fallback-image"));
      }
      function isProbablyReaderable2(doc, options = {}) {
        if (typeof options == "function") {
          options = { visibilityChecker: options };
        }
        var defaultOptions = {
          minScore: 20,
          minContentLength: 140,
          visibilityChecker: isNodeVisible
        };
        options = Object.assign(defaultOptions, options);
        var nodes = doc.querySelectorAll("p, pre, article");
        var brNodes = doc.querySelectorAll("div > br");
        if (brNodes.length) {
          var set = new Set(nodes);
          [].forEach.call(brNodes, function(node) {
            set.add(node.parentNode);
          });
          nodes = Array.from(set);
        }
        var score = 0;
        return [].some.call(nodes, function(node) {
          if (!options.visibilityChecker(node)) {
            return false;
          }
          var matchString = node.className + " " + node.id;
          if (REGEXPS.unlikelyCandidates.test(matchString) && !REGEXPS.okMaybeItsACandidate.test(matchString)) {
            return false;
          }
          if (node.matches("li p")) {
            return false;
          }
          var textContentLength = node.textContent.trim().length;
          if (textContentLength < options.minContentLength) {
            return false;
          }
          score += Math.sqrt(textContentLength - options.minContentLength);
          if (score > options.minScore) {
            return true;
          }
          return false;
        });
      }
      if (typeof module === "object") {
        module.exports = isProbablyReaderable2;
      }
    }
  });

  // node_modules/@mozilla/readability/index.js
  var require_readability = __commonJS({
    "node_modules/@mozilla/readability/index.js"(exports, module) {
      var Readability2 = require_Readability();
      var isProbablyReaderable2 = require_Readability_readerable();
      module.exports = {
        Readability: Readability2,
        isProbablyReaderable: isProbablyReaderable2
      };
    }
  });

  // src/extension/content.ts
  var import_readability = __toESM(require_readability());

  // src/shared/colors.ts
  var baseColors = {
    // Status colors (same in both themes)
    error: "#ef4444",
    errorSubtle: "rgba(239, 68, 68, 0.12)",
    success: "#10b981",
    successSubtle: "rgba(16, 185, 129, 0.12)",
    warning: "#f59e0b",
    warningSubtle: "rgba(245, 158, 11, 0.12)",
    // Severity colors (same in both themes)
    severityCritical: "#ef4444",
    severitySignificant: "#eab308",
    severityMinor: "#737373"
  };
  var colors = {
    ...baseColors,
    // Backgrounds
    bgPrimary: "#0f1117",
    bgSecondary: "#1a1d27",
    bgTertiary: "#242936",
    bgHover: "#2d3344",
    // Text
    textPrimary: "#f0f2f5",
    textSecondary: "#9ca3b0",
    textMuted: "#6b7280",
    // Accent (blue)
    accent: "#60a5fa",
    accentHover: "#93c5fd",
    accentSubtle: "rgba(96, 165, 250, 0.12)",
    // Borders
    border: "rgba(255, 255, 255, 0.08)",
    borderStrong: "rgba(255, 255, 255, 0.12)",
    // Highlight colors (for reasoning gaps)
    highlightCritical: "rgba(239, 68, 68, 0.25)",
    highlightCriticalHover: "rgba(239, 68, 68, 0.4)",
    highlightSignificant: "rgba(234, 179, 8, 0.25)",
    highlightSignificantHover: "rgba(234, 179, 8, 0.4)",
    highlightMinor: "rgba(115, 115, 115, 0.25)",
    highlightMinorHover: "rgba(115, 115, 115, 0.4)",
    highlightDefault: "rgba(96, 165, 250, 0.25)",
    highlightDefaultHover: "rgba(96, 165, 250, 0.4)",
    severityDefault: "#60a5fa"
  };
  var missColors = {
    ...baseColors,
    // Backgrounds (purple-tinted dark)
    bgPrimary: "#1a1520",
    bgSecondary: "#231d2b",
    bgTertiary: "#2d2638",
    bgHover: "#3a3245",
    // Text (lavender-tinted)
    textPrimary: "#f5f0fa",
    textSecondary: "#c4b8d4",
    textMuted: "#8b7fa3",
    // Accent (purple)
    accent: "#c084fc",
    accentHover: "#d8b4fe",
    accentSubtle: "rgba(192, 132, 252, 0.12)",
    // Borders (purple-tinted)
    border: "rgba(192, 132, 252, 0.15)",
    borderStrong: "rgba(192, 132, 252, 0.25)",
    // Highlight colors (purple-shifted for theme consistency)
    highlightCritical: "rgba(239, 68, 68, 0.25)",
    highlightCriticalHover: "rgba(239, 68, 68, 0.4)",
    highlightSignificant: "rgba(234, 179, 8, 0.25)",
    highlightSignificantHover: "rgba(234, 179, 8, 0.4)",
    highlightMinor: "rgba(139, 127, 163, 0.25)",
    highlightMinorHover: "rgba(139, 127, 163, 0.4)",
    highlightDefault: "rgba(192, 132, 252, 0.25)",
    highlightDefaultHover: "rgba(192, 132, 252, 0.4)",
    severityDefault: "#c084fc"
  };
  function generateCssVariables(theme) {
    return `
  --bg-primary: ${theme.bgPrimary};
  --bg-secondary: ${theme.bgSecondary};
  --bg-tertiary: ${theme.bgTertiary};
  --bg-hover: ${theme.bgHover};
  --text-primary: ${theme.textPrimary};
  --text-secondary: ${theme.textSecondary};
  --text-muted: ${theme.textMuted};
  --accent: ${theme.accent};
  --accent-hover: ${theme.accentHover};
  --accent-subtle: ${theme.accentSubtle};
  --error: ${theme.error};
  --error-subtle: ${theme.errorSubtle};
  --success: ${theme.success};
  --success-subtle: ${theme.successSubtle};
  --warning: ${theme.warning};
  --warning-subtle: ${theme.warningSubtle};
  --border: ${theme.border};
  --border-strong: ${theme.borderStrong};
  --severity-critical: ${theme.severityCritical};
  --severity-significant: ${theme.severitySignificant};
  --severity-minor: ${theme.severityMinor};`;
  }
  var cssVariables = `
:root {
${generateCssVariables(colors)}
  --shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 14px;
}
`;
  var missCssVariables = `
body.theme-miss {
${generateCssVariables(missColors)}
}
`;
  function missBackgroundCss(imagePath) {
    return `
body.theme-miss {
  background: 
    linear-gradient(
      to bottom,
      rgba(26, 21, 32, 0.85) 0%,
      rgba(26, 21, 32, 0.75) 50%,
      rgba(26, 21, 32, 0.9) 100%
    ),
    url('${imagePath}');
  background-size: cover;
  background-position: center;
  background-attachment: fixed;
}
`;
  }
  var themeCssVariables = cssVariables + missCssVariables + missBackgroundCss("/static/missinfo_bg.jpg");
  var themeCssVariablesExtension = cssVariables + missCssVariables + missBackgroundCss("icons/missinfo_bg.jpg");

  // src/shared/highlight-styles.ts
  var highlightCSS = `
/* ===== CSS Custom Highlight API Styles ===== */
::highlight(logic-checker-critical) {
  background-color: ${colors.highlightCritical};
}

::highlight(logic-checker-significant) {
  background-color: ${colors.highlightSignificant};
}

::highlight(logic-checker-minor) {
  background-color: ${colors.highlightMinor};
}

::highlight(logic-checker-default) {
  background-color: ${colors.highlightDefault};
}

/* ===== Fallback: Span-based Highlight Styles ===== */
.logic-checker-highlight {
  cursor: help;
  position: relative;
  transition: background 0.2s ease;
  border-radius: 2px;
  padding: 1px 2px;
}

.logic-checker-highlight.critical,
.logic-checker-highlight[data-importance="critical"] {
  background: linear-gradient(to bottom, ${colors.highlightCritical} 0%, rgba(239, 68, 68, 0.15) 100%);
}

.logic-checker-highlight.critical:hover,
.logic-checker-highlight[data-importance="critical"]:hover {
  background: ${colors.highlightCriticalHover};
}

.logic-checker-highlight.significant,
.logic-checker-highlight[data-importance="significant"] {
  background: linear-gradient(to bottom, ${colors.highlightSignificant} 0%, rgba(234, 179, 8, 0.15) 100%);
}

.logic-checker-highlight.significant:hover,
.logic-checker-highlight[data-importance="significant"]:hover {
  background: ${colors.highlightSignificantHover};
}

.logic-checker-highlight.minor,
.logic-checker-highlight[data-importance="minor"] {
  background: linear-gradient(to bottom, ${colors.highlightMinor} 0%, rgba(115, 115, 115, 0.15) 100%);
}

.logic-checker-highlight.minor:hover,
.logic-checker-highlight[data-importance="minor"]:hover {
  background: ${colors.highlightMinorHover};
}

.logic-checker-highlight:not(.critical):not(.significant):not(.minor):not([data-importance]) {
  background: linear-gradient(to bottom, ${colors.highlightDefault} 0%, rgba(96, 165, 250, 0.15) 100%);
}

.logic-checker-highlight:not(.critical):not(.significant):not(.minor):not([data-importance]):hover {
  background: ${colors.highlightDefaultHover};
}
`;
  var tooltipCSS = `
.logic-checker-tooltip {
  position: fixed;
  z-index: 2147483647;
  width: max-content;
  max-width: 400px;
  min-width: 280px;
  background: linear-gradient(135deg, #1a1a1a 0%, #0d0d0d 100%);
  border: 1px solid;
  border-radius: 8px;
  padding: 12px 14px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 13px;
  line-height: 1.5;
  color: #f5f5f5;
  pointer-events: none;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.logic-checker-tooltip.critical {
  border-color: ${colors.severityCritical};
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(239, 68, 68, 0.2);
}

.logic-checker-tooltip.significant {
  border-color: ${colors.severitySignificant};
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(234, 179, 8, 0.2);
}

.logic-checker-tooltip.minor {
  border-color: ${colors.severityMinor};
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(115, 115, 115, 0.2);
}

.logic-checker-tooltip:not(.critical):not(.significant):not(.minor) {
  border-color: ${colors.severityDefault};
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(96, 165, 250, 0.2);
}

.logic-checker-tooltip.visible {
  opacity: 1;
  transform: translateY(0);
}

.logic-checker-tooltip-header {
  margin-bottom: 6px;
}

.logic-checker-tooltip-badge {
  display: inline-block;
  color: #000;
  font-size: 10px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 10px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.logic-checker-tooltip.critical .logic-checker-tooltip-badge {
  background: ${colors.severityCritical};
  color: white;
}

.logic-checker-tooltip.significant .logic-checker-tooltip-badge {
  background: ${colors.severitySignificant};
}

.logic-checker-tooltip.minor .logic-checker-tooltip-badge {
  background: ${colors.severityMinor};
  color: white;
}

.logic-checker-tooltip:not(.critical):not(.significant):not(.minor) .logic-checker-tooltip-badge {
  background: ${colors.severityDefault};
}

.logic-checker-tooltip-explanation {
  color: #d4d4d4;
}
`;
  var contentStyles = highlightCSS + "\n" + tooltipCSS;
  var demoHighlightCSS = `
.demo-highlight {
  cursor: help;
  position: relative;
  background: linear-gradient(to bottom, ${colors.highlightSignificant} 0%, rgba(234, 179, 8, 0.15) 100%);
  border-radius: 2px;
  padding: 1px 2px;
  transition: background 0.2s ease;
}

.demo-highlight:hover {
  background: ${colors.highlightSignificantHover};
}

.demo-highlight.critical {
  background: linear-gradient(to bottom, ${colors.highlightCritical} 0%, rgba(239, 68, 68, 0.15) 100%);
}

.demo-highlight.critical:hover {
  background: ${colors.highlightCriticalHover};
}

.demo-highlight.minor {
  background: linear-gradient(to bottom, ${colors.highlightMinor} 0%, rgba(115, 115, 115, 0.15) 100%);
}

.demo-highlight.minor:hover {
  background: ${colors.highlightMinorHover};
}

.demo-tooltip {
  position: absolute;
  z-index: 1000;
  width: max-content;
  max-width: 400px;
  min-width: 280px;
  background: linear-gradient(135deg, #1a1a1a 0%, #0d0d0d 100%);
  border: 1px solid ${colors.severitySignificant};
  border-radius: 8px;
  padding: 12px 14px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(234, 179, 8, 0.2);
  font-size: 13px;
  line-height: 1.5;
  color: #f5f5f5;
  pointer-events: none;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s ease, transform 0.2s ease;
  left: 0;
  top: 100%;
  margin-top: 8px;
}

.demo-highlight:hover .demo-tooltip {
  opacity: 1;
  transform: translateY(0);
}

.demo-tooltip-header {
  margin-bottom: 6px;
}

.demo-tooltip-badge {
  display: inline-block;
  background: ${colors.severitySignificant};
  color: #000;
  font-size: 10px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 10px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.demo-tooltip-explanation {
  color: #d4d4d4;
}
`;

  // src/shared/kawaii.ts
  function makeKawaii(text) {
    if (!text) return text;
    let result = text;
    result = result.replace(/\.(\s+|$)/g, (match, space) => {
      if (Math.random() < 0.6) {
        return "~" + space;
      }
      return match;
    });
    const loveWords = ["good", "great", "excellent", "helpful", "useful", "important", "nice", "correct", "right", "valid"];
    loveWords.forEach((word) => {
      const regex = new RegExp(`\\b${word}\\b`, "gi");
      result = result.replace(regex, (match) => {
        if (Math.random() < 0.4) {
          return match + " <3";
        }
        return match;
      });
    });
    if (result.length > 30) {
      result = result.replace(/([.!?])(\s+)/g, (match, punct, space) => {
        if (Math.random() < 0.5) {
          const kawaiis = ["uwu", "owo", ">w<", "nya~"];
          const chosen = kawaiis[Math.floor(Math.random() * kawaiis.length)];
          return punct + " " + chosen + space;
        }
        return match;
      });
    }
    result = result.replace(/([.!?])(\s+)/g, (match, punct, space) => {
      if (Math.random() < 0.25) {
        return punct + " \u2728" + space;
      }
      return match;
    });
    if (Math.random() < 0.3) {
      const emoticons = [" (\u25D5\u203F\u25D5)", " (\xB4\u2200\uFF40)", " (\u0E51\u2022\u0300\u03C9\u2022\u0301\u0E51)", " (\u25E1\u203F\u25E1)", " (\uFF61\u25D5\u203F\u25D5\uFF61)"];
      const chosen = emoticons[Math.floor(Math.random() * emoticons.length)];
      result = result.replace(/([.!?])(\s*)$/, "$1" + chosen + "$2");
    }
    result = result.replace(/it is important to note/gi, (match) => Math.random() < 0.7 ? "just so you know~" : match);
    result = result.replace(/it should be noted/gi, (match) => Math.random() < 0.7 ? "heads up~" : match);
    result = result.replace(/it is worth noting/gi, (match) => Math.random() < 0.7 ? "worth mentioning~" : match);
    result = result.replace(/this suggests/gi, (match) => Math.random() < 0.6 ? "this kinda suggests" : match);
    result = result.replace(/this indicates/gi, (match) => Math.random() < 0.6 ? "this kinda indicates" : match);
    result = result.replace(/\bhowever\b/gi, (match, offset) => offset > 0 && Math.random() < 0.7 ? "but" : match);
    result = result.replace(/\bfurthermore\b/gi, (match) => Math.random() < 0.6 ? "also~" : match);
    result = result.replace(/\btherefore\b/gi, (match) => Math.random() < 0.6 ? "so" : match);
    result = result.replace(/\bmoreover\b/gi, (match) => Math.random() < 0.5 ? "plus~" : match);
    result = result.replace(/\bconsequently\b/gi, (match) => Math.random() < 0.5 ? "so" : match);
    result = result.replace(/\bnevertheless\b/gi, (match) => Math.random() < 0.5 ? "but still~" : match);
    if (Math.random() < 0.2) {
      const kawaiiPhrases = [" uwu", " owo", " nya~", " >w<", " (\xB4\uFF61\u2022 \u1D55 \u2022\uFF61`) \u2661"];
      const chosen = kawaiiPhrases[Math.floor(Math.random() * kawaiiPhrases.length)];
      const words = result.split(" ");
      if (words.length > 3 && chosen) {
        const insertAt = Math.floor(words.length / 2) + Math.floor(Math.random() * 3) - 1;
        if (words[insertAt]) {
          words[insertAt] += chosen;
          result = words.join(" ");
        }
      }
    }
    return result;
  }

  // src/extension/config.ts
  if (false) {
    throw new Error("__BACKEND_URL__ must be defined at build time");
  }
  var BACKEND_URL = "https://sanitycheck-production.up.railway.app";

  // src/extension/messaging.ts
  function sendToBackground(message) {
    return chrome.runtime.sendMessage(message);
  }

  // src/extension/content.ts
  (function() {
    if (window.__logicCheckerInjected) return;
    window.__logicCheckerInjected = true;
    const DEBUG_ENABLED = false;
    const EXTENSION_VERSION = "1.2.0";
    const DEBUG_SERVER_URL = `${BACKEND_URL}/debug/log`;
    let isMissInfoMode = false;
    if (typeof chrome !== "undefined" && chrome.storage) {
      chrome.storage.local.get(["theme"], (result) => {
        isMissInfoMode = result.theme === "miss";
      });
      chrome.storage.onChanged.addListener((changes, areaName) => {
        if (areaName === "local" && changes.theme) {
          isMissInfoMode = changes.theme.newValue === "miss";
        }
      });
    }
    const debug = {
      log: (message, data = {}, source = "content") => {
        if (!DEBUG_ENABLED) return;
        void fetch(DEBUG_SERVER_URL, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            level: "log",
            message,
            data: { ...data, url: window.location.href },
            source,
            version: EXTENSION_VERSION,
            timestamp: (/* @__PURE__ */ new Date()).toISOString()
          })
        }).catch(() => {
        });
      },
      warn: (message, data = {}, source = "content") => {
        if (!DEBUG_ENABLED) return;
        void fetch(DEBUG_SERVER_URL, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            level: "warn",
            message,
            data: { ...data, url: window.location.href },
            source,
            version: EXTENSION_VERSION,
            timestamp: (/* @__PURE__ */ new Date()).toISOString()
          })
        }).catch(() => {
        });
      },
      error: (message, error, source = "content", additionalData = {}) => {
        if (!DEBUG_ENABLED) return;
        const err = error;
        void fetch(DEBUG_SERVER_URL, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            level: "error",
            message,
            data: {
              ...additionalData,
              url: window.location.href,
              error: err ? {
                name: err.name,
                message: err.message,
                stack: err.stack
              } : void 0
            },
            source,
            version: EXTENSION_VERSION,
            timestamp: (/* @__PURE__ */ new Date()).toISOString()
          })
        }).catch(() => {
        });
      },
      debug: (message, data = {}, source = "content") => {
        if (!DEBUG_ENABLED) return;
        void fetch(DEBUG_SERVER_URL, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            level: "debug",
            message,
            data: { ...data, url: window.location.href },
            source,
            version: EXTENSION_VERSION,
            timestamp: (/* @__PURE__ */ new Date()).toISOString()
          })
        }).catch(() => {
        });
      }
    };
    window.addEventListener("error", (event) => {
      debug.error("Window error in content script", {
        message: event.message,
        filename: event.filename,
        lineno: event.lineno,
        colno: event.colno
      }, "content-window-error");
    });
    window.addEventListener("unhandledrejection", (event) => {
      debug.error("Unhandled promise rejection in content script", event.reason, "content-promise-rejection");
    });
    const USE_CSS_HIGHLIGHT_API = typeof CSS !== "undefined" && "highlights" in CSS && typeof Highlight !== "undefined";
    debug.log("Content script initialized", {
      url: window.location.href,
      title: document.title,
      useCSSHighlightAPI: USE_CSS_HIGHLIGHT_API,
      hasReadability: typeof import_readability.Readability !== "undefined"
    }, "content-init");
    const highlightRanges = /* @__PURE__ */ new Map();
    const styleId = "logic-checker-styles";
    if (!document.getElementById(styleId)) {
      const style = document.createElement("style");
      style.id = styleId;
      style.textContent = contentStyles;
      document.head.appendChild(style);
      debug.log("Styles injected", { useCSSHighlightAPI: USE_CSS_HIGHLIGHT_API }, "content-init");
    }
    function ensureTooltip() {
      let tooltip2 = document.getElementById("logic-checker-tooltip");
      if (!tooltip2) {
        tooltip2 = document.createElement("div");
        tooltip2.id = "logic-checker-tooltip";
        tooltip2.className = "logic-checker-tooltip";
        document.body.appendChild(tooltip2);
      }
      return tooltip2;
    }
    const tooltip = ensureTooltip();
    function isArticlePage() {
      debug.debug("Checking if page is an article using Readability", {}, "content-article-check");
      const isProbablyArticle = (0, import_readability.isProbablyReaderable)(document, {
        minContentLength: 140,
        minScore: 20
      });
      const hasArticleTag = !!document.querySelector("article");
      const hasArticleSchema = !!document.querySelector('[itemtype*="Article"]') || !!document.querySelector('[itemtype*="BlogPosting"]');
      const ogType = document.querySelector('meta[property="og:type"]');
      const hasArticleMeta = ogType?.content === "article";
      let score = 0;
      if (isProbablyArticle) score += 5;
      if (hasArticleTag) score += 2;
      if (hasArticleSchema) score += 2;
      if (hasArticleMeta) score += 1;
      const result = {
        isArticle: score >= 4 || isProbablyArticle,
        confidence: score,
        indicators: {
          isProbablyReaderable: isProbablyArticle,
          hasArticleTag,
          hasArticleSchema,
          hasArticleMeta
        }
      };
      debug.log("Article check complete (Readability)", {
        isArticle: result.isArticle,
        confidence: result.confidence,
        isProbablyReaderable: isProbablyArticle
      }, "content-article-check");
      return result;
    }
    function getSiteSpecificExtractor() {
      const host = window.location.hostname.toLowerCase();
      if (host.includes("lesswrong.com") || host.includes("greaterwrong.com")) {
        return () => {
          const postContent = document.querySelector("#postContent");
          if (!postContent) return null;
          const title = document.querySelector("h1")?.textContent?.trim() ?? document.title;
          const text = postContent.textContent ?? "";
          const wordCount = text.split(/\s+/).filter((w) => w.length > 0).length;
          if (wordCount < 10) return null;
          return { title, text, wordCount, url: window.location.href, source: "lesswrong" };
        };
      }
      return null;
    }
    function extractArticleText() {
      const siteExtractor = getSiteSpecificExtractor();
      if (siteExtractor) {
        try {
          const result = siteExtractor();
          if (result && result.text && result.wordCount > 10) {
            debug.log("Article extracted", {
              extractor: result.source,
              wordCount: result.wordCount
            }, "content-extract");
            return result;
          }
        } catch (e) {
          const err = e;
          debug.warn("Site-specific extraction failed", { error: err.message }, "content-extract");
        }
      }
      try {
        const docClone = document.cloneNode(true);
        docClone.querySelectorAll("script, style, noscript, iframe, svg").forEach((el) => el.remove());
        const reader = new import_readability.Readability(docClone, { charThreshold: 100 });
        const article = reader.parse();
        if (article?.textContent) {
          const wordCount = article.textContent.split(/\s+/).filter((w) => w.length > 0).length;
          debug.log("Article extracted", { extractor: "readability", wordCount }, "content-extract");
          return {
            title: article.title ?? document.title,
            text: article.textContent,
            wordCount,
            url: window.location.href,
            byline: article.byline ?? void 0,
            siteName: article.siteName ?? void 0,
            excerpt: article.excerpt ?? void 0
          };
        }
        debug.warn("Readability failed, using fallback", {}, "content-extract");
        return fallbackExtraction();
      } catch (error) {
        debug.error("Readability error", error, "content-extract");
        return fallbackExtraction();
      }
    }
    function fallbackExtraction() {
      debug.log("Using fallback extraction", {}, "content-extract-fallback");
      const h1 = document.querySelector("h1");
      const ogTitle = document.querySelector('meta[property="og:title"]');
      const title = h1?.textContent?.trim() ?? ogTitle?.content ?? document.title;
      const paragraphs = document.querySelectorAll("p");
      const textParts = [];
      paragraphs.forEach((p) => {
        const parent = p.closest("nav, footer, header, aside, .sidebar, .comments, .advertisement");
        if (parent) return;
        const text = p.textContent?.trim() ?? "";
        if (text.length > 50) {
          textParts.push(text);
        }
      });
      const articleText = textParts.join("\n\n");
      const wordCount = articleText.split(/\s+/).filter((w) => w.length > 0).length;
      debug.log("Fallback extraction complete", {
        title,
        textLength: articleText.length,
        wordCount
      }, "content-extract-fallback");
      return {
        title,
        text: articleText,
        wordCount,
        url: window.location.href
      };
    }
    function normalizeText(text) {
      return text.toLowerCase().replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"').replace(/[\u2013\u2014]/g, "-").replace(/\u2026/g, "...").replace(/\s+/g, " ").trim();
    }
    function findTextInPage(quote) {
      if (!quote || quote.length < 10) {
        return null;
      }
      const normalizedQuote = normalizeText(quote);
      const walker = document.createTreeWalker(
        document.body,
        NodeFilter.SHOW_TEXT,
        {
          acceptNode: (node2) => {
            const parent = node2.parentElement;
            if (!parent) return NodeFilter.FILTER_REJECT;
            const tagName = parent.tagName.toLowerCase();
            if (["script", "style", "noscript"].includes(tagName)) {
              return NodeFilter.FILTER_REJECT;
            }
            if (parent.closest(".logic-checker-tooltip")) {
              return NodeFilter.FILTER_REJECT;
            }
            if ((node2.textContent?.trim().length ?? 0) === 0) {
              return NodeFilter.FILTER_REJECT;
            }
            return NodeFilter.FILTER_ACCEPT;
          }
        }
      );
      const textNodes = [];
      let node;
      while (node = walker.nextNode()) {
        textNodes.push(node);
      }
      let fullText = "";
      const nodeMap = [];
      for (const textNode of textNodes) {
        const text = textNode.textContent ?? "";
        for (let i = 0; i < text.length; i++) {
          nodeMap.push({ node: textNode, offset: i });
        }
        fullText += text;
      }
      const normalizedFullText = normalizeText(fullText);
      const normalizedToOriginalMap = buildNormalizedMapping(fullText, normalizedFullText);
      let matchIndex = normalizedFullText.indexOf(normalizedQuote);
      if (matchIndex === -1) {
        matchIndex = fuzzyFind(normalizedFullText, normalizedQuote);
      }
      if (matchIndex === -1) {
        return null;
      }
      const matchEndIndex = matchIndex + normalizedQuote.length;
      if (matchIndex >= normalizedToOriginalMap.length || matchEndIndex > normalizedToOriginalMap.length) {
        return null;
      }
      const originalMatchStart = normalizedToOriginalMap[matchIndex];
      const originalMatchEnd = normalizedToOriginalMap[Math.min(matchEndIndex, normalizedToOriginalMap.length - 1)];
      if (originalMatchStart === void 0 || originalMatchEnd === void 0) {
        return null;
      }
      if (originalMatchStart >= nodeMap.length || originalMatchEnd >= nodeMap.length) {
        return null;
      }
      const endPos = Math.min(originalMatchEnd - 1, nodeMap.length - 1);
      const endNodeEntry = nodeMap[endPos];
      if (!endNodeEntry) return null;
      const endNode = endNodeEntry.node;
      const endText = endNode.textContent ?? "";
      let endOffset = endNodeEntry.offset + 1;
      while (endOffset < endText.length && /\w/.test(endText[endOffset] ?? "")) endOffset++;
      const startNodeEntry = nodeMap[originalMatchStart];
      if (!startNodeEntry) return null;
      return {
        startNode: startNodeEntry.node,
        startOffset: startNodeEntry.offset,
        endNode,
        endOffset
      };
    }
    function buildNormalizedMapping(original, normalized) {
      const mapping = [];
      let origIdx = 0;
      let normIdx = 0;
      let lastWasWhitespace = false;
      while (origIdx < original.length && normIdx < normalized.length) {
        const origChar = original[origIdx] ?? "";
        const isWhitespace = /\s/.test(origChar);
        if (isWhitespace) {
          if (!lastWasWhitespace && normalized[normIdx] === " ") {
            mapping[normIdx] = origIdx;
            normIdx++;
          }
          lastWasWhitespace = true;
          origIdx++;
        } else {
          lastWasWhitespace = false;
          let normChar = origChar.toLowerCase();
          if (normChar === "\u2018" || normChar === "\u2019") {
            normChar = "'";
          } else if (normChar === "\u201C" || normChar === "\u201D") {
            normChar = '"';
          } else if (normChar === "\u2013" || normChar === "\u2014") {
            normChar = "-";
          } else if (normChar === "\u2026") {
            if (normIdx + 2 < normalized.length && normalized[normIdx] === "." && normalized[normIdx + 1] === "." && normalized[normIdx + 2] === ".") {
              mapping[normIdx] = origIdx;
              mapping[normIdx + 1] = origIdx;
              mapping[normIdx + 2] = origIdx;
              normIdx += 3;
              origIdx++;
              continue;
            } else {
              origIdx++;
              continue;
            }
          }
          if (normIdx < normalized.length && normChar === normalized[normIdx]) {
            mapping[normIdx] = origIdx;
            normIdx++;
            origIdx++;
          } else {
            origIdx++;
          }
        }
      }
      const lastMapped = mapping.length > 0 ? mapping[mapping.length - 1] ?? 0 : Math.max(0, original.length - 1);
      while (mapping.length < normalized.length) {
        mapping.push(Math.min(lastMapped, original.length - 1));
      }
      return mapping;
    }
    function fuzzyFind(haystack, needle) {
      if (needle.length > haystack.length) return -1;
      for (let len = needle.length; len >= Math.min(30, needle.length * 0.5); len--) {
        const prefix = needle.substring(0, len);
        const idx = haystack.indexOf(prefix);
        if (idx !== -1) {
          return idx;
        }
      }
      const words = needle.split(" ").filter((w) => w.length > 3);
      if (words.length >= 3) {
        for (let i = 0; i <= words.length - 3; i++) {
          const phrase = words.slice(i, i + 3).join(" ");
          const idx = haystack.indexOf(phrase);
          if (idx !== -1) {
            return idx;
          }
        }
      }
      return -1;
    }
    function highlightWithCSSAPI(matchInfo, issue, index) {
      try {
        const range = document.createRange();
        range.setStart(matchInfo.startNode, matchInfo.startOffset);
        range.setEnd(matchInfo.endNode, matchInfo.endOffset);
        const importance = issue.importance ?? "default";
        const highlightName = `logic-checker-${importance}`;
        highlightRanges.set(range, {
          issue,
          index,
          importance,
          explanation: issue.gap ?? issue.why_it_doesnt_follow ?? issue.explanation ?? ""
        });
        let highlight = CSS.highlights.get(highlightName);
        if (!highlight) {
          highlight = new Highlight();
          CSS.highlights.set(highlightName, highlight);
        }
        highlight.add(range);
        return true;
      } catch (_e) {
        return false;
      }
    }
    function clearCSSHighlights() {
      const names = ["logic-checker-critical", "logic-checker-significant", "logic-checker-minor", "logic-checker-default"];
      names.forEach((name) => {
        if (CSS.highlights.has(name)) {
          CSS.highlights.delete(name);
        }
      });
      highlightRanges.clear();
    }
    function getHighlightAtPoint(x, y) {
      let caretPos = null;
      if ("caretPositionFromPoint" in document) {
        const pos = document.caretPositionFromPoint(x, y);
        if (!pos) return null;
        caretPos = pos;
      } else if (document.caretRangeFromPoint) {
        const range = document.caretRangeFromPoint(x, y);
        if (!range) return null;
        caretPos = { offsetNode: range.startContainer, offset: range.startOffset };
      } else {
        return null;
      }
      const node = caretPos.offsetNode;
      const offset = caretPos.offset;
      for (const [range, data] of highlightRanges) {
        try {
          if (range.isPointInRange(node, offset)) {
            return data;
          }
        } catch (_e) {
          continue;
        }
      }
      return null;
    }
    let lastHighlightData = null;
    function handleMouseMoveForHighlight(e) {
      if (!USE_CSS_HIGHLIGHT_API) return;
      const data = getHighlightAtPoint(e.clientX, e.clientY);
      if (data) {
        if (data !== lastHighlightData) {
          showTooltipForData(e, data);
          lastHighlightData = data;
        } else {
          const tooltipEl = ensureTooltip();
          positionTooltip(e, tooltipEl);
        }
      } else {
        if (lastHighlightData) {
          hideTooltip();
          lastHighlightData = null;
        }
      }
    }
    function showTooltipForData(e, data) {
      const tooltipEl = ensureTooltip();
      const importance = data.importance ?? "minor";
      tooltipEl.className = `logic-checker-tooltip ${importance}`;
      const _emoji = importance === "critical" ? "\u{1F534}" : importance === "significant" ? "\u{1F7E0}" : "\u{1F7E1}";
      const typeLabel = data.issue.category ?? data.issue.type ?? "Logic Issue";
      const formattedType = typeLabel.split(/[_-]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
      tooltipEl.innerHTML = `
      <div class="logic-checker-tooltip-badge">${escapeHtml(formattedType)}</div>
      <div class="logic-checker-tooltip-explanation">${escapeHtml(data.explanation || "No explanation available")}</div>
    `;
      positionTooltip(e, tooltipEl);
      tooltipEl.classList.add("visible");
    }
    if (USE_CSS_HIGHLIGHT_API) {
      document.addEventListener("mousemove", handleMouseMoveForHighlight, { passive: true });
    }
    function highlightRangeWithSpan(matchInfo, issue, index) {
      try {
        const range = document.createRange();
        range.setStart(matchInfo.startNode, matchInfo.startOffset);
        range.setEnd(matchInfo.endNode, matchInfo.endOffset);
        const highlight = document.createElement("span");
        const importance = issue.importance ?? "minor";
        highlight.className = `logic-checker-highlight ${importance}`;
        highlight.dataset["issueIndex"] = String(index);
        highlight.dataset["issueType"] = issue.category ?? issue.type ?? issue.importance ?? "issue";
        highlight.dataset["importance"] = importance;
        highlight.dataset["issueExplanation"] = issue.gap ?? issue.why_it_doesnt_follow ?? issue.explanation ?? "";
        range.surroundContents(highlight);
        highlight.addEventListener("mouseenter", showTooltipEvent);
        highlight.addEventListener("mouseleave", hideTooltip);
        highlight.addEventListener("mousemove", moveTooltip);
        return true;
      } catch (_e) {
        return highlightAlternative(matchInfo, issue, index);
      }
    }
    function highlightAlternative(matchInfo, issue, index) {
      try {
        const range = document.createRange();
        range.setStart(matchInfo.startNode, matchInfo.startOffset);
        range.setEnd(matchInfo.endNode, matchInfo.endOffset);
        const fragment = range.cloneContents();
        const textContent = fragment.textContent ?? "";
        if (textContent.length < 10) {
          return false;
        }
        const startText = matchInfo.startNode.textContent ?? "";
        const before = startText.substring(0, matchInfo.startOffset);
        const highlighted = startText.substring(matchInfo.startOffset);
        const wrapper = document.createElement("span");
        const importance = issue.importance ?? "minor";
        wrapper.className = `logic-checker-highlight ${importance}`;
        wrapper.dataset["issueIndex"] = String(index);
        wrapper.dataset["issueType"] = issue.category ?? issue.type ?? issue.importance ?? "issue";
        wrapper.dataset["importance"] = importance;
        wrapper.dataset["issueExplanation"] = issue.gap ?? issue.why_it_doesnt_follow ?? issue.explanation ?? "";
        wrapper.textContent = highlighted;
        const parent = matchInfo.startNode.parentNode;
        if (!parent) return false;
        const beforeNode = document.createTextNode(before);
        parent.insertBefore(beforeNode, matchInfo.startNode);
        parent.insertBefore(wrapper, matchInfo.startNode);
        parent.removeChild(matchInfo.startNode);
        wrapper.addEventListener("mouseenter", showTooltipEvent);
        wrapper.addEventListener("mouseleave", hideTooltip);
        wrapper.addEventListener("mousemove", moveTooltip);
        return true;
      } catch (_e) {
        return false;
      }
    }
    function highlightRange(matchInfo, issue, index) {
      if (USE_CSS_HIGHLIGHT_API) {
        return highlightWithCSSAPI(matchInfo, issue, index);
      } else {
        return highlightRangeWithSpan(matchInfo, issue, index);
      }
    }
    function showTooltipEvent(e) {
      const highlight = e.target.closest(".logic-checker-highlight");
      if (!highlight) return;
      const tooltipEl = ensureTooltip();
      const type = highlight.dataset["issueType"] ?? "issue";
      const explanation = highlight.dataset["issueExplanation"] ?? "";
      const importance = highlight.dataset["importance"] ?? "minor";
      tooltipEl.className = `logic-checker-tooltip ${importance}`;
      const _emoji = importance === "critical" ? "\u{1F534}" : importance === "significant" ? "\u{1F7E0}" : "\u{1F7E1}";
      let displayExplanation = explanation || "No explanation available";
      if (isMissInfoMode) {
        displayExplanation = makeKawaii(displayExplanation);
      }
      const formattedType = type.split(/[_-]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
      tooltipEl.innerHTML = `
      <div class="logic-checker-tooltip-badge">${escapeHtml(formattedType)}</div>
      <div class="logic-checker-tooltip-explanation">${escapeHtml(displayExplanation)}</div>
    `;
      positionTooltip(e, tooltipEl);
      tooltipEl.classList.add("visible");
    }
    function hideTooltip() {
      const tooltipEl = ensureTooltip();
      tooltipEl.classList.remove("visible");
    }
    function moveTooltip(e) {
      const tooltipEl = ensureTooltip();
      positionTooltip(e, tooltipEl);
    }
    function positionTooltip(e, tooltipElement) {
      const padding = 15;
      const tooltipRect = tooltipElement.getBoundingClientRect();
      let x = e.clientX + padding;
      let y = e.clientY + padding;
      if (x + tooltipRect.width > window.innerWidth - padding) {
        x = e.clientX - tooltipRect.width - padding;
      }
      if (y + tooltipRect.height > window.innerHeight - padding) {
        y = e.clientY - tooltipRect.height - padding;
      }
      tooltipElement.style.left = `${Math.max(padding, x)}px`;
      tooltipElement.style.top = `${Math.max(padding, y)}px`;
    }
    function escapeHtml(text) {
      if (!text) return "";
      const div = document.createElement("div");
      div.textContent = text;
      return div.innerHTML;
    }
    function clearHighlights() {
      if (USE_CSS_HIGHLIGHT_API) {
        clearCSSHighlights();
      }
      const highlights = document.querySelectorAll(".logic-checker-highlight");
      highlights.forEach((el) => {
        const parent = el.parentNode;
        if (!parent) return;
        while (el.firstChild) {
          parent.insertBefore(el.firstChild, el);
        }
        parent.removeChild(el);
      });
    }
    function highlightIssues(issues) {
      debug.log("Starting highlight process", {
        issueCount: issues.length,
        method: USE_CSS_HIGHLIGHT_API ? "CSS Highlight API" : "Span wrapping"
      }, "content-highlight");
      ensureTooltip();
      clearHighlights();
      let successCount = 0;
      issues.forEach((issue, index) => {
        if (!issue.quote) return;
        const matchInfo = findTextInPage(issue.quote);
        if (matchInfo) {
          const success = highlightRange(matchInfo, issue, index);
          if (success) successCount++;
        }
      });
      debug.log("Highlight process complete", {
        successCount,
        totalIssues: issues.length
      }, "content-highlight");
    }
    chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => {
      debug.log("Message received", { action: request.action }, "content-message");
      try {
        switch (request.action) {
          case "checkArticle": {
            const result = isArticlePage();
            sendResponse(result);
            break;
          }
          case "extractArticle": {
            const article = extractArticleText();
            const articleCheck = isArticlePage();
            const response = {
              ...article,
              ...articleCheck
            };
            sendResponse(response);
            break;
          }
          case "highlightIssues": {
            highlightIssues(request.issues);
            sendResponse({ success: true });
            break;
          }
          case "showFeedbackDialog": {
            showFeedbackDialog(request.selectedText, request.url, request.title);
            sendResponse({ success: true });
            break;
          }
          default:
            sendResponse({ error: "Unknown action" });
        }
      } catch (error) {
        debug.error("Error handling message", error, "content-message", {
          action: request.action
        });
        sendResponse({ error: error.message });
      }
      return true;
    });
    function showFeedbackDialog(selectedText, url, title) {
      const existing = document.querySelector(".logic-checker-annotation-overlay");
      if (existing) existing.remove();
      const articleText = extractArticleText().text || document.body.innerText.substring(0, 5e4);
      const overlay = document.createElement("div");
      overlay.className = "logic-checker-annotation-overlay";
      const dialog = document.createElement("div");
      dialog.className = "logic-checker-annotation-dialog";
      dialog.innerHTML = `
      <div class="logic-checker-annotation-header">
        <h2 class="logic-checker-annotation-title">\u{1F4AC} Leave Feedback</h2>
        <button class="logic-checker-annotation-close" aria-label="Close">&times;</button>
      </div>
      
      <div class="logic-checker-annotation-quote">"${escapeHtml(selectedText)}"</div>
      
      <label class="logic-checker-annotation-label">Your Feedback</label>
      <textarea 
        class="logic-checker-annotation-textarea" 
        id="lc-feedback-text"
        placeholder="Share your thoughts on this passage..."
      ></textarea>
      
      <div class="logic-checker-annotation-actions">
        <button class="logic-checker-annotation-btn logic-checker-annotation-btn-secondary" id="lc-cancel">
          Cancel
        </button>
        <button class="logic-checker-annotation-btn logic-checker-annotation-btn-primary" id="lc-submit">
          Submit
        </button>
      </div>
    `;
      overlay.appendChild(dialog);
      document.body.appendChild(overlay);
      const textarea = dialog.querySelector("#lc-feedback-text");
      setTimeout(() => textarea.focus(), 100);
      const close = () => {
        overlay.remove();
      };
      overlay.addEventListener("click", (e) => {
        if (e.target === overlay) close();
      });
      dialog.querySelector(".logic-checker-annotation-close")?.addEventListener("click", close);
      dialog.querySelector("#lc-cancel")?.addEventListener("click", close);
      dialog.querySelector("#lc-submit")?.addEventListener("click", () => {
        void (async () => {
          const feedbackText = textarea.value.trim();
          if (!feedbackText) {
            textarea.style.borderColor = "#ef4444";
            textarea.focus();
            return;
          }
          const submitBtn = dialog.querySelector("#lc-submit");
          submitBtn.disabled = true;
          submitBtn.textContent = "Submitting...";
          try {
            const response = await sendToBackground({
              action: "submitFeedback",
              data: {
                url,
                title,
                articleText,
                selectedText,
                commentText: feedbackText
              }
            });
            if (response.success) {
              const actionsEl = dialog.querySelector(".logic-checker-annotation-actions");
              if (actionsEl) {
                actionsEl.innerHTML = `
                <div class="logic-checker-annotation-success">
                  \u2705 Feedback submitted! Thank you.
                </div>
              `;
              }
              setTimeout(close, 1500);
            } else {
              throw new Error(response.error ?? "Failed to submit");
            }
          } catch (error) {
            debug.error("Failed to submit feedback", error, "content-feedback");
            const actionsEl = dialog.querySelector(".logic-checker-annotation-actions");
            if (actionsEl) {
              actionsEl.innerHTML = `
              <div class="logic-checker-annotation-error">
                \u274C ${escapeHtml(error.message)}
              </div>
              <button class="logic-checker-annotation-btn logic-checker-annotation-btn-secondary" id="lc-retry">
                Try Again
              </button>
            `;
            }
          }
        })();
      });
      const escHandler = (e) => {
        if (e.key === "Escape") {
          close();
          document.removeEventListener("keydown", escHandler);
        }
      };
      document.addEventListener("keydown", escHandler);
    }
    void tooltip;
  })();
})();
