/* SortTable version 2 7th April 2007 Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ Instructions: Download this file Add to your HTML Add class="sortable" to any table you'd like to make sortable sortable2 is also available since sortable tends to have global classes. Click on the headers to sort Thanks to many, many people for contributions and suggestions. Licenced as X11: http://www.kryogenix.org/code/browser/licence.html This basically means: do what you want with it. */ var stIsIE=0; if (navigator.appVersion.indexOf("MSIE")!=-1){ stIsIE=1; } sorttable = { init: function() { // quit if this function has already been called if (arguments.callee.done) return; // flag this function so we don't do the same thing twice arguments.callee.done = true; // kill the timer if (_timer) clearInterval(_timer); if (!document.createElement || !document.getElementsByTagName) return; sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; forEach(document.getElementsByTagName('table'), function(table) { if (table.className.search(/\bsortable\b/) != -1) { sorttable.makeSortable(table); } if (table.className.search(/\bsortable2\b/) !=-1) { sorttable.makeSortable(table); } }); }, makeSortable: function(table) { if (table.getElementsByTagName('thead').length == 0) { // table doesn't have a tHead. Since it should have, create one and // put the first table row in it. the = document.createElement('thead'); the.appendChild(table.rows[0]); table.insertBefore(the,table.firstChild); } // Safari doesn't support table.tHead, sigh if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; // if creating the table head made an empty tbody element nuke it var tBody = table.getElementsByTagName('tbody'); if (tBody.length > 1) { var is_empty = 1; for(var i=0;i0 ) ) { mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); if (mtch) { override = mtch[1]; } if (mtch && typeof sorttable["sort_"+override] == 'function') { headrow[i].sorttable_sortfunction = sorttable["sort_"+override]; } else { headrow[i].sorttable_sortfunction = sorttable.guessType(table,i); } // make it clickable to sort headrow[i].sorttable_columnindex = i; headrow[i].sorttable_tbody = table.tBodies[0]; headrow[i].className+=' clickable'; dean_addEvent(headrow[i],"selectstart",function(e) {return false}); dean_addEvent(headrow[i],"mousedown", function(e) {return false}); dean_addEvent(headrow[i],"dblclick", function(e) {return false}); dean_addEvent(headrow[i],"click", function(e) { if (this.className.search(/\bsorttable_sorted\b/) != -1) { // if we're already sorted by this column, just // reverse the table, which is quicker sorttable.reverse(this.sorttable_tbody); this.className = this.className.replace('sorttable_sorted', 'sorttable_sorted_reverse'); this.removeChild(document.getElementById('sorttable_sortfwdind')); sortrevind = document.createElement('span'); sortrevind.id = "sorttable_sortrevind"; sortrevind.innerHTML = stIsIE ? ' 5' : ' ▴'; this.appendChild(sortrevind); return; } if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { // if we're already sorted by this column in reverse, just // re-reverse the table, which is quicker sorttable.reverse(this.sorttable_tbody); this.className = this.className.replace('sorttable_sorted_reverse', 'sorttable_sorted'); this.removeChild(document.getElementById('sorttable_sortrevind')); sortfwdind = document.createElement('span'); sortfwdind.id = "sorttable_sortfwdind"; sortfwdind.innerHTML = stIsIE ? ' 6' : ' ▾'; this.appendChild(sortfwdind); return; } // remove sorttable_sorted classes theadrow = this.parentNode; forEach(theadrow.childNodes, function(cell) { if (cell.nodeType == 1) { // an element cell.className = cell.className.replace('sorttable_sorted_reverse',''); cell.className = cell.className.replace('sorttable_sorted',''); } }); sortfwdind = document.getElementById('sorttable_sortfwdind'); if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } sortrevind = document.getElementById('sorttable_sortrevind'); if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } this.className += ' sorttable_sorted'; sortfwdind = document.createElement('span'); sortfwdind.id = "sorttable_sortfwdind"; sortfwdind.innerHTML = stIsIE ? ' 6' : ' ▾'; this.appendChild(sortfwdind); // build an array to sort. This is a Schwartzian transform thing, // i.e., we "decorate" each row with the actual sort key, // sort based on the sort keys, and then put the rows back in order // which is a lot faster because you only do getInnerText once per row row_array = []; col = this.sorttable_columnindex; rows = this.sorttable_tbody.rows; for (var j=0; j 12) { // definitely dd/mm return sorttable.sort_ddmm; } else if (second > 12) { return sorttable.sort_mmdd; } else { // looks like a date, but we can't tell which, so assume // that it's dd/mm (English imperialism!) and keep looking sortfn = sorttable.sort_ddmm; } } } } return sortfn; }, getInnerText: function(node) { // gets the text we want to use for sorting for a cell. // strips leading and trailing whitespace. // this is *not* a generic getInnerText function; it's special to sorttable. // for example, you can override the cell text with a customkey attribute. // it also gets .value for fields. if (!node) { return ''; } hasInputs = (typeof node.getElementsByTagName == 'function') && node.getElementsByTagName('input').length; if (node.nodeType != 3 && node.getAttribute("sorttable_customkey") != null) { return node.getAttribute("sorttable_customkey"); } else if (typeof node.textContent != 'undefined' && !hasInputs) { return node.textContent.replace(/^\s+|\s+$/g, ''); } else if (typeof node.innerText != 'undefined' && !hasInputs) { return node.innerText.replace(/^\s+|\s+$/g, ''); } else if (typeof node.text != 'undefined' && !hasInputs) { return node.text.replace(/^\s+|\s+$/g, ''); } else { switch (node.nodeType) { case 3: if (node.nodeName.toLowerCase() == 'input') { return node.value.replace(/^\s+|\s+$/g, ''); } case 4: return node.nodeValue.replace(/^\s+|\s+$/g, ''); break; case 1: case 11: var innerText = ''; for (var i = 0; i < node.childNodes.length; i++) { innerText += sorttable.getInnerText(node.childNodes[i]); } return innerText.replace(/^\s+|\s+$/g, ''); break; default: return ''; } } }, reverse: function(tbody) { // reverse the rows in a tbody newrows = []; for (var i=0; i=0; i--) { tbody.appendChild(newrows[i]); } delete newrows; }, /* sort functions each sort function takes two parameters, a and b you are comparing a[0] and b[0] */ sort_space: function(a,b) { abytesunit = a[0].split(/\s+/); aa = unit_to_bytes(abytesunit[0],abytesunit[1]); if (isNaN(aa)) aa = 0; bbytesunit = b[0].split(/\s+/); bb = unit_to_bytes(bbytesunit[0],bbytesunit[1]); if (isNaN(bb)) bb = 0; return aa-bb; }, sort_numeric: function(a,b) { aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); if (isNaN(aa)) aa = 0; bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); if (isNaN(bb)) bb = 0; return aa-bb; }, sort_ip: function(a,b) { var aa = a[0].split(/\./); var bb = b[0].split(/\./); for (var i = 0; i < 4; i++) { var first = parseInt(aa[i]), second = parseInt(bb[i]); if (first < second) return -1; if (first > second) return 1; } return 0; }, sort_alpha: function(a,b) { if (a[0]==b[0]) return 0; if (a[0] 0 ) { var q = list[i]; list[i] = list[i+1]; list[i+1] = q; swap = true; } } // for t--; if (!swap) break; for(var i = t; i > b; --i) { if ( comp_func(list[i], list[i-1]) < 0 ) { var q = list[i]; list[i] = list[i-1]; list[i-1] = q; swap = true; } } // for b++; } // while(swap) } }; /* ****************************************************************** Supporting functions: bundled here to avoid depending on a library ****************************************************************** */ // Dean Edwards/Matthias Miller/John Resig /* for Mozilla/Opera9 */ if (document.addEventListener) { document.addEventListener("DOMContentLoaded", sorttable.init, false); } /* for Internet Explorer */ //NOTE: sorttable.js originally had a conditional comment here for IE, but //JS compressors eat conditional comments, so we've edited to use YUI. if ( YAHOO.env.ua.ie ) { document.write("