Greasy Fork镜像 还支持 简体中文。

Geeklist Tweaks

Various tweaks to improve BGG (boardgamegeek.com).

  1. // ==UserScript==
  2. // @name Geeklist Tweaks
  3. // @namespace tequila_j-script
  4. // @version 0.8.3
  5. // @description Various tweaks to improve BGG (boardgamegeek.com).
  6. //
  7. // @match https://boardgamegeek.com/geeklist/*
  8. // @match https://www.boardgamegeek.com/geeklist/*
  9. // @match http://boardgamegeek.com/geeklist/*
  10. // @match http://www.boardgamegeek.com/geeklist/*
  11. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js
  12. // @require https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js
  13. // @grant GM_addStyle
  14. // ==/UserScript==
  15.  
  16.  
  17. function isMobileDevice() {
  18. var check = false;
  19. (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
  20. return check;
  21. };
  22.  
  23. GM_addStyle(`
  24. div.t-nav-container {
  25. position: absolute;
  26. //top: 0px;
  27. //right: -35px;
  28. top: -2px;
  29. right: 0px;
  30. background-color: rgba(120,120,180,0.4);
  31. border-radius: 5px;
  32. //opacity: 0.5;
  33. //height: 50px;
  34. text-align: center;
  35. padding: 3px 3px;
  36. opacity: 0.4;
  37. }
  38.  
  39. div.t-nav-container:hover {
  40. opacity: 0.95;
  41. }
  42.  
  43. div.t-nav-container span.fa {
  44. font-size: 16px;
  45. padding: 0px 3px;
  46. cursor: pointer;
  47. }
  48.  
  49. div.t-nav-container span.fa-workaround {
  50. font-size: 16px;
  51. padding: 0px 3px;
  52. margin: 0px 2px;
  53. background-color: #090909;
  54. cursor: pointer;
  55. border-radius: 2px;
  56. color: white;
  57. }
  58.  
  59. div.box-new-items:hover {
  60. opacity: 1;
  61. }
  62. div.box-new-items {
  63. position: fixed;
  64. bottom: 0px;
  65. /* height: 20px; */
  66. background-color: lightgray;
  67. right: 7px;
  68. font-size: x-small;
  69. padding: 2px 5px;
  70. border-radius: 3px;
  71. opacity: 0.7;
  72. }
  73.  
  74. a.tj_geeklist-number {
  75. position: relative;
  76. }
  77.  
  78. div.tj_url-copy-helper {
  79. position: absolute;
  80. display:none;
  81. background-color: rgba(120,120,180,0.4);
  82. color: black;
  83. border-radius: 3px;
  84. padding: 3px;
  85. right: -18px;
  86. top: -14px;
  87. }
  88.  
  89. a.tj_geeklist-number:hover div.tj_url-copy-helper {
  90. display:block;
  91. }
  92.  
  93. div.geekitem_flash_info {
  94. position: fixed;
  95. top:50px;
  96. left: 0;
  97. right:0;
  98. opacity: 0.9;
  99. background: #d0cece;
  100. }
  101.  
  102. `);
  103.  
  104.  
  105. if (isMobileDevice()) {
  106. console.debug("Mobile View");
  107. GM_addStyle(`
  108. div.t-nav-container span.fa {
  109. font-size: 28px;
  110. padding: 0px 15px;
  111. marging: 0px 7px;
  112. cursor: pointer;
  113. }
  114. div.t-nav-container span.fa-workaround {
  115. font-size: 28px;
  116. padding: 0px 15px;
  117. marging: 0px 7px;
  118. cursor: pointer;
  119. }
  120. `);
  121. }
  122.  
  123. var __tj = {
  124. 'gap' : -75,
  125. show_gotosubbed: false
  126. }
  127.  
  128.  
  129.  
  130. function urlParam(param) {
  131. var vars = {};
  132. window.location.href.replace(location.hash, '').replace(
  133. /[?&]+([^=&]+)=?([^&]*)?/gi, // regexp
  134. function (m, key, value) { // callback
  135. vars[key] = value !== undefined ? value : '';
  136. }
  137. );
  138.  
  139. if (param) {
  140. return vars[param] ? vars[param] : null;
  141. }
  142. return vars;
  143. }
  144.  
  145. function insertParam(key, value, url) {
  146. key = escape(key); value = escape(value);
  147. var hash = url.substring(url.indexOf("#"));
  148. var localurl = url.substring(0, url.indexOf("#"));
  149. var urlparts = localurl.split('?');
  150. var resultquerystring = "";
  151.  
  152. if (urlparts.length <= 1) { //there isn't a query string
  153. resultquerystring += '?' + key + '=' + value;
  154. }
  155. else { //there is query strings
  156. var pairs = urlparts[1].split("&");
  157. for (var i = 0; i < pairs.length; i++) {
  158. var t = pairs[i].split('=');
  159. if (t[0] == key) {
  160. if (value) {
  161. t[1] = value;
  162. pairs[i] = t.join('=');
  163. }
  164. else
  165. pairs[i] = t[0];
  166. break;
  167. }
  168. }
  169. //this will reload the page, it's likely better to store this until finished
  170. resultquerystring = pairs.join('&');
  171. }
  172. return urlparts + resultquerystring + hash;
  173. }
  174.  
  175.  
  176. $(document).ready(function () {
  177. 'use strict';
  178. /*jshint multistr: true */
  179.  
  180. // $("head").append(
  181. // '<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" type="text/css">'
  182. // );
  183.  
  184. //Crete a container to display geekitem
  185. var $geekintemflash = $('<div class="geekitem_flash_info"></div>');
  186. $('body').append($geekintemflash);
  187.  
  188. function displayGeekItemHeader(event, element) {
  189. $geekintemflash.finish().hide();
  190. event.preventDefault();
  191. //geeklists has class mb5, so, we ignore then
  192. console.log($(element).attr('class'));
  193. if ($(element).parent().hasClass('mb5')) return true; //this is a geekitem list, we do not need to show anything
  194. //display geekitem title
  195. var $element = $(element);
  196. var gl = $element.parents('div.mb5').find('div.geeklist_item_title:first');
  197. if (typeof gl === undefined) return true;
  198. $geekintemflash.html($(gl).clone());
  199. $geekintemflash.show().delay(3000).fadeOut('fast');
  200. }
  201.  
  202. $(document).on( "scrollFinished",displayGeekItemHeader);
  203.  
  204.  
  205.  
  206. //add a parameter to geeklist items so you can have no auto scroll between people that uises the script
  207. // also, put a small icon to copy url
  208. $(".geeklist_item_title > .fl").each(function () {
  209. var $this = $(this);
  210. if ($this.children('a').length < 2) return true; //this is not a geeklist item in a geeklist , it is just a direct item (only shows this item in page)
  211. var $listNumber = $this.children('a:first');
  212. $listNumber.addClass("tj_geeklist-number");
  213. var url = $listNumber.attr('href');
  214. var newurl = insertParam('__ns', 't', url);
  215. $listNumber.attr('href', newurl);
  216. var copyLink = $('<div class="tj_url-copy-helper"><i class="fa fa-files-o" aria-hidden="true"></i></div>');
  217. copyLink.attr('data-clipboard-text', $listNumber[0].href);
  218. copyLink.click(function (event) { event.preventDefault() });
  219. $listNumber.append(copyLink);
  220. new Clipboard(copyLink[0]);//convert jquery to htmlelemnt
  221. });
  222.  
  223.  
  224. //lets find items, excluding multiple comments
  225. var firstItems = [];
  226. var nonFirstItems = [];
  227.  
  228. var geekItems = $('div.mb5');
  229. var geekSubbedPosition = -1;
  230.  
  231. for (var i = 0; i < geekItems.length; i++) {
  232. var thisGI = $(geekItems[i]);
  233. var newSI = thisGI.find('div.subbed_selected,div.subbed');
  234. var firstIsGeekItem = false;
  235. if (newSI.length > 0) {
  236. var el = newSI[0];
  237. firstItems.push(newSI[0]);
  238. firstIsGeekItem = $(el).attr('data-objecttype') == "listitem";
  239. geekSubbedPosition = firstItems.length - 1
  240. }
  241. if (newSI.length > 1 && firstIsGeekItem) {
  242. firstItems.push(newSI[1])
  243. geekSubbedPosition = firstItems.length - 1;
  244. }
  245. }
  246.  
  247. // console.log('New items in geeklists:' + firstItems.length);
  248.  
  249. //we will try to find comments at the end of the page also
  250. var pageComments = $('div.mb5:first').parent().children('div:not(.mb5)').find('div.comment_ctrl').find('div.subbed_selected,div.subbed');
  251. console.log(pageComments);
  252. if (pageComments.length > 0) {
  253. firstItems.push(pageComments[0]);
  254. geekSubbedPosition = firstItems.length - 1;
  255. // console.log("New page comments also ");
  256. }
  257.  
  258. // console.log("Size: " + firstItems.length);
  259.  
  260. var iconUp = $('<span class="fa-workaround">&lt;&lt;</span>');
  261. var iconDown = $('<span class="fa-workaround">&gt;&gt;</span>');
  262. var iconNew = $('<span class="fa fa-ellipsis-h" aria-hidden="true"></span>');
  263. var iconBox = $('<div class="t-nav-container"></div>');
  264.  
  265.  
  266.  
  267. $(firstItems).each(function (index) {
  268.  
  269. var thisItem = $(this);
  270.  
  271. thisItem.css('position', 'relative');
  272.  
  273. var iconContainer = iconBox.clone();
  274. thisItem.append(iconContainer);
  275.  
  276. thisItem.find("dl.commentbody > dd.right").css("padding-right", "40px");
  277. //go new
  278. if (__tj.show_gotosubbed) {
  279. if (!$(firstItems[index]).hasClass('subbed_selected')) {
  280. var upI = iconNew.clone();
  281. upI.click(function () {
  282. $( document ).trigger( "scrollFinished", [ firstItems[geekSubbedPosition ] ]);
  283. $("html, body").animate({ scrollTop: $(firstItems[geekSubbedPosition]).offset().top + __tj.gap }, "fast");
  284. console.log("subbed");
  285. return false;
  286. });
  287. iconContainer.append(upI);
  288. } else {
  289. iconContainer.append(iconNew.clone().css('visibility', 'hidden'));
  290. }
  291. }
  292.  
  293. //go up
  294. if (index != 0) {
  295. var upI = iconUp.clone();
  296. upI.click(function () {
  297. $( document ).trigger( "scrollFinished", [ firstItems[index - 1] ] );
  298. $("html, body").animate({ scrollTop: $(firstItems[index - 1]).offset().top + __tj.gap }, "fast");
  299. console.log("up");
  300. return false;
  301. });
  302. iconContainer.append(upI);
  303.  
  304. } else {
  305. iconContainer.append(iconDown.clone().css('visibility', 'hidden'));
  306. }
  307.  
  308. //go down
  309. if (index != firstItems.length - 1) {
  310. var downI = iconDown.clone();
  311. downI.click(function () {
  312. $( document ).trigger( "scrollFinished", [ firstItems[index + 1] ] );
  313. $("html, body").animate({ scrollTop: $(firstItems[index + 1]).offset().top + __tj.gap }, "fast");
  314. console.log("down");
  315. return false;
  316. });
  317. iconContainer.append(downI);
  318. } else {
  319. iconContainer.append(iconUp.clone().css('visibility', 'hidden'));
  320. }
  321.  
  322. });
  323.  
  324. //lets see if this is a selected article (user clicked to see a subjetc, not new items).
  325. //If not, We can scroll to the top of the list
  326. if ($("div.article.selected").length == 0 && urlParam("__ns") == null) {
  327. $("html, body").scrollTop($(firstItems[0]).offset().top + __tj.gap);
  328. $( document ).trigger( "scrollFinished", [ $(firstItems[0]) ] );
  329. } else {
  330. $( document ).trigger( "scrollFinished", [ $(selectedUrlItem) ] );
  331. }
  332.  
  333.  
  334. //puts a small box saying how many new items are here:
  335. var news = $(document.createElement("div")).addClass("box-new-items");
  336. $("body").append(news);
  337. news.html("New items in this page: " + $('div.subbed_selected,div.subbed').length);
  338.  
  339. });
  340.  

QingJ © 2025

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