GitHub Diff Links

A userscript that adds links to diff and pull request headers to jump back & forth between files

As of 2016-12-29. See the latest version.

  1. // ==UserScript==
  2. // @name GitHub Diff Links
  3. // @version 1.1.2
  4. // @description A userscript that adds links to diff and pull request headers to jump back & forth between files
  5. // @license https://creativecommons.org/licenses/by-sa/4.0/
  6. // @namespace http://github.com/Mottie
  7. // @include https://github.com/*
  8. // @run-at document-idle
  9. // @grant GM_addStyle
  10. // @author Rob Garrison
  11. // ==/UserScript==
  12. /* jshint esnext:true, unused:true */
  13. (() => {
  14. "use strict";
  15.  
  16. // sometimes tooltips are too narrow...
  17. GM_addStyle(".gh-diff-links:after { min-width: 120px; }");
  18.  
  19. const button = document.createElement("a"),
  20. // button [ InnerHTML, tooltip ]
  21. nextBtn = ["Next", "Jump to next file\n("],
  22. prevBtn = ["Prev", "Jump to previous file\n(" ];
  23.  
  24. button.className = "btn btn-sm tooltipped tooltipped-s tooltipped-multiline" +
  25. "gh-diff-links";
  26. button.setAttribute("rel", "nofollow");
  27.  
  28. function addButton(el, content, link) {
  29. let btn = button.cloneNode(),
  30. txt = el.classList.contains("select-menu-item") ?
  31. $(".description", el).textContent :
  32. link.textContent || "";
  33. // clean up whitespace
  34. txt = txt.replace(/\s+/g, " ").trim();
  35. // only add file name to tooltip
  36. txt = txt.substring(txt.lastIndexOf("/") + 1, txt.length);
  37. btn.innerHTML = content[0];
  38. btn.setAttribute("aria-label", content[1] + txt + ")" );
  39. btn.href = link.hash;
  40. // prepend button
  41. el.insertBefore(btn, el.childNodes[0]);
  42. }
  43.  
  44. function addSpace(el, content) {
  45. let btn = button.cloneNode();
  46. btn.disabled = true;
  47. btn.className = "btn btn-sm gh-diff-links disabled";
  48. btn.innerHTML = content[0];
  49. el.insertBefore(btn, el.childNodes[0]);
  50. }
  51.  
  52. function addLinks() {
  53. let last,
  54. diffLinks = $$(".gh-diff-links"),
  55. links = $$(`
  56. #toc ol.content li > a,
  57. .pr-toolbar .toc-select .select-menu-item
  58. `);
  59. // add diff links if they don't already exist
  60. if (links.length !== diffLinks.length) {
  61. // remove old links (just in case)
  62. diffLinks.forEach(el => {
  63. el.parentNode.removeChild(el);
  64. });
  65. // links & file-actions "should" be the same length
  66. last = links.length - 1;
  67. $$(".file-actions").forEach((el, indx) => {
  68. if (indx === 0) {
  69. addButton(el, nextBtn, links[indx + 1]);
  70. addSpace(el, prevBtn);
  71. } else if (indx === last) {
  72. // add dummy "next" button to keep spacing
  73. addSpace(el, nextBtn);
  74. addButton(el, prevBtn, links[indx - 1]);
  75. } else {
  76. addButton(el, nextBtn, links[indx + 1]);
  77. addButton(el, prevBtn, links[indx - 1]);
  78. }
  79. });
  80. }
  81. }
  82.  
  83. function init() {
  84. if ($("#files.diff-view") || $(".pr-toolbar")) {
  85. addLinks();
  86. }
  87. }
  88.  
  89. function $(selector, el) {
  90. return (el || document).querySelector(selector);
  91. }
  92. function $$(selector, el) {
  93. return Array.from((el || document).querySelectorAll(selector));
  94. }
  95.  
  96. document.addEventListener("pjax:end", init);
  97. init();
  98.  
  99. })();