/*
 * File:        jquery.dataTables.grouping.js
 * Version:     1.2.11.
 * Author:      Jovan Popovic
 *
 * https://github.com/djwoodward/jquery-datatables-row-grouping/blob/master/media/js/jquery.dataTables.rowGrouping.js
 * Originally hosted at https://code.google.com/p/jquery-datatables-row-grouping
 *
 * Copyright 2013 Jovan Popovic, all rights reserved.
 *
 * This source file is free software, under either the GPL v2 license or a
 * BSD style license, as supplied with this software.
 *
 * This source file is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.
 * Parameters:
 * @iGroupingColumnIndex                                 Integer             Index of the column that will be used for grouping - default 0
 * @sGroupingColumnSortDirection                         Enumeration         Sort direction of the group
 * @iGroupingOrderByColumnIndex                          Integer             Index of the column that will be used for ordering groups
 * @sGroupingClass                                       String              Class that will be associated to the group row. Default - "group"
 * @sGroupItemClass                                      String              Class that will be associated to the group row of group items. Default - "group-item"
 * @bSetGroupingClassOnTR                                Boolean             If set class will be set to the TR instead of the TD withing the grouping TR
 * @bHideGroupingColumn                                  Boolean             Hide column used for grouping once results are grouped. Default - true
 * @bHideGroupingOrderByColumn                           Boolean             Hide column used for ordering groups once results are grouped. Default - true
 * @sGroupBy                                             Enumeration         Type of grouping that should be applied. Values "name"(default), "letter", "year"
 * @sGroupLabelPrefix                                    String              Prefix that will be added to each group cell
 * @bExpandableGrouping                                  Boolean             Attach expand/collapse handlers to the grouping rows
 * @bExpandSingleGroup                                   Boolean             Use accordon grouping
 * @iExpandGroupOffset                                   Integer             Number of pixels to set scroll position above the currently selected group. If -1 scroll will be alligned to the table
 * General settings
 * @sDateFormat: "dd/MM/yyyy"                            String              Date format used for grouping
 * @sEmptyGroupLabel                                     String              Lable that will be placed as group if grouping cells are empty. Default "-"

 * Parameters used in the second level grouping
 * @iGroupingColumnIndex2                                Integer             Index of the secondary column that will be used for grouping - default 0
 * @sGroupingColumnSortDirection2                        Enumeration         Sort direction of the secondary group
 * @iGroupingOrderByColumnIndex2                         Integer             Index of the column that will be used for ordering secondary groups
 * @sGroupingClass2                                      String              Class that will be associated to the secondary group row. Default "subgroup"
 * @sGroupItemClass2                                     String              Class that will be associated to the secondary group row of group items. Default "subgroup-item"
 * @bHideGroupingColumn2                                 Boolean             Hide column used for secondary grouping once results are grouped. Default - true,
 * @bHideGroupingOrderByColumn2                          Boolean             Hide column used for ordering secondary groups once results are grouped. Default - true,
 * @sGroupBy2                                            Enumeration         Type of grouping that should be applied to secondary column. Values "name"(default), "letter", "year",
 * @sGroupLabelPrefix2                                   String              Prefix that will be added to each secondary group cell
 * @fnOnGrouped                                          Function            Function that is called when grouping is finished. Function has no parameters.
 */
