monkey-gpt

monkeygpt

  1. // ==UserScript==
  2. // @name monkey-gpt
  3. // @namespace monkeygpt
  4. // @version 0.0.3
  5. // @author monkey
  6. // @icon https://jisuai.cn/logo.png
  7. // @match *://*/*
  8. // @require https://cdn.jsdelivr.net/npm/vue@3.4.38/dist/vue.global.prod.js
  9. // @grant GM_addStyle
  10. // @description monkeygpt
  11. // ==/UserScript==
  12.  
  13. (e => {
  14. if (typeof GM_addStyle == "function") {
  15. GM_addStyle(e);
  16. return
  17. }
  18. const o = document.createElement("style");
  19. o.textContent = e, document.head.append(o)
  20. })(" :root{font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%}#monkeygpt{position:fixed;top:0;right:0;z-index:10000;font-size:14px;line-height:2em}#monkeygpt xmp,#monkeygpt pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}#monkeygpt .loader{border:8px solid #f3f3f3;border-top:8px solid #3498db;border-radius:50%;width:60px;height:60px;animation:spin 1s linear infinite}#monkeygpt pre{background-color:#333;border-radius:5px;padding:.8em;font-family:Consolas,Courier New,monospace;font-size:14px;color:#c4d4d8;border:1px solid #e0e0e0}.monkeygpt-card{position:absolute;top:30vh;right:0;border-radius:.5em}.monkeygpt-card button{border:none;background:#3b5998;text-decoration:none;font-weight:700;color:#fff;cursor:pointer;width:auto;overflow:visible;padding:6px;font-size:14px;line-height:1.5em;font-family:Lucida Grande,Tahoma,Arial,Verdana,sans-serif;border-radius:.1em}.monkeygpt-warp{background-color:#fffc;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);width:30vw;height:100vh;margin:0 auto;box-shadow:-4px 4px 6px #0000001a;overflow:auto}.monkeygpt-warp .monkeygpt-card{top:0;padding:1.5em;width:100%;box-sizing:border-box}.monkeygpt-warp .monkeygpt-card button{padding:4px 15px;margin-right:8px;border-radius:2px;box-shadow:0 2px #0000000b}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media screen and (max-width: 1560px){.monkeygpt-warp{width:50vw}.monkeygpt-warp .monkeygpt-card{padding:2em}}@media screen and (max-width: 720px){.monkeygpt-warp{width:100vw}}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}}.monkeygpt-body[data-v-1db30056]{margin-top:1em}.title[data-v-1db30056]{display:flex;align-content:center;margin-bottom:1em}.title img[data-v-1db30056]{margin-left:1em}.close[data-v-1db30056]{width:1.5em;cursor:pointer}.nav[data-v-1db30056]{display:flex;justify-content:space-between}.loading[data-v-1db30056]{width:3em} ");
  21.  
  22. (function (vue) {
  23. 'use strict';
  24.  
  25. var __defProp = Object.defineProperty;
  26. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
  27. enumerable: true,
  28. configurable: true,
  29. writable: true,
  30. value
  31. }) : obj[key] = value;
  32. var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  33. var Readability$1 = {
  34. exports: {}
  35. };
  36. (function (module) {
  37. function Readability2(doc, options) {
  38. if (options && options.documentElement) {
  39. doc = options;
  40. options = arguments[2];
  41. } else if (!doc || !doc.documentElement) {
  42. throw new Error("First argument to Readability constructor should be a document object.");
  43. }
  44. options = options || {};
  45. this._doc = doc;
  46. this._docJSDOMParser = this._doc.firstChild.__JSDOMParser__;
  47. this._articleTitle = null;
  48. this._articleByline = null;
  49. this._articleDir = null;
  50. this._articleSiteName = null;
  51. this._attempts = [];
  52. this._debug = !!options.debug;
  53. this._maxElemsToParse = options.maxElemsToParse || this.DEFAULT_MAX_ELEMS_TO_PARSE;
  54. this._nbTopCandidates = options.nbTopCandidates || this.DEFAULT_N_TOP_CANDIDATES;
  55. this._charThreshold = options.charThreshold || this.DEFAULT_CHAR_THRESHOLD;
  56. this._classesToPreserve = this.CLASSES_TO_PRESERVE.concat(options.classesToPreserve || []);
  57. this._keepClasses = !!options.keepClasses;
  58. this._serializer = options.serializer || function (el) {
  59. return el.innerHTML;
  60. };
  61. this._disableJSONLD = !!options.disableJSONLD;
  62. this._allowedVideoRegex = options.allowedVideoRegex || this.REGEXPS.videos;
  63. this._flags = this.FLAG_STRIP_UNLIKELYS | this.FLAG_WEIGHT_CLASSES | this.FLAG_CLEAN_CONDITIONALLY;
  64. if (this._debug) {
  65. let logNode = function (node) {
  66. if (node.nodeType == node.TEXT_NODE) {
  67. return `${node.nodeName} ("${node.textContent}")`;
  68. }
  69. let attrPairs = Array.from(node.attributes || [], function (attr) {
  70. return `${attr.name}="${attr.value}"`;
  71. }).join(" ");
  72. return `<${node.localName} ${attrPairs}>`;
  73. };
  74. this.log = function () {
  75. if (typeof console !== "undefined") {
  76. let args = Array.from(arguments, (arg) => {
  77. if (arg && arg.nodeType == this.ELEMENT_NODE) {
  78. return logNode(arg);
  79. }
  80. return arg;
  81. });
  82. args.unshift("Reader: (Readability)");
  83. console.log.apply(console, args);
  84. } else if (typeof dump !== "undefined") {
  85. var msg = Array.prototype.map.call(arguments, function (x) {
  86. return x && x.nodeName ? logNode(x) : x;
  87. }).join(" ");
  88. dump("Reader: (Readability) " + msg + "\n");
  89. }
  90. };
  91. } else {
  92. this.log = function () {};
  93. }
  94. }
  95. Readability2.prototype = {
  96. FLAG_STRIP_UNLIKELYS: 1,
  97. FLAG_WEIGHT_CLASSES: 2,
  98. FLAG_CLEAN_CONDITIONALLY: 4,
  99. // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
  100. ELEMENT_NODE: 1,
  101. TEXT_NODE: 3,
  102. // Max number of nodes supported by this parser. Default: 0 (no limit)
  103. DEFAULT_MAX_ELEMS_TO_PARSE: 0,
  104. // The number of top candidates to consider when analysing how
  105. // tight the competition is among candidates.
  106. DEFAULT_N_TOP_CANDIDATES: 5,
  107. // Element tags to score by default.
  108. DEFAULT_TAGS_TO_SCORE: "section,h2,h3,h4,h5,h6,p,td,pre".toUpperCase().split(","),
  109. // The default number of chars an article must have in order to return a result
  110. DEFAULT_CHAR_THRESHOLD: 500,
  111. // All of the regular expressions in use within readability.
  112. // Defined up here so we don't instantiate them repeatedly in loops.
  113. REGEXPS: {
  114. // NOTE: These two regular expressions are duplicated in
  115. // Readability-readerable.js. Please keep both copies in sync.
  116. 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,
  117. okMaybeItsACandidate: /and|article|body|column|content|main|shadow/i,
  118. positive: /article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story/i,
  119. negative: /-ad-|hidden|^hid$| hid$| hid |^hid |banner|combx|comment|com-|contact|foot|footer|footnote|gdpr|masthead|media|meta|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|tool|widget/i,
  120. extraneous: /print|archive|comment|discuss|e[\-]?mail|share|reply|all|login|sign|single|utility/i,
  121. byline: /byline|author|dateline|writtenby|p-author/i,
  122. replaceFonts: /<(\/?)font[^>]*>/gi,
  123. normalize: /\s{2,}/g,
  124. videos: /\/\/(www\.)?((dailymotion|youtube|youtube-nocookie|player\.vimeo|v\.qq)\.com|(archive|upload\.wikimedia)\.org|player\.twitch\.tv)/i,
  125. shareElements: /(\b|_)(share|sharedaddy)(\b|_)/i,
  126. nextLink: /(next|weiter|continue|>([^\|]|$)|»([^\|]|$))/i,
  127. prevLink: /(prev|earl|old|new|<|«)/i,
  128. tokenize: /\W+/g,
  129. whitespace: /^\s*$/,
  130. hasContent: /\S$/,
  131. hashUrl: /^#.+/,
  132. srcsetUrl: /(\S+)(\s+[\d.]+[xw])?(\s*(?:,|$))/g,
  133. b64DataUrl: /^data:\s*([^\s;,]+)\s*;\s*base64\s*,/i,
  134. // Commas as used in Latin, Sindhi, Chinese and various other scripts.
  135. // see: https://en.wikipedia.org/wiki/Comma#Comma_variants
  136. commas: /\u002C|\u060C|\uFE50|\uFE10|\uFE11|\u2E41|\u2E34|\u2E32|\uFF0C/g,
  137. // See: https://schema.org/Article
  138. jsonLdArticleTypes: /^Article|AdvertiserContentArticle|NewsArticle|AnalysisNewsArticle|AskPublicNewsArticle|BackgroundNewsArticle|OpinionNewsArticle|ReportageNewsArticle|ReviewNewsArticle|Report|SatiricalArticle|ScholarlyArticle|MedicalScholarlyArticle|SocialMediaPosting|BlogPosting|LiveBlogPosting|DiscussionForumPosting|TechArticle|APIReference$/
  139. },
  140. UNLIKELY_ROLES: ["menu", "menubar", "complementary", "navigation", "alert", "alertdialog", "dialog"],
  141. DIV_TO_P_ELEMS: /* @__PURE__ */ new Set(["BLOCKQUOTE", "DL", "DIV", "IMG", "OL", "P", "PRE", "TABLE", "UL"]),
  142. ALTER_TO_DIV_EXCEPTIONS: ["DIV", "ARTICLE", "SECTION", "P"],
  143. PRESENTATIONAL_ATTRIBUTES: ["align", "background", "bgcolor", "border", "cellpadding", "cellspacing", "frame", "hspace", "rules", "style", "valign", "vspace"],
  144. DEPRECATED_SIZE_ATTRIBUTE_ELEMS: ["TABLE", "TH", "TD", "HR", "PRE"],
  145. // The commented out elements qualify as phrasing content but tend to be
  146. // removed by readability when put into paragraphs, so we ignore them here.
  147. PHRASING_ELEMS: [
  148. // "CANVAS", "IFRAME", "SVG", "VIDEO",
  149. "ABBR",
  150. "AUDIO",
  151. "B",
  152. "BDO",
  153. "BR",
  154. "BUTTON",
  155. "CITE",
  156. "CODE",
  157. "DATA",
  158. "DATALIST",
  159. "DFN",
  160. "EM",
  161. "EMBED",
  162. "I",
  163. "IMG",
  164. "INPUT",
  165. "KBD",
  166. "LABEL",
  167. "MARK",
  168. "MATH",
  169. "METER",
  170. "NOSCRIPT",
  171. "OBJECT",
  172. "OUTPUT",
  173. "PROGRESS",
  174. "Q",
  175. "RUBY",
  176. "SAMP",
  177. "SCRIPT",
  178. "SELECT",
  179. "SMALL",
  180. "SPAN",
  181. "STRONG",
  182. "SUB",
  183. "SUP",
  184. "TEXTAREA",
  185. "TIME",
  186. "VAR",
  187. "WBR"
  188. ],
  189. // These are the classes that readability sets itself.
  190. CLASSES_TO_PRESERVE: ["page"],
  191. // These are the list of HTML entities that need to be escaped.
  192. HTML_ESCAPE_MAP: {
  193. "lt": "<",
  194. "gt": ">",
  195. "amp": "&",
  196. "quot": '"',
  197. "apos": "'"
  198. },
  199. /**
  200. * Run any post-process modifications to article content as necessary.
  201. *
  202. * @param Element
  203. * @return void
  204. **/
  205. _postProcessContent: function (articleContent) {
  206. this._fixRelativeUris(articleContent);
  207. this._simplifyNestedElements(articleContent);
  208. if (!this._keepClasses) {
  209. this._cleanClasses(articleContent);
  210. }
  211. },
  212. /**
  213. * Iterates over a NodeList, calls `filterFn` for each node and removes node
  214. * if function returned `true`.
  215. *
  216. * If function is not passed, removes all the nodes in node list.
  217. *
  218. * @param NodeList nodeList The nodes to operate on
  219. * @param Function filterFn the function to use as a filter
  220. * @return void
  221. */
  222. _removeNodes: function (nodeList, filterFn) {
  223. if (this._docJSDOMParser && nodeList._isLiveNodeList) {
  224. throw new Error("Do not pass live node lists to _removeNodes");
  225. }
  226. for (var i = nodeList.length - 1; i >= 0; i--) {
  227. var node = nodeList[i];
  228. var parentNode = node.parentNode;
  229. if (parentNode) {
  230. if (!filterFn || filterFn.call(this, node, i, nodeList)) {
  231. parentNode.removeChild(node);
  232. }
  233. }
  234. }
  235. },
  236. /**
  237. * Iterates over a NodeList, and calls _setNodeTag for each node.
  238. *
  239. * @param NodeList nodeList The nodes to operate on
  240. * @param String newTagName the new tag name to use
  241. * @return void
  242. */
  243. _replaceNodeTags: function (nodeList, newTagName) {
  244. if (this._docJSDOMParser && nodeList._isLiveNodeList) {
  245. throw new Error("Do not pass live node lists to _replaceNodeTags");
  246. }
  247. for (const node of nodeList) {
  248. this._setNodeTag(node, newTagName);
  249. }
  250. },
  251. /**
  252. * Iterate over a NodeList, which doesn't natively fully implement the Array
  253. * interface.
  254. *
  255. * For convenience, the current object context is applied to the provided
  256. * iterate function.
  257. *
  258. * @param NodeList nodeList The NodeList.
  259. * @param Function fn The iterate function.
  260. * @return void
  261. */
  262. _forEachNode: function (nodeList, fn) {
  263. Array.prototype.forEach.call(nodeList, fn, this);
  264. },
  265. /**
  266. * Iterate over a NodeList, and return the first node that passes
  267. * the supplied test function
  268. *
  269. * For convenience, the current object context is applied to the provided
  270. * test function.
  271. *
  272. * @param NodeList nodeList The NodeList.
  273. * @param Function fn The test function.
  274. * @return void
  275. */
  276. _findNode: function (nodeList, fn) {
  277. return Array.prototype.find.call(nodeList, fn, this);
  278. },
  279. /**
  280. * Iterate over a NodeList, return true if any of the provided iterate
  281. * function calls returns true, false otherwise.
  282. *
  283. * For convenience, the current object context is applied to the
  284. * provided iterate function.
  285. *
  286. * @param NodeList nodeList The NodeList.
  287. * @param Function fn The iterate function.
  288. * @return Boolean
  289. */
  290. _someNode: function (nodeList, fn) {
  291. return Array.prototype.some.call(nodeList, fn, this);
  292. },
  293. /**
  294. * Iterate over a NodeList, return true if all of the provided iterate
  295. * function calls return true, false otherwise.
  296. *
  297. * For convenience, the current object context is applied to the
  298. * provided iterate function.
  299. *
  300. * @param NodeList nodeList The NodeList.
  301. * @param Function fn The iterate function.
  302. * @return Boolean
  303. */
  304. _everyNode: function (nodeList, fn) {
  305. return Array.prototype.every.call(nodeList, fn, this);
  306. },
  307. /**
  308. * Concat all nodelists passed as arguments.
  309. *
  310. * @return ...NodeList
  311. * @return Array
  312. */
  313. _concatNodeLists: function () {
  314. var slice = Array.prototype.slice;
  315. var args = slice.call(arguments);
  316. var nodeLists = args.map(function (list2) {
  317. return slice.call(list2);
  318. });
  319. return Array.prototype.concat.apply([], nodeLists);
  320. },
  321. _getAllNodesWithTag: function (node, tagNames) {
  322. if (node.querySelectorAll) {
  323. return node.querySelectorAll(tagNames.join(","));
  324. }
  325. return [].concat.apply([], tagNames.map(function (tag2) {
  326. var collection = node.getElementsByTagName(tag2);
  327. return Array.isArray(collection) ? collection : Array.from(collection);
  328. }));
  329. },
  330. /**
  331. * Removes the class="" attribute from every element in the given
  332. * subtree, except those that match CLASSES_TO_PRESERVE and
  333. * the classesToPreserve array from the options object.
  334. *
  335. * @param Element
  336. * @return void
  337. */
  338. _cleanClasses: function (node) {
  339. var classesToPreserve = this._classesToPreserve;
  340. var className = (node.getAttribute("class") || "").split(/\s+/).filter(function (cls) {
  341. return classesToPreserve.indexOf(cls) != -1;
  342. }).join(" ");
  343. if (className) {
  344. node.setAttribute("class", className);
  345. } else {
  346. node.removeAttribute("class");
  347. }
  348. for (node = node.firstElementChild; node; node = node.nextElementSibling) {
  349. this._cleanClasses(node);
  350. }
  351. },
  352. /**
  353. * Converts each <a> and <img> uri in the given element to an absolute URI,
  354. * ignoring #ref URIs.
  355. *
  356. * @param Element
  357. * @return void
  358. */
  359. _fixRelativeUris: function (articleContent) {
  360. var baseURI = this._doc.baseURI;
  361. var documentURI = this._doc.documentURI;
  362.  
  363. function toAbsoluteURI(uri) {
  364. if (baseURI == documentURI && uri.charAt(0) == "#") {
  365. return uri;
  366. }
  367. try {
  368. return new URL(uri, baseURI).href;
  369. } catch (ex) {}
  370. return uri;
  371. }
  372. var links = this._getAllNodesWithTag(articleContent, ["a"]);
  373. this._forEachNode(links, function (link2) {
  374. var href = link2.getAttribute("href");
  375. if (href) {
  376. if (href.indexOf("javascript:") === 0) {
  377. if (link2.childNodes.length === 1 && link2.childNodes[0].nodeType === this.TEXT_NODE) {
  378. var text = this._doc.createTextNode(link2.textContent);
  379. link2.parentNode.replaceChild(text, link2);
  380. } else {
  381. var container = this._doc.createElement("span");
  382. while (link2.firstChild) {
  383. container.appendChild(link2.firstChild);
  384. }
  385. link2.parentNode.replaceChild(container, link2);
  386. }
  387. } else {
  388. link2.setAttribute("href", toAbsoluteURI(href));
  389. }
  390. }
  391. });
  392. var medias = this._getAllNodesWithTag(articleContent, [
  393. "img",
  394. "picture",
  395. "figure",
  396. "video",
  397. "audio",
  398. "source"
  399. ]);
  400. this._forEachNode(medias, function (media) {
  401. var src = media.getAttribute("src");
  402. var poster = media.getAttribute("poster");
  403. var srcset = media.getAttribute("srcset");
  404. if (src) {
  405. media.setAttribute("src", toAbsoluteURI(src));
  406. }
  407. if (poster) {
  408. media.setAttribute("poster", toAbsoluteURI(poster));
  409. }
  410. if (srcset) {
  411. var newSrcset = srcset.replace(this.REGEXPS.srcsetUrl, function (_, p1, p2, p3) {
  412. return toAbsoluteURI(p1) + (p2 || "") + p3;
  413. });
  414. media.setAttribute("srcset", newSrcset);
  415. }
  416. });
  417. },
  418. _simplifyNestedElements: function (articleContent) {
  419. var node = articleContent;
  420. while (node) {
  421. if (node.parentNode && ["DIV", "SECTION"].includes(node.tagName) && !(node.id && node.id.startsWith("readability"))) {
  422. if (this._isElementWithoutContent(node)) {
  423. node = this._removeAndGetNext(node);
  424. continue;
  425. } else if (this._hasSingleTagInsideElement(node, "DIV") || this._hasSingleTagInsideElement(node, "SECTION")) {
  426. var child = node.children[0];
  427. for (var i = 0; i < node.attributes.length; i++) {
  428. child.setAttribute(node.attributes[i].name, node.attributes[i].value);
  429. }
  430. node.parentNode.replaceChild(child, node);
  431. node = child;
  432. continue;
  433. }
  434. }
  435. node = this._getNextNode(node);
  436. }
  437. },
  438. /**
  439. * Get the article title as an H1.
  440. *
  441. * @return string
  442. **/
  443. _getArticleTitle: function () {
  444. var doc = this._doc;
  445. var curTitle = "";
  446. var origTitle = "";
  447. try {
  448. curTitle = origTitle = doc.title.trim();
  449. if (typeof curTitle !== "string")
  450. curTitle = origTitle = this._getInnerText(doc.getElementsByTagName("title")[0]);
  451. } catch (e) {}
  452. var titleHadHierarchicalSeparators = false;
  453.  
  454. function wordCount(str) {
  455. return str.split(/\s+/).length;
  456. }
  457. if (/ [\|\-\\\/>»] /.test(curTitle)) {
  458. titleHadHierarchicalSeparators = / [\\\/>»] /.test(curTitle);
  459. curTitle = origTitle.replace(/(.*)[\|\-\\\/>»] .*/gi, "$1");
  460. if (wordCount(curTitle) < 3)
  461. curTitle = origTitle.replace(/[^\|\-\\\/>»]*[\|\-\\\/>»](.*)/gi, "$1");
  462. } else if (curTitle.indexOf(": ") !== -1) {
  463. var headings = this._concatNodeLists(
  464. doc.getElementsByTagName("h1"),
  465. doc.getElementsByTagName("h2")
  466. );
  467. var trimmedTitle = curTitle.trim();
  468. var match = this._someNode(headings, function (heading2) {
  469. return heading2.textContent.trim() === trimmedTitle;
  470. });
  471. if (!match) {
  472. curTitle = origTitle.substring(origTitle.lastIndexOf(":") + 1);
  473. if (wordCount(curTitle) < 3) {
  474. curTitle = origTitle.substring(origTitle.indexOf(":") + 1);
  475. } else if (wordCount(origTitle.substr(0, origTitle.indexOf(":"))) > 5) {
  476. curTitle = origTitle;
  477. }
  478. }
  479. } else if (curTitle.length > 150 || curTitle.length < 15) {
  480. var hOnes = doc.getElementsByTagName("h1");
  481. if (hOnes.length === 1)
  482. curTitle = this._getInnerText(hOnes[0]);
  483. }
  484. curTitle = curTitle.trim().replace(this.REGEXPS.normalize, " ");
  485. var curTitleWordCount = wordCount(curTitle);
  486. if (curTitleWordCount <= 4 && (!titleHadHierarchicalSeparators || curTitleWordCount != wordCount(origTitle.replace(/[\|\-\\\/>»]+/g, "")) - 1)) {
  487. curTitle = origTitle;
  488. }
  489. return curTitle;
  490. },
  491. /**
  492. * Prepare the HTML document for readability to scrape it.
  493. * This includes things like stripping javascript, CSS, and handling terrible markup.
  494. *
  495. * @return void
  496. **/
  497. _prepDocument: function () {
  498. var doc = this._doc;
  499. this._removeNodes(this._getAllNodesWithTag(doc, ["style"]));
  500. if (doc.body) {
  501. this._replaceBrs(doc.body);
  502. }
  503. this._replaceNodeTags(this._getAllNodesWithTag(doc, ["font"]), "SPAN");
  504. },
  505. /**
  506. * Finds the next node, starting from the given node, and ignoring
  507. * whitespace in between. If the given node is an element, the same node is
  508. * returned.
  509. */
  510. _nextNode: function (node) {
  511. var next = node;
  512. while (next && next.nodeType != this.ELEMENT_NODE && this.REGEXPS.whitespace.test(next.textContent)) {
  513. next = next.nextSibling;
  514. }
  515. return next;
  516. },
  517. /**
  518. * Replaces 2 or more successive <br> elements with a single <p>.
  519. * Whitespace between <br> elements are ignored. For example:
  520. * <div>foo<br>bar<br> <br><br>abc</div>
  521. * will become:
  522. * <div>foo<br>bar<p>abc</p></div>
  523. */
  524. _replaceBrs: function (elem) {
  525. this._forEachNode(this._getAllNodesWithTag(elem, ["br"]), function (br2) {
  526. var next = br2.nextSibling;
  527. var replaced = false;
  528. while ((next = this._nextNode(next)) && next.tagName == "BR") {
  529. replaced = true;
  530. var brSibling = next.nextSibling;
  531. next.parentNode.removeChild(next);
  532. next = brSibling;
  533. }
  534. if (replaced) {
  535. var p = this._doc.createElement("p");
  536. br2.parentNode.replaceChild(p, br2);
  537. next = p.nextSibling;
  538. while (next) {
  539. if (next.tagName == "BR") {
  540. var nextElem = this._nextNode(next.nextSibling);
  541. if (nextElem && nextElem.tagName == "BR")
  542. break;
  543. }
  544. if (!this._isPhrasingContent(next))
  545. break;
  546. var sibling = next.nextSibling;
  547. p.appendChild(next);
  548. next = sibling;
  549. }
  550. while (p.lastChild && this._isWhitespace(p.lastChild)) {
  551. p.removeChild(p.lastChild);
  552. }
  553. if (p.parentNode.tagName === "P")
  554. this._setNodeTag(p.parentNode, "DIV");
  555. }
  556. });
  557. },
  558. _setNodeTag: function (node, tag2) {
  559. this.log("_setNodeTag", node, tag2);
  560. if (this._docJSDOMParser) {
  561. node.localName = tag2.toLowerCase();
  562. node.tagName = tag2.toUpperCase();
  563. return node;
  564. }
  565. var replacement = node.ownerDocument.createElement(tag2);
  566. while (node.firstChild) {
  567. replacement.appendChild(node.firstChild);
  568. }
  569. node.parentNode.replaceChild(replacement, node);
  570. if (node.readability)
  571. replacement.readability = node.readability;
  572. for (var i = 0; i < node.attributes.length; i++) {
  573. try {
  574. replacement.setAttribute(node.attributes[i].name, node.attributes[i].value);
  575. } catch (ex) {}
  576. }
  577. return replacement;
  578. },
  579. /**
  580. * Prepare the article node for display. Clean out any inline styles,
  581. * iframes, forms, strip extraneous <p> tags, etc.
  582. *
  583. * @param Element
  584. * @return void
  585. **/
  586. _prepArticle: function (articleContent) {
  587. this._cleanStyles(articleContent);
  588. this._markDataTables(articleContent);
  589. this._fixLazyImages(articleContent);
  590. this._cleanConditionally(articleContent, "form");
  591. this._cleanConditionally(articleContent, "fieldset");
  592. this._clean(articleContent, "object");
  593. this._clean(articleContent, "embed");
  594. this._clean(articleContent, "footer");
  595. this._clean(articleContent, "link");
  596. this._clean(articleContent, "aside");
  597. var shareElementThreshold = this.DEFAULT_CHAR_THRESHOLD;
  598. this._forEachNode(articleContent.children, function (topCandidate) {
  599. this._cleanMatchedNodes(topCandidate, function (node, matchString) {
  600. return this.REGEXPS.shareElements.test(matchString) && node.textContent.length < shareElementThreshold;
  601. });
  602. });
  603. this._clean(articleContent, "iframe");
  604. this._clean(articleContent, "input");
  605. this._clean(articleContent, "textarea");
  606. this._clean(articleContent, "select");
  607. this._clean(articleContent, "button");
  608. this._cleanHeaders(articleContent);
  609. this._cleanConditionally(articleContent, "table");
  610. this._cleanConditionally(articleContent, "ul");
  611. this._cleanConditionally(articleContent, "div");
  612. this._replaceNodeTags(this._getAllNodesWithTag(articleContent, ["h1"]), "h2");
  613. this._removeNodes(this._getAllNodesWithTag(articleContent, ["p"]), function (paragraph2) {
  614. var imgCount = paragraph2.getElementsByTagName("img").length;
  615. var embedCount = paragraph2.getElementsByTagName("embed").length;
  616. var objectCount = paragraph2.getElementsByTagName("object").length;
  617. var iframeCount = paragraph2.getElementsByTagName("iframe").length;
  618. var totalCount = imgCount + embedCount + objectCount + iframeCount;
  619. return totalCount === 0 && !this._getInnerText(paragraph2, false);
  620. });
  621. this._forEachNode(this._getAllNodesWithTag(articleContent, ["br"]), function (br2) {
  622. var next = this._nextNode(br2.nextSibling);
  623. if (next && next.tagName == "P")
  624. br2.parentNode.removeChild(br2);
  625. });
  626. this._forEachNode(this._getAllNodesWithTag(articleContent, ["table"]), function (table) {
  627. var tbody = this._hasSingleTagInsideElement(table, "TBODY") ? table.firstElementChild : table;
  628. if (this._hasSingleTagInsideElement(tbody, "TR")) {
  629. var row = tbody.firstElementChild;
  630. if (this._hasSingleTagInsideElement(row, "TD")) {
  631. var cell = row.firstElementChild;
  632. cell = this._setNodeTag(cell, this._everyNode(cell.childNodes, this._isPhrasingContent) ? "P" : "DIV");
  633. table.parentNode.replaceChild(cell, table);
  634. }
  635. }
  636. });
  637. },
  638. /**
  639. * Initialize a node with the readability object. Also checks the
  640. * className/id for special names to add to its score.
  641. *
  642. * @param Element
  643. * @return void
  644. **/
  645. _initializeNode: function (node) {
  646. node.readability = {
  647. "contentScore": 0
  648. };
  649. switch (node.tagName) {
  650. case "DIV":
  651. node.readability.contentScore += 5;
  652. break;
  653. case "PRE":
  654. case "TD":
  655. case "BLOCKQUOTE":
  656. node.readability.contentScore += 3;
  657. break;
  658. case "ADDRESS":
  659. case "OL":
  660. case "UL":
  661. case "DL":
  662. case "DD":
  663. case "DT":
  664. case "LI":
  665. case "FORM":
  666. node.readability.contentScore -= 3;
  667. break;
  668. case "H1":
  669. case "H2":
  670. case "H3":
  671. case "H4":
  672. case "H5":
  673. case "H6":
  674. case "TH":
  675. node.readability.contentScore -= 5;
  676. break;
  677. }
  678. node.readability.contentScore += this._getClassWeight(node);
  679. },
  680. _removeAndGetNext: function (node) {
  681. var nextNode = this._getNextNode(node, true);
  682. node.parentNode.removeChild(node);
  683. return nextNode;
  684. },
  685. /**
  686. * Traverse the DOM from node to node, starting at the node passed in.
  687. * Pass true for the second parameter to indicate this node itself
  688. * (and its kids) are going away, and we want the next node over.
  689. *
  690. * Calling this in a loop will traverse the DOM depth-first.
  691. */
  692. _getNextNode: function (node, ignoreSelfAndKids) {
  693. if (!ignoreSelfAndKids && node.firstElementChild) {
  694. return node.firstElementChild;
  695. }
  696. if (node.nextElementSibling) {
  697. return node.nextElementSibling;
  698. }
  699. do {
  700. node = node.parentNode;
  701. } while (node && !node.nextElementSibling);
  702. return node && node.nextElementSibling;
  703. },
  704. // compares second text to first one
  705. // 1 = same text, 0 = completely different text
  706. // works the way that it splits both texts into words and then finds words that are unique in second text
  707. // the result is given by the lower length of unique parts
  708. _textSimilarity: function (textA, textB) {
  709. var tokensA = textA.toLowerCase().split(this.REGEXPS.tokenize).filter(Boolean);
  710. var tokensB = textB.toLowerCase().split(this.REGEXPS.tokenize).filter(Boolean);
  711. if (!tokensA.length || !tokensB.length) {
  712. return 0;
  713. }
  714. var uniqTokensB = tokensB.filter((token) => !tokensA.includes(token));
  715. var distanceB = uniqTokensB.join(" ").length / tokensB.join(" ").length;
  716. return 1 - distanceB;
  717. },
  718. _checkByline: function (node, matchString) {
  719. if (this._articleByline) {
  720. return false;
  721. }
  722. if (node.getAttribute !== void 0) {
  723. var rel = node.getAttribute("rel");
  724. var itemprop = node.getAttribute("itemprop");
  725. }
  726. if ((rel === "author" || itemprop && itemprop.indexOf("author") !== -1 || this.REGEXPS.byline.test(matchString)) && this._isValidByline(node.textContent)) {
  727. this._articleByline = node.textContent.trim();
  728. return true;
  729. }
  730. return false;
  731. },
  732. _getNodeAncestors: function (node, maxDepth) {
  733. maxDepth = maxDepth || 0;
  734. var i = 0,
  735. ancestors = [];
  736. while (node.parentNode) {
  737. ancestors.push(node.parentNode);
  738. if (maxDepth && ++i === maxDepth)
  739. break;
  740. node = node.parentNode;
  741. }
  742. return ancestors;
  743. },
  744. /***
  745. * grabArticle - Using a variety of metrics (content score, classname, element types), find the content that is
  746. * most likely to be the stuff a user wants to read. Then return it wrapped up in a div.
  747. *
  748. * @param page a document to run upon. Needs to be a full document, complete with body.
  749. * @return Element
  750. **/
  751. _grabArticle: function (page) {
  752. this.log("**** grabArticle ****");
  753. var doc = this._doc;
  754. var isPaging = page !== null;
  755. page = page ? page : this._doc.body;
  756. if (!page) {
  757. this.log("No body found in document. Abort.");
  758. return null;
  759. }
  760. var pageCacheHtml = page.innerHTML;
  761. while (true) {
  762. this.log("Starting grabArticle loop");
  763. var stripUnlikelyCandidates = this._flagIsActive(this.FLAG_STRIP_UNLIKELYS);
  764. var elementsToScore = [];
  765. var node = this._doc.documentElement;
  766. let shouldRemoveTitleHeader = true;
  767. while (node) {
  768. if (node.tagName === "HTML") {
  769. this._articleLang = node.getAttribute("lang");
  770. }
  771. var matchString = node.className + " " + node.id;
  772. if (!this._isProbablyVisible(node)) {
  773. this.log("Removing hidden node - " + matchString);
  774. node = this._removeAndGetNext(node);
  775. continue;
  776. }
  777. if (node.getAttribute("aria-modal") == "true" && node.getAttribute("role") == "dialog") {
  778. node = this._removeAndGetNext(node);
  779. continue;
  780. }
  781. if (this._checkByline(node, matchString)) {
  782. node = this._removeAndGetNext(node);
  783. continue;
  784. }
  785. if (shouldRemoveTitleHeader && this._headerDuplicatesTitle(node)) {
  786. this.log("Removing header: ", node.textContent.trim(), this._articleTitle.trim());
  787. shouldRemoveTitleHeader = false;
  788. node = this._removeAndGetNext(node);
  789. continue;
  790. }
  791. if (stripUnlikelyCandidates) {
  792. 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") {
  793. this.log("Removing unlikely candidate - " + matchString);
  794. node = this._removeAndGetNext(node);
  795. continue;
  796. }
  797. if (this.UNLIKELY_ROLES.includes(node.getAttribute("role"))) {
  798. this.log("Removing content with role " + node.getAttribute("role") + " - " + matchString);
  799. node = this._removeAndGetNext(node);
  800. continue;
  801. }
  802. }
  803. 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)) {
  804. node = this._removeAndGetNext(node);
  805. continue;
  806. }
  807. if (this.DEFAULT_TAGS_TO_SCORE.indexOf(node.tagName) !== -1) {
  808. elementsToScore.push(node);
  809. }
  810. if (node.tagName === "DIV") {
  811. var p = null;
  812. var childNode = node.firstChild;
  813. while (childNode) {
  814. var nextSibling = childNode.nextSibling;
  815. if (this._isPhrasingContent(childNode)) {
  816. if (p !== null) {
  817. p.appendChild(childNode);
  818. } else if (!this._isWhitespace(childNode)) {
  819. p = doc.createElement("p");
  820. node.replaceChild(p, childNode);
  821. p.appendChild(childNode);
  822. }
  823. } else if (p !== null) {
  824. while (p.lastChild && this._isWhitespace(p.lastChild)) {
  825. p.removeChild(p.lastChild);
  826. }
  827. p = null;
  828. }
  829. childNode = nextSibling;
  830. }
  831. if (this._hasSingleTagInsideElement(node, "P") && this._getLinkDensity(node) < 0.25) {
  832. var newNode = node.children[0];
  833. node.parentNode.replaceChild(newNode, node);
  834. node = newNode;
  835. elementsToScore.push(node);
  836. } else if (!this._hasChildBlockElement(node)) {
  837. node = this._setNodeTag(node, "P");
  838. elementsToScore.push(node);
  839. }
  840. }
  841. node = this._getNextNode(node);
  842. }
  843. var candidates = [];
  844. this._forEachNode(elementsToScore, function (elementToScore) {
  845. if (!elementToScore.parentNode || typeof elementToScore.parentNode.tagName === "undefined")
  846. return;
  847. var innerText = this._getInnerText(elementToScore);
  848. if (innerText.length < 25)
  849. return;
  850. var ancestors2 = this._getNodeAncestors(elementToScore, 5);
  851. if (ancestors2.length === 0)
  852. return;
  853. var contentScore = 0;
  854. contentScore += 1;
  855. contentScore += innerText.split(this.REGEXPS.commas).length;
  856. contentScore += Math.min(Math.floor(innerText.length / 100), 3);
  857. this._forEachNode(ancestors2, function (ancestor, level) {
  858. if (!ancestor.tagName || !ancestor.parentNode || typeof ancestor.parentNode.tagName === "undefined")
  859. return;
  860. if (typeof ancestor.readability === "undefined") {
  861. this._initializeNode(ancestor);
  862. candidates.push(ancestor);
  863. }
  864. if (level === 0)
  865. var scoreDivider = 1;
  866. else if (level === 1)
  867. scoreDivider = 2;
  868. else
  869. scoreDivider = level * 3;
  870. ancestor.readability.contentScore += contentScore / scoreDivider;
  871. });
  872. });
  873. var topCandidates = [];
  874. for (var c = 0, cl = candidates.length; c < cl; c += 1) {
  875. var candidate = candidates[c];
  876. var candidateScore = candidate.readability.contentScore * (1 - this._getLinkDensity(candidate));
  877. candidate.readability.contentScore = candidateScore;
  878. this.log("Candidate:", candidate, "with score " + candidateScore);
  879. for (var t = 0; t < this._nbTopCandidates; t++) {
  880. var aTopCandidate = topCandidates[t];
  881. if (!aTopCandidate || candidateScore > aTopCandidate.readability.contentScore) {
  882. topCandidates.splice(t, 0, candidate);
  883. if (topCandidates.length > this._nbTopCandidates)
  884. topCandidates.pop();
  885. break;
  886. }
  887. }
  888. }
  889. var topCandidate = topCandidates[0] || null;
  890. var neededToCreateTopCandidate = false;
  891. var parentOfTopCandidate;
  892. if (topCandidate === null || topCandidate.tagName === "BODY") {
  893. topCandidate = doc.createElement("DIV");
  894. neededToCreateTopCandidate = true;
  895. while (page.firstChild) {
  896. this.log("Moving child out:", page.firstChild);
  897. topCandidate.appendChild(page.firstChild);
  898. }
  899. page.appendChild(topCandidate);
  900. this._initializeNode(topCandidate);
  901. } else if (topCandidate) {
  902. var alternativeCandidateAncestors = [];
  903. for (var i = 1; i < topCandidates.length; i++) {
  904. if (topCandidates[i].readability.contentScore / topCandidate.readability.contentScore >= 0.75) {
  905. alternativeCandidateAncestors.push(this._getNodeAncestors(topCandidates[i]));
  906. }
  907. }
  908. var MINIMUM_TOPCANDIDATES = 3;
  909. if (alternativeCandidateAncestors.length >= MINIMUM_TOPCANDIDATES) {
  910. parentOfTopCandidate = topCandidate.parentNode;
  911. while (parentOfTopCandidate.tagName !== "BODY") {
  912. var listsContainingThisAncestor = 0;
  913. for (var ancestorIndex = 0; ancestorIndex < alternativeCandidateAncestors.length && listsContainingThisAncestor < MINIMUM_TOPCANDIDATES; ancestorIndex++) {
  914. listsContainingThisAncestor += Number(alternativeCandidateAncestors[ancestorIndex].includes(parentOfTopCandidate));
  915. }
  916. if (listsContainingThisAncestor >= MINIMUM_TOPCANDIDATES) {
  917. topCandidate = parentOfTopCandidate;
  918. break;
  919. }
  920. parentOfTopCandidate = parentOfTopCandidate.parentNode;
  921. }
  922. }
  923. if (!topCandidate.readability) {
  924. this._initializeNode(topCandidate);
  925. }
  926. parentOfTopCandidate = topCandidate.parentNode;
  927. var lastScore = topCandidate.readability.contentScore;
  928. var scoreThreshold = lastScore / 3;
  929. while (parentOfTopCandidate.tagName !== "BODY") {
  930. if (!parentOfTopCandidate.readability) {
  931. parentOfTopCandidate = parentOfTopCandidate.parentNode;
  932. continue;
  933. }
  934. var parentScore = parentOfTopCandidate.readability.contentScore;
  935. if (parentScore < scoreThreshold)
  936. break;
  937. if (parentScore > lastScore) {
  938. topCandidate = parentOfTopCandidate;
  939. break;
  940. }
  941. lastScore = parentOfTopCandidate.readability.contentScore;
  942. parentOfTopCandidate = parentOfTopCandidate.parentNode;
  943. }
  944. parentOfTopCandidate = topCandidate.parentNode;
  945. while (parentOfTopCandidate.tagName != "BODY" && parentOfTopCandidate.children.length == 1) {
  946. topCandidate = parentOfTopCandidate;
  947. parentOfTopCandidate = topCandidate.parentNode;
  948. }
  949. if (!topCandidate.readability) {
  950. this._initializeNode(topCandidate);
  951. }
  952. }
  953. var articleContent = doc.createElement("DIV");
  954. if (isPaging)
  955. articleContent.id = "readability-content";
  956. var siblingScoreThreshold = Math.max(10, topCandidate.readability.contentScore * 0.2);
  957. parentOfTopCandidate = topCandidate.parentNode;
  958. var siblings = parentOfTopCandidate.children;
  959. for (var s = 0, sl = siblings.length; s < sl; s++) {
  960. var sibling = siblings[s];
  961. var append = false;
  962. this.log("Looking at sibling node:", sibling, sibling.readability ? "with score " + sibling.readability.contentScore : "");
  963. this.log("Sibling has score", sibling.readability ? sibling.readability.contentScore : "Unknown");
  964. if (sibling === topCandidate) {
  965. append = true;
  966. } else {
  967. var contentBonus = 0;
  968. if (sibling.className === topCandidate.className && topCandidate.className !== "")
  969. contentBonus += topCandidate.readability.contentScore * 0.2;
  970. if (sibling.readability && sibling.readability.contentScore + contentBonus >= siblingScoreThreshold) {
  971. append = true;
  972. } else if (sibling.nodeName === "P") {
  973. var linkDensity = this._getLinkDensity(sibling);
  974. var nodeContent = this._getInnerText(sibling);
  975. var nodeLength = nodeContent.length;
  976. if (nodeLength > 80 && linkDensity < 0.25) {
  977. append = true;
  978. } else if (nodeLength < 80 && nodeLength > 0 && linkDensity === 0 && nodeContent.search(/\.( |$)/) !== -1) {
  979. append = true;
  980. }
  981. }
  982. }
  983. if (append) {
  984. this.log("Appending node:", sibling);
  985. if (this.ALTER_TO_DIV_EXCEPTIONS.indexOf(sibling.nodeName) === -1) {
  986. this.log("Altering sibling:", sibling, "to div.");
  987. sibling = this._setNodeTag(sibling, "DIV");
  988. }
  989. articleContent.appendChild(sibling);
  990. siblings = parentOfTopCandidate.children;
  991. s -= 1;
  992. sl -= 1;
  993. }
  994. }
  995. if (this._debug)
  996. this.log("Article content pre-prep: " + articleContent.innerHTML);
  997. this._prepArticle(articleContent);
  998. if (this._debug)
  999. this.log("Article content post-prep: " + articleContent.innerHTML);
  1000. if (neededToCreateTopCandidate) {
  1001. topCandidate.id = "readability-page-1";
  1002. topCandidate.className = "page";
  1003. } else {
  1004. var div = doc.createElement("DIV");
  1005. div.id = "readability-page-1";
  1006. div.className = "page";
  1007. while (articleContent.firstChild) {
  1008. div.appendChild(articleContent.firstChild);
  1009. }
  1010. articleContent.appendChild(div);
  1011. }
  1012. if (this._debug)
  1013. this.log("Article content after paging: " + articleContent.innerHTML);
  1014. var parseSuccessful = true;
  1015. var textLength = this._getInnerText(articleContent, true).length;
  1016. if (textLength < this._charThreshold) {
  1017. parseSuccessful = false;
  1018. page.innerHTML = pageCacheHtml;
  1019. if (this._flagIsActive(this.FLAG_STRIP_UNLIKELYS)) {
  1020. this._removeFlag(this.FLAG_STRIP_UNLIKELYS);
  1021. this._attempts.push({
  1022. articleContent,
  1023. textLength
  1024. });
  1025. } else if (this._flagIsActive(this.FLAG_WEIGHT_CLASSES)) {
  1026. this._removeFlag(this.FLAG_WEIGHT_CLASSES);
  1027. this._attempts.push({
  1028. articleContent,
  1029. textLength
  1030. });
  1031. } else if (this._flagIsActive(this.FLAG_CLEAN_CONDITIONALLY)) {
  1032. this._removeFlag(this.FLAG_CLEAN_CONDITIONALLY);
  1033. this._attempts.push({
  1034. articleContent,
  1035. textLength
  1036. });
  1037. } else {
  1038. this._attempts.push({
  1039. articleContent,
  1040. textLength
  1041. });
  1042. this._attempts.sort(function (a, b) {
  1043. return b.textLength - a.textLength;
  1044. });
  1045. if (!this._attempts[0].textLength) {
  1046. return null;
  1047. }
  1048. articleContent = this._attempts[0].articleContent;
  1049. parseSuccessful = true;
  1050. }
  1051. }
  1052. if (parseSuccessful) {
  1053. var ancestors = [parentOfTopCandidate, topCandidate].concat(this._getNodeAncestors(parentOfTopCandidate));
  1054. this._someNode(ancestors, function (ancestor) {
  1055. if (!ancestor.tagName)
  1056. return false;
  1057. var articleDir = ancestor.getAttribute("dir");
  1058. if (articleDir) {
  1059. this._articleDir = articleDir;
  1060. return true;
  1061. }
  1062. return false;
  1063. });
  1064. return articleContent;
  1065. }
  1066. }
  1067. },
  1068. /**
  1069. * Check whether the input string could be a byline.
  1070. * This verifies that the input is a string, and that the length
  1071. * is less than 100 chars.
  1072. *
  1073. * @param possibleByline {string} - a string to check whether its a byline.
  1074. * @return Boolean - whether the input string is a byline.
  1075. */
  1076. _isValidByline: function (byline) {
  1077. if (typeof byline == "string" || byline instanceof String) {
  1078. byline = byline.trim();
  1079. return byline.length > 0 && byline.length < 100;
  1080. }
  1081. return false;
  1082. },
  1083. /**
  1084. * Converts some of the common HTML entities in string to their corresponding characters.
  1085. *
  1086. * @param str {string} - a string to unescape.
  1087. * @return string without HTML entity.
  1088. */
  1089. _unescapeHtmlEntities: function (str) {
  1090. if (!str) {
  1091. return str;
  1092. }
  1093. var htmlEscapeMap = this.HTML_ESCAPE_MAP;
  1094. return str.replace(/&(quot|amp|apos|lt|gt);/g, function (_, tag2) {
  1095. return htmlEscapeMap[tag2];
  1096. }).replace(/&#(?:x([0-9a-z]{1,4})|([0-9]{1,4}));/gi, function (_, hex, numStr) {
  1097. var num = parseInt(hex || numStr, hex ? 16 : 10);
  1098. return String.fromCharCode(num);
  1099. });
  1100. },
  1101. /**
  1102. * Try to extract metadata from JSON-LD object.
  1103. * For now, only Schema.org objects of type Article or its subtypes are supported.
  1104. * @return Object with any metadata that could be extracted (possibly none)
  1105. */
  1106. _getJSONLD: function (doc) {
  1107. var scripts = this._getAllNodesWithTag(doc, ["script"]);
  1108. var metadata;
  1109. this._forEachNode(scripts, function (jsonLdElement) {
  1110. if (!metadata && jsonLdElement.getAttribute("type") === "application/ld+json") {
  1111. try {
  1112. var content = jsonLdElement.textContent.replace(/^\s*<!\[CDATA\[|\]\]>\s*$/g, "");
  1113. var parsed = JSON.parse(content);
  1114. if (!parsed["@context"] || !parsed["@context"].match(/^https?\:\/\/schema\.org$/)) {
  1115. return;
  1116. }
  1117. if (!parsed["@type"] && Array.isArray(parsed["@graph"])) {
  1118. parsed = parsed["@graph"].find(function (it) {
  1119. return (it["@type"] || "").match(
  1120. this.REGEXPS.jsonLdArticleTypes
  1121. );
  1122. });
  1123. }
  1124. if (!parsed || !parsed["@type"] || !parsed["@type"].match(this.REGEXPS.jsonLdArticleTypes)) {
  1125. return;
  1126. }
  1127. metadata = {};
  1128. if (typeof parsed.name === "string" && typeof parsed.headline === "string" && parsed.name !== parsed.headline) {
  1129. var title = this._getArticleTitle();
  1130. var nameMatches = this._textSimilarity(parsed.name, title) > 0.75;
  1131. var headlineMatches = this._textSimilarity(parsed.headline, title) > 0.75;
  1132. if (headlineMatches && !nameMatches) {
  1133. metadata.title = parsed.headline;
  1134. } else {
  1135. metadata.title = parsed.name;
  1136. }
  1137. } else if (typeof parsed.name === "string") {
  1138. metadata.title = parsed.name.trim();
  1139. } else if (typeof parsed.headline === "string") {
  1140. metadata.title = parsed.headline.trim();
  1141. }
  1142. if (parsed.author) {
  1143. if (typeof parsed.author.name === "string") {
  1144. metadata.byline = parsed.author.name.trim();
  1145. } else if (Array.isArray(parsed.author) && parsed.author[0] && typeof parsed.author[0].name === "string") {
  1146. metadata.byline = parsed.author.filter(function (author) {
  1147. return author && typeof author.name === "string";
  1148. }).map(function (author) {
  1149. return author.name.trim();
  1150. }).join(", ");
  1151. }
  1152. }
  1153. if (typeof parsed.description === "string") {
  1154. metadata.excerpt = parsed.description.trim();
  1155. }
  1156. if (parsed.publisher && typeof parsed.publisher.name === "string") {
  1157. metadata.siteName = parsed.publisher.name.trim();
  1158. }
  1159. if (typeof parsed.datePublished === "string") {
  1160. metadata.datePublished = parsed.datePublished.trim();
  1161. }
  1162. return;
  1163. } catch (err) {
  1164. this.log(err.message);
  1165. }
  1166. }
  1167. });
  1168. return metadata ? metadata : {};
  1169. },
  1170. /**
  1171. * Attempts to get excerpt and byline metadata for the article.
  1172. *
  1173. * @param {Object} jsonld — object containing any metadata that
  1174. * could be extracted from JSON-LD object.
  1175. *
  1176. * @return Object with optional "excerpt" and "byline" properties
  1177. */
  1178. _getArticleMetadata: function (jsonld) {
  1179. var metadata = {};
  1180. var values = {};
  1181. var metaElements = this._doc.getElementsByTagName("meta");
  1182. var propertyPattern = /\s*(article|dc|dcterm|og|twitter)\s*:\s*(author|creator|description|published_time|title|site_name)\s*/gi;
  1183. var namePattern = /^\s*(?:(dc|dcterm|og|twitter|weibo:(article|webpage))\s*[\.:]\s*)?(author|creator|description|title|site_name)\s*$/i;
  1184. this._forEachNode(metaElements, function (element) {
  1185. var elementName = element.getAttribute("name");
  1186. var elementProperty = element.getAttribute("property");
  1187. var content = element.getAttribute("content");
  1188. if (!content) {
  1189. return;
  1190. }
  1191. var matches = null;
  1192. var name = null;
  1193. if (elementProperty) {
  1194. matches = elementProperty.match(propertyPattern);
  1195. if (matches) {
  1196. name = matches[0].toLowerCase().replace(/\s/g, "");
  1197. values[name] = content.trim();
  1198. }
  1199. }
  1200. if (!matches && elementName && namePattern.test(elementName)) {
  1201. name = elementName;
  1202. if (content) {
  1203. name = name.toLowerCase().replace(/\s/g, "").replace(/\./g, ":");
  1204. values[name] = content.trim();
  1205. }
  1206. }
  1207. });
  1208. 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"];
  1209. if (!metadata.title) {
  1210. metadata.title = this._getArticleTitle();
  1211. }
  1212. metadata.byline = jsonld.byline || values["dc:creator"] || values["dcterm:creator"] || values["author"];
  1213. 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"];
  1214. metadata.siteName = jsonld.siteName || values["og:site_name"];
  1215. metadata.publishedTime = jsonld.datePublished || values["article:published_time"] || null;
  1216. metadata.title = this._unescapeHtmlEntities(metadata.title);
  1217. metadata.byline = this._unescapeHtmlEntities(metadata.byline);
  1218. metadata.excerpt = this._unescapeHtmlEntities(metadata.excerpt);
  1219. metadata.siteName = this._unescapeHtmlEntities(metadata.siteName);
  1220. metadata.publishedTime = this._unescapeHtmlEntities(metadata.publishedTime);
  1221. return metadata;
  1222. },
  1223. /**
  1224. * Check if node is image, or if node contains exactly only one image
  1225. * whether as a direct child or as its descendants.
  1226. *
  1227. * @param Element
  1228. **/
  1229. _isSingleImage: function (node) {
  1230. if (node.tagName === "IMG") {
  1231. return true;
  1232. }
  1233. if (node.children.length !== 1 || node.textContent.trim() !== "") {
  1234. return false;
  1235. }
  1236. return this._isSingleImage(node.children[0]);
  1237. },
  1238. /**
  1239. * Find all <noscript> that are located after <img> nodes, and which contain only one
  1240. * <img> element. Replace the first image with the image from inside the <noscript> tag,
  1241. * and remove the <noscript> tag. This improves the quality of the images we use on
  1242. * some sites (e.g. Medium).
  1243. *
  1244. * @param Element
  1245. **/
  1246. _unwrapNoscriptImages: function (doc) {
  1247. var imgs = Array.from(doc.getElementsByTagName("img"));
  1248. this._forEachNode(imgs, function (img) {
  1249. for (var i = 0; i < img.attributes.length; i++) {
  1250. var attr = img.attributes[i];
  1251. switch (attr.name) {
  1252. case "src":
  1253. case "srcset":
  1254. case "data-src":
  1255. case "data-srcset":
  1256. return;
  1257. }
  1258. if (/\.(jpg|jpeg|png|webp)/i.test(attr.value)) {
  1259. return;
  1260. }
  1261. }
  1262. img.parentNode.removeChild(img);
  1263. });
  1264. var noscripts = Array.from(doc.getElementsByTagName("noscript"));
  1265. this._forEachNode(noscripts, function (noscript) {
  1266. var tmp = doc.createElement("div");
  1267. tmp.innerHTML = noscript.innerHTML;
  1268. if (!this._isSingleImage(tmp)) {
  1269. return;
  1270. }
  1271. var prevElement = noscript.previousElementSibling;
  1272. if (prevElement && this._isSingleImage(prevElement)) {
  1273. var prevImg = prevElement;
  1274. if (prevImg.tagName !== "IMG") {
  1275. prevImg = prevElement.getElementsByTagName("img")[0];
  1276. }
  1277. var newImg = tmp.getElementsByTagName("img")[0];
  1278. for (var i = 0; i < prevImg.attributes.length; i++) {
  1279. var attr = prevImg.attributes[i];
  1280. if (attr.value === "") {
  1281. continue;
  1282. }
  1283. if (attr.name === "src" || attr.name === "srcset" || /\.(jpg|jpeg|png|webp)/i.test(attr.value)) {
  1284. if (newImg.getAttribute(attr.name) === attr.value) {
  1285. continue;
  1286. }
  1287. var attrName = attr.name;
  1288. if (newImg.hasAttribute(attrName)) {
  1289. attrName = "data-old-" + attrName;
  1290. }
  1291. newImg.setAttribute(attrName, attr.value);
  1292. }
  1293. }
  1294. noscript.parentNode.replaceChild(tmp.firstElementChild, prevElement);
  1295. }
  1296. });
  1297. },
  1298. /**
  1299. * Removes script tags from the document.
  1300. *
  1301. * @param Element
  1302. **/
  1303. _removeScripts: function (doc) {
  1304. this._removeNodes(this._getAllNodesWithTag(doc, ["script", "noscript"]));
  1305. },
  1306. /**
  1307. * Check if this node has only whitespace and a single element with given tag
  1308. * Returns false if the DIV node contains non-empty text nodes
  1309. * or if it contains no element with given tag or more than 1 element.
  1310. *
  1311. * @param Element
  1312. * @param string tag of child element
  1313. **/
  1314. _hasSingleTagInsideElement: function (element, tag2) {
  1315. if (element.children.length != 1 || element.children[0].tagName !== tag2) {
  1316. return false;
  1317. }
  1318. return !this._someNode(element.childNodes, function (node) {
  1319. return node.nodeType === this.TEXT_NODE && this.REGEXPS.hasContent.test(node.textContent);
  1320. });
  1321. },
  1322. _isElementWithoutContent: function (node) {
  1323. return node.nodeType === this.ELEMENT_NODE && node.textContent.trim().length == 0 && (node.children.length == 0 || node.children.length == node.getElementsByTagName("br").length + node.getElementsByTagName("hr").length);
  1324. },
  1325. /**
  1326. * Determine whether element has any children block level elements.
  1327. *
  1328. * @param Element
  1329. */
  1330. _hasChildBlockElement: function (element) {
  1331. return this._someNode(element.childNodes, function (node) {
  1332. return this.DIV_TO_P_ELEMS.has(node.tagName) || this._hasChildBlockElement(node);
  1333. });
  1334. },
  1335. /***
  1336. * Determine if a node qualifies as phrasing content.
  1337. * https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content
  1338. **/
  1339. _isPhrasingContent: function (node) {
  1340. return node.nodeType === this.TEXT_NODE || this.PHRASING_ELEMS.indexOf(node.tagName) !== -1 || (node.tagName === "A" || node.tagName === "DEL" || node.tagName === "INS") && this._everyNode(node.childNodes, this._isPhrasingContent);
  1341. },
  1342. _isWhitespace: function (node) {
  1343. return node.nodeType === this.TEXT_NODE && node.textContent.trim().length === 0 || node.nodeType === this.ELEMENT_NODE && node.tagName === "BR";
  1344. },
  1345. /**
  1346. * Get the inner text of a node - cross browser compatibly.
  1347. * This also strips out any excess whitespace to be found.
  1348. *
  1349. * @param Element
  1350. * @param Boolean normalizeSpaces (default: true)
  1351. * @return string
  1352. **/
  1353. _getInnerText: function (e, normalizeSpaces) {
  1354. normalizeSpaces = typeof normalizeSpaces === "undefined" ? true : normalizeSpaces;
  1355. var textContent = e.textContent.trim();
  1356. if (normalizeSpaces) {
  1357. return textContent.replace(this.REGEXPS.normalize, " ");
  1358. }
  1359. return textContent;
  1360. },
  1361. /**
  1362. * Get the number of times a string s appears in the node e.
  1363. *
  1364. * @param Element
  1365. * @param string - what to split on. Default is ","
  1366. * @return number (integer)
  1367. **/
  1368. _getCharCount: function (e, s) {
  1369. s = s || ",";
  1370. return this._getInnerText(e).split(s).length - 1;
  1371. },
  1372. /**
  1373. * Remove the style attribute on every e and under.
  1374. * TODO: Test if getElementsByTagName(*) is faster.
  1375. *
  1376. * @param Element
  1377. * @return void
  1378. **/
  1379. _cleanStyles: function (e) {
  1380. if (!e || e.tagName.toLowerCase() === "svg")
  1381. return;
  1382. for (var i = 0; i < this.PRESENTATIONAL_ATTRIBUTES.length; i++) {
  1383. e.removeAttribute(this.PRESENTATIONAL_ATTRIBUTES[i]);
  1384. }
  1385. if (this.DEPRECATED_SIZE_ATTRIBUTE_ELEMS.indexOf(e.tagName) !== -1) {
  1386. e.removeAttribute("width");
  1387. e.removeAttribute("height");
  1388. }
  1389. var cur = e.firstElementChild;
  1390. while (cur !== null) {
  1391. this._cleanStyles(cur);
  1392. cur = cur.nextElementSibling;
  1393. }
  1394. },
  1395. /**
  1396. * Get the density of links as a percentage of the content
  1397. * This is the amount of text that is inside a link divided by the total text in the node.
  1398. *
  1399. * @param Element
  1400. * @return number (float)
  1401. **/
  1402. _getLinkDensity: function (element) {
  1403. var textLength = this._getInnerText(element).length;
  1404. if (textLength === 0)
  1405. return 0;
  1406. var linkLength = 0;
  1407. this._forEachNode(element.getElementsByTagName("a"), function (linkNode) {
  1408. var href = linkNode.getAttribute("href");
  1409. var coefficient = href && this.REGEXPS.hashUrl.test(href) ? 0.3 : 1;
  1410. linkLength += this._getInnerText(linkNode).length * coefficient;
  1411. });
  1412. return linkLength / textLength;
  1413. },
  1414. /**
  1415. * Get an elements class/id weight. Uses regular expressions to tell if this
  1416. * element looks good or bad.
  1417. *
  1418. * @param Element
  1419. * @return number (Integer)
  1420. **/
  1421. _getClassWeight: function (e) {
  1422. if (!this._flagIsActive(this.FLAG_WEIGHT_CLASSES))
  1423. return 0;
  1424. var weight = 0;
  1425. if (typeof e.className === "string" && e.className !== "") {
  1426. if (this.REGEXPS.negative.test(e.className))
  1427. weight -= 25;
  1428. if (this.REGEXPS.positive.test(e.className))
  1429. weight += 25;
  1430. }
  1431. if (typeof e.id === "string" && e.id !== "") {
  1432. if (this.REGEXPS.negative.test(e.id))
  1433. weight -= 25;
  1434. if (this.REGEXPS.positive.test(e.id))
  1435. weight += 25;
  1436. }
  1437. return weight;
  1438. },
  1439. /**
  1440. * Clean a node of all elements of type "tag".
  1441. * (Unless it's a youtube/vimeo video. People love movies.)
  1442. *
  1443. * @param Element
  1444. * @param string tag to clean
  1445. * @return void
  1446. **/
  1447. _clean: function (e, tag2) {
  1448. var isEmbed = ["object", "embed", "iframe"].indexOf(tag2) !== -1;
  1449. this._removeNodes(this._getAllNodesWithTag(e, [tag2]), function (element) {
  1450. if (isEmbed) {
  1451. for (var i = 0; i < element.attributes.length; i++) {
  1452. if (this._allowedVideoRegex.test(element.attributes[i].value)) {
  1453. return false;
  1454. }
  1455. }
  1456. if (element.tagName === "object" && this._allowedVideoRegex.test(element.innerHTML)) {
  1457. return false;
  1458. }
  1459. }
  1460. return true;
  1461. });
  1462. },
  1463. /**
  1464. * Check if a given node has one of its ancestor tag name matching the
  1465. * provided one.
  1466. * @param HTMLElement node
  1467. * @param String tagName
  1468. * @param Number maxDepth
  1469. * @param Function filterFn a filter to invoke to determine whether this node 'counts'
  1470. * @return Boolean
  1471. */
  1472. _hasAncestorTag: function (node, tagName, maxDepth, filterFn) {
  1473. maxDepth = maxDepth || 3;
  1474. tagName = tagName.toUpperCase();
  1475. var depth = 0;
  1476. while (node.parentNode) {
  1477. if (maxDepth > 0 && depth > maxDepth)
  1478. return false;
  1479. if (node.parentNode.tagName === tagName && (!filterFn || filterFn(node.parentNode)))
  1480. return true;
  1481. node = node.parentNode;
  1482. depth++;
  1483. }
  1484. return false;
  1485. },
  1486. /**
  1487. * Return an object indicating how many rows and columns this table has.
  1488. */
  1489. _getRowAndColumnCount: function (table) {
  1490. var rows = 0;
  1491. var columns = 0;
  1492. var trs = table.getElementsByTagName("tr");
  1493. for (var i = 0; i < trs.length; i++) {
  1494. var rowspan = trs[i].getAttribute("rowspan") || 0;
  1495. if (rowspan) {
  1496. rowspan = parseInt(rowspan, 10);
  1497. }
  1498. rows += rowspan || 1;
  1499. var columnsInThisRow = 0;
  1500. var cells = trs[i].getElementsByTagName("td");
  1501. for (var j = 0; j < cells.length; j++) {
  1502. var colspan = cells[j].getAttribute("colspan") || 0;
  1503. if (colspan) {
  1504. colspan = parseInt(colspan, 10);
  1505. }
  1506. columnsInThisRow += colspan || 1;
  1507. }
  1508. columns = Math.max(columns, columnsInThisRow);
  1509. }
  1510. return {
  1511. rows,
  1512. columns
  1513. };
  1514. },
  1515. /**
  1516. * Look for 'data' (as opposed to 'layout') tables, for which we use
  1517. * similar checks as
  1518. * https://searchfox.org/mozilla-central/rev/f82d5c549f046cb64ce5602bfd894b7ae807c8f8/accessible/generic/TableAccessible.cpp#19
  1519. */
  1520. _markDataTables: function (root) {
  1521. var tables = root.getElementsByTagName("table");
  1522. for (var i = 0; i < tables.length; i++) {
  1523. var table = tables[i];
  1524. var role = table.getAttribute("role");
  1525. if (role == "presentation") {
  1526. table._readabilityDataTable = false;
  1527. continue;
  1528. }
  1529. var datatable = table.getAttribute("datatable");
  1530. if (datatable == "0") {
  1531. table._readabilityDataTable = false;
  1532. continue;
  1533. }
  1534. var summary = table.getAttribute("summary");
  1535. if (summary) {
  1536. table._readabilityDataTable = true;
  1537. continue;
  1538. }
  1539. var caption = table.getElementsByTagName("caption")[0];
  1540. if (caption && caption.childNodes.length > 0) {
  1541. table._readabilityDataTable = true;
  1542. continue;
  1543. }
  1544. var dataTableDescendants = ["col", "colgroup", "tfoot", "thead", "th"];
  1545. var descendantExists = function (tag2) {
  1546. return !!table.getElementsByTagName(tag2)[0];
  1547. };
  1548. if (dataTableDescendants.some(descendantExists)) {
  1549. this.log("Data table because found data-y descendant");
  1550. table._readabilityDataTable = true;
  1551. continue;
  1552. }
  1553. if (table.getElementsByTagName("table")[0]) {
  1554. table._readabilityDataTable = false;
  1555. continue;
  1556. }
  1557. var sizeInfo = this._getRowAndColumnCount(table);
  1558. if (sizeInfo.rows >= 10 || sizeInfo.columns > 4) {
  1559. table._readabilityDataTable = true;
  1560. continue;
  1561. }
  1562. table._readabilityDataTable = sizeInfo.rows * sizeInfo.columns > 10;
  1563. }
  1564. },
  1565. /* convert images and figures that have properties like data-src into images that can be loaded without JS */
  1566. _fixLazyImages: function (root) {
  1567. this._forEachNode(this._getAllNodesWithTag(root, ["img", "picture", "figure"]), function (elem) {
  1568. if (elem.src && this.REGEXPS.b64DataUrl.test(elem.src)) {
  1569. var parts = this.REGEXPS.b64DataUrl.exec(elem.src);
  1570. if (parts[1] === "image/svg+xml") {
  1571. return;
  1572. }
  1573. var srcCouldBeRemoved = false;
  1574. for (var i = 0; i < elem.attributes.length; i++) {
  1575. var attr = elem.attributes[i];
  1576. if (attr.name === "src") {
  1577. continue;
  1578. }
  1579. if (/\.(jpg|jpeg|png|webp)/i.test(attr.value)) {
  1580. srcCouldBeRemoved = true;
  1581. break;
  1582. }
  1583. }
  1584. if (srcCouldBeRemoved) {
  1585. var b64starts = elem.src.search(/base64\s*/i) + 7;
  1586. var b64length = elem.src.length - b64starts;
  1587. if (b64length < 133) {
  1588. elem.removeAttribute("src");
  1589. }
  1590. }
  1591. }
  1592. if ((elem.src || elem.srcset && elem.srcset != "null") && elem.className.toLowerCase().indexOf("lazy") === -1) {
  1593. return;
  1594. }
  1595. for (var j = 0; j < elem.attributes.length; j++) {
  1596. attr = elem.attributes[j];
  1597. if (attr.name === "src" || attr.name === "srcset" || attr.name === "alt") {
  1598. continue;
  1599. }
  1600. var copyTo = null;
  1601. if (/\.(jpg|jpeg|png|webp)\s+\d/.test(attr.value)) {
  1602. copyTo = "srcset";
  1603. } else if (/^\s*\S+\.(jpg|jpeg|png|webp)\S*\s*$/.test(attr.value)) {
  1604. copyTo = "src";
  1605. }
  1606. if (copyTo) {
  1607. if (elem.tagName === "IMG" || elem.tagName === "PICTURE") {
  1608. elem.setAttribute(copyTo, attr.value);
  1609. } else if (elem.tagName === "FIGURE" && !this._getAllNodesWithTag(elem, ["img", "picture"]).length) {
  1610. var img = this._doc.createElement("img");
  1611. img.setAttribute(copyTo, attr.value);
  1612. elem.appendChild(img);
  1613. }
  1614. }
  1615. }
  1616. });
  1617. },
  1618. _getTextDensity: function (e, tags) {
  1619. var textLength = this._getInnerText(e, true).length;
  1620. if (textLength === 0) {
  1621. return 0;
  1622. }
  1623. var childrenLength = 0;
  1624. var children = this._getAllNodesWithTag(e, tags);
  1625. this._forEachNode(children, (child) => childrenLength += this._getInnerText(child, true).length);
  1626. return childrenLength / textLength;
  1627. },
  1628. /**
  1629. * Clean an element of all tags of type "tag" if they look fishy.
  1630. * "Fishy" is an algorithm based on content length, classnames, link density, number of images & embeds, etc.
  1631. *
  1632. * @return void
  1633. **/
  1634. _cleanConditionally: function (e, tag2) {
  1635. if (!this._flagIsActive(this.FLAG_CLEAN_CONDITIONALLY))
  1636. return;
  1637. this._removeNodes(this._getAllNodesWithTag(e, [tag2]), function (node) {
  1638. var isDataTable = function (t) {
  1639. return t._readabilityDataTable;
  1640. };
  1641. var isList = tag2 === "ul" || tag2 === "ol";
  1642. if (!isList) {
  1643. var listLength = 0;
  1644. var listNodes = this._getAllNodesWithTag(node, ["ul", "ol"]);
  1645. this._forEachNode(listNodes, (list2) => listLength += this._getInnerText(list2).length);
  1646. isList = listLength / this._getInnerText(node).length > 0.9;
  1647. }
  1648. if (tag2 === "table" && isDataTable(node)) {
  1649. return false;
  1650. }
  1651. if (this._hasAncestorTag(node, "table", -1, isDataTable)) {
  1652. return false;
  1653. }
  1654. if (this._hasAncestorTag(node, "code")) {
  1655. return false;
  1656. }
  1657. var weight = this._getClassWeight(node);
  1658. this.log("Cleaning Conditionally", node);
  1659. var contentScore = 0;
  1660. if (weight + contentScore < 0) {
  1661. return true;
  1662. }
  1663. if (this._getCharCount(node, ",") < 10) {
  1664. var p = node.getElementsByTagName("p").length;
  1665. var img = node.getElementsByTagName("img").length;
  1666. var li = node.getElementsByTagName("li").length - 100;
  1667. var input = node.getElementsByTagName("input").length;
  1668. var headingDensity = this._getTextDensity(node, ["h1", "h2", "h3", "h4", "h5", "h6"]);
  1669. var embedCount = 0;
  1670. var embeds = this._getAllNodesWithTag(node, ["object", "embed", "iframe"]);
  1671. for (var i = 0; i < embeds.length; i++) {
  1672. for (var j = 0; j < embeds[i].attributes.length; j++) {
  1673. if (this._allowedVideoRegex.test(embeds[i].attributes[j].value)) {
  1674. return false;
  1675. }
  1676. }
  1677. if (embeds[i].tagName === "object" && this._allowedVideoRegex.test(embeds[i].innerHTML)) {
  1678. return false;
  1679. }
  1680. embedCount++;
  1681. }
  1682. var linkDensity = this._getLinkDensity(node);
  1683. var contentLength = this._getInnerText(node).length;
  1684. var haveToRemove = img > 1 && p / img < 0.5 && !this._hasAncestorTag(node, "figure") || !isList && li > p || input > Math.floor(p / 3) || !isList && headingDensity < 0.9 && contentLength < 25 && (img === 0 || img > 2) && !this._hasAncestorTag(node, "figure") || !isList && weight < 25 && linkDensity > 0.2 || weight >= 25 && linkDensity > 0.5 || (embedCount === 1 && contentLength < 75 || embedCount > 1);
  1685. if (isList && haveToRemove) {
  1686. for (var x = 0; x < node.children.length; x++) {
  1687. let child = node.children[x];
  1688. if (child.children.length > 1) {
  1689. return haveToRemove;
  1690. }
  1691. }
  1692. let li_count = node.getElementsByTagName("li").length;
  1693. if (img == li_count) {
  1694. return false;
  1695. }
  1696. }
  1697. return haveToRemove;
  1698. }
  1699. return false;
  1700. });
  1701. },
  1702. /**
  1703. * Clean out elements that match the specified conditions
  1704. *
  1705. * @param Element
  1706. * @param Function determines whether a node should be removed
  1707. * @return void
  1708. **/
  1709. _cleanMatchedNodes: function (e, filter) {
  1710. var endOfSearchMarkerNode = this._getNextNode(e, true);
  1711. var next = this._getNextNode(e);
  1712. while (next && next != endOfSearchMarkerNode) {
  1713. if (filter.call(this, next, next.className + " " + next.id)) {
  1714. next = this._removeAndGetNext(next);
  1715. } else {
  1716. next = this._getNextNode(next);
  1717. }
  1718. }
  1719. },
  1720. /**
  1721. * Clean out spurious headers from an Element.
  1722. *
  1723. * @param Element
  1724. * @return void
  1725. **/
  1726. _cleanHeaders: function (e) {
  1727. let headingNodes = this._getAllNodesWithTag(e, ["h1", "h2"]);
  1728. this._removeNodes(headingNodes, function (node) {
  1729. let shouldRemove = this._getClassWeight(node) < 0;
  1730. if (shouldRemove) {
  1731. this.log("Removing header with low class weight:", node);
  1732. }
  1733. return shouldRemove;
  1734. });
  1735. },
  1736. /**
  1737. * Check if this node is an H1 or H2 element whose content is mostly
  1738. * the same as the article title.
  1739. *
  1740. * @param Element the node to check.
  1741. * @return boolean indicating whether this is a title-like header.
  1742. */
  1743. _headerDuplicatesTitle: function (node) {
  1744. if (node.tagName != "H1" && node.tagName != "H2") {
  1745. return false;
  1746. }
  1747. var heading2 = this._getInnerText(node, false);
  1748. this.log("Evaluating similarity of header:", heading2, this._articleTitle);
  1749. return this._textSimilarity(this._articleTitle, heading2) > 0.75;
  1750. },
  1751. _flagIsActive: function (flag) {
  1752. return (this._flags & flag) > 0;
  1753. },
  1754. _removeFlag: function (flag) {
  1755. this._flags = this._flags & ~flag;
  1756. },
  1757. _isProbablyVisible: function (node) {
  1758. return (!node.style || node.style.display != "none") && (!node.style || node.style.visibility != "hidden") && !node.hasAttribute("hidden") && (!node.hasAttribute("aria-hidden") || node.getAttribute("aria-hidden") != "true" || node.className && node.className.indexOf && node.className.indexOf("fallback-image") !== -1);
  1759. },
  1760. /**
  1761. * Runs readability.
  1762. *
  1763. * Workflow:
  1764. * 1. Prep the document by removing script tags, css, etc.
  1765. * 2. Build readability's DOM tree.
  1766. * 3. Grab the article content from the current dom tree.
  1767. * 4. Replace the current DOM tree with the new one.
  1768. * 5. Read peacefully.
  1769. *
  1770. * @return void
  1771. **/
  1772. parse: function () {
  1773. if (this._maxElemsToParse > 0) {
  1774. var numTags = this._doc.getElementsByTagName("*").length;
  1775. if (numTags > this._maxElemsToParse) {
  1776. throw new Error("Aborting parsing document; " + numTags + " elements found");
  1777. }
  1778. }
  1779. this._unwrapNoscriptImages(this._doc);
  1780. var jsonLd = this._disableJSONLD ? {} : this._getJSONLD(this._doc);
  1781. this._removeScripts(this._doc);
  1782. this._prepDocument();
  1783. var metadata = this._getArticleMetadata(jsonLd);
  1784. this._articleTitle = metadata.title;
  1785. var articleContent = this._grabArticle();
  1786. if (!articleContent)
  1787. return null;
  1788. this.log("Grabbed: " + articleContent.innerHTML);
  1789. this._postProcessContent(articleContent);
  1790. if (!metadata.excerpt) {
  1791. var paragraphs = articleContent.getElementsByTagName("p");
  1792. if (paragraphs.length > 0) {
  1793. metadata.excerpt = paragraphs[0].textContent.trim();
  1794. }
  1795. }
  1796. var textContent = articleContent.textContent;
  1797. return {
  1798. title: this._articleTitle,
  1799. byline: metadata.byline || this._articleByline,
  1800. dir: this._articleDir,
  1801. lang: this._articleLang,
  1802. content: this._serializer(articleContent),
  1803. textContent,
  1804. length: textContent.length,
  1805. excerpt: metadata.excerpt,
  1806. siteName: metadata.siteName || this._articleSiteName,
  1807. publishedTime: metadata.publishedTime
  1808. };
  1809. }
  1810. }; {
  1811. module.exports = Readability2;
  1812. }
  1813. })(Readability$1);
  1814. var ReadabilityExports = Readability$1.exports;
  1815. var ReadabilityReaderable = {
  1816. exports: {}
  1817. };
  1818. (function (module) {
  1819. var REGEXPS = {
  1820. // NOTE: These two regular expressions are duplicated in
  1821. // Readability.js. Please keep both copies in sync.
  1822. 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,
  1823. okMaybeItsACandidate: /and|article|body|column|content|main|shadow/i
  1824. };
  1825.  
  1826. function isNodeVisible(node) {
  1827. return (!node.style || node.style.display != "none") && !node.hasAttribute("hidden") && (!node.hasAttribute("aria-hidden") || node.getAttribute("aria-hidden") != "true" || node.className && node.className.indexOf && node.className.indexOf("fallback-image") !== -1);
  1828. }
  1829.  
  1830. function isProbablyReaderable2(doc, options = {}) {
  1831. if (typeof options == "function") {
  1832. options = {
  1833. visibilityChecker: options
  1834. };
  1835. }
  1836. var defaultOptions = {
  1837. minScore: 20,
  1838. minContentLength: 140,
  1839. visibilityChecker: isNodeVisible
  1840. };
  1841. options = Object.assign(defaultOptions, options);
  1842. var nodes = doc.querySelectorAll("p, pre, article");
  1843. var brNodes = doc.querySelectorAll("div > br");
  1844. if (brNodes.length) {
  1845. var set = new Set(nodes);
  1846. [].forEach.call(brNodes, function (node) {
  1847. set.add(node.parentNode);
  1848. });
  1849. nodes = Array.from(set);
  1850. }
  1851. var score = 0;
  1852. return [].some.call(nodes, function (node) {
  1853. if (!options.visibilityChecker(node)) {
  1854. return false;
  1855. }
  1856. var matchString = node.className + " " + node.id;
  1857. if (REGEXPS.unlikelyCandidates.test(matchString) && !REGEXPS.okMaybeItsACandidate.test(matchString)) {
  1858. return false;
  1859. }
  1860. if (node.matches("li p")) {
  1861. return false;
  1862. }
  1863. var textContentLength = node.textContent.trim().length;
  1864. if (textContentLength < options.minContentLength) {
  1865. return false;
  1866. }
  1867. score += Math.sqrt(textContentLength - options.minContentLength);
  1868. if (score > options.minScore) {
  1869. return true;
  1870. }
  1871. return false;
  1872. });
  1873. } {
  1874. module.exports = isProbablyReaderable2;
  1875. }
  1876. })(ReadabilityReaderable);
  1877. var ReadabilityReaderableExports = ReadabilityReaderable.exports;
  1878. var Readability = ReadabilityExports;
  1879. var isProbablyReaderable = ReadabilityReaderableExports;
  1880. var readability = {
  1881. Readability,
  1882. isProbablyReaderable
  1883. };
  1884.  
  1885. function getSimpleText(doc) {
  1886. doc = doc ? doc : document;
  1887. doc = doc.cloneNode(true);
  1888. const app = doc.querySelector("#monkeygpt");
  1889. app.remove();
  1890. let article = new readability.Readability(doc).parse();
  1891. let str = replaceEmpty(article.textContent);
  1892. return str;
  1893. }
  1894.  
  1895. function replaceEmpty(str) {
  1896. while (str.includes(" ")) {
  1897. str = str.replaceAll(" ", " ");
  1898. }
  1899. while (str.includes("\n\n")) {
  1900. str = str.replaceAll("\n\n", "\n");
  1901. }
  1902. while (str.includes(" ")) {
  1903. str = str.replaceAll(" ", " ");
  1904. }
  1905. return str;
  1906. }
  1907. const engineQueryKey = {
  1908. "www.google.com": "q",
  1909. "www.baidu.com": "wd",
  1910. "www.bing.com": "q",
  1911. "www.so.com": "q"
  1912. };
  1913.  
  1914. function getSearchKey() {
  1915. const seName = getSeName();
  1916. if (seName) {
  1917. return new URLSearchParams(location.search).get(engineQueryKey[seName]);
  1918. } else {
  1919. return null;
  1920. }
  1921. }
  1922.  
  1923. function getSeName() {
  1924. const se = Object.keys(engineQueryKey);
  1925. const seNames = se.filter((s) => window.location.host.includes(s));
  1926. return seNames.length > 0 ? seNames[0] : null;
  1927. }
  1928. async function chat$1(messages, model = "gpt-4o-mini", apiKey, apiEndpoint = "https://api.openai.com/v1/chat/completions") {
  1929. const headers = {
  1930. "Content-Type": "application/json",
  1931. "Authorization": `Bearer ${apiKey}`
  1932. };
  1933. const requestBody = {
  1934. model,
  1935. // 使用的模型名称
  1936. messages
  1937. };
  1938. return fetch(apiEndpoint, {
  1939. method: "POST",
  1940. headers,
  1941. body: JSON.stringify(requestBody)
  1942. }).then((response) => response.json()).then(async (data) => {
  1943. const reply = data.choices[0].message.content;
  1944. return reply;
  1945. });
  1946. }
  1947. const defaultConfig = {
  1948. model: "gpt-4o-mini",
  1949. apiKey: "",
  1950. apiEndpoint: "https://ai.01234.fun/v1/chat/completions"
  1951. };
  1952. async function chat(content, config = defaultConfig) {
  1953. return await chat$1([{
  1954. role: "user",
  1955. content
  1956. }], config.model, config.apiKey, config.apiEndpoint);
  1957. }
  1958. async function summarize(text, config = defaultConfig) {
  1959. return chat(`请帮忙总结一下 :"${text}"`, config);
  1960. }
  1961. async function ask(text, config = defaultConfig) {
  1962. return chat(`请帮忙回复一下 :"${text}"`, config);
  1963. }
  1964. async function search(text, config = defaultConfig) {
  1965. return chat(`现在你是谷歌,请帮忙回答一下 :"${text}"`, config);
  1966. }
  1967.  
  1968. function _getDefaults() {
  1969. return {
  1970. async: false,
  1971. breaks: false,
  1972. extensions: null,
  1973. gfm: true,
  1974. hooks: null,
  1975. pedantic: false,
  1976. renderer: null,
  1977. silent: false,
  1978. tokenizer: null,
  1979. walkTokens: null
  1980. };
  1981. }
  1982. let _defaults = _getDefaults();
  1983.  
  1984. function changeDefaults(newDefaults) {
  1985. _defaults = newDefaults;
  1986. }
  1987. const escapeTest = /[&<>"']/;
  1988. const escapeReplace = new RegExp(escapeTest.source, "g");
  1989. const escapeTestNoEncode = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/;
  1990. const escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, "g");
  1991. const escapeReplacements = {
  1992. "&": "&amp;",
  1993. "<": "&lt;",
  1994. ">": "&gt;",
  1995. '"': "&quot;",
  1996. "'": "&#39;"
  1997. };
  1998. const getEscapeReplacement = (ch) => escapeReplacements[ch];
  1999.  
  2000. function escape$1(html2, encode) {
  2001. if (encode) {
  2002. if (escapeTest.test(html2)) {
  2003. return html2.replace(escapeReplace, getEscapeReplacement);
  2004. }
  2005. } else {
  2006. if (escapeTestNoEncode.test(html2)) {
  2007. return html2.replace(escapeReplaceNoEncode, getEscapeReplacement);
  2008. }
  2009. }
  2010. return html2;
  2011. }
  2012. const caret = /(^|[^\[])\^/g;
  2013.  
  2014. function edit(regex, opt) {
  2015. let source = typeof regex === "string" ? regex : regex.source;
  2016. opt = opt || "";
  2017. const obj = {
  2018. replace: (name, val) => {
  2019. let valSource = typeof val === "string" ? val : val.source;
  2020. valSource = valSource.replace(caret, "$1");
  2021. source = source.replace(name, valSource);
  2022. return obj;
  2023. },
  2024. getRegex: () => {
  2025. return new RegExp(source, opt);
  2026. }
  2027. };
  2028. return obj;
  2029. }
  2030.  
  2031. function cleanUrl(href) {
  2032. try {
  2033. href = encodeURI(href).replace(/%25/g, "%");
  2034. } catch {
  2035. return null;
  2036. }
  2037. return href;
  2038. }
  2039. const noopTest = {
  2040. exec: () => null
  2041. };
  2042.  
  2043. function splitCells(tableRow, count) {
  2044. const row = tableRow.replace(/\|/g, (match, offset, str) => {
  2045. let escaped = false;
  2046. let curr = offset;
  2047. while (--curr >= 0 && str[curr] === "\\")
  2048. escaped = !escaped;
  2049. if (escaped) {
  2050. return "|";
  2051. } else {
  2052. return " |";
  2053. }
  2054. }),
  2055. cells = row.split(/ \|/);
  2056. let i = 0;
  2057. if (!cells[0].trim()) {
  2058. cells.shift();
  2059. }
  2060. if (cells.length > 0 && !cells[cells.length - 1].trim()) {
  2061. cells.pop();
  2062. }
  2063. if (count) {
  2064. if (cells.length > count) {
  2065. cells.splice(count);
  2066. } else {
  2067. while (cells.length < count)
  2068. cells.push("");
  2069. }
  2070. }
  2071. for (; i < cells.length; i++) {
  2072. cells[i] = cells[i].trim().replace(/\\\|/g, "|");
  2073. }
  2074. return cells;
  2075. }
  2076.  
  2077. function rtrim(str, c, invert) {
  2078. const l = str.length;
  2079. if (l === 0) {
  2080. return "";
  2081. }
  2082. let suffLen = 0;
  2083. while (suffLen < l) {
  2084. const currChar = str.charAt(l - suffLen - 1);
  2085. if (currChar === c && !invert) {
  2086. suffLen++;
  2087. } else if (currChar !== c && invert) {
  2088. suffLen++;
  2089. } else {
  2090. break;
  2091. }
  2092. }
  2093. return str.slice(0, l - suffLen);
  2094. }
  2095.  
  2096. function findClosingBracket(str, b) {
  2097. if (str.indexOf(b[1]) === -1) {
  2098. return -1;
  2099. }
  2100. let level = 0;
  2101. for (let i = 0; i < str.length; i++) {
  2102. if (str[i] === "\\") {
  2103. i++;
  2104. } else if (str[i] === b[0]) {
  2105. level++;
  2106. } else if (str[i] === b[1]) {
  2107. level--;
  2108. if (level < 0) {
  2109. return i;
  2110. }
  2111. }
  2112. }
  2113. return -1;
  2114. }
  2115.  
  2116. function outputLink(cap, link2, raw, lexer) {
  2117. const href = link2.href;
  2118. const title = link2.title ? escape$1(link2.title) : null;
  2119. const text = cap[1].replace(/\\([\[\]])/g, "$1");
  2120. if (cap[0].charAt(0) !== "!") {
  2121. lexer.state.inLink = true;
  2122. const token = {
  2123. type: "link",
  2124. raw,
  2125. href,
  2126. title,
  2127. text,
  2128. tokens: lexer.inlineTokens(text)
  2129. };
  2130. lexer.state.inLink = false;
  2131. return token;
  2132. }
  2133. return {
  2134. type: "image",
  2135. raw,
  2136. href,
  2137. title,
  2138. text: escape$1(text)
  2139. };
  2140. }
  2141.  
  2142. function indentCodeCompensation(raw, text) {
  2143. const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
  2144. if (matchIndentToCode === null) {
  2145. return text;
  2146. }
  2147. const indentToCode = matchIndentToCode[1];
  2148. return text.split("\n").map((node) => {
  2149. const matchIndentInNode = node.match(/^\s+/);
  2150. if (matchIndentInNode === null) {
  2151. return node;
  2152. }
  2153. const [indentInNode] = matchIndentInNode;
  2154. if (indentInNode.length >= indentToCode.length) {
  2155. return node.slice(indentToCode.length);
  2156. }
  2157. return node;
  2158. }).join("\n");
  2159. }
  2160. class _Tokenizer {
  2161. // set by the lexer
  2162. constructor(options) {
  2163. __publicField(this, "options");
  2164. __publicField(this, "rules");
  2165. // set by the lexer
  2166. __publicField(this, "lexer");
  2167. this.options = options || _defaults;
  2168. }
  2169. space(src) {
  2170. const cap = this.rules.block.newline.exec(src);
  2171. if (cap && cap[0].length > 0) {
  2172. return {
  2173. type: "space",
  2174. raw: cap[0]
  2175. };
  2176. }
  2177. }
  2178. code(src) {
  2179. const cap = this.rules.block.code.exec(src);
  2180. if (cap) {
  2181. const text = cap[0].replace(/^ {1,4}/gm, "");
  2182. return {
  2183. type: "code",
  2184. raw: cap[0],
  2185. codeBlockStyle: "indented",
  2186. text: !this.options.pedantic ? rtrim(text, "\n") : text
  2187. };
  2188. }
  2189. }
  2190. fences(src) {
  2191. const cap = this.rules.block.fences.exec(src);
  2192. if (cap) {
  2193. const raw = cap[0];
  2194. const text = indentCodeCompensation(raw, cap[3] || "");
  2195. return {
  2196. type: "code",
  2197. raw,
  2198. lang: cap[2] ? cap[2].trim().replace(this.rules.inline.anyPunctuation, "$1") : cap[2],
  2199. text
  2200. };
  2201. }
  2202. }
  2203. heading(src) {
  2204. const cap = this.rules.block.heading.exec(src);
  2205. if (cap) {
  2206. let text = cap[2].trim();
  2207. if (/#$/.test(text)) {
  2208. const trimmed = rtrim(text, "#");
  2209. if (this.options.pedantic) {
  2210. text = trimmed.trim();
  2211. } else if (!trimmed || / $/.test(trimmed)) {
  2212. text = trimmed.trim();
  2213. }
  2214. }
  2215. return {
  2216. type: "heading",
  2217. raw: cap[0],
  2218. depth: cap[1].length,
  2219. text,
  2220. tokens: this.lexer.inline(text)
  2221. };
  2222. }
  2223. }
  2224. hr(src) {
  2225. const cap = this.rules.block.hr.exec(src);
  2226. if (cap) {
  2227. return {
  2228. type: "hr",
  2229. raw: rtrim(cap[0], "\n")
  2230. };
  2231. }
  2232. }
  2233. blockquote(src) {
  2234. const cap = this.rules.block.blockquote.exec(src);
  2235. if (cap) {
  2236. let lines = rtrim(cap[0], "\n").split("\n");
  2237. let raw = "";
  2238. let text = "";
  2239. const tokens = [];
  2240. while (lines.length > 0) {
  2241. let inBlockquote = false;
  2242. const currentLines = [];
  2243. let i;
  2244. for (i = 0; i < lines.length; i++) {
  2245. if (/^ {0,3}>/.test(lines[i])) {
  2246. currentLines.push(lines[i]);
  2247. inBlockquote = true;
  2248. } else if (!inBlockquote) {
  2249. currentLines.push(lines[i]);
  2250. } else {
  2251. break;
  2252. }
  2253. }
  2254. lines = lines.slice(i);
  2255. const currentRaw = currentLines.join("\n");
  2256. const currentText = currentRaw.replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g, "\n $1").replace(/^ {0,3}>[ \t]?/gm, "");
  2257. raw = raw ? `${raw}
  2258. ${currentRaw}` : currentRaw;
  2259. text = text ? `${text}
  2260. ${currentText}` : currentText;
  2261. const top = this.lexer.state.top;
  2262. this.lexer.state.top = true;
  2263. this.lexer.blockTokens(currentText, tokens, true);
  2264. this.lexer.state.top = top;
  2265. if (lines.length === 0) {
  2266. break;
  2267. }
  2268. const lastToken = tokens[tokens.length - 1];
  2269. if ((lastToken == null ? void 0 : lastToken.type) === "code") {
  2270. break;
  2271. } else if ((lastToken == null ? void 0 : lastToken.type) === "blockquote") {
  2272. const oldToken = lastToken;
  2273. const newText = oldToken.raw + "\n" + lines.join("\n");
  2274. const newToken = this.blockquote(newText);
  2275. tokens[tokens.length - 1] = newToken;
  2276. raw = raw.substring(0, raw.length - oldToken.raw.length) + newToken.raw;
  2277. text = text.substring(0, text.length - oldToken.text.length) + newToken.text;
  2278. break;
  2279. } else if ((lastToken == null ? void 0 : lastToken.type) === "list") {
  2280. const oldToken = lastToken;
  2281. const newText = oldToken.raw + "\n" + lines.join("\n");
  2282. const newToken = this.list(newText);
  2283. tokens[tokens.length - 1] = newToken;
  2284. raw = raw.substring(0, raw.length - lastToken.raw.length) + newToken.raw;
  2285. text = text.substring(0, text.length - oldToken.raw.length) + newToken.raw;
  2286. lines = newText.substring(tokens[tokens.length - 1].raw.length).split("\n");
  2287. continue;
  2288. }
  2289. }
  2290. return {
  2291. type: "blockquote",
  2292. raw,
  2293. tokens,
  2294. text
  2295. };
  2296. }
  2297. }
  2298. list(src) {
  2299. let cap = this.rules.block.list.exec(src);
  2300. if (cap) {
  2301. let bull = cap[1].trim();
  2302. const isordered = bull.length > 1;
  2303. const list2 = {
  2304. type: "list",
  2305. raw: "",
  2306. ordered: isordered,
  2307. start: isordered ? +bull.slice(0, -1) : "",
  2308. loose: false,
  2309. items: []
  2310. };
  2311. bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`;
  2312. if (this.options.pedantic) {
  2313. bull = isordered ? bull : "[*+-]";
  2314. }
  2315. const itemRegex = new RegExp(`^( {0,3}${bull})((?:[ ][^\\n]*)?(?:\\n|$))`);
  2316. let endsWithBlankLine = false;
  2317. while (src) {
  2318. let endEarly = false;
  2319. let raw = "";
  2320. let itemContents = "";
  2321. if (!(cap = itemRegex.exec(src))) {
  2322. break;
  2323. }
  2324. if (this.rules.block.hr.test(src)) {
  2325. break;
  2326. }
  2327. raw = cap[0];
  2328. src = src.substring(raw.length);
  2329. let line = cap[2].split("\n", 1)[0].replace(/^\t+/, (t) => " ".repeat(3 * t.length));
  2330. let nextLine = src.split("\n", 1)[0];
  2331. let blankLine = !line.trim();
  2332. let indent = 0;
  2333. if (this.options.pedantic) {
  2334. indent = 2;
  2335. itemContents = line.trimStart();
  2336. } else if (blankLine) {
  2337. indent = cap[1].length + 1;
  2338. } else {
  2339. indent = cap[2].search(/[^ ]/);
  2340. indent = indent > 4 ? 1 : indent;
  2341. itemContents = line.slice(indent);
  2342. indent += cap[1].length;
  2343. }
  2344. if (blankLine && /^ *$/.test(nextLine)) {
  2345. raw += nextLine + "\n";
  2346. src = src.substring(nextLine.length + 1);
  2347. endEarly = true;
  2348. }
  2349. if (!endEarly) {
  2350. const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`);
  2351. const hrRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`);
  2352. const fencesBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`);
  2353. const headingBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`);
  2354. while (src) {
  2355. const rawLine = src.split("\n", 1)[0];
  2356. nextLine = rawLine;
  2357. if (this.options.pedantic) {
  2358. nextLine = nextLine.replace(/^ {1,4}(?=( {4})*[^ ])/g, " ");
  2359. }
  2360. if (fencesBeginRegex.test(nextLine)) {
  2361. break;
  2362. }
  2363. if (headingBeginRegex.test(nextLine)) {
  2364. break;
  2365. }
  2366. if (nextBulletRegex.test(nextLine)) {
  2367. break;
  2368. }
  2369. if (hrRegex.test(src)) {
  2370. break;
  2371. }
  2372. if (nextLine.search(/[^ ]/) >= indent || !nextLine.trim()) {
  2373. itemContents += "\n" + nextLine.slice(indent);
  2374. } else {
  2375. if (blankLine) {
  2376. break;
  2377. }
  2378. if (line.search(/[^ ]/) >= 4) {
  2379. break;
  2380. }
  2381. if (fencesBeginRegex.test(line)) {
  2382. break;
  2383. }
  2384. if (headingBeginRegex.test(line)) {
  2385. break;
  2386. }
  2387. if (hrRegex.test(line)) {
  2388. break;
  2389. }
  2390. itemContents += "\n" + nextLine;
  2391. }
  2392. if (!blankLine && !nextLine.trim()) {
  2393. blankLine = true;
  2394. }
  2395. raw += rawLine + "\n";
  2396. src = src.substring(rawLine.length + 1);
  2397. line = nextLine.slice(indent);
  2398. }
  2399. }
  2400. if (!list2.loose) {
  2401. if (endsWithBlankLine) {
  2402. list2.loose = true;
  2403. } else if (/\n *\n *$/.test(raw)) {
  2404. endsWithBlankLine = true;
  2405. }
  2406. }
  2407. let istask = null;
  2408. let ischecked;
  2409. if (this.options.gfm) {
  2410. istask = /^\[[ xX]\] /.exec(itemContents);
  2411. if (istask) {
  2412. ischecked = istask[0] !== "[ ] ";
  2413. itemContents = itemContents.replace(/^\[[ xX]\] +/, "");
  2414. }
  2415. }
  2416. list2.items.push({
  2417. type: "list_item",
  2418. raw,
  2419. task: !!istask,
  2420. checked: ischecked,
  2421. loose: false,
  2422. text: itemContents,
  2423. tokens: []
  2424. });
  2425. list2.raw += raw;
  2426. }
  2427. list2.items[list2.items.length - 1].raw = list2.items[list2.items.length - 1].raw.trimEnd();
  2428. list2.items[list2.items.length - 1].text = list2.items[list2.items.length - 1].text.trimEnd();
  2429. list2.raw = list2.raw.trimEnd();
  2430. for (let i = 0; i < list2.items.length; i++) {
  2431. this.lexer.state.top = false;
  2432. list2.items[i].tokens = this.lexer.blockTokens(list2.items[i].text, []);
  2433. if (!list2.loose) {
  2434. const spacers = list2.items[i].tokens.filter((t) => t.type === "space");
  2435. const hasMultipleLineBreaks = spacers.length > 0 && spacers.some((t) => /\n.*\n/.test(t.raw));
  2436. list2.loose = hasMultipleLineBreaks;
  2437. }
  2438. }
  2439. if (list2.loose) {
  2440. for (let i = 0; i < list2.items.length; i++) {
  2441. list2.items[i].loose = true;
  2442. }
  2443. }
  2444. return list2;
  2445. }
  2446. }
  2447. html(src) {
  2448. const cap = this.rules.block.html.exec(src);
  2449. if (cap) {
  2450. const token = {
  2451. type: "html",
  2452. block: true,
  2453. raw: cap[0],
  2454. pre: cap[1] === "pre" || cap[1] === "script" || cap[1] === "style",
  2455. text: cap[0]
  2456. };
  2457. return token;
  2458. }
  2459. }
  2460. def(src) {
  2461. const cap = this.rules.block.def.exec(src);
  2462. if (cap) {
  2463. const tag2 = cap[1].toLowerCase().replace(/\s+/g, " ");
  2464. const href = cap[2] ? cap[2].replace(/^<(.*)>$/, "$1").replace(this.rules.inline.anyPunctuation, "$1") : "";
  2465. const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline.anyPunctuation, "$1") : cap[3];
  2466. return {
  2467. type: "def",
  2468. tag: tag2,
  2469. raw: cap[0],
  2470. href,
  2471. title
  2472. };
  2473. }
  2474. }
  2475. table(src) {
  2476. const cap = this.rules.block.table.exec(src);
  2477. if (!cap) {
  2478. return;
  2479. }
  2480. if (!/[:|]/.test(cap[2])) {
  2481. return;
  2482. }
  2483. const headers = splitCells(cap[1]);
  2484. const aligns = cap[2].replace(/^\||\| *$/g, "").split("|");
  2485. const rows = cap[3] && cap[3].trim() ? cap[3].replace(/\n[ \t]*$/, "").split("\n") : [];
  2486. const item = {
  2487. type: "table",
  2488. raw: cap[0],
  2489. header: [],
  2490. align: [],
  2491. rows: []
  2492. };
  2493. if (headers.length !== aligns.length) {
  2494. return;
  2495. }
  2496. for (const align of aligns) {
  2497. if (/^ *-+: *$/.test(align)) {
  2498. item.align.push("right");
  2499. } else if (/^ *:-+: *$/.test(align)) {
  2500. item.align.push("center");
  2501. } else if (/^ *:-+ *$/.test(align)) {
  2502. item.align.push("left");
  2503. } else {
  2504. item.align.push(null);
  2505. }
  2506. }
  2507. for (let i = 0; i < headers.length; i++) {
  2508. item.header.push({
  2509. text: headers[i],
  2510. tokens: this.lexer.inline(headers[i]),
  2511. header: true,
  2512. align: item.align[i]
  2513. });
  2514. }
  2515. for (const row of rows) {
  2516. item.rows.push(splitCells(row, item.header.length).map((cell, i) => {
  2517. return {
  2518. text: cell,
  2519. tokens: this.lexer.inline(cell),
  2520. header: false,
  2521. align: item.align[i]
  2522. };
  2523. }));
  2524. }
  2525. return item;
  2526. }
  2527. lheading(src) {
  2528. const cap = this.rules.block.lheading.exec(src);
  2529. if (cap) {
  2530. return {
  2531. type: "heading",
  2532. raw: cap[0],
  2533. depth: cap[2].charAt(0) === "=" ? 1 : 2,
  2534. text: cap[1],
  2535. tokens: this.lexer.inline(cap[1])
  2536. };
  2537. }
  2538. }
  2539. paragraph(src) {
  2540. const cap = this.rules.block.paragraph.exec(src);
  2541. if (cap) {
  2542. const text = cap[1].charAt(cap[1].length - 1) === "\n" ? cap[1].slice(0, -1) : cap[1];
  2543. return {
  2544. type: "paragraph",
  2545. raw: cap[0],
  2546. text,
  2547. tokens: this.lexer.inline(text)
  2548. };
  2549. }
  2550. }
  2551. text(src) {
  2552. const cap = this.rules.block.text.exec(src);
  2553. if (cap) {
  2554. return {
  2555. type: "text",
  2556. raw: cap[0],
  2557. text: cap[0],
  2558. tokens: this.lexer.inline(cap[0])
  2559. };
  2560. }
  2561. }
  2562. escape(src) {
  2563. const cap = this.rules.inline.escape.exec(src);
  2564. if (cap) {
  2565. return {
  2566. type: "escape",
  2567. raw: cap[0],
  2568. text: escape$1(cap[1])
  2569. };
  2570. }
  2571. }
  2572. tag(src) {
  2573. const cap = this.rules.inline.tag.exec(src);
  2574. if (cap) {
  2575. if (!this.lexer.state.inLink && /^<a /i.test(cap[0])) {
  2576. this.lexer.state.inLink = true;
  2577. } else if (this.lexer.state.inLink && /^<\/a>/i.test(cap[0])) {
  2578. this.lexer.state.inLink = false;
  2579. }
  2580. if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
  2581. this.lexer.state.inRawBlock = true;
  2582. } else if (this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
  2583. this.lexer.state.inRawBlock = false;
  2584. }
  2585. return {
  2586. type: "html",
  2587. raw: cap[0],
  2588. inLink: this.lexer.state.inLink,
  2589. inRawBlock: this.lexer.state.inRawBlock,
  2590. block: false,
  2591. text: cap[0]
  2592. };
  2593. }
  2594. }
  2595. link(src) {
  2596. const cap = this.rules.inline.link.exec(src);
  2597. if (cap) {
  2598. const trimmedUrl = cap[2].trim();
  2599. if (!this.options.pedantic && /^</.test(trimmedUrl)) {
  2600. if (!/>$/.test(trimmedUrl)) {
  2601. return;
  2602. }
  2603. const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), "\\");
  2604. if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
  2605. return;
  2606. }
  2607. } else {
  2608. const lastParenIndex = findClosingBracket(cap[2], "()");
  2609. if (lastParenIndex > -1) {
  2610. const start = cap[0].indexOf("!") === 0 ? 5 : 4;
  2611. const linkLen = start + cap[1].length + lastParenIndex;
  2612. cap[2] = cap[2].substring(0, lastParenIndex);
  2613. cap[0] = cap[0].substring(0, linkLen).trim();
  2614. cap[3] = "";
  2615. }
  2616. }
  2617. let href = cap[2];
  2618. let title = "";
  2619. if (this.options.pedantic) {
  2620. const link2 = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
  2621. if (link2) {
  2622. href = link2[1];
  2623. title = link2[3];
  2624. }
  2625. } else {
  2626. title = cap[3] ? cap[3].slice(1, -1) : "";
  2627. }
  2628. href = href.trim();
  2629. if (/^</.test(href)) {
  2630. if (this.options.pedantic && !/>$/.test(trimmedUrl)) {
  2631. href = href.slice(1);
  2632. } else {
  2633. href = href.slice(1, -1);
  2634. }
  2635. }
  2636. return outputLink(cap, {
  2637. href: href ? href.replace(this.rules.inline.anyPunctuation, "$1") : href,
  2638. title: title ? title.replace(this.rules.inline.anyPunctuation, "$1") : title
  2639. }, cap[0], this.lexer);
  2640. }
  2641. }
  2642. reflink(src, links) {
  2643. let cap;
  2644. if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) {
  2645. const linkString = (cap[2] || cap[1]).replace(/\s+/g, " ");
  2646. const link2 = links[linkString.toLowerCase()];
  2647. if (!link2) {
  2648. const text = cap[0].charAt(0);
  2649. return {
  2650. type: "text",
  2651. raw: text,
  2652. text
  2653. };
  2654. }
  2655. return outputLink(cap, link2, cap[0], this.lexer);
  2656. }
  2657. }
  2658. emStrong(src, maskedSrc, prevChar = "") {
  2659. let match = this.rules.inline.emStrongLDelim.exec(src);
  2660. if (!match)
  2661. return;
  2662. if (match[3] && prevChar.match(/[\p{L}\p{N}]/u))
  2663. return;
  2664. const nextChar = match[1] || match[2] || "";
  2665. if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
  2666. const lLength = [...match[0]].length - 1;
  2667. let rDelim, rLength, delimTotal = lLength,
  2668. midDelimTotal = 0;
  2669. const endReg = match[0][0] === "*" ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd;
  2670. endReg.lastIndex = 0;
  2671. maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
  2672. while ((match = endReg.exec(maskedSrc)) != null) {
  2673. rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
  2674. if (!rDelim)
  2675. continue;
  2676. rLength = [...rDelim].length;
  2677. if (match[3] || match[4]) {
  2678. delimTotal += rLength;
  2679. continue;
  2680. } else if (match[5] || match[6]) {
  2681. if (lLength % 3 && !((lLength + rLength) % 3)) {
  2682. midDelimTotal += rLength;
  2683. continue;
  2684. }
  2685. }
  2686. delimTotal -= rLength;
  2687. if (delimTotal > 0)
  2688. continue;
  2689. rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
  2690. const lastCharLength = [...match[0]][0].length;
  2691. const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);
  2692. if (Math.min(lLength, rLength) % 2) {
  2693. const text2 = raw.slice(1, -1);
  2694. return {
  2695. type: "em",
  2696. raw,
  2697. text: text2,
  2698. tokens: this.lexer.inlineTokens(text2)
  2699. };
  2700. }
  2701. const text = raw.slice(2, -2);
  2702. return {
  2703. type: "strong",
  2704. raw,
  2705. text,
  2706. tokens: this.lexer.inlineTokens(text)
  2707. };
  2708. }
  2709. }
  2710. }
  2711. codespan(src) {
  2712. const cap = this.rules.inline.code.exec(src);
  2713. if (cap) {
  2714. let text = cap[2].replace(/\n/g, " ");
  2715. const hasNonSpaceChars = /[^ ]/.test(text);
  2716. const hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
  2717. if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
  2718. text = text.substring(1, text.length - 1);
  2719. }
  2720. text = escape$1(text, true);
  2721. return {
  2722. type: "codespan",
  2723. raw: cap[0],
  2724. text
  2725. };
  2726. }
  2727. }
  2728. br(src) {
  2729. const cap = this.rules.inline.br.exec(src);
  2730. if (cap) {
  2731. return {
  2732. type: "br",
  2733. raw: cap[0]
  2734. };
  2735. }
  2736. }
  2737. del(src) {
  2738. const cap = this.rules.inline.del.exec(src);
  2739. if (cap) {
  2740. return {
  2741. type: "del",
  2742. raw: cap[0],
  2743. text: cap[2],
  2744. tokens: this.lexer.inlineTokens(cap[2])
  2745. };
  2746. }
  2747. }
  2748. autolink(src) {
  2749. const cap = this.rules.inline.autolink.exec(src);
  2750. if (cap) {
  2751. let text, href;
  2752. if (cap[2] === "@") {
  2753. text = escape$1(cap[1]);
  2754. href = "mailto:" + text;
  2755. } else {
  2756. text = escape$1(cap[1]);
  2757. href = text;
  2758. }
  2759. return {
  2760. type: "link",
  2761. raw: cap[0],
  2762. text,
  2763. href,
  2764. tokens: [{
  2765. type: "text",
  2766. raw: text,
  2767. text
  2768. }]
  2769. };
  2770. }
  2771. }
  2772. url(src) {
  2773. var _a;
  2774. let cap;
  2775. if (cap = this.rules.inline.url.exec(src)) {
  2776. let text, href;
  2777. if (cap[2] === "@") {
  2778. text = escape$1(cap[0]);
  2779. href = "mailto:" + text;
  2780. } else {
  2781. let prevCapZero;
  2782. do {
  2783. prevCapZero = cap[0];
  2784. const tmp = ((_a = this.rules.inline._backpedal.exec(cap[0])) == null ? void 0 : _a[0])
  2785. cap[0] = tmp ? tmp : "";
  2786. } while (prevCapZero !== cap[0]);
  2787. text = escape$1(cap[0]);
  2788. if (cap[1] === "www.") {
  2789. href = "http://" + cap[0];
  2790. } else {
  2791. href = cap[0];
  2792. }
  2793. }
  2794. return {
  2795. type: "link",
  2796. raw: cap[0],
  2797. text,
  2798. href,
  2799. tokens: [{
  2800. type: "text",
  2801. raw: text,
  2802. text
  2803. }]
  2804. };
  2805. }
  2806. }
  2807. inlineText(src) {
  2808. const cap = this.rules.inline.text.exec(src);
  2809. if (cap) {
  2810. let text;
  2811. if (this.lexer.state.inRawBlock) {
  2812. text = cap[0];
  2813. } else {
  2814. text = escape$1(cap[0]);
  2815. }
  2816. return {
  2817. type: "text",
  2818. raw: cap[0],
  2819. text
  2820. };
  2821. }
  2822. }
  2823. }
  2824. const newline = /^(?: *(?:\n|$))+/;
  2825. const blockCode = /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/;
  2826. const fences = /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/;
  2827. const hr = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/;
  2828. const heading = /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/;
  2829. const bullet = /(?:[*+-]|\d{1,9}[.)])/;
  2830. const lheading = edit(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/).replace(/bull/g, bullet).replace(/blockCode/g, / {4}/).replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g, / {0,3}>/).replace(/heading/g, / {0,3}#{1,6}/).replace(/html/g, / {0,3}<[^\n>]+>\n/).getRegex();
  2831. const _paragraph = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/;
  2832. const blockText = /^[^\n]+/;
  2833. const _blockLabel = /(?!\s*\])(?:\\.|[^\[\]\\])+/;
  2834. const def = edit(/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/).replace("label", _blockLabel).replace("title", /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex();
  2835. const list = edit(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g, bullet).getRegex();
  2836. const _tag = "address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul";
  2837. const _comment = /<!--(?:-?>|[\s\S]*?(?:-->|$))/;
  2838. const html = edit("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|<![A-Z][\\s\\S]*?(?:>\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$))", "i").replace("comment", _comment).replace("tag", _tag).replace("attribute", / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex();
  2839. const paragraph = edit(_paragraph).replace("hr", hr).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("|table", "").replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)]) ").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", _tag).getRegex();
  2840. const blockquote = edit(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph", paragraph).getRegex();
  2841. const blockNormal = {
  2842. blockquote,
  2843. code: blockCode,
  2844. def,
  2845. fences,
  2846. heading,
  2847. hr,
  2848. html,
  2849. lheading,
  2850. list,
  2851. newline,
  2852. paragraph,
  2853. table: noopTest,
  2854. text: blockText
  2855. };
  2856. const gfmTable = edit("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr", hr).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("blockquote", " {0,3}>").replace("code", " {4}[^\\n]").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)]) ").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", _tag).getRegex();
  2857. const blockGfm = {
  2858. ...blockNormal,
  2859. table: gfmTable,
  2860. paragraph: edit(_paragraph).replace("hr", hr).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("table", gfmTable).replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)]) ").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", _tag).getRegex()
  2861. };
  2862. const blockPedantic = {
  2863. ...blockNormal,
  2864. html: edit(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:"[^"]*"|'[^']*'|\\s[^'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment", _comment).replace(/tag/g, "(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),
  2865. def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
  2866. heading: /^(#{1,6})(.*)(?:\n+|$)/,
  2867. fences: noopTest,
  2868. // fences not supported
  2869. lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,
  2870. paragraph: edit(_paragraph).replace("hr", hr).replace("heading", " *#{1,6} *[^\n]").replace("lheading", lheading).replace("|table", "").replace("blockquote", " {0,3}>").replace("|fences", "").replace("|list", "").replace("|html", "").replace("|tag", "").getRegex()
  2871. };
  2872. const escape = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/;
  2873. const inlineCode = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/;
  2874. const br = /^( {2,}|\\)\n(?!\s*$)/;
  2875. const inlineText = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/;
  2876. const _punctuation = "\\p{P}\\p{S}";
  2877. const punctuation = edit(/^((?![*_])[\spunctuation])/, "u").replace(/punctuation/g, _punctuation).getRegex();
  2878. const blockSkip = /\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g;
  2879. const emStrongLDelim = edit(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/, "u").replace(/punct/g, _punctuation).getRegex();
  2880. const emStrongRDelimAst = edit("^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)[punct](\\*+)(?=[\\s]|$)|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])|[\\s](\\*+)(?!\\*)(?=[punct])|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])|[^punct\\s](\\*+)(?=[^punct\\s])", "gu").replace(/punct/g, _punctuation).getRegex();
  2881. const emStrongRDelimUnd = edit("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\\s]|$)|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)|(?!_)[punct\\s](_+)(?=[^punct\\s])|[\\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])", "gu").replace(/punct/g, _punctuation).getRegex();
  2882. const anyPunctuation = edit(/\\([punct])/, "gu").replace(/punct/g, _punctuation).getRegex();
  2883. const autolink = edit(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme", /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email", /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex();
  2884. const _inlineComment = edit(_comment).replace("(?:-->|$)", "-->").getRegex();
  2885. const tag = edit("^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>").replace("comment", _inlineComment).replace("attribute", /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex();
  2886. const _inlineLabel = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
  2887. const link = edit(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label", _inlineLabel).replace("href", /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title", /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex();
  2888. const reflink = edit(/^!?\[(label)\]\[(ref)\]/).replace("label", _inlineLabel).replace("ref", _blockLabel).getRegex();
  2889. const nolink = edit(/^!?\[(ref)\](?:\[\])?/).replace("ref", _blockLabel).getRegex();
  2890. const reflinkSearch = edit("reflink|nolink(?!\\()", "g").replace("reflink", reflink).replace("nolink", nolink).getRegex();
  2891. const inlineNormal = {
  2892. _backpedal: noopTest,
  2893. // only used for GFM url
  2894. anyPunctuation,
  2895. autolink,
  2896. blockSkip,
  2897. br,
  2898. code: inlineCode,
  2899. del: noopTest,
  2900. emStrongLDelim,
  2901. emStrongRDelimAst,
  2902. emStrongRDelimUnd,
  2903. escape,
  2904. link,
  2905. nolink,
  2906. punctuation,
  2907. reflink,
  2908. reflinkSearch,
  2909. tag,
  2910. text: inlineText,
  2911. url: noopTest
  2912. };
  2913. const inlinePedantic = {
  2914. ...inlineNormal,
  2915. link: edit(/^!?\[(label)\]\((.*?)\)/).replace("label", _inlineLabel).getRegex(),
  2916. reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label", _inlineLabel).getRegex()
  2917. };
  2918. const inlineGfm = {
  2919. ...inlineNormal,
  2920. escape: edit(escape).replace("])", "~|])").getRegex(),
  2921. url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, "i").replace("email", /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),
  2922. _backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,
  2923. del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,
  2924. text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/
  2925. };
  2926. const inlineBreaks = {
  2927. ...inlineGfm,
  2928. br: edit(br).replace("{2,}", "*").getRegex(),
  2929. text: edit(inlineGfm.text).replace("\\b_", "\\b_| {2,}\\n").replace(/\{2,\}/g, "*").getRegex()
  2930. };
  2931. const block = {
  2932. normal: blockNormal,
  2933. gfm: blockGfm,
  2934. pedantic: blockPedantic
  2935. };
  2936. const inline = {
  2937. normal: inlineNormal,
  2938. gfm: inlineGfm,
  2939. breaks: inlineBreaks,
  2940. pedantic: inlinePedantic
  2941. };
  2942. class _Lexer {
  2943. constructor(options) {
  2944. __publicField(this, "tokens");
  2945. __publicField(this, "options");
  2946. __publicField(this, "state");
  2947. __publicField(this, "tokenizer");
  2948. __publicField(this, "inlineQueue");
  2949. this.tokens = [];
  2950. this.tokens.links = /* @__PURE__ */ Object.create(null);
  2951. this.options = options || _defaults;
  2952. this.options.tokenizer = this.options.tokenizer || new _Tokenizer();
  2953. this.tokenizer = this.options.tokenizer;
  2954. this.tokenizer.options = this.options;
  2955. this.tokenizer.lexer = this;
  2956. this.inlineQueue = [];
  2957. this.state = {
  2958. inLink: false,
  2959. inRawBlock: false,
  2960. top: true
  2961. };
  2962. const rules = {
  2963. block: block.normal,
  2964. inline: inline.normal
  2965. };
  2966. if (this.options.pedantic) {
  2967. rules.block = block.pedantic;
  2968. rules.inline = inline.pedantic;
  2969. } else if (this.options.gfm) {
  2970. rules.block = block.gfm;
  2971. if (this.options.breaks) {
  2972. rules.inline = inline.breaks;
  2973. } else {
  2974. rules.inline = inline.gfm;
  2975. }
  2976. }
  2977. this.tokenizer.rules = rules;
  2978. }
  2979. /**
  2980. * Expose Rules
  2981. */
  2982. static get rules() {
  2983. return {
  2984. block,
  2985. inline
  2986. };
  2987. }
  2988. /**
  2989. * Static Lex Method
  2990. */
  2991. static lex(src, options) {
  2992. const lexer = new _Lexer(options);
  2993. return lexer.lex(src);
  2994. }
  2995. /**
  2996. * Static Lex Inline Method
  2997. */
  2998. static lexInline(src, options) {
  2999. const lexer = new _Lexer(options);
  3000. return lexer.inlineTokens(src);
  3001. }
  3002. /**
  3003. * Preprocessing
  3004. */
  3005. lex(src) {
  3006. src = src.replace(/\r\n|\r/g, "\n");
  3007. this.blockTokens(src, this.tokens);
  3008. for (let i = 0; i < this.inlineQueue.length; i++) {
  3009. const next = this.inlineQueue[i];
  3010. this.inlineTokens(next.src, next.tokens);
  3011. }
  3012. this.inlineQueue = [];
  3013. return this.tokens;
  3014. }
  3015. blockTokens(src, tokens = [], lastParagraphClipped = false) {
  3016. if (this.options.pedantic) {
  3017. src = src.replace(/\t/g, " ").replace(/^ +$/gm, "");
  3018. } else {
  3019. src = src.replace(/^( *)(\t+)/gm, (_, leading, tabs) => {
  3020. return leading + " ".repeat(tabs.length);
  3021. });
  3022. }
  3023. let token;
  3024. let lastToken;
  3025. let cutSrc;
  3026. while (src) {
  3027. if (this.options.extensions && this.options.extensions.block && this.options.extensions.block.some((extTokenizer) => {
  3028. if (token = extTokenizer.call({
  3029. lexer: this
  3030. }, src, tokens)) {
  3031. src = src.substring(token.raw.length);
  3032. tokens.push(token);
  3033. return true;
  3034. }
  3035. return false;
  3036. })) {
  3037. continue;
  3038. }
  3039. if (token = this.tokenizer.space(src)) {
  3040. src = src.substring(token.raw.length);
  3041. if (token.raw.length === 1 && tokens.length > 0) {
  3042. tokens[tokens.length - 1].raw += "\n";
  3043. } else {
  3044. tokens.push(token);
  3045. }
  3046. continue;
  3047. }
  3048. if (token = this.tokenizer.code(src)) {
  3049. src = src.substring(token.raw.length);
  3050. lastToken = tokens[tokens.length - 1];
  3051. if (lastToken && (lastToken.type === "paragraph" || lastToken.type === "text")) {
  3052. lastToken.raw += "\n" + token.raw;
  3053. lastToken.text += "\n" + token.text;
  3054. this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
  3055. } else {
  3056. tokens.push(token);
  3057. }
  3058. continue;
  3059. }
  3060. if (token = this.tokenizer.fences(src)) {
  3061. src = src.substring(token.raw.length);
  3062. tokens.push(token);
  3063. continue;
  3064. }
  3065. if (token = this.tokenizer.heading(src)) {
  3066. src = src.substring(token.raw.length);
  3067. tokens.push(token);
  3068. continue;
  3069. }
  3070. if (token = this.tokenizer.hr(src)) {
  3071. src = src.substring(token.raw.length);
  3072. tokens.push(token);
  3073. continue;
  3074. }
  3075. if (token = this.tokenizer.blockquote(src)) {
  3076. src = src.substring(token.raw.length);
  3077. tokens.push(token);
  3078. continue;
  3079. }
  3080. if (token = this.tokenizer.list(src)) {
  3081. src = src.substring(token.raw.length);
  3082. tokens.push(token);
  3083. continue;
  3084. }
  3085. if (token = this.tokenizer.html(src)) {
  3086. src = src.substring(token.raw.length);
  3087. tokens.push(token);
  3088. continue;
  3089. }
  3090. if (token = this.tokenizer.def(src)) {
  3091. src = src.substring(token.raw.length);
  3092. lastToken = tokens[tokens.length - 1];
  3093. if (lastToken && (lastToken.type === "paragraph" || lastToken.type === "text")) {
  3094. lastToken.raw += "\n" + token.raw;
  3095. lastToken.text += "\n" + token.raw;
  3096. this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
  3097. } else if (!this.tokens.links[token.tag]) {
  3098. this.tokens.links[token.tag] = {
  3099. href: token.href,
  3100. title: token.title
  3101. };
  3102. }
  3103. continue;
  3104. }
  3105. if (token = this.tokenizer.table(src)) {
  3106. src = src.substring(token.raw.length);
  3107. tokens.push(token);
  3108. continue;
  3109. }
  3110. if (token = this.tokenizer.lheading(src)) {
  3111. src = src.substring(token.raw.length);
  3112. tokens.push(token);
  3113. continue;
  3114. }
  3115. cutSrc = src;
  3116. if (this.options.extensions && this.options.extensions.startBlock) {
  3117. let startIndex = Infinity;
  3118. const tempSrc = src.slice(1);
  3119. let tempStart;
  3120. this.options.extensions.startBlock.forEach((getStartIndex) => {
  3121. tempStart = getStartIndex.call({
  3122. lexer: this
  3123. }, tempSrc);
  3124. if (typeof tempStart === "number" && tempStart >= 0) {
  3125. startIndex = Math.min(startIndex, tempStart);
  3126. }
  3127. });
  3128. if (startIndex < Infinity && startIndex >= 0) {
  3129. cutSrc = src.substring(0, startIndex + 1);
  3130. }
  3131. }
  3132. if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) {
  3133. lastToken = tokens[tokens.length - 1];
  3134. if (lastParagraphClipped && (lastToken == null ? void 0 : lastToken.type) === "paragraph") {
  3135. lastToken.raw += "\n" + token.raw;
  3136. lastToken.text += "\n" + token.text;
  3137. this.inlineQueue.pop();
  3138. this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
  3139. } else {
  3140. tokens.push(token);
  3141. }
  3142. lastParagraphClipped = cutSrc.length !== src.length;
  3143. src = src.substring(token.raw.length);
  3144. continue;
  3145. }
  3146. if (token = this.tokenizer.text(src)) {
  3147. src = src.substring(token.raw.length);
  3148. lastToken = tokens[tokens.length - 1];
  3149. if (lastToken && lastToken.type === "text") {
  3150. lastToken.raw += "\n" + token.raw;
  3151. lastToken.text += "\n" + token.text;
  3152. this.inlineQueue.pop();
  3153. this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
  3154. } else {
  3155. tokens.push(token);
  3156. }
  3157. continue;
  3158. }
  3159. if (src) {
  3160. const errMsg = "Infinite loop on byte: " + src.charCodeAt(0);
  3161. if (this.options.silent) {
  3162. console.error(errMsg);
  3163. break;
  3164. } else {
  3165. throw new Error(errMsg);
  3166. }
  3167. }
  3168. }
  3169. this.state.top = true;
  3170. return tokens;
  3171. }
  3172. inline(src, tokens = []) {
  3173. this.inlineQueue.push({
  3174. src,
  3175. tokens
  3176. });
  3177. return tokens;
  3178. }
  3179. /**
  3180. * Lexing/Compiling
  3181. */
  3182. inlineTokens(src, tokens = []) {
  3183. let token, lastToken, cutSrc;
  3184. let maskedSrc = src;
  3185. let match;
  3186. let keepPrevChar, prevChar;
  3187. if (this.tokens.links) {
  3188. const links = Object.keys(this.tokens.links);
  3189. if (links.length > 0) {
  3190. while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
  3191. if (links.includes(match[0].slice(match[0].lastIndexOf("[") + 1, -1))) {
  3192. maskedSrc = maskedSrc.slice(0, match.index) + "[" + "a".repeat(match[0].length - 2) + "]" + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
  3193. }
  3194. }
  3195. }
  3196. }
  3197. while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
  3198. maskedSrc = maskedSrc.slice(0, match.index) + "[" + "a".repeat(match[0].length - 2) + "]" + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
  3199. }
  3200. while ((match = this.tokenizer.rules.inline.anyPunctuation.exec(maskedSrc)) != null) {
  3201. maskedSrc = maskedSrc.slice(0, match.index) + "++" + maskedSrc.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);
  3202. }
  3203. while (src) {
  3204. if (!keepPrevChar) {
  3205. prevChar = "";
  3206. }
  3207. keepPrevChar = false;
  3208. if (this.options.extensions && this.options.extensions.inline && this.options.extensions.inline.some((extTokenizer) => {
  3209. if (token = extTokenizer.call({
  3210. lexer: this
  3211. }, src, tokens)) {
  3212. src = src.substring(token.raw.length);
  3213. tokens.push(token);
  3214. return true;
  3215. }
  3216. return false;
  3217. })) {
  3218. continue;
  3219. }
  3220. if (token = this.tokenizer.escape(src)) {
  3221. src = src.substring(token.raw.length);
  3222. tokens.push(token);
  3223. continue;
  3224. }
  3225. if (token = this.tokenizer.tag(src)) {
  3226. src = src.substring(token.raw.length);
  3227. lastToken = tokens[tokens.length - 1];
  3228. if (lastToken && token.type === "text" && lastToken.type === "text") {
  3229. lastToken.raw += token.raw;
  3230. lastToken.text += token.text;
  3231. } else {
  3232. tokens.push(token);
  3233. }
  3234. continue;
  3235. }
  3236. if (token = this.tokenizer.link(src)) {
  3237. src = src.substring(token.raw.length);
  3238. tokens.push(token);
  3239. continue;
  3240. }
  3241. if (token = this.tokenizer.reflink(src, this.tokens.links)) {
  3242. src = src.substring(token.raw.length);
  3243. lastToken = tokens[tokens.length - 1];
  3244. if (lastToken && token.type === "text" && lastToken.type === "text") {
  3245. lastToken.raw += token.raw;
  3246. lastToken.text += token.text;
  3247. } else {
  3248. tokens.push(token);
  3249. }
  3250. continue;
  3251. }
  3252. if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) {
  3253. src = src.substring(token.raw.length);
  3254. tokens.push(token);
  3255. continue;
  3256. }
  3257. if (token = this.tokenizer.codespan(src)) {
  3258. src = src.substring(token.raw.length);
  3259. tokens.push(token);
  3260. continue;
  3261. }
  3262. if (token = this.tokenizer.br(src)) {
  3263. src = src.substring(token.raw.length);
  3264. tokens.push(token);
  3265. continue;
  3266. }
  3267. if (token = this.tokenizer.del(src)) {
  3268. src = src.substring(token.raw.length);
  3269. tokens.push(token);
  3270. continue;
  3271. }
  3272. if (token = this.tokenizer.autolink(src)) {
  3273. src = src.substring(token.raw.length);
  3274. tokens.push(token);
  3275. continue;
  3276. }
  3277. if (!this.state.inLink && (token = this.tokenizer.url(src))) {
  3278. src = src.substring(token.raw.length);
  3279. tokens.push(token);
  3280. continue;
  3281. }
  3282. cutSrc = src;
  3283. if (this.options.extensions && this.options.extensions.startInline) {
  3284. let startIndex = Infinity;
  3285. const tempSrc = src.slice(1);
  3286. let tempStart;
  3287. this.options.extensions.startInline.forEach((getStartIndex) => {
  3288. tempStart = getStartIndex.call({
  3289. lexer: this
  3290. }, tempSrc);
  3291. if (typeof tempStart === "number" && tempStart >= 0) {
  3292. startIndex = Math.min(startIndex, tempStart);
  3293. }
  3294. });
  3295. if (startIndex < Infinity && startIndex >= 0) {
  3296. cutSrc = src.substring(0, startIndex + 1);
  3297. }
  3298. }
  3299. if (token = this.tokenizer.inlineText(cutSrc)) {
  3300. src = src.substring(token.raw.length);
  3301. if (token.raw.slice(-1) !== "_") {
  3302. prevChar = token.raw.slice(-1);
  3303. }
  3304. keepPrevChar = true;
  3305. lastToken = tokens[tokens.length - 1];
  3306. if (lastToken && lastToken.type === "text") {
  3307. lastToken.raw += token.raw;
  3308. lastToken.text += token.text;
  3309. } else {
  3310. tokens.push(token);
  3311. }
  3312. continue;
  3313. }
  3314. if (src) {
  3315. const errMsg = "Infinite loop on byte: " + src.charCodeAt(0);
  3316. if (this.options.silent) {
  3317. console.error(errMsg);
  3318. break;
  3319. } else {
  3320. throw new Error(errMsg);
  3321. }
  3322. }
  3323. }
  3324. return tokens;
  3325. }
  3326. }
  3327. class _Renderer {
  3328. // set by the parser
  3329. constructor(options) {
  3330. __publicField(this, "options");
  3331. __publicField(this, "parser");
  3332. this.options = options || _defaults;
  3333. }
  3334. space(token) {
  3335. return "";
  3336. }
  3337. code({
  3338. text,
  3339. lang,
  3340. escaped
  3341. }) {
  3342. var _a;
  3343. const langString = (_a = (lang || "").match(/^\S*/)) == null ? void 0 : _a[0];
  3344. const code = text.replace(/\n$/, "") + "\n";
  3345. if (!langString) {
  3346. return "<pre><code>" + (escaped ? code : escape$1(code, true)) + "</code></pre>\n";
  3347. }
  3348. return '<pre><code class="language-' + escape$1(langString) + '">' + (escaped ? code : escape$1(code, true)) + "</code></pre>\n";
  3349. }
  3350. blockquote({
  3351. tokens
  3352. }) {
  3353. const body = this.parser.parse(tokens);
  3354. return `<blockquote>
  3355. ${body}</blockquote>
  3356. `;
  3357. }
  3358. html({
  3359. text
  3360. }) {
  3361. return text;
  3362. }
  3363. heading({
  3364. tokens,
  3365. depth
  3366. }) {
  3367. return `<h${depth}>${this.parser.parseInline(tokens)}</h${depth}>
  3368. `;
  3369. }
  3370. hr(token) {
  3371. return "<hr>\n";
  3372. }
  3373. list(token) {
  3374. const ordered = token.ordered;
  3375. const start = token.start;
  3376. let body = "";
  3377. for (let j = 0; j < token.items.length; j++) {
  3378. const item = token.items[j];
  3379. body += this.listitem(item);
  3380. }
  3381. const type = ordered ? "ol" : "ul";
  3382. const startAttr = ordered && start !== 1 ? ' start="' + start + '"' : "";
  3383. return "<" + type + startAttr + ">\n" + body + "</" + type + ">\n";
  3384. }
  3385. listitem(item) {
  3386. let itemBody = "";
  3387. if (item.task) {
  3388. const checkbox = this.checkbox({
  3389. checked: !!item.checked
  3390. });
  3391. if (item.loose) {
  3392. if (item.tokens.length > 0 && item.tokens[0].type === "paragraph") {
  3393. item.tokens[0].text = checkbox + " " + item.tokens[0].text;
  3394. if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === "text") {
  3395. item.tokens[0].tokens[0].text = checkbox + " " + item.tokens[0].tokens[0].text;
  3396. }
  3397. } else {
  3398. item.tokens.unshift({
  3399. type: "text",
  3400. raw: checkbox + " ",
  3401. text: checkbox + " "
  3402. });
  3403. }
  3404. } else {
  3405. itemBody += checkbox + " ";
  3406. }
  3407. }
  3408. itemBody += this.parser.parse(item.tokens, !!item.loose);
  3409. return `<li>${itemBody}</li>
  3410. `;
  3411. }
  3412. checkbox({
  3413. checked
  3414. }) {
  3415. return "<input " + (checked ? 'checked="" ' : "") + 'disabled="" type="checkbox">';
  3416. }
  3417. paragraph({
  3418. tokens
  3419. }) {
  3420. return `<p>${this.parser.parseInline(tokens)}</p>
  3421. `;
  3422. }
  3423. table(token) {
  3424. let header = "";
  3425. let cell = "";
  3426. for (let j = 0; j < token.header.length; j++) {
  3427. cell += this.tablecell(token.header[j]);
  3428. }
  3429. header += this.tablerow({
  3430. text: cell
  3431. });
  3432. let body = "";
  3433. for (let j = 0; j < token.rows.length; j++) {
  3434. const row = token.rows[j];
  3435. cell = "";
  3436. for (let k = 0; k < row.length; k++) {
  3437. cell += this.tablecell(row[k]);
  3438. }
  3439. body += this.tablerow({
  3440. text: cell
  3441. });
  3442. }
  3443. if (body)
  3444. body = `<tbody>${body}</tbody>`;
  3445. return "<table>\n<thead>\n" + header + "</thead>\n" + body + "</table>\n";
  3446. }
  3447. tablerow({
  3448. text
  3449. }) {
  3450. return `<tr>
  3451. ${text}</tr>
  3452. `;
  3453. }
  3454. tablecell(token) {
  3455. const content = this.parser.parseInline(token.tokens);
  3456. const type = token.header ? "th" : "td";
  3457. const tag2 = token.align ? `<${type} align="${token.align}">` : `<${type}>`;
  3458. return tag2 + content + `</${type}>
  3459. `;
  3460. }
  3461. /**
  3462. * span level renderer
  3463. */
  3464. strong({
  3465. tokens
  3466. }) {
  3467. return `<strong>${this.parser.parseInline(tokens)}</strong>`;
  3468. }
  3469. em({
  3470. tokens
  3471. }) {
  3472. return `<em>${this.parser.parseInline(tokens)}</em>`;
  3473. }
  3474. codespan({
  3475. text
  3476. }) {
  3477. return `<code>${text}</code>`;
  3478. }
  3479. br(token) {
  3480. return "<br>";
  3481. }
  3482. del({
  3483. tokens
  3484. }) {
  3485. return `<del>${this.parser.parseInline(tokens)}</del>`;
  3486. }
  3487. link({
  3488. href,
  3489. title,
  3490. tokens
  3491. }) {
  3492. const text = this.parser.parseInline(tokens);
  3493. const cleanHref = cleanUrl(href);
  3494. if (cleanHref === null) {
  3495. return text;
  3496. }
  3497. href = cleanHref;
  3498. let out = '<a href="' + href + '"';
  3499. if (title) {
  3500. out += ' title="' + title + '"';
  3501. }
  3502. out += ">" + text + "</a>";
  3503. return out;
  3504. }
  3505. image({
  3506. href,
  3507. title,
  3508. text
  3509. }) {
  3510. const cleanHref = cleanUrl(href);
  3511. if (cleanHref === null) {
  3512. return text;
  3513. }
  3514. href = cleanHref;
  3515. let out = `<img src="${href}" alt="${text}"`;
  3516. if (title) {
  3517. out += ` title="${title}"`;
  3518. }
  3519. out += ">";
  3520. return out;
  3521. }
  3522. text(token) {
  3523. return "tokens" in token && token.tokens ? this.parser.parseInline(token.tokens) : token.text;
  3524. }
  3525. }
  3526. class _TextRenderer {
  3527. // no need for block level renderers
  3528. strong({
  3529. text
  3530. }) {
  3531. return text;
  3532. }
  3533. em({
  3534. text
  3535. }) {
  3536. return text;
  3537. }
  3538. codespan({
  3539. text
  3540. }) {
  3541. return text;
  3542. }
  3543. del({
  3544. text
  3545. }) {
  3546. return text;
  3547. }
  3548. html({
  3549. text
  3550. }) {
  3551. return text;
  3552. }
  3553. text({
  3554. text
  3555. }) {
  3556. return text;
  3557. }
  3558. link({
  3559. text
  3560. }) {
  3561. return "" + text;
  3562. }
  3563. image({
  3564. text
  3565. }) {
  3566. return "" + text;
  3567. }
  3568. br() {
  3569. return "";
  3570. }
  3571. }
  3572. class _Parser {
  3573. constructor(options) {
  3574. __publicField(this, "options");
  3575. __publicField(this, "renderer");
  3576. __publicField(this, "textRenderer");
  3577. this.options = options || _defaults;
  3578. this.options.renderer = this.options.renderer || new _Renderer();
  3579. this.renderer = this.options.renderer;
  3580. this.renderer.options = this.options;
  3581. this.renderer.parser = this;
  3582. this.textRenderer = new _TextRenderer();
  3583. }
  3584. /**
  3585. * Static Parse Method
  3586. */
  3587. static parse(tokens, options) {
  3588. const parser = new _Parser(options);
  3589. return parser.parse(tokens);
  3590. }
  3591. /**
  3592. * Static Parse Inline Method
  3593. */
  3594. static parseInline(tokens, options) {
  3595. const parser = new _Parser(options);
  3596. return parser.parseInline(tokens);
  3597. }
  3598. /**
  3599. * Parse Loop
  3600. */
  3601. parse(tokens, top = true) {
  3602. let out = "";
  3603. for (let i = 0; i < tokens.length; i++) {
  3604. const anyToken = tokens[i];
  3605. if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[anyToken.type]) {
  3606. const genericToken = anyToken;
  3607. const ret = this.options.extensions.renderers[genericToken.type].call({
  3608. parser: this
  3609. }, genericToken);
  3610. if (ret !== false || !["space", "hr", "heading", "code", "table", "blockquote", "list", "html", "paragraph", "text"].includes(genericToken.type)) {
  3611. out += ret || "";
  3612. continue;
  3613. }
  3614. }
  3615. const token = anyToken;
  3616. switch (token.type) {
  3617. case "space": {
  3618. out += this.renderer.space(token);
  3619. continue;
  3620. }
  3621. case "hr": {
  3622. out += this.renderer.hr(token);
  3623. continue;
  3624. }
  3625. case "heading": {
  3626. out += this.renderer.heading(token);
  3627. continue;
  3628. }
  3629. case "code": {
  3630. out += this.renderer.code(token);
  3631. continue;
  3632. }
  3633. case "table": {
  3634. out += this.renderer.table(token);
  3635. continue;
  3636. }
  3637. case "blockquote": {
  3638. out += this.renderer.blockquote(token);
  3639. continue;
  3640. }
  3641. case "list": {
  3642. out += this.renderer.list(token);
  3643. continue;
  3644. }
  3645. case "html": {
  3646. out += this.renderer.html(token);
  3647. continue;
  3648. }
  3649. case "paragraph": {
  3650. out += this.renderer.paragraph(token);
  3651. continue;
  3652. }
  3653. case "text": {
  3654. let textToken = token;
  3655. let body = this.renderer.text(textToken);
  3656. while (i + 1 < tokens.length && tokens[i + 1].type === "text") {
  3657. textToken = tokens[++i];
  3658. body += "\n" + this.renderer.text(textToken);
  3659. }
  3660. if (top) {
  3661. out += this.renderer.paragraph({
  3662. type: "paragraph",
  3663. raw: body,
  3664. text: body,
  3665. tokens: [{
  3666. type: "text",
  3667. raw: body,
  3668. text: body
  3669. }]
  3670. });
  3671. } else {
  3672. out += body;
  3673. }
  3674. continue;
  3675. }
  3676. default: {
  3677. const errMsg = 'Token with "' + token.type + '" type was not found.';
  3678. if (this.options.silent) {
  3679. console.error(errMsg);
  3680. return "";
  3681. } else {
  3682. throw new Error(errMsg);
  3683. }
  3684. }
  3685. }
  3686. }
  3687. return out;
  3688. }
  3689. /**
  3690. * Parse Inline Tokens
  3691. */
  3692. parseInline(tokens, renderer) {
  3693. renderer = renderer || this.renderer;
  3694. let out = "";
  3695. for (let i = 0; i < tokens.length; i++) {
  3696. const anyToken = tokens[i];
  3697. if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[anyToken.type]) {
  3698. const ret = this.options.extensions.renderers[anyToken.type].call({
  3699. parser: this
  3700. }, anyToken);
  3701. if (ret !== false || !["escape", "html", "link", "image", "strong", "em", "codespan", "br", "del", "text"].includes(anyToken.type)) {
  3702. out += ret || "";
  3703. continue;
  3704. }
  3705. }
  3706. const token = anyToken;
  3707. switch (token.type) {
  3708. case "escape": {
  3709. out += renderer.text(token);
  3710. break;
  3711. }
  3712. case "html": {
  3713. out += renderer.html(token);
  3714. break;
  3715. }
  3716. case "link": {
  3717. out += renderer.link(token);
  3718. break;
  3719. }
  3720. case "image": {
  3721. out += renderer.image(token);
  3722. break;
  3723. }
  3724. case "strong": {
  3725. out += renderer.strong(token);
  3726. break;
  3727. }
  3728. case "em": {
  3729. out += renderer.em(token);
  3730. break;
  3731. }
  3732. case "codespan": {
  3733. out += renderer.codespan(token);
  3734. break;
  3735. }
  3736. case "br": {
  3737. out += renderer.br(token);
  3738. break;
  3739. }
  3740. case "del": {
  3741. out += renderer.del(token);
  3742. break;
  3743. }
  3744. case "text": {
  3745. out += renderer.text(token);
  3746. break;
  3747. }
  3748. default: {
  3749. const errMsg = 'Token with "' + token.type + '" type was not found.';
  3750. if (this.options.silent) {
  3751. console.error(errMsg);
  3752. return "";
  3753. } else {
  3754. throw new Error(errMsg);
  3755. }
  3756. }
  3757. }
  3758. }
  3759. return out;
  3760. }
  3761. }
  3762. class _Hooks {
  3763. constructor(options) {
  3764. __publicField(this, "options");
  3765. __publicField(this, "block");
  3766. this.options = options || _defaults;
  3767. }
  3768. /**
  3769. * Process markdown before marked
  3770. */
  3771. preprocess(markdown) {
  3772. return markdown;
  3773. }
  3774. /**
  3775. * Process HTML after marked is finished
  3776. */
  3777. postprocess(html2) {
  3778. return html2;
  3779. }
  3780. /**
  3781. * Process all tokens before walk tokens
  3782. */
  3783. processAllTokens(tokens) {
  3784. return tokens;
  3785. }
  3786. /**
  3787. * Provide function to tokenize markdown
  3788. */
  3789. provideLexer() {
  3790. return this.block ? _Lexer.lex : _Lexer.lexInline;
  3791. }
  3792. /**
  3793. * Provide function to parse tokens
  3794. */
  3795. provideParser() {
  3796. return this.block ? _Parser.parse : _Parser.parseInline;
  3797. }
  3798. }
  3799. __publicField(_Hooks, "passThroughHooks", /* @__PURE__ */ new Set([
  3800. "preprocess",
  3801. "postprocess",
  3802. "processAllTokens"
  3803. ]));
  3804. class Marked {
  3805. constructor(...args) {
  3806. __publicField(this, "defaults", _getDefaults());
  3807. __publicField(this, "options", this.setOptions);
  3808. __publicField(this, "parse", this.parseMarkdown(true));
  3809. __publicField(this, "parseInline", this.parseMarkdown(false));
  3810. __publicField(this, "Parser", _Parser);
  3811. __publicField(this, "Renderer", _Renderer);
  3812. __publicField(this, "TextRenderer", _TextRenderer);
  3813. __publicField(this, "Lexer", _Lexer);
  3814. __publicField(this, "Tokenizer", _Tokenizer);
  3815. __publicField(this, "Hooks", _Hooks);
  3816. this.use(...args);
  3817. }
  3818. /**
  3819. * Run callback for every token
  3820. */
  3821. walkTokens(tokens, callback) {
  3822. var _a, _b;
  3823. let values = [];
  3824. for (const token of tokens) {
  3825. values = values.concat(callback.call(this, token));
  3826. switch (token.type) {
  3827. case "table": {
  3828. const tableToken = token;
  3829. for (const cell of tableToken.header) {
  3830. values = values.concat(this.walkTokens(cell.tokens, callback));
  3831. }
  3832. for (const row of tableToken.rows) {
  3833. for (const cell of row) {
  3834. values = values.concat(this.walkTokens(cell.tokens, callback));
  3835. }
  3836. }
  3837. break;
  3838. }
  3839. case "list": {
  3840. const listToken = token;
  3841. values = values.concat(this.walkTokens(listToken.items, callback));
  3842. break;
  3843. }
  3844. default: {
  3845. const genericToken = token;
  3846. if ((_b = (_a = this.defaults.extensions) == null ? void 0 : _a.childTokens) == null ? void 0 : _b[genericToken.type]) {
  3847. this.defaults.extensions.childTokens[genericToken.type].forEach((childTokens) => {
  3848. const tokens2 = genericToken[childTokens].flat(Infinity);
  3849. values = values.concat(this.walkTokens(tokens2, callback));
  3850. });
  3851. } else if (genericToken.tokens) {
  3852. values = values.concat(this.walkTokens(genericToken.tokens, callback));
  3853. }
  3854. }
  3855. }
  3856. }
  3857. return values;
  3858. }
  3859. use(...args) {
  3860. const extensions = this.defaults.extensions || {
  3861. renderers: {},
  3862. childTokens: {}
  3863. };
  3864. args.forEach((pack) => {
  3865. const opts = {
  3866. ...pack
  3867. };
  3868. opts.async = this.defaults.async || opts.async || false;
  3869. if (pack.extensions) {
  3870. pack.extensions.forEach((ext) => {
  3871. if (!ext.name) {
  3872. throw new Error("extension name required");
  3873. }
  3874. if ("renderer" in ext) {
  3875. const prevRenderer = extensions.renderers[ext.name];
  3876. if (prevRenderer) {
  3877. extensions.renderers[ext.name] = function (...args2) {
  3878. let ret = ext.renderer.apply(this, args2);
  3879. if (ret === false) {
  3880. ret = prevRenderer.apply(this, args2);
  3881. }
  3882. return ret;
  3883. };
  3884. } else {
  3885. extensions.renderers[ext.name] = ext.renderer;
  3886. }
  3887. }
  3888. if ("tokenizer" in ext) {
  3889. if (!ext.level || ext.level !== "block" && ext.level !== "inline") {
  3890. throw new Error("extension level must be 'block' or 'inline'");
  3891. }
  3892. const extLevel = extensions[ext.level];
  3893. if (extLevel) {
  3894. extLevel.unshift(ext.tokenizer);
  3895. } else {
  3896. extensions[ext.level] = [ext.tokenizer];
  3897. }
  3898. if (ext.start) {
  3899. if (ext.level === "block") {
  3900. if (extensions.startBlock) {
  3901. extensions.startBlock.push(ext.start);
  3902. } else {
  3903. extensions.startBlock = [ext.start];
  3904. }
  3905. } else if (ext.level === "inline") {
  3906. if (extensions.startInline) {
  3907. extensions.startInline.push(ext.start);
  3908. } else {
  3909. extensions.startInline = [ext.start];
  3910. }
  3911. }
  3912. }
  3913. }
  3914. if ("childTokens" in ext && ext.childTokens) {
  3915. extensions.childTokens[ext.name] = ext.childTokens;
  3916. }
  3917. });
  3918. opts.extensions = extensions;
  3919. }
  3920. if (pack.renderer) {
  3921. const renderer = this.defaults.renderer || new _Renderer(this.defaults);
  3922. for (const prop in pack.renderer) {
  3923. if (!(prop in renderer)) {
  3924. throw new Error(`renderer '${prop}' does not exist`);
  3925. }
  3926. if (["options", "parser"].includes(prop)) {
  3927. continue;
  3928. }
  3929. const rendererProp = prop;
  3930. const rendererFunc = pack.renderer[rendererProp];
  3931. const prevRenderer = renderer[rendererProp];
  3932. renderer[rendererProp] = (...args2) => {
  3933. let ret = rendererFunc.apply(renderer, args2);
  3934. if (ret === false) {
  3935. ret = prevRenderer.apply(renderer, args2);
  3936. }
  3937. return ret || "";
  3938. };
  3939. }
  3940. opts.renderer = renderer;
  3941. }
  3942. if (pack.tokenizer) {
  3943. const tokenizer = this.defaults.tokenizer || new _Tokenizer(this.defaults);
  3944. for (const prop in pack.tokenizer) {
  3945. if (!(prop in tokenizer)) {
  3946. throw new Error(`tokenizer '${prop}' does not exist`);
  3947. }
  3948. if (["options", "rules", "lexer"].includes(prop)) {
  3949. continue;
  3950. }
  3951. const tokenizerProp = prop;
  3952. const tokenizerFunc = pack.tokenizer[tokenizerProp];
  3953. const prevTokenizer = tokenizer[tokenizerProp];
  3954. tokenizer[tokenizerProp] = (...args2) => {
  3955. let ret = tokenizerFunc.apply(tokenizer, args2);
  3956. if (ret === false) {
  3957. ret = prevTokenizer.apply(tokenizer, args2);
  3958. }
  3959. return ret;
  3960. };
  3961. }
  3962. opts.tokenizer = tokenizer;
  3963. }
  3964. if (pack.hooks) {
  3965. const hooks = this.defaults.hooks || new _Hooks();
  3966. for (const prop in pack.hooks) {
  3967. if (!(prop in hooks)) {
  3968. throw new Error(`hook '${prop}' does not exist`);
  3969. }
  3970. if (["options", "block"].includes(prop)) {
  3971. continue;
  3972. }
  3973. const hooksProp = prop;
  3974. const hooksFunc = pack.hooks[hooksProp];
  3975. const prevHook = hooks[hooksProp];
  3976. if (_Hooks.passThroughHooks.has(prop)) {
  3977. hooks[hooksProp] = (arg) => {
  3978. if (this.defaults.async) {
  3979. return Promise.resolve(hooksFunc.call(hooks, arg)).then((ret2) => {
  3980. return prevHook.call(hooks, ret2);
  3981. });
  3982. }
  3983. const ret = hooksFunc.call(hooks, arg);
  3984. return prevHook.call(hooks, ret);
  3985. };
  3986. } else {
  3987. hooks[hooksProp] = (...args2) => {
  3988. let ret = hooksFunc.apply(hooks, args2);
  3989. if (ret === false) {
  3990. ret = prevHook.apply(hooks, args2);
  3991. }
  3992. return ret;
  3993. };
  3994. }
  3995. }
  3996. opts.hooks = hooks;
  3997. }
  3998. if (pack.walkTokens) {
  3999. const walkTokens = this.defaults.walkTokens;
  4000. const packWalktokens = pack.walkTokens;
  4001. opts.walkTokens = function (token) {
  4002. let values = [];
  4003. values.push(packWalktokens.call(this, token));
  4004. if (walkTokens) {
  4005. values = values.concat(walkTokens.call(this, token));
  4006. }
  4007. return values;
  4008. };
  4009. }
  4010. this.defaults = {
  4011. ...this.defaults,
  4012. ...opts
  4013. };
  4014. });
  4015. return this;
  4016. }
  4017. setOptions(opt) {
  4018. this.defaults = {
  4019. ...this.defaults,
  4020. ...opt
  4021. };
  4022. return this;
  4023. }
  4024. lexer(src, options) {
  4025. return _Lexer.lex(src, options ? options : this.defaults);
  4026. }
  4027. parser(tokens, options) {
  4028. return _Parser.parse(tokens, options ? options : this.defaults);
  4029. }
  4030. parseMarkdown(blockType) {
  4031. const parse = (src, options) => {
  4032. const origOpt = {
  4033. ...options
  4034. };
  4035. const opt = {
  4036. ...this.defaults,
  4037. ...origOpt
  4038. };
  4039. const throwError = this.onError(!!opt.silent, !!opt.async);
  4040. if (this.defaults.async === true && origOpt.async === false) {
  4041. return throwError(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));
  4042. }
  4043. if (typeof src === "undefined" || src === null) {
  4044. return throwError(new Error("marked(): input parameter is undefined or null"));
  4045. }
  4046. if (typeof src !== "string") {
  4047. return throwError(new Error("marked(): input parameter is of type " + Object.prototype.toString.call(src) + ", string expected"));
  4048. }
  4049. if (opt.hooks) {
  4050. opt.hooks.options = opt;
  4051. opt.hooks.block = blockType;
  4052. }
  4053. const lexer = opt.hooks ? opt.hooks.provideLexer() : blockType ? _Lexer.lex : _Lexer.lexInline;
  4054. const parser = opt.hooks ? opt.hooks.provideParser() : blockType ? _Parser.parse : _Parser.parseInline;
  4055. if (opt.async) {
  4056. return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src).then((src2) => lexer(src2, opt)).then((tokens) => opt.hooks ? opt.hooks.processAllTokens(tokens) : tokens).then((tokens) => opt.walkTokens ? Promise.all(this.walkTokens(tokens, opt.walkTokens)).then(() => tokens) : tokens).then((tokens) => parser(tokens, opt)).then((html2) => opt.hooks ? opt.hooks.postprocess(html2) : html2).catch(throwError);
  4057. }
  4058. try {
  4059. if (opt.hooks) {
  4060. src = opt.hooks.preprocess(src);
  4061. }
  4062. let tokens = lexer(src, opt);
  4063. if (opt.hooks) {
  4064. tokens = opt.hooks.processAllTokens(tokens);
  4065. }
  4066. if (opt.walkTokens) {
  4067. this.walkTokens(tokens, opt.walkTokens);
  4068. }
  4069. let html2 = parser(tokens, opt);
  4070. if (opt.hooks) {
  4071. html2 = opt.hooks.postprocess(html2);
  4072. }
  4073. return html2;
  4074. } catch (e) {
  4075. return throwError(e);
  4076. }
  4077. };
  4078. return parse;
  4079. }
  4080. onError(silent, async) {
  4081. return (e) => {
  4082. e.message += "\nPlease report this to https://github.com/markedjs/marked.";
  4083. if (silent) {
  4084. const msg = "<p>An error occurred:</p><pre>" + escape$1(e.message + "", true) + "</pre>";
  4085. if (async) {
  4086. return Promise.resolve(msg);
  4087. }
  4088. return msg;
  4089. }
  4090. if (async) {
  4091. return Promise.reject(e);
  4092. }
  4093. throw e;
  4094. };
  4095. }
  4096. }
  4097. const markedInstance = new Marked();
  4098.  
  4099. function marked(src, opt) {
  4100. return markedInstance.parse(src, opt);
  4101. }
  4102. marked.options = marked.setOptions = function (options) {
  4103. markedInstance.setOptions(options);
  4104. marked.defaults = markedInstance.defaults;
  4105. changeDefaults(marked.defaults);
  4106. return marked;
  4107. };
  4108. marked.getDefaults = _getDefaults;
  4109. marked.defaults = _defaults;
  4110. marked.use = function (...args) {
  4111. markedInstance.use(...args);
  4112. marked.defaults = markedInstance.defaults;
  4113. changeDefaults(marked.defaults);
  4114. return marked;
  4115. };
  4116. marked.walkTokens = function (tokens, callback) {
  4117. return markedInstance.walkTokens(tokens, callback);
  4118. };
  4119. marked.parseInline = markedInstance.parseInline;
  4120. marked.Parser = _Parser;
  4121. marked.parser = _Parser.parse;
  4122. marked.Renderer = _Renderer;
  4123. marked.TextRenderer = _TextRenderer;
  4124. marked.Lexer = _Lexer;
  4125. marked.lexer = _Lexer.lex;
  4126. marked.Tokenizer = _Tokenizer;
  4127. marked.Hooks = _Hooks;
  4128. marked.parse = marked;
  4129. marked.options;
  4130. marked.setOptions;
  4131. marked.use;
  4132. marked.walkTokens;
  4133. marked.parseInline;
  4134. _Parser.parse;
  4135. _Lexer.lex;
  4136.  
  4137. function md2html(md) {
  4138. return marked(md);
  4139. }
  4140. const _export_sfc = (sfc, props) => {
  4141. const target = sfc.__vccOpts || sfc;
  4142. for (const [key, val] of props) {
  4143. target[key] = val;
  4144. }
  4145. return target;
  4146. };
  4147. const _withScopeId = (n) => (vue.pushScopeId("data-v-1db30056"), n = n(), vue.popScopeId(), n);
  4148. const _hoisted_1 = {
  4149. class: "monkeygpt-card"
  4150. };
  4151. const _hoisted_2 = {
  4152. class: "monkeygpt-header"
  4153. };
  4154. const _hoisted_3 = {
  4155. key: 0,
  4156. class: "nav"
  4157. };
  4158. const _hoisted_4 = {
  4159. class: "title"
  4160. };
  4161. const _hoisted_5 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("a", {
  4162. href: "https://github.com/weekend-project-space/monkey-gpt"
  4163. }, [
  4164. /* @__PURE__ */
  4165. vue.createElementVNode("img", {
  4166. src: "https://img.shields.io/github/stars/weekend-project-space/monkey-gpt.svg?style=social&label=Stars",
  4167. alt: ""
  4168. })
  4169. ], -1));
  4170. const _hoisted_6 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("path", {
  4171. d: "M18 6L6 18M6 6l12 12",
  4172. stroke: "black",
  4173. "stroke-width": "2",
  4174. "stroke-linecap": "round"
  4175. }, null, -1));
  4176. const _hoisted_7 = [
  4177. _hoisted_6
  4178. ];
  4179. const _hoisted_8 = {
  4180. key: 0,
  4181. class: "monkeygpt-body"
  4182. };
  4183. const _hoisted_9 = {
  4184. key: 0,
  4185. class: "loading",
  4186. viewBox: "0 0 100 100",
  4187. xmlns: "http://www.w3.org/2000/svg"
  4188. };
  4189. const _hoisted_10 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("circle", {
  4190. cx: "50",
  4191. cy: "50",
  4192. r: "0",
  4193. fill: "black"
  4194. }, [
  4195. /* @__PURE__ */
  4196. vue.createElementVNode("animate", {
  4197. attributeName: "r",
  4198. values: "10;30;10",
  4199. dur: "1s",
  4200. repeatCount: "indefinite"
  4201. })
  4202. ], -1));
  4203. const _hoisted_11 = [
  4204. _hoisted_10
  4205. ];
  4206. const _hoisted_12 = ["innerHTML"];
  4207. const _sfc_main$1 = {
  4208. __name: "MonkeyGPT",
  4209. props: {
  4210. msg: String
  4211. },
  4212. setup(__props) {
  4213. const txt = vue.ref("");
  4214. const loading = vue.ref(false);
  4215. const canSearch = vue.ref(false);
  4216.  
  4217. function getText() {
  4218. loading.value = true;
  4219. txt.value = md2html(getSimpleText());
  4220. loading.value = false;
  4221. }
  4222. async function chat2(chatfun) {
  4223. clear();
  4224. loading.value = true;
  4225. try {
  4226. txt.value = md2html(await chatfun(getSimpleText()));
  4227. } catch {
  4228. txt.value = "生成失败,请检查配置是否正确并刷新重试!";
  4229. }
  4230. loading.value = false;
  4231. }
  4232.  
  4233. function clear() {
  4234. txt.value = "";
  4235. }
  4236. window.addEventListener("hashchange", () => {
  4237. clear();
  4238. autoSearch();
  4239. });
  4240. window.addEventListener("popstate", () => {
  4241. clear();
  4242. autoSearch();
  4243. });
  4244. autoSearch();
  4245.  
  4246. function autoSearch() {
  4247. const searchKey = getSearchKey();
  4248. canSearch.value = false;
  4249. if (searchKey) {
  4250. canSearch.value = true;
  4251. chat2(async () => {
  4252. return `### 以下是针对 "${searchKey}" 生成的结果
  4253.  
  4254. ` + await search(searchKey);
  4255. });
  4256. }
  4257. }
  4258. const bodyShow = vue.computed(() => loading.value || txt.value);
  4259. return (_ctx, _cache) => {
  4260. return vue.openBlock(), vue.createElementBlock("div", {
  4261. class: vue.normalizeClass({
  4262. "monkeygpt-warp": bodyShow.value
  4263. })
  4264. }, [
  4265. vue.createElementVNode("div", _hoisted_1, [
  4266. vue.createElementVNode("div", _hoisted_2, [
  4267. bodyShow.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3, [
  4268. vue.createElementVNode("h3", _hoisted_4, [
  4269. vue.createTextVNode(vue.toDisplayString(__props.msg) + " ", 1),
  4270. _hoisted_5
  4271. ]),
  4272. (vue.openBlock(), vue.createElementBlock("svg", {
  4273. class: "close",
  4274. onClick: clear,
  4275. viewBox: "0 0 24 24",
  4276. xmlns: "http://www.w3.org/2000/svg"
  4277. }, _hoisted_7))
  4278. ])) : vue.createCommentVNode("", true),
  4279. vue.createElementVNode("div", null, [
  4280. canSearch.value ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, {
  4281. key: 0
  4282. }, [
  4283. vue.createElementVNode("button", {
  4284. onClick: autoSearch
  4285. }, "生成"),
  4286. vue.createElementVNode("button", {
  4287. onClick: _cache[0] || (_cache[0] = ($event) => chat2(vue.unref(summarize)))
  4288. }, "总结本页面")
  4289. ], 64)) : (vue.openBlock(), vue.createElementBlock(vue.Fragment, {
  4290. key: 1
  4291. }, [
  4292. vue.createElementVNode("button", {
  4293. onClick: getText
  4294. }, "正文"),
  4295. vue.createElementVNode("button", {
  4296. onClick: _cache[1] || (_cache[1] = ($event) => chat2(vue.unref(summarize)))
  4297. }, "总结"),
  4298. vue.createElementVNode("button", {
  4299. onClick: _cache[2] || (_cache[2] = ($event) => chat2(vue.unref(ask)))
  4300. }, "回复")
  4301. ], 64))
  4302. ])
  4303. ]),
  4304. txt.value || loading.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_8, [
  4305. loading.value ? (vue.openBlock(), vue.createElementBlock("svg", _hoisted_9, _hoisted_11)) : txt.value ? (vue.openBlock(), vue.createElementBlock("div", {
  4306. key: 1,
  4307. innerHTML: txt.value
  4308. }, null, 8, _hoisted_12)) : vue.createCommentVNode("", true)
  4309. ])) : vue.createCommentVNode("", true)
  4310. ])
  4311. ], 2);
  4312. };
  4313. }
  4314. };
  4315. const MonkeyGPT = /* @__PURE__ */ _export_sfc(_sfc_main$1, [
  4316. ["__scopeId", "data-v-1db30056"]
  4317. ]);
  4318. const _sfc_main = {
  4319. __name: "App",
  4320. setup(__props) {
  4321. return (_ctx, _cache) => {
  4322. return vue.openBlock(), vue.createBlock(MonkeyGPT, {
  4323. msg: "monkey gpt"
  4324. });
  4325. };
  4326. }
  4327. };
  4328. vue.createApp(_sfc_main).mount(
  4329. (() => {
  4330. const app = document.createElement("div");
  4331. app.id = "monkeygpt";
  4332. const firstChild = document.body.firstChild;
  4333. document.body.insertBefore(app, firstChild);
  4334. return app;
  4335. })()
  4336. );
  4337.  
  4338. })(Vue);