2 Table sorting script by Joost de Valk, check it out at http://www.joostdevalk.nl/code/sortable-table/.
3 Based on a script from http://www.kryogenix.org/code/browser/sorttable/.
4 Distributed under the MIT license: http://www.kryogenix.org/code/browser/licence.html .
6 Copyright (c) 1997-2007 Stuart Langridge, Joost de Valk.
11 /* You can change these values */
12 var image_path = "/images/";
13 var image_up = "sort_up.gif";
14 var image_down = "sort_down.gif";
15 var image_none = "sort_none.gif";
16 var europeandate = true;
17 var alternate_row_colors = true;
19 /* Don't change anything below this unless you know what you're doing */
20 addEvent(window, "load", sortables_init);
22 var SORT_COLUMN_INDEX;
25 function sortables_init() {
26 // Find all tables with class sortable and make them sortable
27 if (!document.getElementsByTagName) return;
28 tbls = document.getElementsByTagName("table");
29 for (ti=0;ti<tbls.length;ti++) {
31 if (((' '+thisTbl.className+' ').indexOf("sortable") != -1) && (thisTbl.id)) {
32 ts_makeSortable(thisTbl);
37 function ts_makeSortable(t) {
38 if (t.rows && t.rows.length > 0) {
39 if (t.tHead && t.tHead.rows.length > 0) {
40 var firstRow = t.tHead.rows[t.tHead.rows.length-1];
43 var firstRow = t.rows[0];
46 if (!firstRow) return;
48 // We have a first row: assume it's the header, and make its contents clickable links
49 for (var i=0;i<firstRow.cells.length;i++) {
50 var cell = firstRow.cells[i];
51 var txt = ts_getInnerText(cell);
52 if (cell.className != "unsortable" && cell.className.indexOf("unsortable") == -1) {
53 cell.innerHTML = '<a href="#" class="sortheader" onclick="ts_resortTable(this, '+i+');return false;">'+txt+'<span class="sortarrow"> <img src="'+ image_path + image_none + '" alt="↓"/></span></a>';
56 if (alternate_row_colors) {
61 function ts_getInnerText(el) {
62 if (typeof el == "string") return el;
63 if (typeof el == "undefined") { return el };
64 if (el.innerText) return el.innerText; //Not needed but it is faster
67 var cs = el.childNodes;
69 for (var i = 0; i < l; i++) {
70 switch (cs[i].nodeType) {
71 case 1: //ELEMENT_NODE
72 str += ts_getInnerText(cs[i]);
75 str += cs[i].nodeValue;
82 function ts_resortTable(lnk, clid) {
84 for (var ci=0;ci<lnk.childNodes.length;ci++) {
85 if (lnk.childNodes[ci].tagName && lnk.childNodes[ci].tagName.toLowerCase() == 'span') span = lnk.childNodes[ci];
87 var spantext = ts_getInnerText(span);
88 var td = lnk.parentNode;
89 var column = clid || td.cellIndex;
90 var t = getParent(td,'TABLE');
91 // Work out a type for the column
92 if (t.rows.length <= 1) return;
95 while (itm == "" && i < t.tBodies[0].rows.length) {
96 var itm = ts_getInnerText(t.tBodies[0].rows[i].cells[column]);
98 if (itm.substr(0,4) == "<!--" || itm.length == 0) {
103 if (itm == "") return;
104 sortfn = ts_sort_caseinsensitive;
105 if (itm.match(/^\d\d[\/\.-][a-zA-z][a-zA-Z][a-zA-Z][\/\.-]\d\d\d\d$/)) sortfn = ts_sort_date;
106 if (itm.match(/^\d\d[\/\.-]\d\d[\/\.-]\d\d\d{2}?$/)) sortfn = ts_sort_date;
107 if (itm.match(/^-?[£$
\80Û¢´]\d/)) sortfn = ts_sort_numeric;
108 // ape: added to provide numeric sort on size for the docs tools.
109 if (itm.match(/^\d+ *(B|KB|MB)$/)) sortfn = ts_sort_numeric;
110 if (itm.match(/^-?(\d+[,\.]?)+(E[-+][\d]+)?%?$/)) sortfn = ts_sort_numeric;
111 SORT_COLUMN_INDEX = column;
112 var firstRow = new Array();
113 var newRows = new Array();
114 for (k=0;k<t.tBodies.length;k++) {
115 for (i=0;i<t.tBodies[k].rows[0].length;i++) {
116 firstRow[i] = t.tBodies[k].rows[0][i];
119 for (k=0;k<t.tBodies.length;k++) {
121 // Skip the first row
122 for (j=1;j<t.tBodies[k].rows.length;j++) {
123 newRows[j-1] = t.tBodies[k].rows[j];
126 // Do NOT skip the first row
127 for (j=0;j<t.tBodies[k].rows.length;j++) {
128 newRows[j] = t.tBodies[k].rows[j];
132 newRows.sort(sortfn);
133 if (span.getAttribute("sortdir") == 'down') {
134 ARROW = ' <img src="'+ image_path + image_down + '" alt="↓"/>';
136 span.setAttribute('sortdir','up');
138 ARROW = ' <img src="'+ image_path + image_up + '" alt="↑"/>';
139 span.setAttribute('sortdir','down');
141 // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
142 // don't do sortbottom rows
143 for (i=0; i<newRows.length; i++) {
144 if (!newRows[i].className || (newRows[i].className && (newRows[i].className.indexOf('sortbottom') == -1))) {
145 t.tBodies[0].appendChild(newRows[i]);
148 // do sortbottom rows only
149 for (i=0; i<newRows.length; i++) {
150 if (newRows[i].className && (newRows[i].className.indexOf('sortbottom') != -1))
151 t.tBodies[0].appendChild(newRows[i]);
153 // Delete any other arrows there may be showing
154 var allspans = document.getElementsByTagName("span");
155 for (var ci=0;ci<allspans.length;ci++) {
156 if (allspans[ci].className == 'sortarrow') {
157 if (getParent(allspans[ci],"table") == getParent(lnk,"table")) { // in the same table as us?
158 allspans[ci].innerHTML = ' <img src="'+ image_path + image_none + '" alt="↓"/>';
162 span.innerHTML = ARROW;
166 function getParent(el, pTagName) {
169 } else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase()) {
172 return getParent(el.parentNode, pTagName);
176 function sort_date(date) {
177 // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
179 if (date.length == 11) {
180 mtstr = date.substr(3,3);
181 mtstr = mtstr.toLowerCase();
183 case "jan": var mt = "01"; break;
184 case "feb": var mt = "02"; break;
185 case "mar": var mt = "03"; break;
186 case "apr": var mt = "04"; break;
187 case "may": var mt = "05"; break;
188 case "jun": var mt = "06"; break;
189 case "jul": var mt = "07"; break;
190 case "aug": var mt = "08"; break;
191 case "sep": var mt = "09"; break;
192 case "oct": var mt = "10"; break;
193 case "nov": var mt = "11"; break;
194 case "dec": var mt = "12"; break;
195 // default: var mt = "00";
197 dt = date.substr(7,4)+mt+date.substr(0,2);
199 } else if (date.length == 10) {
200 if (europeandate == false) {
201 dt = date.substr(6,4)+date.substr(0,2)+date.substr(3,2);
204 dt = date.substr(6,4)+date.substr(3,2)+date.substr(0,2);
207 } else if (date.length == 8) {
208 yr = date.substr(6,2);
209 if (parseInt(yr) < 50) {
214 if (europeandate == true) {
215 dt = yr+date.substr(3,2)+date.substr(0,2);
218 dt = yr+date.substr(0,2)+date.substr(3,2);
225 function ts_sort_date(a,b) {
226 dt1 = sort_date(ts_getInnerText(a.cells[SORT_COLUMN_INDEX]));
227 dt2 = sort_date(ts_getInnerText(b.cells[SORT_COLUMN_INDEX]));
237 function ts_sort_numeric(a,b) {
238 var aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
240 var bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
242 return compare_numeric(aa,bb);
244 function compare_numeric(a,b) {
245 var aa = parseFloat(a);
246 aa = (isNaN(aa) ? 0 : aa);
247 var bb = parseFloat(b);
248 bb = (isNaN(bb) ? 0 : bb);
251 function ts_sort_caseinsensitive(a,b) {
252 var aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).toLowerCase();
253 var bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
262 function ts_sort_default(a,b) {
263 var aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
264 var bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
273 function addEvent(elm, evType, fn, useCapture)
274 // addEvent and removeEvent
275 // cross-browser event handling for IE5+, NS6 and Mozilla
278 if (elm.addEventListener){
279 elm.addEventListener(evType, fn, useCapture);
281 } else if (elm.attachEvent){
282 var r = elm.attachEvent("on"+evType, fn);
285 alert("Handler could not be removed");
289 function clean_num(str) {
290 str = str.replace(new RegExp(/[^-?0-9.]/g),"");
294 return s.replace(/^\s+|\s+$/g, "");
296 function alternate(table) {
297 // Take object table and get all it's tbodies.
298 var tableBodies = table.getElementsByTagName("tbody");
299 // Loop through these tbodies
300 for (var i = 0; i < tableBodies.length; i++) {
301 // Take the tbody, and get all it's rows
302 var tableRows = tableBodies[i].getElementsByTagName("tr");
303 // Loop through these rows
304 // Start at 1 because we want to leave the heading row untouched
305 for (var j = 0; j < tableRows.length; j++) {
306 // Check if j is even, and apply classes for both possible results
307 if ( (j % 2) == 0 ) {
308 if ( !(tableRows[j].className.indexOf('odd') == -1) ) {
309 tableRows[j].className = tableRows[j].className.replace('odd', 'even');
311 if ( tableRows[j].className.indexOf('even') == -1 ) {
312 tableRows[j].className += " even";
316 if ( !(tableRows[j].className.indexOf('even') == -1) ) {
317 tableRows[j].className = tableRows[j].className.replace('even', 'odd');
319 if ( tableRows[j].className.indexOf('odd') == -1 ) {
320 tableRows[j].className += " odd";