(function ($) {

    "use strict";

    $.fn.rowGrouping = function (options) {

        function _fnOnGrouped() {

        }

        function _fnOnGroupCreated(oGroup, sGroup, iLevel) {
            ///<summary>
            ///Function called when a new grouping row is created(it should be overriden in properties)
            ///</summary>
        }

        function _fnOnGroupCompleted(oGroup, sGroup, iLevel) {
            ///<summary>
            ///Function called when a new grouping row is created(it should be overriden in properties)
            ///</summary>
        }

        function _getMonthName(iMonth) {
            var asMonths = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
            return asMonths[iMonth - 1];
        }

        var defaults = {

            iGroupingColumnIndex: 0,
            sGroupingColumnSortDirection: "",
            iGroupingOrderByColumnIndex: -1,
            sGroupingClass: "group",
            sGroupItemClass: "group-item",
            bHideGroupingColumn: true,
            bHideGroupingOrderByColumn: true,
            sGroupBy: "name",
            sGroupLabelPrefix: "",
            fnGroupLabelFormat: function (label) {
                return label;
            },
            bExpandableGrouping: false,
            bExpandSingleGroup: false,
            iExpandGroupOffset: 100,
            asExpandedGroups: null,

            sDateFormat: "dd/MM/yyyy",
            sEmptyGroupLabel: "-",
            bSetGroupingClassOnTR: false,

            iGroupingColumnIndex2: -1,
            sGroupingColumnSortDirection2: "",
            iGroupingOrderByColumnIndex2: -1,
            sGroupingClass2: "subgroup",
            sGroupItemClass2: "subgroup-item",
            bHideGroupingColumn2: true,
            bHideGroupingOrderByColumn2: true,
            sGroupBy2: "name",
            sGroupLabelPrefix2: "",
            fnGroupLabelFormat2: function (label) {
                return label;
            },
            bExpandableGrouping2: false,

            fnOnGrouped: _fnOnGrouped,

            fnOnGroupCreated: _fnOnGroupCreated,
            fnOnGroupCompleted: _fnOnGroupCompleted,

            oHideEffect: null, // { method: "hide", duration: "fast", easing: "linear" },
            oShowEffect: null,//{ method: "show", duration: "slow", easing: "linear" }

            bUseFilteringForGrouping: false // This is still work in progress option
        };
        return this.each(function (index, elem) {

            var oTable = $(elem).dataTable();

            var aoGroups = new Array();
            $(this).dataTableExt.aoGroups = aoGroups;

            //Added argument iColSPan2 if fixed columns are on
            function fnCreateGroupRow(sGroupCleaned, sGroup, iColspan, iColSpan2) {
                var nGroup = document.createElement('tr');
                var nCell = document.createElement('td');
                // Adding new td for fixed columns
                var nCellFC = document.createElement('td');
                nGroup.id = "group-id-" + oTable.attr("id") + "_" + sGroupCleaned;

                var oGroup = {
                    id: nGroup.id,
                    key: sGroupCleaned,
                    text: sGroup,
                    level: 0,
                    groupItemClass: ".group-item-" + sGroupCleaned,
                    dataGroup: sGroupCleaned,
                    aoSubgroups: new Array()
                };


                if (properties.bSetGroupingClassOnTR) {
                    nGroup.className = properties.sGroupingClass + " " + sGroupCleaned;
                } else {
                    nCell.className = properties.sGroupingClass + " " + sGroupCleaned;
                    nCellFC.className = properties.sGroupingClass + " " + sGroupCleaned;
                }

                nCell.colSpan = iColspan;
                // Adding colspan for new td (fixed columns) and adding class and style to td
                nCellFC.colSpan = iColSpan2;
                if (iColSpan2 != 0) {
                    nCell.className += ' dtfc-fixed-left ';
                    nCell.style.position = 'sticky';
                    nCell.style.left = '0px';
                }
                nCell.innerHTML = properties.sGroupLabelPrefix + properties.fnGroupLabelFormat(sGroup == "" ? properties.sEmptyGroupLabel : sGroup, oGroup);
                if (properties.bExpandableGrouping) {

                    if (!_fnIsGroupCollapsed(sGroupCleaned)) {
                        nCell.className += " expanded-group";
                        oGroup.state = "expanded";
                    } else {
                        nCell.className += " collapsed-group";
                        oGroup.state = "collapsed";
                    }
                    nCell.className += " group-item-expander";
                    $(nCell).attr('data-group', oGroup.dataGroup); //Fix provided by mssskhalsa (Issue 5)
                    $(nCell).attr("data-group-level", oGroup.level);
                    $(nCell).click(_fnOnGroupClick);
                }
                nGroup.appendChild(nCell)
                // Append td if fixed columns are on
                if (iColSpan2 != 0) {
                    nGroup.appendChild(nCellFC);
                }
                aoGroups[sGroupCleaned] = oGroup;
                oGroup.nGroup = nGroup;
                properties.fnOnGroupCreated(oGroup, sGroupCleaned, 1);
                return oGroup;
            }

            function _fnCreateGroup2Row(sGroup2, sGroupLabel, iColspan, oParentGroup) {

                var nGroup2 = document.createElement('tr');
                nGroup2.id = oParentGroup.id + "_" + sGroup2;
                var nCell2 = document.createElement('td');
                var dataGroup = oParentGroup.dataGroup + '_' + sGroup2;

                var oGroup = {
                    id: nGroup2.id,
                    key: sGroup2,
                    text: sGroupLabel,
                    level: oParentGroup.level + 1,
                    groupItemClass: ".group-item-" + dataGroup,
                    dataGroup: dataGroup,
                    aoSubgroups: new Array()
                };

                if (properties.bSetGroupingClassOnTR) {
                    nGroup2.className = properties.sGroupingClass2 + " " + sGroup2;
                } else {
                    nCell2.className = properties.sGroupingClass2 + " " + sGroup2;
                }

                nCell2.colSpan = iColspan;
                nCell2.innerHTML = properties.sGroupLabelPrefix2 + properties.fnGroupLabelFormat2(sGroupLabel == "" ? properties.sEmptyGroupLabel : sGroupLabel, oGroup);

                if (properties.bExpandableGrouping) {

                    nGroup2.className += " group-item-" + oParentGroup.dataGroup;
                }


                if (properties.bExpandableGrouping && properties.bExpandableGrouping2) {

                    if (!_fnIsGroupCollapsed(oGroup.dataGroup)) {
                        nCell2.className += " expanded-group";
                        oGroup.state = "expanded";
                    } else {
                        nCell2.className += " collapsed-group";
                        oGroup.state = "collapsed";
                    }
                    nCell2.className += " group-item-expander";
                    $(nCell2).attr('data-group', oGroup.dataGroup);
                    $(nCell2).attr("data-group-level", oGroup.level);
                    $(nCell2).click(_fnOnGroupClick);
                }

                nGroup2.appendChild(nCell2);

                oParentGroup.aoSubgroups[oGroup.dataGroup] = oGroup;
                aoGroups[oGroup.dataGroup] = oGroup;
                oGroup.nGroup = nGroup2;
                properties.fnOnGroupCreated(oGroup, sGroup2, 2);
                return oGroup;
            }

            function _fnIsGroupCollapsed(sGroup) {
                if (aoGroups[sGroup] != null)
                    return (aoGroups[sGroup].state == "collapsed");
                else if (sGroup.indexOf("_") > -1)
                    true;
                else if (bInitialGrouping && (asExpandedGroups == null || asExpandedGroups.length == 0))
                    return false;// initially if asExpandedGroups is empty - no one is collapsed
                else
                    return ($.inArray(sGroup, asExpandedGroups) == -1); //the last chance check asExpandedGroups
            }

            function _fnGetYear(x) {
                if (x.length < (iYearIndex + iYearLength))
                    return x;
                else
                    return x.substr(iYearIndex, iYearLength);
            }

            function _fnGetGroupByName(x) {
                return x;
            }

            function _fnGetGroupByLetter(x) {
                return x.substr(0, 1);
            }

            function _fnGetGroupByYear(x) {
                return _fnGetYear(x);
                //return Date.parseExact(x, properties.sDateFormat).getFullYear();//slooooow
            }

            function _fnGetGroupByYearMonth(x) {
                //var date = Date.parseExact(x, "dd/MM/yyyy");
                //return date.getFullYear() + " / " + date.getMonthName();
                //return x.substr(iYearIndex, iYearLength) + '/' + x.substr(iMonthIndex, iMonthLength);
                return x.substr(iYearIndex, iYearLength) + ' ' + _getMonthName(x.substr(iMonthIndex, iMonthLength));
            }

            function _fnGetCleanedGroup(sGroup) {

                if (typeof sGroup === "undefined" || sGroup === "") return properties.sEmptyGroupLabel;
                return sGroup.toLowerCase().replace(/[^a-zA-Z0-9\u0080-\uFFFF]+/g, "-"); //fix for unicode characters (Issue 23)
                //return sGroup.toLowerCase().replace(/\W+/g, "-"); //Fix provided by bmathews (Issue 7)
            }

            function _rowGroupingRowFilter(oSettings, aData, iDataIndex) {
                ///<summary>Used to expand/collapse groups with DataTables filtering</summary>
                if (oSettings.nTable.id !== oTable[0].id) return true;
                var sColData = aData[properties.iGroupingColumnIndex];
                if (typeof sColData === "undefined")
                    sColData = aData[oSettings.aoColumns[properties.iGroupingColumnIndex].mData];
                if (_fnIsGroupCollapsed(_fnGetCleanedGroup(sColData))) {
                    if (oTable.fnIsOpen(oTable.fnGetNodes(iDataIndex))) {
                        if (properties.fnOnRowClosed != null) {
                            properties.fnOnRowClosed(this); //    $(this.cells[0].children[0]).attr('src', '../../Images/details.png');
                        }
                        oTable.fnClose(oTable.fnGetNodes(iDataIndex));
                    }
                    return false;
                }
                ;
                return true;
            } //end of function _rowGroupingRowFilter


            function fnExpandGroup(sGroup) {
                ///<summary>Expand group if expanadable grouping is used</summary>

                aoGroups[sGroup].state = "expanded";

                $("td[data-group='" + sGroup + "']").removeClass("collapsed-group");
                $("td[data-group='" + sGroup + "']").addClass("expanded-group");


                if (properties.bUseFilteringForGrouping) {
                    oTable.fnDraw();
                    return;//Because rows are expanded with _rowGroupingRowFilter function
                }

                if (jQuery.inArray(sGroup, asExpandedGroups) == -1)
                    asExpandedGroups.push(sGroup);

                if (properties.oHideEffect != null)
                    $(".group-item-" + sGroup, oTable)
                        [properties.oShowEffect.method](properties.oShowEffect.duration,
                        properties.oShowEffect.easing,
                        function () {
                        });
                else
                    $(".group-item-" + sGroup, oTable).show();


            } //end of function fnExpandGroup

            function fnCollapseGroup(sGroup) {
                ///<summary>Collapse group if expanadable grouping is used</summary>

                aoGroups[sGroup].state = "collapsed";
                $("td[data-group='" + sGroup + "']").removeClass("expanded-group");
                $("td[data-group='" + sGroup + "']").addClass("collapsed-group");

                if (properties.bUseFilteringForGrouping) {
                    oTable.fnDraw();
                    return;//Because rows are expanded with _rowGroupingRowFilter function
                }
                //var index = $.inArray(sGroup, asExpandedGroups);
                //asExpandedGroups.splice(index, 1);

                $('.group-item-' + sGroup).each(function () {
                    //Issue 24 - Patch provided by Bob Graham
                    if (oTable.fnIsOpen(this)) {
                        if (properties.fnOnRowClosed != null) {
                            properties.fnOnRowClosed(this); //    $(this.cells[0].children[0]).attr('src', '../../Images/details.png');
                        }
                        oTable.fnClose(this);
                    }
                });

                if (properties.oHideEffect != null)
                    $(".group-item-" + sGroup, oTable)
                        [properties.oHideEffect.method](properties.oHideEffect.duration,
                        properties.oHideEffect.easing,
                        function () {
                        });
                else
                    $(".group-item-" + sGroup, oTable).hide();

            } //end of function fnCollapseGroup

            function _fnOnGroupClick(e) {
                ///<summary>
                ///Function that is called when user click on the group cell in order to
                ///expand of collapse group
                ///</summary>

                //var sGroup = $(this).attr("rel");
                var sGroup = $(this).attr("data-group");
                var iGroupLevel = $(this).attr("data-group-level");

                var bIsExpanded = !_fnIsGroupCollapsed(sGroup);
                if (properties.bExpandSingleGroup) {
                    if (!bIsExpanded) {
                        var sCurrentGroup = $("td.expanded-group").attr("data-group");
                        fnCollapseGroup(sCurrentGroup);
                        fnExpandGroup(sGroup);

                        if (properties.iExpandGroupOffset != -1) {
                            var position = $("#group-id-" + oTable.attr("id") + "_" + sGroup).offset().top - properties.iExpandGroupOffset;
                            window.scroll(0, position);
                        } else {
                            var position = oTable.offset().top;
                            window.scroll(0, position);
                        }
                    }
                } else {
                    if (bIsExpanded) {
                        fnCollapseGroup(sGroup);
                    } else {
                        fnExpandGroup(sGroup);
                    }
                }
                e.preventDefault();

            }; //end function _fnOnGroupClick


            function _fnDrawCallBackWithGrouping(oSettings) {

                if (oTable.fnSettings().oFeatures.bServerSide)
                    bInitialGrouping = true;
                var bUseSecondaryGrouping = false;

                if (properties.iGroupingColumnIndex2 != -1)
                    bUseSecondaryGrouping = true;

                //-----Start grouping

                if (oSettings.aiDisplayMaster.length == 0) { //aiDisplay
                    return;
                }

                var nTrs = $('tbody tr', oTable);
                var iColspan = 0; //nTrs[0].getElementsByTagName('td').length;
                var iColspanFC = 0; //colspan for fixed columns
                for (var iColIndex = 0; iColIndex < oSettings.aoColumns.length; iColIndex++) {
                    if (oSettings.aoColumns[iColIndex].bVisible)
                        iColspan += 1;
                }
                if (oSettings.oInstance.api().settings()[0]._fixedColumns != undefined && oSettings.oInstance.api().fixedColumns().left() != 0) {
                    iColspanFC = iColspan - oSettings.oInstance.api().fixedColumns().left();
                    iColspan = oSettings.oInstance.api().fixedColumns().left(); // for fixed columns all-left fixed
                }
                if (oSettings.oInstance.api().settings()[0]._fixedHeader != undefined){
                    oSettings.oInstance.api().fixedHeader.adjust();
                }
                var sLastGroup = null;
                var sLastGroup2 = null;
                if (oSettings.aiDisplay.length > 0) {
                    for (var i = 0; i < nTrs.length; i++) {


                        var iDisplayIndex = oSettings._iDisplayStart + i;
                        if (oTable.fnSettings().oFeatures.bServerSide)
                            iDisplayIndex = i;
                        var sGroupData = "";
                        var sGroup = null;
                        var sGroupData2 = "";
                        var sGroup2 = null;

                        //Issue 31 - Start fix provided by Fabien Taysse
//                      sGroupData = oSettings.aoData[oSettings.aiDisplay[iDisplayIndex]]._aData[properties.iGroupingColumnIndex];
//                      if (sGroupData == undefined)
//                          sGroupData = oSettings.aoData[oSettings.aiDisplay[iDisplayIndex]]._aData[oSettings.aoColumns[properties.iGroupingColumnIndex].mData];
                        sGroupData = this.fnGetData(nTrs[i], properties.iGroupingColumnIndex);
                        //Issue 31 - End fix provided by Fabien Taysse

                        var sGroup = sGroupData;
                        if (properties.sGroupBy != "year")
                            sGroup = fnGetGroup(sGroupData);

                        if (bUseSecondaryGrouping) {
                            sGroupData2 = oSettings.aoData[oSettings.aiDisplay[iDisplayIndex]]._aData[properties.iGroupingColumnIndex2];
                            if (sGroupData2 == undefined)
                                sGroupData2 = oSettings.aoData[oSettings.aiDisplay[iDisplayIndex]]._aData[oSettings.aoColumns[properties.iGroupingColumnIndex2].mData];
                            if (properties.sGroupBy2 != "year")
                                sGroup2 = fnGetGroup(sGroupData2);
                        }


                        if (sLastGroup == null || _fnGetCleanedGroup(sGroup) != _fnGetCleanedGroup(sLastGroup)) { // new group encountered (or first of group)
                            var sGroupCleaned = _fnGetCleanedGroup(sGroup);

                            if (sLastGroup != null) {
                                properties.fnOnGroupCompleted(aoGroups[_fnGetCleanedGroup(sLastGroup)]);
                            }
                            /*
                             if (properties.bExpandableGrouping && bInitialGrouping) {
                             if (properties.bExpandSingleGroup) {
                             if (asExpandedGroups.length == 0)
                             asExpandedGroups.push(sGroupCleaned);
                             } else {
                             asExpandedGroups.push(sGroupCleaned);
                             }
                             }
                             */
                            if (properties.bAddAllGroupsAsExpanded && jQuery.inArray(sGroupCleaned, asExpandedGroups) == -1)
                                asExpandedGroups.push(sGroupCleaned);

                            var oGroup = fnCreateGroupRow(sGroupCleaned, sGroup, iColspan, iColspanFC);
                            var nGroup = oGroup.nGroup;

                            if (nTrs[i].parentNode != null)
                                nTrs[i].parentNode.insertBefore(nGroup, nTrs[i]);
                            else
                                $(nTrs[i]).before(nGroup);

                            sLastGroup = sGroup;
                            sLastGroup2 = null; //to reset second level grouping


                        } // end if (sLastGroup == null || sGroup != sLastGroup)

                        $(nTrs[i]).attr("data-group", aoGroups[sGroupCleaned].dataGroup);

                        $(nTrs[i]).addClass(properties.sGroupItemClass);
                        $(nTrs[i]).addClass("group-item-" + sGroupCleaned);
                        if (properties.bExpandableGrouping) {
                            if (_fnIsGroupCollapsed(sGroupCleaned) && !properties.bUseFilteringForGrouping) {
                                $(nTrs[i]).hide();
                            }
                        }


                        if (bUseSecondaryGrouping) {

                            if (sLastGroup2 == null || _fnGetCleanedGroup(sGroup2) != _fnGetCleanedGroup(sLastGroup2)) {
                                var sGroup2Id = _fnGetCleanedGroup(sGroup) + '-' + _fnGetCleanedGroup(sGroup2);
                                var oGroup2 = _fnCreateGroup2Row(sGroup2Id, sGroup2, iColspan, aoGroups[sGroupCleaned])
                                var nGroup2 = oGroup2.nGroup;
                                nTrs[i].parentNode.insertBefore(nGroup2, nTrs[i]);

                                sLastGroup2 = sGroup2;
                            }

                            $(nTrs[i]).attr("data-group", oGroup2.dataGroup)
                                .addClass(properties.sGroupItemClass2)
                                .addClass("group-item-" + oGroup2.dataGroup);
                        } //end if (bUseSecondaryGrouping)


                    } // end for (var i = 0; i < nTrs.length; i++)
                }
                ; // if (oSettings.aiDisplay.length > 0)

                if (sLastGroup != null) {
                    properties.fnOnGroupCompleted(aoGroups[_fnGetCleanedGroup(sLastGroup)]);
                }


                //-----End grouping
                properties.fnOnGrouped(aoGroups);

                bInitialGrouping = false;

            }; // end of _fnDrawCallBackWithGrouping = function (oSettings)


            //var oTable = this;
            var iYearIndex = 6;
            var iYearLength = 4;
            var asExpandedGroups = new Array();
            var bInitialGrouping = true;

            var properties = $.extend(defaults, options);

            if (properties.iGroupingOrderByColumnIndex == -1) {
                properties.bCustomColumnOrdering = false;
                properties.iGroupingOrderByColumnIndex = properties.iGroupingColumnIndex;
            } else {
                properties.bCustomColumnOrdering = true;
            }

            if (properties.sGroupingColumnSortDirection == "") {
                if (properties.sGroupBy == "year")
                    properties.sGroupingColumnSortDirection = "desc";
                else
                    properties.sGroupingColumnSortDirection = "asc";
            }


            if (properties.iGroupingOrderByColumnIndex2 == -1) {
                properties.bCustomColumnOrdering2 = false;
                properties.iGroupingOrderByColumnIndex2 = properties.iGroupingColumnIndex2;
            } else {
                properties.bCustomColumnOrdering2 = true;
            }

            if (properties.sGroupingColumnSortDirection2 == "") {
                if (properties.sGroupBy2 == "year")
                    properties.sGroupingColumnSortDirection2 = "desc";
                else
                    properties.sGroupingColumnSortDirection2 = "asc";
            }


            iYearIndex = properties.sDateFormat.toLowerCase().indexOf('yy');
            iYearLength = properties.sDateFormat.toLowerCase().lastIndexOf('y') - properties.sDateFormat.toLowerCase().indexOf('y') + 1;

            var iMonthIndex = properties.sDateFormat.toLowerCase().indexOf('mm');
            var iMonthLength = properties.sDateFormat.toLowerCase().lastIndexOf('m') - properties.sDateFormat.toLowerCase().indexOf('m') + 1;

            var fnGetGroup = _fnGetGroupByName;
            switch (properties.sGroupBy) {
                case "letter":
                    fnGetGroup = _fnGetGroupByLetter;
                    break;
                case "year":
                    fnGetGroup = _fnGetGroupByYear;
                    break;
                case "month":
                    fnGetGroup = _fnGetGroupByYearMonth;
                    break;
                default:
                    fnGetGroup = _fnGetGroupByName;
                    break;
            }


            if (properties.asExpandedGroups != null) {
                if (properties.asExpandedGroups == "NONE") {
                    properties.asExpandedGroups = [];
                    asExpandedGroups = properties.asExpandedGroups;
                    bInitialGrouping = false;
                } else if (properties.asExpandedGroups == "ALL") {
                    properties.bAddAllGroupsAsExpanded = true;
                } else if (properties.asExpandedGroups.constructor == String) {
                    var currentGroup = properties.asExpandedGroups;
                    properties.asExpandedGroups = new Array();
                    properties.asExpandedGroups.push(_fnGetCleanedGroup(currentGroup));
                    asExpandedGroups = properties.asExpandedGroups;
                    bInitialGrouping = false;
                } else if (properties.asExpandedGroups.constructor == Array) {
                    for (var i = 0; i < properties.asExpandedGroups.length; i++) {
                        asExpandedGroups.push(_fnGetCleanedGroup(properties.asExpandedGroups[i]));
                        if (properties.bExpandSingleGroup)
                            break;
                    }
                    bInitialGrouping = false;
                }
            } else {
                properties.asExpandedGroups = new Array();
                properties.bAddAllGroupsAsExpanded = true;
            }
            if (properties.bExpandSingleGroup) {
                var nTrs = $('tbody tr', oTable);
                var sGroupData = oTable.fnGetData(nTrs[0], properties.iGroupingColumnIndex);

                var sGroup = sGroupData;
                if (properties.sGroupBy != "year")
                    sGroup = fnGetGroup(sGroupData);

                var sGroupCleaned = _fnGetCleanedGroup(sGroup);
                properties.asExpandedGroups = new Array();
                properties.asExpandedGroups.push(sGroupCleaned);

            }

            oTable.fnSetColumnVis(properties.iGroupingColumnIndex, !properties.bHideGroupingColumn);
            if (properties.bCustomColumnOrdering) {
                oTable.fnSetColumnVis(properties.iGroupingOrderByColumnIndex, !properties.bHideGroupingOrderByColumn);
            }
            if (properties.iGroupingColumnIndex2 != -1) {
                oTable.fnSetColumnVis(properties.iGroupingColumnIndex2, !properties.bHideGroupingColumn2);
            }
            if (properties.bCustomColumnOrdering2) {
                oTable.fnSetColumnVis(properties.iGroupingOrderByColumnIndex2, !properties.bHideGroupingOrderByColumn2);
            }
            oTable.fnSettings().aoDrawCallback.push({
                "fn": _fnDrawCallBackWithGrouping,
                "sName": "fnRowGrouping"
            });

            var aaSortingFixed = new Array();
            aaSortingFixed.push([properties.iGroupingOrderByColumnIndex, properties.sGroupingColumnSortDirection]);
            if (properties.iGroupingColumnIndex2 != -1) {
                aaSortingFixed.push([properties.iGroupingOrderByColumnIndex2, properties.sGroupingColumnSortDirection2]);
            } // end of if (properties.iGroupingColumnIndex2 != -1)

            oTable.fnSettings().aaSortingFixed = aaSortingFixed;
            //Old way
            //oTable.fnSettings().aaSortingFixed = [[properties.iGroupingOrderByColumnIndex, properties.sGroupingColumnSortDirection]];

            switch (properties.sGroupBy) {
                case "name":
                    break;


                case "letter":

                    /* Create an array with the values of all the input boxes in a column */
                    oTable.fnSettings().aoColumns[properties.iGroupingOrderByColumnIndex].sSortDataType = "rg-letter";
                    $.fn.dataTableExt.afnSortData['rg-letter'] = function (oSettings, iColumn) {
                        var aData = [];
                        $('td:eq(' + iColumn + ')', oSettings.oApi._fnGetTrNodes(oSettings)).each(function () {
                            aData.push(_fnGetGroupByLetter(this.innerHTML));
                        });
                        return aData;
                    }


                    break;


                case "year":
                    /* Create an array with the values of all the input boxes in a column */
                    oTable.fnSettings().aoColumns[properties.iGroupingOrderByColumnIndex].sSortDataType = "rg-date";
                    $.fn.dataTableExt.afnSortData['rg-date'] = function (oSettings, iColumn) {
                        var aData = [];
                        var nTrs = oSettings.oApi._fnGetTrNodes(oSettings);
                        for (i = 0; i < nTrs.length; i++) {
                            aData.push(_fnGetYear(oTable.fnGetData(nTrs[i], iColumn)));
                        }

                        /*
                         $('td:eq(' + iColumn + ')', oSettings.oApi._fnGetTrNodes(oSettings)).each(function () {
                         aData.push(_fnGetYear(this.innerHTML));
                         });
                         */
                        return aData;
                    }
                    break;
                default:
                    break;

            } // end of switch (properties.sGroupBy)

            if (properties.bUseFilteringForGrouping)
                $.fn.dataTableExt.afnFiltering.push(_rowGroupingRowFilter);

            oTable.fnDraw();


        });
    };
})(jQuery);