v2exMarkdown

为v2ex而生的markdown渲染

当前为 2018-06-17 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name v2exMarkdown
  3. // @namespace https://github.com/hundan2020/v2exMarkdown
  4. // @version 0.6
  5. // @description 为v2ex而生的markdown渲染
  6. // @author hundan,ccsiyu
  7. // @match https://*.v2ex.com/t/*
  8. // @require https://cdn.staticfile.org/showdown/1.8.6/showdown.js
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12.  
  13. (function () {
  14. // markdown有天生的不安全性,这意味着可能导致xss的产生,我正在努力避免这个问题的发生
  15. // jquery.js和highlight.js都由v2ex自身提供,不再向外部重复请求
  16. var preFix = function(rawReply){
  17. var picRe = function(reply){
  18. return reply.replace(/(?:!\[.*?\])?!\[.*?\]\(\s*(https?:\/\/i\.loli\.net\/\d{4}\/\d{2}\/\d{2}\/[a-z0-9]+.[a-z]+)\)|(https:\/\/i\.loli\.net\/\d{4}\/\d{2}\/\d{2}\/[a-z0-9]+.[a-z]+)/ig, '![]( ' + encodeURI('$1$2') + ' )');
  19. }
  20. var xssFilter = function(reply){
  21. var sReply = reply;
  22. sReply = sReply.replace(/(!?\[.*?\]\(\s*?javascript.*?\))/igs, '`$1`');
  23. return sReply;
  24. }
  25. var fixedReply = rawReply;
  26. fixedReply = fixedReply.replace(/#(\d{1,3}\s)/ig, '#$1 '); // 避免楼层号加粗 safe
  27. fixedReply = fixedReply.replace(/<a target="_blank" href="(\S+?)"><img src="(\S+?)" class="embedded_image"><\/a>/ig, '![]($2)'); // 正常显示的图片处理 safe
  28. fixedReply = fixedReply.replace(/@<a href="\/member\/(\S+?)">(\S+?)<\/a>/ig, '@[$1](/member/$2)'); // 论坛内@处理,考虑到代码段中的@应当正常显示 safe
  29. fixedReply = fixedReply.replace(/<a target="_blank" href="(\/t\/\d+)">\/t\/\d+<\/a>/ig, '[$1]($1)'); // 论坛内链处理,考虑到在代码段中应当正常显示 safe
  30. fixedReply = fixedReply.replace(/<a.*? href="(\S+?)".*?>(\S+?)<\/a>/ig, '$2'); // 链接处理 safe
  31. fixedReply = fixedReply.replace(/(\n)?<br *\/?>/ig, "\n"); // 换行处理,避免多行代码无法正常工作 safe
  32. fixedReply = picRe(fixedReply);
  33. fixedReply = xssFilter(fixedReply);
  34. // 不安全的是,从原生markdown格式转变到html
  35. return fixedReply;
  36. }
  37. var endFix = function(markedReply){
  38. var fixedReply = markedReply;
  39. fixedReply = fixedReply.replace(/\n/ig, '<br />'); //safe markdown软回车转硬回车
  40. fixedReply = fixedReply.replace(/(<\/ul>|<\/li>|<\/p>|<\/table>|<\/h\d>)\s*<br\s*\/?>/ig, '$1'); // safe 表格换行删除
  41. fixedReply = fixedReply.replace(/<br\s*\/?>(<li>|<ul>|<p>|<table>|<h\d>)/ig, '$1'); // safe 表格换行删除
  42. fixedReply = fixedReply.replace(/(<\/?table>|<\/?tbody>|<\/?thead>|<\/?tr>|<\/?th>|<\/?td>)<br\s*\/?>/ig, '$1'); // safe 表格换行删除
  43. fixedReply = fixedReply.replace(/(<br\s*\/?>\s*){2,}/ig, '<br />'); // safe 多重换行转单行
  44. fixedReply = fixedReply.replace(/@\[(\S+?)\]\(\/member\/\S+\)/ig, '@$1'); // 代码段中的@ 还原
  45. fixedReply = fixedReply.replace(/\[(\/t\/\d+)\]\(\/t\/\d+\)/ig, '$1'); // 代码段中的内链还原
  46. fixedReply = fixedReply.replace(/&amp;/ig, '&'); // 对重复转义的 & 进行还原,而不必对<>进行操作,有效的避免了XSS发生
  47. return fixedReply;
  48. }
  49. var processMarkdown = function(){
  50. $("div.reply_content").each(function () {
  51. var reply = $(this)[0];
  52. var rawReply = reply.innerHTML;
  53. var converter = new showdown.Converter({
  54. omitExtraWLInCodeBlocks: true,
  55. parseImgDimensions: true,
  56. simplifiedAutoLink: true,
  57. literalMidWordUnderscores: true,
  58. strikethrough: true,
  59. tables: true,
  60. ghCodeBlocks: true,
  61. tasklists: true,
  62. smoothLivePreview: true,
  63. ghCompatibleHeaderId: true,
  64. encodeEmails: true,
  65. emoji: true
  66. });
  67. var markedReply = converter.makeHtml(preFix(rawReply));
  68. reply.innerHTML = endFix(markedReply);
  69. reply.className = 'reply_content markdown_body';
  70. // 开启代码高亮
  71. hljs.configure({useBR: true});
  72. $('div.reply_content code').each(function(i, block) {
  73. hljs.highlightBlock(block);
  74. });
  75. });
  76. }
  77. processMarkdown();
  78. console.clear();
  79. console.log("\n\n\n Thanks for using my script~\n\n\n\n");
  80. })();

QingJ © 2025

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