/*

This jQuery plugin is learned from https://www.open2space.com/projects/fixedtable.
We have improved it, and fixed some bugs.

Example:

$(document).ready(function() {
    $(".tableDiv").each(function() {
        var Id = $(this).get(0).id;
        var maintbheight = 555;
        var maintbwidth = 760;

        $("#" + Id + " .FixedTables").fixedTable({
            width: maintbwidth,
            height: maintbheight,
            fixedColumns: 1,
            classHeader: "fixedHead",
            classFooter: "fixedFoot",
            classColumn: "fixedColumn",
            fixedColumnWidth: 150,
            outerId: Id,
            backcolor: "#FFFFFF",
            hovercolor: "#99CCFF"
        });
    });
});

*/

(function($) {
    // ***********************************************
    //The main fixedTable function
    $.fn.fixedTable = function(opts) {
        //default options defined in $.fn.fixedTable.defaults - at the bottom of this file.
        var options = $.extend({}, $.fn.fixedTable.defaults, opts);
        var mainid = "#" + options.outerId;
        var tbl = this;
        var layout = buildLayout(tbl, opts);
        //see the buildLayout() function below

        //we need to set the width (in pixels) for each of the tables in the fixedContainer area
        //but, we need to subtract the width of the fixedColumn area.
        var w = options.width - options.fixedColumnWidth;
        //sanity check
        if (w <= 0) { w = options.width; }

        $(".fixedContainer", layout).width(w);

        $(".fixedContainer ." + options.classHeader, layout).css({
            width: (w) + "px",
            "float": "left",
            "overflow": "hidden"
        });

        $(".fixedContainer .fixedTable", layout).css({
            "float": "left",
            width: (w + 16) + "px",
            "overflow": "auto"
        });
        $(".fixedContainer", layout).css({
            width: w - 1,
            "float": "left"
        });    //adjust the main container to be just larger than the fixedTable

        $(".fixedContainer ." + options.classFooter, layout).css({
            width: (w) + "px",
            "float": "left",
            "overflow": "hidden"
        });
        $("." + options.classColumn + " > .fixedTable", layout).css({
            "width": options.fixedColumnWidth,
            "overflow": "hidden",
            "border-collapse": $(tbl).css("border-collapse"),
            "padding": "0"
        });

        $("." + options.classColumn, layout).width(options.fixedColumnWidth);
        $("." + options.classColumn, layout).height(options.height);
        $("." + options.classColumn + " ." + options.classHeader + " table tbody tr td", layout).width(options.fixedColumnWidth);
        $("." + options.classColumn + " ." + options.classFooter + " table tbody tr td", layout).width(options.fixedColumnWidth);

        //adjust the table widths in the fixedContainer area
        var fh = $(".fixedContainer > ." + options.classHeader + " > table", layout);
        var ft = $(".fixedContainer > .fixedTable > table", layout);
        var ff = $(".fixedContainer > ." + options.classFooter + " > table", layout);

        var maxWidth = fh.width();
        if (ft.length > 0 && ft.width() > maxWidth) { maxWidth = ft.width(); }
        if (ff.length > 0 && ff.width() > maxWidth) { maxWidth = ff.width(); }


        if (fh.length) { fh.width(maxWidth); }
        if (ft.length) { ft.width(maxWidth); }
        if (ff.length) { ff.width(maxWidth); }

        //adjust the widths of the fixedColumn header/footer to match the fixed columns themselves
        $("." + options.classColumn + " > ." + options.classHeader + " > table > tbody > tr:first > td", layout).each(function(pos) {
            var tblCell = $("." + options.classColumn + " > .fixedTable > table > tbody > tr:first > td:eq(" + pos + ")", layout);
            var tblFoot = $("." + options.classColumn + " > ." + options.classFooter + " > table > tbody > tr:first > td:eq(" + pos + ")", layout);
            var maxWidth = $(this).width();
            if (tblCell.width() > maxWidth) { maxWidth = tblCell.width(); }
            if (tblFoot.length && tblFoot.width() > maxWidth) { maxWidth = tblFoot.width(); }
            $(this).width(maxWidth);
            $(tblCell).width(maxWidth);
            if (tblFoot.length) { $(tblFoot).width(maxWidth); }
        });


        //set the height of the table area, minus the heights of the header/footer.
        // note: we need to do this after the other adjustments, otherwise these changes would be overwrote
        var h = options.height - parseInt($(".fixedContainer > ." + options.classHeader, layout).height()) - parseInt($(".fixedContainer > ." + options.classFooter, layout).height());
        //sanity check
        if (h < 0) { h = options.height; }
        $(".fixedContainer > .fixedTable", layout).height(h);
        $("." + options.classColumn + " > .fixedTable", layout).height(h);

        //Adjust the fixed column area if we have a horizontal scrollbar on the main table
        // - specifically, make sure our fixedTable area matches the main table area minus the scrollbar height,
        //   and the fixed column footer area lines up with the main footer area (shift down by the scrollbar height)
        var h = $(".fixedContainer > .fixedTable", layout)[0].offsetHeight - 16;
        $("." + options.classColumn + " > .fixedTable", layout).height(h);  //make sure the row area of the fixed column matches the height of the main table, with the scrollbar

        // Apply the scroll handlers
        $(".fixedContainer > .fixedTable", layout).scroll(function() { handleScroll(mainid, options); });
        //the handleScroll() method is defined near the bottom of this file.

        //$.fn.fixedTable.adjustSizes(mainid);
        adjustSizes(options);
        return tbl;
    }

    function buildLayout(src, options) {
        //create a working area and add it just after our table.
        //The table will be moved into this working area
        var area = $("<div class=\"fixedArea\"></div>").appendTo($(src).parent());

        //fixed column items
        var fc = $("<div class=\"" + options.classColumn + "\" style=\"float: left;\"></div>").appendTo(area);
        var fch = $("<div class=\"" + options.classHeader + "\"></div>").appendTo(fc);
        var fct = $("<div class=\"fixedTable\"></div>").appendTo(fc);
        var fcf = $("<div class=\"" + options.classFooter + "\"></div>").appendTo(fc);

        //fixed container items
        var fcn = $("<div class=\"fixedContainer\"></div>").appendTo(area);
        var fcnh = $("<div class=\"" + options.classHeader + "\"></div>").appendTo(fcn);
        var fcnt = $("<div class=\"fixedTable\"></div>").appendTo(fcn);
        var fcnf = $("<div class=\"" + options.classFooter + "\"></div>").appendTo(fcn);

        //create the fixed column area
        if (options.fixedColumns > 0 && !isNaN(options.fixedColumns)) {
            buildFixedColumns(src, "thead", options.fixedColumns, fch);
            buildFixedColumns(src, "tbody", options.fixedColumns, fct);
            buildFixedColumns(src, "tfoot", options.fixedColumns, fcf);
            //see the buildFixedColumns() function below
        }

        //Build header / footer areas
        buildFixedTable(src, "thead", fcnh);
        buildFixedTable(src, "tfoot", fcnf);
        //see the buildFixedTable() function below

        //Build the main table
        //we'll cheat here - the src table should only be a tbody section, with the remaining columns, 
        //so we'll just add it to the fixedContainer table area.
        fcnt.append(src);
        return area;
    }

    /* ******************************************************************** */
    // duplicate a table section (thead, tfoot, tbody), but only for the desired number of columns
    function buildFixedColumns(src, section, cols, target) {
        //TFOOT - get the needed columns from the table footer
        if ($(section, src).length) {
            var colHead = $("<table></table>").appendTo(target);

            //If we have a thead or tfoot, we're looking for "TH" elements, otherwise we're looking for "TD" elements
            var cellType = "td";  //deafault cell type
            if (section.toLowerCase() == "thead" || section.toLowerCase() == "tfoot") { cellType = "th"; }

            //check each of the rows in the thead
            $(section + " tr", src).each(function() {
                var tr = $("<tr></tr>").appendTo(colHead);
                $(cellType + ":lt(" + cols + ")", this).each(function() {
                    $("<td>" + $(this).html() + "</td>").addClass(this.className).attr("id", this.id).appendTo(tr);
                    //Note, we copy the class names and ID from the original table cells in case there is any processing on them.
                    //However, if the class does anything with regards to the cell size or position, it could mess up the layout.

                    //Remove the item from our src table.
                    $(this).remove();
                });
            });
        }
    }

    /* ******************************************************************** */
    // duplicate a table section (thead, tfoot, tbody)
    function buildFixedTable(src, section, target) {
        if ($(section, src).length) {
            var th = $("<table></table>").appendTo(target);
            var tr = null;

            //If we have a thead or tfoot, we're looking for "TH" elements, otherwise we're looking for "TD" elements
            var cellType = "td";  //deafault cell type
            if (section.toLowerCase() == "thead" || section.toLowerCase() == "tfoot") { cellType = "th"; }

            $(section + " tr", src).each(function() {
                var tr = $("<tr></tr>").appendTo(th);
                $(cellType, this).each(function() {
                    $("<td>" + $(this).html() + "</td>").appendTo(tr);
                });

            });
            //The header *should* be added to our head area now, so we can remove the table header
            $(section, src).remove();
        }
    }

    // ***********************************************
    // Handle the scroll events
    function handleScroll(mainid, options) {
        //Find the scrolling offsets
        var tblarea = $(mainid + " .fixedContainer > .fixedTable");
        var x = tblarea[0].scrollLeft;
        var y = tblarea[0].scrollTop;

        $(mainid + " ." + options.classColumn + " > .fixedTable")[0].scrollTop = y;
        $(mainid + " .fixedContainer > ." + options.classHeader)[0].scrollLeft = x;
        $(mainid + " .fixedContainer > ." + options.classFooter)[0].scrollLeft = x;
    }

    // ***********************************************
    //  Reset the heights of the rows in our fixedColumn area
    function adjustSizes(options) {

        var Id = options.outerId;
        var maintbheight = options.height;
        var backcolor = options.Contentbackcolor;
        var hovercolor = options.Contenthovercolor;
        var fixedColumnbackcolor = options.fixedColumnbackcolor;
        var fixedColumnhovercolor = options.fixedColumnhovercolor;

        // row height
        $("#" + Id + " ." + options.classColumn + " .fixedTable table tbody tr").each(function(i) {
            var maxh = 0;
            var fixedh = $(this).height();
            var contenth = $("#" + Id + " .fixedContainer .fixedTable table tbody tr").eq(i).height();
            if (contenth > fixedh) {
                maxh = contenth;
            }
            else {
                maxh = fixedh;
            }
            //$(this).height(contenth);
            $(this).children("td").height(maxh);
            $("#" + Id + " .fixedContainer .fixedTable table tbody tr").eq(i).children("td").height(maxh);
        });

        //adjust the cell widths so the header/footer and table cells line up
        var htbale = $("#" + Id + " .fixedContainer ." + options.classHeader + " table");
        var ttable = $("#" + Id + " .fixedContainer .fixedTable table");
        var ccount = $("#" + Id + " .fixedContainer ." + options.classHeader + " table tr:first td").size();
        var widthArray = new Array();
        var totall = 0;

        $("#" + Id + " .fixedContainer ." + options.classHeader + " table tr:first td").each(function(pos) {
            var cwidth = $(this).width();
            $("#" + Id + " .fixedContainer .fixedTable table tbody td").each(function(i) {
                if (i % ccount == pos) {
                    if ($(this).width() > cwidth) {
                        cwidth = $(this).width();
                    }
                }
            });
            widthArray[pos] = cwidth;
            totall += (cwidth + 2);
        });

        $("#" + Id + " .fixedContainer ." + options.classHeader + " table").width(totall + 100);
        $("#" + Id + " .fixedContainer .fixedTable table").width(totall + 100);
        $("#" + Id + " .fixedContainer ." + options.classFooter + " table").width(totall + 100);
        for (i = 0; i < widthArray.length; i++) {
            $("#" + Id + " .fixedContainer ." + options.classHeader + " table tr td").each(function(j) {
                if (j % ccount == i) {
                    $(this).attr("width", widthArray[i] + "px");
                }
            });

            $("#" + Id + " .fixedContainer .fixedTable table tr td").each(function(j) {
                if (j % ccount == i) {
                    $(this).attr("width", widthArray[i] + "px");
                }
            });

            $("#" + Id + " .fixedContainer ." + options.classFooter + " table tr td").each(function(j) {
                if (j % ccount == i) {
                    $(this).attr("width", widthArray[i] + "px");
                }
            });
        }

        // mouse in/out fixedColumn's fixedtable, change background color.
        $("#" + Id + " ." + options.classColumn + " .fixedTable table tr").each(function(i) {
            $(this).mouseover(function() {
                $(this).children("td").css({
                    "background-color": fixedColumnhovercolor
                });
                var obj = $("#" + Id + " .fixedContainer .fixedTable table tr").eq(i);
                obj.children("td").css({
                    "background-color": hovercolor
                });
                obj.children("td").children("pre").css({
                    "background-color": hovercolor
                });
            });
            $(this).mouseout(function() {
                $(this).children("td").css({
                    "background-color": fixedColumnbackcolor
                });
                var obj = $("#" + Id + " .fixedContainer .fixedTable table tr").eq(i);
                obj.children("td").css({
                    "background-color": backcolor
                });
                obj.children("td").children("pre").css({
                    "background-color": backcolor
                });
            });
        });

        // mouse in/out fixedContainer's fixedtable, change background color.
        $("#" + Id + " .fixedContainer .fixedTable table tr").each(function(i) {
            $(this).mouseover(function() {
                $(this).children("td").css({
                    "background-color": hovercolor
                });
                $(this).children("td").children("pre").css({
                    "background-color": hovercolor
                });
                var obj = $("#" + Id + " ." + options.classColumn + " .fixedTable table tr").eq(i);
                obj.children("td").css({
                    "background-color": fixedColumnhovercolor
                });

            });
            $(this).mouseout(function() {
                $(this).children("td").css({
                    "background-color": backcolor
                });
                $(this).children("td").children("pre").css({
                    "background-color": backcolor
                });
                var obj = $("#" + Id + " ." + options.classColumn + " .fixedTable table tr").eq(i);
                obj.children("td").css({
                    "background-color": fixedColumnbackcolor
                });
            });
        });

        var contenttbH = $("#" + Id + " .fixedContainer .fixedTable table").height();
        if (contenttbH < maintbheight) {
            $("#" + Id + " ." + options.classColumn + " .fixedTable").height(contenttbH + 20);
            $("#" + Id + " .fixedContainer .fixedTable").height(contenttbH + 20);

            $("#" + Id + " .fixedContainer ." + options.classHeader).width($("#" + Id + " .fixedContainer ." + options.classHeader).width() + 16);
            $("#" + Id + " .fixedContainer ." + options.classFooter).width($("#" + Id + " .fixedContainer ." + options.classHeader).width());
        }
        else {
            //offset the footer by the height of the scrollbar so that it lines up right.
            $("#" + Id + " ." + options.classColumn + " > ." + options.classFooter).css({
                "position": "relative",
                "top": 16
            });
        }
    }

})(jQuery);