/** TableSorter class. Original Author: Manos Batsis, mailto:xcircuit@yahoo.com Hacked by Joe Fritz (mailto:jfritz@steptools.com) Dec 18, 2002 for the following changes: Copy the entire DOM elements in the TD, and not just the text Added numeric sort type, and modes date types to 3 and 4 moved initTable to this file, to enable simplified HTML code that uses this JS. Added SortableObject structure, to eliminate concatenating and parsing the row from the sorted item. Removed support for javascript arrays. It now only deals with DOM tables. main methods: sortOn(column to base the sort, button/header matching the column) setSortFuncs(array of inegers. used to set the sort function for each column: 0 for default, 1 for case-insensitive, 2 for numeric, 3 for date (US), 4 for date (european)) autoCalled: readTable(table object) reads a table Simple interface: Load the file: <HEAD> <script type="text/javascript" src="TableSorter.js"></script> </HEAD> Assign a TABLE element to a instance of TableSorter (named sorter) : <BODY onload="sorter = initTable('rsTable', [1,2,0])" > Use the table: <TABLE id="rsTable"> <TBODY> <TR> <TH> <A HREF="javascript:sortIt(sorter, 0)">Col 1</A></TH> <TH> <A HREF="javascript:sortIt(sorter, 1)">Col 2</A></TH> <TH> <A HREF="javascript:sortIt(sorter, 2)">Col 3</A></TH> </TR> <TR><TD><A HREF="xx">foo</A></TD><TD>120</TD><TD>FUNCTION</TD></TR> <TR><TD>ybar</TD><TD>04</TD><TD>METHOD</TD></TR> <TR><TD>xxx.yyy</TD><TD>24</TD><TD>ATTRIBTE</TD></TR> </TBODY> </TABLE> */ function TableSorter(table, skipRowOne) { if(skipRowOne && skipRowOne == 1) this.skipRowOne = 1; else this.skipRowOne = 0; this.matrix = this.readTable(table); if (!this.matrix) return null; this.lastColIndex = -1; this.currentColIndex = -1; this.arrSortFuncs = new Array(); this.arrowDirection = null; if (table.nodeName != "TABLE") { throw ("Element must be a table is "+table.nodeName); } this.element = table; } TableSorter.prototype.sortOn = function(iColIndex) { this.currentColIndex = iColIndex; var temp = new Array(this.matrix.length); for(var i=0;i<temp.length;i++) { temp[i] = new SortableObject(textContent(this.matrix[i][iColIndex]),i); } if(this.lastColIndex == iColIndex) { this.arrowDirection = !(this.arrowDirection); temp.reverse(); } else { this.Arrowdirection = true; var iFunc = this.arrSortFuncs[iColIndex]; if(iFunc == 0) temp.sort(this.caseFunc); else if(iFunc == 1)temp.sort(this.noCaseFunc); else if(iFunc == 2)temp.sort(this.numericFunc); else if(iFunc == 3)temp.sort(this.dateFunc); else if(iFunc == 4)temp.sort(this.dateEUFunc); } var tempMatrix = new Array(this.matrix.length); for(var j=0; j < tempMatrix.length; j++) { var iRow = temp[j].idx; if (j+1 < temp.length && temp[j].key == temp[j+1].key) { // We have multiple copies of the same key, so we keep things in // the old order so that things remain sorted var key = temp[j].key; for (var k=0; k<this.matrix.length; k++) { var cmp = textContent(this.matrix[k][iColIndex]); if (cmp == key) { tempMatrix[j] = new Array(); for (var m=0; m<this.matrix[k].length; m++) { tempMatrix[j][m] = this.matrix[k][m]; } j++; } } j--; } else { tempMatrix[j] = new Array(); for(var k=0; k < this.matrix[j].length; k++) tempMatrix[j][k] = this.matrix[iRow][k]; } } this.matrix = tempMatrix; this.lastColIndex = iColIndex; this.updateTblCells(this.element); } TableSorter.prototype.setSortFuncs = function(arr) { for(var i=0;i<arr.length;i++) this.arrSortFuncs[i] = arr[i]; } TableSorter.prototype.caseFunc = function(a, b) { var strA = a.key; var strB = b.key; if(strA < strB) return -1; else if(strA > strB) return 1; else return 0; } TableSorter.prototype.noCaseFunc = function(a, b) { var strA = a.key.toLowerCase(); var strB = b.key.toLowerCase(); if(strA < strB) return -1; else if(strA > strB) return 1; else return 0; } TableSorter.prototype.numericFunc = function(a, b) { return a.key-b.key; } TableSorter.prototype.dateFunc = function(a, b) { var datA = new Date(a.key); var datB = new Date(b.key); if(datA < datB) return -1; else if(datA > datB) return 1; else return 0; } TableSorter.prototype.dateEUFunc = function(a, b) { var strA = a.key.split("/"), strB = b.key.split("/"), datA = new Date(strA[2], strA[1], strA[0]), datB = new Date(strB[2], strB[1], strB[0]); if(datA < datB) return -1; else if(datA > datB) return 1; else return 0; } TableSorter.prototype.readTable = function(elem) { if (!elem) return null; if(elem.nodeName != "tbody") elem = elem.getElementsByTagName("tbody")[0]; if(!elem) { throw ("No tbody element"); } var iRows = elem.getElementsByTagName("tr"); var arrX = new Array(); for(var i=0; i+this.skipRowOne < iRows.length; i++) { arrX[i] = new Array(); var iCols = iRows[i+this.skipRowOne].getElementsByTagName("td"); for(var j=0; j < iCols.length; j++) { arrX[i][j] = iCols[j].cloneNode(true); } } return arrX; } TableSorter.prototype.updateTblCells = function(elem) { if(elem.nodeName != "tbody") elem = elem.getElementsByTagName("tbody")[0]; if(!elem) return var iRows = elem.getElementsByTagName("tr"); for(var i=0; i+this.skipRowOne < iRows.length; i++) { var row = iRows[i+this.skipRowOne]; var iCols = row.getElementsByTagName("td"); for(var j=0; j < iCols.length; j++) { replaceContent(iCols[j], this.matrix[i][j]); } } } //---------------------------------------------------------------- // The element of the array to be sorted function SortableObject (key, idx) { this.key = key; this.idx = idx; } //---------------------------------------------------------------- // Utility functions function textContent(node) { var ret = ""; var kids = node.childNodes.length; for (var i=0; i<kids; i++) { var n = node.childNodes[i]; if (n.nodeName == "#text") { ret += n.data; } else { ret += textContent(n); } } return ret; } // Replace all the child elements of dest with the children of src function replaceContent(dest, src) { var child; // Empty the destination while (child = dest.firstChild) { // alert ("Removing "+child); dest.removeChild(child); } for (var i=0; i<src.childNodes.length; i++) { dest.appendChild(src.childNodes[i].cloneNode(true)); } } // This function should be called from the onload attribute in the body. // id parameter identifies the table to be sorted, // sort_by parameter is an array of values defining the sort algorithm to use function initTable(id, sort_by) { // alert ("Loading table"); var elem = document.getElementById(id); if (!elem) return null; // 1 means "omit 1st row (headers)" var sorter = new TableSorter(elem, 1); sorter.setSortFuncs(sort_by); return sorter; } function sortIt(tbl, iColIndex) { tbl.sortOn(iColIndex); }