tftv keyboard shortcuts

Adds keyboard shortcuts for navigating forum pages on teamfortress.tv

当前为 2016-01-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name tftv keyboard shortcuts
  3. // @namespace Lense
  4. // @description Adds keyboard shortcuts for navigating forum pages on teamfortress.tv
  5. // @include /^https?://(www\.)?teamfortress.tv/.*/
  6. // @version 0.5.3
  7. // @grant none
  8. // ==/UserScript==
  9.  
  10. // Toggle highlighting of focused post. With this off, there are no visual changes.
  11. var focus_highlight = true;
  12.  
  13. /*
  14. * Style post with a highlight
  15. */
  16. function focus_post(post) {
  17. if(!focus_highlight) {
  18. return;
  19. }
  20. post.style.background = "#fff";
  21. post.style.boxShadow = "1px 1px 8px";
  22. post.style.zIndex = "1";
  23. }
  24.  
  25. /*
  26. * Reset style to normal
  27. */
  28. function unfocus_post(post) {
  29. post.style.background = "";
  30. post.style.boxShadow = "";
  31. post.style.zIndex = "";
  32. }
  33.  
  34. /*
  35. * Helper function to scroll to post by id
  36. */
  37. function goto_post(id) {
  38. unfocus_post(document.getElementById(prev_post_id));
  39. prev_post_id = id;
  40.  
  41. var post = document.getElementById(id);
  42. post.scrollIntoView();
  43. // Show upper border
  44. window.scrollBy(0,-1);
  45. // Highlight the current post
  46. focus_post(post);
  47. }
  48.  
  49. /*
  50. * Update styles on plus/minus frag and send the http request
  51. * id is the post id
  52. * frag_action is either plus or minus
  53. */
  54. function frag(id, frag_action) {
  55. var btn_container = document.querySelector('.post-frag-container[data-post-id="'+id+'"]');
  56. var type;
  57. if(btn_container.classList.contains("self")) {
  58. // This post doesn't actually have frags; it's the OP.
  59. type = "thread";
  60. btn_container = document.querySelector('.thread-frag-container' + (type=="post" ? '[data-'+type+'-id="'+id+'"]' : ''));
  61. } else {
  62. type = "post";
  63. }
  64.  
  65. // Most of this is ripped+modified from tftv js
  66. var frag_status = btn_container.attributes["data-frag-status"].value;
  67. if(frag_status == 'neutral' && frag_action == 'plus' || frag_status == 'minused' && frag_action == 'minus')
  68. var diff = 1;
  69. if(frag_status == 'neutral' && frag_action == 'minus' || frag_status == 'plused' && frag_action == 'plus')
  70. var diff = -1;
  71. if(frag_status == 'minused' && frag_action == 'plus')
  72. var diff = 2;
  73. if(frag_status == 'plused' && frag_action == 'minus')
  74. var diff = -2;
  75. var frag_count_container = type=="post" ? btn_container.firstElementChild : btn_container.querySelector("#thread-frag-count");
  76. var new_frag_count = parseInt(frag_count_container.textContent, 10) + diff;
  77. frag_count_container.textContent = new_frag_count;
  78. if(type == "post") {
  79. btn_container.children[0].classList.remove("positive");
  80. btn_container.children[0].classList.remove("negative");
  81. btn_container.children[0].classList.remove("neutral");
  82. btn_container.children[0].classList.add(new_frag_count > 0 ? "positive" : (new_frag_count < 0 ? "negative" : "neutral"));
  83. }
  84.  
  85. if(frag_status == 'plused' && frag_action == 'plus' || frag_status == 'minused' && frag_action == 'minus')
  86. new_status = 'neutral';
  87. if(frag_action == 'plus' && frag_status !== 'plused')
  88. new_status = 'plused';
  89. if(frag_action == 'minus' && frag_status !== 'minused')
  90. new_status = 'minused';
  91. btn_container.setAttribute("data-frag-status", new_status);
  92.  
  93. var plus_classes = btn_container.querySelector("." + type + "-frag-btn.plus").classList;
  94. var minus_classes = btn_container.querySelector("." + type + "-frag-btn.minus").classList;
  95. if(frag_action == "plus") {
  96. if(plus_classes.contains("clicked")) {
  97. plus_classes.remove("clicked");
  98. } else {
  99. plus_classes.add("clicked");
  100. }
  101. minus_classes.remove("clicked");
  102. } else {
  103. if(minus_classes.contains("clicked")) {
  104. minus_classes.remove("clicked");
  105. } else {
  106. minus_classes.add("clicked");
  107. }
  108. plus_classes.remove("clicked");
  109. }
  110.  
  111. var xmlhttp = new XMLHttpRequest();
  112. xmlhttp.onreadystatechange = function() {
  113. if(xmlhttp.readyState == XMLHttpRequest.DONE) {
  114. if(xmlhttp.status != 200) {
  115. alert('VOTE NOT SENT');
  116. }
  117. }
  118. }
  119. xmlhttp.open("POST", '/' + type + '/frag/' + (type=="post" ? id : btn_container.attributes["data-thread-id"].value) + '/' + frag_action, true);
  120. xmlhttp.send();
  121. }
  122.  
  123. /*
  124. * Find the number of pages in the thread
  125. */
  126. var pageList = document.querySelectorAll(".mod-page");
  127. var lastPage = parseInt(pageList[pageList.length-2].textContent);
  128. if(isNaN(lastPage)) {
  129. lastPage = 1;
  130. }
  131.  
  132. var posts = document.querySelectorAll(".post");
  133. focus_post(posts[0]);
  134. var cur_post_index = 0;
  135. var prev_post_id = posts[0].id;
  136.  
  137. /*
  138. * Handle keypresses
  139. */
  140. document.onkeypress = function(e) {
  141. // Not entirely sure why this is useful, but it can't break anything that isn't already broken
  142. e = e || window.event;
  143. // Ignore keypresses in text boxes, but allow if nothing or a link is selected
  144. if(e.target.nodeName != "BODY" && e.target.nodeName != "A") {
  145. // Got keypress, but it's probably in a textbox, so ignore it
  146. return;
  147. }
  148.  
  149. // Current page number
  150. var page = window.location.search == "" || parseInt(window.location.search.match("page=([0-9]*)")[1]);
  151.  
  152. switch(e["key"]) {
  153. /*
  154. * Pages (left/right)
  155. */
  156. case "d":
  157. if(page != lastPage) {
  158. page = (page+1).toString();
  159. window.location.search = "?page=" + page;
  160. }
  161. break;
  162. case "a":
  163. if(page != 1) {
  164. page = (page-1).toString();
  165. window.location.search = "?page=" + page;
  166. }
  167. break;
  168. case "D":
  169. if(page != lastPage) {
  170. window.location.search = "?page=" + lastPage.toString();
  171. }
  172. break;
  173. case "A":
  174. if(page != 1) {
  175. window.location.search = "?page=1";
  176. }
  177. break;
  178. /*
  179. * Posts (up/down)
  180. */
  181. case "w":
  182. if(cur_post_index > 0) {
  183. cur_post_index--;
  184. }
  185. goto_post(posts[cur_post_index].id);
  186. break;
  187. case "s":
  188. if(cur_post_index < posts.length - 1) {
  189. cur_post_index++;
  190. }
  191. goto_post(posts[cur_post_index].id);
  192. break;
  193. case "W":
  194. cur_post_index = 0;
  195. goto_post(posts[cur_post_index].id);
  196. break;
  197. case "S":
  198. cur_post_index = posts.length - 1;
  199. goto_post(posts[cur_post_index].id);
  200. break;
  201. /*
  202. * Frags (+/-)
  203. */
  204. case "q":
  205. frag(posts[cur_post_index].id.slice(8), "plus");
  206. break;
  207. case "e":
  208. frag(posts[cur_post_index].id.slice(8), "minus");
  209. break;
  210. }};