/**
 * Written by Neil Crosby. 
 * http://www.workingwith.me.uk/articles/scripting/standardista_table_sorting
 *
 * This module is based on Stuart Langridge's "sorttable" code.  Specifically, 
 * the determineSortFunction, sortCaseInsensitive, sortDate, sortNumeric, and
 * sortCurrency functions are heavily based on his code.  This module would not
 * have been possible without Stuart's earlier outstanding work.
 *
 * Use this wherever you want, but please keep this comment at the top of this file.
 *
 * Copyright (c) 2006 Neil Crosby
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal 
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
 * SOFTWARE.
 **/
/**
/* Modificato da Leo Bartoloni per gestire l'ordinamento in assenza di THEAD
/* in quanto l'elemento THEAD su Opera 8.54 crea problemi di visualizzazione
/* Modificato anche per non considerare l'indirizzo dei tag A per l'ordinamento
/* questo problema ? causato da FireFox
 **/
var initialrow=0;

var standardistaTableSorting = {

that: false,
isOdd: false,

sortColumnIndex : -1,
lastAssignedId : 0,
newRows: -1,
lastSortedTable: -1,

/**
 * Initialises the Standardista Table Sorting module
 **/
init : function() {
// first, check whether this web browser is capable of running this script
if (!document.getElementsByTagName) {
return;
}

this.that = this;

this.run();

},

/**
 * Runs over each table in the document, making it sortable if it has a class
 * assigned named "sortable" and an id assigned.
 **/
run : function() {
var tables = document.getElementsByTagName("table");

for (var i=0; i < tables.length; i++) {
var thisTable = tables[i];

if (css.elementHasClass(thisTable, 'sortable')) {
this.makeSortable(thisTable);
}
}
},

/**
 * Makes the given table sortable.
 **/
makeSortable : function(table) {

// first, check if the table has an id.  if it doesn't, give it one
if (!table.id) {
table.id = 'sortableTable'+this.lastAssignedId++;
}

// if this table does not have a thead, we don't want to know about it
if (!table.tHead || !table.tHead.rows || 0 == table.tHead.rows.length) {
var row = table.rows[0];
initialrow=1;
}
else {
// we'll assume that the last row of headings in the thead is the row that 
// wants to become clickable
var row = table.tHead.rows[table.tHead.rows.length - 1];
initialrow=0;
}

for (var i=0; i < row.cells.length; i++) {

// create a link with an onClick event which will 
// control the sorting of the table
var linkEl = createElement('a');
linkEl.href = '#';
linkEl.onclick = this.headingClicked;
linkEl.setAttribute('columnId', i);
linkEl.title = 'Click to sort';

// move the current contents of the cell that we're 
// hyperlinking into the hyperlink
var innerEls = row.cells[i].childNodes;
for (var j = 0; j < innerEls.length; j++) {
linkEl.appendChild(innerEls[j]);
}

// and finally add the new link back into the cell
row.cells[i].appendChild(linkEl);

var spanEl = createElement('span');
spanEl.className = 'tableSortArrow';
spanEl.appendChild(document.createTextNode('\u00A0\u00A0'));
row.cells[i].appendChild(spanEl);

}

if (css.elementHasClass(table, 'autostripe')) {
this.isOdd = false;
var rows = table.tBodies[0].rows;

// We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
for (var i=initialrow;i<rows.length;i++) { 
this.doStripe(rows[i]);
}
}
},

headingClicked: function(e) {

var that = standardistaTableSorting.that;

// linkEl is the hyperlink that was clicked on which caused
// this method to be called
var linkEl = getEventTarget(e);

// directly outside it is a td, tr, thead and table
var td     = linkEl.parentNode;
var tr     = td.parentNode;
var thead  = tr.parentNode;
var table  = thead.parentNode;

// if the table we're looking at doesn't have any rows
// (or only has one) then there's no point trying to sort it
if (!table.tBodies || table.tBodies[0].rows.length <= 1) {
return false;
}

// the column we want is indicated by td.cellIndex
var column = linkEl.getAttribute('columnId') || td.cellIndex;
//var column = td.cellIndex;

// find out what the current sort order of this column is
var arrows = css.getElementsByClass(td, 'tableSortArrow', 'span');
var previousSortOrder = '';
if (arrows.length > 0) {
previousSortOrder = arrows[0].getAttribute('sortOrder');
}

// work out how we want to sort this column using the data in the first cell
// but just getting the first cell is no good if it contains no data
// so if the first cell just contains white space then we need to track
// down until we find a cell which does contain some actual data
var itm = ''
var rowNum = initialrow;
while ('' == itm && rowNum < table.tBodies[0].rows.length) {
itm = that.getInnerText(table.tBodies[0].rows[rowNum].cells[column]);
rowNum++;
}
var sortfn = that.determineSortFunction(itm);

// if the last column that was sorted was this one, then all we need to 
// do is reverse the sorting on this column
if (table.id == that.lastSortedTable && column == that.sortColumnIndex) {
newRows = that.newRows;
newRows.reverse();
// otherwise, we have to do the full sort
} else {
that.sortColumnIndex = column;
var newRows = new Array();

for (var j = initialrow; j < table.tBodies[0].rows.length; j++) { 
newRows[j-initialrow] = table.tBodies[0].rows[j]; 
}

newRows.sort(sortfn);
}

that.moveRows(table, newRows);
that.newRows = newRows;
that.lastSortedTable = table.id;

// now, give the user some feedback about which way the column is sorted

// first, get rid of any arrows in any heading cells
var arrows = css.getElementsByClass(tr, 'tableSortArrow', 'span');
for (var j = 0; j < arrows.length; j++) {
var arrowParent = arrows[j].parentNode;
arrowParent.removeChild(arrows[j]);

if (arrowParent != td) {
spanEl = createElement('span');
spanEl.className = 'tableSortArrow';
spanEl.appendChild(document.createTextNode('\u00A0\u00A0'));
arrowParent.appendChild(spanEl);
}
}

// now, add back in some feedback 
var spanEl = createElement('span');
spanEl.className = 'tableSortArrow';
if (null == previousSortOrder || '' == previousSortOrder || 'DESC' == previousSortOrder) {
//spanEl.appendChild(document.createTextNode(' \u2191'));
spanEl.appendChild(document.createTextNode(' \u25B2'));
spanEl.setAttribute('sortOrder', 'ASC');
} else {
//spanEl.appendChild(document.createTextNode(' \u2193'));
spanEl.appendChild(document.createTextNode(' \u25BC'));
spanEl.setAttribute('sortOrder', 'DESC');
}

td.appendChild(spanEl);

return false;
},

getInnerText : function(el) {

if ('string' == typeof el || 'undefined' == typeof el) {
return el;
}

if (el.innerText) {
return el.innerText;  // Not needed but it is faster
}

var str = el.getAttribute('standardistaTableSortingInnerText');
if (null != str && '' != str) {
return str;
}
str = '';
var cs = el.childNodes;
var l = cs.length;
for (var i = 0; i < l; i++) {
// 'if' is considerably quicker than a 'switch' statement, 
// in Internet Explorer which translates up to a good time 
// reduction since this is a very often called recursive function
if (1 == cs[i].nodeType) { // ELEMENT NODE
if(cs[i].tagName=="A"){
if (this.getInnerText(cs[i])!=""){
str += this.getInnerText(cs[i]);
break;
}
} else {
str += this.getInnerText(cs[i]);
break;
}
} else if (3 == cs[i].nodeType) { //TEXT_NODE
str += cs[i].nodeValue;
break;
}
}

// set the innertext for this element directly on the element
// so that it can be retrieved early next time the innertext
// is requested
el.setAttribute('standardistaTableSortingInnerText', str);

return str;
},

determineSortFunction : function(itm) {

var sortfn = this.sortCaseInsensitive;

if (itm.match(/^\d\d[\/-]\d\d[\/-]\d\d\d\d$/)) {
sortfn = this.sortDate;
}
if (itm.match(/^\d\d[\/-]\d\d[\/-]\d\d$/)) {
sortfn = this.sortDate;
}
if (itm.match(/^[??$]/)) {
sortfn = this.sortCurrency;
}
if (itm.match(/^\d?\.?\d+$/)) {
sortfn = this.sortNumeric;
}
if (itm.match(/^[+-]?\d*\.?\d+([eE]-?\d+)?$/)) {
sortfn = this.sortNumeric;
}
    if (itm.match(/^([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$/)) {
        sortfn = this.sortIP;
   }

return sortfn;
},

sortCaseInsensitive : function(a, b) {
var that = standardistaTableSorting.that;

var aa = that.getInnerText(a.cells[that.sortColumnIndex]).toLowerCase();
var bb = that.getInnerText(b.cells[that.sortColumnIndex]).toLowerCase();
if (aa==bb) {
return 0;
} else if (aa<bb) {
return -1;
} else {
return 1;
}
},

sortDate : function(a,b) {
var that = standardistaTableSorting.that;

// y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
var aa = that.getInnerText(a.cells[that.sortColumnIndex]);
var bb = that.getInnerText(b.cells[that.sortColumnIndex]);

var dt1, dt2, yr = -1;

if (aa.length == 10) {
dt1 = aa.substr(6,4)+aa.substr(3,2)+aa.substr(0,2);
} else {
yr = aa.substr(6,2);
if (parseInt(yr) < 50) { 
yr = '20'+yr; 
} else { 
yr = '19'+yr; 
}
dt1 = yr+aa.substr(3,2)+aa.substr(0,2);
}

if (bb.length == 10) {
dt2 = bb.substr(6,4)+bb.substr(3,2)+bb.substr(0,2);
} else {
yr = bb.substr(6,2);
if (parseInt(yr) < 50) { 
yr = '20'+yr; 
} else { 
yr = '19'+yr; 
}
dt2 = yr+bb.substr(3,2)+bb.substr(0,2);
}

if (dt1==dt2) {
return 0;
} else if (dt1<dt2) {
return -1;
}
return 1;
},

sortCurrency : function(a,b) { 
var that = standardistaTableSorting.that;

var aa = that.getInnerText(a.cells[that.sortColumnIndex]).replace(/[^0-9.]/g,'');
var bb = that.getInnerText(b.cells[that.sortColumnIndex]).replace(/[^0-9.]/g,'');
return parseFloat(aa) - parseFloat(bb);
},

sortNumeric : function(a,b) { 
var that = standardistaTableSorting.that;

var aa = parseFloat(that.getInnerText(a.cells[that.sortColumnIndex]));
if (isNaN(aa)) { 
aa = 0;
}
var bb = parseFloat(that.getInnerText(b.cells[that.sortColumnIndex])); 
if (isNaN(bb)) { 
bb = 0;
}
return aa-bb;
},

makeStandardIPAddress : function(val) {
var vals = val.split('.');

for (x in vals) {
val = vals[x];

while (3 > val.length) {
val = '0'+val;
}
vals[x] = val;
}

val = vals.join('.');

return val;
},

sortIP : function(a,b) { 
var that = standardistaTableSorting.that;

var aa = that.makeStandardIPAddress(that.getInnerText(a.cells[that.sortColumnIndex]).toLowerCase());
var bb = that.makeStandardIPAddress(that.getInnerText(b.cells[that.sortColumnIndex]).toLowerCase());
if (aa==bb) {
return 0;
} else if (aa<bb) {
return -1;
} else {
return 1;
}
},

moveRows : function(table, newRows) {
this.isOdd = false;

// We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
for (var i=initialrow;i<newRows.length;i++) { 
var rowItem = newRows[i];

this.doStripe(rowItem);

table.tBodies[0].appendChild(rowItem); 
}
},

doStripe : function(rowItem) {
if (this.isOdd) {
css.addClassToElement(rowItem, 'odd');
} else {
css.removeClassFromElement(rowItem, 'odd');
}

this.isOdd = !this.isOdd;
}

}

function standardistaTableSortingInit() {
standardistaTableSorting.init();
}

