GitHub - Make PRs easier to diff

Add some functionality to github

As of 2017-04-19. See the latest version.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

  1. // ==UserScript==
  2. // @name GitHub - Make PRs easier to diff
  3. // @namespace https://github.com/drKnoxy/
  4. // @version 1.3
  5. // @description Add some functionality to github
  6. // @author DrKnoxy
  7. // @include https://github.com/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function(){
  12. // Toggle headers on click
  13. monitorHeader();
  14.  
  15. // Add whitespace toggle
  16. whitespaceToggles();
  17.  
  18. ////////////////
  19.  
  20. function whitespaceToggles() {
  21. _addToggle();
  22. document.addEventListener('pjax:success', _addToggle);
  23.  
  24. ///
  25.  
  26. function _addToggle() {
  27. const search = _getSearchObj();
  28. const isHidingWhitespace = _isHidingWhitespace(search);
  29.  
  30. // we want the url for the otherway
  31. const newSearch = Object.assign({}, search, {w: _getReverse(search.w)});
  32. const url = _getURL(newSearch);
  33.  
  34. const btn = `
  35.  
  36. `;
  37. const tmpl = `
  38. <div class="diffbar-item">
  39. <a
  40. class="btn btn-sm btn-outline"
  41. href="${url}"
  42. title="Toggle whitespace visibility">
  43. ${isHidingWhitespace ? 'Show' : 'Hide'} whitespace
  44. </a>
  45. </div>
  46. `;
  47.  
  48. // Sometimes we have a PR
  49. const toolbar = document.querySelector('.pr-review-tools');
  50. if (toolbar) {
  51. toolbar.insertAdjacentHTML('afterbegin', tmpl);
  52. }
  53.  
  54. // Sometimes we have a commit
  55. const diffbar = document.querySelector('.js-details-container');
  56. if (diffbar) {
  57. //todo: more here
  58. // diffbar.insertAdjacentHTML('afterbegin', tmpl);
  59. }
  60. }
  61. }
  62.  
  63. function _getReverse(w) {
  64. return (w === 0 || typeof w === 'undefined') ? 1 : 0;
  65. }
  66.  
  67. function _isHidingWhitespace(search) {
  68. return search.w === 1;
  69. }
  70.  
  71. function _getURL(search) {
  72. const newQuery = Object.keys(search).map(k => `${k}=${search[k]}`).join('&');
  73. return `${location.pathname}?${newQuery}`;
  74. }
  75.  
  76. function _getSearchObj() {
  77. if (location.search === '') return {};
  78.  
  79. return location.search
  80. .replace(/^\?/, '')
  81. .split('&')
  82. .reduce((query, p) => {
  83. const [key, val, ...whatev] = p.split('=');
  84. if (key == 'w')
  85. query[key] = parseInt(val, 10);
  86. else
  87. query[key] = val;
  88.  
  89. return query;
  90. }, {});
  91. }
  92.  
  93. function monitorHeader() {
  94. // Attach this event listener up high because
  95. // github uses some fancy pjax to swap content
  96. document.addEventListener('click', _monitor);
  97.  
  98. ///
  99.  
  100. function _monitor(e){
  101. const el = e.target.closest('.file-header');
  102.  
  103. // is this a file header
  104. if (!el) return;
  105.  
  106. // is it next to a blobl-wrapper
  107. const next = el.nextElementSibling;
  108. if (!next.classList.contains('js-file-content')) return;
  109.  
  110. // through the gauntlet
  111. toggleVis(next);
  112. }
  113.  
  114. function toggleVis(el) {
  115. if(el.style.display === '') {
  116. el.style.display = 'none';
  117. } else {
  118. el.style.display = '';
  119. }
  120. }
  121. }
  122.  
  123. })();