i-v2ex

A better script for v2ex.com

  1. // ==UserScript==
  2. // @name i-v2ex
  3. // @version 0.68.5
  4. // @description A better script for v2ex.com
  5. // @author hundan
  6. // @match https://*.v2ex.com/t/*
  7. // @require https://cdn.staticfile.org/showdown/1.8.6/showdown.min.js
  8. // @require https://cdn.staticfile.org/fancybox/3.3.5/jquery.fancybox.min.js
  9. // @require https://cdn.staticfile.org/utf8/3.0.0/utf8.min.js
  10. // @require https://cdn.staticfile.org/highlight.js/9.15.10/highlight.min.js
  11. // @grant none
  12. // @namespace https://github.com/hundan2020/i-v2ex
  13. // ==/UserScript==
  14.  
  15. (function () {
  16. // jquery.js由v2ex自身提供,不再向外部重复请求
  17. // 预处理以解决与 v2ex plus 的冲突
  18. $(".reply_content img").each(function(){
  19. var $this = $(this)
  20. if ($this[0].src.indexOf(".sinaimg.cn") != -1 && $this[0].src.indexOf("http://") != -1) {
  21. $this[0].src = "https" + $this[0].src.substr(4)
  22. }
  23. })
  24. // markdown处理
  25. var preFix = function(rawReply){
  26. //rawReply = 'content string for debug';
  27. var picRe = function(reply){
  28. reply = reply.replace(/(?:!\[.*?\])?!\[.*?\]\(\s*((?:https?)?:\/\/i\.loli\.net\/\d{4}\/\d{2}\/\d{2}\/[a-z0-9]+.[a-z]+)\s*\)|(https:\/\/i\.loli\.net\/\d{4}\/\d{2}\/\d{2}\/[a-z0-9]+.[a-z]+)/ig, '![]( ' + encodeURI('$1$2') + ' )') // sm.ms
  29. reply = reply.replace(/(?:!\[.*?\])?!\[.*?\]\(\s*((?:https?)?:\/\/imgurl\.org\/temp\/\d{4}\/[a-z0-9]+\.[a-z0-9]+)\)|(https?:\/\/imgurl\.org\/temp\/\d{4}\/[a-z0-9]+\.[a-z0-9]+)/ig, '![]( ' + encodeURI('$1$2') + ' )') // 小z图床
  30. reply = reply.replace(/(?<!http:|https:)\/\/([a-z0-9]+\.sinaimg.cn\/(?:[a-z]+)\/[a-z0-9]+\.(?:jpg|png|gif|bmp))/ig, '![]( ' + encodeURI('https://$1') + ' )') // 新浪微博不规则图片链接
  31. return reply
  32. }
  33. var xssFilter = function(reply){
  34. var sReply = reply
  35. sReply = sReply.replace(/(!?\[.*?\]\(\s*?javascript.*?\))/igs, '`$1`')
  36. return sReply
  37. }
  38. var fixedReply = rawReply
  39. fixedReply = fixedReply.replace(/#(\d+)/ig, '&#x23;$1') // 避免楼层号加粗
  40. fixedReply = fixedReply.replace(/(!\[(\S*?)\]\(\s+?)<a target="_blank" href="(\S+?)".*?><img src="(\S+?)" class="embedded_image".*?><\/a>\)+/ig, '![$2]($3)') // 正常显示的图片处理
  41. fixedReply = fixedReply.replace(/<img src="(\S+?)" class="embedded_image"[^>]*?>/ig, '![]($1)') // 正常显示的图片处理
  42. fixedReply = fixedReply.replace(/&lt;img src="(.+?)" \/&gt;/ig, '$1') // 不规则图片链接处理
  43. fixedReply = fixedReply.replace(/@<a href="\/member\/(\S+?)">(\S+?)<\/a>/ig, '@[$1](/member/$2)') // 论坛内@处理,考虑到代码段中的@应当正常显示
  44. fixedReply = fixedReply.replace(/<a target="_blank" href="(\/t\/\d+)"\s*?(?:rel="nofollow")?>\/t\/\d+<\/a>/ig, '[$1]($1)') // 论坛内链处理,考虑到在代码段中应当正常显示
  45. fixedReply = fixedReply.replace(/<a.*? href="(\S+?)".*?>(\S+?)<\/a>/ig, '$2') // 链接处理
  46. fixedReply = fixedReply.replace(/\[!\[(\S+?)\]\(\s*(\S+?)\)\]\(\s*\S+?\)/ig, '![$1]($2)') // 不规则图片链接处理,不规则案例见 `https://www.v2ex.com/t/463469#r_5792042`
  47. fixedReply = fixedReply.replace(/!\[\]\(\s*!\[\]\((.+?)\)\s*\)/ig,' ![]($1) ') // 不规则图片链接处理,见 `https://www.v2ex.com/t/608455?p=1#r_8013651`
  48. fixedReply = fixedReply.replace(/(\n)?<br *\/?>/ig, "\n") // 换行处理,避免多行代码无法正常工作
  49. fixedReply = fixedReply.replace(/(https?:\/\/[\x00-\xff]+)/ig, '$1 ') // 修正a标签链接,解决中文与链接混用导致的错误解析
  50. fixedReply = picRe(fixedReply)
  51. fixedReply = xssFilter(fixedReply)
  52. return fixedReply
  53. }
  54. var endFix = function(markedReply){
  55. var fixedReply = markedReply
  56. fixedReply = fixedReply.replace(/\n/ig, '<br />') // markdown软回车转硬回车
  57. fixedReply = fixedReply.replace(/(<\/ul>|<\/li>|<\/p>|<\/table>|<\/h\d>)\s*<br\s*\/?>/ig, '$1') // 表格换行删除
  58. fixedReply = fixedReply.replace(/<br\s*\/?>(<li>|<ul>|<p>|<table>|<h\d>)/ig, '$1') // 表格换行删除
  59. fixedReply = fixedReply.replace(/(<\/?table>|<\/?tbody>|<\/?thead>|<\/?tr>|<\/?th>|<\/?td>)<br\s*\/?>/ig, '$1') // 表格换行删除
  60. fixedReply = fixedReply.replace(/(<br\s*\/?>\s*){2,}/ig, '<br />') // 多重换行转单行
  61. fixedReply = fixedReply.replace(/@\[(\S+?)\]\(\/member\/\S+\)/ig, '@$1') // 代码段中的@ 还原
  62. fixedReply = fixedReply.replace(/\[(\/t\/\d+)\]\(\/t\/\d+\)/ig, '$1') // 代码段中的内链还原
  63. fixedReply = fixedReply.replace(/&amp;/ig, '&') // 对重复转义的 & 进行还原,而不必对<>进行操作,有效的避免了XSS发生
  64. return fixedReply
  65. }
  66. var processMarkdown = function(){
  67. $("div.reply_content").each(function () {
  68. var reply = $(this)[0]
  69. var rawReply = reply.innerHTML
  70. var converter = new showdown.Converter({
  71. omitExtraWLInCodeBlocks: true,
  72. parseImgDimensions: true,
  73. simplifiedAutoLink: true,
  74. literalMidWordUnderscores: true,
  75. strikethrough: true,
  76. tables: true,
  77. ghCodeBlocks: true,
  78. tasklists: true,
  79. smoothLivePreview: true,
  80. ghCompatibleHeaderId: true,
  81. encodeEmails: true,
  82. emoji: true
  83. })
  84. var markedReply = converter.makeHtml(preFix(rawReply))
  85. reply.innerHTML = endFix(markedReply)
  86. reply.className = 'reply_content markdown_body'
  87. // 开启代码高亮
  88. hljs.configure({useBR: true})
  89. $('div.reply_content code').each(function(i, block) {
  90. hljs.highlightBlock(block)
  91. $(this).css({'display':'inline'})
  92. })
  93. })
  94. }
  95. processMarkdown()
  96. // 加载看图插件
  97. function loadStyle(url){
  98. var link = document.createElement('link')
  99. link.type = 'text/css'
  100. link.rel = 'stylesheet'
  101. link.href = url
  102. var head = document.getElementsByTagName('head')[0]
  103. head.appendChild(link)
  104. }
  105. loadStyle('https://cdn.staticfile.org/fancybox/3.3.5/jquery.fancybox.min.css')
  106. var a_wrap = $('<a></a>')
  107. var imgs = $('.markdown_body img, .topic_content img')
  108. imgs.wrap('<a></a>')
  109. $.each(imgs, function (i, j) {
  110. $(j).css({'max-height':'25vh'})
  111. $(j).parent().attr('id', 'img' + i).attr('href', $(this).attr('src'))
  112. // 通过延迟解决与v2ex plus的冲突
  113. setTimeout(function(){
  114. $("#img" + i).fancybox({
  115. buttons: [
  116. "slideShow",
  117. "thumbs",
  118. "close"
  119. ],
  120. 'zoomSpeedIn': 300,
  121. 'zoomSpeedOut': 300,
  122. 'overlayShow': false,
  123. 'overlayOpacity': 0.3
  124. })
  125. },500)
  126. })
  127. // 添加楼主标记
  128. let author = $('#Main > div:nth-child(2) > div.header > small > a').text()
  129. $('table > tbody > tr > td > strong > a').each(function(){
  130. if ($(this).text() == author){
  131. var sign = $('<span></span>')
  132. sign.text('楼主')
  133. sign.css({"padding": "1px 5px", "margin": "0 6px", "font-size": "x-small", "color": "#777", "border-radius": "8px", "border": "1px solid"})
  134. $(this).parent().after(sign)
  135. }
  136. })
  137. // 修复新浪外链
  138. $("img").each(function(){
  139. })
  140. window.refererBypass = `
  141. <script src="https://cdn.staticfile.org/jquery/3.4.0/jquery.min.js">
  142. <\/script>
  143. <script>
  144. $(parent.document).find("img").each(function(){
  145. let img_src = $(this).attr("src")
  146. let preload_img = \`<img src="\$\{img_src\}" />\`
  147. document.write(preload_img)
  148. })
  149. $('img').on('load', function () {
  150. $(parent.document).find('img[src$="'+this.src+'"]').each(function () {
  151. this.src = this.src
  152. })
  153. })
  154. <\/script>
  155. `
  156. // Base64 解码
  157. $('body').append('<iframe src="javascript:parent.refererBypass;" style="display: none;"></iframe>')
  158. let style = "ICAgICAgICAuYmFzZTY0OmhvdmVyIHsKICAgICAgICAgICAgYm9yZGVyLWNvbG9yOiAjYzBjNGNjOwogICAgICAgIH0KICAgICAgICAuYmFzZTY0OmZvY3VzIHsKICAgICAgICAgICAgb3V0bGluZTogbm9uZTsKICAgICAgICAgICAgYm9yZGVyLWNvbG9yOiAjNDA5ZWZmOwogICAgICAgIH0KICAgICAgICAuYmFzZTY0IHsKCQkJb3ZlcmZsb3c6IGhpZGRlbjsKICAgICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogcmdiKDI1NSwgMjU1LCAyNTUpOwogICAgICAgICAgICBiYWNrZ3JvdW5kLWltYWdlOiBub25lOwogICAgICAgICAgICBib3JkZXItYm90dG9tLWNvbG9yOiByZ2IoMTkyLCAxOTYsIDIwNCk7CiAgICAgICAgICAgIGJvcmRlci1ib3R0b20tbGVmdC1yYWRpdXM6IDRweDsKICAgICAgICAgICAgYm9yZGVyLWJvdHRvbS1yaWdodC1yYWRpdXM6IDRweDsKICAgICAgICAgICAgYm9yZGVyLWJvdHRvbS1zdHlsZTogc29saWQ7CiAgICAgICAgICAgIGJvcmRlci1ib3R0b20td2lkdGg6IDFweDsKICAgICAgICAgICAgYm9yZGVyLWltYWdlLW91dHNldDogMHB4OwogICAgICAgICAgICBib3JkZXItaW1hZ2UtcmVwZWF0OiBzdHJldGNoOwogICAgICAgICAgICBib3JkZXItaW1hZ2Utc2xpY2U6IDEwMCU7CiAgICAgICAgICAgIGJvcmRlci1pbWFnZS1zb3VyY2U6IG5vbmU7CiAgICAgICAgICAgIGJvcmRlci1pbWFnZS13aWR0aDogMTsKICAgICAgICAgICAgYm9yZGVyLWxlZnQtY29sb3I6IHJnYigxOTIsIDE5NiwgMjA0KTsKICAgICAgICAgICAgYm9yZGVyLWxlZnQtc3R5bGU6IHNvbGlkOwogICAgICAgICAgICBib3JkZXItbGVmdC13aWR0aDogMXB4OwogICAgICAgICAgICBib3JkZXItcmlnaHQtY29sb3I6IHJnYigxOTIsIDE5NiwgMjA0KTsKICAgICAgICAgICAgYm9yZGVyLXJpZ2h0LXN0eWxlOiBzb2xpZDsKICAgICAgICAgICAgYm9yZGVyLXJpZ2h0LXdpZHRoOiAxcHg7CiAgICAgICAgICAgIGJvcmRlci10b3AtY29sb3I6IHJnYigxOTIsIDE5NiwgMjA0KTsKICAgICAgICAgICAgYm9yZGVyLXRvcC1sZWZ0LXJhZGl1czogNHB4OwogICAgICAgICAgICBib3JkZXItdG9wLXJpZ2h0LXJhZGl1czogNHB4OwogICAgICAgICAgICBib3JkZXItdG9wLXN0eWxlOiBzb2xpZDsKICAgICAgICAgICAgYm9yZGVyLXRvcC13aWR0aDogMXB4OwogICAgICAgICAgICBib3gtc2l6aW5nOiBib3JkZXItYm94OwogICAgICAgICAgICBjb2xvcjogcmdiKDk2LCA5OCwgMTAyKTsKICAgICAgICAgICAgY3Vyc29yOiB0ZXh0OwogICAgICAgICAgICBkaXNwbGF5OiBibG9jazsKICAgICAgICAgICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjsKICAgICAgICAgICAgZm9udC1mYW1pbHk6ICJIZWx2ZXRpY2EgTmV1ZSIsIEhlbHZldGljYSwgIlBpbmdGYW5nIFNDIiwgIkhpcmFnaW5vIFNhbnMgR0IiLCAiTWljcm9zb2Z0IFlhSGVpIiwgU2ltU3VuLCBzYW5zLXNlcmlmOwogICAgICAgICAgICBmb250LXNpemU6IDE0cHg7CiAgICAgICAgICAgIGZvbnQtc3RyZXRjaDogMTAwJTsKICAgICAgICAgICAgZm9udC1zdHlsZTogbm9ybWFsOwogICAgICAgICAgICBmb250LXZhcmlhbnQtY2Fwczogbm9ybWFsOwogICAgICAgICAgICBmb250LXZhcmlhbnQtZWFzdC1hc2lhbjogbm9ybWFsOwogICAgICAgICAgICBmb250LXZhcmlhbnQtbGlnYXR1cmVzOiBub3JtYWw7CiAgICAgICAgICAgIGZvbnQtdmFyaWFudC1udW1lcmljOiBub3JtYWw7CiAgICAgICAgICAgIGxldHRlci1zcGFjaW5nOiBub3JtYWw7CiAgICAgICAgICAgIGxpbmUtaGVpZ2h0OiAyMXB4OwogICAgICAgICAgICBtYXJnaW4tYm90dG9tOiAwcHg7CiAgICAgICAgICAgIG1hcmdpbi1sZWZ0OiAwcHg7CiAgICAgICAgICAgIG1hcmdpbi1yaWdodDogMHB4OwogICAgICAgICAgICBtYXJnaW4tdG9wOiAwcHg7CiAgICAgICAgICAgIG1pbi1oZWlnaHQ6IDMzcHg7CiAgICAgICAgICAgIG92ZXJmbG93LXdyYXA6IGJyZWFrLXdvcmQ7CiAgICAgICAgICAgIHBhZGRpbmctYm90dG9tOiA1cHg7CiAgICAgICAgICAgIHBhZGRpbmctbGVmdDogMTVweDsKICAgICAgICAgICAgcGFkZGluZy1yaWdodDogMTVweDsKICAgICAgICAgICAgcGFkZGluZy10b3A6IDVweDsKICAgICAgICAgICAgcmVzaXplOiBub25lOwogICAgICAgICAgICB0ZXh0LWFsaWduOiBzdGFydDsKICAgICAgICAgICAgdGV4dC1pbmRlbnQ6IDBweDsKICAgICAgICAgICAgdGV4dC1yZW5kZXJpbmc6IGF1dG87CiAgICAgICAgICAgIHRleHQtc2hhZG93OiBub25lOwogICAgICAgICAgICB0ZXh0LXRyYW5zZm9ybTogbm9uZTsKICAgICAgICAgICAgdHJhbnNpdGlvbi1kZWxheTogMHM7CiAgICAgICAgICAgIHRyYW5zaXRpb24tZHVyYXRpb246IDAuMnM7CiAgICAgICAgICAgIHRyYW5zaXRpb24tcHJvcGVydHk6IGJvcmRlci1jb2xvcjsKICAgICAgICAgICAgdHJhbnNpdGlvbi10aW1pbmctZnVuY3Rpb246IGN1YmljLWJlemllcigwLjY0NSwgMC4wNDUsIDAuMzU1LCAxKTsKICAgICAgICAgICAgd2hpdGUtc3BhY2U6IHByZS13cmFwOwogICAgICAgICAgICB3b3JkLXNwYWNpbmc6IDBweDsKICAgICAgICAgICAgd3JpdGluZy1tb2RlOiBob3Jpem9udGFsLXRiOwogICAgICAgIH0K"
  159. $('head').append($('<style>' + atob(style) + '</style>'))
  160. let textarea = $(`<textarea style="position: fixed;left: 0px;top: 0px;display: none;" class="base64" name="" id="" cols="15" rows="1" ></textarea>`)
  161. $('body').append(textarea)
  162. function decode(text) {
  163. try{
  164. if(text.length % 4 !== 1) {
  165. textarea.val(utf8.decode(atob(text)))
  166. textarea.css('background-color', '#fff')
  167. textarea.css('display', '')
  168. textarea[0].cols = textarea.val().length
  169. textarea[0].style.height = ''
  170. textarea[0].style.height = textarea[0].scrollHeight + 'px'
  171. textarea.css('left', `${event.clientX + 10}px`)
  172. textarea.css('top', `${event.clientY + 10}px`)
  173. }
  174. }catch(e){
  175. textarea.val('')
  176. textarea.css('display', 'none')
  177. }
  178. }
  179. let state = 0
  180. let just_moved = 0
  181. textarea[0].addEventListener('dblclick', function(){
  182. this.select()
  183. if (document.execCommand('copy')) {
  184. textarea.css('background-color', '#c8faff')
  185. }
  186. })
  187. $('#Wrapper')[0].addEventListener('mouseup', function(){
  188. state = 0
  189. })
  190. $('#Wrapper')[0].addEventListener('mousedown', function(){
  191. state = 1
  192. })
  193. $('#Wrapper')[0].addEventListener('click', function(){
  194. if(just_moved === 0){
  195. textarea.css('display', 'none')
  196. state = 0
  197. } else {
  198. just_moved = 0
  199. }
  200. })
  201. $('#Wrapper')[0].addEventListener('mousemove', function(){
  202. if(state === 1) {
  203. let t = window.getSelection().toString().trim()
  204. if(t.length !== 0) {
  205. decode(t)
  206. just_moved = 1
  207. }
  208. }
  209. })
  210. $('#Wrapper')[0].addEventListener('dblclick', function(){
  211. let t = window.getSelection().toString().trim()
  212. if(t.length !== 0) {
  213. decode(t)
  214. }
  215. })
  216. // 底注
  217. $(function(){
  218. setTimeout(function(){
  219. //console.clear()
  220. console.log("\n\n\n Thanks for using my script~\n\n\n\n")
  221. }, 2000)
  222. })
  223. })()

QingJ © 2025

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