GitHub Sortable Filelist

appends sorting function to github directories

当前为 2014-11-19 提交的版本,查看 最新版本

  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.19.13
  7. // 14.11.19.13 fixes for latest github changes
  8. // .12 new age format; fix for chrome
  9. // .11 patch for the very first page;
  10. // .10 datetime auto-updating fix; right-aligned datetime column; proper local time; .ext sorting fix;
  11. // .8 sorting by file extention
  12. // .7 date/time display mode switching
  13. // .4 now works on all github pages
  14. // @created 2014-11-10
  15. // @updated 2014-11-18
  16. // @author trespassersW
  17. // @license MIT
  18. // @icon https://i.imgur.com/8buFLcs.png
  19. // (C) Icon: Aaron Nichols CC Attribution 3.0 Unported
  20. // @run-at document-end
  21. // @grant GM_none
  22. // ==/UserScript==
  23.  
  24. if(document.body && document.querySelector('#js-repo-pjax-container')){
  25.  
  26. var llii=0, _l= function(){/* * /
  27. for (var s=++llii +':', li=arguments.length, i = 0; i<li; i++)
  28. s+=' ' + arguments[i];
  29. console.log(s)
  30. /* */
  31. }
  32. var fakejs = // avoid compiler warning
  33. (function(){ "use strict";
  34.  
  35. var ii=0,tt;
  36. var d0=[0,0,1];
  37. var C=[{c:1, d: 0, s: 0},{c:2, d: 0, s: 0},{c:3, d: 1, s: 0}];
  38. var ASC;
  39. var oa=[],ca=[],clock,ext,dtStyle;
  40. var D=document, TB;
  41. var catcher,locStor;
  42. var prefs={dtStyle:0, ext: 0};
  43.  
  44. function stickStyle(css){
  45. var s=document.createElement("style"); s.type="text/css";
  46. s.appendChild(document.createTextNode(css));
  47. return (document.head||document.documentElement).appendChild(s);
  48. }
  49. function insBefore(n,e){
  50. return e.parentNode.insertBefore(n,e);
  51. }
  52. function insAfter(n,e){
  53. if(e.nextElementSibling)
  54. return e.parentNode.insertBefore(n,e.nextElementSibling);
  55. return e.parentNode.appendChild(n);
  56. }
  57. function outerNode(target, node) {
  58. if (target.nodeName==node) return target;
  59. if (target.parentNode)
  60. while (target = target.parentNode) try{
  61. if (target.nodeName==node)
  62. return target;
  63. }catch(e){};
  64. return null;
  65. }
  66. function savePrefs(){
  67. if(locStor) locStor.setItem('GHSFL',JSON.stringify(prefs));
  68. }
  69.  
  70. function css(){
  71. stickStyle('\
  72. .fsort-butt,\n\
  73. .tables.file td.content, .tables.file td.message, .tables.file td.age\n\
  74. {position: relative; }\n\
  75. .fsort-butt:before{\n\
  76. position: absolute; display: inline-block;\n\
  77. cursor: pointer;\n\
  78. content: "";\n\
  79. width: 16px; height: 16px;\n\
  80. }\n\
  81. .fsort-butt.fsort-asc:before,.fsort-butt.fsort-desc:before{\n\
  82. opacity:.2;\n\
  83. left:1.5em; top: -1em;\n\
  84. width: 0; height: 0;\n\
  85. }\n\
  86. td.age.fsort-butt.fsort-asc:before,td.age.fsort-butt.fsort-desc:before{\n\
  87. right:3em;\n\
  88. }\n\
  89. .fsort-asc:before,.fsort-desc:before{\n\
  90. border-style: solid;\n\
  91. border-color: #654;\n\
  92. }\n\
  93. .fsort-asc:before,\n\
  94. .fsort-desc.fsort-sel:hover:before\n\
  95. {\n\
  96. border-left: 6px solid transparent;\n\
  97. border-right: 6px solid transparent;\n\
  98. border-bottom-width: 14px;\n\
  99. border-top-width: 0;\n\
  100. }\n\
  101. .fsort-desc:before,\n\
  102. .fsort-asc.fsort-sel:hover:before{\n\
  103. border-left: 6px solid transparent;\n\
  104. border-right: 6px solid transparent;\n\
  105. border-bottom-width: 0;\n\
  106. border-top-width: 14px;\n\
  107. }\n\
  108. \n\
  109. .fsort-sel:before,\n\
  110. .fsort-sel:before{\n\
  111. border-bottom-color: #4183C4 !important;\n\
  112. border-top-color: #4183C4 !important;\n\
  113. }\n\
  114. \n\
  115. .fsort-butt.fsort-sel:before{ opacity: .6 }\n\
  116. .fsort-butt:hover:before{ opacity: 1 !important;}\n\
  117. \n\
  118. #fsort-clock:before{\n\
  119. left:4.5em; top:-1.2em;\n\
  120. border-radius: 16px;\n\
  121. opacity:1;\n\
  122. content: url(data:image/png;base64,\
  123. 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=\n\
  124. );}\n\
  125. #fsort-clock:before{ background-color: #CCC }\n\
  126. #fsort-clock.fsort-on:before{ background-color: #4183C4 }\n\
  127. \n\
  128. td.age .fsort-butt.fsort-asc:before,td.age .fsort-butt.fsort-desc:before{\
  129. left:3em !important;\
  130. }\
  131. #fsort-ext:before{\n\
  132. left:4em; top:-14px;\n\
  133. border-radius: 6px;\n\
  134. width:28px;height:16px;\n\
  135. opacity:.3;\n\
  136. content:url(data:image/png;base64,\
  137. iVBORw0KGgoAAAANSUhEUgAAABoAAAANCAYAAAC3mX7tAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAABWUlEQVQ4y+2SP0sDQRDFT7BQTGtnITYSK8Emha1NCrXwC4gWYiUKiiB4jYKdWAmC4AewtxEExSqVjZ1/OEgwrEQwkLvbmd/aTCBI0IBYCD7YYmaYfW/mTRT9VWRZNq6qm79OBBwBrueGPM8ngWvgHbgXkQX7aC+EEFR1x+IDi7dUdQOQYACuviSp1+tDQA2oquoycAlInucT1Wp1EHgA3r3300AG3FUqlX4RKQOvQFNVV0Rk9ksiEZkzRbtJkgx476faqq1etnoK4L0vdazuqefVqepq6ALgKIqiKI7jPuDRcpVPHn1P5JwrxHHc1zHRofe+1H5pmo6akCWrv4QQgojMdxA9AI3vzPfAsXOuACRAXVW3VXUNOM2yrNhsNofNh1qapmPAG5A45wpGdGMiToD9bvdftKZ9I54ALoCGXd5tq9UaAc7Mr0Wbbr09vfk3Azybf+fRP36KDxJ2sN2uMATcAAAAAElFTkSuQmCC);\n\
  138. }\n\
  139. #fsort-ext:before{ background-color: #CCC}\n\
  140. .fsort-sel ~ #fsort-ext:before {opacity:.6;}\n\
  141. .fsort-on:before{ background-color: #4183C4 !important;}\n\
  142. .fsort-on:hover:before{ opacity:1}\n\
  143. \n\
  144. table.files td.age .css-truncate.css-truncate-target{\n\
  145. width: 99% !important; \n\
  146. max-width: none !important;\n\
  147. }\n\
  148. /*table.files td.age span.css-truncate time{\n\
  149. position: relative !important;\n\
  150. }*/\n\
  151. .fsort-time {\n\
  152. visibility: hidden;\n\
  153. display: none;\n\
  154. padding-right: 14px;\n\
  155. }\n\
  156. .fsort-time i {\
  157. display:inline-block;\
  158. color: #BBB;\
  159. font-style: normal !important;;\
  160. transform: scale(0.9);\
  161. /* font-size: 12px;*/\
  162. }\
  163. \n\
  164. /* patches */\n\
  165. table.files td.age {text-align: right !important; padding-right: 10px !important;\n\
  166. width:12em!important;\n\
  167. min-width:12em!important;\n\
  168. max-width:none!important;\n\
  169. overflow:visible!important;\n\
  170. }\n\
  171. table.files td.message {overflow: visible !important;}\n\
  172. /*.file-wrap .include-fragment-error { display: table-row !important;}*/\
  173. ');
  174.  
  175. dtStyle=stickStyle('\
  176. td.age span.css-truncate time{\
  177. visibility: hidden !important;\
  178. display: none !important;\
  179. }\
  180. td.age span.css-truncate .fsort-time {\
  181. visibility: visible !important;\
  182. display: inline !important;\
  183. }\
  184. ')
  185. }
  186.  
  187. function setC(n){
  188. for(var i=0,il=C.length; i<il; i++ ){
  189. if(i!=n) C[i].s= 0, C[i].d=d0[i];
  190. else C[i].s=1;
  191. oa[i].className='fsort-butt fsort-'+(C[i].d?'desc':'asc')+(C[i].s?' fsort-sel':'') ;
  192. oa[i].title=C[i].d? '\u21ca' : '\u21c8';
  193. }
  194. }
  195.  
  196. function dd(s)
  197. { s=s.toString(); if(s.length<2)return'0'+s; return s}
  198. function d2s(n){
  199. var hs=dd(n.getHours())+':'+dd(n.getMinutes());
  200. return {
  201. d: n.getFullYear()+'-'+dd(n.getMonth()+1)+'-'+dd(n.getDate())+' <i>'+ hs+'</i>',
  202. t: hs+':'+dd(n.getSeconds())
  203. }
  204. }
  205.  
  206. function setDateTime(x){
  207. var dt,dtm,dta,dtd,tc,m,now,t;
  208. var DT=D.querySelectorAll('td.age span.css-truncate time');
  209. _l('sDT',x?'refresh':'create');
  210. try{
  211. now = new Date();
  212. for(var dl=DT.length, i=0; i<dl; i++){
  213. dta=DT[i].getAttribute('datetime');
  214. dtd=new Date(dta);
  215. dt= d2s(dtd); // 2014-07-24T17:06:11Z
  216. dtm=null;
  217. if(x){
  218. dtm=DT[i].parentNode.querySelector('.fsort-time');
  219. }
  220. if(!dtm){
  221. dtm=D.createElement('span');
  222. dtm.className='fsort-time';
  223. x=0;
  224. }
  225. if(!x || !dtm.title || dtm.title != DT[i].title)
  226. { dtm.title= DT[i].title;
  227. t= dt.d;
  228. if( (now.getTime() - dtd.getTime() < 12*3600*1000) ||
  229. ((now.getTime() - dtd.getTime() < 24*3600*1000) &&
  230. (now.getDate() == dtd.getDate()) )
  231. ) t=dt.t;
  232. dtm.innerHTML=t;
  233. }
  234. if(!x) insAfter(dtm,DT[i]);
  235. }
  236. }catch(e){(console.log(e+'\n*GHSFL* wrong datetime'+x))}
  237. }
  238.  
  239. function isDir(x){
  240. var c= TB.rows[x].cells[0].querySelector("span");
  241. if(c.className.indexOf("-directory")>0) return 0;
  242. if(c.className.indexOf("-file")>0) return -1;
  243. return 1;
  244. }
  245. function getCell(r,c,s,p){
  246. var rc=TB.rows[r].cells[c],q=null;
  247. if(typeof rc == "undefined") {
  248. _l('r:',r,'c:',c,'- ???' );
  249. }else
  250. q=rc.querySelector(s);
  251. if(q) q= p? q.getAttribute(p): q.textContent;
  252. if(q) return q;
  253. return "";
  254. }
  255. var sDir,sCells,sExts;
  256. var fa=[
  257. function(a){
  258. return getCell(a,1,'span.css-truncate-target a');
  259. },
  260. function(a){
  261. return getCell(a,2,'span.css-truncate');
  262. },
  263. function(a){
  264. var c = getCell(a,3,'span.css-truncate>time','datetime');
  265. if(c) return c;
  266. return "2099-12-31T23:59:59Z"
  267. }
  268. ]
  269.  
  270. var b9='\x20\x20\x20'; b9+=b9+b9;
  271. function pad9(s){
  272. if(s.length<9) return (s+b9).substr(0,9);
  273. return s;
  274. }
  275. function sort_p(n){// prepare data for sorting
  276. sDir=[],sCells=[];
  277. for(var tl=TB.rows.length, a=0; a<tl; a++)
  278. sDir.push(isDir(a));
  279. if( n === 0 && prefs.ext ){
  280. for( a=0; a<tl; a++){ // f.x -> x.f
  281. var x=fa[n](a),
  282. m= x.match(/(.*)(\..*)$/);
  283. if(!m || !m[2]) m=['',x,''];
  284. x=pad9(m[2])+' '+m[1];
  285. sCells.push(x);
  286. }
  287. }else for( a=0; a<tl; a++) sCells.push(fa[n](a));
  288. }
  289.  
  290. function sort_fn(a,b){
  291. var x=sDir[a], y=sDir[b];
  292. if(x!=y) return ((x<y)? 1: -1);
  293. x= sCells[a], y= sCells[b];
  294. return x==y? 0: (((x>y)^ASC)<<1)-1;
  295. }
  296.  
  297. var CNn={content: 0, message: 1, age: 2}
  298.  
  299. function oClr(){
  300. var o= catcher.querySelectorAll('.fsort-butt,.fsort-time')
  301. for(var ol=o.length,i=0;i<ol;i++)
  302. o[i].parentNode.removeChild(o[i]);
  303. }
  304. //
  305. function extclassName(){
  306. ext.className='fsort-butt'+ (prefs.ext? ' fsort-on': '' );
  307. }
  308. function clockclassName(){
  309. clock.className='fsort-butt'+ (prefs.dtStyle? '': ' fsort-on');
  310. }
  311. //
  312. function doSort(t){
  313. TB=outerNode(t,'TBODY');
  314. if(!TB){ _l( "*GHSFL* TBODY not found"); return; }
  315. var n=CNn[t.parentNode.className];
  316. if(typeof n=="undefined"){ _l( "*GHSFL* undefined col"); return; }
  317. if(t.id=='fsort-clock'){
  318. dtStyle.disabled = (prefs.dtStyle ^= 1);
  319. savePrefs();
  320. clockclassName();
  321. return;
  322. }
  323. var xt = (t.id=='fsort-ext');
  324. if( xt ){
  325. if(C[n].s) prefs.ext ^= 1;
  326. else prefs.ext= 1;
  327. savePrefs();
  328. extclassName();
  329. C[n].d^=C[n].s; // don't toggle dir on ext.click
  330. }
  331. var tb=[],ix=[], i, tl;
  332. _l('n:'+n);
  333. tl=TB.rows.length;
  334. ASC=C[n].d^=C[n].s;
  335. for( i=0; i<tl; i++)
  336. ix.push(i);
  337. oClr();
  338. sort_p(n);
  339. ix.sort(sort_fn);
  340. for( i=0; i<tl; i++)
  341. tb.push(TB.rows[ix[i]].innerHTML);
  342. for( i=0; i<tl; i++)
  343. TB.rows[i].innerHTML=tb[i];
  344. setC(n);
  345. gitDir1(0);
  346. }
  347.  
  348. function onClik(e){doSort(e.target)}
  349.  
  350. function gitDir1(x){
  351. if(x && document.querySelector('.fsort-butt')) {
  352. _l('gitDir'+x+ '- already'); return;
  353. }
  354. _l('gitDir',x?'create':'refresh')
  355. var c,o;
  356. ca=[];
  357. c= D.querySelector('.file-wrap table.files td.content >span');
  358. if(!c){ _l( '*GHSFL* no content') ; return; }
  359. ca.push(c);
  360. c=D.querySelector('.file-wrap table.files td.message >span');
  361. if(!c){ _l( '*GHSFL* no messages'); return; }
  362. ca.push(c);
  363. c=D.querySelector('.file-wrap table.files td.age >span');
  364. if(!c){_l( '*GHSFL* no ages'); return; }
  365. ca.push(c);
  366. if(x){ oClr(); oa=[];
  367. o=D.createElement('span');
  368. o.textContent='';
  369. oa.push(o);
  370. o=o.cloneNode(true);
  371. oa.push(o);
  372. o=o.cloneNode(true);
  373. oa.push(o);
  374. clock=D.createElement('span');
  375. clock.id='fsort-clock'; clockclassName();
  376. ext=D.createElement('span');
  377. ext.id='fsort-ext'; extclassName();
  378. setDateTime();
  379. setC(-1);
  380. }
  381. insBefore(oa[0],ca[0]);
  382. insBefore(ext,ca[0]);
  383. insBefore(oa[1],ca[1]);
  384. insBefore(oa[2],ca[2]);
  385. insBefore(clock,ca[2]);
  386. }
  387.  
  388. function gitDir(){
  389. gitDir1(1);
  390. }
  391.  
  392. catcher= D.querySelector('#js-repo-pjax-container');
  393. if(!catcher){ _l( "*GHSFL* err0r"); return; }
  394.  
  395. catcher.addEventListener('mousedown',function(e){
  396. if(e.target.nodeName && e.target.nodeName=='SPAN' &&
  397. e.target.className.indexOf('fsort-butt')>-1)
  398. { onClik(e); }
  399. }
  400. ,false);
  401.  
  402. _l('startup()');
  403.  
  404. try {
  405. locStor = window.localStorage;
  406. tt=locStor.getItem("GHSFL");
  407. } catch(e){ locStor =null}
  408.  
  409. if(locStor && tt) try{
  410. prefs =JSON.parse(tt);
  411. }catch(e){ console.log(e+"\n*GHSFL* bad prefs") }
  412.  
  413. css();
  414. dtStyle.disabled=(prefs.dtStyle===1);
  415.  
  416. gitDir();
  417. var target = catcher; //document.body; //D.querSelector('.file-wrap');
  418. var MO = window.MutationObserver;
  419. if(!MO) MO= window.WebKitMutationObserver;
  420. if(!MO) return;
  421. var __started=0;
  422. var mutI=0;
  423. var observer = new MO(function(mutations) {
  424.  
  425. for(var m,t, ml=mutations.length, i=0; i<ml; i++)
  426. {
  427. m=mutations[i],t = m.target;
  428. if( m.type=="attributes")
  429. {
  430. if( t.nodeName == 'DIV' &&
  431. t.className == "file-wrap"
  432. ){
  433. gitDir();
  434. return;
  435. }
  436. // patch for the very first page
  437. if( 0===__started && t.nodeName=='TIME' )
  438. {
  439. if( t.parentNode.parentNode.className=="age" )
  440. {
  441. //_l('T'+mutI++,ml,' T:' +m.type,'N:'+t.nodeName, 'C:',"age" ) ;
  442. if(!catcher.querySelector('.fsort-butt'))
  443. gitDir(1); //chrome ?!11
  444. setDateTime(1);
  445. __started=1;
  446. return;
  447. }
  448. else continue;
  449. }
  450. }
  451. if( m.type=="childList" )
  452. {
  453. if( t.className=='age' )
  454. {
  455. if(!catcher.querySelector('.fsort-butt'))
  456. gitDir(1); //chrome ?!11
  457. //_l('C'+mutI++,ml,' T:' +m.type,'N:'+t.nodeName,'C:'+ t.className,t.querySelector('TIME').textContent) ;
  458. setDateTime(1);
  459. return;
  460. }
  461. else continue;
  462. }
  463. }
  464. });
  465.  
  466. observer.observe(D.body, { attributes: true, childList: true, subtree: true } );
  467. /* attributes: true , childList: true, subtree: true,
  468. characterData: true, attributeOldValue:true, characterDataOldValue:true
  469. */
  470.  
  471. })()};