GitHub Sortable Filelist

appends sorting function to github directories

目前为 2014-11-12 提交的版本。查看 最新版本

  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.13.7
  7. // .7 date/time display mode switching
  8. // .4 now works on all github pages
  9. // @created 2014-11-10
  10. // @updated 2014-11-13
  11. // @author trespassersW
  12. // @licence MIT
  13. // @run-at document-end
  14. // @grant GM_none
  15. // ==/UserScript==
  16.  
  17. if(document.body || document.querySelector('#js-repo-pjax-container')){ // .file-wrap
  18.  
  19. var llii=0; function _l(m){/* * /
  20. console.log(++llii +': '+m)
  21. /* */
  22. }
  23.  
  24. (function(){ "use strict";
  25.  
  26. var ii=0,tt;
  27. var d0=[0,0,1];
  28. var C=[{c:1, d: 0, s: 0},{c:2, d: 0, s: 0},{c:3, d: 1, s: 0}];
  29. var ASC;
  30. var oa=[],ca=[],clock,dtStyle;
  31. var D=document, TB;
  32. var catcher,locStor;
  33. var prefs={dtStyle:0};
  34.  
  35. function stickStyle(css){
  36. var s=document.createElement("style"); s.type="text/css";
  37. s.appendChild(document.createTextNode(css));
  38. return (document.head||document.documentElement).appendChild(s);
  39. }
  40. function insBefore(n,e){
  41. return e.parentNode.insertBefore(n,e);
  42. }
  43. function insAfter(n,e){
  44. if(e.nextElementSibling)
  45. return e.parentNode.insertBefore(n,e.nextElementSibling);
  46. return e.parentNode.appendChild(n);
  47. }
  48. function outerNode(target, node) {
  49. if (target.nodeName==node) return target;
  50. if (target.parentNode)
  51. while (target = target.parentNode) try{
  52. if (target.nodeName==node)
  53. return target;
  54. }catch(e){};
  55. return null;
  56. }
  57.  
  58. function css(){
  59. stickStyle('\
  60. .fsort-butt, .tables.file {position: relative; }\
  61. .fsort-butt:before{\
  62. position: absolute; left:1.5em; top: -1em; \
  63. cursor: pointer;\
  64. content: "";\
  65. z-index:99999;\
  66. width: 0; height: 0;\
  67. opacity:.2;\
  68. }\
  69. .fsort-asc:before,\
  70. .fsort-desc.fsort-sel:hover:before\
  71. {\
  72. border-left: 6px solid transparent;\
  73. border-right: 6px solid transparent;\
  74. border-bottom: 14px solid #444;\
  75. border-top: 0;\
  76. }\
  77. .fsort-desc:before,\
  78. .fsort-asc.fsort-sel:hover:before{\
  79. border-left: 6px solid transparent;\
  80. border-right: 6px solid transparent;\
  81. border-bottom: 0;\
  82. border-top: 14px solid #444;\
  83. }\
  84. .fsort-butt.fsort-desc.fsort-sel:hover:before,\
  85. .fsort-butt.fsort-asc.fsort-sel:before{\
  86. border-bottom: 14px solid #4183C4;\
  87. border-top: 0;\
  88. }\
  89. .fsort-butt.fsort-desc.fsort-sel:before,\
  90. .fsort-butt.fsort-asc.fsort-sel:hover:before{\
  91. border-bottom: 0;\
  92. border-top: 14px solid #4183C4;\
  93. }\
  94. \
  95. .fsort-butt.fsort-sel:before{ opacity: .6 }\
  96. .fsort-butt:hover:before{ opacity: 1 !important;}\
  97. \
  98. .fsort-butt.fsort-clock:before{\
  99. position:absolute; left:3em; top:-1.2em;\
  100. opacity:1!important;\
  101. content: url(data:image/png;base64,\
  102. iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlZJREFUeNqkU0tLG1EUvjORFJPMw6mhTUgxy75EUxiJj6CGLKQV0o2IQd3pxp2bbPs/3MVCIHSRgC3uXBhCSErjtLRkK7TcNCYhk4eGPHvO6AwjXXrgu/fOOff77plzz2VGoxF5iI3hEA6HCcuyxGKxEIZhOMBLcHsAwt0+FfAbDvsFaA4GAzIcDkkqlboVMNlzB8+vzgUCq3N+v+xyOl3opFdXNJfN5nPn52dNVT0DV/FeBjpZcjrfR/b3Dzwul0e02ciPiwsyPTtLptxur7C25n0xMzMfPzoSypQmdREWB0iLt3NcKLK3dzAhSR5+fFxT/JbPG+row9g2HABZhpBjCMA/vZYDgRVOFD02q5WwDKOROp2OIYA+jDlgz8Ly8gpyDIF+v++d9vlkXDdubshlpaKhWCwaawTGsHg+WZaRY9QAPiYEUXyinzY1OanNzWbTWGv7gPynViO4FzlmARavcWgivw2FSDAYJDubm4TjOA0Oh4O8WVwk80tLGscsoLZUtWwThGf6afFEgmxvbZGP8biRAbac2u2S60ajjByjBr1e7/JrJqOMQSNhmmiCJGlknHVAAckjuI1cOq0gx3wLyudkMt1ttejgTkAXMRsWkO106KdEIo0cQwAC9b+l0umHaPRYrVaphWX/63n0XasqjR4eHlNKT5FzrxPBoXxXFEtkY6Ozs7u78G59/dVTt/sxdkSJ0uqXk5OfsVgsU6lUUjzPKzqPwdfo8/lIF4pTr9dJu92WID0/tjZAv8MKti48tqzdbq+JUAsrNFWhULgVeIj9E2AAamUckFr2UCoAAAAASUVORK5CYII=\
  103. );\
  104. }\
  105. table.files td.age .css-truncate.css-truncate-target{\
  106. width: 99% !important; \
  107. max-width: none !important;\
  108. }\
  109. table.files td.age span.css-truncate time{\
  110. position: relative !important;\
  111. }\
  112. \
  113. /* patches */\
  114. table.files td.age {text-align: left !important; padding-: 0 4px !important;\
  115. }\
  116. table.files td.message {overflow: visible !important;}\
  117. ');//#80A6CD
  118.  
  119. dtStyle=stickStyle('\
  120. table.files td.age span.css-truncate time{\
  121. visibility: hidden !important;\
  122. }\
  123. table.files td.age span.css-truncate > time:before{\
  124. content: attr(time-or-date);\
  125. visibility: visible !important;\
  126. position: absolute !important;\
  127. }\
  128. ')
  129. }
  130.  
  131. function setC(n){
  132. for(var i=0,il=C.length; i<il; i++ ){
  133. if(i!=n) C[i].s= 0, C[i].d=d0[i];
  134. else C[i].s=1;
  135. oa[i].className='fsort-butt fsort-'+(C[i].d?'desc':'asc')+(C[i].s?' fsort-sel':'') ;
  136. oa[i].title=C[i].d? '\u21ca' : '\u21c8';
  137. }
  138. }
  139.  
  140. function setDateTime(){
  141. try{ //014-10-02T16:09:05Z
  142. var DT=D.querySelectorAll('td.age span.css-truncate time'),res;
  143. for(var dt, dl=DT.length, i=0; i<dl; i++){
  144. dt = DT[i].getAttribute('datetime').match(/([0-9\-]+).([0-9\:]+)./);
  145. if(/minut|hour|just/.test(DT[i].textContent))
  146. DT[i].setAttribute("time-or-date",dt[2]);
  147. else
  148. DT[i].setAttribute("time-or-date",dt[1]);
  149. }
  150. }catch(e){(_l(e+'\n*GHSFL* wrong datetime'))}
  151. }
  152.  
  153. function isDir(x){
  154. return (TB.rows[x].cells[0].querySelector("span.octicon-file-directory")) != null;
  155. }
  156.  
  157. var sDir,sCells;
  158. var fa=[
  159. function(a){
  160. sCells.push(TB.rows[a].cells[1].querySelector('span.css-truncate-target a').textContent);
  161. },
  162. function(a){
  163. sCells.push(TB.rows[a].cells[2].querySelector('span.css-truncate').textContent);
  164. },
  165. function(a){
  166. sCells.push(TB.rows[a].cells[3].querySelector('span.css-truncate>time').getAttribute('datetime'));
  167. }
  168. ]
  169. function sort_p(n){// prepare data for sorting
  170. sDir=[],sCells=[];
  171. for(var tl=TB.rows.length, a=0; a<tl; a++){
  172. sDir.push(isDir(a));
  173. fa[n](a);
  174. }
  175. }
  176.  
  177. function sort_fn(a,b){
  178. var x=sDir[a], y=sDir[b];
  179. if(x!=y) return ((x<y)<<1)-1;
  180. x= sCells[a], y= sCells[b];
  181. return x==y? 0: (((x>y)^ASC)<<1)-1;
  182. }
  183.  
  184. var CNn={content: 0, message: 1, age: 2}
  185.  
  186. function oClr(){
  187. var o= catcher.querySelectorAll('.fsort-butt')
  188. for(var ol=o.length,i=0;i<ol;i++)
  189. o[i].parentNode.removeChild(o[i]);
  190. }
  191. //
  192. function doSort(t){
  193. if(t.className.indexOf('fsort-clock')>-1){
  194. dtStyle.disabled = (1===(prefs.dtStyle ^= 1));
  195. if(locStor) locStor.setItem('GHSFL',JSON.stringify(prefs))
  196. return;
  197. }
  198. TB=outerNode(t,'TBODY');
  199. var tb=[],ix=[], i, tl;
  200. if(!TB){ _l( "*GHSFL* TBODY not found"); return; }
  201. var n=CNn[t.parentNode.className];
  202. if(typeof n=="undefined"){ _l( "*GHSFL* undefined col"); return; }
  203. _l('n:'+n);
  204. tl=TB.rows.length;
  205. ASC=C[n].d^=C[n].s;
  206. for( i=0; i<tl; i++)
  207. ix.push(i);
  208. oClr();
  209. sort_p(n);
  210. ix.sort(sort_fn);
  211. for( i=0; i<tl; i++)
  212. tb.push(TB.rows[ix[i]].innerHTML);
  213. for( i=0; i<tl; i++)
  214. TB.rows[i].innerHTML=tb[i];
  215. setC(n);
  216. gitDir1(0);
  217. }
  218.  
  219. function onClik(e){doSort(e.target)}
  220.  
  221. function gitDir1(x){
  222. if(x && document.querySelector('.fsort-butt')) {
  223. _l('gitDir'+x+' - already'); return;
  224. }
  225. _l('gitDir'+x)
  226. var c,o;
  227. ca=[];
  228. c= D.querySelector('.file-wrap table.files td.content >span');
  229. if(!c){ _l( '*GHSFL* no content') ; return; }
  230. ca.push(c);
  231. c=D.querySelector('.file-wrap table.files td.message >span');
  232. if(!c){ _l( '*GHSFL* no messages'); return; }
  233. ca.push(c);
  234. c=D.querySelector('.file-wrap table.files td.age >span');
  235. if(!c){_l( '*GHSFL* no ages'); return; }
  236. ca.push(c);
  237. if(x){ oClr(); oa=[];
  238. o=D.createElement('span');
  239. o.textContent='';
  240. oa.push(o);
  241. o=o.cloneNode(true);
  242. oa.push(o);
  243. o=o.cloneNode(true);
  244. oa.push(o);
  245. clock=D.createElement('span');
  246. clock.className='fsort-butt fsort-clock';
  247. setDateTime();
  248. setC(-1);
  249. }
  250. insBefore(oa[0],ca[0]);
  251. insBefore(oa[1],ca[1]);
  252. insBefore(oa[2],ca[2]);
  253. insBefore(clock,ca[2]);
  254. }
  255.  
  256. function gitDir(){
  257. gitDir1(1);
  258. }
  259.  
  260.  
  261. catcher= D.querySelector('#js-repo-pjax-container');
  262. if(!catcher){ _l( "*GHSFL* err0r"); return; }
  263.  
  264. catcher.addEventListener('mousedown',function(e){
  265. if(e.target.nodeName && e.target.nodeName=='SPAN' &&
  266. e.target.className.indexOf('fsort-butt')>-1)
  267. { onClik(e); }
  268. }
  269. ,false);
  270. _l('startup()');
  271.  
  272. try {
  273. locStor = window.localStorage;
  274. tt=locStor.getItem("GHSFL");
  275. } catch(e){ locStor =null}
  276.  
  277. if(locStor && tt) try{
  278. prefs =JSON.parse(tt);
  279. }catch(e){ console.log(e+"\n*GHSFL* bad prefs") }
  280.  
  281. css();
  282. dtStyle.disabled=(prefs.dtStyle===1);
  283.  
  284. gitDir();
  285.  
  286. var target = catcher; //document.body; //D.querSelector('.file-wrap');
  287. var MO = window.MutationObserver;
  288. if(!MO) MO= window.WebKitMutationObserver;
  289. if(!MO) return;
  290. var observer = new MO(function(mutations) {
  291. mutations.forEach(function(m) {
  292. if( m.type= "attributes" &&
  293. m.target.nodeName == 'DIV' &&
  294. m.target.className == "file-wrap" )
  295. gitDir();
  296. return;
  297. });
  298. });
  299.  
  300. //if(m.target.nodeName!='TIME')_l('mo'+' '+m.type +'.'+m.target.nodeName+m.target.className));
  301.  
  302. observer.observe(D.body, { attributes: true, subtree: true } );
  303. /* attributes: true , childList: true, subtree: true,
  304. characterData: true, attributeOldValue:true, characterDataOldValue:true
  305. */
  306.  
  307. })()};
  308.  
  309. /*
  310. to do: persistent settings; sorting by file extensions; toggling date/time display mode
  311. ... do we really need it?
  312. */