BGG Shortcuts

Keyboard shortcuts for the Geek

当前为 2016-05-23 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name BGG Shortcuts
  3. // @namespace BGG Shortcuts
  4. // @version 0.9.4
  5. // @description Keyboard shortcuts for the Geek
  6. // @include http://*.boardgamegeek.*/*
  7. // @include http://boardgamegeek.*/*
  8. // @include https://*.boardgamegeek.*/*
  9. // @include https://boardgamegeek.*/*
  10. // @copyright 2013+, JB McMichael
  11. // ==/UserScript==
  12.  
  13. /*
  14. * CHANGLOG::
  15. * ============================================
  16. * 0.9.4 - When loading a comment in a geeklist, scroll to item the comment is for
  17. * 0.9.3 - Fixed an error on Firefox relating to using strict
  18. * 0.9.2 - Better next link
  19. * 0.9.1 - Fixed a stupid bug
  20. * 0.9.0 - Updated so that the shortcuts work on the new game page style
  21. * 0.8.1 - Updated to work on https
  22. * 0.8.0 - Forum links pop up in a modal so that you don't lose your place on a page
  23. * 0.7.0 - Changed the links to just be J for next item, H for home, and / for search, but disabled shortcuts in form elements
  24. * 0.6.1 - Give the page some time to load its scripts before changing links
  25. * 0.6.0 - Added a shortcut to go to the searchbox Ctrl + /
  26. * 0.5.0 - Added homepage links opening in new tab
  27. * 0.4.0 - If search returns one result just go to that result
  28. * 0.3.0 - Added homepage shortcut; Ctrl + Shift + H
  29. * 0.2.0 - Cleaned up the subscription jump link
  30. * 0.1.0 - First version, shortcut for subscriptions; Ctrl + M
  31. *
  32. */
  33.  
  34. (function ()
  35. {
  36. "use strict";
  37. console.log('Loaded BGG Shortcuts');
  38.  
  39. document.body.addEventListener('keydown', function (e)
  40. {
  41. var active = document.activeElement.tagName.toLowerCase(),
  42. badElements = ['input', 'textarea', 'select'];
  43.  
  44. // ignore shortcuts if we are in some form of input
  45. if (badElements.indexOf(active) === -1)
  46. {
  47.  
  48. // Next subscription item J
  49. if (e.keyCode === 74)
  50. {
  51. if (!!document.querySelector('[href="/subscriptions/next"]'))
  52. {
  53. var next = document.querySelectorAll('[href="/subscriptions/next"]');
  54. next[0].click();
  55. }
  56. else if (!!document.querySelectorAll("img:not(dn).nextsubcol")[0])
  57. {
  58. [].slice.call(document.querySelectorAll("img:not(dn).nextsubcol")[0].parentNode.parentNode.click());
  59. }
  60. }
  61.  
  62. // Home page H
  63. if (e.keyCode === 72 && window.location.href !== window.location.origin)
  64. {
  65. window.location.href = window.location.origin;
  66. }
  67.  
  68. // Search box jump /
  69. if (e.keyCode === 191)
  70. {
  71. var searchbox = !!document.getElementById('sitesearch') ? document.getElementById('sitesearch') : document.querySelector('[ng-model="searchctrl.search.q"]');
  72. document.body.scrollTop = 0;
  73. searchbox.focus();
  74. window.setTimeout(function ()
  75. {
  76. searchbox.value = '';
  77. }, 10);
  78. }
  79. }
  80.  
  81. }, false);
  82.  
  83. //check for one result on the search page
  84. if (window.location.pathname === '/geeksearch.php' && window.location.search.search(/action=search/))
  85. {
  86. var results = document.querySelectorAll('#collectionitems tbody tr');
  87. console.log('We are searching');
  88. if (results.length === 2)
  89. {
  90. console.log('Found just one result, redirect');
  91. var link = results[1].querySelectorAll('#results_objectname1 a'),
  92. href = link[0].getAttribute('href');
  93.  
  94. window.location.href = window.location.origin + href;
  95. }
  96. }
  97.  
  98. // set all homepage module links to open in new tab
  99. if (window.location.pathname === '/')
  100. {
  101. window.setTimeout(function ()
  102. {
  103. console.log('Changing homepage links');
  104. var links = document.querySelectorAll('.innermoduletable tbody td a.ng-binding'),
  105. linkArray = [].slice.call(links);
  106.  
  107. linkArray.forEach(function (item, index)
  108. {
  109. item.setAttribute('target', '_blank');
  110. }
  111. );
  112. }, 500);
  113. }
  114.  
  115. // popup forum links in a dialog
  116. if (window.location.pathname.split('/')[1] === 'boardgame')
  117. {
  118. // grab all forum link clicks
  119. document.addEventListener('click', forumClick, false);
  120. }
  121.  
  122. function forumClick(e)
  123. {
  124. if (e.target.tagName === 'A' && e.target.href.split('/')[3] === 'thread')
  125. {
  126. e.preventDefault();
  127.  
  128. // use the BGG API to get the thread
  129. var req = new XMLHttpRequest(),
  130. apiUrl = window.location.protocol + '//' + window.location.host + '/xmlapi2/thread?id=',
  131. diag = document.createElement('dialog'),
  132. content = document.createElement('div'),
  133. close = document.createElement('button'),
  134. thread = e.target.href.split('/')[4];
  135.  
  136. diag.style.width = '80%';
  137. diag.style.height = '80%';
  138. diag.style.border = '2px solid rgba(0, 0, 0, 0.3)';
  139. diag.style.borderRadius = '6px';
  140. diag.style.boxShadow = '0 3px 7px rgba(0, 0, 0, 0.3)';
  141.  
  142. content.style.overflowY = 'auto';
  143. content.style.height = '95%';
  144. content.style.margin = '5px 0px';
  145.  
  146. close.innerText = 'Close';
  147.  
  148. req.onreadystatechange = showContents;
  149.  
  150. req.open('GET', apiUrl + thread, true);
  151.  
  152. req.send();
  153.  
  154. showContents(e, req);
  155. }
  156. }
  157.  
  158. function showContents(e, req)
  159. {
  160. if (req.readyState === 4 && req.status === 200)
  161. {
  162. var subject = req.responseXML.documentElement.children[0].firstChild.nodeValue,
  163. articles = req.responseXML.documentElement.children[1].children;
  164.  
  165. for (var i = 0; i < articles.length; i++)
  166. {
  167. var article = articles[i];
  168. var user = article.getAttribute('username');
  169. var title = article.children[0].firstChild.nodeValue;
  170. var body = article.children[1].firstChild.nodeValue;
  171. var postdate = article.getAttribute('postdate');
  172. var articleDiv = document.createElement('div');
  173. var dl = document.createElement('dl');
  174. var ddLeft = document.createElement('dd');
  175. var ddRight = document.createElement('dd');
  176. var userDiv = document.createElement('div');
  177. var bottomDl = document.createElement('dl');
  178. var ddLeft2 = document.createElement('dd');
  179. var ddCommands = document.createElement('dd');
  180. var ul = document.createElement('ul');
  181. var li = document.createElement('li');
  182. var ulInfo = document.createElement('ul');
  183. var liInfo = document.createElement('li');
  184. var postLink = document.createElement('a');
  185. var clearDiv = document.createElement('div');
  186. var rollsDiv = document.createElement('div');
  187. var userInfo = getUser(user);
  188.  
  189. articleDiv.addClass('article');
  190.  
  191. rollsDiv.addClass('rollsblock');
  192.  
  193. ddLeft.addClass('left');
  194. ddRight.addClass('right');
  195. userDiv.addClass('username');
  196. userDiv.innerHTML = user;
  197.  
  198. ddLeft.appendChild(userDiv);
  199. ddRight.innerHTML = body;
  200.  
  201. dl.appendChild(ddLeft);
  202. dl.appendChild(ddRight);
  203.  
  204. articleDiv.appendChild(dl);
  205.  
  206. ddLeft2.addClass('left');
  207. ddCommands.addClass('commands');
  208.  
  209. ul.appendChild(li);
  210. ddCommands.appendChild(ul);
  211.  
  212. clearDiv.addClass('clear');
  213. ulInfo.addClass('information');
  214.  
  215. postLink.innerHTML = postdate;
  216. liInfo.appendChild(postLink);
  217. ulInfo.appendChild(liInfo);
  218. ddCommands.appendChild(ulInfo);
  219.  
  220. bottomDl.appendChild(ddLeft2);
  221. bottomDl.appendChild(ddCommands);
  222.  
  223. articleDiv.appendChild(bottomDl);
  224. articleDiv.appendChild(clearDiv);
  225. articleDiv.appendChild(rollsDiv);
  226.  
  227. content.appendChild(articleDiv);
  228. }
  229.  
  230. diag.insertBefore(close, diag.childNodes[0]);
  231. diag.insertBefore(content, diag.childNodes[0]);
  232. close.addEventListener('click', function (e)
  233. {
  234. diag.close();
  235. }
  236. );
  237. document.body.insertBefore(diag, document.body.childNodes[0]);
  238. diag.showModal();
  239. }
  240. }
  241.  
  242. // Check for readystate so that we can shift the page if needed
  243. var interval = setInterval(function() {
  244. if(document.readyState === 'complete') {
  245. clearInterval(interval);
  246. checkForComment();
  247. }
  248. }, 100);
  249.  
  250. function checkForComment() {
  251. if (/comment[0-9]+/.test(window.location.hash) && window.location.pathname.includes('/geeklist/')) {
  252. var hash = window.location.hash;
  253. var comment = document.querySelector(hash);
  254. var parent = comment.parentElement.parentElement.parentElement.parentElement; // get the actual item
  255.  
  256. window.scroll(0, findPos(parent));
  257. }
  258. }
  259.  
  260. function findPos(obj) {
  261. var curtop = 0;
  262. if (obj.offsetParent) {
  263. do {
  264. curtop += obj.offsetTop;
  265. } while (obj = obj.offsetParent);
  266. return [curtop];
  267. }
  268. }
  269.  
  270. function getUser(name) {
  271. var req = new XMLHttpRequest(),
  272. apiUrl = window.location.protocol + '//' + window.location.host + '/xmlapi2/user?name=' + name;
  273.  
  274. req.onreadystatechange = function (e)
  275. {
  276. if (req.readyState === 4 && req.status === 200)
  277. {
  278. console.log(req.responseXML);
  279. }
  280. };
  281.  
  282. req.open('GET', apiUrl, true);
  283.  
  284. req.send();
  285. }
  286. }
  287. ());