lc-to-markdown-txt-html

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

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