/*

        TableSort revisited v2.9 by frequency-decoder.com



        Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)



        Please credit frequency decoder in any derivative work - thanks

        

        You are free:



        * to copy, distribute, display, and perform the work

        * to make derivative works

        * to make commercial use of the work



        Under the following conditions:



                by Attribution.

                --------------

                You must attribute the work in the manner specified by the author or licensor.



                sa

                --

                Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.



        * For any reuse or distribution, you must make clear to others the license terms of this work.

        * Any of these conditions can be waived if you get permission from the copyright holder.

*/



var fdTableSort = {



        regExp_Currency:        /^[£$€¥¤]/,

        regExp_Number:          /^(\-)?[0-9]+(\.[0-9]*)?$/,

        pos:                    -1,

        uniqueHash:             1,

        thNode:                 null,

        tableCache:             {},

        tableId:                null,



        addEvent: function(obj, type, fn) {

                if( obj.attachEvent ) {

                        obj["e"+type+fn] = fn;

                        obj[type+fn] = function(){obj["e"+type+fn]( window.event );}

                        obj.attachEvent( "on"+type, obj[type+fn] );

                } else

                        obj.addEventListener( type, fn, false );

        },



        stopEvent: function(e) {

                e = e || window.event;



                if(e.stopPropagation) {

                        e.stopPropagation();

                        e.preventDefault();

                }

                /*@cc_on@*/

                /*@if(@_win32)

                e.cancelBubble = true;

                e.returnValue = false;

                /*@end@*/

                return false;

        },



        init: function() {

                if (!document.getElementsByTagName) return;



                var tables = document.getElementsByTagName('table');

                var sortable, headers, thtext, aclone, a, span, columnNum, noArrow, reverseSortOnInit;



                a               = document.createElement("a");

                a.href          = "#";

                a.onkeypress    = fdTableSort.keyWrapper;



                span            = document.createElement("span");



                for(var t = 0, tbl; tbl = tables[t]; t++) {

                

                        headers = fdTableSort.getTableHeaders(tbl);

                        

                        sortable  = false;

                        columnNum = tbl.className.search(/sortable-onload-([0-9]+)/) != -1 ? parseInt(tbl.className.match(/sortable-onload-([0-9]+)/)[1]) - 1 : -1;

                        showArrow = tbl.className.search(/no-arrow/) == -1;

                        reverse   = tbl.className.search(/sortable-onload-([0-9]+)-reverse/) != -1;

                        

                        // Remove any old dataObj for this table (tables created from an ajax callback require this)

                        if(tbl.id && tbl.id in fdTableSort.tableCache) delete fdTableSort.tableCache[tbl.id];



                        var colCnt = -1;

                        

                        for (var z=0, th; th = headers[z]; z++) {

                        

                                colCnt += th.getAttribute("colspan") ? Number(th.getAttribute("colspan")) : 1;

                                

                                if(th.className && th.className.match('sortable') && (!th.getAttribute("colspan") || th.getAttribute("colspan") == 1)) {



                                        // Remove previously applied classes for the ajaxers also

                                        th.className = th.className.replace(/forwardSort|reverseSort/, "");



                                        if(colCnt == columnNum) sortable = th;

                                        thtext = fdTableSort.getInnerText(th);



                                        while(th.firstChild) th.removeChild(th.firstChild);



                                        // Create the link

                                        aclone = a.cloneNode(true);

                                        aclone.appendChild(document.createTextNode(thtext));

                                        aclone.title = "Sort on " + thtext;

                                        a.onclick = th.onclick = fdTableSort.clickWrapper;

                                        th.appendChild(aclone);



                                        // Add the span if needs be

                                        if(showArrow) th.appendChild(span.cloneNode(false));



                                        var cn = "fd-column-" + colCnt;

                                        th.className = th.className.replace(/fd-identical|fd-not-identical/, "").replace(/fd-column-([0-9]+)/, "") + " " + cn;

                                        fdTableSort.disableSelection(th);

                                };

                        };



                        if(sortable) {

                                fdTableSort.thNode = sortable;

                                fdTableSort.initSort();

                                if(reverse) {

                                        fdTableSort.thNode = sortable;

                                        fdTableSort.initSort();

                                };

                        };

                };

        },

        

        disableSelection: function(element) {

                element.onselectstart = function() {

                        return false;

                };

                element.unselectable = "on";

                element.style.MozUserSelect = "none";

        },

        

        getTableHeaders: function(tbl) {

                var headers;

                var thead = tbl.getElementsByTagName('thead');



                if(thead && thead.length) {

                        thead = thead[0];

                        headers = thead.getElementsByTagName('tr');

                        headers = headers[headers.length - 1].getElementsByTagName('th');

                } else {

                        headers = tbl.getElementsByTagName('th');

                }

                return headers;

        },

        

        countColumns: function(thList) {

                var colCnt = 0;

                for(var i = 0, th; th = thList[i]; i++) {

                        colCnt += th.getAttribute("colspan") ? Number(th.getAttribute("colspan")) : 1;

                };



                return colCnt;

        },

        

        getTH: function(thList, pos) {

                var thList = fdTableSort.thNode.parentNode.getElementsByTagName("th");



                for(var i = 0, th; th = thList[i]; i++) {

                        var re = new RegExp( "(fd-column-" + pos + ")([^0-9]+)" );

                        if(th.className.search(re) != -1) return th;

                };

        },

        

        clickWrapper: function(e) {

                e = e || window.event;

                if(fdTableSort.thNode == null) {

                        fdTableSort.thNode = this;

                        fdTableSort.addSortActiveClass();

                        setTimeout("fdTableSort.initSort()",5);



                };

                return fdTableSort.stopEvent(e);

        },



        keyWrapper: function(e) {

                e = e || window.event;

                var kc = e.keyCode != null ? e.keyCode : e.charCode;

                if(kc == 13) {

                        var targ = this;

                        while(targ.tagName.toLowerCase() != "th") targ = targ.parentNode;



                        fdTableSort.thNode = targ;

                        fdTableSort.addSortActiveClass();

                        setTimeout("fdTableSort.initSort()",5);



                        return fdTableSort.stopEvent(e);

                };

                return true;

        },



        jsWrapper: function(tableid, colNum) {

                var table = document.getElementById(tableid);

                fdTableSort.thNode = fdTableSort.getTH(table.getElementsByTagName('th'), colnum);

                if(!fdTableSort.thNode || fdTableSort.thNode.className.search(/fd-column/) == -1) return false;

                fdTableSort.addSortActiveClass();

                fdTableSort.initSort();

        },



        addSortActiveClass: function() {

                if(fdTableSort.thNode == null) return;

                fdTableSort.addClass(fdTableSort.thNode, "sort-active");

                fdTableSort.addClass(document.getElementsByTagName('body')[0], "sort-active");

                if("sortInitiatedCallback" in window) {

                        var tableElem   = fdTableSort.thNode;

                        while(tableElem.tagName.toLowerCase() != 'table' && tableElem.parentNode) {

                                tableElem = tableElem.parentNode;

                        };

                        sortInitiatedCallback(tableElem.id);

                };

        },



        removeSortActiveClass: function() {

                fdTableSort.removeClass(fdTableSort.thNode, "sort-active");

                fdTableSort.removeClass(document.getElementsByTagName('body')[0], "sort-active");

                if("sortCompleteCallback" in window) {

                        var tableElem   = fdTableSort.thNode;

                        while(tableElem.tagName.toLowerCase() != 'table' && tableElem.parentNode) {

                                tableElem = tableElem.parentNode;

                        };

                        sortCompleteCallback(tableElem.id);

                };

        },



        addClass: function(e,c) {

                if(new RegExp("(^|\\s)" + c + "(\\s|$)").test(e.className)) return;

                e.className += ( e.className ? " " : "" ) + c;

        },



        removeClass: function(e,c) {

                e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s*\\b[^-])"+c+"($|\\b(?=[^-]))", "g"), "");

        },



        prepareTableData: function(table) {

                // Create a table id if needs be

                if(!table.id) table.id = "fd-table-" + fdTableSort.uniqueHash++;



                var data = [];



                var start = table.getElementsByTagName('tbody');

                start = start.length ? start[0] : table;



                var trs = start.getElementsByTagName('tr');

                var ths = fdTableSort.getTableHeaders(table);



                var numberOfRows = trs.length;

                var numberOfCols = fdTableSort.countColumns(ths);



                var data = [];

                var identical = new Array(numberOfCols);

                var identVal  = new Array(numberOfCols);



                var tr, td, th, txt, tds, col, row;



                var rowCnt = 0;



                // Start to create the 2D matrix of data

                for(row = 0; row < numberOfRows; row++) {



                        tr              = trs[row];



                        // Have we any th tags or are we in a tfoot ?

                        if(tr.getElementsByTagName('th').length > 0 || (tr.parentNode && tr.parentNode.tagName.toLowerCase() == "tfoot")) continue;



                        data[rowCnt]    = [];

                        tds             = tr.getElementsByTagName('td');

                        col             = -1;



                        for(var tmp = 0, th; th = ths[tmp]; tmp++) {



                                col += th.getAttribute("colspan") ? Number(th.getAttribute("colspan")) : 1;



                                if(th.className.search(/sortable/) == -1 || (th.getAttribute("colspan") && Number(th.getAttribute("colspan")) > 1)) continue;



                                td  = tds[col];



                                txt = fdTableSort.getInnerText(td) + " ";



                                txt = txt.replace(/^\s+/,'').replace(/\s+$/,'');



                                if(th.className.search(/sortable-date/) != -1) {

                                        txt = fdTableSort.dateFormat(txt, th.className.search(/sortable-date-dmy/) != -1);

                                } else if(th.className.search(/sortable-numeric|sortable-currency/) != -1) {

                                        txt = parseFloat(txt.replace(/[^0-9\.\-]/g,''));

                                        if(isNaN(txt)) txt = "";

                                } else if(th.className.search(/sortable-text/) != -1) {

                                        txt = txt.toLowerCase();

                                } else if(th.className.search(/sortable-([a-zA-Z\_]+)/) != -1) {

                                        if((th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "PrepareData") in window) {

                                                txt = window[th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "PrepareData"](td, txt);

                                        };

                                } else {

                                        if(txt != "") {

                                                fdTableSort.removeClass(th, "sortable");

                                                if(fdTableSort.dateFormat(txt) != 0) {

                                                        fdTableSort.addClass(th, "sortable-date");

                                                        txt = fdTableSort.dateFormat(txt);

                                                } else if(txt.search(fdTableSort.regExp_Number) != -1 || txt.search(fdTableSort.regExp_Currency) != -1) {

                                                        fdTableSort.addClass(th, "sortable-numeric");

                                                        txt = parseFloat(txt.replace(/[^0-9\.\-]/g,''));

                                                        if(isNaN(txt)) txt = "";

                                                } else {

                                                        fdTableSort.addClass(th, "sortable-text");

                                                        txt = txt.toLowerCase();

                                                };

                                        };

                                };



                                if(rowCnt > 0 && identVal[col] != txt) {

                                        identical[col] = false;

                                };



                                identVal[col]     = txt;

                                data[rowCnt][col] = txt;

                        };



                        // Add the tr for this row

                        data[rowCnt][numberOfCols] = tr;



                        // Increment the row count

                        rowCnt++;

                }



                // Get the row and column styles

                var colStyle = table.className.search(/colstyle-([\S]+)/) != -1 ? table.className.match(/colstyle-([\S]+)/)[1] : false;

                var rowStyle = table.className.search(/rowstyle-([\S]+)/) != -1 ? table.className.match(/rowstyle-([\S]+)/)[1] : false;



                // Cache the data object for this table

                fdTableSort.tableCache[table.id] = { data:data, pos:-1, identical:identical, colStyle:colStyle, rowStyle:rowStyle, noArrow:table.className.search(/no-arrow/) != -1 };

        },



        initSort: function() {



                var span;

                var thNode      = fdTableSort.thNode;



                // Get the table

                var tableElem   = fdTableSort.thNode;

                while(tableElem.tagName.toLowerCase() != 'table' && tableElem.parentNode) {

                        tableElem = tableElem.parentNode;

                };



                // If this is the first time that this table has been sorted, create the data object

                if(!tableElem.id || !(tableElem.id in fdTableSort.tableCache)) {

                        fdTableSort.prepareTableData(tableElem);

                };



                // Cache the table id

                fdTableSort.tableId = tableElem.id;



                // Get the column position using the className added earlier

                fdTableSort.pos = thNode.className.match(/fd-column-([0-9]+)/)[1];



                // Grab the data object for this table

                var dataObj     = fdTableSort.tableCache[tableElem.id];



                // Get the position of the last column that was sorted

                var lastPos     = dataObj.pos;



                // Get the stored data object for this table

                var data        = dataObj.data;

                var colStyle    = dataObj.colStyle;

                var rowStyle    = dataObj.rowStyle;

                var len1        = data.length;

                var len2        = data[0].length - 1;

                var identical   = dataObj.identical[fdTableSort.pos] == false ? false : true;

                var noArrow     = dataObj.noArrow;



                if(lastPos != fdTableSort.pos && lastPos != -1) {

                        var th = fdTableSort.getTH(thNode.parentNode.getElementsByTagName('th'), lastPos);



                        fdTableSort.removeClass(th, "forwardSort");

                        fdTableSort.removeClass(th, "reverseSort");

                        if(!noArrow) {

                                // Remove arrow

                                span = th.getElementsByTagName('span')[0];

                                while(span.firstChild) span.removeChild(span.firstChild);

                        };

                };



                // If the same column is being sorted then just reverse the data object contents.

                var classToAdd = "forwardSort";



                if(lastPos == fdTableSort.pos && !identical) {

                        data.reverse();

                        classToAdd = thNode.className.search(/reverseSort/) != -1 ? "forwardSort" : "reverseSort";

                } else {

                        fdTableSort.tableCache[tableElem.id].pos = fdTableSort.pos;

                        if(!identical) {

                                if(thNode.className.match(/sortable-numeric|sortable-currency|sortable-date/)) {

                                        data.sort(fdTableSort.sortNumeric);

                                } else if(thNode.className.match('sortable-text')) {

                                        data.sort(fdTableSort.sortText);

                                } else if(thNode.className.search(/sortable-([a-zA-Z\_]+)/) != -1 && thNode.className.match(/sortable-([a-zA-Z\_]+)/)[1] in window) {

                                        data.sort(window[thNode.className.match(/sortable-([a-zA-Z\_]+)/)[1]]);

                                };

                        };

                };



                fdTableSort.removeClass(thNode, "forwardSort");

                fdTableSort.removeClass(thNode, "reverseSort");

                fdTableSort.addClass(thNode, classToAdd);



                if(!noArrow) {

                        var arrow = thNode.className.search(/forwardSort/) != -1 ? " \u2193" : " \u2191";

                        span = thNode.getElementsByTagName('span')[0];

                        while(span.firstChild) span.removeChild(span.firstChild);

                        span.appendChild(document.createTextNode(arrow));

                };



                if(!rowStyle && !colStyle && identical) {

                        fdTableSort.removeSortActiveClass();

                        fdTableSort.thNode = null;

                        return;

                }



                var hook = tableElem.getElementsByTagName('tbody');

                hook = hook.length ? hook[0] : tableElem;



                var td, tr;



                for(var i = 0; i < len1; i++) {

                        tr = data[i][len2];

                        if(colStyle) {

                                if(lastPos != -1) {

                                        fdTableSort.removeClass(tr.getElementsByTagName('td')[lastPos], colStyle);

                                }

                                fdTableSort.addClass(tr.getElementsByTagName('td')[fdTableSort.pos], colStyle);

                        };

                        if(!identical) {



                                if(rowStyle) {

                                        if(i % 2) fdTableSort.addClass(tr, rowStyle);

                                        else fdTableSort.removeClass(tr, rowStyle);

                                };



                                hook.removeChild(tr); /* Netscape 8.1.2 requires the removeChild call */

                                hook.appendChild(tr);

                        };

                };

                fdTableSort.removeSortActiveClass();

                fdTableSort.thNode = null;

        },



        getInnerText: function(el) {

                if (typeof el == "string" || typeof el == "undefined") return el;

                if(el.innerText) return el.innerText;



                var txt = '', i;

                for (i = el.firstChild; i; i = i.nextSibling) {

                        if (i.nodeType == 3)            txt += i.nodeValue;

                        else if (i.nodeType == 1)       txt += fdTableSort.getInnerText(i);

                };



                return txt;

        },

        

        dateFormat: function(dateIn, favourDMY) {

                var dateTest = [

                        { regExp:/^(0[1-9]|1[012])([- \/.])(0[1-9]|[12][0-9]|3[01])([- \/.])(\d\d?\d\d)$/, d:3, m:1, y:5 },  // mdy

                        { regExp:/^(0[1-9]|[12][0-9]|3[01])([- \/.])(0[1-9]|1[012])([- \/.])(\d\d?\d\d)$/, d:1, m:3, y:5 },  // dmy

                        { regExp:/^(\d\d?\d\d)([- \/.])(0[1-9]|1[012])([- \/.])(0[1-9]|[12][0-9]|3[01])$/, d:5, m:3, y:1 }   // ymd

                        ];



                var start;

                var cnt = 0;



                while(cnt < 3) {

                        start = (cnt + (favourDMY ? 4 : 3)) % 3;



                        if(dateIn.match(dateTest[start].regExp)) {

                                res = dateIn.match(dateTest[start].regExp);

                                y = res[dateTest[start].y];

                                m = res[dateTest[start].m];

                                d = res[dateTest[start].d];

                                if(m.length == 1) m = "0" + m;

                                if(d.length == 1) d = "0" + d;

                                if(y.length != 4) y = (parseInt(y) < 50) ? '20' + y : '19' + y;



                                return y+m+d;

                        }



                        cnt++;

                }



                return 0;

        },



        sortDate: function(a,b) {

                var aa = a[fdTableSort.pos];

                var bb = b[fdTableSort.pos];



                return aa - bb;

        },



        sortNumeric:function (a,b) {

                var aa = a[fdTableSort.pos];

                var bb = b[fdTableSort.pos];



                if(aa === "" && !isNaN(bb)) return -1;

                else if(bb === "" && !isNaN(aa)) return 1;

                else if(aa == bb) return 0;



                return aa - bb;

        },



        sortText:function (a,b) {

                var aa = a[fdTableSort.pos];

                var bb = b[fdTableSort.pos];



                if(aa == bb) return 0;

                if(aa < bb)  return -1;



                return 1;

        }

};



fdTableSort.addEvent(window, "load", fdTableSort.init);



