github-file-icon

change github file icon

  1. // ==UserScript==
  2. // @name github-file-icon
  3. // @namespace github-file-icon
  4. // @description change github file icon
  5. // @version 1.0.0
  6. // @author roojay <roojay520@gmail.com>
  7. // @homepage https://github.com/roojay520/github-file-icon
  8. // @license MIT
  9.  
  10. // @include https://github.com/*
  11. // @run-at document-start
  12.  
  13. // @require https://cdn.jsdelivr.net/npm/file-icons-js@1.0.3/index.min.js
  14. // @noframes
  15. // @connect *
  16. // ==/UserScript==
  17.  
  18. (function (FileIcons) {
  19. 'use strict';
  20.  
  21. FileIcons = FileIcons && Object.prototype.hasOwnProperty.call(FileIcons, 'default') ? FileIcons['default'] : FileIcons;
  22.  
  23. const hasLoaded = () => document.readyState === 'interactive' || document.readyState === 'complete';
  24.  
  25. const domLoaded = new Promise(resolve => {
  26. if (hasLoaded()) {
  27. resolve();
  28. } else {
  29. document.addEventListener('DOMContentLoaded', () => {
  30. resolve();
  31. }, {
  32. capture: true,
  33. once: true,
  34. passive: true
  35. });
  36. }
  37. });
  38.  
  39. Object.defineProperty(domLoaded, 'hasLoaded', {
  40. get: () => hasLoaded()
  41. });
  42.  
  43. var domLoaded_1 = domLoaded;
  44.  
  45. // Types inspired by
  46. // https://github.com/Microsoft/TypeScript/blob/9d3707d/src/lib/dom.generated.d.ts#L10581
  47. // Type predicate for TypeScript
  48. function isQueryable(object) {
  49. return typeof object.querySelectorAll === 'function';
  50. }
  51. function select(selectors, baseElement) {
  52. // Shortcut with specified-but-null baseElement
  53. if (arguments.length === 2 && !baseElement) {
  54. return null;
  55. }
  56. return (baseElement !== null && baseElement !== void 0 ? baseElement : document).querySelector(String(selectors));
  57. }
  58. function selectLast(selectors, baseElement) {
  59. // Shortcut with specified-but-null baseElement
  60. if (arguments.length === 2 && !baseElement) {
  61. return null;
  62. }
  63. const all = (baseElement !== null && baseElement !== void 0 ? baseElement : document).querySelectorAll(String(selectors));
  64. return all[all.length - 1];
  65. }
  66. /**
  67. * @param selectors One or more CSS selectors separated by commas
  68. * @param [baseElement] The element to look inside of
  69. * @return Whether it's been found
  70. */
  71. function selectExists(selectors, baseElement) {
  72. if (arguments.length === 2) {
  73. return Boolean(select(selectors, baseElement));
  74. }
  75. return Boolean(select(selectors));
  76. }
  77. function selectAll(selectors, baseElements) {
  78. // Shortcut with specified-but-null baseElements
  79. if (arguments.length === 2 && !baseElements) {
  80. return [];
  81. }
  82. // Can be: select.all('selectors') or select.all('selectors', singleElementOrDocument)
  83. if (!baseElements || isQueryable(baseElements)) {
  84. const elements = (baseElements !== null && baseElements !== void 0 ? baseElements : document).querySelectorAll(String(selectors));
  85. return Array.apply(null, elements);
  86. }
  87. const all = [];
  88. for (let i = 0; i < baseElements.length; i++) {
  89. const current = baseElements[i].querySelectorAll(String(selectors));
  90. for (let ii = 0; ii < current.length; ii++) {
  91. all.push(current[ii]);
  92. }
  93. }
  94. // Preserves IE11 support and performs 3x better than `...all` in Safari
  95. const array = [];
  96. all.forEach(function (v) {
  97. array.push(v);
  98. });
  99. return array;
  100. }
  101. select.last = selectLast;
  102. select.exists = selectExists;
  103. select.all = selectAll;
  104.  
  105. var __async = (__this, __arguments, generator) => {
  106. return new Promise((resolve, reject) => {
  107. var fulfilled = (value) => {
  108. try {
  109. step(generator.next(value));
  110. } catch (e) {
  111. reject(e);
  112. }
  113. };
  114. var rejected = (value) => {
  115. try {
  116. step(generator.throw(value));
  117. } catch (e) {
  118. reject(e);
  119. }
  120. };
  121. var step = (result) => {
  122. return result.done ? resolve(result.value) : Promise.resolve(result.value).then(fulfilled, rejected);
  123. };
  124. step((generator = generator.apply(__this, __arguments)).next());
  125. });
  126. };
  127. const FILE_NAME_SELECTOR = 'div.js-navigation-item > div[role="rowheader"] > span';
  128. const FILE_ICON_SELECTOR = 'div.js-navigation-item > div[role="gridcell"]:first-child';
  129. const FILE_LIST_WRAPPER = ".repository-content .Box.mb-3";
  130. const update = () => {
  131. const filenameDoms = select.all(FILE_NAME_SELECTOR);
  132. const iconDoms = select.all(FILE_ICON_SELECTOR);
  133. for (let i = 0; i < filenameDoms.length; i += 1) {
  134. const filename = filenameDoms[i].innerText.trim();
  135. const iconDom = iconDoms[i].querySelector(".octicon");
  136. const isDirectory = iconDom.classList.contains("octicon-file-directory");
  137. const className = FileIcons.getClassWithColor(filename);
  138. if (className && !isDirectory) {
  139. const icon = document.createElement("span");
  140. icon.className = `icon octicon ${className}`;
  141. iconDom.parentNode.replaceChild(icon, iconDom);
  142. }
  143. }
  144. };
  145. const observer = new MutationObserver(update);
  146. const observeFragment = () => {
  147. const ajaxFiles = select(FILE_LIST_WRAPPER);
  148. if (ajaxFiles) {
  149. observer.observe(ajaxFiles, {
  150. childList: true
  151. });
  152. }
  153. };
  154. const init = () => __async(undefined, [], function* () {
  155. observeFragment();
  156. yield domLoaded_1;
  157. update();
  158. document.addEventListener("pjax:end", update);
  159. document.addEventListener("pjax:end", observeFragment);
  160. });
  161. init();
  162.  
  163. }(FileIcons));