lc-to-markdown-txt-html

力扣题目描述,讨论发布内容复制 复制为 markdown、txt、html 等格式

  1. // ==UserScript==
  2. // @name lc-to-markdown-txt-html
  3. // @author wuxin0011
  4. // @version 0.0.6
  5. // @namespace https://github.com/wuxin0011/tampermonkey-script/tree/main/lc-to-markdown-txt-html
  6. // @description 力扣题目描述,讨论发布内容复制 复制为 markdown、txt、html 等格式
  7. // @icon data:image/png;base64,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
  8. // @match https://leetcode.cn/circle/discuss/*
  9. // @match https://leetcode.cn/problems/*
  10. // @match https://leetcode.cn/contest/weekly-contest-*/problems/*
  11. // @match https://leetcode.cn/contest/biweekly-contest-*/problems/*
  12. // @require https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.11/clipboard.min.js
  13. // @require https://unpkg.com/turndown@7.2.0/dist/turndown.js
  14. // @grant GM_registerMenuCommand
  15. // @grant GM_unregisterMenuCommand
  16. // @grant GM_setValue
  17. // @grant GM_getValue
  18. // @grant GM_cookie
  19. // @license MIT
  20. // ==/UserScript==
  21.  
  22. (function () {
  23. 'use strict';
  24. const url = window.location.href
  25. const HTML_CONVERT = '__HTML_CONVERT__'
  26. const TXT_CONVERT = '__TXT_CONVERT__'
  27. const MARKDOWN_CONVERT = '__MARKDOWN_CONVERT__'
  28. const mark = 'mark-solution-button'
  29. const markdownURL = "https://stonehank.github.io/html-to-md/"
  30. const SOLUTION_KEY = '__SOLUTION_KEY__'
  31. const targetClass = 'my-button-target'
  32. const solutionClass = `${targetClass}-solution`
  33. const isDev = () => true
  34. const log = (...args) => {
  35. if (!isDev()) {
  36. return;
  37. }
  38. console.log('lx-md-html-txt tip:', ...args)
  39. }
  40. const isDiscuss = () => url.indexOf('https://leetcode.cn/circle/discuss') != -1
  41. const isProblem = () => url.indexOf('https://leetcode.cn/problems') != -1
  42. const isContest = () => url.indexOf('https://leetcode.cn/contest/weekly-contest') != -1 || url.indexOf('https://leetcode.cn/contest/biweekly-contest') != -1
  43.  
  44. const isAutoKey = '__auto_pluging_key' + (isDiscuss() ? '__Discuss__' : isProblem() ? '__Problem__' : '__Contest__')
  45.  
  46. //
  47. const use = (key) => typeof GM_getValue(key) == 'undefined' ? true : GM_getValue(key)
  48. const isUseMarkDown = () => use(MARKDOWN_CONVERT)
  49. const isUseTxt = () => use(TXT_CONVERT)
  50. const isUseHTML = () => use(HTML_CONVERT)
  51. let timerId = null
  52. let loadOk = false
  53. const isUsePlugins = () => isUseHTML() || isUseMarkDown() || isUseTxt()
  54. const isUsePluginInThis = () => use(isAutoKey) // 当前页面是否使用该插件
  55. const isOpenSlution = () => use(SOLUTION_KEY)
  56. let isFindButtonContainer = false
  57. const updateDisplay = (element, u) => element && element instanceof HTMLElement ? (element.style.display = u ? 'inline-block' : 'none') : ''
  58. const SUPPORT_TYPE = {
  59. 'md': 'md',
  60. 'txt': 'txt',
  61. 'html': 'html'
  62. }
  63. log('markdown', isUseMarkDown(), 'txt', isUseTxt(), 'html', isUseHTML(), 'solution:', isOpenSlution())
  64.  
  65. const BUTTON_ID = `#${targetClass}`
  66. let domId = 0
  67. const loadButton = () => {
  68. const buttons = []
  69. // domId++
  70. for (let i = 0; i < 3; i++) {
  71. const temp = document.createElement('button')
  72. temp.style.marginLeft = '10px'
  73. temp.className = 'my-button-target relative inline-flex items-center justify-center text-caption px-2 py-1 gap-1 rounded-full bg-fill-secondary text-difficulty-easy dark:text-difficulty-easy'
  74. const type = i == 0 ? SUPPORT_TYPE['md'] : i == 1 ? SUPPORT_TYPE['txt'] : SUPPORT_TYPE['html']
  75. temp.title = `复制为 ${type == 'md' ? 'markdown' : type} 格式`
  76. temp.id = `${BUTTON_ID}-${type}-${domId}`
  77. temp.textContent = type
  78. temp.copytype = type
  79. buttons.push(temp)
  80. domId++
  81.  
  82. }
  83. updateDisplay(buttons[0], isUseMarkDown())
  84. updateDisplay(buttons[1], isUseTxt())
  85. updateDisplay(buttons[2], isUseHTML())
  86. return buttons
  87.  
  88. }
  89.  
  90. const btns = loadButton()
  91. // markdown button
  92. const markdownButton = btns[0]
  93. // txt button
  94. const txtButton = btns[1]
  95. // html button
  96. const htmlButton = btns[2]
  97.  
  98. function getHtmlContent(className) {
  99. const htmlContent = document.querySelector(className)
  100. return htmlContent ? htmlContent.innerHTML : ''
  101. }
  102.  
  103. function updateElementShow(element) {
  104. if (!element instanceof HTMLElement) {
  105. return
  106. }
  107. element.style.display = element.style.display == 'none' ? 'inline-block' : 'none'
  108. }
  109.  
  110.  
  111. function runQuestionActionsContainer() {
  112. const className = '[class$=MarkdownContent]';
  113. const questionActionsContainer = document.querySelector('[class*=QuestionActionsContainer]')
  114. markdownButton.className = 'e11vgnte0 css-yf7o-BaseButtonComponent-ThemedButton ery7n2v0'
  115. htmlButton.className = 'e11vgnte0 css-yf7o-BaseButtonComponent-ThemedButton ery7n2v0'
  116. txtButton.className = 'e11vgnte0 css-yf7o-BaseButtonComponent-ThemedButton ery7n2v0'
  117. const htmlContent = getHtmlContent(className)
  118. runCopy(questionActionsContainer, markdownButton, htmlContent, SUPPORT_TYPE['md'])
  119. runCopy(questionActionsContainer, htmlButton, htmlContent, SUPPORT_TYPE['html'])
  120. }
  121.  
  122.  
  123.  
  124. const toMarkdown = (htmlContent) => {
  125. try {
  126. var turndownService = new TurndownService()
  127. var markdown = turndownService.turndown(htmlContent)
  128. return markdown
  129. } catch (e) {
  130. if (confirm('markdown转换失败,跳转到网站转换?')) {
  131. if (window?.navigator?.clipboard?.writeText) {
  132. window.navigator.clipboard.writeText(htmlContent).then(() => {
  133. window.open(markdownURL, '_blank')
  134. }, () => {
  135.  
  136. })
  137. }
  138. } else {
  139. console.error('convert markdown error default convert txt !', e)
  140. const d = document.createElement('div')
  141. d.innerHTML = content
  142. const txt = handlerText(d.textContent)
  143. return txt
  144. }
  145. }
  146. }
  147.  
  148. function runProblems() {
  149. // log('~~~ run problem ~~~~', url)
  150. addSolutionButton()
  151. addClickWatch()
  152. let buttonClassName = 'relative inline-flex items-center justify-center text-caption px-2 py-1 gap-1 rounded-full bg-fill-secondary text-difficulty-easy dark:text-difficulty-easy'
  153. let className = "[data-track-load=description_content]"
  154. let titleClassName = '#qd-content [class*=text-title]'
  155. const isFlexMode = !!document.querySelector('#__next')
  156. // console.log('is find', !!document.querySelector(className))
  157. if (isContest()) {
  158. // log('isFlexMode', isFlexMode)
  159. if (isFlexMode) {
  160. // className = ".FN9Jv"
  161. titleClassName = '#qd-content a'
  162. } else {
  163. className = '#base_content .question-content'
  164. titleClassName = '#base_content .question-title h3'
  165. }
  166.  
  167. } else {
  168.  
  169. // LCP 老版本的 容器 https://leetcode.cn/problems/1ybDKD/description/
  170. if (!document.querySelector(className)) {
  171. className = ".FN9Jv"
  172. titleClassName = '#qd-content a'
  173. }
  174. }
  175.  
  176. let title = document.querySelector(titleClassName)
  177. const titleTxt = title?.textContent
  178. title = title ? '<h2>' + (title?.textContent) + '</h2>' : ''
  179. let u = window.location.href
  180. let orginUrl = title ? `<a href="${u}">` + (u) + '</a>' : ''
  181. let htmlContent = title + getHtmlContent(className) + orginUrl
  182. let container = null
  183.  
  184. // https://leetcode.cn/contest/weekly-contest-312
  185. if (isContest() && !isFlexMode) {
  186. if (!isFindButtonContainer) {
  187. const c = document.querySelector('.contest-question-info')
  188. if (c && !c.querySelector('#lx-markdown-plugins')) {
  189. const str = `<li class="list-group-item lx-markdown-plugins" id="lx-markdown-plugins">
  190. <span>插件</span>
  191. </li>`
  192. c.innerHTML = c.innerHTML + str
  193. container = c.querySelector('.lx-markdown-plugins')
  194. if (container) {
  195. isFindButtonContainer = true
  196. }
  197. }
  198.  
  199. }
  200.  
  201. } else {
  202. let originContainer = document.querySelector(className)
  203.  
  204. if (!container) {
  205. Array.from(originContainer?.parentElement?.childNodes || { length: 0 }).forEach(e => {
  206. // console.log(e.textContent)
  207. if (!container && e.textContent.indexOf('相关企业') != -1 && e.querySelector('[data-state="closed"]')) {
  208. container = e
  209. }
  210. })
  211. }
  212.  
  213. if (!container) {
  214. Array.from(originContainer?.parentElement?.parentElement?.childNodes || { length: 0 }).forEach(e => {
  215. // console.log(e.textContent)
  216. if (!container && e.textContent.indexOf('相关企业') != -1 && e.querySelector('[data-state="closed"]')) {
  217. container = e
  218. }
  219. })
  220. }
  221.  
  222.  
  223.  
  224. loadSolution(document)
  225. }
  226. if (!container) {
  227. if (times >= MAX_CNT - 2) {
  228. log('找不到 容器,将手动创建容器!', url)
  229. addButton(document.querySelector(className))
  230. }
  231. } else {
  232. if (loadOk) {
  233. return
  234. }
  235. markdownButton.className = buttonClassName
  236. txtButton.className = buttonClassName
  237. htmlButton.className = buttonClassName
  238. runCopy(container, txtButton, htmlContent, SUPPORT_TYPE['txt'], titleTxt)
  239. runCopy(container, htmlButton, htmlContent, SUPPORT_TYPE['html'])
  240. runCopy(container, markdownButton, htmlContent, SUPPORT_TYPE['md'])
  241. }
  242. }
  243.  
  244. const addSolutionButton = () => {
  245. const buttons = document.querySelectorAll('.flexlayout__tab_button_content')
  246. let solutionbutton
  247. if (buttons) {
  248. for (let d of buttons) {
  249. if (d && d.textContent.indexOf('题解') != -1) {
  250. solutionbutton = d
  251. break
  252. }
  253. }
  254. }
  255. if (solutionbutton && !solutionbutton.getAttribute(mark)) {
  256. solutionbutton.setAttribute(mark, 'ok')
  257. solutionbutton.onclick = () => {
  258. addClickWatch()
  259. }
  260. }
  261.  
  262. }
  263.  
  264. function addClickWatch() {
  265. for (let d of document.querySelectorAll('.group.flex.w-full.cursor-pointer')) {
  266. if (d.getAttribute(mark)) {
  267. continue
  268. }
  269. d.onclick = () => {
  270. loadSolution(document)
  271. }
  272. d.setAttribute(mark, 'ok')
  273. }
  274. }
  275.  
  276. function addButton(solutionContainer, p) {
  277. if (!(solutionContainer instanceof HTMLElement)) {
  278. return
  279. }
  280. if (!p) {
  281. p = solutionContainer?.parentElement
  282. if (!p) return
  283. }
  284. if (
  285. solutionContainer.querySelector(solutionClass) || solutionContainer.getAttribute(mark)
  286. ||
  287. (p && p.querySelector(solutionClass))
  288. ) {
  289. return
  290. }
  291.  
  292. let buttonContainer = document.createElement('div')
  293. buttonContainer.style.marginTop = '10px'
  294. buttonContainer.style.marginBottom = '10px'
  295. buttonContainer.className = solutionClass
  296. let t = solutionContainer.innerHTML
  297. let buttons = loadButton()
  298. runCopy(buttonContainer, buttons[0], t, SUPPORT_TYPE['md'])
  299. runCopy(buttonContainer, buttons[1], t, SUPPORT_TYPE['txt'], '')
  300. runCopy(buttonContainer, buttons[2], t, SUPPORT_TYPE['html'])
  301. p.insertBefore(buttonContainer, solutionContainer)
  302. solutionContainer.setAttribute(mark, 'ok')
  303. urlChangeLoadOk = true
  304. loadOk = true
  305. }
  306.  
  307.  
  308. function loadSolution(dom, loadCnt = 0, loadMaxCnt = 20) {
  309. if (!isOpenSlution()) {
  310. return;
  311. }
  312. try {
  313. if (loadCnt > loadMaxCnt) {
  314. return
  315. }
  316. if (!dom) {
  317. return
  318. }
  319. let solutionContainer = dom.querySelector('[class^=break-words]')
  320. if (solutionContainer) {
  321. const o = solutionContainer?.parentNode
  322.  
  323. if (o.querySelector(`[class="${solutionClass}"]`)) return
  324. addButton(solutionContainer, o)
  325. } else {
  326. setTimeout(() => {
  327. loadSolution(dom, loadCnt + 1, loadMaxCnt)
  328. }, 1500);
  329. }
  330. } catch (e) {
  331. console.error('load solution error:', e)
  332. }
  333. }
  334.  
  335.  
  336. function copy(w, element) {
  337. if (!element || !(element instanceof HTMLElement)) {
  338. return
  339. }
  340.  
  341. try {
  342. let clipboard = element?.clipboardObject
  343. if (clipboard) {
  344. //console.log('clipboard destroy')
  345. clipboard.destroy();
  346. }
  347. clipboard = new ClipboardJS(element, {
  348. text: function () {
  349. return w;
  350. }
  351. })
  352. // console.log('update txt >>>>>>>>>')
  353. element.clipboardObject = clipboard
  354. clipboard.on('success', function (e) {
  355. updateButtonStatus(element)
  356. })
  357. clipboard.on('error', function (e) {
  358. updateButtonStatus(element, 'copy error!')
  359. })
  360.  
  361.  
  362. } catch (error) {
  363. // 如果 clipboardjs 引入失败 使用原生的
  364. // use navigator writeText
  365. element.onclick = () => {
  366. navigator.clipboard.writeText(w).then(() => {
  367. //updateButtonStatus(element)
  368. }, () => {
  369. updateButtonStatus(element, 'copy error!')
  370. })
  371. }
  372.  
  373. }
  374.  
  375. }
  376.  
  377.  
  378.  
  379.  
  380. function runCopy(container, ele, htmlContent, type = SUPPORT_TYPE['md'], title = '') {
  381.  
  382. if (!ele || !container || !htmlContent || !type) {
  383. return
  384. }
  385. if (!(container instanceof HTMLElement && ele instanceof HTMLElement)) {
  386. return;
  387. }
  388. if (!container.querySelector(ele.id)) {
  389. ele.originClass = ele.className
  390. container.appendChild(ele)
  391. } else {
  392. // 加载完成 初始化
  393. loadOk = true
  394. // initConmand()
  395. updateButtonStatus(ele, ele.copytype, '', 1000)
  396. clearTimeId()
  397. }
  398.  
  399. if (type == SUPPORT_TYPE['md']) {
  400. const markdown = toMarkdown(htmlContent)
  401. copy(markdown, ele)
  402. } else if (type == SUPPORT_TYPE['txt']) {
  403. const d = document.createElement('div')
  404. d.innerHTML = htmlContent
  405. const txt = handlerText(d.textContent, title)
  406. copy(txt, ele)
  407. } else if (type == SUPPORT_TYPE['html']) {
  408. // html
  409. copy(htmlContent, ele)
  410. } else {
  411. console.warn('no support format ' + type)
  412. }
  413.  
  414. }
  415.  
  416. const handlerText = (str, title = '') => {
  417. if (!str) return str
  418. // 移出空白字符
  419. str = str.replaceAll(' ', '')
  420. str = str.replaceAll('​​​​​​​​​​​​​​​​​​​​​​​', '')
  421. str = str.replaceAll('&nbsp;', '')
  422. str = str.replace('。', "。\n")
  423. str = str.replace(/\n{2,}/g, "\n")
  424. str = str.replace('http', '\n\nhttp')
  425. str = str.replaceAll('示例', "\n示例")
  426. str = str.replace('231', '2^31')
  427. str = str.replace(/10(?!0)(\d+)/g, '10^$1')
  428. str = str.replace('提示', "\n提示")
  429. if (title != '') {
  430. str = str.replace(title, title + "\n\n")
  431. }
  432. return str
  433. }
  434.  
  435.  
  436. const updateButtonStatus = (element, newText = 'copied!', newClass = '', timeout = 1000) => {
  437. if (!element) {
  438. return;
  439. }
  440. // console.log('update button status', element, newText)
  441. element.textContent = newText
  442. if (newClass) {
  443. element.className = newClass
  444. }
  445. setTimeout(() => {
  446. element.textContent = element.copytype
  447. element.className = element.originClass
  448. }, timeout)
  449. }
  450.  
  451.  
  452.  
  453. const initConmand = () => {
  454. try {
  455. const isAutoPluginCommand = GM_registerMenuCommand(`当前页面 ${isUsePluginInThis() ? '关闭' : '启用'} 插件 `, () => {
  456. GM_setValue(isAutoKey, !isUsePluginInThis())
  457. window.location.reload()
  458. }, { title: `当前页面 ${isUseHTML() ? '关闭' : '启用'} 插件 ` })
  459.  
  460.  
  461.  
  462. if (!isUsePluginInThis()) {
  463. return;
  464. }
  465.  
  466. // const message = (u, type) => u ? '关闭' : '启用' + (type == 'md' ? ' markdown ' : ` ${type} `)
  467.  
  468. const html_to_markdown = GM_registerMenuCommand(`${isUseMarkDown() ? '关闭' : '启用'} markdown `, () => {
  469. GM_setValue(MARKDOWN_CONVERT, !isUseMarkDown())
  470. updateElementShow(markdownButton)
  471. }, { title: `点击 ${isUseMarkDown() ? '关闭' : '启用'} markdown ` })
  472.  
  473.  
  474. const html_to_txt = GM_registerMenuCommand(`${isUseTxt() ? '关闭' : '启用'} txt `, () => {
  475. GM_setValue(TXT_CONVERT, !isUseTxt())
  476. updateElementShow(txtButton)
  477. }, { title: `点击 ${isUseTxt() ? '关闭' : '启用'} txt ` })
  478.  
  479. const html_to_html = GM_registerMenuCommand(`${isUseHTML() ? '关闭' : '启用'} html `, () => {
  480. GM_setValue(HTML_CONVERT, !isUseHTML())
  481. updateElementShow(htmlButton)
  482. }, { title: `点击 ${isUseHTML() ? '关闭' : '启用'} html ` })
  483.  
  484.  
  485.  
  486. const html_to_markdown_web = GM_registerMenuCommand('html转换markdown网站', () => {
  487. window.open(markdownURL, '_blank')
  488. }, { title: '如果格式转换有问题,请复制为 html 然后用这个网站转换' })
  489.  
  490.  
  491. if (isProblem()) {
  492. let close_solution_command_id
  493. close_solution_command_id = GM_registerMenuCommand(`${isOpenSlution() ? '关闭' : '开启'} 题解复制`, () => {
  494. GM_setValue(SOLUTION_KEY, !isOpenSlution())
  495. }, { title: '如果不想题解中显示复制相关按钮请关闭,默认开启' })
  496. }
  497.  
  498. } catch (e) {
  499. console.log('init command error', e)
  500. }
  501.  
  502. }
  503.  
  504. let times = 0
  505. const MAX_CNT = 15
  506. const TIME_OUT = 1500
  507. initConmand()
  508.  
  509.  
  510. function clearTimeId() {
  511. if (timerId != null) {
  512. window.cancelIdleCallback(timerId)
  513. window.clearInterval(timerId)
  514. window.clearTimeout(timerId)
  515. timerId = null;
  516. }
  517. }
  518. let support = true
  519. const start = () => {
  520. times += 1
  521. if (times > MAX_CNT || !support) {
  522. // console.info('>>>>>>>>>>>>>>>>>>>clear<<<<<<<<<<<<<<<<<<<<<<<<<')
  523. clearTimeId()
  524. return
  525. }
  526. if (!isUsePlugins() || !isUsePluginInThis()) {
  527. clearTimeId()
  528. return;
  529. }
  530. if (loadOk) {
  531. log('load ok')
  532. return
  533. }
  534. timerId = setTimeout(() => {
  535. try {
  536. if (isDiscuss()) {
  537. runQuestionActionsContainer()
  538. } else if (isProblem() || isContest()) {
  539. runProblems()
  540. } else {
  541. support = false
  542. }
  543. } catch (e) {
  544. console.error('install fail ', e)
  545. }
  546. if (!loadOk) {
  547. start()
  548. }
  549. }, TIME_OUT)
  550.  
  551. }
  552.  
  553.  
  554.  
  555. const updateUrl = () => {
  556. updateTimes += 1
  557. if (updateTimes >= 10 || urlChangeLoadOk) {
  558. clearTimeId()
  559. return;
  560. }
  561. timerId = requestIdleCallback(() => {
  562. if (isDiscuss()) {
  563. runQuestionActionsContainer()
  564. } else if (isProblem() || isContest()) {
  565. runProblems()
  566. }
  567.  
  568. if (!urlChangeLoadOk) {
  569. updateUrl()
  570. }
  571.  
  572. }, { timeout: TIME_OUT })
  573. }
  574.  
  575.  
  576. window.onload = () => {
  577. times = 0
  578. start()
  579. try {
  580. // loadOK();
  581. } catch (e) {
  582.  
  583. }
  584. addClickWatch()
  585. }
  586.  
  587. // 监听地址改变
  588. // 重新修改描述
  589. let urlChangeLoadOk = false
  590. let updateTimes = 0
  591. window.addEventListener("urlchange", () => {
  592. updateTimes = 0
  593. urlChangeLoadOk = false
  594. updateUrl();
  595. })
  596.  
  597. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址