// ==UserScript==
// @name GitHub Sortable Filelist
// @namespace trespassersW
// @description appends sorting function to github directories
// @include https://github.com/*
// @version 14.11.13.7
// .7 date/time display mode switching
// .4 now works on all github pages
// @created 2014-11-10
// @updated 2014-11-13
// @author trespassersW
// @licence MIT
// @run-at document-end
// @grant GM_none
// ==/UserScript==
if(document.body || document.querySelector('#js-repo-pjax-container')){ // .file-wrap
var llii=0; function _l(m){/* * /
console.log(++llii +': '+m)
/* */
}
(function(){ "use strict";
var ii=0,tt;
var d0=[0,0,1];
var C=[{c:1, d: 0, s: 0},{c:2, d: 0, s: 0},{c:3, d: 1, s: 0}];
var ASC;
var oa=[],ca=[],clock,dtStyle;
var D=document, TB;
var catcher,locStor;
var prefs={dtStyle:0};
function stickStyle(css){
var s=document.createElement("style"); s.type="text/css";
s.appendChild(document.createTextNode(css));
return (document.head||document.documentElement).appendChild(s);
}
function insBefore(n,e){
return e.parentNode.insertBefore(n,e);
}
function insAfter(n,e){
if(e.nextElementSibling)
return e.parentNode.insertBefore(n,e.nextElementSibling);
return e.parentNode.appendChild(n);
}
function outerNode(target, node) {
if (target.nodeName==node) return target;
if (target.parentNode)
while (target = target.parentNode) try{
if (target.nodeName==node)
return target;
}catch(e){};
return null;
}
function css(){
stickStyle('\
.fsort-butt, .tables.file {position: relative; }\
.fsort-butt:before{\
position: absolute; left:1.5em; top: -1em; \
cursor: pointer;\
content: "";\
z-index:99999;\
width: 0; height: 0;\
opacity:.2;\
}\
.fsort-asc:before,\
.fsort-desc.fsort-sel:hover:before\
{\
border-left: 6px solid transparent;\
border-right: 6px solid transparent;\
border-bottom: 14px solid #444;\
border-top: 0;\
}\
.fsort-desc:before,\
.fsort-asc.fsort-sel:hover:before{\
border-left: 6px solid transparent;\
border-right: 6px solid transparent;\
border-bottom: 0;\
border-top: 14px solid #444;\
}\
.fsort-butt.fsort-desc.fsort-sel:hover:before,\
.fsort-butt.fsort-asc.fsort-sel:before{\
border-bottom: 14px solid #4183C4;\
border-top: 0;\
}\
.fsort-butt.fsort-desc.fsort-sel:before,\
.fsort-butt.fsort-asc.fsort-sel:hover:before{\
border-bottom: 0;\
border-top: 14px solid #4183C4;\
}\
\
.fsort-butt.fsort-sel:before{ opacity: .6 }\
.fsort-butt:hover:before{ opacity: 1 !important;}\
\
.fsort-butt.fsort-clock:before{\
position:absolute; left:3em; top:-1.2em;\
opacity:1!important;\
content: url(data:image/png;base64,\
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=\
);\
}\
table.files td.age .css-truncate.css-truncate-target{\
width: 99% !important; \
max-width: none !important;\
}\
table.files td.age span.css-truncate time{\
position: relative !important;\
}\
\
/* patches */\
table.files td.age {text-align: left !important; padding-: 0 4px !important;\
}\
table.files td.message {overflow: visible !important;}\
');//#80A6CD
dtStyle=stickStyle('\
table.files td.age span.css-truncate time{\
visibility: hidden !important;\
}\
table.files td.age span.css-truncate > time:before{\
content: attr(time-or-date);\
visibility: visible !important;\
position: absolute !important;\
}\
')
}
function setC(n){
for(var i=0,il=C.length; i<il; i++ ){
if(i!=n) C[i].s= 0, C[i].d=d0[i];
else C[i].s=1;
oa[i].className='fsort-butt fsort-'+(C[i].d?'desc':'asc')+(C[i].s?' fsort-sel':'') ;
oa[i].title=C[i].d? '\u21ca' : '\u21c8';
}
}
function setDateTime(){
try{ //014-10-02T16:09:05Z
var DT=D.querySelectorAll('td.age span.css-truncate time'),res;
for(var dt, dl=DT.length, i=0; i<dl; i++){
dt = DT[i].getAttribute('datetime').match(/([0-9\-]+).([0-9\:]+)./);
if(/minut|hour|just/.test(DT[i].textContent))
DT[i].setAttribute("time-or-date",dt[2]);
else
DT[i].setAttribute("time-or-date",dt[1]);
}
}catch(e){(_l(e+'\n*GHSFL* wrong datetime'))}
}
function isDir(x){
return (TB.rows[x].cells[0].querySelector("span.octicon-file-directory")) != null;
}
var sDir,sCells;
var fa=[
function(a){
sCells.push(TB.rows[a].cells[1].querySelector('span.css-truncate-target a').textContent);
},
function(a){
sCells.push(TB.rows[a].cells[2].querySelector('span.css-truncate').textContent);
},
function(a){
sCells.push(TB.rows[a].cells[3].querySelector('span.css-truncate>time').getAttribute('datetime'));
}
]
function sort_p(n){// prepare data for sorting
sDir=[],sCells=[];
for(var tl=TB.rows.length, a=0; a<tl; a++){
sDir.push(isDir(a));
fa[n](a);
}
}
function sort_fn(a,b){
var x=sDir[a], y=sDir[b];
if(x!=y) return ((x<y)<<1)-1;
x= sCells[a], y= sCells[b];
return x==y? 0: (((x>y)^ASC)<<1)-1;
}
var CNn={content: 0, message: 1, age: 2}
function oClr(){
var o= catcher.querySelectorAll('.fsort-butt')
for(var ol=o.length,i=0;i<ol;i++)
o[i].parentNode.removeChild(o[i]);
}
//
function doSort(t){
if(t.className.indexOf('fsort-clock')>-1){
dtStyle.disabled = (1===(prefs.dtStyle ^= 1));
if(locStor) locStor.setItem('GHSFL',JSON.stringify(prefs))
return;
}
TB=outerNode(t,'TBODY');
var tb=[],ix=[], i, tl;
if(!TB){ _l( "*GHSFL* TBODY not found"); return; }
var n=CNn[t.parentNode.className];
if(typeof n=="undefined"){ _l( "*GHSFL* undefined col"); return; }
_l('n:'+n);
tl=TB.rows.length;
ASC=C[n].d^=C[n].s;
for( i=0; i<tl; i++)
ix.push(i);
oClr();
sort_p(n);
ix.sort(sort_fn);
for( i=0; i<tl; i++)
tb.push(TB.rows[ix[i]].innerHTML);
for( i=0; i<tl; i++)
TB.rows[i].innerHTML=tb[i];
setC(n);
gitDir1(0);
}
function onClik(e){doSort(e.target)}
function gitDir1(x){
if(x && document.querySelector('.fsort-butt')) {
_l('gitDir'+x+' - already'); return;
}
_l('gitDir'+x)
var c,o;
ca=[];
c= D.querySelector('.file-wrap table.files td.content >span');
if(!c){ _l( '*GHSFL* no content') ; return; }
ca.push(c);
c=D.querySelector('.file-wrap table.files td.message >span');
if(!c){ _l( '*GHSFL* no messages'); return; }
ca.push(c);
c=D.querySelector('.file-wrap table.files td.age >span');
if(!c){_l( '*GHSFL* no ages'); return; }
ca.push(c);
if(x){ oClr(); oa=[];
o=D.createElement('span');
o.textContent='';
oa.push(o);
o=o.cloneNode(true);
oa.push(o);
o=o.cloneNode(true);
oa.push(o);
clock=D.createElement('span');
clock.className='fsort-butt fsort-clock';
setDateTime();
setC(-1);
}
insBefore(oa[0],ca[0]);
insBefore(oa[1],ca[1]);
insBefore(oa[2],ca[2]);
insBefore(clock,ca[2]);
}
function gitDir(){
gitDir1(1);
}
catcher= D.querySelector('#js-repo-pjax-container');
if(!catcher){ _l( "*GHSFL* err0r"); return; }
catcher.addEventListener('mousedown',function(e){
if(e.target.nodeName && e.target.nodeName=='SPAN' &&
e.target.className.indexOf('fsort-butt')>-1)
{ onClik(e); }
}
,false);
_l('startup()');
try {
locStor = window.localStorage;
tt=locStor.getItem("GHSFL");
} catch(e){ locStor =null}
if(locStor && tt) try{
prefs =JSON.parse(tt);
}catch(e){ console.log(e+"\n*GHSFL* bad prefs") }
css();
dtStyle.disabled=(prefs.dtStyle===1);
gitDir();
var target = catcher; //document.body; //D.querSelector('.file-wrap');
var MO = window.MutationObserver;
if(!MO) MO= window.WebKitMutationObserver;
if(!MO) return;
var observer = new MO(function(mutations) {
mutations.forEach(function(m) {
if( m.type= "attributes" &&
m.target.nodeName == 'DIV' &&
m.target.className == "file-wrap" )
gitDir();
return;
});
});
//if(m.target.nodeName!='TIME')_l('mo'+' '+m.type +'.'+m.target.nodeName+m.target.className));
observer.observe(D.body, { attributes: true, subtree: true } );
/* attributes: true , childList: true, subtree: true,
characterData: true, attributeOldValue:true, characterDataOldValue:true
*/
})()};
/*
to do: persistent settings; sorting by file extensions; toggling date/time display mode
... do we really need it?
*/