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 */
13 var image_up = "arrow-up.gif";
14 var image_down = "arrow-down.gif";
15 var image_none = "arrow-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(/^-?[£$€Û¢´]\d/)) sortfn = ts_sort_numeric;
108 // ignore stuff in () after the numbers.
109 if (itm.match(/^-?(\d+[,\.]?)+(E[-+][\d]+)?%?( \(.*\))?$/)) sortfn = ts_sort_numeric;
110 SORT_COLUMN_INDEX = column;
111 var firstRow = new Array();
112 var newRows = new Array();
113 for (k=0;k<t.tBodies.length;k++) {
114 for (i=0;i<t.tBodies[k].rows[0].length;i++) {
115 firstRow[i] = t.tBodies[k].rows[0][i];
118 for (k=0;k<t.tBodies.length;k++) {
120 // Skip the first row
121 for (j=1;j<t.tBodies[k].rows.length;j++) {
122 newRows[j-1] = t.tBodies[k].rows[j];
125 // Do NOT skip the first row
126 for (j=0;j<t.tBodies[k].rows.length;j++) {
127 newRows[j] = t.tBodies[k].rows[j];
131 newRows.sort(sortfn);
132 if (span.getAttribute("sortdir") == 'down') {
133 ARROW = ' <img src="'+ image_path + image_down + '" alt="↓"/>';
135 span.setAttribute('sortdir','up');
137 ARROW = ' <img src="'+ image_path + image_up + '" alt="↑"/>';
138 span.setAttribute('sortdir','down');
140 // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
141 // don't do sortbottom rows
142 for (i=0; i<newRows.length; i++) {
143 if (!newRows[i].className || (newRows[i].className && (newRows[i].className.indexOf('sortbottom') == -1))) {
144 t.tBodies[0].appendChild(newRows[i]);
147 // do sortbottom rows only
148 for (i=0; i<newRows.length; i++) {
149 if (newRows[i].className && (newRows[i].className.indexOf('sortbottom') != -1))
150 t.tBodies[0].appendChild(newRows[i]);
152 // Delete any other arrows there may be showing
153 var allspans = document.getElementsByTagName("span");
154 for (var ci=0;ci<allspans.length;ci++) {
155 if (allspans[ci].className == 'sortarrow') {
156 if (getParent(allspans[ci],"table") == getParent(lnk,"table")) { // in the same table as us?
157 allspans[ci].innerHTML = ' <img src="'+ image_path + image_none + '" alt="↓"/>';
161 span.innerHTML = ARROW;
165 function getParent(el, pTagName) {
168 } else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase()) {
171 return getParent(el.parentNode, pTagName);
175 function sort_date(date) {
176 // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
178 if (date.length == 11) {
179 mtstr = date.substr(3,3);
180 mtstr = mtstr.toLowerCase();
182 case "jan": var mt = "01"; break;
183 case "feb": var mt = "02"; break;
184 case "mar": var mt = "03"; break;
185 case "apr": var mt = "04"; break;
186 case "may": var mt = "05"; break;
187 case "jun": var mt = "06"; break;
188 case "jul": var mt = "07"; break;
189 case "aug": var mt = "08"; break;
190 case "sep": var mt = "09"; break;
191 case "oct": var mt = "10"; break;
192 case "nov": var mt = "11"; break;
193 case "dec": var mt = "12"; break;
194 // default: var mt = "00";
196 dt = date.substr(7,4)+mt+date.substr(0,2);
198 } else if (date.length == 10) {
199 if (europeandate == false) {
200 dt = date.substr(6,4)+date.substr(0,2)+date.substr(3,2);
203 dt = date.substr(6,4)+date.substr(3,2)+date.substr(0,2);
206 } else if (date.length == 8) {
207 yr = date.substr(6,2);
208 if (parseInt(yr) < 50) {
213 if (europeandate == true) {
214 dt = yr+date.substr(3,2)+date.substr(0,2);
217 dt = yr+date.substr(0,2)+date.substr(3,2);
224 function ts_sort_date(a,b) {
225 dt1 = sort_date(ts_getInnerText(a.cells[SORT_COLUMN_INDEX]));
226 dt2 = sort_date(ts_getInnerText(b.cells[SORT_COLUMN_INDEX]));
236 function ts_sort_numeric(a,b) {
237 var aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
239 var bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
241 return compare_numeric(aa,bb);
243 function compare_numeric(a,b) {
244 var a = parseFloat(a);
245 a = (isNaN(a) ? 0 : a);
246 var b = parseFloat(b);
247 b = (isNaN(b) ? 0 : b);
250 function ts_sort_caseinsensitive(a,b) {
251 aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).toLowerCase();
252 bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
261 function ts_sort_default(a,b) {
262 aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
263 bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
272 function addEvent(elm, evType, fn, useCapture)
273 // addEvent and removeEvent
274 // cross-browser event handling for IE5+, NS6 and Mozilla
277 if (elm.addEventListener){
278 elm.addEventListener(evType, fn, useCapture);
280 } else if (elm.attachEvent){
281 var r = elm.attachEvent("on"+evType, fn);
284 alert("Handler could not be removed");
287 function clean_num(str) {
288 str = str.replace(new RegExp(/[^-?0-9.]/g),"");
292 return s.replace(/^\s+|\s+$/g, "");
294 function alternate(table) {
295 // Take object table and get all it's tbodies.
296 var tableBodies = table.getElementsByTagName("tbody");
297 // Loop through these tbodies
298 for (var i = 0; i < tableBodies.length; i++) {
299 // Take the tbody, and get all it's rows
300 var tableRows = tableBodies[i].getElementsByTagName("tr");
301 // Loop through these rows
302 // Start at 1 because we want to leave the heading row untouched
303 for (var j = 0; j < tableRows.length; j++) {
304 // Check if j is even, and apply classes for both possible results
305 if ( (j % 2) == 0 ) {
306 if ( !(tableRows[j].className.indexOf('odd') == -1) ) {
307 tableRows[j].className = tableRows[j].className.replace('odd', 'even');
309 if ( tableRows[j].className.indexOf('even') == -1 ) {
310 tableRows[j].className += " even";
314 if ( !(tableRows[j].className.indexOf('even') == -1) ) {
315 tableRows[j].className = tableRows[j].className.replace('even', 'odd');
317 if ( tableRows[j].className.indexOf('odd') == -1 ) {
318 tableRows[j].className += " odd";