Easy Markdown for StackExchange

Adds "Markdown" and "Copy" buttons to display original Markdown content in StackExchange sites.

目前為 2023-11-29 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Easy Markdown for StackExchange
  3. // @name:zh-CN 在StackExchange中显示Markdown
  4. // @version 2.3.1
  5. // @author Murphy Lo (http://github.com/MurphyLo)
  6. // @description:en-US Adds "Markdown" and "Copy" buttons to display original Markdown content in StackExchange sites.
  7. // @description:zh-CN 在StackExchange网站上添加“Markdown”和“Copy”按钮,以显示原始Markdown内容
  8. // @license GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html)
  9. // @match https://*.stackoverflow.com/questions/*
  10. // @match https://*.superuser.com/questions/*
  11. // @match https://*.serverfault.com/questions/*
  12. // @match https://*.stackexchange.com/questions/*
  13. // @match https://*.askubuntu.com/questions/*
  14. // @match https://*.math.stackexchange.com/questions/*
  15. // @match https://*.tex.stackexchange.com/questions/*
  16. // @match https://*.english.stackexchange.com/questions/*
  17. // @match https://*.gaming.stackexchange.com/questions/*
  18. // @match https://*.physics.stackexchange.com/questions/*
  19. // @match https://*.chemistry.stackexchange.com/questions/*
  20. // @match https://*.biology.stackexchange.com/questions/*
  21. // @match https://*.programmers.stackexchange.com/questions/*
  22. // @match https://*.electronics.stackexchange.com/questions/*
  23. // @grant none
  24. // @namespace https://gf.qytechs.cn/users/1224148
  25. // @description Adds "Markdown" and "Copy" buttons to display original Markdown content in StackExchange sites.
  26. // ==/UserScript==
  27.  
  28. // This script is a modified version of an original script by Manish Goregaokar (http://stackapps.com/users/10098/manishearth)
  29. // Original script: https://github.com/Manishearth/Manish-Codes/raw/master/StackExchange/PrintPost.user.js
  30. // The original script is licensed under the GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html)
  31. // Modifications made by Murphy Lo
  32.  
  33. (async function() {
  34. 'use strict';
  35.  
  36. // Function to fetch content of a post
  37. async function fetchMarkdown(postId) {
  38. const response = await fetch(`/posts/${postId}/edit-inline`);
  39. // Check user login status.
  40. if (response.status === 404) {
  41. return "--- Please LOG IN StackExchange/Stackoverflow to view and copy the Markdown content. ---";
  42. }
  43.  
  44. const data = await response.text();
  45. let markdown = data.match(/<textarea[^>]*>([\s\S]*?)<\/textarea>/)[1];
  46. // Decode HTML entities
  47. return decodeHtmlEntities(markdown);
  48. }
  49.  
  50. // Function to decode HTML entities from the content into Markdown plain text
  51. function decodeHtmlEntities(str) {
  52. const tempElement = document.createElement('div');
  53. tempElement.innerHTML = str;
  54. return tempElement.textContent;
  55. }
  56.  
  57. // Function to show markdown content in a modal
  58. function showMarkdown(event) {
  59. // Prevent the default action
  60. event.preventDefault();
  61.  
  62. // Disable scrolling on the main page
  63. const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
  64. document.body.style.overflow = 'hidden';
  65. document.body.style.paddingRight = `${scrollbarWidth}px`;
  66.  
  67. // Add paddingRight to fixed positioned elements
  68. const fixedElements = document.querySelectorAll('header.s-topbar');
  69. fixedElements.forEach(el => el.style.paddingRight = `${scrollbarWidth}px`);
  70.  
  71. // Create a modal to display the markdown (updated style)
  72. const modal = document.createElement('div');
  73. modal.style.cssText = `
  74. position: fixed;
  75. z-index: 5051;
  76. left: 0;
  77. top: 0;
  78. width: 100%;
  79. height: 100%;
  80. overflow: none;
  81. background-color: rgba(0,0,0,0.5);`;
  82.  
  83. const modalContent = document.createElement('div');
  84. modalContent.style.cssText = `
  85. background-color: #FFF;
  86. margin: 5% auto;
  87. padding: 20px;
  88. border: 1px solid #888;
  89. width: 70%;
  90. max-height: 90%;
  91. overflow-y: auto;
  92. box-sizing: border-box;
  93. border-radius: 5px;
  94. box-shadow: 0 4px 8px 0 rgba(0,0,0,0.3);`;
  95.  
  96. // Updated close button style
  97. const closeButton = document.createElement('span');
  98. closeButton.style.cssText = `
  99. color: #aaa;
  100. float: right;
  101. font-size: 28px;
  102. font-weight: bold;
  103. cursor: pointer;
  104. margin-left: 10px;`;
  105. closeButton.textContent = '✕';
  106. closeButton.onmouseover = () => closeButton.style.color = 'black';
  107. closeButton.onmouseout = () => closeButton.style.color = '#aaa';
  108.  
  109. const markdownElement = document.createElement('pre');
  110. markdownElement.style.cssText = `
  111. margin: 0;
  112. white-space: pre-wrap;
  113. word-wrap: break-word;
  114. overflow-y: auto;`;
  115. markdownElement.textContent = this.markdownContent;
  116.  
  117. modalContent.appendChild(closeButton);
  118. modalContent.appendChild(markdownElement);
  119. modal.appendChild(modalContent);
  120. document.body.appendChild(modal);
  121.  
  122. // Close modal behaviors
  123. function closeModal() {
  124. // Removing the modal from the DOM
  125. document.body.removeChild(modal);
  126. document.removeEventListener('keydown', handleKeyDown);
  127.  
  128. // Re-enable scrolling on the main page and remove the paddingRight
  129. document.body.style.overflow = '';
  130. document.body.style.paddingRight = '';
  131.  
  132. // Remove paddingRight from fixed positioned elements
  133. const fixedElements = document.querySelectorAll('header.s-topbar');
  134. fixedElements.forEach(el => el.style.paddingRight = '');
  135. }
  136.  
  137. // Click `x` to close modal
  138. closeButton.onclick = closeModal;
  139. // Press `Esc` to close modal
  140. const handleKeyDown = (event) => event.keyCode === 27 && closeModal(); // 27 is the keyCode for the Esc key
  141. document.addEventListener('keydown', handleKeyDown);
  142. // Click modal background to close modal
  143. modal.onclick = (e) => e.target === modal && closeModal();
  144.  
  145. }
  146.  
  147. // Function to copy text to clipboard
  148. async function copyToClipboard(text) {
  149. try {
  150. await navigator.clipboard.writeText(text);
  151. // console.log('Markdown content copied to clipboard');
  152. } catch (err) {
  153. console.error('Failed to copy Markdown content: ', err);
  154. }
  155. }
  156.  
  157. // Add "Markdown" and "Copy" buttons to each post
  158. const posts = document.querySelectorAll('.question, .answer');
  159. for (const post of posts) {
  160. const postMenu = post.querySelector('.d-flex.gs8.s-anchors.s-anchors__muted.fw-wrap');
  161. const separator = document.createElement('span');
  162. separator.className = 'lsep';
  163. separator.textContent = '|';
  164. postMenu.appendChild(separator);
  165.  
  166. // Add Markdown button
  167. const printButton = document.createElement('a');
  168. printButton.href = '#';
  169. printButton.textContent = 'Markdown';
  170. printButton.title = 'View this post\'s original Markdown content';
  171. printButton.onclick = showMarkdown;
  172.  
  173. const printButtonWrapper = document.createElement('div');
  174. printButtonWrapper.className = 'flex--item';
  175. printButtonWrapper.appendChild(printButton);
  176.  
  177. postMenu.appendChild(printButtonWrapper);
  178.  
  179. // Add Copy button
  180. const copyButton = document.createElement('a');
  181. copyButton.href = '#';
  182. copyButton.textContent = 'Copy';
  183. copyButton.title = 'Copy this post\'s original Markdown content to clipboard';
  184. copyButton.onclick = (event) => {
  185. event.preventDefault();
  186. copyToClipboard(printButton.markdownContent);
  187. };
  188.  
  189. const copyButtonWrapper = document.createElement('div');
  190. copyButtonWrapper.className = 'flex--item';
  191. copyButtonWrapper.appendChild(copyButton);
  192.  
  193. postMenu.appendChild(copyButtonWrapper);
  194.  
  195. // Fetch and store markdown content
  196. const postId = post.dataset.questionid || post.dataset.answerid;
  197. printButton.markdownContent = await fetchMarkdown(postId);
  198. }
  199. })();

QingJ © 2025

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