/* © 2011 by Markus Kwaśnicki */

/**
 * Class to provide filtering functionality to HTML tables. Each table needs to have the class "filterable" defined.
 * @param String CLASS
 */
function TableFilter(CLASS) {
    // Pseudo constructor
    {
        addEvent(window, "load", register, false);
    }
    // ---

    /**
     * Register filtering functionality to tables with the appropriate class. Assuming all tables are W3C compliant, e.g. table > (thead | tfoot | tbody) > (th | td)
     */
    function register() {
        var tables = getElementsByClassName(document, CLASS);
        
        for(var i = 0; i < tables.length; i++) {
            var thead = tables[i].getElementsByTagName("thead")[0];
            var ths = thead.getElementsByTagName("th");

            // Add filter widgets
            var trElement = document.createElement("tr");
            for(var j = 0; j < ths.length; j++) {
                var thElement = document.createElement("th");
                var inputElement = document.createElement("input");
                inputElement.setAttribute("type", "text");
                addEvent(inputElement, "keyup", tablefilter, false);
                thElement.appendChild(inputElement);
                trElement.appendChild(thElement);
            }
            thead.appendChild(trElement);
            // ---
        }
    }
    
    /**
     * Event handler for the filtering callback function.
     * @param Object event The fired event.
     */
    function tablefilter(event) {
        var eventSourceElement = getEventSourceElement(event);
        var tableId = eventSourceElement.parentNode.parentNode.parentNode.parentNode.id;   
        var columnIndex = eventSourceElement.parentNode.cellIndex; 
        var tableBody = document.getElementById(tableId).getElementsByTagName("tbody")[0];
        var tableRows = tableBody.getElementsByTagName("tr");
        var filterTerm = eventSourceElement.value;
        var pattern = null;
        
        // IE fails to compile some regular expressions
        try {
            pattern = new RegExp(filterTerm, "i");
        }
        catch(exception) {
            return; // Exit in such cases 
        }
        // ---
        
        // Reset all other filters but the active one
        var inputElements = eventSourceElement.parentNode.parentNode.getElementsByTagName("input");
        for(var i = 0; i < inputElements.length; i++) {
            if(columnIndex == i) {
                continue;
            }
            else {
                inputElements[i].value = new String();
            }
        }
        // ---
        
        for(var i = 0; i < tableRows.length; i++) {
            var tdElement = tableRows[i].getElementsByTagName("td")[columnIndex];
            var innerValue;
            
            if(tdElement.textContent)   // Firefox
            {
                innerValue = tdElement.textContent;
            }
            else    // Safari, Opera & IE
            {
                innerValue = tdElement.innerText;
            }
            
            if(!pattern.test(innerValue)) {
                tableRows[i].style.display = "none";
            }
            else {
                tableRows[i].style.display = "table-row";
            }
        }
    }
}

/**
 * Class to provide sorting functionality to HTML tables. Each table needs to have the class "sortable" defined and must have an ID defined also.
 * @param String CLASS
 */
function TableSort(CLASS) {
    var tableIdReferences = new Array();    // Each table stores the sorting precedence via its table id; false = ascending, true = descending
    var columnIndex = null; // Needed global temporarily
    var tableId = null; // Needed global temporarily

    // Pseudo constructor
    {
        addEvent(window, "load", register, false);
    }
    // ---

    /**
     * Register sorting functionality to tables with the appropriate class. Assuming all tables are W3C compliant, e.g. table > (thead | tfoot | tbody) > (th | td)
     */
    function register() {
        var tables = getElementsByClassName(document, CLASS);
        
        for(var i = 0; i < tables.length; i++) {
            var thead = tables[i].getElementsByTagName("thead")[0]; 
            var ths = thead.getElementsByTagName("tr")[0].getElementsByTagName("th");   // First table row only
            
            for(var j = 0; j < ths.length; j++) {
                addEvent(ths[j], "click", tablesort, false);
            }
        }
    }
    
    /**
     * Event handler for the sorting callback function.
     * @param Object event The fired event.
     */
    function tablesort(event) {
        var eventSourceElement = getEventSourceElement(event);
        tableId = eventSourceElement.parentNode.parentNode.parentNode.id;   // Defined globally
        columnIndex = eventSourceElement.cellIndex; // Defined globally
        var tableBody = document.getElementById(tableId).getElementsByTagName("tbody")[0];
        var tableRows = tableBody.getElementsByTagName("tr");
        var tmpSort = new Array();
        
        // Sort ascending or descending?
        if(!tableIdReferences[tableId]) {
            tableIdReferences[tableId] = new Array();
        }
        
        if(tableIdReferences[tableId][columnIndex] === undefined) {
            tableIdReferences[tableId][columnIndex] = false;
        }
        else {
            tableIdReferences[tableId][columnIndex] = !tableIdReferences[tableId][columnIndex];
        }
        // ---
        
        for(var i = 0; i < tableRows.length; i++) {
            tmpSort.push(tableRows[i]);
        }
        
        tmpSort.sort(sortByIndex);
        
        for(var i = 0; i < tableRows.length; i++) {
            tableBody.appendChild(tmpSort[i]);
        }
    }
    
    /**
     * Callback function for sorting the array with table rows. See JavaScript documentation for more information.
     * @param Object A
     * @param Object B
     * @return Integer
     */
    function sortByIndex(A, B) {
        var a = null;
        var b = null;
        
        if(A.getElementsByTagName("td")[columnIndex].firstChild)
            a = A.getElementsByTagName("td")[columnIndex].firstChild.nodeValue;
        else
            a = new String();
        if(B.getElementsByTagName("td")[columnIndex].firstChild)
            b = B.getElementsByTagName("td")[columnIndex].firstChild.nodeValue;
        else
            b = new String();
        
        var returnValue = a.localeCompare(b);
        
        if(tableIdReferences[tableId][columnIndex]) // ASC | DESC
        {
            returnValue *= -1;
        }
        
        return returnValue;
    }
}

new TableSort("sortable");
new TableFilter("filterable");

