248 lines
7.5 KiB
JavaScript
248 lines
7.5 KiB
JavaScript
/*!
|
||
* tsorter 2.0.0 - Copyright 2015 Terrill Dent, http://terrill.ca
|
||
* JavaScript HTML Table Sorter
|
||
* Released under MIT license, http://terrill.ca/sorting/tsorter/LICENSE
|
||
*/
|
||
var tsorter = (function()
|
||
{
|
||
'use strict';
|
||
|
||
var sorterPrototype,
|
||
addEvent,
|
||
removeEvent,
|
||
hasEventListener = !!document.addEventListener;
|
||
|
||
if( !Object.create ){
|
||
// Define Missing Function
|
||
Object.create = function( prototype ) {
|
||
var Obj = function(){return undefined;};
|
||
Obj.prototype = prototype;
|
||
return new Obj();
|
||
};
|
||
}
|
||
|
||
sorterPrototype = {
|
||
|
||
getCell: function(row)
|
||
{
|
||
var that = this;
|
||
return that.trs[row].cells[that.column];
|
||
},
|
||
|
||
/* SORT
|
||
* Sorts a particular column. If it has been sorted then call reverse
|
||
* if not, then use quicksort to get it sorted.
|
||
* Sets the arrow direction in the headers.
|
||
* @param oTH - the table header cell (<th>) object that is clicked
|
||
*/
|
||
sort: function( e )
|
||
{
|
||
var that = this,
|
||
th = e.target;
|
||
|
||
// TODO: make sure target 'th' is not a child element of a <th>
|
||
// We can't use currentTarget because of backwards browser support
|
||
// IE6,7,8 don't have it.
|
||
|
||
// set the data retrieval function for this column
|
||
that.column = th.cellIndex;
|
||
that.get = that.getAccessor( th.getAttribute('data-tsorter') );
|
||
|
||
if( that.prevCol === that.column )
|
||
{
|
||
// if already sorted, reverse
|
||
th.className = th.className !== 'descend' ? 'descend' : 'ascend';
|
||
that.reverseTable();
|
||
}
|
||
else
|
||
{
|
||
// not sorted - call quicksort
|
||
th.className = 'descend';
|
||
if( that.prevCol !== -1 && that.ths[that.prevCol].className !== 'exc_cell'){
|
||
that.ths[that.prevCol].className = '';
|
||
}
|
||
that.quicksort(0, that.trs.length);
|
||
}
|
||
that.prevCol = that.column;
|
||
},
|
||
|
||
/*
|
||
* Choose Data Accessor Function
|
||
* @param: the html structure type (from the data-type attribute)
|
||
*/
|
||
getAccessor: function(sortType)
|
||
{
|
||
var that = this,
|
||
accessors = that.accessors;
|
||
|
||
if( accessors && accessors[ sortType ] ){
|
||
return accessors[ sortType ];
|
||
}
|
||
|
||
switch( sortType )
|
||
{
|
||
case "link":
|
||
return function(row){
|
||
return that.getCell(row).firstChild.firstChild.nodeValue;
|
||
};
|
||
case "input":
|
||
return function(row){
|
||
return that.getCell(row).firstChild.value;
|
||
};
|
||
case "numeric":
|
||
return function(row){
|
||
return parseFloat( that.getCell(row).firstChild.nodeValue.replace(/\D/g,''), 10 );
|
||
};
|
||
case "number-range":
|
||
return function(row){
|
||
return parseFloat(
|
||
String(this.getCell(row).childNodes[0].nodeValue)
|
||
.split('–')[0]
|
||
.trim()
|
||
.replace('ƒ', '')
|
||
);
|
||
};
|
||
case "in-a-link":
|
||
return function(row){
|
||
return String(
|
||
this.getCell(row).getElementsByTagName('a')[0].childNodes[0].nodeValue
|
||
).toLowerCase();
|
||
};
|
||
default: /* Plain Text */
|
||
return function(row){
|
||
return that.getCell(row).firstChild.nodeValue.toLowerCase();
|
||
};
|
||
}
|
||
},
|
||
|
||
/*
|
||
* Exchange
|
||
* A complicated way of exchanging two rows in a table.
|
||
* Exchanges rows at index i and j
|
||
*/
|
||
exchange: function(i, j)
|
||
{
|
||
var that = this,
|
||
tbody = that.tbody,
|
||
trs = that.trs,
|
||
tmpNode;
|
||
|
||
if( i === j+1 ) {
|
||
tbody.insertBefore(trs[i], trs[j]);
|
||
} else if( j === i+1 ) {
|
||
tbody.insertBefore(trs[j], trs[i]);
|
||
} else {
|
||
tmpNode = tbody.replaceChild(trs[i], trs[j]);
|
||
if( !trs[i] ) {
|
||
tbody.appendChild(tmpNode);
|
||
} else {
|
||
tbody.insertBefore(tmpNode, trs[i]);
|
||
}
|
||
}
|
||
},
|
||
|
||
/*
|
||
* REVERSE TABLE
|
||
* Reverses a table ordering
|
||
*/
|
||
reverseTable: function()
|
||
{
|
||
var that = this,
|
||
i;
|
||
|
||
for( i = 1; i < that.trs.length; i++ ) {
|
||
that.tbody.insertBefore( that.trs[i], that.trs[0] );
|
||
}
|
||
},
|
||
|
||
/*
|
||
* QUICKSORT
|
||
* @param: lo - the low index of the array to sort
|
||
* @param: hi - the high index of the array to sort
|
||
*/
|
||
quicksort: function(lo, hi)
|
||
{
|
||
var i, j, pivot,
|
||
that = this;
|
||
|
||
if( hi <= lo+1 ){ return; }
|
||
|
||
if( (hi - lo) === 2 ) {
|
||
if(that.get(hi-1) > that.get(lo)) {
|
||
that.exchange(hi-1, lo);
|
||
}
|
||
return;
|
||
}
|
||
|
||
i = lo + 1;
|
||
j = hi - 1;
|
||
|
||
if( that.get(lo) > that.get( i) ){ that.exchange( i, lo); }
|
||
if( that.get( j) > that.get(lo) ){ that.exchange(lo, j); }
|
||
if( that.get(lo) > that.get( i) ){ that.exchange( i, lo); }
|
||
|
||
pivot = that.get(lo);
|
||
|
||
while(true) {
|
||
j--;
|
||
while(pivot > that.get(j)){ j--; }
|
||
i++;
|
||
while(that.get(i) > pivot){ i++; }
|
||
if(j <= i){ break; }
|
||
that.exchange(i, j);
|
||
}
|
||
that.exchange(lo, j);
|
||
|
||
if((j-lo) < (hi-j)) {
|
||
that.quicksort(lo, j);
|
||
that.quicksort(j+1, hi);
|
||
} else {
|
||
that.quicksort(j+1, hi);
|
||
that.quicksort(lo, j);
|
||
}
|
||
},
|
||
|
||
init: function( table, initialSortedColumn, customDataAccessors ){
|
||
var that = this,
|
||
i;
|
||
|
||
if( typeof table === 'string' ){
|
||
table = document.getElementById(table);
|
||
}
|
||
|
||
that.table = table;
|
||
that.ths = table.getElementsByTagName("th");
|
||
that.tbody = table.tBodies[0];
|
||
that.trs = that.tbody.getElementsByTagName("tr");
|
||
that.prevCol = ( initialSortedColumn && initialSortedColumn > 0 ) ? initialSortedColumn : -1;
|
||
that.accessors = customDataAccessors;
|
||
that.boundSort = that.sort.bind( that );
|
||
|
||
for( i = 0; i < that.ths.length; i++ ) {
|
||
that.ths[i].addEventListener('click', that.boundSort, false);
|
||
}
|
||
},
|
||
|
||
destroy: function(){
|
||
var that = this,
|
||
i;
|
||
|
||
if( that.ths ){
|
||
for( i = 0; i < that.ths.length; i++ ) {
|
||
that.ths[i].removeEventListener('click', that.boundSort, false);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
// Create a new sorter given a table element
|
||
return {
|
||
create: function( table, initialSortedColumn, customDataAccessors )
|
||
{
|
||
var sorter = Object.create( sorterPrototype );
|
||
sorter.init( table, initialSortedColumn, customDataAccessors );
|
||
return sorter;
|
||
}
|
||
};
|
||
}());
|