GitHub FileSize Viewer Revised

Show the file size next to it on the website

  1. // ==UserScript==
  2. // @name GitHub FileSize Viewer Revised
  3. // @namespace https://github.com/mo-san/GitHub-FileSize-Viewer
  4. // @version 0.9.12.2
  5. // @description Show the file size next to it on the website
  6. // @author nmaxcom (Original)
  7. // @author Masaki(mo-san)
  8. // @license MIT
  9. // @match https://*.github.com/*
  10. // @grant GM_xmlhttpRequest
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. var TEXT_COLOR = '#6a737d'; // Default github style
  15. // var TEXT_COLOR = '#888'; // my dark github style
  16. const SHOW_BYTES = false; // false: always KB, i.e. '>1 KB'; true: i.e. '180 B' when less than 1 KB
  17. const ABSOLUTE_DATE = true; // shows commit datetime absolutely, otherwise originally relatively.
  18.  
  19. var vars, response;
  20. const css = style => document.head.insertAdjacentHTML("beforeend", `<style>${style}</style>`);
  21. css(`
  22. td.filesize { color: ${TEXT_COLOR}; text-align: right; 'padding-right: 50px !important; }
  23. table.files td.message { max-width: 250px !important; }
  24. `)
  25. XMLHttpRequest.prototype.open = () => {
  26. this.addEventListener('loadend', () => tableCheckandGo());
  27. XMLHttpRequest.prototype.open.apply(this, arguments);
  28. };
  29. tableCheckandGo();
  30.  
  31.  
  32. /*
  33. * Order of business:
  34. * - Detect table, if present, launch async API call and insert new blank cells
  35. * - Detect necessary info to make the async API call
  36. * - When promised is successful, change the blanks for the numbers
  37. * done: detect page change since github does that youtube thing
  38. */
  39. function tableCheckandGo() {
  40. if (!document.querySelector('table.files')) return;
  41.  
  42. /*
  43. * GET must be implemented in the api: /repos/:owner/:repo/contents/:path?ref=branch
  44. * With this regex we capture the title: \w+(.*?)\sat\s(.*?)\s.*?(\w+)\/(\w+)
  45. * where (1) dir path, (2) branch, (3) owner, (4) repo;
  46. * That regex does not work in root
  47. */
  48. var title = document.title;
  49. // Root folder:
  50. var match3 = title.match(/.*?([\w\d.-]+)\/([\w\d.-]+):/i);
  51. // Non root folder, any branch:
  52. // Root folder, we'll extract branch from scrape
  53. var match2 = title.match(/.+?\/([\w\d.\/-]+).*?·\s([\w\d.-]+)\/([\w\d.\/-]+)/i);
  54. if (match3) {
  55. vars = {
  56. dir: "",
  57. owner: match3[1],
  58. repo: match3[2],
  59. branch: document.querySelector('.branch-select-menu button span').innerHTML
  60. };
  61. } else if (match2) {
  62. vars = {
  63. dir: match2[1],
  64. owner: match2[2],
  65. repo: match2[3],
  66. branch: document.querySelector('.branch-select-menu button span').innerHTML
  67. };
  68. };
  69.  
  70. callGitHub().then((resp) => {
  71. insertBlankCells();
  72. fillTheBlanks(JSON.parse(resp.responseText));
  73. })
  74. }
  75.  
  76. /*
  77. * API call
  78. * We're forced to use GM_xmlhttpRequest to avoid Same Origin Policy issues
  79. */
  80. function callGitHub() {
  81. return new Promise((resolve, reject) =>
  82. GM_xmlhttpRequest({
  83. method: "GET",
  84. url: `https://api.github.com/repos/${vars.owner}/${vars.repo}/contents/${vars.dir}?ref=${vars.branch}`,
  85. onload: (res) => resolve(res),
  86. onerror: (res) => reject(res)
  87. })
  88. );
  89. }
  90.  
  91. /*
  92. * - Directories get new cellmate too
  93. *
  94. */
  95. function insertBlankCells() {
  96. const filenameCells = document.querySelectorAll('tr[class~="js-navigation-item"] > td.content');
  97. for (let cell of filenameCells) {
  98. const newtd = document.createElement('td');
  99. newtd.className = 'filesize';
  100. cell.parentNode.insertBefore(newtd, cell.nextSibling);
  101. }
  102. }
  103.  
  104. /*
  105. * If we get the data, we insert it carefully so each filename gets matched
  106. * with the correct filesize.
  107. */
  108. function fillTheBlanks(JSONelements) {
  109. const nametds = document.querySelectorAll('tr[class~="js-navigation-item"] > td.content a');
  110. toploop:
  111. for (let element of JSONelements) {
  112. for (let td of nametds) {
  113. if (element.name !== td.innerHTML) continue;
  114. if (element.type !== 'file') continue;
  115. let sizeNumber = (element.size / 1024).toFixed(0);
  116. if (SHOW_BYTES) {
  117. sizeNumber = sizeNumber < 1 ? element.size + ' B' : sizeNumber + ' KB';
  118. } else {
  119. sizeNumber = sizeNumber < 1 ? '> 1 KB' : sizeNumber + ' KB';
  120. }
  121. td.parentNode.parentNode.nextSibling.innerHTML = sizeNumber;
  122. }
  123. }
  124. }
  125.  
  126.  
  127. /*
  128. * Shows commit time in absolute, instead of relative.
  129. */
  130. if (ABSOLUTE_DATE) {
  131. css(`
  132. table.files td.age {
  133. padding: 0;
  134. }
  135. table.files td.age span.css-truncate {
  136. display: block;
  137. }
  138. td.age time-ago,
  139. relative-time {
  140. font-size: 0;
  141. }
  142. td.age time-ago::before,
  143. relative-time::before {
  144. content: attr(title);
  145. font-size: 14px;
  146. }
  147. `)
  148. }
  149.  
  150. })();