GitHub Sortable Filelist

appends sorting function to github directories

As of 2014-11-10. See the latest version.

  1. // ==UserScript==
  2. // @name GitHub Sortable Filelist
  3. // @namespace trespassersW
  4. // @description appends sorting function to github directories
  5. // @include https://github.com/*
  6. // @version 14.11.10.0
  7. // @created 2014-11-10
  8. // @updated 2014-11-10
  9. // @author trespassersW
  10. // @licence MIT
  11. // @run-at document-end
  12. // @grant GM_none
  13. // ==/UserScript==
  14.  
  15. if(document.querySelector('.file-wrap')){
  16.  
  17. (function(){
  18.  
  19. function stickStyle(css){
  20. var s=document.createElement("style"); s.type="text/css";
  21. s.appendChild(document.createTextNode(css));
  22. return (document.head||document.documentElement).appendChild(s);
  23. }
  24. function insBefore(n,e){
  25. return e.parentNode.insertBefore(n,e);
  26. }
  27. function insAfter(n,e){
  28. if(e.nextElementSibling)
  29. return e.parentNode.insertBefore(n,e.nextElementSibling);
  30. return e.parentNode.appendChild(n);
  31. }
  32. function outerNode(target, node) {
  33. if (target.nodeName==node) return target;
  34. if (target.parentNode)
  35. while (target = target.parentNode) try{
  36. if (target.nodeName==node)
  37. return target;
  38. }catch(e){};
  39. return null;
  40. }
  41. /*.file-wrap table.files td.icon,\
  42. .file-wrap table.files td.content,\
  43. .file-wrap table.files td.message,\
  44. .file-wrap table.files td.age\
  45. */
  46. function css(){
  47. stickStyle('\
  48. .fsort-butt, .tables.file {position: relative; }\
  49. .fsort-butt:before{\
  50. position: absolute; left:1.5em; top: -1em; \
  51. cursor: pointer;\
  52. content: "";\
  53. z-index:99999;\
  54. width: 0; height: 0;\
  55. opacity:.2;\
  56. }\
  57. .fsort-asc:before,\
  58. .fsort-desc.fsort-sel:hover:before\
  59. {\
  60. border-left: 6px solid transparent;\
  61. border-right: 6px solid transparent;\
  62. border-bottom: 14px solid #444;\
  63. border-top: 0;\
  64. }\
  65. .fsort-desc:before,\
  66. .fsort-asc.fsort-sel:hover:before{\
  67. border-left: 6px solid transparent;\
  68. border-right: 6px solid transparent;\
  69. border-bottom: 0;\
  70. border-top: 14px solid #444;\
  71. }\
  72. .fsort-butt.fsort-desc.fsort-sel:hover:before,\
  73. .fsort-butt.fsort-asc.fsort-sel:before{\
  74. border-bottom: 14px solid #4183C4;\
  75. border-top: 0;\
  76. }\
  77. .fsort-butt.fsort-desc.fsort-sel:before,\
  78. .fsort-butt.fsort-asc.fsort-sel:hover:before{\
  79. border-bottom: 0;\
  80. border-top: 14px solid #4183C4;\
  81. }\
  82. \
  83. .fsort-butt.fsort-sel:before{ opacity: .6 }\
  84. .fsort-butt:hover:before{ opacity: 1 !important;}\
  85. /* patches */\
  86. table.files td.age {text-align: left !important;}\
  87. table.files td.message {overflow-x: visible}\
  88. ');//#80A6CD
  89. }
  90. var ii=0;
  91. var d0=[0,0,1];
  92. var C=[{c:1, d: 0, s: 0},{c:2, d: 0, s: 0},{c:3, d: 1, s: 0}];
  93. var ASC;
  94. var oa=[],ca=[];
  95. var D=document, TB;
  96.  
  97. function setC(n){
  98. for(var i=0,il=C.length; i<il; i++ ){
  99. if(i!=n) C[i].s= 0, C[i].d=d0[i];
  100. else C[i].s=1;
  101. oa[i].className='fsort-butt fsort-'+(C[i].d?'desc':'asc')+(C[i].s?' fsort-sel':'') ;
  102. }
  103. }
  104.  
  105. function isDir(x){
  106. return (TB.rows[x].cells[0].querySelector("span.octicon-file-directory")) != null;
  107. }
  108.  
  109. var sort_fn =[
  110. function(a,b){
  111. var x=isDir(a), y=isDir(b);
  112. if(x!=y) return (x<y)*2-1;
  113. x=TB.rows[a].cells[1].querySelector('span.css-truncate-target a').textContent;
  114. y=TB.rows[b].cells[1].querySelector('span.css-truncate-target a').textContent;
  115. return x==y? 0: ((x>y)^ASC)*2-1;
  116. return 0;
  117. }
  118. ,
  119. function(a,b){
  120. var x=isDir(a), y=isDir(b);
  121. if(x!=y) return (x<y)*2-1;
  122. x=TB.rows[a].cells[2].querySelector('span.css-truncate').textContent;
  123. y=TB.rows[b].cells[2].querySelector('span.css-truncate').textContent;
  124. return x==y? 0: ((x>y)^ASC)*2-1;
  125. }
  126. ,
  127. function(a,b){
  128. var x=isDir(a), y=isDir(b);
  129. if(x!=y) return (x<y)*2-1;
  130. x=TB.rows[a].cells[3].querySelector('span.css-truncate>time').getAttribute('datetime');
  131. y=TB.rows[b].cells[3].querySelector('span.css-truncate>time').getAttribute('datetime');
  132. return x==y? 0: ((x>y)^ASC)*2-1;
  133. }
  134. ]
  135. //
  136. function doSort(t){
  137. TB=outerNode(t,'TBODY');
  138. var tb=[],ix=[], i, tl;
  139. if(!TB) throw' *GHSFL* TBODY not found';
  140. var n=t.fsort_N;
  141. tl=TB.rows.length;
  142. ASC=C[n];
  143. ASC=C[n].d^=C[n].s;
  144. for( i=0; i<tl; i++)
  145. ix.push(i);
  146. if(oa[0])
  147. oa[0].parentNode.removeChild(oa[0]),
  148. oa[1].parentNode.removeChild(oa[1]),
  149. oa[2].parentNode.removeChild(oa[2]);
  150. ix.sort(sort_fn[n]);
  151. for( i=0; i<tl; i++)
  152. tb.push(TB.rows[ix[i]].innerHTML);
  153. for( i=0; i<tl; i++)
  154. TB.rows[i].innerHTML=tb[i];
  155. setC(n);
  156. gitDir1(0);
  157. }
  158.  
  159. function onClik(e){doSort(e.target)}
  160.  
  161. function gitDir1(x){
  162. if(x && document.querySelector('.fsort-butt')) {return;}
  163. var c,o;
  164. ca=[];
  165. c= D.querySelector('.file-wrap table.files td.content >span');
  166. if(!c) throw '*GHSFL* no content';
  167. ca.push(c);
  168. c=D.querySelector('.file-wrap table.files td.message >span');
  169. if(!c) throw '*GHSFL* no messages';
  170. ca.push(c);
  171. c=D.querySelector('.file-wrap table.files td.age >span');
  172. if(!c) throw '*GHSFL* no ages';
  173. ca.push(c);
  174. if(x){ oa=[];
  175. o=D.createElement('span');
  176. o.textContent=''; o.fsort_N=0;
  177. o.addEventListener('mousedown',onClik,false);
  178. oa.push(o);
  179. o=o.cloneNode(true); o.fsort_N=1;
  180. o.addEventListener('mousedown',onClik,false)
  181. oa.push(o);
  182. o=o.cloneNode(true); o.fsort_N=2;
  183. o.addEventListener('mousedown',onClik,false)
  184. oa.push(o);
  185. setC(-1);
  186. }
  187. insBefore(oa[0],ca[0]);
  188. insBefore(oa[1],ca[1]);
  189. insBefore(oa[2],ca[2]);
  190. }
  191.  
  192. function gitDir(x){gitDir1(1)}
  193.  
  194. css();
  195. gitDir();
  196.  
  197. var target = document.body; //D.querSelector('.file-wrap');
  198. var MO = window.MutationObserver;
  199. if(!MO) MO= window.WebKitMutationObserver;
  200. if(!MO) return;
  201. var observer = new MO(function(mutations) {
  202. mutations.forEach(function(m) {
  203. if( m.type= "attributes" &&
  204. m.target.nodeName == 'DIV' &&
  205. m.target.className == "file-wrap" )
  206. gitDir(m.target);
  207. });
  208. });
  209. observer.observe(D.body, { attributes: true, subtree: true } );
  210. /* attributes: true , childList: true, subtree: true,
  211. characterData: true, attributeOldValue:true, characterDataOldValue:true
  212. */
  213.  
  214. })()};
  215.  
  216. /*
  217. to do: persistent settings; sorting by file extensions; toggling date/time display mode
  218. ... do we really need it?
  219. */