monkey-gpt

monkeygpt

当前为 2024-08-31 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name monkey-gpt
  3. // @namespace monkeygpt
  4. // @version 0.0.2
  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 t = document.createElement("style");
  19. t.textContent = e, document.head.append(t)
  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:2rem}#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-card{position:absolute;top:30vh;right:0;border-radius:.5rem}.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.5rem;font-family:Lucida Grande,Tahoma,Arial,Verdana,sans-serif;border-radius:.1rem}.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.5rem;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}}@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-b0fd14ee]{margin-top:1rem}h3[data-v-b0fd14ee]{display:flex;align-content:center;margin-bottom:1rem}h3 img[data-v-b0fd14ee]{margin-left:1rem}.close[data-v-b0fd14ee]{width:1.5rem;cursor:pointer}.nav[data-v-b0fd14ee]{display:flex;justify-content:space-between}.loading[data-v-b0fd14ee]{width:3rem} ");
  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. async function chat$1(messages, model = "gpt-4o-mini", apiKey, apiEndpoint = "https://api.openai.com/v1/chat/completions") {
  1908. const headers = {
  1909. "Content-Type": "application/json",
  1910. "Authorization": `Bearer ${apiKey}`
  1911. };
  1912. const requestBody = {
  1913. model,
  1914. // 使用的模型名称
  1915. messages
  1916. };
  1917. return fetch(apiEndpoint, {
  1918. method: "POST",
  1919. headers,
  1920. body: JSON.stringify(requestBody)
  1921. }).then((response) => response.json()).then(async (data) => {
  1922. const reply = data.choices[0].message.content;
  1923. return reply;
  1924. });
  1925. }
  1926. const defaultConfig = {
  1927. model: "gpt-4o-mini",
  1928. apiKey: "",
  1929. apiEndpoint: "https://ai.01234.fun/v1/chat/completions"
  1930. };
  1931. async function chat(content, config = defaultConfig) {
  1932. return await chat$1([{
  1933. role: "user",
  1934. content
  1935. }], config.model, config.apiKey, config.apiEndpoint);
  1936. }
  1937. async function summarize(text, config = defaultConfig) {
  1938. return chat(`请帮忙总结一下 :"${text}"`, config);
  1939. }
  1940. async function ask(text, config = defaultConfig) {
  1941. return chat(`请帮忙回复一下 :"${text}"`, config);
  1942. }
  1943.  
  1944. function _getDefaults() {
  1945. return {
  1946. async: false,
  1947. breaks: false,
  1948. extensions: null,
  1949. gfm: true,
  1950. hooks: null,
  1951. pedantic: false,
  1952. renderer: null,
  1953. silent: false,
  1954. tokenizer: null,
  1955. walkTokens: null
  1956. };
  1957. }
  1958. let _defaults = _getDefaults();
  1959.  
  1960. function changeDefaults(newDefaults) {
  1961. _defaults = newDefaults;
  1962. }
  1963. const escapeTest = /[&<>"']/;
  1964. const escapeReplace = new RegExp(escapeTest.source, "g");
  1965. const escapeTestNoEncode = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/;
  1966. const escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, "g");
  1967. const escapeReplacements = {
  1968. "&": "&amp;",
  1969. "<": "&lt;",
  1970. ">": "&gt;",
  1971. '"': "&quot;",
  1972. "'": "&#39;"
  1973. };
  1974. const getEscapeReplacement = (ch) => escapeReplacements[ch];
  1975.  
  1976. function escape$1(html2, encode) {
  1977. if (encode) {
  1978. if (escapeTest.test(html2)) {
  1979. return html2.replace(escapeReplace, getEscapeReplacement);
  1980. }
  1981. } else {
  1982. if (escapeTestNoEncode.test(html2)) {
  1983. return html2.replace(escapeReplaceNoEncode, getEscapeReplacement);
  1984. }
  1985. }
  1986. return html2;
  1987. }
  1988. const caret = /(^|[^\[])\^/g;
  1989.  
  1990. function edit(regex, opt) {
  1991. let source = typeof regex === "string" ? regex : regex.source;
  1992. opt = opt || "";
  1993. const obj = {
  1994. replace: (name, val) => {
  1995. let valSource = typeof val === "string" ? val : val.source;
  1996. valSource = valSource.replace(caret, "$1");
  1997. source = source.replace(name, valSource);
  1998. return obj;
  1999. },
  2000. getRegex: () => {
  2001. return new RegExp(source, opt);
  2002. }
  2003. };
  2004. return obj;
  2005. }
  2006.  
  2007. function cleanUrl(href) {
  2008. try {
  2009. href = encodeURI(href).replace(/%25/g, "%");
  2010. } catch {
  2011. return null;
  2012. }
  2013. return href;
  2014. }
  2015. const noopTest = {
  2016. exec: () => null
  2017. };
  2018.  
  2019. function splitCells(tableRow, count) {
  2020. const row = tableRow.replace(/\|/g, (match, offset, str) => {
  2021. let escaped = false;
  2022. let curr = offset;
  2023. while (--curr >= 0 && str[curr] === "\\")
  2024. escaped = !escaped;
  2025. if (escaped) {
  2026. return "|";
  2027. } else {
  2028. return " |";
  2029. }
  2030. }),
  2031. cells = row.split(/ \|/);
  2032. let i = 0;
  2033. if (!cells[0].trim()) {
  2034. cells.shift();
  2035. }
  2036. if (cells.length > 0 && !cells[cells.length - 1].trim()) {
  2037. cells.pop();
  2038. }
  2039. if (count) {
  2040. if (cells.length > count) {
  2041. cells.splice(count);
  2042. } else {
  2043. while (cells.length < count)
  2044. cells.push("");
  2045. }
  2046. }
  2047. for (; i < cells.length; i++) {
  2048. cells[i] = cells[i].trim().replace(/\\\|/g, "|");
  2049. }
  2050. return cells;
  2051. }
  2052.  
  2053. function rtrim(str, c, invert) {
  2054. const l = str.length;
  2055. if (l === 0) {
  2056. return "";
  2057. }
  2058. let suffLen = 0;
  2059. while (suffLen < l) {
  2060. const currChar = str.charAt(l - suffLen - 1);
  2061. if (currChar === c && !invert) {
  2062. suffLen++;
  2063. } else if (currChar !== c && invert) {
  2064. suffLen++;
  2065. } else {
  2066. break;
  2067. }
  2068. }
  2069. return str.slice(0, l - suffLen);
  2070. }
  2071.  
  2072. function findClosingBracket(str, b) {
  2073. if (str.indexOf(b[1]) === -1) {
  2074. return -1;
  2075. }
  2076. let level = 0;
  2077. for (let i = 0; i < str.length; i++) {
  2078. if (str[i] === "\\") {
  2079. i++;
  2080. } else if (str[i] === b[0]) {
  2081. level++;
  2082. } else if (str[i] === b[1]) {
  2083. level--;
  2084. if (level < 0) {
  2085. return i;
  2086. }
  2087. }
  2088. }
  2089. return -1;
  2090. }
  2091.  
  2092. function outputLink(cap, link2, raw, lexer) {
  2093. const href = link2.href;
  2094. const title = link2.title ? escape$1(link2.title) : null;
  2095. const text = cap[1].replace(/\\([\[\]])/g, "$1");
  2096. if (cap[0].charAt(0) !== "!") {
  2097. lexer.state.inLink = true;
  2098. const token = {
  2099. type: "link",
  2100. raw,
  2101. href,
  2102. title,
  2103. text,
  2104. tokens: lexer.inlineTokens(text)
  2105. };
  2106. lexer.state.inLink = false;
  2107. return token;
  2108. }
  2109. return {
  2110. type: "image",
  2111. raw,
  2112. href,
  2113. title,
  2114. text: escape$1(text)
  2115. };
  2116. }
  2117.  
  2118. function indentCodeCompensation(raw, text) {
  2119. const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
  2120. if (matchIndentToCode === null) {
  2121. return text;
  2122. }
  2123. const indentToCode = matchIndentToCode[1];
  2124. return text.split("\n").map((node) => {
  2125. const matchIndentInNode = node.match(/^\s+/);
  2126. if (matchIndentInNode === null) {
  2127. return node;
  2128. }
  2129. const [indentInNode] = matchIndentInNode;
  2130. if (indentInNode.length >= indentToCode.length) {
  2131. return node.slice(indentToCode.length);
  2132. }
  2133. return node;
  2134. }).join("\n");
  2135. }
  2136. class _Tokenizer {
  2137. // set by the lexer
  2138. constructor(options) {
  2139. __publicField(this, "options");
  2140. __publicField(this, "rules");
  2141. // set by the lexer
  2142. __publicField(this, "lexer");
  2143. this.options = options || _defaults;
  2144. }
  2145. space(src) {
  2146. const cap = this.rules.block.newline.exec(src);
  2147. if (cap && cap[0].length > 0) {
  2148. return {
  2149. type: "space",
  2150. raw: cap[0]
  2151. };
  2152. }
  2153. }
  2154. code(src) {
  2155. const cap = this.rules.block.code.exec(src);
  2156. if (cap) {
  2157. const text = cap[0].replace(/^ {1,4}/gm, "");
  2158. return {
  2159. type: "code",
  2160. raw: cap[0],
  2161. codeBlockStyle: "indented",
  2162. text: !this.options.pedantic ? rtrim(text, "\n") : text
  2163. };
  2164. }
  2165. }
  2166. fences(src) {
  2167. const cap = this.rules.block.fences.exec(src);
  2168. if (cap) {
  2169. const raw = cap[0];
  2170. const text = indentCodeCompensation(raw, cap[3] || "");
  2171. return {
  2172. type: "code",
  2173. raw,
  2174. lang: cap[2] ? cap[2].trim().replace(this.rules.inline.anyPunctuation, "$1") : cap[2],
  2175. text
  2176. };
  2177. }
  2178. }
  2179. heading(src) {
  2180. const cap = this.rules.block.heading.exec(src);
  2181. if (cap) {
  2182. let text = cap[2].trim();
  2183. if (/#$/.test(text)) {
  2184. const trimmed = rtrim(text, "#");
  2185. if (this.options.pedantic) {
  2186. text = trimmed.trim();
  2187. } else if (!trimmed || / $/.test(trimmed)) {
  2188. text = trimmed.trim();
  2189. }
  2190. }
  2191. return {
  2192. type: "heading",
  2193. raw: cap[0],
  2194. depth: cap[1].length,
  2195. text,
  2196. tokens: this.lexer.inline(text)
  2197. };
  2198. }
  2199. }
  2200. hr(src) {
  2201. const cap = this.rules.block.hr.exec(src);
  2202. if (cap) {
  2203. return {
  2204. type: "hr",
  2205. raw: rtrim(cap[0], "\n")
  2206. };
  2207. }
  2208. }
  2209. blockquote(src) {
  2210. const cap = this.rules.block.blockquote.exec(src);
  2211. if (cap) {
  2212. let lines = rtrim(cap[0], "\n").split("\n");
  2213. let raw = "";
  2214. let text = "";
  2215. const tokens = [];
  2216. while (lines.length > 0) {
  2217. let inBlockquote = false;
  2218. const currentLines = [];
  2219. let i;
  2220. for (i = 0; i < lines.length; i++) {
  2221. if (/^ {0,3}>/.test(lines[i])) {
  2222. currentLines.push(lines[i]);
  2223. inBlockquote = true;
  2224. } else if (!inBlockquote) {
  2225. currentLines.push(lines[i]);
  2226. } else {
  2227. break;
  2228. }
  2229. }
  2230. lines = lines.slice(i);
  2231. const currentRaw = currentLines.join("\n");
  2232. const currentText = currentRaw.replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g, "\n $1").replace(/^ {0,3}>[ \t]?/gm, "");
  2233. raw = raw ? `${raw}
  2234. ${currentRaw}` : currentRaw;
  2235. text = text ? `${text}
  2236. ${currentText}` : currentText;
  2237. const top = this.lexer.state.top;
  2238. this.lexer.state.top = true;
  2239. this.lexer.blockTokens(currentText, tokens, true);
  2240. this.lexer.state.top = top;
  2241. if (lines.length === 0) {
  2242. break;
  2243. }
  2244. const lastToken = tokens[tokens.length - 1];
  2245. if ((lastToken == null ? void 0 : lastToken.type) === "code") {
  2246. break;
  2247. } else if ((lastToken == null ? void 0 : lastToken.type) === "blockquote") {
  2248. const oldToken = lastToken;
  2249. const newText = oldToken.raw + "\n" + lines.join("\n");
  2250. const newToken = this.blockquote(newText);
  2251. tokens[tokens.length - 1] = newToken;
  2252. raw = raw.substring(0, raw.length - oldToken.raw.length) + newToken.raw;
  2253. text = text.substring(0, text.length - oldToken.text.length) + newToken.text;
  2254. break;
  2255. } else if ((lastToken == null ? void 0 : lastToken.type) === "list") {
  2256. const oldToken = lastToken;
  2257. const newText = oldToken.raw + "\n" + lines.join("\n");
  2258. const newToken = this.list(newText);
  2259. tokens[tokens.length - 1] = newToken;
  2260. raw = raw.substring(0, raw.length - lastToken.raw.length) + newToken.raw;
  2261. text = text.substring(0, text.length - oldToken.raw.length) + newToken.raw;
  2262. lines = newText.substring(tokens[tokens.length - 1].raw.length).split("\n");
  2263. continue;
  2264. }
  2265. }
  2266. return {
  2267. type: "blockquote",
  2268. raw,
  2269. tokens,
  2270. text
  2271. };
  2272. }
  2273. }
  2274. list(src) {
  2275. let cap = this.rules.block.list.exec(src);
  2276. if (cap) {
  2277. let bull = cap[1].trim();
  2278. const isordered = bull.length > 1;
  2279. const list2 = {
  2280. type: "list",
  2281. raw: "",
  2282. ordered: isordered,
  2283. start: isordered ? +bull.slice(0, -1) : "",
  2284. loose: false,
  2285. items: []
  2286. };
  2287. bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`;
  2288. if (this.options.pedantic) {
  2289. bull = isordered ? bull : "[*+-]";
  2290. }
  2291. const itemRegex = new RegExp(`^( {0,3}${bull})((?:[ ][^\\n]*)?(?:\\n|$))`);
  2292. let endsWithBlankLine = false;
  2293. while (src) {
  2294. let endEarly = false;
  2295. let raw = "";
  2296. let itemContents = "";
  2297. if (!(cap = itemRegex.exec(src))) {
  2298. break;
  2299. }
  2300. if (this.rules.block.hr.test(src)) {
  2301. break;
  2302. }
  2303. raw = cap[0];
  2304. src = src.substring(raw.length);
  2305. let line = cap[2].split("\n", 1)[0].replace(/^\t+/, (t) => " ".repeat(3 * t.length));
  2306. let nextLine = src.split("\n", 1)[0];
  2307. let blankLine = !line.trim();
  2308. let indent = 0;
  2309. if (this.options.pedantic) {
  2310. indent = 2;
  2311. itemContents = line.trimStart();
  2312. } else if (blankLine) {
  2313. indent = cap[1].length + 1;
  2314. } else {
  2315. indent = cap[2].search(/[^ ]/);
  2316. indent = indent > 4 ? 1 : indent;
  2317. itemContents = line.slice(indent);
  2318. indent += cap[1].length;
  2319. }
  2320. if (blankLine && /^ *$/.test(nextLine)) {
  2321. raw += nextLine + "\n";
  2322. src = src.substring(nextLine.length + 1);
  2323. endEarly = true;
  2324. }
  2325. if (!endEarly) {
  2326. const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`);
  2327. const hrRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`);
  2328. const fencesBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`);
  2329. const headingBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`);
  2330. while (src) {
  2331. const rawLine = src.split("\n", 1)[0];
  2332. nextLine = rawLine;
  2333. if (this.options.pedantic) {
  2334. nextLine = nextLine.replace(/^ {1,4}(?=( {4})*[^ ])/g, " ");
  2335. }
  2336. if (fencesBeginRegex.test(nextLine)) {
  2337. break;
  2338. }
  2339. if (headingBeginRegex.test(nextLine)) {
  2340. break;
  2341. }
  2342. if (nextBulletRegex.test(nextLine)) {
  2343. break;
  2344. }
  2345. if (hrRegex.test(src)) {
  2346. break;
  2347. }
  2348. if (nextLine.search(/[^ ]/) >= indent || !nextLine.trim()) {
  2349. itemContents += "\n" + nextLine.slice(indent);
  2350. } else {
  2351. if (blankLine) {
  2352. break;
  2353. }
  2354. if (line.search(/[^ ]/) >= 4) {
  2355. break;
  2356. }
  2357. if (fencesBeginRegex.test(line)) {
  2358. break;
  2359. }
  2360. if (headingBeginRegex.test(line)) {
  2361. break;
  2362. }
  2363. if (hrRegex.test(line)) {
  2364. break;
  2365. }
  2366. itemContents += "\n" + nextLine;
  2367. }
  2368. if (!blankLine && !nextLine.trim()) {
  2369. blankLine = true;
  2370. }
  2371. raw += rawLine + "\n";
  2372. src = src.substring(rawLine.length + 1);
  2373. line = nextLine.slice(indent);
  2374. }
  2375. }
  2376. if (!list2.loose) {
  2377. if (endsWithBlankLine) {
  2378. list2.loose = true;
  2379. } else if (/\n *\n *$/.test(raw)) {
  2380. endsWithBlankLine = true;
  2381. }
  2382. }
  2383. let istask = null;
  2384. let ischecked;
  2385. if (this.options.gfm) {
  2386. istask = /^\[[ xX]\] /.exec(itemContents);
  2387. if (istask) {
  2388. ischecked = istask[0] !== "[ ] ";
  2389. itemContents = itemContents.replace(/^\[[ xX]\] +/, "");
  2390. }
  2391. }
  2392. list2.items.push({
  2393. type: "list_item",
  2394. raw,
  2395. task: !!istask,
  2396. checked: ischecked,
  2397. loose: false,
  2398. text: itemContents,
  2399. tokens: []
  2400. });
  2401. list2.raw += raw;
  2402. }
  2403. list2.items[list2.items.length - 1].raw = list2.items[list2.items.length - 1].raw.trimEnd();
  2404. list2.items[list2.items.length - 1].text = list2.items[list2.items.length - 1].text.trimEnd();
  2405. list2.raw = list2.raw.trimEnd();
  2406. for (let i = 0; i < list2.items.length; i++) {
  2407. this.lexer.state.top = false;
  2408. list2.items[i].tokens = this.lexer.blockTokens(list2.items[i].text, []);
  2409. if (!list2.loose) {
  2410. const spacers = list2.items[i].tokens.filter((t) => t.type === "space");
  2411. const hasMultipleLineBreaks = spacers.length > 0 && spacers.some((t) => /\n.*\n/.test(t.raw));
  2412. list2.loose = hasMultipleLineBreaks;
  2413. }
  2414. }
  2415. if (list2.loose) {
  2416. for (let i = 0; i < list2.items.length; i++) {
  2417. list2.items[i].loose = true;
  2418. }
  2419. }
  2420. return list2;
  2421. }
  2422. }
  2423. html(src) {
  2424. const cap = this.rules.block.html.exec(src);
  2425. if (cap) {
  2426. const token = {
  2427. type: "html",
  2428. block: true,
  2429. raw: cap[0],
  2430. pre: cap[1] === "pre" || cap[1] === "script" || cap[1] === "style",
  2431. text: cap[0]
  2432. };
  2433. return token;
  2434. }
  2435. }
  2436. def(src) {
  2437. const cap = this.rules.block.def.exec(src);
  2438. if (cap) {
  2439. const tag2 = cap[1].toLowerCase().replace(/\s+/g, " ");
  2440. const href = cap[2] ? cap[2].replace(/^<(.*)>$/, "$1").replace(this.rules.inline.anyPunctuation, "$1") : "";
  2441. const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline.anyPunctuation, "$1") : cap[3];
  2442. return {
  2443. type: "def",
  2444. tag: tag2,
  2445. raw: cap[0],
  2446. href,
  2447. title
  2448. };
  2449. }
  2450. }
  2451. table(src) {
  2452. const cap = this.rules.block.table.exec(src);
  2453. if (!cap) {
  2454. return;
  2455. }
  2456. if (!/[:|]/.test(cap[2])) {
  2457. return;
  2458. }
  2459. const headers = splitCells(cap[1]);
  2460. const aligns = cap[2].replace(/^\||\| *$/g, "").split("|");
  2461. const rows = cap[3] && cap[3].trim() ? cap[3].replace(/\n[ \t]*$/, "").split("\n") : [];
  2462. const item = {
  2463. type: "table",
  2464. raw: cap[0],
  2465. header: [],
  2466. align: [],
  2467. rows: []
  2468. };
  2469. if (headers.length !== aligns.length) {
  2470. return;
  2471. }
  2472. for (const align of aligns) {
  2473. if (/^ *-+: *$/.test(align)) {
  2474. item.align.push("right");
  2475. } else if (/^ *:-+: *$/.test(align)) {
  2476. item.align.push("center");
  2477. } else if (/^ *:-+ *$/.test(align)) {
  2478. item.align.push("left");
  2479. } else {
  2480. item.align.push(null);
  2481. }
  2482. }
  2483. for (let i = 0; i < headers.length; i++) {
  2484. item.header.push({
  2485. text: headers[i],
  2486. tokens: this.lexer.inline(headers[i]),
  2487. header: true,
  2488. align: item.align[i]
  2489. });
  2490. }
  2491. for (const row of rows) {
  2492. item.rows.push(splitCells(row, item.header.length).map((cell, i) => {
  2493. return {
  2494. text: cell,
  2495. tokens: this.lexer.inline(cell),
  2496. header: false,
  2497. align: item.align[i]
  2498. };
  2499. }));
  2500. }
  2501. return item;
  2502. }
  2503. lheading(src) {
  2504. const cap = this.rules.block.lheading.exec(src);
  2505. if (cap) {
  2506. return {
  2507. type: "heading",
  2508. raw: cap[0],
  2509. depth: cap[2].charAt(0) === "=" ? 1 : 2,
  2510. text: cap[1],
  2511. tokens: this.lexer.inline(cap[1])
  2512. };
  2513. }
  2514. }
  2515. paragraph(src) {
  2516. const cap = this.rules.block.paragraph.exec(src);
  2517. if (cap) {
  2518. const text = cap[1].charAt(cap[1].length - 1) === "\n" ? cap[1].slice(0, -1) : cap[1];
  2519. return {
  2520. type: "paragraph",
  2521. raw: cap[0],
  2522. text,
  2523. tokens: this.lexer.inline(text)
  2524. };
  2525. }
  2526. }
  2527. text(src) {
  2528. const cap = this.rules.block.text.exec(src);
  2529. if (cap) {
  2530. return {
  2531. type: "text",
  2532. raw: cap[0],
  2533. text: cap[0],
  2534. tokens: this.lexer.inline(cap[0])
  2535. };
  2536. }
  2537. }
  2538. escape(src) {
  2539. const cap = this.rules.inline.escape.exec(src);
  2540. if (cap) {
  2541. return {
  2542. type: "escape",
  2543. raw: cap[0],
  2544. text: escape$1(cap[1])
  2545. };
  2546. }
  2547. }
  2548. tag(src) {
  2549. const cap = this.rules.inline.tag.exec(src);
  2550. if (cap) {
  2551. if (!this.lexer.state.inLink && /^<a /i.test(cap[0])) {
  2552. this.lexer.state.inLink = true;
  2553. } else if (this.lexer.state.inLink && /^<\/a>/i.test(cap[0])) {
  2554. this.lexer.state.inLink = false;
  2555. }
  2556. if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
  2557. this.lexer.state.inRawBlock = true;
  2558. } else if (this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
  2559. this.lexer.state.inRawBlock = false;
  2560. }
  2561. return {
  2562. type: "html",
  2563. raw: cap[0],
  2564. inLink: this.lexer.state.inLink,
  2565. inRawBlock: this.lexer.state.inRawBlock,
  2566. block: false,
  2567. text: cap[0]
  2568. };
  2569. }
  2570. }
  2571. link(src) {
  2572. const cap = this.rules.inline.link.exec(src);
  2573. if (cap) {
  2574. const trimmedUrl = cap[2].trim();
  2575. if (!this.options.pedantic && /^</.test(trimmedUrl)) {
  2576. if (!/>$/.test(trimmedUrl)) {
  2577. return;
  2578. }
  2579. const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), "\\");
  2580. if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
  2581. return;
  2582. }
  2583. } else {
  2584. const lastParenIndex = findClosingBracket(cap[2], "()");
  2585. if (lastParenIndex > -1) {
  2586. const start = cap[0].indexOf("!") === 0 ? 5 : 4;
  2587. const linkLen = start + cap[1].length + lastParenIndex;
  2588. cap[2] = cap[2].substring(0, lastParenIndex);
  2589. cap[0] = cap[0].substring(0, linkLen).trim();
  2590. cap[3] = "";
  2591. }
  2592. }
  2593. let href = cap[2];
  2594. let title = "";
  2595. if (this.options.pedantic) {
  2596. const link2 = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
  2597. if (link2) {
  2598. href = link2[1];
  2599. title = link2[3];
  2600. }
  2601. } else {
  2602. title = cap[3] ? cap[3].slice(1, -1) : "";
  2603. }
  2604. href = href.trim();
  2605. if (/^</.test(href)) {
  2606. if (this.options.pedantic && !/>$/.test(trimmedUrl)) {
  2607. href = href.slice(1);
  2608. } else {
  2609. href = href.slice(1, -1);
  2610. }
  2611. }
  2612. return outputLink(cap, {
  2613. href: href ? href.replace(this.rules.inline.anyPunctuation, "$1") : href,
  2614. title: title ? title.replace(this.rules.inline.anyPunctuation, "$1") : title
  2615. }, cap[0], this.lexer);
  2616. }
  2617. }
  2618. reflink(src, links) {
  2619. let cap;
  2620. if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) {
  2621. const linkString = (cap[2] || cap[1]).replace(/\s+/g, " ");
  2622. const link2 = links[linkString.toLowerCase()];
  2623. if (!link2) {
  2624. const text = cap[0].charAt(0);
  2625. return {
  2626. type: "text",
  2627. raw: text,
  2628. text
  2629. };
  2630. }
  2631. return outputLink(cap, link2, cap[0], this.lexer);
  2632. }
  2633. }
  2634. emStrong(src, maskedSrc, prevChar = "") {
  2635. let match = this.rules.inline.emStrongLDelim.exec(src);
  2636. if (!match)
  2637. return;
  2638. if (match[3] && prevChar.match(/[\p{L}\p{N}]/u))
  2639. return;
  2640. const nextChar = match[1] || match[2] || "";
  2641. if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
  2642. const lLength = [...match[0]].length - 1;
  2643. let rDelim, rLength, delimTotal = lLength,
  2644. midDelimTotal = 0;
  2645. const endReg = match[0][0] === "*" ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd;
  2646. endReg.lastIndex = 0;
  2647. maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
  2648. while ((match = endReg.exec(maskedSrc)) != null) {
  2649. rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
  2650. if (!rDelim)
  2651. continue;
  2652. rLength = [...rDelim].length;
  2653. if (match[3] || match[4]) {
  2654. delimTotal += rLength;
  2655. continue;
  2656. } else if (match[5] || match[6]) {
  2657. if (lLength % 3 && !((lLength + rLength) % 3)) {
  2658. midDelimTotal += rLength;
  2659. continue;
  2660. }
  2661. }
  2662. delimTotal -= rLength;
  2663. if (delimTotal > 0)
  2664. continue;
  2665. rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
  2666. const lastCharLength = [...match[0]][0].length;
  2667. const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);
  2668. if (Math.min(lLength, rLength) % 2) {
  2669. const text2 = raw.slice(1, -1);
  2670. return {
  2671. type: "em",
  2672. raw,
  2673. text: text2,
  2674. tokens: this.lexer.inlineTokens(text2)
  2675. };
  2676. }
  2677. const text = raw.slice(2, -2);
  2678. return {
  2679. type: "strong",
  2680. raw,
  2681. text,
  2682. tokens: this.lexer.inlineTokens(text)
  2683. };
  2684. }
  2685. }
  2686. }
  2687. codespan(src) {
  2688. const cap = this.rules.inline.code.exec(src);
  2689. if (cap) {
  2690. let text = cap[2].replace(/\n/g, " ");
  2691. const hasNonSpaceChars = /[^ ]/.test(text);
  2692. const hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
  2693. if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
  2694. text = text.substring(1, text.length - 1);
  2695. }
  2696. text = escape$1(text, true);
  2697. return {
  2698. type: "codespan",
  2699. raw: cap[0],
  2700. text
  2701. };
  2702. }
  2703. }
  2704. br(src) {
  2705. const cap = this.rules.inline.br.exec(src);
  2706. if (cap) {
  2707. return {
  2708. type: "br",
  2709. raw: cap[0]
  2710. };
  2711. }
  2712. }
  2713. del(src) {
  2714. const cap = this.rules.inline.del.exec(src);
  2715. if (cap) {
  2716. return {
  2717. type: "del",
  2718. raw: cap[0],
  2719. text: cap[2],
  2720. tokens: this.lexer.inlineTokens(cap[2])
  2721. };
  2722. }
  2723. }
  2724. autolink(src) {
  2725. const cap = this.rules.inline.autolink.exec(src);
  2726. if (cap) {
  2727. let text, href;
  2728. if (cap[2] === "@") {
  2729. text = escape$1(cap[1]);
  2730. href = "mailto:" + text;
  2731. } else {
  2732. text = escape$1(cap[1]);
  2733. href = text;
  2734. }
  2735. return {
  2736. type: "link",
  2737. raw: cap[0],
  2738. text,
  2739. href,
  2740. tokens: [{
  2741. type: "text",
  2742. raw: text,
  2743. text
  2744. }]
  2745. };
  2746. }
  2747. }
  2748. url(src) {
  2749. var _a;
  2750. let cap;
  2751. if (cap = this.rules.inline.url.exec(src)) {
  2752. let text, href;
  2753. if (cap[2] === "@") {
  2754. text = escape$1(cap[0]);
  2755. href = "mailto:" + text;
  2756. } else {
  2757. let prevCapZero;
  2758. do {
  2759. prevCapZero = cap[0];
  2760. let tmp = ((_a = this.rules.inline._backpedal.exec(cap[0])) == null ? void 0 : _a[0])
  2761. cap[0] = tmp ? tmp : "";
  2762. } while (prevCapZero !== cap[0]);
  2763. text = escape$1(cap[0]);
  2764. if (cap[1] === "www.") {
  2765. href = "http://" + cap[0];
  2766. } else {
  2767. href = cap[0];
  2768. }
  2769. }
  2770. return {
  2771. type: "link",
  2772. raw: cap[0],
  2773. text,
  2774. href,
  2775. tokens: [{
  2776. type: "text",
  2777. raw: text,
  2778. text
  2779. }]
  2780. };
  2781. }
  2782. }
  2783. inlineText(src) {
  2784. const cap = this.rules.inline.text.exec(src);
  2785. if (cap) {
  2786. let text;
  2787. if (this.lexer.state.inRawBlock) {
  2788. text = cap[0];
  2789. } else {
  2790. text = escape$1(cap[0]);
  2791. }
  2792. return {
  2793. type: "text",
  2794. raw: cap[0],
  2795. text
  2796. };
  2797. }
  2798. }
  2799. }
  2800. const newline = /^(?: *(?:\n|$))+/;
  2801. const blockCode = /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/;
  2802. const fences = /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/;
  2803. const hr = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/;
  2804. const heading = /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/;
  2805. const bullet = /(?:[*+-]|\d{1,9}[.)])/;
  2806. 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();
  2807. const _paragraph = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/;
  2808. const blockText = /^[^\n]+/;
  2809. const _blockLabel = /(?!\s*\])(?:\\.|[^\[\]\\])+/;
  2810. const def = edit(/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/).replace("label", _blockLabel).replace("title", /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex();
  2811. const list = edit(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g, bullet).getRegex();
  2812. 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";
  2813. const _comment = /<!--(?:-?>|[\s\S]*?(?:-->|$))/;
  2814. 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();
  2815. 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();
  2816. const blockquote = edit(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph", paragraph).getRegex();
  2817. const blockNormal = {
  2818. blockquote,
  2819. code: blockCode,
  2820. def,
  2821. fences,
  2822. heading,
  2823. hr,
  2824. html,
  2825. lheading,
  2826. list,
  2827. newline,
  2828. paragraph,
  2829. table: noopTest,
  2830. text: blockText
  2831. };
  2832. 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();
  2833. const blockGfm = {
  2834. ...blockNormal,
  2835. table: gfmTable,
  2836. 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()
  2837. };
  2838. const blockPedantic = {
  2839. ...blockNormal,
  2840. 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(),
  2841. def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
  2842. heading: /^(#{1,6})(.*)(?:\n+|$)/,
  2843. fences: noopTest,
  2844. // fences not supported
  2845. lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,
  2846. 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()
  2847. };
  2848. const escape = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/;
  2849. const inlineCode = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/;
  2850. const br = /^( {2,}|\\)\n(?!\s*$)/;
  2851. const inlineText = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/;
  2852. const _punctuation = "\\p{P}\\p{S}";
  2853. const punctuation = edit(/^((?![*_])[\spunctuation])/, "u").replace(/punctuation/g, _punctuation).getRegex();
  2854. const blockSkip = /\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g;
  2855. const emStrongLDelim = edit(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/, "u").replace(/punct/g, _punctuation).getRegex();
  2856. 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();
  2857. const emStrongRDelimUnd = edit("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\\s]|$)|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)|(?!_)[punct\\s](_+)(?=[^punct\\s])|[\\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])", "gu").replace(/punct/g, _punctuation).getRegex();
  2858. const anyPunctuation = edit(/\\([punct])/, "gu").replace(/punct/g, _punctuation).getRegex();
  2859. 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();
  2860. const _inlineComment = edit(_comment).replace("(?:-->|$)", "-->").getRegex();
  2861. 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();
  2862. const _inlineLabel = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
  2863. const link = edit(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label", _inlineLabel).replace("href", /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title", /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex();
  2864. const reflink = edit(/^!?\[(label)\]\[(ref)\]/).replace("label", _inlineLabel).replace("ref", _blockLabel).getRegex();
  2865. const nolink = edit(/^!?\[(ref)\](?:\[\])?/).replace("ref", _blockLabel).getRegex();
  2866. const reflinkSearch = edit("reflink|nolink(?!\\()", "g").replace("reflink", reflink).replace("nolink", nolink).getRegex();
  2867. const inlineNormal = {
  2868. _backpedal: noopTest,
  2869. // only used for GFM url
  2870. anyPunctuation,
  2871. autolink,
  2872. blockSkip,
  2873. br,
  2874. code: inlineCode,
  2875. del: noopTest,
  2876. emStrongLDelim,
  2877. emStrongRDelimAst,
  2878. emStrongRDelimUnd,
  2879. escape,
  2880. link,
  2881. nolink,
  2882. punctuation,
  2883. reflink,
  2884. reflinkSearch,
  2885. tag,
  2886. text: inlineText,
  2887. url: noopTest
  2888. };
  2889. const inlinePedantic = {
  2890. ...inlineNormal,
  2891. link: edit(/^!?\[(label)\]\((.*?)\)/).replace("label", _inlineLabel).getRegex(),
  2892. reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label", _inlineLabel).getRegex()
  2893. };
  2894. const inlineGfm = {
  2895. ...inlineNormal,
  2896. escape: edit(escape).replace("])", "~|])").getRegex(),
  2897. 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(),
  2898. _backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,
  2899. del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,
  2900. text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/
  2901. };
  2902. const inlineBreaks = {
  2903. ...inlineGfm,
  2904. br: edit(br).replace("{2,}", "*").getRegex(),
  2905. text: edit(inlineGfm.text).replace("\\b_", "\\b_| {2,}\\n").replace(/\{2,\}/g, "*").getRegex()
  2906. };
  2907. const block = {
  2908. normal: blockNormal,
  2909. gfm: blockGfm,
  2910. pedantic: blockPedantic
  2911. };
  2912. const inline = {
  2913. normal: inlineNormal,
  2914. gfm: inlineGfm,
  2915. breaks: inlineBreaks,
  2916. pedantic: inlinePedantic
  2917. };
  2918. class _Lexer {
  2919. constructor(options) {
  2920. __publicField(this, "tokens");
  2921. __publicField(this, "options");
  2922. __publicField(this, "state");
  2923. __publicField(this, "tokenizer");
  2924. __publicField(this, "inlineQueue");
  2925. this.tokens = [];
  2926. this.tokens.links = /* @__PURE__ */ Object.create(null);
  2927. this.options = options || _defaults;
  2928. this.options.tokenizer = this.options.tokenizer || new _Tokenizer();
  2929. this.tokenizer = this.options.tokenizer;
  2930. this.tokenizer.options = this.options;
  2931. this.tokenizer.lexer = this;
  2932. this.inlineQueue = [];
  2933. this.state = {
  2934. inLink: false,
  2935. inRawBlock: false,
  2936. top: true
  2937. };
  2938. const rules = {
  2939. block: block.normal,
  2940. inline: inline.normal
  2941. };
  2942. if (this.options.pedantic) {
  2943. rules.block = block.pedantic;
  2944. rules.inline = inline.pedantic;
  2945. } else if (this.options.gfm) {
  2946. rules.block = block.gfm;
  2947. if (this.options.breaks) {
  2948. rules.inline = inline.breaks;
  2949. } else {
  2950. rules.inline = inline.gfm;
  2951. }
  2952. }
  2953. this.tokenizer.rules = rules;
  2954. }
  2955. /**
  2956. * Expose Rules
  2957. */
  2958. static get rules() {
  2959. return {
  2960. block,
  2961. inline
  2962. };
  2963. }
  2964. /**
  2965. * Static Lex Method
  2966. */
  2967. static lex(src, options) {
  2968. const lexer = new _Lexer(options);
  2969. return lexer.lex(src);
  2970. }
  2971. /**
  2972. * Static Lex Inline Method
  2973. */
  2974. static lexInline(src, options) {
  2975. const lexer = new _Lexer(options);
  2976. return lexer.inlineTokens(src);
  2977. }
  2978. /**
  2979. * Preprocessing
  2980. */
  2981. lex(src) {
  2982. src = src.replace(/\r\n|\r/g, "\n");
  2983. this.blockTokens(src, this.tokens);
  2984. for (let i = 0; i < this.inlineQueue.length; i++) {
  2985. const next = this.inlineQueue[i];
  2986. this.inlineTokens(next.src, next.tokens);
  2987. }
  2988. this.inlineQueue = [];
  2989. return this.tokens;
  2990. }
  2991. blockTokens(src, tokens = [], lastParagraphClipped = false) {
  2992. if (this.options.pedantic) {
  2993. src = src.replace(/\t/g, " ").replace(/^ +$/gm, "");
  2994. } else {
  2995. src = src.replace(/^( *)(\t+)/gm, (_, leading, tabs) => {
  2996. return leading + " ".repeat(tabs.length);
  2997. });
  2998. }
  2999. let token;
  3000. let lastToken;
  3001. let cutSrc;
  3002. while (src) {
  3003. if (this.options.extensions && this.options.extensions.block && this.options.extensions.block.some((extTokenizer) => {
  3004. if (token = extTokenizer.call({
  3005. lexer: this
  3006. }, src, tokens)) {
  3007. src = src.substring(token.raw.length);
  3008. tokens.push(token);
  3009. return true;
  3010. }
  3011. return false;
  3012. })) {
  3013. continue;
  3014. }
  3015. if (token = this.tokenizer.space(src)) {
  3016. src = src.substring(token.raw.length);
  3017. if (token.raw.length === 1 && tokens.length > 0) {
  3018. tokens[tokens.length - 1].raw += "\n";
  3019. } else {
  3020. tokens.push(token);
  3021. }
  3022. continue;
  3023. }
  3024. if (token = this.tokenizer.code(src)) {
  3025. src = src.substring(token.raw.length);
  3026. lastToken = tokens[tokens.length - 1];
  3027. if (lastToken && (lastToken.type === "paragraph" || lastToken.type === "text")) {
  3028. lastToken.raw += "\n" + token.raw;
  3029. lastToken.text += "\n" + token.text;
  3030. this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
  3031. } else {
  3032. tokens.push(token);
  3033. }
  3034. continue;
  3035. }
  3036. if (token = this.tokenizer.fences(src)) {
  3037. src = src.substring(token.raw.length);
  3038. tokens.push(token);
  3039. continue;
  3040. }
  3041. if (token = this.tokenizer.heading(src)) {
  3042. src = src.substring(token.raw.length);
  3043. tokens.push(token);
  3044. continue;
  3045. }
  3046. if (token = this.tokenizer.hr(src)) {
  3047. src = src.substring(token.raw.length);
  3048. tokens.push(token);
  3049. continue;
  3050. }
  3051. if (token = this.tokenizer.blockquote(src)) {
  3052. src = src.substring(token.raw.length);
  3053. tokens.push(token);
  3054. continue;
  3055. }
  3056. if (token = this.tokenizer.list(src)) {
  3057. src = src.substring(token.raw.length);
  3058. tokens.push(token);
  3059. continue;
  3060. }
  3061. if (token = this.tokenizer.html(src)) {
  3062. src = src.substring(token.raw.length);
  3063. tokens.push(token);
  3064. continue;
  3065. }
  3066. if (token = this.tokenizer.def(src)) {
  3067. src = src.substring(token.raw.length);
  3068. lastToken = tokens[tokens.length - 1];
  3069. if (lastToken && (lastToken.type === "paragraph" || lastToken.type === "text")) {
  3070. lastToken.raw += "\n" + token.raw;
  3071. lastToken.text += "\n" + token.raw;
  3072. this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
  3073. } else if (!this.tokens.links[token.tag]) {
  3074. this.tokens.links[token.tag] = {
  3075. href: token.href,
  3076. title: token.title
  3077. };
  3078. }
  3079. continue;
  3080. }
  3081. if (token = this.tokenizer.table(src)) {
  3082. src = src.substring(token.raw.length);
  3083. tokens.push(token);
  3084. continue;
  3085. }
  3086. if (token = this.tokenizer.lheading(src)) {
  3087. src = src.substring(token.raw.length);
  3088. tokens.push(token);
  3089. continue;
  3090. }
  3091. cutSrc = src;
  3092. if (this.options.extensions && this.options.extensions.startBlock) {
  3093. let startIndex = Infinity;
  3094. const tempSrc = src.slice(1);
  3095. let tempStart;
  3096. this.options.extensions.startBlock.forEach((getStartIndex) => {
  3097. tempStart = getStartIndex.call({
  3098. lexer: this
  3099. }, tempSrc);
  3100. if (typeof tempStart === "number" && tempStart >= 0) {
  3101. startIndex = Math.min(startIndex, tempStart);
  3102. }
  3103. });
  3104. if (startIndex < Infinity && startIndex >= 0) {
  3105. cutSrc = src.substring(0, startIndex + 1);
  3106. }
  3107. }
  3108. if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) {
  3109. lastToken = tokens[tokens.length - 1];
  3110. if (lastParagraphClipped && (lastToken == null ? void 0 : lastToken.type) === "paragraph") {
  3111. lastToken.raw += "\n" + token.raw;
  3112. lastToken.text += "\n" + token.text;
  3113. this.inlineQueue.pop();
  3114. this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
  3115. } else {
  3116. tokens.push(token);
  3117. }
  3118. lastParagraphClipped = cutSrc.length !== src.length;
  3119. src = src.substring(token.raw.length);
  3120. continue;
  3121. }
  3122. if (token = this.tokenizer.text(src)) {
  3123. src = src.substring(token.raw.length);
  3124. lastToken = tokens[tokens.length - 1];
  3125. if (lastToken && lastToken.type === "text") {
  3126. lastToken.raw += "\n" + token.raw;
  3127. lastToken.text += "\n" + token.text;
  3128. this.inlineQueue.pop();
  3129. this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
  3130. } else {
  3131. tokens.push(token);
  3132. }
  3133. continue;
  3134. }
  3135. if (src) {
  3136. const errMsg = "Infinite loop on byte: " + src.charCodeAt(0);
  3137. if (this.options.silent) {
  3138. console.error(errMsg);
  3139. break;
  3140. } else {
  3141. throw new Error(errMsg);
  3142. }
  3143. }
  3144. }
  3145. this.state.top = true;
  3146. return tokens;
  3147. }
  3148. inline(src, tokens = []) {
  3149. this.inlineQueue.push({
  3150. src,
  3151. tokens
  3152. });
  3153. return tokens;
  3154. }
  3155. /**
  3156. * Lexing/Compiling
  3157. */
  3158. inlineTokens(src, tokens = []) {
  3159. let token, lastToken, cutSrc;
  3160. let maskedSrc = src;
  3161. let match;
  3162. let keepPrevChar, prevChar;
  3163. if (this.tokens.links) {
  3164. const links = Object.keys(this.tokens.links);
  3165. if (links.length > 0) {
  3166. while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
  3167. if (links.includes(match[0].slice(match[0].lastIndexOf("[") + 1, -1))) {
  3168. maskedSrc = maskedSrc.slice(0, match.index) + "[" + "a".repeat(match[0].length - 2) + "]" + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
  3169. }
  3170. }
  3171. }
  3172. }
  3173. while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
  3174. maskedSrc = maskedSrc.slice(0, match.index) + "[" + "a".repeat(match[0].length - 2) + "]" + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
  3175. }
  3176. while ((match = this.tokenizer.rules.inline.anyPunctuation.exec(maskedSrc)) != null) {
  3177. maskedSrc = maskedSrc.slice(0, match.index) + "++" + maskedSrc.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);
  3178. }
  3179. while (src) {
  3180. if (!keepPrevChar) {
  3181. prevChar = "";
  3182. }
  3183. keepPrevChar = false;
  3184. if (this.options.extensions && this.options.extensions.inline && this.options.extensions.inline.some((extTokenizer) => {
  3185. if (token = extTokenizer.call({
  3186. lexer: this
  3187. }, src, tokens)) {
  3188. src = src.substring(token.raw.length);
  3189. tokens.push(token);
  3190. return true;
  3191. }
  3192. return false;
  3193. })) {
  3194. continue;
  3195. }
  3196. if (token = this.tokenizer.escape(src)) {
  3197. src = src.substring(token.raw.length);
  3198. tokens.push(token);
  3199. continue;
  3200. }
  3201. if (token = this.tokenizer.tag(src)) {
  3202. src = src.substring(token.raw.length);
  3203. lastToken = tokens[tokens.length - 1];
  3204. if (lastToken && token.type === "text" && lastToken.type === "text") {
  3205. lastToken.raw += token.raw;
  3206. lastToken.text += token.text;
  3207. } else {
  3208. tokens.push(token);
  3209. }
  3210. continue;
  3211. }
  3212. if (token = this.tokenizer.link(src)) {
  3213. src = src.substring(token.raw.length);
  3214. tokens.push(token);
  3215. continue;
  3216. }
  3217. if (token = this.tokenizer.reflink(src, this.tokens.links)) {
  3218. src = src.substring(token.raw.length);
  3219. lastToken = tokens[tokens.length - 1];
  3220. if (lastToken && token.type === "text" && lastToken.type === "text") {
  3221. lastToken.raw += token.raw;
  3222. lastToken.text += token.text;
  3223. } else {
  3224. tokens.push(token);
  3225. }
  3226. continue;
  3227. }
  3228. if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) {
  3229. src = src.substring(token.raw.length);
  3230. tokens.push(token);
  3231. continue;
  3232. }
  3233. if (token = this.tokenizer.codespan(src)) {
  3234. src = src.substring(token.raw.length);
  3235. tokens.push(token);
  3236. continue;
  3237. }
  3238. if (token = this.tokenizer.br(src)) {
  3239. src = src.substring(token.raw.length);
  3240. tokens.push(token);
  3241. continue;
  3242. }
  3243. if (token = this.tokenizer.del(src)) {
  3244. src = src.substring(token.raw.length);
  3245. tokens.push(token);
  3246. continue;
  3247. }
  3248. if (token = this.tokenizer.autolink(src)) {
  3249. src = src.substring(token.raw.length);
  3250. tokens.push(token);
  3251. continue;
  3252. }
  3253. if (!this.state.inLink && (token = this.tokenizer.url(src))) {
  3254. src = src.substring(token.raw.length);
  3255. tokens.push(token);
  3256. continue;
  3257. }
  3258. cutSrc = src;
  3259. if (this.options.extensions && this.options.extensions.startInline) {
  3260. let startIndex = Infinity;
  3261. const tempSrc = src.slice(1);
  3262. let tempStart;
  3263. this.options.extensions.startInline.forEach((getStartIndex) => {
  3264. tempStart = getStartIndex.call({
  3265. lexer: this
  3266. }, tempSrc);
  3267. if (typeof tempStart === "number" && tempStart >= 0) {
  3268. startIndex = Math.min(startIndex, tempStart);
  3269. }
  3270. });
  3271. if (startIndex < Infinity && startIndex >= 0) {
  3272. cutSrc = src.substring(0, startIndex + 1);
  3273. }
  3274. }
  3275. if (token = this.tokenizer.inlineText(cutSrc)) {
  3276. src = src.substring(token.raw.length);
  3277. if (token.raw.slice(-1) !== "_") {
  3278. prevChar = token.raw.slice(-1);
  3279. }
  3280. keepPrevChar = true;
  3281. lastToken = tokens[tokens.length - 1];
  3282. if (lastToken && lastToken.type === "text") {
  3283. lastToken.raw += token.raw;
  3284. lastToken.text += token.text;
  3285. } else {
  3286. tokens.push(token);
  3287. }
  3288. continue;
  3289. }
  3290. if (src) {
  3291. const errMsg = "Infinite loop on byte: " + src.charCodeAt(0);
  3292. if (this.options.silent) {
  3293. console.error(errMsg);
  3294. break;
  3295. } else {
  3296. throw new Error(errMsg);
  3297. }
  3298. }
  3299. }
  3300. return tokens;
  3301. }
  3302. }
  3303. class _Renderer {
  3304. // set by the parser
  3305. constructor(options) {
  3306. __publicField(this, "options");
  3307. __publicField(this, "parser");
  3308. this.options = options || _defaults;
  3309. }
  3310. space(token) {
  3311. return "";
  3312. }
  3313. code({
  3314. text,
  3315. lang,
  3316. escaped
  3317. }) {
  3318. var _a;
  3319. const langString = (_a = (lang || "").match(/^\S*/)) == null ? void 0 : _a[0];
  3320. const code = text.replace(/\n$/, "") + "\n";
  3321. if (!langString) {
  3322. return "<pre><code>" + (escaped ? code : escape$1(code, true)) + "</code></pre>\n";
  3323. }
  3324. return '<pre><code class="language-' + escape$1(langString) + '">' + (escaped ? code : escape$1(code, true)) + "</code></pre>\n";
  3325. }
  3326. blockquote({
  3327. tokens
  3328. }) {
  3329. const body = this.parser.parse(tokens);
  3330. return `<blockquote>
  3331. ${body}</blockquote>
  3332. `;
  3333. }
  3334. html({
  3335. text
  3336. }) {
  3337. return text;
  3338. }
  3339. heading({
  3340. tokens,
  3341. depth
  3342. }) {
  3343. return `<h${depth}>${this.parser.parseInline(tokens)}</h${depth}>
  3344. `;
  3345. }
  3346. hr(token) {
  3347. return "<hr>\n";
  3348. }
  3349. list(token) {
  3350. const ordered = token.ordered;
  3351. const start = token.start;
  3352. let body = "";
  3353. for (let j = 0; j < token.items.length; j++) {
  3354. const item = token.items[j];
  3355. body += this.listitem(item);
  3356. }
  3357. const type = ordered ? "ol" : "ul";
  3358. const startAttr = ordered && start !== 1 ? ' start="' + start + '"' : "";
  3359. return "<" + type + startAttr + ">\n" + body + "</" + type + ">\n";
  3360. }
  3361. listitem(item) {
  3362. let itemBody = "";
  3363. if (item.task) {
  3364. const checkbox = this.checkbox({
  3365. checked: !!item.checked
  3366. });
  3367. if (item.loose) {
  3368. if (item.tokens.length > 0 && item.tokens[0].type === "paragraph") {
  3369. item.tokens[0].text = checkbox + " " + item.tokens[0].text;
  3370. if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === "text") {
  3371. item.tokens[0].tokens[0].text = checkbox + " " + item.tokens[0].tokens[0].text;
  3372. }
  3373. } else {
  3374. item.tokens.unshift({
  3375. type: "text",
  3376. raw: checkbox + " ",
  3377. text: checkbox + " "
  3378. });
  3379. }
  3380. } else {
  3381. itemBody += checkbox + " ";
  3382. }
  3383. }
  3384. itemBody += this.parser.parse(item.tokens, !!item.loose);
  3385. return `<li>${itemBody}</li>
  3386. `;
  3387. }
  3388. checkbox({
  3389. checked
  3390. }) {
  3391. return "<input " + (checked ? 'checked="" ' : "") + 'disabled="" type="checkbox">';
  3392. }
  3393. paragraph({
  3394. tokens
  3395. }) {
  3396. return `<p>${this.parser.parseInline(tokens)}</p>
  3397. `;
  3398. }
  3399. table(token) {
  3400. let header = "";
  3401. let cell = "";
  3402. for (let j = 0; j < token.header.length; j++) {
  3403. cell += this.tablecell(token.header[j]);
  3404. }
  3405. header += this.tablerow({
  3406. text: cell
  3407. });
  3408. let body = "";
  3409. for (let j = 0; j < token.rows.length; j++) {
  3410. const row = token.rows[j];
  3411. cell = "";
  3412. for (let k = 0; k < row.length; k++) {
  3413. cell += this.tablecell(row[k]);
  3414. }
  3415. body += this.tablerow({
  3416. text: cell
  3417. });
  3418. }
  3419. if (body)
  3420. body = `<tbody>${body}</tbody>`;
  3421. return "<table>\n<thead>\n" + header + "</thead>\n" + body + "</table>\n";
  3422. }
  3423. tablerow({
  3424. text
  3425. }) {
  3426. return `<tr>
  3427. ${text}</tr>
  3428. `;
  3429. }
  3430. tablecell(token) {
  3431. const content = this.parser.parseInline(token.tokens);
  3432. const type = token.header ? "th" : "td";
  3433. const tag2 = token.align ? `<${type} align="${token.align}">` : `<${type}>`;
  3434. return tag2 + content + `</${type}>
  3435. `;
  3436. }
  3437. /**
  3438. * span level renderer
  3439. */
  3440. strong({
  3441. tokens
  3442. }) {
  3443. return `<strong>${this.parser.parseInline(tokens)}</strong>`;
  3444. }
  3445. em({
  3446. tokens
  3447. }) {
  3448. return `<em>${this.parser.parseInline(tokens)}</em>`;
  3449. }
  3450. codespan({
  3451. text
  3452. }) {
  3453. return `<code>${text}</code>`;
  3454. }
  3455. br(token) {
  3456. return "<br>";
  3457. }
  3458. del({
  3459. tokens
  3460. }) {
  3461. return `<del>${this.parser.parseInline(tokens)}</del>`;
  3462. }
  3463. link({
  3464. href,
  3465. title,
  3466. tokens
  3467. }) {
  3468. const text = this.parser.parseInline(tokens);
  3469. const cleanHref = cleanUrl(href);
  3470. if (cleanHref === null) {
  3471. return text;
  3472. }
  3473. href = cleanHref;
  3474. let out = '<a href="' + href + '"';
  3475. if (title) {
  3476. out += ' title="' + title + '"';
  3477. }
  3478. out += ">" + text + "</a>";
  3479. return out;
  3480. }
  3481. image({
  3482. href,
  3483. title,
  3484. text
  3485. }) {
  3486. const cleanHref = cleanUrl(href);
  3487. if (cleanHref === null) {
  3488. return text;
  3489. }
  3490. href = cleanHref;
  3491. let out = `<img src="${href}" alt="${text}"`;
  3492. if (title) {
  3493. out += ` title="${title}"`;
  3494. }
  3495. out += ">";
  3496. return out;
  3497. }
  3498. text(token) {
  3499. return "tokens" in token && token.tokens ? this.parser.parseInline(token.tokens) : token.text;
  3500. }
  3501. }
  3502. class _TextRenderer {
  3503. // no need for block level renderers
  3504. strong({
  3505. text
  3506. }) {
  3507. return text;
  3508. }
  3509. em({
  3510. text
  3511. }) {
  3512. return text;
  3513. }
  3514. codespan({
  3515. text
  3516. }) {
  3517. return text;
  3518. }
  3519. del({
  3520. text
  3521. }) {
  3522. return text;
  3523. }
  3524. html({
  3525. text
  3526. }) {
  3527. return text;
  3528. }
  3529. text({
  3530. text
  3531. }) {
  3532. return text;
  3533. }
  3534. link({
  3535. text
  3536. }) {
  3537. return "" + text;
  3538. }
  3539. image({
  3540. text
  3541. }) {
  3542. return "" + text;
  3543. }
  3544. br() {
  3545. return "";
  3546. }
  3547. }
  3548. class _Parser {
  3549. constructor(options) {
  3550. __publicField(this, "options");
  3551. __publicField(this, "renderer");
  3552. __publicField(this, "textRenderer");
  3553. this.options = options || _defaults;
  3554. this.options.renderer = this.options.renderer || new _Renderer();
  3555. this.renderer = this.options.renderer;
  3556. this.renderer.options = this.options;
  3557. this.renderer.parser = this;
  3558. this.textRenderer = new _TextRenderer();
  3559. }
  3560. /**
  3561. * Static Parse Method
  3562. */
  3563. static parse(tokens, options) {
  3564. const parser = new _Parser(options);
  3565. return parser.parse(tokens);
  3566. }
  3567. /**
  3568. * Static Parse Inline Method
  3569. */
  3570. static parseInline(tokens, options) {
  3571. const parser = new _Parser(options);
  3572. return parser.parseInline(tokens);
  3573. }
  3574. /**
  3575. * Parse Loop
  3576. */
  3577. parse(tokens, top = true) {
  3578. let out = "";
  3579. for (let i = 0; i < tokens.length; i++) {
  3580. const anyToken = tokens[i];
  3581. if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[anyToken.type]) {
  3582. const genericToken = anyToken;
  3583. const ret = this.options.extensions.renderers[genericToken.type].call({
  3584. parser: this
  3585. }, genericToken);
  3586. if (ret !== false || !["space", "hr", "heading", "code", "table", "blockquote", "list", "html", "paragraph", "text"].includes(genericToken.type)) {
  3587. out += ret || "";
  3588. continue;
  3589. }
  3590. }
  3591. const token = anyToken;
  3592. switch (token.type) {
  3593. case "space": {
  3594. out += this.renderer.space(token);
  3595. continue;
  3596. }
  3597. case "hr": {
  3598. out += this.renderer.hr(token);
  3599. continue;
  3600. }
  3601. case "heading": {
  3602. out += this.renderer.heading(token);
  3603. continue;
  3604. }
  3605. case "code": {
  3606. out += this.renderer.code(token);
  3607. continue;
  3608. }
  3609. case "table": {
  3610. out += this.renderer.table(token);
  3611. continue;
  3612. }
  3613. case "blockquote": {
  3614. out += this.renderer.blockquote(token);
  3615. continue;
  3616. }
  3617. case "list": {
  3618. out += this.renderer.list(token);
  3619. continue;
  3620. }
  3621. case "html": {
  3622. out += this.renderer.html(token);
  3623. continue;
  3624. }
  3625. case "paragraph": {
  3626. out += this.renderer.paragraph(token);
  3627. continue;
  3628. }
  3629. case "text": {
  3630. let textToken = token;
  3631. let body = this.renderer.text(textToken);
  3632. while (i + 1 < tokens.length && tokens[i + 1].type === "text") {
  3633. textToken = tokens[++i];
  3634. body += "\n" + this.renderer.text(textToken);
  3635. }
  3636. if (top) {
  3637. out += this.renderer.paragraph({
  3638. type: "paragraph",
  3639. raw: body,
  3640. text: body,
  3641. tokens: [{
  3642. type: "text",
  3643. raw: body,
  3644. text: body
  3645. }]
  3646. });
  3647. } else {
  3648. out += body;
  3649. }
  3650. continue;
  3651. }
  3652. default: {
  3653. const errMsg = 'Token with "' + token.type + '" type was not found.';
  3654. if (this.options.silent) {
  3655. console.error(errMsg);
  3656. return "";
  3657. } else {
  3658. throw new Error(errMsg);
  3659. }
  3660. }
  3661. }
  3662. }
  3663. return out;
  3664. }
  3665. /**
  3666. * Parse Inline Tokens
  3667. */
  3668. parseInline(tokens, renderer) {
  3669. renderer = renderer || this.renderer;
  3670. let out = "";
  3671. for (let i = 0; i < tokens.length; i++) {
  3672. const anyToken = tokens[i];
  3673. if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[anyToken.type]) {
  3674. const ret = this.options.extensions.renderers[anyToken.type].call({
  3675. parser: this
  3676. }, anyToken);
  3677. if (ret !== false || !["escape", "html", "link", "image", "strong", "em", "codespan", "br", "del", "text"].includes(anyToken.type)) {
  3678. out += ret || "";
  3679. continue;
  3680. }
  3681. }
  3682. const token = anyToken;
  3683. switch (token.type) {
  3684. case "escape": {
  3685. out += renderer.text(token);
  3686. break;
  3687. }
  3688. case "html": {
  3689. out += renderer.html(token);
  3690. break;
  3691. }
  3692. case "link": {
  3693. out += renderer.link(token);
  3694. break;
  3695. }
  3696. case "image": {
  3697. out += renderer.image(token);
  3698. break;
  3699. }
  3700. case "strong": {
  3701. out += renderer.strong(token);
  3702. break;
  3703. }
  3704. case "em": {
  3705. out += renderer.em(token);
  3706. break;
  3707. }
  3708. case "codespan": {
  3709. out += renderer.codespan(token);
  3710. break;
  3711. }
  3712. case "br": {
  3713. out += renderer.br(token);
  3714. break;
  3715. }
  3716. case "del": {
  3717. out += renderer.del(token);
  3718. break;
  3719. }
  3720. case "text": {
  3721. out += renderer.text(token);
  3722. break;
  3723. }
  3724. default: {
  3725. const errMsg = 'Token with "' + token.type + '" type was not found.';
  3726. if (this.options.silent) {
  3727. console.error(errMsg);
  3728. return "";
  3729. } else {
  3730. throw new Error(errMsg);
  3731. }
  3732. }
  3733. }
  3734. }
  3735. return out;
  3736. }
  3737. }
  3738. class _Hooks {
  3739. constructor(options) {
  3740. __publicField(this, "options");
  3741. __publicField(this, "block");
  3742. this.options = options || _defaults;
  3743. }
  3744. /**
  3745. * Process markdown before marked
  3746. */
  3747. preprocess(markdown) {
  3748. return markdown;
  3749. }
  3750. /**
  3751. * Process HTML after marked is finished
  3752. */
  3753. postprocess(html2) {
  3754. return html2;
  3755. }
  3756. /**
  3757. * Process all tokens before walk tokens
  3758. */
  3759. processAllTokens(tokens) {
  3760. return tokens;
  3761. }
  3762. /**
  3763. * Provide function to tokenize markdown
  3764. */
  3765. provideLexer() {
  3766. return this.block ? _Lexer.lex : _Lexer.lexInline;
  3767. }
  3768. /**
  3769. * Provide function to parse tokens
  3770. */
  3771. provideParser() {
  3772. return this.block ? _Parser.parse : _Parser.parseInline;
  3773. }
  3774. }
  3775. __publicField(_Hooks, "passThroughHooks", /* @__PURE__ */ new Set([
  3776. "preprocess",
  3777. "postprocess",
  3778. "processAllTokens"
  3779. ]));
  3780. class Marked {
  3781. constructor(...args) {
  3782. __publicField(this, "defaults", _getDefaults());
  3783. __publicField(this, "options", this.setOptions);
  3784. __publicField(this, "parse", this.parseMarkdown(true));
  3785. __publicField(this, "parseInline", this.parseMarkdown(false));
  3786. __publicField(this, "Parser", _Parser);
  3787. __publicField(this, "Renderer", _Renderer);
  3788. __publicField(this, "TextRenderer", _TextRenderer);
  3789. __publicField(this, "Lexer", _Lexer);
  3790. __publicField(this, "Tokenizer", _Tokenizer);
  3791. __publicField(this, "Hooks", _Hooks);
  3792. this.use(...args);
  3793. }
  3794. /**
  3795. * Run callback for every token
  3796. */
  3797. walkTokens(tokens, callback) {
  3798. var _a, _b;
  3799. let values = [];
  3800. for (const token of tokens) {
  3801. values = values.concat(callback.call(this, token));
  3802. switch (token.type) {
  3803. case "table": {
  3804. const tableToken = token;
  3805. for (const cell of tableToken.header) {
  3806. values = values.concat(this.walkTokens(cell.tokens, callback));
  3807. }
  3808. for (const row of tableToken.rows) {
  3809. for (const cell of row) {
  3810. values = values.concat(this.walkTokens(cell.tokens, callback));
  3811. }
  3812. }
  3813. break;
  3814. }
  3815. case "list": {
  3816. const listToken = token;
  3817. values = values.concat(this.walkTokens(listToken.items, callback));
  3818. break;
  3819. }
  3820. default: {
  3821. const genericToken = token;
  3822. if ((_b = (_a = this.defaults.extensions) == null ? void 0 : _a.childTokens) == null ? void 0 : _b[genericToken.type]) {
  3823. this.defaults.extensions.childTokens[genericToken.type].forEach((childTokens) => {
  3824. const tokens2 = genericToken[childTokens].flat(Infinity);
  3825. values = values.concat(this.walkTokens(tokens2, callback));
  3826. });
  3827. } else if (genericToken.tokens) {
  3828. values = values.concat(this.walkTokens(genericToken.tokens, callback));
  3829. }
  3830. }
  3831. }
  3832. }
  3833. return values;
  3834. }
  3835. use(...args) {
  3836. const extensions = this.defaults.extensions || {
  3837. renderers: {},
  3838. childTokens: {}
  3839. };
  3840. args.forEach((pack) => {
  3841. const opts = {
  3842. ...pack
  3843. };
  3844. opts.async = this.defaults.async || opts.async || false;
  3845. if (pack.extensions) {
  3846. pack.extensions.forEach((ext) => {
  3847. if (!ext.name) {
  3848. throw new Error("extension name required");
  3849. }
  3850. if ("renderer" in ext) {
  3851. const prevRenderer = extensions.renderers[ext.name];
  3852. if (prevRenderer) {
  3853. extensions.renderers[ext.name] = function (...args2) {
  3854. let ret = ext.renderer.apply(this, args2);
  3855. if (ret === false) {
  3856. ret = prevRenderer.apply(this, args2);
  3857. }
  3858. return ret;
  3859. };
  3860. } else {
  3861. extensions.renderers[ext.name] = ext.renderer;
  3862. }
  3863. }
  3864. if ("tokenizer" in ext) {
  3865. if (!ext.level || ext.level !== "block" && ext.level !== "inline") {
  3866. throw new Error("extension level must be 'block' or 'inline'");
  3867. }
  3868. const extLevel = extensions[ext.level];
  3869. if (extLevel) {
  3870. extLevel.unshift(ext.tokenizer);
  3871. } else {
  3872. extensions[ext.level] = [ext.tokenizer];
  3873. }
  3874. if (ext.start) {
  3875. if (ext.level === "block") {
  3876. if (extensions.startBlock) {
  3877. extensions.startBlock.push(ext.start);
  3878. } else {
  3879. extensions.startBlock = [ext.start];
  3880. }
  3881. } else if (ext.level === "inline") {
  3882. if (extensions.startInline) {
  3883. extensions.startInline.push(ext.start);
  3884. } else {
  3885. extensions.startInline = [ext.start];
  3886. }
  3887. }
  3888. }
  3889. }
  3890. if ("childTokens" in ext && ext.childTokens) {
  3891. extensions.childTokens[ext.name] = ext.childTokens;
  3892. }
  3893. });
  3894. opts.extensions = extensions;
  3895. }
  3896. if (pack.renderer) {
  3897. const renderer = this.defaults.renderer || new _Renderer(this.defaults);
  3898. for (const prop in pack.renderer) {
  3899. if (!(prop in renderer)) {
  3900. throw new Error(`renderer '${prop}' does not exist`);
  3901. }
  3902. if (["options", "parser"].includes(prop)) {
  3903. continue;
  3904. }
  3905. const rendererProp = prop;
  3906. const rendererFunc = pack.renderer[rendererProp];
  3907. const prevRenderer = renderer[rendererProp];
  3908. renderer[rendererProp] = (...args2) => {
  3909. let ret = rendererFunc.apply(renderer, args2);
  3910. if (ret === false) {
  3911. ret = prevRenderer.apply(renderer, args2);
  3912. }
  3913. return ret || "";
  3914. };
  3915. }
  3916. opts.renderer = renderer;
  3917. }
  3918. if (pack.tokenizer) {
  3919. const tokenizer = this.defaults.tokenizer || new _Tokenizer(this.defaults);
  3920. for (const prop in pack.tokenizer) {
  3921. if (!(prop in tokenizer)) {
  3922. throw new Error(`tokenizer '${prop}' does not exist`);
  3923. }
  3924. if (["options", "rules", "lexer"].includes(prop)) {
  3925. continue;
  3926. }
  3927. const tokenizerProp = prop;
  3928. const tokenizerFunc = pack.tokenizer[tokenizerProp];
  3929. const prevTokenizer = tokenizer[tokenizerProp];
  3930. tokenizer[tokenizerProp] = (...args2) => {
  3931. let ret = tokenizerFunc.apply(tokenizer, args2);
  3932. if (ret === false) {
  3933. ret = prevTokenizer.apply(tokenizer, args2);
  3934. }
  3935. return ret;
  3936. };
  3937. }
  3938. opts.tokenizer = tokenizer;
  3939. }
  3940. if (pack.hooks) {
  3941. const hooks = this.defaults.hooks || new _Hooks();
  3942. for (const prop in pack.hooks) {
  3943. if (!(prop in hooks)) {
  3944. throw new Error(`hook '${prop}' does not exist`);
  3945. }
  3946. if (["options", "block"].includes(prop)) {
  3947. continue;
  3948. }
  3949. const hooksProp = prop;
  3950. const hooksFunc = pack.hooks[hooksProp];
  3951. const prevHook = hooks[hooksProp];
  3952. if (_Hooks.passThroughHooks.has(prop)) {
  3953. hooks[hooksProp] = (arg) => {
  3954. if (this.defaults.async) {
  3955. return Promise.resolve(hooksFunc.call(hooks, arg)).then((ret2) => {
  3956. return prevHook.call(hooks, ret2);
  3957. });
  3958. }
  3959. const ret = hooksFunc.call(hooks, arg);
  3960. return prevHook.call(hooks, ret);
  3961. };
  3962. } else {
  3963. hooks[hooksProp] = (...args2) => {
  3964. let ret = hooksFunc.apply(hooks, args2);
  3965. if (ret === false) {
  3966. ret = prevHook.apply(hooks, args2);
  3967. }
  3968. return ret;
  3969. };
  3970. }
  3971. }
  3972. opts.hooks = hooks;
  3973. }
  3974. if (pack.walkTokens) {
  3975. const walkTokens = this.defaults.walkTokens;
  3976. const packWalktokens = pack.walkTokens;
  3977. opts.walkTokens = function (token) {
  3978. let values = [];
  3979. values.push(packWalktokens.call(this, token));
  3980. if (walkTokens) {
  3981. values = values.concat(walkTokens.call(this, token));
  3982. }
  3983. return values;
  3984. };
  3985. }
  3986. this.defaults = {
  3987. ...this.defaults,
  3988. ...opts
  3989. };
  3990. });
  3991. return this;
  3992. }
  3993. setOptions(opt) {
  3994. this.defaults = {
  3995. ...this.defaults,
  3996. ...opt
  3997. };
  3998. return this;
  3999. }
  4000. lexer(src, options) {
  4001. return _Lexer.lex(src, options ? options : this.defaults);
  4002. }
  4003. parser(tokens, options) {
  4004. return _Parser.parse(tokens, options ? options : this.defaults);
  4005. }
  4006. parseMarkdown(blockType) {
  4007. const parse = (src, options) => {
  4008. const origOpt = {
  4009. ...options
  4010. };
  4011. const opt = {
  4012. ...this.defaults,
  4013. ...origOpt
  4014. };
  4015. const throwError = this.onError(!!opt.silent, !!opt.async);
  4016. if (this.defaults.async === true && origOpt.async === false) {
  4017. 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."));
  4018. }
  4019. if (typeof src === "undefined" || src === null) {
  4020. return throwError(new Error("marked(): input parameter is undefined or null"));
  4021. }
  4022. if (typeof src !== "string") {
  4023. return throwError(new Error("marked(): input parameter is of type " + Object.prototype.toString.call(src) + ", string expected"));
  4024. }
  4025. if (opt.hooks) {
  4026. opt.hooks.options = opt;
  4027. opt.hooks.block = blockType;
  4028. }
  4029. const lexer = opt.hooks ? opt.hooks.provideLexer() : blockType ? _Lexer.lex : _Lexer.lexInline;
  4030. const parser = opt.hooks ? opt.hooks.provideParser() : blockType ? _Parser.parse : _Parser.parseInline;
  4031. if (opt.async) {
  4032. 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);
  4033. }
  4034. try {
  4035. if (opt.hooks) {
  4036. src = opt.hooks.preprocess(src);
  4037. }
  4038. let tokens = lexer(src, opt);
  4039. if (opt.hooks) {
  4040. tokens = opt.hooks.processAllTokens(tokens);
  4041. }
  4042. if (opt.walkTokens) {
  4043. this.walkTokens(tokens, opt.walkTokens);
  4044. }
  4045. let html2 = parser(tokens, opt);
  4046. if (opt.hooks) {
  4047. html2 = opt.hooks.postprocess(html2);
  4048. }
  4049. return html2;
  4050. } catch (e) {
  4051. return throwError(e);
  4052. }
  4053. };
  4054. return parse;
  4055. }
  4056. onError(silent, async) {
  4057. return (e) => {
  4058. e.message += "\nPlease report this to https://github.com/markedjs/marked.";
  4059. if (silent) {
  4060. const msg = "<p>An error occurred:</p><pre>" + escape$1(e.message + "", true) + "</pre>";
  4061. if (async) {
  4062. return Promise.resolve(msg);
  4063. }
  4064. return msg;
  4065. }
  4066. if (async) {
  4067. return Promise.reject(e);
  4068. }
  4069. throw e;
  4070. };
  4071. }
  4072. }
  4073. const markedInstance = new Marked();
  4074.  
  4075. function marked(src, opt) {
  4076. return markedInstance.parse(src, opt);
  4077. }
  4078. marked.options = marked.setOptions = function (options) {
  4079. markedInstance.setOptions(options);
  4080. marked.defaults = markedInstance.defaults;
  4081. changeDefaults(marked.defaults);
  4082. return marked;
  4083. };
  4084. marked.getDefaults = _getDefaults;
  4085. marked.defaults = _defaults;
  4086. marked.use = function (...args) {
  4087. markedInstance.use(...args);
  4088. marked.defaults = markedInstance.defaults;
  4089. changeDefaults(marked.defaults);
  4090. return marked;
  4091. };
  4092. marked.walkTokens = function (tokens, callback) {
  4093. return markedInstance.walkTokens(tokens, callback);
  4094. };
  4095. marked.parseInline = markedInstance.parseInline;
  4096. marked.Parser = _Parser;
  4097. marked.parser = _Parser.parse;
  4098. marked.Renderer = _Renderer;
  4099. marked.TextRenderer = _TextRenderer;
  4100. marked.Lexer = _Lexer;
  4101. marked.lexer = _Lexer.lex;
  4102. marked.Tokenizer = _Tokenizer;
  4103. marked.Hooks = _Hooks;
  4104. marked.parse = marked;
  4105. marked.options;
  4106. marked.setOptions;
  4107. marked.use;
  4108. marked.walkTokens;
  4109. marked.parseInline;
  4110. _Parser.parse;
  4111. _Lexer.lex;
  4112.  
  4113. function md2html(md) {
  4114. return marked(md);
  4115. }
  4116. const _export_sfc = (sfc, props) => {
  4117. const target = sfc.__vccOpts || sfc;
  4118. for (const [key, val] of props) {
  4119. target[key] = val;
  4120. }
  4121. return target;
  4122. };
  4123. const _withScopeId = (n) => (vue.pushScopeId("data-v-b0fd14ee"), n = n(), vue.popScopeId(), n);
  4124. const _hoisted_1 = {
  4125. class: "monkeygpt-card"
  4126. };
  4127. const _hoisted_2 = {
  4128. class: "monkeygpt-header"
  4129. };
  4130. const _hoisted_3 = {
  4131. key: 0,
  4132. class: "nav"
  4133. };
  4134. const _hoisted_4 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("a", {
  4135. href: "https://github.com/weekend-project-space/monkey-gpt"
  4136. }, [
  4137. /* @__PURE__ */
  4138. vue.createElementVNode("img", {
  4139. src: "https://img.shields.io/github/stars/weekend-project-space/monkey-gpt.svg?style=social&label=Stars",
  4140. alt: ""
  4141. })
  4142. ], -1));
  4143. const _hoisted_5 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("path", {
  4144. d: "M18 6L6 18M6 6l12 12",
  4145. stroke: "black",
  4146. "stroke-width": "2",
  4147. "stroke-linecap": "round"
  4148. }, null, -1));
  4149. const _hoisted_6 = [
  4150. _hoisted_5
  4151. ];
  4152. const _hoisted_7 = {
  4153. key: 0,
  4154. class: "monkeygpt-body"
  4155. };
  4156. const _hoisted_8 = {
  4157. key: 0,
  4158. class: "loading",
  4159. viewBox: "0 0 100 100",
  4160. xmlns: "http://www.w3.org/2000/svg"
  4161. };
  4162. const _hoisted_9 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("circle", {
  4163. cx: "50",
  4164. cy: "50",
  4165. r: "0",
  4166. fill: "black"
  4167. }, [
  4168. /* @__PURE__ */
  4169. vue.createElementVNode("animate", {
  4170. attributeName: "r",
  4171. values: "10;30;10",
  4172. dur: "1s",
  4173. repeatCount: "indefinite"
  4174. })
  4175. ], -1));
  4176. const _hoisted_10 = [
  4177. _hoisted_9
  4178. ];
  4179. const _hoisted_11 = ["innerHTML"];
  4180. const _sfc_main$1 = {
  4181. __name: "MonkeyGPT",
  4182. props: {
  4183. msg: String
  4184. },
  4185. setup(__props) {
  4186. const txt = vue.ref("");
  4187. const loading = vue.ref(false);
  4188.  
  4189. function getText() {
  4190. loading.value = true;
  4191. txt.value = md2html(getSimpleText());
  4192. loading.value = false;
  4193. }
  4194. async function chat2(chatfun) {
  4195. clear();
  4196. loading.value = true;
  4197. try {
  4198. txt.value = md2html(await chatfun(getSimpleText()));
  4199. } catch {
  4200. txt.value = "生成失败,请检查配置是否正确并刷新重试!";
  4201. }
  4202. loading.value = false;
  4203. }
  4204.  
  4205. function clear() {
  4206. txt.value = "";
  4207. }
  4208. window.addEventListener("popstate", clear);
  4209. const bodyShow = vue.computed(() => loading.value || txt.value);
  4210. return (_ctx, _cache) => {
  4211. return vue.openBlock(), vue.createElementBlock("div", {
  4212. class: vue.normalizeClass({
  4213. "monkeygpt-warp": bodyShow.value
  4214. })
  4215. }, [
  4216. vue.createElementVNode("div", _hoisted_1, [
  4217. vue.createElementVNode("div", _hoisted_2, [
  4218. bodyShow.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3, [
  4219. vue.createElementVNode("h3", null, [
  4220. vue.createTextVNode(vue.toDisplayString(__props.msg) + " ", 1),
  4221. _hoisted_4
  4222. ]),
  4223. (vue.openBlock(), vue.createElementBlock("svg", {
  4224. class: "close",
  4225. onClick: clear,
  4226. viewBox: "0 0 24 24",
  4227. xmlns: "http://www.w3.org/2000/svg"
  4228. }, _hoisted_6))
  4229. ])) : vue.createCommentVNode("", true),
  4230. vue.createElementVNode("div", null, [
  4231. vue.createElementVNode("button", {
  4232. onClick: getText
  4233. }, "正文"),
  4234. vue.createElementVNode("button", {
  4235. onClick: _cache[0] || (_cache[0] = ($event) => chat2(vue.unref(summarize)))
  4236. }, "总结"),
  4237. vue.createElementVNode("button", {
  4238. onClick: _cache[1] || (_cache[1] = ($event) => chat2(vue.unref(ask)))
  4239. }, "回复")
  4240. ])
  4241. ]),
  4242. txt.value || loading.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_7, [
  4243. loading.value ? (vue.openBlock(), vue.createElementBlock("svg", _hoisted_8, _hoisted_10)) : txt.value ? (vue.openBlock(), vue.createElementBlock("div", {
  4244. key: 1,
  4245. innerHTML: txt.value
  4246. }, null, 8, _hoisted_11)) : vue.createCommentVNode("", true)
  4247. ])) : vue.createCommentVNode("", true)
  4248. ])
  4249. ], 2);
  4250. };
  4251. }
  4252. };
  4253. const MonkeyGPT = /* @__PURE__ */ _export_sfc(_sfc_main$1, [
  4254. ["__scopeId", "data-v-b0fd14ee"]
  4255. ]);
  4256. const _sfc_main = {
  4257. __name: "App",
  4258. setup(__props) {
  4259. return (_ctx, _cache) => {
  4260. return vue.openBlock(), vue.createBlock(MonkeyGPT, {
  4261. msg: "monkey gpt"
  4262. });
  4263. };
  4264. }
  4265. };
  4266. vue.createApp(_sfc_main).mount(
  4267. (() => {
  4268. const app = document.createElement("div");
  4269. app.id = "monkeygpt";
  4270. const firstChild = document.body.firstChild;
  4271. document.body.insertBefore(app, firstChild);
  4272. return app;
  4273. })()
  4274. );
  4275.  
  4276. })(Vue);