🏠 Home 

V2EXcellent.js

A Better V2EX


安装此脚本?
  1. // ==UserScript==
  2. // @name V2EXcellent.js
  3. // @namespace http://vitovan.github.io/v2excellent.js/
  4. // @version 1.1.11
  5. // @description A Better V2EX
  6. // @author VitoVan
  7. // @include http*://*v2ex.com/*
  8. // @require https://cdnjs.cloudflare.com/ajax/libs/markdown-it/8.4.2/markdown-it.min.js
  9. // @grant none
  10. // ==/UserScript==
  11. $('document').ready(function() {
  12. window.loaded = true;
  13. });
  14. var POST_PROCESS_FUNCS = [
  15. function done() {
  16. console.log('V2EXcellented!');
  17. },
  18. ];
  19. //Fix night mode
  20. var divWrapper = document.getElementById("Wrapper");
  21. if(divWrapper.className == 'Night'){
  22. divWrapper.style.backgroundColor ='#00000000';
  23. divWrapper.style.backgroundImage ="url('/static/img/shadow.png'), url('//static.v2ex.com/bgs/pixels.png')";
  24. }
  25. // markdown-it 初始化
  26. var md = window.markdownit({
  27. html: true,
  28. linkify: true,
  29. breaks: true,
  30. langPrefix: "hljs ",
  31. highlight: function (str, lang) {
  32. if (lang && hljs.getLanguage(lang)) {
  33. try {
  34. return hljs.highlight(lang, str).value;
  35. } catch (__) { }
  36. }
  37. return ''; // use external default escaping
  38. }
  39. });
  40. // 图片链接自动转换成图片 代码来自caoyue@v2ex
  41. POST_PROCESS_FUNCS.push(function linksToImgs() {
  42. var links = document.links;
  43. for (var x in links) {
  44. var link = links[x];
  45. if (
  46. /^http.*\.(?:jpg|jpeg|jpe|bmp|png|gif)/i.test(link.href) &&
  47. !/<img\s/i.test(link.innerHTML)
  48. ) {
  49. link.innerHTML =
  50. "<img title='" + link.href + "' src='" + link.href + "' style='max-width:100%' />";
  51. }
  52. }
  53. });
  54. // 回复内容做markdown渲染
  55. POST_PROCESS_FUNCS.push(function mdRender() {
  56. $(".reply_content").each(function(index, item) {
  57. var replyContent = $(item).html();
  58. replyContent = replyContent.replace(/<br\s*\/?>/gi,"\r\n");
  59. $(item).html(md.render(replyContent));
  60. });
  61. });
  62. function postProcess() {
  63. $(POST_PROCESS_FUNCS).each(function(i, f) {
  64. if (typeof f === 'function') {
  65. f();
  66. console.log('V2EXcellent Post Processing: ' + f.name);
  67. }
  68. });
  69. }
  70. var language = (
  71. window.navigator.userLanguage || window.navigator.language
  72. ).toLowerCase();
  73. var currentLocation = location.href;
  74. //If this is the thread page
  75. if (currentLocation.match(/\/t\/\d+/g)) {
  76. //Enable Reply Directly Feature
  77. $('div.topic_buttons').append(
  78. ' &nbsp;<a " href="#;" onclick="$(\'#reply_content\').focus();" class="tb">回复</a>',
  79. );
  80. //Enable Img Uploader Feature
  81. enableUploadImg();
  82. var comments = [];
  83. //loading
  84. showSpinner();
  85. //Get comments from current page
  86. fillComments($('body'));
  87. //Get other pages comments
  88. var CURRENT_PAGE_URLS = [];
  89. $('a[href].page_normal').each(function(i, o) {
  90. if (CURRENT_PAGE_URLS.indexOf(o.href) === -1) {
  91. CURRENT_PAGE_URLS.push(o.href);
  92. }
  93. });
  94. var LEFT_PAGES_COUNT = CURRENT_PAGE_URLS.length;
  95. var CURRENT_PAGE = 0;
  96. var DOMS = [$(document)];
  97. if (LEFT_PAGES_COUNT > 0) {
  98. $(CURRENT_PAGE_URLS).each(function(i, o) {
  99. $.get(o, function(r###lt) {
  100. var r###ltDom = $('<output>').append($.parseHTML(r###lt));
  101. DOMS.push(r###ltDom);
  102. fillComments(r###ltDom);
  103. // 替换收藏的链接
  104. var collectUrl = r###ltDom
  105. .find('.topic_buttons .tb:contains("收藏")')
  106. .attr('href');
  107. $('.topic_buttons .tb:contains("收藏")').attr('href', collectUrl);
  108. CURRENT_PAGE++;
  109. //if all comments are sucked.
  110. if (CURRENT_PAGE === LEFT_PAGES_COUNT) {
  111. //stack'em
  112. stackComments();
  113. //reArrange
  114. reArrangeComments();
  115. // post process functions
  116. postProcess();
  117. }
  118. });
  119. });
  120. } else {
  121. stackComments();
  122. //reArrange
  123. reArrangeComments();
  124. // post process functions
  125. postProcess();
  126. }
  127. // Clear Default Pager
  128. $('a[href^="?p="]')
  129. .parents('div.cell')
  130. .remove();
  131. } else if (currentLocation.match(/\/new/)) {
  132. $(
  133. '<a href="https://imgur.com/upload" target="_blank" style="padding:0 5px;">上传图片</a>',
  134. ).insertAfter($('button[onclick="previewTopic();"]'));
  135. }
  136. function jumpToReply() {
  137. var floorSpecArr = currentLocation.match(/#reply\d+/g);
  138. var floorSpec = floorSpecArr && floorSpecArr.length ? floorSpecArr[0] : false;
  139. if (floorSpec) {
  140. floorSpec = floorSpec.match(/\d+/g)[0];
  141. var specFloor = $('span.no').filter(function() {
  142. return $(this).text() === floorSpec;
  143. });
  144. var scrollFunc = function() {
  145. window.scrollTo(0, specFloor.offset().top - $('body').offset().top);
  146. };
  147. if (window.loaded) {
  148. scrollFunc();
  149. } else {
  150. window.onload = function() {
  151. setTimeout(function() {
  152. scrollFunc();
  153. }, 1);
  154. };
  155. }
  156. }
  157. }
  158. //Remove #reply42 from index
  159. $('span.item_title>a').attr('href', function(i, val) {
  160. return val.replace(/#reply\d+/g, '');
  161. });
  162. function fillComments(jqDom) {
  163. jqDom.find('div[id^="r_"]').each(function(i, o) {
  164. var cmno = parseInt(
  165. $(o)
  166. .find('span.no')
  167. .text(),
  168. );
  169. comments[cmno] = {
  170. id: $(o).attr('id'),
  171. no: cmno,
  172. user: $(o)
  173. .find('strong>a')
  174. .text(),
  175. content: $(o)
  176. .find('div.reply_content')
  177. .text(),
  178. mentioned: (function() {
  179. var mentionedNames = [];
  180. $(o)
  181. .find('div.reply_content>a[href^="/member/"]:not("dark")')
  182. .each(function(i, o) {
  183. mentionedNames.push(o.innerHTML);
  184. });
  185. return mentionedNames;
  186. })(),
  187. subComments: [],
  188. };
  189. });
  190. }
  191. //Enable Floor Specification Feature
  192. $('a[href="#;"]:has(img[alt="Reply"])').click(function(e) {
  193. var floorNo = $(e.currentTarget)
  194. .parent()
  195. .find('span.no')
  196. .text();
  197. replyContent = $('#reply_content');
  198. oldContent = replyContent.val().replace(/^#\d+ /g, '');
  199. postfix = ' ' + '#' + floorNo + ' ';
  200. newContent = '';
  201. if (oldContent.length > 0) {
  202. if (oldContent != postfix) {
  203. newContent = oldContent + postfix;
  204. }
  205. } else {
  206. newContent = postfix;
  207. }
  208. replyContent.focus();
  209. replyContent.val(newContent);
  210. moveEnd($('#reply_content'));
  211. });
  212. //Enable Gift ClickOnce Feature
  213. $('a[href="/mission/daily"]')
  214. .attr('id', 'gift_v2excellent')
  215. .attr('href', '#')
  216. .click(function() {
  217. $('#gift_v2excellent').text('正在#取......');
  218. $.get('/mission/daily', function(r###lt) {
  219. var giftLink = $('<output>')
  220. .append($.parseHTML(r###lt))
  221. .find('input[value^="#取"]')
  222. .attr('onclick')
  223. .match(/\/mission\/daily\/redeem\?once=\d+/g)[0];
  224. $.get(giftLink, function(checkR###lt) {
  225. var okSign = $('<output>')
  226. .append($.parseHTML(checkR###lt))
  227. .find('li.fa.fa-ok-sign');
  228. if (okSign.length > 0) {
  229. $.get('/balance', function(r###lt) {
  230. var amount = $('<output>')
  231. .append($.parseHTML(r###lt))
  232. .find('table>tbody>tr:contains("每日登录"):first>td:nth(2)')
  233. .text();
  234. $('#gift_v2excellent').html(
  235. '已#取 <strong>' + amount + '</strong> 铜币。',
  236. );
  237. setTimeout(function() {
  238. $('#Rightbar>.sep20:nth(1)').remove();
  239. $('#Rightbar>.box:nth(1)').remove();
  240. }, 2000);
  241. });
  242. }
  243. });
  244. });
  245. return false;
  246. });
  247. //Get comment's parent
  248. function findParentComment(comment) {
  249. var parent;
  250. if (comment) {
  251. var floorRegex = comment.content.match(/#\d+ /g);
  252. if (floorRegex && floorRegex.length > 0) {
  253. var floorNo = parseInt(floorRegex[0].match(/\d+/g)[0]);
  254. parent = comments[floorNo];
  255. } else {
  256. for (var i = comment.no - 1; i > 0; i--) {
  257. var cc = comments[i];
  258. if (cc) {
  259. if (
  260. $.inArray(cc.user, comment.mentioned) !== -1 &&
  261. parent === undefined
  262. ) {
  263. parent = cc;
  264. }
  265. //If they have conversation, then make them together.
  266. if (
  267. comment.mentioned.length > 0 &&
  268. cc.user === comment.mentioned[0] &&
  269. cc.mentioned[0] === comment.user
  270. ) {
  271. parent = cc;
  272. break;
  273. }
  274. }
  275. }
  276. }
  277. }
  278. return parent;
  279. }
  280. //Stack comments, make it a tree
  281. function stackComments() {
  282. for (var i = comments.length - 1; i > 0; i--) {
  283. var parent = findParentComment(comments[i]);
  284. if (parent) {
  285. parent.subComments.unshift(comments[i]);
  286. comments.splice(i, 1);
  287. }
  288. }
  289. }
  290. function getCommentDom(id) {
  291. var commentDom;
  292. $.each(DOMS, function(i, o) {
  293. var r###lt = o.find('div[id="' + id + '"]');
  294. if (r###lt.length > 0) {
  295. commentDom = r###lt;
  296. }
  297. });
  298. return commentDom;
  299. }
  300. function moveComment(comment, parent) {
  301. if (comment) {
  302. var commentDom = getCommentDom(comment.id);
  303. $.each(comment.subComments, function(i, o) {
  304. moveComment(o, commentDom);
  305. });
  306. commentDom.appendTo(parent);
  307. }
  308. }
  309. function getCommentBox() {
  310. var commentBox = $('#Main>div.box:nth(1)');
  311. if (commentBox.length === 0) {
  312. // Maybe using mobile
  313. commentBox = $('#Wrapper>div.content>div.box:nth(1)');
  314. if ($('#v2excellent-mobile-tip').length === 0) {
  315. $(
  316. '<div class="cell" id="v2excellent-mobile-tip" style="background: #CC0000;font-weight: bold;text-align: center;"><span><a style="color:white;text-decoration:underline;" target="_blank" href="https://github.com/VitoVan/v2excellent.js/issues/7#issuecomment-304674654">About V2EXcellent.js on Mobile</a></span></div>',
  317. ).insertBefore('#Wrapper>div.content>div.box:nth(1)>.cell:first');
  318. }
  319. }
  320. return commentBox;
  321. }
  322. function showSpinner() {
  323. var commentBox = getCommentBox();
  324. $('body').append(
  325. '<style>.spinner{width:40px;height:40px;position:relative;margin:100px auto}.double-bounce1,.double-bounce2{width:100%;height:100%;border-radius:50%;background-color:#333;opacity:.6;position:absolute;top:0;left:0;-webkit-animation:sk-bounce 2.0s infinite ease-in-out;animation:sk-bounce 2.0s infinite ease-in-out}.double-bounce2{-webkit-animation-delay:-1.0s;animation-delay:-1.0s}@-webkit-keyframes sk-bounce{0%,100%{-webkit-transform:scale(0.0)}50%{-webkit-transform:scale(1.0)}}@keyframes sk-bounce{0%,100%{transform:scale(0.0);-webkit-transform:scale(0.0)}50%{transform:scale(1.0);-webkit-transform:scale(1.0)}}</style>',
  326. );
  327. $(
  328. '<div class="spinner"><div class="double-bounce1"></div><div class="double-bounce2"></div></div>',
  329. ).insertBefore(commentBox);
  330. commentBox.hide();
  331. }
  332. function reArrangeComments() {
  333. $('div.inner:has(a[href^="/t/"].page_normal)').remove();
  334. var commentBox = getCommentBox();
  335. $.each(comments, function(i, o) {
  336. moveComment(o, commentBox);
  337. });
  338. $('div[id^="r_"]>table>tbody>tr>td:first-child').attr('width', '20');
  339. $('td img.avatar').css('width', '');
  340. $('body').append(
  341. '<style>.cell{background-color: inherit;}.cell .cell{padding-bottom:0;border-bottom:none;min-width: 250px;padding-right:0;}div[id^="r_"] img.avatar{width:20px;height:20px;border-radius:50%;}div[id^="r_"]>div{margin-left: 5px;}</style>',
  342. );
  343. commentBox.show();
  344. //removeSpinner
  345. $('.spinner').remove();
  346. jumpToReply();
  347. }
  348. function enableUploadImg() {
  349. $('div.cell:contains("添加一条新回复")').append(
  350. '<div class="fr"><a href="https://imgur.com/upload" target="_blank"> 上传图片</a> - </div>',
  351. );
  352. }