stabilize sorttable.js even if the underlying Javascript sort() is unstable
[lhc/web/wiklou.git] / skins / common / sorttable.js
1 /*
2 * From: http://www.kryogenix.org/code/browser/sorttable/
3 * Licence: X11
4 */
5
6 hookEvent( "load", sortables_init);
7
8 var NO_ARROW = stylepath+'/common/sort_none.gif';
9 var UP_ARROW = stylepath+'/common/sort_up.gif';
10 var DOWN_ARROW = stylepath+'/common/sort_down.gif';
11
12 function sortables_init() {
13 var idnum = 0;
14 // Find all tables with class sortable and make them sortable
15 if (!document.getElementsByTagName) return;
16 tbls = document.getElementsByTagName("table");
17 for (ti=0;ti<tbls.length;ti++) {
18 thisTbl = tbls[ti];
19 if ((' '+thisTbl.className+' ').indexOf(" sortable ") != -1) {
20 if (!thisTbl.id) {
21 thisTbl.setAttribute('id','sortable_table_id_'+idnum);
22 ++idnum;
23 }
24 //initTable(thisTbl.id);
25 ts_makeSortable(thisTbl);
26 }
27 }
28 }
29
30 function ts_makeSortable(table) {
31 if (table.rows && table.rows.length > 0) {
32 var firstRow = table.rows[0];
33 }
34 if (!firstRow) return;
35
36 // We have a first row: assume it's the header, and make its contents clickable links
37 for (var i=0;i<firstRow.cells.length;i++) {
38 var cell = firstRow.cells[i];
39 var txt = ts_getInnerText(cell);
40 cell.innerHTML = txt+'<a href="#" class="sortheader" onclick="ts_resortTable(this);return false;"><img class="sortarrow" src="'+NO_ARROW+'" alt="&#x2195;" /></a>';
41 }
42 }
43
44 function ts_getInnerText(el) {
45 if (typeof el == "string") return el;
46 if (typeof el == "undefined") { return el };
47 if (el.innerText) return el.innerText; //Not needed but it is faster
48 var str = "";
49
50 var cs = el.childNodes;
51 var l = cs.length;
52 for (var i = 0; i < l; i++) {
53 switch (cs[i].nodeType) {
54 case 1: //ELEMENT_NODE
55 str += ts_getInnerText(cs[i]);
56 break;
57 case 3: //TEXT_NODE
58 str += cs[i].nodeValue;
59 break;
60 }
61 }
62 return str;
63 }
64
65 function ts_resortTable(lnk) {
66 // get the arrow image
67 var img;
68 for (var ci=0;ci<lnk.childNodes.length;ci++) {
69 if (lnk.childNodes[ci].tagName && lnk.childNodes[ci].tagName.toLowerCase() == 'img') img = lnk.childNodes[ci];
70 }
71 var reverse = (img.getAttribute("sortdir") == 'down');
72
73 var td = lnk.parentNode;
74 var column = td.cellIndex;
75 var table = getParent(td,'TABLE');
76
77 // Work out a type for the column
78 if (table.rows.length <= 1) return;
79 var itm = ts_getInnerText(table.rows[1].cells[column]);
80 sortfn = ts_sort_caseinsensitive;
81 // Note: The trailing \n$ is needed because that's how MediaWiki spits out its table syntax.
82 if (itm.match(/^\s*\d{2}[\/-]\d{2}[\/-]\d{4}\s*$/)) sortfn = ts_sort_date;
83 if (itm.match(/^\s*\d{2}[\/-]\d{2}[\/-]\d{2}\s*$/)) sortfn = ts_sort_date;
84 if (itm.match(/^\s*[?$]/)) sortfn = ts_sort_currency;
85 if (itm.match(/^\s*[\d\.]+\s*$/)) sortfn = ts_sort_numeric;
86 var firstRow = new Array();
87 var newRows = new Array();
88 for (i=0;i<table.rows[0].length;i++) { firstRow[i] = table.rows[0][i]; }
89 for (j=1;j<table.rows.length;j++) {
90 var obj = new Object();
91 obj.row = table.rows[j];
92 obj.grp = ((' '+obj.row.className+' ').indexOf(' sortbottom ') == -1 ? 0 : reverse ? -1 : 1);
93 obj.txt = ts_getInnerText(obj.row.cells[column]);
94 obj.idx = (reverse ? -j : j);
95 newRows[j-1] = obj;
96 }
97
98 newRows.sort(sortfn);
99
100 if (reverse) {
101 ARROW = UP_ARROW;
102 newRows.reverse();
103 img.setAttribute('sortdir','up');
104 } else {
105 ARROW = DOWN_ARROW;
106 img.setAttribute('sortdir','down');
107 }
108
109 // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
110 for (i=0;i<newRows.length;i++) { table.tBodies[0].appendChild(newRows[i].row); }
111
112 // Delete any other arrows there may be showing
113 var allimgs = document.getElementsByTagName("img");
114 for (var ci=0;ci<allimgs.length;ci++) {
115 if (allimgs[ci].className == 'sortarrow') {
116 if (getParent(allimgs[ci],"table") == getParent(lnk,"table")) { // in the same table as us?
117 allimgs[ci].setAttribute('src',NO_ARROW);
118 }
119 }
120 }
121
122 img.setAttribute('src',ARROW);
123 img.setAttribute('alt',img.getAttribute("sortdir") == 'down' ? '&darr;' : '&uarr;');
124 }
125
126 function getParent(el, pTagName) {
127 if (el == null) return null;
128 else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase()) // Gecko bug, supposed to be uppercase
129 return el;
130 else
131 return getParent(el.parentNode, pTagName);
132 }
133 function ts_sort_date(a,b) {
134 if (a.grp != b.grp) return a.grp-b.grp;
135 // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
136 aa = a.txt;
137 bb = b.txt;
138 if (aa.length == 10) {
139 dt1 = aa.substr(6,4)+aa.substr(3,2)+aa.substr(0,2);
140 } else {
141 yr = aa.substr(6,2);
142 if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }
143 dt1 = yr+aa.substr(3,2)+aa.substr(0,2);
144 }
145 if (bb.length == 10) {
146 dt2 = bb.substr(6,4)+bb.substr(3,2)+bb.substr(0,2);
147 } else {
148 yr = bb.substr(6,2);
149 if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }
150 dt2 = yr+bb.substr(3,2)+bb.substr(0,2);
151 }
152 if (dt1==dt2) return a.idx-b.idx;
153 if (dt1<dt2) return -1;
154 return 1;
155 }
156
157 function ts_sort_currency(a,b) {
158 if (a.grp != b.grp) return a.grp-b.grp;
159 aa = parseFloat(a.txt.replace(/[^0-9.]/g,''));
160 bb = parseFloat(b.txt.replace(/[^0-9.]/g,''));
161 if (isNaN(aa)) aa = 0;
162 if (isNaN(bb)) bb = 0;
163 if (aa==bb) return a.idx-b.idx;
164 return aa-bb;
165 }
166
167 function ts_sort_numeric(a,b) {
168 if (a.grp != b.grp) return a.grp-b.grp;
169 aa = parseFloat(a.txt);
170 bb = parseFloat(b.txt);
171 if (isNaN(aa)) aa = 0;
172 if (isNaN(bb)) bb = 0;
173 if (aa==bb) return a.idx-b.idx;
174 return aa-bb;
175 }
176
177 function ts_sort_caseinsensitive(a,b) {
178 if (a.grp != b.grp) return a.grp-b.grp;
179 aa = a.txt.toLowerCase();
180 bb = b.txt.toLowerCase();
181 if (aa==bb) return a.idx-b.idx;
182 if (aa<bb) return -1;
183 return 1;
184 }
185
186 function ts_sort_default(a,b) {
187 if (a.grp != b.grp) return a.grp-b.grp;
188 aa = a.txt;
189 bb = b.txt;
190 if (aa==bb) return a.idx-b.idx;
191 if (aa<bb) return -1;
192 return 1;
193 }