V2EXcellent.js

A Better V2EX

当前为 2020-01-02 提交的版本,查看 最新版本

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

QingJ © 2025

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