/*** Copyright 2013 Teun Duynstee Licensed under the Apache License, Version 2.0 ***/ !(function(
    n,
    t
) {
    "function" == typeof define && define.amd ?
        define([], t) :
        "object" == typeof exports ?
        (module.exports = t()) :
        (n.firstBy = t());
})(this, function() {
    var n = (function() {
        function n(n) {
            return n;
        }

        function t(n) {
            return "string" == typeof n ? n.toLowerCase() : n;
        }

        function e(e, r) {
            if (
                ((r =
                        "number" == typeof r ? {
                            direction: r,
                        } :
                        r || {}),
                    "function" != typeof e)
            ) {
                var i = e;
                e = function(n) {
                    return n[i] ? n[i] : "";
                };
            }
            if (1 === e.length) {
                var o = e,
                    f = r.ignoreCase ? t : n;
                e = function(n, t) {
                    return f(o(n)) < f(o(t)) ? -1 : f(o(n)) > f(o(t)) ? 1 : 0;
                };
            }
            return r.direction === -1 ?

                function(n, t) {
                    return -e(n, t);
                } :
                e;
        }

        function r(n, t) {
            var i = "function" == typeof this && this,
                o = e(n, t),
                f = i ?

                function(n, t) {
                    return i(n, t) || o(n, t);
                } :
                o;
            return (f.thenBy = r), f;
        }
        return r;
    })();
    return n;
});

function sqGrid(gridElementId) {
    this.rows = []; // array of arrays
    this.sortedRowsBuffer = [];
    this.sortOrderDesc = null;
    this.columns = [];
    this.widths = [];
    this.gridElementId = gridElementId;
    this.showItemsFrom = 0;
    this.selectedRows = [];
    this.lastSelectedRow = null;
    this.checkedRows = [];
    this.checkedAll = false;
    this.onCellClick = null;
    this.onCellDoubleClick = null;
    this.onCellEnter = null;
    this.onCellLeave = null;
    this.onRowEnter = null;
    this.onRowLeave = null;
    this.onSelectionChanged = null;
    this.onKeyPress = null;
    this.currentGridFocused = false;
    this.firstColIsId = false;
    this.hideFirstCol = false;
    this.headerMover = null;
    this.headerMoverIndex = 0;
    this.headerMoverStartOffset = 0;
    this.itemsWillFit = 0;
    this.dblClickCounter = 0;
    this.dblClickTimer = 0;
    this.dblClickedRowBuffer = null;
    this.appendSortDebouncerTimer = null;
    this.redrawBodyDebouncerTimer = null;
    this.bodyHeight = 0;
    this.checkboxesOn = false;
    this.multiselectOn = false;
    this.selectedTrNode = null;
    this.cellEventHandler = null;
    this.scrollerClicked = false;
    this.sortedColumns = [];
    this.emptyGridText = "";
    this.smartRenderingUrl = false;
    this.smartRenderTotalRows = 0;
    this.smartRenderAllRowsClasses = {};
    this.smartRenderingSorting = false;
    this.httpLastRequestUrl = "";
    this.rowHeight = 21;
    this.widgets = {};
    this.xhr = null;
    this.hasFlexibleColumn = false;
    this.totalScrollHeight = 0;
    this.scrollResolution = 1;
    this.recalculatedScrolling = false;
    this.sortDisabled = false;
    this.headerHidden = false;
    this.hadError = false;
    this.showCellTitles = false;
    this.cellTitlesNewlines = "";
    this.scrollingByKeys = false;
    this.htmlCells = false;
    this.isVisible = false;
    this.sortChanged = true;
    this.sortColumnsChanged = true;
    this.onGridUserInteracted = null;
    this.lastTotalRowsHeight = null;
    this.doNotReScrollTimer = null;
    this.scrollEvents = [];
    this.init();
}

sqGrid.fn = sqGrid.prototype;

sqGrid.fn.init = function() {
    var thisSqGrid = this;
    this.gridElement = document.getElementById(this.gridElementId);
    if (this.gridElement == undefined) {
        return;
    }
    this.gridElement.sqgrid = this;
    this.gridElement.innerHTML = "";
    this.gridElement.classList.add("sq-grid");
    this.gridElement.classList.add("sq-grid-2");
    this.gridElementInner = document.createElement("div");
    this.gridElementInner.classList.add("sq-grid-inner");
    this.popupElement = document.createElement("div");
    this.popupElement.innerHTML = "Text<br>adsad";
    this.popupElement.classList.add("sq-grid-popup");
    this.headerTable = document.createElement("table");
    this.headerTable.classList.add("sq-grid-header");
    this.bodyTable = document.createElement("table");
    this.bodyTable.classList.add("sq-grid-body");
    this.scrollerBoxOuter = document.createElement("div");
    this.scrollerBoxOuter.classList.add("sq-grid-scrollerbox");
    this.scrollerBox = document.createElement("div");
    this.scrollerBox.classList.add("sq-grid-scrollerbox-inner");
    this.gridElementInner.appendChild(this.headerTable);
    this.gridElementInner.appendChild(this.bodyTable);
    this.gridElement.appendChild(this.gridElementInner);
    this.gridElement.appendChild(this.scrollerBoxOuter);
    document.body.appendChild(this.popupElement);
    this.scrollerBoxOuter.appendChild(this.scrollerBox);
    this.scrollerBoxOuter.addEventListener("scroll", function(e){
        thisSqGrid.handleScrollEvent(thisSqGrid, e);
    });
    /*     this.scrollerBoxOuter.onscroll = function (e) {
      
          }; */
    this.scrollerBoxOuter.onmousedown = function() {
        thisSqGrid.scrollerClicked = true;
        thisSqGrid.bodyTable.classList.add("sq-grid-body-moving");
    };
    this.gridElement.addEventListener("mousewheel", function(e) {
        thisSqGrid.mouseWheelEventThrottled(e);
    });
    this.gridElement.addEventListener("DOMMouseScroll", function(e) {
        thisSqGrid.mouseWheelEventThrottled(e);
    });
    this.scrollerBoxOuter.addEventListener("mousewheel", function(e) {
        return false;
    });
    this.scrollerBoxOuter.addEventListener("DOMMouseScroll", function(e) {
        return false;
    });
    this.gridElement.addEventListener("mouseleave", function(e) {
        thisSqGrid.popupElement.style.display = "none";
    });
    this.gridElement.addEventListener("click", function(e) {
        e.stopPropagation();
        thisSqGrid.currentGridFocused = true;
        thisSqGrid.gridElement.classList.add("focused");
    });
    this.headerTable.addEventListener("click", function(e) {
        e.stopPropagation();
        thisSqGrid.currentGridFocused = true;
        thisSqGrid.gridElement.classList.add("focused");
    });
    this.bodyTable.addEventListener("click", function(e) {
        e.stopPropagation();
        thisSqGrid.currentGridFocused = true;
        thisSqGrid.gridElement.classList.add("focused");
    });
    this.gridElementInner.addEventListener("click", function(e) {
        if (thisSqGrid.selectedRows.length > 0) {
            thisSqGrid.selectedRows = [];
            if (thisSqGrid.onSelectionChanged !== null) {
                thisSqGrid.onSelectionChanged([], []);
            }
            thisSqGrid.bodyRedraw();
        }
    });
    document.addEventListener("click", function(event) {
        var isClickInside = thisSqGrid.gridElement.contains(event.target);
        if (!isClickInside) {
            thisSqGrid.currentGridFocused = false;
            thisSqGrid.gridElement.classList.remove("focused");
        }
    });
    /* window.addEventListener("click", function (e) {
          thisSqGrid.currentGridFocused = false;
      }); */
    window.addEventListener("keydown", function(e) {
        if (thisSqGrid.currentGridFocused && e.code == "KeyA" && e.ctrlKey) {
            if (thisSqGrid.checkboxesOn) {
                thisSqGrid.selectAllCheckboxes();
                thisSqGrid.headerRedraw();
                thisSqGrid.bodyRedraw();
            }
            if (thisSqGrid.multiselectOn) {
                thisSqGrid.selectAllRows();
                thisSqGrid.bodyRedraw();
            }
            e.preventDefault();
        }
        if (thisSqGrid.currentGridFocused) {
            var lastSelected = -1;
            var originalLastSelected = lastSelected;
            if (thisSqGrid.selectedRows.length > 0) {
                lastSelected =
                    thisSqGrid.selectedRows[thisSqGrid.selectedRows.length - 1];
                originalLastSelected = lastSelected;
                if (thisSqGrid.sortedColumns.length > 0) {
                    lastSelected = thisSqGrid.getSortedIndexFromRowIndex(lastSelected);
                }
            }

            var newIndex = lastSelected;

            if (e.code == "ArrowUp") {
                thisSqGrid.scrollingByKeys = true;
                newIndex--;
                if (newIndex == -1) {
                    if (thisSqGrid.smartRenderingUrl) {
                        newIndex = thisSqGrid.smartRenderTotalRows - 1;
                    } else {
                        newIndex = thisSqGrid.rows.length - 1;
                    }
                }
                thisSqGrid.selectedRows = [];
                if (thisSqGrid.sortedColumns.length == 0) {
                    thisSqGrid.selectedRows = [newIndex];
                } else {
                    thisSqGrid.selectedRows = [
                        thisSqGrid.sortedRowsBuffer[newIndex].index,
                    ];
                }
            }

            if (e.code == "ArrowDown") {
                thisSqGrid.scrollingByKeys = true;
                newIndex++;
                thisSqGrid.selectedRows = [];
                if (thisSqGrid.sortedColumns.length == 0) {
                    if (
                        newIndex < thisSqGrid.rows.length ||
                        (thisSqGrid.smartRenderingUrl &&
                            newIndex < thisSqGrid.smartRenderTotalRows)
                    ) {
                        thisSqGrid.selectedRows = [newIndex];
                    } else {
                        thisSqGrid.selectedRows = [0];
                    }
                } else {
                    if (newIndex < thisSqGrid.sortedRowsBuffer.length) {
                        thisSqGrid.selectedRows = [
                            thisSqGrid.sortedRowsBuffer[newIndex].index,
                        ];
                    } else {
                        thisSqGrid.selectedRows = [thisSqGrid.sortedRowsBuffer[0].index];
                    }
                }
            }
            if (e.code == "Enter" || e.code == "NumpadEnter") {
                thisSqGrid.scrollingByKeys = true;
                newIndex++;
                if (thisSqGrid.sortedColumns.length == 0) {
                    if (newIndex < thisSqGrid.rows.length) {
                        thisSqGrid.selectedRows = [newIndex];
                    } else {
                        thisSqGrid.selectedRows = [0];
                    }
                } else {
                    if (newIndex < thisSqGrid.sortedRowsBuffer.length) {
                        thisSqGrid.selectedRows = [
                            thisSqGrid.sortedRowsBuffer[newIndex].index,
                        ];
                    } else {
                        thisSqGrid.selectedRows = [thisSqGrid.sortedRowsBuffer[0].index];
                    }
                }

                var clickIndex = newIndex;
                if (!thisSqGrid.smartRenderingUrl) {
                    if (clickIndex == thisSqGrid.rows.length) {
                        clickIndex = 0;
                    }
                    if (thisSqGrid.sortedRowsBuffer.length > 0) {
                        clickIndex = thisSqGrid.sortedRowsBuffer[clickIndex].index;
                    }

                    if (
                        thisSqGrid.selectedRows.length > 0 &&
                        thisSqGrid.onCellDoubleClick != null
                    ) {
                        thisSqGrid.onCellDoubleClick(
                            clickIndex,
                            0,
                            thisSqGrid.rows[clickIndex].cells
                        );
                    }
                }
            }

            if (e.code == "PageUp") {
                thisSqGrid.scrollingByKeys = true;
                newIndex -= thisSqGrid.itemsWillFit - 1;

                if (newIndex < 0) {
                    newIndex = 0;
                }

                if (thisSqGrid.sortedColumns.length == 0) {
                    thisSqGrid.selectedRows = [newIndex];
                } else {
                    thisSqGrid.selectedRows = [
                        thisSqGrid.sortedRowsBuffer[newIndex].index,
                    ];
                }
            }
            if (e.code == "Home") {
                thisSqGrid.scrollingByKeys = true;
                newIndex = 0;
                thisSqGrid.scrollerBoxOuter.scrollTop = 0;
                thisSqGrid.showItemsFrom = 0;

                if (thisSqGrid.sortedColumns.length == 0) {
                    thisSqGrid.selectedRows = [newIndex];
                } else {
                    thisSqGrid.selectedRows = [
                        thisSqGrid.sortedRowsBuffer[newIndex].index,
                    ];
                }
            }

            if (e.code == "End") {
                thisSqGrid.scrollingByKeys = true;

                if (thisSqGrid.smartRenderingUrl) {
                    newIndex = thisSqGrid.smartRenderTotalRows - 1;
                } else {
                    newIndex = thisSqGrid.rows.length - 1;
                }

                if (thisSqGrid.sortedColumns.length == 0) {
                    thisSqGrid.selectedRows = [newIndex];
                } else {
                    thisSqGrid.selectedRows = [
                        thisSqGrid.sortedRowsBuffer[newIndex].index,
                    ];
                }

                thisSqGrid.showItemsFrom = newIndex - thisSqGrid.itemsWillFit + 3;
                thisSqGrid.scrollerBoxOuter.scrollTop =
                    thisSqGrid.showItemsFrom * thisSqGrid.rowHeight;
            }

            if (e.code == "PageDown") {
                thisSqGrid.scrollingByKeys = true;
                newIndex += thisSqGrid.itemsWillFit - 2;
                if (
                    thisSqGrid.smartRenderingUrl &&
                    newIndex >= thisSqGrid.smartRenderTotalRows
                ) {
                    newIndex = thisSqGrid.smartRenderTotalRows - 1;
                } else {
                    if (!thisSqGrid.smartRenderingUrl &&
                        newIndex >= thisSqGrid.rows.length
                    ) {
                        newIndex = thisSqGrid.rows.length - 1;
                    }
                }

                if (thisSqGrid.sortedColumns.length == 0) {
                    thisSqGrid.selectedRows = [newIndex];
                } else {
                    thisSqGrid.selectedRows = [
                        thisSqGrid.sortedRowsBuffer[newIndex].index,
                    ];
                }
            }

            if (thisSqGrid.checkboxesOn || thisSqGrid.multiselectOn) {
                if (e.code == "Space") {
                    if (thisSqGrid.multiselectOn) {
                        newIndex++;
                        if (thisSqGrid.sortedColumns.length == 0) {
                            if (newIndex < thisSqGrid.rows.length) {
                                thisSqGrid.selectedRows.push(newIndex);
                            }
                        } else {
                            if (newIndex < thisSqGrid.sortedRowsBuffer.length) {
                                thisSqGrid.selectedRows.push(
                                    thisSqGrid.sortedRowsBuffer[newIndex].index
                                );
                            }
                        }
                    } else {
                        var addedSelRow = false;
                        if (thisSqGrid.selectedRows.length > 0) {
                            var index = thisSqGrid.checkedRows.indexOf(
                                thisSqGrid.selectedRows[0]
                            );
                            if (index == -1) {
                                thisSqGrid.checkedRows.push(thisSqGrid.selectedRows[0]);
                                addedSelRow = true;
                            } else {
                                thisSqGrid.checkedRows.splice(index, 1);
                                if (thisSqGrid.checkedAll) {
                                    thisSqGrid.checkedAll = false;
                                    thisSqGrid.headerRedraw();
                                }
                            }
                            if (thisSqGrid.onSelectionChanged !== null) {
                                thisSqGrid.onSelectionChanged(
                                    [thisSqGrid.selectedRows[0]], [addedSelRow]
                                );
                            }
                        }
                    }
                }
            }

            if (lastSelected != newIndex) {
                if (
                    thisSqGrid.smartRenderingUrl &&
                    newIndex >= thisSqGrid.smartRenderTotalRows
                ) {
                    thisSqGrid.scrollerBoxOuter.scrollTop = 0;
                    thisSqGrid.showItemsFrom = 0;
                    thisSqGrid.selectedRows = [0];
                } else if (newIndex == 0 || newIndex == thisSqGrid.rows.length) {
                    thisSqGrid.showItemsFrom = 0;
                    thisSqGrid.scrollerBoxOuter.scrollTop = 0;
                } else if (
                    newIndex >
                    thisSqGrid.showItemsFrom + thisSqGrid.itemsWillFit - 3
                ) {
                    thisSqGrid.showItemsFrom = newIndex - thisSqGrid.itemsWillFit + 3;
                    thisSqGrid.scrollerBoxOuter.scrollTop =
                        thisSqGrid.showItemsFrom * thisSqGrid.rowHeight;
                } else if (newIndex < thisSqGrid.showItemsFrom) {
                    thisSqGrid.showItemsFrom = newIndex;
                    thisSqGrid.scrollerBoxOuter.scrollTop =
                        thisSqGrid.showItemsFrom * thisSqGrid.rowHeight;
                }

                if (e.shiftKey && thisSqGrid.checkboxesOn) {
                    if (newIndex < lastSelected) {
                        for (var k = newIndex; k <= lastSelected; k++) {
                            var rowIndex =
                                thisSqGrid.sortedColumns.length == 0 ?
                                k :
                                thisSqGrid.sortedRowsBuffer[k].index;
                            thisSqGrid.checkedRows.push(rowIndex);
                        }
                    } else if (newIndex > lastSelected) {
                        for (var k = lastSelected; k <= newIndex; k++) {
                            var rowIndex =
                                thisSqGrid.sortedColumns.length == 0 ?
                                k :
                                thisSqGrid.sortedRowsBuffer[k].index;
                            thisSqGrid.checkedRows.push(rowIndex);
                        }
                    }

                    if (thisSqGrid.onSelectionChanged !== null) {
                        thisSqGrid.onSelectionChanged([], []);
                    }
                }

                if (thisSqGrid.checkedRows.length == 0) {
                    if (thisSqGrid.onSelectionChanged !== null) {
                        thisSqGrid.onSelectionChanged([], []);
                    }
                }
            }

            if (lastSelected == -1) {
                lastSelected = null;
            }

            thisSqGrid.lastSelectedRow = lastSelected;
            if (thisSqGrid.sortedColumns.length > 0) {
                thisSqGrid.lastSelectedRow = originalLastSelected;
            }

            if (thisSqGrid.onKeyPress != null) {
                thisSqGrid.onKeyPress(e.code, e);
            }
            thisSqGrid.bodyRedraw();
        }
    });
    window.addEventListener("keyup", function(e) {
        thisSqGrid.scrollingByKeys = false;
    });
    document.addEventListener("mousemove", function(e) {
        if (thisSqGrid.headerMover) {
            thisSqGrid.headerMover.style.width =
                thisSqGrid.headerMoverStartOffset + e.pageX + "px";
            thisSqGrid.widths[thisSqGrid.headerMoverIndex] =
                thisSqGrid.headerMover.style.width;
            thisSqGrid.bodyRedraw();
        }
        /* if (thisSqGrid.scrollerClicked) {
                    thisSqGrid.showItemsFrom = Math.round((thisSqGrid.scrollerBoxOuter.scrollTop * thisSqGrid.scrollResolution) / thisSqGrid.rowHeight);
                    thisSqGrid.bodyRedrawThrottled();
                } */
    });
    document.addEventListener("mouseup", function() {
        if (thisSqGrid.headerMover) {
            thisSqGrid.headerMover = undefined;
        }
        if (thisSqGrid.scrollerClicked) {
            thisSqGrid.scrollerClicked = false;
            thisSqGrid.bodyTable.classList.remove("sq-grid-body-moving");
        }
    });
    window.addEventListener("resize", function(e) {
        thisSqGrid.setNewDimensions();
        thisSqGrid.sortAll();
    });
    this.setNewDimensions();
    this.sortAll();
};

sqGrid.fn.sortAll = function(disableBodyRedraw) {
    if (!this.isVisible) {
        return;
    }

    var thisSqGrid = this;
    if (thisSqGrid.sortedColumns.length > 0 && thisSqGrid.sortChanged) {
        if (thisSqGrid.smartRenderingSorting) {
            if (this.sortColumnsChanged || this.headerTable.innerHTML == "") {
                this.headerRedraw();
                this.sortColumnsChanged = false;
            }
            this.bodyRedraw();
            return;
        }

        thisSqGrid.sortedRowsBuffer = [];

        for (let j = 0; j < thisSqGrid.rows.length; j++) {
            thisSqGrid.sortedRowsBuffer.push({
                index: j,
                values: [],
            });
        }

        var sortFunctions = [];

        for (var i = 0; i < this.sortedColumns.length; i++) {
            var columnIndex = thisSqGrid.sortedColumns[i].columnIndex;
            var sortType = thisSqGrid.columns[columnIndex].sort;
            if (sortType == undefined) {
                sortType = "text";
            }

            var thisSortFunction = thisSqGrid.sortFunctionText;

            if (sortType == "number") {
                thisSortFunction = thisSqGrid.sortFunctionNumber;
            }

            if (sortType == "dateDotted") {
                thisSortFunction = thisSqGrid.sortFunctionDateDotted;
            }

            var getType = {};
            if (getType.toString.call(sortType) === "[object Function]") {
                thisSortFunction = sortType;
            }

            sortFunctions.push(thisSortFunction);

            for (var j = 0; j < this.rows.length; j++) {
                var iValue = thisSqGrid.rows[j].cells[columnIndex]
                if ((typeof iValue === 'undefined') && sortType == "text"){
                    iValue="";
                }

                thisSqGrid.sortedRowsBuffer[j].values.push(
                    iValue
                );
            }
        }

        /*
            thisSqGrid.sortedRowsBuffer.sort(function (a, b) {
                return thisSqGrid.sortFunctionNumber(a.index, b.index);
            }); */

        var sortWrapperFunction = firstBy(
            function(a, b) {
                return sortFunctions[0](a.values[0], b.values[0]);
            },
            thisSqGrid.sortedColumns[0].isDesc ? -1 : 1
        ).thenBy(function(a, b) {
            return thisSqGrid.sortFunctionNumber(a.index, b.index);
        });

        if (thisSqGrid.sortedColumns.length == 2) {
            sortWrapperFunction = firstBy(
                function(a, b) {
                    return sortFunctions[0](a.values[0], b.values[0]);
                },
                thisSqGrid.sortedColumns[0].isDesc ? -1 : 1
            ).thenBy(
                function(a, b) {
                    return sortFunctions[1](a.values[1], b.values[1]);
                },
                thisSqGrid.sortedColumns[1].isDesc ? -1 : 1
            );
        }

        if (thisSqGrid.sortedColumns.length == 3) {
            sortWrapperFunction = firstBy(
                    function(a, b) {
                        return sortFunctions[0](a.values[0], b.values[0]);
                    },
                    thisSqGrid.sortedColumns[0].isDesc ? -1 : 1
                )
                .thenBy(
                    function(a, b) {
                        return sortFunctions[1](a.values[1], b.values[1]);
                    },
                    thisSqGrid.sortedColumns[1].isDesc ? -1 : 1
                )
                .thenBy(
                    function(a, b) {
                        return sortFunctions[2](a.values[2], b.values[2]);
                    },
                    thisSqGrid.sortedColumns[2].isDesc ? -1 : 1
                );
        }

        this.sortedRowsBuffer.sort(sortWrapperFunction);

        thisSqGrid.sortChanged = false;
    }

    if (disableBodyRedraw != true) {
        if (this.sortColumnsChanged || this.headerTable.innerHTML == "") {
            this.headerRedraw();
            this.sortColumnsChanged = false;
        }
        this.bodyRedraw();
    }
};

sqGrid.fn.selectRow = function(selecteIndex, uncheckAll, selectOnly) {
    if (uncheckAll) {
        this.selectedRows = [];
        this.checkedRows = [];
    }

    this.selectedRows.push(selecteIndex);
    if (this.checkboxesOn && selectOnly != true) {
        this.checkedRows.push(selecteIndex);
    }

    this.bodyRedraw();
};

sqGrid.fn.deselectAllCheckboxes = function(doNotEmitChangeEvent) {
    var changedRows = [];
    var truedRows = [];
    if (this.checkboxesOn) {
        for (var i = 0; i < this.checkedRows.length; i++) {
            changedRows.push(i);
            truedRows.push(false);
        }
        this.checkedRows = [];
    } else {
        this.selectedRows = [];
    }
    if (this.onSelectionChanged !== null && !doNotEmitChangeEvent) {
        this.onSelectionChanged(changedRows, truedRows);
    }
    this.checkedAll = false;

    this.bodyRedraw();
};

sqGrid.fn.selectAllCheckboxes = function() {
    this.deselectAllCheckboxes(true);
    var changedRows = [];
    var truedRows = [];
    for (var i = 0; i < this.rows.length; i++) {
        if (this.rows[i].disabled) {
            continue;
        }
        if (this.checkboxesOn) {
            this.checkedRows.push(i);
        } else {
            this.selectedRows.push(i);
        }
        changedRows.push(i);
        truedRows.push(true);
    }
    if (this.onSelectionChanged !== null) {
        this.onSelectionChanged(changedRows, truedRows);
    }
    this.checkedAll = true;

    this.bodyRedraw();
};

sqGrid.fn.setNewDimensions = function() {
    this.isVisible = this.gridElement.offsetParent !== null;
    if (this.isVisible) {
        this.bodyHeight = this.gridElement.offsetHeight - 24;
    }
    // this.httpLastRequestUrl = "";
};

sqGrid.fn.setEmptyGridText = function(text) {
    this.emptyGridText = text;
};

sqGrid.fn.enableCellTitles = function(enable, charForNewLines) {
    if (enable == undefined) enable = true;
    this.showCellTitles = enable;
    if (charForNewLines != undefined) {
        this.cellTitlesNewlines = charForNewLines;
    }
};

sqGrid.fn.enableHtmlCells = function(enable) {
    if (enable == undefined) enable = true;
    this.htmlCells = enable;
};

sqGrid.fn.setSmartRendering = function(dataUrl, callback, disableRedraw) {
    this.httpLastRequestUrl = "";
    this.smartRenderingUrl = dataUrl;
    this.showItemsFrom = 0;
    if (disableRedraw) {
        this.bodyRedraw(false, callback);
    } else {
        if (callback) callback();
    }
};

sqGrid.fn.setSmartRenderingSorting = function(
    sortColumnKey,
    sortDirectionKey
) {
    if (sortColumnKey == undefined) {
        sortColumnKey = "sortColumn";
    }
    if (sortDirectionKey == undefined) {
        sortDirectionKey = "sortDirection";
    }
    this.smartRenderingSorting = {
        columnKey: sortColumnKey,
        directionKey: sortDirectionKey,
    };
    this.headerRedraw();
};

sqGrid.fn.getSortedIndexFromRowIndex = function(rowIndex) {
    if (this.sortedColumns.length == 0) {
        return rowIndex;
    }
    for (var i = 0; i < this.sortedRowsBuffer.length; i++) {
        if (this.sortedRowsBuffer[i].index == rowIndex) {
            return i;
        }
    }
    return 0;
};

sqGrid.fn.setFirstColumnAsId = function(firstColIsId, hideFirstCol) {
    this.firstColIsId = firstColIsId;
    this.hideFirstCol = hideFirstCol;
};

sqGrid.fn.enableCheckboxes = function(enableCheckboxes, enableMultiSelect) {
    if (enableCheckboxes === undefined) {
        enableCheckboxes = true;
    }
    if (enableMultiSelect === undefined) {
        enableMultiSelect = false;
    }
    this.checkboxesOn = enableCheckboxes;
    this.multiselectOn = enableMultiSelect;
};

sqGrid.fn.disableSorting = function(disableSorting) {
    if (disableSorting === undefined) {
        disableSorting = true;
    }
    this.sortDisabled = disableSorting;
};

sqGrid.fn.hideHeader = function(headerHidden) {
    if (headerHidden === undefined) {
        headerHidden = true;
    }
    this.headerHidden = headerHidden;

    if (this.headerHidden) {
        this.gridElement.classList.add("sq-grid-headerless");
    } else {
        this.gridElement.classList.remove("sq-grid-headerless");
    }
};

sqGrid.fn.mouseWheelEventThrottled = sqGridThrottle(
    function(e) {
        this.mouseWheelEvent(e);
    },
    50,
    true
);

sqGrid.fn.mouseWheelEvent = function(e) {
    e.preventDefault();
    e.stopPropagation();
    var delta = e.wheelDelta ? e.wheelDelta : -e.detail;
    if (!this.smartRenderingUrl && this.rows.length < 300) {
        delta = delta > 0 ? this.rowHeight : -this.rowHeight;
        var thisTopScrollMax =
            parseInt(this.scrollerBox.style.height) -
            this.itemsWillFit * this.rowHeight;
        if (delta < 0) {
            if (
                this.scrollerBoxOuter.scrollTop + this.rowHeight >=
                thisTopScrollMax
            ) {
                delta = -this.rowHeight * 3;
            }
        } else {
            if (this.showItemsFrom == 1) {
                delta = this.rowHeight * 3;
            }
        }
    }

    this.scrollerBoxOuter.scrollTop -= delta;
    /*     this.showItemsFrom = Math.round(
              (this.scrollerBoxOuter.scrollTop * this.scrollResolution) / this.rowHeight
          ); */
    /*     console.error("scrolltop", this.scrollerBoxOuter.scrollTop);
          console.error("mouse wheel items from ", this.showItemsFrom); */
    /*     if (!this.smartRenderingUrl) {
              this.bodyRedraw();
          } else {
              this.bodyRedrawThrottled();
          } */
    if (this.onGridUserInteracted != null) {
        this.onGridUserInteracted("scroll");
    }
};

sqGrid.fn.headerRedraw = function() {
    if (!this.isVisible) {
        return;
    }

    if (this.headerHidden) {
        this.headerTable.innerHTML = "";

        return;
    }

    var thisSqGrid = this;
    var hasSetWidths = this.widths.length == this.columns.length;
    this.headerTable.innerHTML = "";
    var tr = document.createElement("tr");
    if (this.checkboxesOn) {
        var td = document.createElement("td");
        td.innerHTML = "";
        td.classList.add("sq-grid-column-checkbox");
        var checkbox = document.createElement("input");
        checkbox.type = "checkbox";
        checkbox.checked = this.checkedAll;
        checkbox.onclick = function(e) {
            if (checkbox.checked) {
                thisSqGrid.selectAllCheckboxes();
            } else {
                thisSqGrid.deselectAllCheckboxes();
            }
            thisSqGrid.bodyRedraw();
        };
        td.appendChild(checkbox);
        tr.appendChild(td);
    }
    for (var i = 0; i < this.columns.length; i++) {
        if (this.hideFirstCol && i == 0) {
            continue;
        }
        var hasFixedWidth = false;
        var td = document.createElement("td");
        if ("tooltip" in this.columns[i] && this.columns[i].tooltip != undefined) {
            td.title = this.columns[i].tooltip;
        }
        var tn = document.createTextNode(this.columns[i].title);
        if (hasSetWidths) {
            if (this.widths[i] != "*px") {
                if (this.widths[i].indexOf("f") != -1) {
                    hasFixedWidth = true;
                    td.style.width = this.widths[i].replace("f", "");
                } else {
                    td.style.width = this.widths[i];
                }
            }
        }

        if ("class" in this.columns[i] && this.columns[i].class != undefined) {
            td.classList.add(this.columns[i].class.trim());
        }

        (function(sortIndex) {
            td.onclick = function(e) {
                if (thisSqGrid.onGridUserInteracted != null) {
                    thisSqGrid.onGridUserInteracted("sort");
                }
                thisSqGrid.currentGridFocused = true;
                thisSqGrid.gridElement.classList.add("focused");

                if (
                    (thisSqGrid.smartRenderingUrl && !thisSqGrid.smartRenderingSorting) ||
                    thisSqGrid.sortDisabled
                ) {
                    return;
                }

                var thisDesc = false;
                var thisIndex = null;
                for (var k = 0; k < thisSqGrid.sortedColumns.length; k++) {
                    if (thisSqGrid.sortedColumns[k].columnIndex == sortIndex) {
                        if (thisSqGrid.sortedColumns[k].isDesc === false) {
                            thisDesc = true;
                        } else if (thisSqGrid.sortedColumns[k].isDesc === true) {
                            thisDesc = null;
                        }
                        thisIndex = k;
                        break;
                    }
                }

                if (!e.ctrlKey || thisSqGrid.smartRenderingSorting) {
                    thisSqGrid.sortedColumns = [];
                    thisSqGrid.sortColumnsChanged = true;
                }

                if (
                    e.ctrlKey &&
                    thisSqGrid.sortedColumns.length > 2 &&
                    thisIndex === null &&
                    !thisSqGrid.smartRenderingSorting
                ) {
                    return;
                }

                if (thisDesc !== null) {
                    var thisNewObject = {
                        columnIndex: sortIndex,
                        isDesc: thisDesc,
                    };
                    if (thisIndex !== null && thisSqGrid.sortedColumns.length > 0) {
                        thisSqGrid.sortedColumns[thisIndex] = thisNewObject;
                        thisSqGrid.sortColumnsChanged = true;
                    } else {
                        thisSqGrid.sortedColumns.push(thisNewObject);
                        thisSqGrid.sortColumnsChanged = true;
                    }
                } else if (thisSqGrid.sortedColumns.length > 0 && thisIndex !== null) {
                    thisSqGrid.sortedColumns.splice(thisIndex, 1);
                    thisSqGrid.sortColumnsChanged = true;
                }

                thisSqGrid.sortChanged = true;

                thisSqGrid.sortAll();
            };
        })(i);

        for (var k = 0; k < thisSqGrid.sortedColumns.length; k++) {
            if (thisSqGrid.sortedColumns[k].columnIndex == i) {
                if (thisSqGrid.sortedColumns[k].isDesc) {
                    td.classList.add("sq-sort-desc");
                } else {
                    td.classList.add("sq-sort-asc");
                }
                if (!thisSqGrid.smartRenderingSorting) {
                    var sortNumNode = document.createElement("div");
                    sortNumNode.classList.add("sq-header-sortnum");
                    sortNumNode.innerHTML = k + 1;
                    td.appendChild(sortNumNode);
                }
            }
        }

        var grip = document.createElement("div");
        grip.innerHTML = "&nbsp;";
        grip.classList.add("sq-header-grip");
        (function(thisTd, index) {
            grip.addEventListener("mousedown", function(e) {
                e.stopPropagation();
                thisSqGrid.headerMover = thisTd;
                thisSqGrid.headerMoverIndex = index;
                thisSqGrid.headerMoverStartOffset = thisTd.offsetWidth - e.pageX;
            });
        })(td, i);

        grip.addEventListener("click", function(e) {
            e.stopPropagation();
            thisSqGrid.currentGridFocused = true;
            thisSqGrid.gridElement.classList.add("focused");
        });

        if (!hasFixedWidth) {
            td.appendChild(grip);
        }

        td.appendChild(tn);
        tr.appendChild(td);
    }
    if (hasSetWidths) {
        var td = document.createElement("td");
        td.classList.add("sq-grid-column-placeholder");
        tr.appendChild(td);
    }
    this.headerTable.appendChild(tr);
};

sqGrid.fn.makeHttpRequest = function(url, callback, force, errorCallback) {
    if (this.httpLastRequestUrl == url && !force) {
        this.rowsRedraw();
        if (this.lastTotalRowsHeight != 0) {
            this.setScrollHeight(this.lastTotalRowsHeight);
        }
        if (errorCallback) errorCallback();
        return;
    }

    this.httpLastRequestUrl = url;

    if (this.xhr != null) {
        this.xhr.abort();
    }

    this.xhr = new XMLHttpRequest();
    this.xhr.onreadystatechange = function() {
        if (this.readyState == 4) {
            if (this.status == 200) {
                callback(this.responseText);
            } else {
                if (errorCallback) errorCallback();
            }
        }
    };
    this.xhr.open("GET", url, true);
    if (getAuthToken()) {
        this.xhr.setRequestHeader("sq-auth-token", getAuthToken());
    }
    this.xhr.send(null);
};

sqGrid.fn.smartRenderingRedrawBody = function(force, callback) {
    if (
        this.smartRenderTotalRows > 0 &&
        this.showItemsFrom >= this.smartRenderTotalRows
    ) {
        this.showItemsFrom = Math.floor(this.itemsWillFit / 2);
    }

    var thisSqGrid = this;

    var sortUrlAddon = "";

    if (this.smartRenderingSorting && this.sortedColumns.length > 0) {
        sortUrlAddon =
            "&" +
            this.smartRenderingSorting.columnKey +
            "=" +
            this.sortedColumns[0].columnIndex +
            "&" +
            this.smartRenderingSorting.directionKey +
            "=" +
            (this.sortedColumns[0].isDesc ? "desc" : "asc");
    }

    this.makeHttpRequest(
        this.smartRenderingUrl +
        "&posStart=" +
        this.showItemsFrom +
        "&count=" +
        this.itemsWillFit +
        sortUrlAddon,
        function(data) {
            var jsonData = JSON.parse(data);

            var totalRowsHeight = 0;
            thisSqGrid.rows = [];

            if ("error" in jsonData) {
                thisSqGrid.hadError = jsonData.error;
            } else {
                thisSqGrid.hadError = false;
                totalRowsHeight =
                    jsonData.total_count * thisSqGrid.rowHeight + thisSqGrid.rowHeight;
                thisSqGrid.smartRenderTotalRows = jsonData.total_count;
                thisSqGrid.rows = [];
                for (var i = 0; i < jsonData.rows.length; i++) {
                    var newRowParsed = thisSqGrid.parseOneRow(jsonData.rows[i].data);
                    var newRow = {
                        index: thisSqGrid.rows.length,
                        cells: newRowParsed.cells,
                        userData: {},
                        icons: [],
                        class: "",
                        cellClass: newRowParsed.cellsClasses,
                        disabled: false,
                        styles: newRowParsed.cellsStyles,
                    };

                    if ("bgColor" in jsonData.rows[i] && jsonData.rows[i].bgColor != "") {
                        newRow.userData.bgColor = jsonData.rows[i].bgColor;
                    }

                    thisSqGrid.rows.push(newRow);
                }
            }

            if (thisSqGrid.onAfterDataGot != undefined) {
                thisSqGrid.onAfterDataGot(thisSqGrid.rows);
            }

            thisSqGrid.rowsRedraw();

            thisSqGrid.lastTotalRowsHeight = totalRowsHeight;
            thisSqGrid.setScrollHeight(totalRowsHeight);

            if (callback) callback();
        },
        force || false,
        callback
    );
};

sqGrid.fn.setScrollHeight = function(totalRowsHeight, ignoreTimer) {
    var thisSqGrid = this;
    if (!ignoreTimer) {
        thisSqGrid.handleDoNotReScrollTimer(thisSqGrid);
        this.totalScrollHeight = totalRowsHeight;
    }

    var hasHorizontalScrollbar =
        this.gridElementInner.scrollWidth > this.gridElementInner.clientWidth;
    if (hasHorizontalScrollbar) {
        this.gridElement.classList.add("sq-grid-horizontal-scrollbar");
    } else {
        this.gridElement.classList.remove("sq-grid-horizontal-scrollbar");
    }

    if (totalRowsHeight > 30000000) {
        var multiplier =
            (totalRowsHeight / this.rowHeight / this.itemsWillFit / 100000) *
            1.1042402826855 -
            0.47526501766785;
        var additionHeight = Math.ceil(
            this.rowHeight * this.itemsWillFit * multiplier
        );
        this.scrollResolution = (totalRowsHeight + additionHeight) / 30000000;
        this.scrollerBox.style.height = Math.floor(30000000).toString() + "px";
    } else {
        var additionHeight = Math.ceil(this.rowHeight * 1);
        /* if (hasHorizontalScrollbar) {
                additionHeight += Math.ceil(
                    this.rowHeight * 1
                );
            } */
        this.scrollResolution = 1;
        this.scrollerBox.style.height =
            (totalRowsHeight + additionHeight).toString() + "px";
    }

    this.setScrollbarPaddings();
};

sqGrid.fn.rowsRedraw = function() {
    this.bodyTable.innerHTML = "";

    if (this.hadError) {
        this.bodyTable.innerHTML =
            '<tr><td class="sq-grid-empty-text">' + this.hadError + "</td></tr>";
        return;
    }

    for (var i = 0; i < this.rows.length; i++) {
        var realI = this.smartRenderingUrl ? i + this.showItemsFrom : i;
        this.drawOneRow(this.rows[i], i, realI);
    }

    if (this.rows.length == 0 && this.emptyGridText != "") {
        this.bodyTable.innerHTML =
            '<tr><td class="sq-grid-empty-text">' + this.emptyGridText + "</td></tr>";
    }
};

sqGrid.fn.handleDoNotReScrollTimer = function(thisSqGrid) {
    this.doNotReScrollTimer = setTimeout(function() {
        thisSqGrid.doNotReScrollTimer = null;
        if(thisSqGrid.scrollEvents.length > 0){
            const event = thisSqGrid.scrollEvents[thisSqGrid.scrollEvents.length - 1];
            thisSqGrid.scrollEvents = []
            thisSqGrid.handleScrollEvent(thisSqGrid, event);
        }
    }, 100);
}

sqGrid.fn.handleScrollEvent = function(thisSqGrid, e){
    if (thisSqGrid.doNotReScrollTimer) {
        thisSqGrid.scrollEvents.push(e)
        return;
    }
    if (!thisSqGrid.scrollingByKeys) {
        var scrolledTo = e.target.scrollTop;
        if (
            thisSqGrid.smartRenderingUrl &&
            e.target.scrollTop + e.target.clientHeight + 5 >= e.target.scrollHeight
        ) {
            thisSqGrid.showItemsFrom =
                thisSqGrid.smartRenderTotalRows - thisSqGrid.itemsWillFit + 2;
        } else {
            thisSqGrid.showItemsFrom = Math.round(
                (scrolledTo * thisSqGrid.scrollResolution) / thisSqGrid.rowHeight
            );
        }
    }
    if (thisSqGrid.onGridUserInteracted != null) {
        thisSqGrid.onGridUserInteracted("scroll");
    }
    if (!thisSqGrid.smartRenderingUrl) {
        thisSqGrid.bodyRedraw();
    } else {
        thisSqGrid.bodyRedrawThrottled();
    }
}

sqGrid.fn.bodyRedraw = function(force, callback) {
    if (!this.isVisible) {
        if (callback) callback();
        return;
    }
    if (this.bodyHeight < 10) {
        if (callback) callback();
        return;
    }

    var event = new Event("change", { bubbles: true });
    var inputsWidgets = this.bodyTable.querySelectorAll("input:focus");
    for (var i = 0; i < inputsWidgets.length; i++) {
        inputsWidgets[i].dispatchEvent(event);
    }

    this.itemsWillFit = Math.ceil(this.bodyHeight / this.rowHeight);

    if (this.smartRenderingUrl) {
        this.smartRenderingRedrawBody(force, callback);
        return;
    }

    var totalRendered = 0;

    if (this.showItemsFrom > this.rows.length) {
        var newItemsFrom = this.rows.length - this.itemsWillFit;
        if (newItemsFrom < 0) newItemsFrom = 0;
        this.showItemsFrom = newItemsFrom;
        this.scrollerBoxOuter.scrollTop = this.showItemsFrom * this.rowHeight;
    }

    if (this.itemsWillFit > this.rows.length) {
        this.showItemsFrom = 0;
        this.scrollerBoxOuter.scrollTop = this.showItemsFrom * this.rowHeight;
    }

    if (this.showItemsFrom % 2 == 1) {
        this.gridElement.classList.add("sq-grid-starts-odd");
    } else {
        this.gridElement.classList.remove("sq-grid-starts-odd");
    }

    totalRendered = 0;

    var thisSqGrid = this;

    var hasSetWidths = this.widths.length == this.columns.length;
    IncrementalDOM.patch(this.bodyTable, function() {
        for (var i = thisSqGrid.showItemsFrom; i < thisSqGrid.rows.length; i++) {
            if (thisSqGrid.sortedColumns.length > 0) {
                var origI = thisSqGrid.sortedRowsBuffer[i].index;
            } else {
                var origI = i;
            }
            var thisRowRealI = origI;
            var rowIsChecked = thisSqGrid.checkedRows.indexOf(thisRowRealI) != -1;
            var row = thisSqGrid.rows[origI];
            var rowClasses = ["sq-grid-row"];
            if ("class" in row && row.class != "") {
                var classes = [];
                if (Array.isArray(row.class)) {
                    classes = row.class;
                } else {
                    classes = [row.class];
                }
                for (var clI = 0; clI < classes.length; clI++) {
                    if (!classes[clI] || classes[clI].length == 0) {
                        continue;
                    }
                    rowClasses.push(classes[clI]);
                }
            }
            if (row.disabled) {
                rowClasses.push("disabled");
            }
            if (thisSqGrid.selectedRows.indexOf(thisRowRealI) != -1) {
                rowClasses.push("sq-grid-selectedrow");
            }
            if (rowIsChecked) {
                rowClasses.push("sq-grid-checkedrow");
            }
            IncrementalDOM.elementOpen(
                "tr",
                null,
                null,
                "class",
                rowClasses.join(" "),
                "data-row-index",
                thisRowRealI,
                "data-rowIndex",
                thisRowRealI
            );
            if (thisSqGrid.checkboxesOn) {
                IncrementalDOM.elementOpen(
                    "td",
                    null,
                    null,
                    "class",
                    "sq-grid-column-checkbox",
                    "onmousedown",
                    function(e) {
                        if (!e.shiftKey) {
                            Object.defineProperty(e, "ctrlKey", {
                                writable: false,
                                value: true,
                            });
                        }
                        thisSqGrid.onCellMouseDownPrivate(e);
                        thisSqGrid.onCellClickPrivate(e);
                    }
                );
                IncrementalDOM.elementOpenStart("input");
                IncrementalDOM.attr("type", "checkbox");
                IncrementalDOM.attr("onclick", function(e) {
                    e.preventDefault();
                    return false;
                });
                IncrementalDOM.attr("onmousedown", function(e) {
                    e.preventDefault();
                    return false;
                });
                if (rowIsChecked) {
                    IncrementalDOM.attr("checked", "true");
                }
                IncrementalDOM.elementOpenEnd("input");
                IncrementalDOM.elementClose("input");
                IncrementalDOM.elementClose("td");
            }
            for (var j = 0; j < thisSqGrid.columns.length; j++) {
                if (thisSqGrid.hideFirstCol && j == 0) {
                    continue;
                }
                var cellWidth = "auto";
                if (i == thisSqGrid.showItemsFrom && hasSetWidths) {
                    if (thisSqGrid.widths[j] != "*px") {
                        if (thisSqGrid.widths[j].indexOf("f") != -1) {
                            cellWidth = thisSqGrid.widths[j].replace("f", "");
                        } else {
                            cellWidth = thisSqGrid.widths[j];
                        }
                    }
                }
                IncrementalDOM.elementOpenStart("td");
                IncrementalDOM.attr("data-cell-index", j);
                IncrementalDOM.attr("data-colIndex", j);
                var cellClasses = ["sq-grid-cell"];
                cellClasses.push("sq-grid-cell-align-" + thisSqGrid.columns[j].align);
                if (
                    thisSqGrid.smartRenderingUrl == false &&
                    thisSqGrid.getCellCustomData(thisRowRealI, j, "cellsDisabled")
                ) {
                    cellClasses.push("disabled");
                }

                for (var k = 0; k < thisSqGrid.columns[j].cellClasses.length; k++) {
                    cellClasses.push(thisSqGrid.columns[j].cellClasses[k]);
                }

                if ("cellClass" in row && j in row.cellClass) {
                    for (var k = 0; k < row.cellClass[j].length; k++) {
                        cellClasses.push(row.cellClass[j][k].trim());
                    }
                }

                IncrementalDOM.attr("class", cellClasses.join(" "));
                if ("styles" in row) {
                    if (row.styles[j] != "") {
                        IncrementalDOM.attr("style", row.styles[j]);
                    }
                }
                if (cellWidth != "auto") {
                    IncrementalDOM.attr("width", cellWidth);
                }
				
                if (thisSqGrid.showCellTitles) {
                    var cellTitle = String(row.cells[j]);
                    if (cellTitle != "" && thisSqGrid.cellTitlesNewlines != "") {
                        cellTitle = cellTitle
                            .split(thisSqGrid.cellTitlesNewlines)
                            .join("\n");
                    }
                    if (cellTitle.indexOf("</a>") < 0 && !cellTitle.startsWith('{{')) {
                        IncrementalDOM.attr("title", cellTitle);
                    }
                }
                var thisCellIcon = null;
                if (thisSqGrid.smartRenderingUrl == false) {
                    thisCellIcon = thisSqGrid.getIconPrivate(thisRowRealI, j);
                }
                if (thisCellIcon != null && thisCellIcon.showIcon) {
                    IncrementalDOM.attr("data-icontext", thisCellIcon.iconText);
                    IncrementalDOM.attr("onmouseover", function(e) {
                        if (e.target.classList.contains("sq-grid-icon")) {
                            thisSqGrid.popupElement.innerHTML =
                                this.getAttribute("data-icontext");
                            thisSqGrid.popupElement.style.display = "block";
                            var viewportOffset = e.target.getBoundingClientRect();
                            var viewportOffsetPopup =
                                thisSqGrid.popupElement.getBoundingClientRect();
                            var top = viewportOffset.top;
                            var left = viewportOffset.left;
                            var height = viewportOffsetPopup.height;
                            thisSqGrid.popupElement.style.top = top - height + 16 + "px";
                            thisSqGrid.popupElement.style.left = left + 20 + "px";
                        }
                    });
                    IncrementalDOM.attr("onmouseout", function(e) {
                        thisSqGrid.popupElement.style.display = "none";
                    });
                }
                IncrementalDOM.attr("onmousedown", function(e) {
                    thisSqGrid.onCellMouseDownPrivate(e);
                    thisSqGrid.onCellClickPrivate(e);
                    if (thisSqGrid.dblClickCounter == 0) {
                        thisSqGrid.dblClickTimer = setTimeout(function() {
                            thisSqGrid.dblClickCounter = 0;
                        }, 400);
                        thisSqGrid.dblClickCounter = 1;
                    } else if (thisSqGrid.dblClickCounter == 1) {
                        clearTimeout(thisSqGrid.dblClickTimer);
                        thisSqGrid.dblClickCounter = 0;
                        thisSqGrid.onDblcCellClickPrivate(e);
                    }
                });
                IncrementalDOM.attr("onmouseenter", function(e) {
                    thisSqGrid.onCellEnterPrivate(e);
                });
                IncrementalDOM.attr("onmouseleave", function(e) {
                    thisSqGrid.onCellLeavePrivate(e);
                });
                IncrementalDOM.elementOpenEnd();
                var hasWidget = false;
                var widgetMatched = thisSqGrid.parseWidget(row.cells[j]);
                if (widgetMatched && widgetMatched.widget in thisSqGrid.widgets) {
                    thisSqGrid.widgets[widgetMatched.widget](
                        thisSqGrid,
                        thisRowRealI,
                        j,
                        row,
                        widgetMatched.params
                    );
                    hasWidget = true;
                }
                if (!hasWidget) {
                    if (thisCellIcon != null && thisCellIcon.showIcon) {
                        var iconClass =
                            "sq-grid-icon sq-grid-icon-" +
                            thisCellIcon.iconAlign +
                            " " +
                            thisCellIcon.iconClass;
                        IncrementalDOM.elementOpen("div", null, null, "class", iconClass);
                        IncrementalDOM.elementClose("div");
                    }
                    thisSqGrid.formatCellTextICDom(
                        row.cells[j],
                        thisSqGrid.columns[j].type
                    );
                }
                IncrementalDOM.elementClose("td");
            }
            if (hasSetWidths) {
                IncrementalDOM.elementOpen(
                    "td",
                    null,
                    null,
                    "class",
                    "sq-grid-column-placeholder"
                );
                IncrementalDOM.elementClose("td");
            }
            IncrementalDOM.elementClose("tr");
            if (totalRendered == thisSqGrid.itemsWillFit) {
                break;
            }
            totalRendered++;
        }
    });

    var checkboxes = this.bodyTable.querySelectorAll(
        ".sq-grid-column-checkbox input[type=checkbox]"
    );
    for (var i = 0; i < checkboxes.length; i++) {
        checkboxes[i].checked = checkboxes[i].defaultChecked;
    }

    /* var domInputs = this.bodyTable.querySelectorAll(".grid-widget-input input[type=text]:focus");
      for (var i = 0; i < domInputs.length; i++) {
          domInputs[i].blur();
      } */

    if (this.rows.length == 0 && this.emptyGridText != "") {
        this.bodyTable.innerHTML =
            '<tr><td class="sq-grid-empty-text">' + this.emptyGridText + "</td></tr>";
    }

    // this.popupElement.style.display = "none";

    var totalRowsHeight = this.rows.length * this.rowHeight;
    this.setScrollHeight(totalRowsHeight, true);

    if (callback) callback();
};

sqGrid.fn.scrollToRowIndex = function(rowIndex, doNotSelect) {
    if (this.smartRenderingUrl) {
        if (rowIndex >= this.smartRenderTotalRows) {
            console.error("Scroll to index larger than number of rows");
            return false;
        }
        if (this.smartRenderTotalRows - rowIndex < this.itemsWillFit) {
            this.showItemsFrom = rowIndex - this.itemsWillFit + 3;
        } else {
            this.showItemsFrom = rowIndex;
        }

        var pxPerRow =
            Number(this.scrollerBox.style.height.replace("px", "")) /
            this.smartRenderTotalRows;
        var scrollTop = Math.floor(this.showItemsFrom * pxPerRow);
        var thisSqGrid = this;
        thisSqGrid.handleDoNotReScrollTimer(thisSqGrid);
        this.scrollerBoxOuter.scrollTop = scrollTop;

        if (!doNotSelect) {
            this.selectRow(rowIndex, true, true);
        }
        if (this.onGridUserInteracted != null) {
            this.onGridUserInteracted("scroll");
        }
        this.bodyRedraw();
        return true;
    }

    if (rowIndex >= this.rows.length) {
        console.error("Scroll to index larger than number of rows");
        return false;
    }

    if (this.itemsWillFit > this.rows.length) {} else {
        if (this.rows.length - rowIndex < this.itemsWillFit - 2) {
            this.showItemsFrom = rowIndex - this.itemsWillFit + 3;
        } else {
            this.showItemsFrom = rowIndex;
        }
        this.scrollerBoxOuter.scrollTop = this.showItemsFrom * this.rowHeight;
    }
    if (!doNotSelect) {
        this.selectRow(rowIndex, true, true);
    } else {
        this.bodyRedraw();
    }
    if (this.onGridUserInteracted != null) {
        this.onGridUserInteracted("scroll");
    }
    return true;
};

sqGrid.fn.setScrollbarPaddings = function() {
    if (
        this.itemsWillFit * this.rowHeight <=
        parseFloat(this.scrollerBox.style.height)
    ) {
        this.gridElement.classList.add("sq-grid-vertical-scrollbar");
    } else {
        this.gridElement.classList.remove("sq-grid-vertical-scrollbar");
    }
};

sqGrid.fn.drawOneRow = function(row, i, origI) {
    if (row == undefined) {
        return;
    }
    var thisRowRealI = origI;
    var thisSqGrid = this;
    var hasSetWidths = this.widths.length == this.columns.length;
    var rowIsChecked = this.checkedRows.indexOf(i) != -1;
    var tr = document.createElement("tr");
    tr.rowCellIndex = thisRowRealI;
    tr.dataset.rowIndex = thisRowRealI;
    tr.classList.add("sq-grid-row");
    var rowClasses = [];
    if ("class" in row && row.class != "") {
        if (Array.isArray(row.class)) {
            rowClasses = row.class;
        } else {
            rowClasses = [row.class];
        }
    }
    if (this.smartRenderAllRowsClasses[thisRowRealI]) {
        if (Array.isArray(this.smartRenderAllRowsClasses[thisRowRealI])) {
            rowClasses = this.smartRenderAllRowsClasses[thisRowRealI];
        } else {
            rowClasses = [this.smartRenderAllRowsClasses[thisRowRealI]];
        }
    }
    for (var clI = 0; clI < rowClasses.length; clI++) {
        if (!rowClasses[clI] || rowClasses[clI].length == 0) {
            continue;
        }
        tr.classList.add(rowClasses[clI]);
    }
    if (i % 2 == 1) {
        tr.classList.add("sq-grid-row-odd");
    }
    if (row.disabled) {
        tr.classList.add("disabled");
    }
    tr.onmouseenter = function(e) {
        thisSqGrid.onRowEnterPrivate(e);
    };
    tr.onmouseleave = function(e) {
        thisSqGrid.onRowLeavePrivate(e);
    };

    if (this.checkboxesOn) {
        var td = document.createElement("td");
        td.classList.add("sq-grid-column-checkbox");
        td.onmousedown = function(e) {
            if (!e.shiftKey) {
                Object.defineProperty(e, "ctrlKey", {
                    writable: false,
                    value: true,
                });
            }
            thisSqGrid.onCellMouseDownPrivate(e);
            thisSqGrid.onCellClickPrivate(e, origI);
        };
        td.ondblclick = function(e) {
            thisSqGrid.onDblcCellClickPrivate(e);
        };
        var checkbox = document.createElement("input");
        checkbox.type = "checkbox";
        checkbox.checked = rowIsChecked;
        (function(checkbox, rowI, tr) {
            checkbox.onclick = function(e) {
                e.preventDefault();
            };
        })(checkbox, thisRowRealI, tr);
        td.appendChild(checkbox);
        tr.appendChild(td);
    }
    for (var j = 0; j < this.columns.length; j++) {
        if (this.hideFirstCol && j == 0) {
            continue;
        }
        var cellText = this.formatCellText(row.cells[j], this.columns[j].type);
        if (cellText == undefined) {
            cellText = "";
        }
        var td = document.createElement("td");
        td.classList.add("sq-grid-cell");
        td.dataset.cellIndex = j;
        if ("styles" in row) {
            td.setAttribute("style", row.styles[j]);
        }
        if (this.showCellTitles) {
            var cellTitle = String(row.cells[j]);
            if (cellTitle != "" && this.cellTitlesNewlines != "") {
                cellTitle = cellTitle.split(this.cellTitlesNewlines).join("\n");
            }
            td.setAttribute("title", cellTitle);
        }
        td.classList.add("sq-grid-cell-align-" + this.columns[j].align);
        for (var k = 0; k < this.columns[j].cellClasses.length; k++) {
            td.classList.add(this.columns[j].cellClasses[k]);
        }
        if (
            this.smartRenderingUrl == false &&
            thisSqGrid.getCellCustomData(thisRowRealI, j, "cellsDisabled")
        ) {
            td.classList.add("disabled");
        }
        if ("cellClass" in row && j in row.cellClass) {
            for (var k = 0; k < row.cellClass[j].length; k++) {
                td.classList.add(row.cellClass[j][k].trim());
            }
        }
        td.cellIndex = j;
        var thisCellIcon = null;
        if (this.smartRenderingUrl == false) {
            thisCellIcon = this.getIconPrivate(thisRowRealI, j);
        }
        if (thisCellIcon != null && thisCellIcon.showIcon) {
            var iconClass =
                "sq-grid-icon sq-grid-icon-" +
                thisCellIcon.iconAlign +
                " " +
                thisCellIcon.iconClass;
            cellText = '<div class="' + iconClass + '"></div>' + cellText;
            (function(td, iconText) {
                td.onmouseover = function(e) {
                    if (e.target.classList.contains("sq-grid-icon")) {
                        thisSqGrid.popupElement.innerHTML = iconText;
                        thisSqGrid.popupElement.style.display = "block";
                        var viewportOffset = e.target.getBoundingClientRect();
                        var viewportOffsetPopup =
                            thisSqGrid.popupElement.getBoundingClientRect();
                        var top = viewportOffset.top;
                        var left = viewportOffset.left;
                        var height = viewportOffsetPopup.height;
                        thisSqGrid.popupElement.style.top = top - height + 16 + "px";
                        thisSqGrid.popupElement.style.left = left + 20 + "px";
                    }
                };
                td.onmouseout = function() {
                    thisSqGrid.popupElement.style.display = "none";
                };
            })(td, thisCellIcon.iconText);
        }
        var hasWidget = false;
        var widgetMatched = thisSqGrid.parseWidget(row.cells[j]);
        if (widgetMatched && widgetMatched.widget in thisSqGrid.widgets) {
            var widgetElement = thisSqGrid.widgets[widgetMatched.widget](
                thisSqGrid,
                thisRowRealI,
                j,
                row,
                widgetMatched.params
            );
            if (widgetElement instanceof Element) {
                td.appendChild(widgetElement);
            } else {
                td.innerHTML = widgetElement;
            }
            hasWidget = true;
        }

        if (!hasWidget) {
            td.innerHTML = cellText;
        }
        if (hasSetWidths) {
            if (this.widths[j] != "*px") {
                if (this.widths[j].indexOf("f") != -1) {
                    td.style.width = this.widths[j].replace("f", "");
                } else {
                    td.style.width = this.widths[j];
                }
            }
        }
        td.onmousedown = function(e) {
            thisSqGrid.onCellMouseDownPrivate(e);
            thisSqGrid.onCellClickPrivate(e, origI);
            if (thisSqGrid.dblClickCounter == 0) {
                thisSqGrid.dblClickTimer = setTimeout(function() {
                    thisSqGrid.dblClickCounter = 0;
                }, 400);
                thisSqGrid.dblClickCounter = 1;
            } else if (thisSqGrid.dblClickCounter == 1) {
                clearTimeout(thisSqGrid.dblClickTimer);
                thisSqGrid.dblClickCounter = 0;
                thisSqGrid.onDblcCellClickPrivate(e);
            }
        };
        td.onmouseenter = function(e) {
            thisSqGrid.onCellEnterPrivate(e);
        };
        td.onmouseleave = function(e) {
            thisSqGrid.onCellLeavePrivate(e);
        };

        tr.appendChild(td);
    }
    if (hasSetWidths) {
        var td = document.createElement("td");
        td.classList.add("sq-grid-column-placeholder");
        td.onclick = function(e) {
            thisSqGrid.onCellClickPrivate(e);
        };
        td.ondblclick = function(e) {
            thisSqGrid.onDblcCellClickPrivate(e);
        };
        td.onmousedown = function(e) {
            thisSqGrid.onCellMouseDownPrivate(e);
            thisSqGrid.onCellClickPrivate(e, origI);
            if (thisSqGrid.dblClickCounter == 0) {
                thisSqGrid.dblClickTimer = setTimeout(function() {
                    thisSqGrid.dblClickCounter = 0;
                }, 400);
                thisSqGrid.dblClickCounter = 1;
            } else if (thisSqGrid.dblClickCounter == 1) {
                clearTimeout(thisSqGrid.dblClickTimer);
                thisSqGrid.dblClickCounter = 0;
                thisSqGrid.onDblcCellClickPrivate(e);
            }
        };
        tr.appendChild(td);
    }
    if (this.selectedRows.indexOf(thisRowRealI) != -1) {
        tr.classList.add("sq-grid-selectedrow");
        this.selectedTrNode = tr;
    }
    if (rowIsChecked) {
        tr.classList.add("sq-grid-checkedrow");
    }
    if ("bgColor" in row.userData && row.userData.bgColor != "") {
        tr.style.backgroundColor = row.userData.bgColor;
    }
    this.bodyTable.appendChild(tr);
};

sqGrid.fn.htmlCellFormat = function(cellText) {
    var outputText = cellText;
    var styles = "";
    var classes = "";
    if (typeof cellText == "string") {
        if (this.htmlCells) {
            if (cellText.indexOf(" style=") != -1) {
                var regex = /style=["']([^"']*)["']/gm;
                var array1 = regex.exec(cellText);
                if (array1 != null) {
                    styles = array1[1];
                }
            }
            if (cellText.indexOf(" class=") != -1) {
                var regex = /class=["']([^"']*)["']/gm;
                var array1 = regex.exec(cellText);
                if (array1 != null) {
                    if (array1[1].indexOf(",") !== -1) {
                        classes = array1[1].split(",");
                    } else {
                        classes = array1[1].split(" ");
                    }
                }
            }
            outputText = cellText.replace(/<[^= ](?:.|\n|)*?>/gm, "");
        }
    }
    return {
        text: outputText,
        styles: styles,
        classes: classes,
    };
};

sqGrid.fn.parseOneRow = function(rowData) {
    var totalCells = [];
    var totalStyles = [];
    var totalClasses = [];

    for (var i = 0; i < rowData.length; i++) {
        var parsedCell = this.htmlCellFormat(rowData[i]);
        totalCells.push(parsedCell.text);
        totalStyles.push(parsedCell.styles);
        totalClasses.push(parsedCell.classes);
    }

    return {
        cells: totalCells,
        cellsStyles: totalStyles,
        cellsClasses: totalClasses,
    };
};

sqGrid.fn.parseWidget = function(cellText) {
    var widgetParams = [];
    var widgetPattern = /^{{([^ }]+)(.*)}}$/g;
    var widgetMatches = widgetPattern.exec(cellText);
    if (widgetMatches !== null) {
        if (widgetMatches[2] != "") {
            var tmpParams = widgetMatches[2].trim();
            tmpParams = tmpParams.split("' ");
            for (var parI = 0; parI < tmpParams.length; parI++) {
                var tmpParamSingle = tmpParams[parI].split("=");
                if (tmpParamSingle.length >= 2) {
                    var rest = tmpParams[parI].substr(tmpParamSingle[0].length + 1); //widget value may contain '=' character
                    widgetParams[tmpParamSingle[0]] = rest.replace(/^\'+|\'+$/g, "");
                }
            }
        }
        return {
            widget: widgetMatches[1],
            params: widgetParams,
        };
    } else {
        return false;
    }
};

sqGrid.fn.formatCellText = function(cellText, formatType) {
    if (cellText == "N/A") {
        return cellText;
    }

    if (formatType == undefined) {
        formatType = "text";
    }

    if (formatType == "integer") {
        cellText = parseInt(cellText);
    }
    if (formatType == "float5") {
        cellText = parseInt(cellText * 100000) / 100000;
    }
    if (formatType == "float2") {
        cellText = sqFormatNumberTo2Decimals(cellText);
    }
    if (formatType == "dollars") {
        cellText = sqFormatNumberTo2Decimals(cellText);
        cellText = cellText.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
        cellText = "$ " + cellText;
    }
    if (formatType == "dollarsColoured") {
        var colorClassName =
            cellText < 0 ? "sq-number-negative" : "sq-number-positive";
        cellText = sqFormatNumberTo2Decimals(cellText);
        cellText = cellText.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
        cellText = '<span class="' + colorClassName + '">$ ' + cellText + "</span>";
    }
    if (formatType == "pips") {
        cellText = parseInt(cellText * 10) / 10 + " pips";
    }
    if (formatType == "percent") {
        cellText = sqFormatNumberTo2Decimals(cellText) + " %";
    }
    if (formatType == "date") {
        var jsDate = new Date(cellText);
        cellText = this.getDateFormatted(jsDate);
    }
    if (formatType == "dateTime") {
        var jsDate = new Date(cellText);
        cellText =
            this.getDateFormatted(jsDate) + " " + this.getTimeFormatted(jsDate);
    }

    return cellText;
};

sqGrid.fn.formatCellTextICDom = function(cellText, formatType) {
    if (cellText == "N/A") {
        IncrementalDOM.text(cellText);
        return;
    }

    if (cellText === null || cellText === undefined) {
        IncrementalDOM.text("");
        return;
    }

    if (formatType == undefined) {
        formatType = "text";
    }

    if (formatType == "empty") {
        cellText = "";
    }
    if (formatType == "integer") {
        formatType = parseInt(cellText);
    }
    if (formatType == "float5") {
        cellText = parseInt(cellText * 100000) / 100000;
    }
    if (formatType == "float2") {
        cellText = sqFormatNumberTo2Decimals(cellText);
    }
    if (formatType == "dollars") {
        cellText = sqFormatNumberTo2Decimals(cellText);
        cellText = cellText.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
        cellText = "$ " + cellText;
    }
    if (formatType == "pips") {
        cellText = parseInt(cellText * 10) / 10 + " pips";
    }
    if (formatType == "percent") {
        cellText = sqFormatNumberTo2Decimals(cellText) + " %";
    }
    if (formatType == "date") {
        var jsDate = new Date(cellText);
        cellText = this.getDateFormatted(jsDate);
    }
    if (formatType == "dateTime") {
        if (cellText == -1) {
            cellText = "NA";
        } else {
            try {
                var jsDate = new Date(parseInt(cellText));
                cellText =
                    this.getDateFormatted(jsDate) + " " + this.getTimeFormatted(jsDate);
            } catch (e) {
                console.error(e);
            }
        }
    }

    if (formatType == "dollarsColoured") {
        var colorClassName =
            cellText < 0 ? "sq-number-negative" : "sq-number-positive";
        cellText = sqFormatNumberTo2Decimals(cellText);
        cellText = cellText.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
        IncrementalDOM.elementOpen("span", null, null, "class", colorClassName);
        IncrementalDOM.text("$ " + cellText);
        IncrementalDOM.elementClose("span");
    } else if (formatType == "filterResult") {
        var colorClassName =
            cellText == "PASSED" ? "sq-number-positive" : "sq-number-negative";
        colorClassName = "sq-number-positive";
        IncrementalDOM.elementOpen("span", null, null, "class", colorClassName);
        IncrementalDOM.text(cellText);
        IncrementalDOM.elementClose("span");
    } else {
        if (typeof cellText == "string" && cellText.indexOf("<") > -1) {
            ICDomHtmlBlob(cellText);
        } else {
            IncrementalDOM.text(cellText);
        }
    }
};

sqGrid.fn.getDateFormatted = function(jsDate) {
    var year = jsDate.getUTCFullYear();
    var month = "0" + (jsDate.getUTCMonth() + 1);
    var day = "0" + jsDate.getUTCDate();
    return year + "." + month.substr(-2) + "." + day.substr(-2);
};

sqGrid.fn.getTimeFormatted = function(jsDate) {
    var hours = "0" + jsDate.getUTCHours();
    var minutes = "0" + jsDate.getUTCMinutes();
    var seconds = "0" + jsDate.getUTCSeconds();
    return hours.substr(-2) + ":" + minutes.substr(-2) + ":" + seconds.substr(-2);
};

sqGrid.fn.fakeGridFocus = function(e) {
    this.currentGridFocused = true;
    this.gridElement.classList.add("focused");
};

sqGrid.fn.onCellMouseDownPrivate = function(e) {
    this.currentGridFocused = true;
    this.gridElement.classList.add("focused");
    var clickedTd = e.target;
    while (clickedTd.tagName != "TD") {
        clickedTd = clickedTd.parentElement;
        if (clickedTd == null) {
            return;
        }
    }
    var clickedTr = clickedTd.parentElement;
    var selectedRowNumber = parseInt(clickedTr.dataset.rowIndex);
    if (!this.multiselectOn) {
        this.selectedRows = [selectedRowNumber];
    }
    if (e.ctrlKey && (this.checkboxesOn || this.multiselectOn)) {
        var selectedTrue = false;
        var needsRedraw = false;
        if (this.checkboxesOn && !this.rows[selectedRowNumber].disabled) {
            var index = this.checkedRows.indexOf(selectedRowNumber);
            if (index == -1) {
                this.checkedRows.push(selectedRowNumber);
                selectedTrue = true;
            } else {
                this.checkedRows.splice(index, 1);
                selectedTrue = false;
                if (this.checkedAll) {
                    this.checkedAll = false;
                    this.headerRedraw();
                }
            }
            this.selectedRows = [selectedRowNumber];
        }
        if (this.multiselectOn) {
            var ioS = this.selectedRows.indexOf(selectedRowNumber);
            if (ioS != -1) {
                this.selectedRows.splice(ioS, 1);
            } else {
                this.selectedRows.push(selectedRowNumber);
            }
        }
        if (this.onSelectionChanged !== null) {
            this.onSelectionChanged([selectedRowNumber], [selectedTrue]);
        }
        if (needsRedraw) {}
    } else if (
        e.shiftKey &&
        this.lastSelectedRow != null &&
        (this.checkboxesOn || this.multiselectOn)
    ) {
        var newSelectedRow = this.getSortedIndexFromRowIndex(selectedRowNumber);
        var newLastSelectedRow = this.getSortedIndexFromRowIndex(
            this.lastSelectedRow
        );
        var plusOrMinus = newSelectedRow < newLastSelectedRow ? 1 : -1;
        var checkedRows = [];
        var truesRows = [];
        if (plusOrMinus == 1) {
            for (var k = newSelectedRow; k <= newLastSelectedRow; k++) {
                var rowIndex =
                    this.sortedColumns.length == 0 ? k : this.sortedRowsBuffer[k].index;
                if (this.multiselectOn) {
                    this.selectedRows.push(rowIndex);
                } else {
                    this.checkedRows.push(rowIndex);
                    checkedRows.push(rowIndex);
                    truesRows.push(true);
                }
            }
        } else {
            for (var k = newSelectedRow; k >= newLastSelectedRow; k--) {
                var rowIndex =
                    this.sortedColumns.length == 0 ? k : this.sortedRowsBuffer[k].index;
                if (this.multiselectOn) {
                    this.selectedRows.push(rowIndex);
                } else {
                    this.checkedRows.push(rowIndex);
                    checkedRows.push(rowIndex);
                    truesRows.push(true);
                }
            }
        }
        if (this.onSelectionChanged !== null) {
            this.onSelectionChanged(checkedRows, truesRows);
        }
        if (this.multiselectOn) {}
    } else {
        this.selectedRows = [selectedRowNumber];
        if (this.multiselectOn) {}
    }

    if (this.checkedRows.length == 0) {
        if (this.onSelectionChanged !== null) {
            this.onSelectionChanged();
        }
    }

    this.lastSelectedRow = selectedRowNumber;

    this.bodyRedraw();
};

sqGrid.fn.onCellClickPrivate = function(e, indexSmart) {
    var clickedTd = e.target;
    while (clickedTd.tagName != "TD") {
        clickedTd = clickedTd.parentElement;
        if (clickedTd == null) {
            return;
        }
    }
    var clickedCol = whichChild(clickedTd);
    var clickedRow = whichChild(clickedTd.parentElement);
    if (this.dblClickCounter == 0) {
        this.dblClickedRowBuffer = clickedRow;
    }
    var selectedRowNumber = parseInt(clickedTd.parentElement.dataset.rowIndex);
    var dataIndex = selectedRowNumber;
    if (this.smartRenderingUrl) {
        selectedRowNumber = this.showItemsFrom + indexSmart;
        //dataIndex = indexSmart;
		dataIndex = dataIndex - this.showItemsFrom;
    }
    if (this.onCellClick != null) {
        this.onCellClick(
            selectedRowNumber,
            clickedCol,
            this.rows[dataIndex].cells,
            e
        );
    }
};

sqGrid.fn.onDblcCellClickPrivate = function(e) {
    var clickedTd = e.target;
    while (clickedTd.tagName != "TD") {
        clickedTd = clickedTd.parentElement;
    }
    var clickedCol = whichChild(clickedTd);
    var clickedRow = whichChild(clickedTd.parentElement);
    var selectedRowNumber = parseInt(clickedTd.parentElement.dataset.rowIndex);
    if (
        this.onCellDoubleClick != null &&
        this.dblClickedRowBuffer == clickedRow
    ) {
        this.onCellDoubleClick(
            selectedRowNumber,
            clickedCol,
            this.rows[selectedRowNumber].cells
        );
    }
};

sqGrid.fn.onCellEnterPrivate = function(e) {
    var clickedCol = whichChild(e.target);
    var clickedRow = whichChild(e.target.parentElement);
    var selectedRowNumber = this.showItemsFrom + clickedRow;
    if (this.onCellEnter != null) {
        this.onCellEnter(
            selectedRowNumber,
            clickedCol,
            this.rows[selectedRowNumber].cells
        );
    }
};

sqGrid.fn.onCellLeavePrivate = function(e) {
    var clickedCol = whichChild(e.target);
    var clickedRow = whichChild(e.target.parentElement);
    var selectedRowNumber = this.showItemsFrom + clickedRow;
    if (this.onCellLeave != null) {
        this.onCellLeave(
            selectedRowNumber,
            clickedCol,
            this.rows[selectedRowNumber].cells
        );
    }
};

sqGrid.fn.onRowEnterPrivate = function(e) {
    var clickedRow = whichChild(e.target);
    var selectedRowNumber = this.showItemsFrom + clickedRow;
    if (this.onRowEnter != null) {
        this.onRowEnter(selectedRowNumber, this.rows[selectedRowNumber].cells);
    }
};

sqGrid.fn.onRowLeavePrivate = function(e) {
    var clickedRow = whichChild(e.target);
    var selectedRowNumber = this.showItemsFrom + clickedRow;
    if (this.onRowLeave != null) {
        this.onRowLeave(selectedRowNumber, this.rows[selectedRowNumber].cells);
    }
};

sqGrid.fn.selectAllRows = function() {
    this.selectedRows = [];
    for (var i = 0; i < this.rows.length; i++) {
        this.selectedRows.push(i);
    }
    this.bodyRedraw();
};

sqGrid.fn.selectNoneRows = function() {
    this.selectedRows = [];
    this.bodyRedraw();
};

sqGrid.fn.setColumns = function(columns, disableBodyRedraw) {
    if (!Array.isArray(columns)) {
        console.error("Argument has to be array in specified format.");
        return;
        /* format of array elements: {
                    title: 'columnTitle'
                    type: 'columnType' (podle toho se bude renderovat obsah)
                    sort: 'sortType' (podle toho se bude sortovat tento sloupec)
                    align: 'sortType'
                } */
    }
    this.columns = [];
    var widths = [];
    for (var i = 0; i < columns.length; i++) {
        var type = columns[i].type;
        var cclass = [];
        if (type.indexOf(",") != -1) {
            type = type.split(",");

            for (var j = 1; j < type.length; j++) {
                cclass.push(type[j]);
            }
            type = type[0];
        }
        this.columns.push({
            title: columns[i].title,
            tooltip: columns[i].tooltip,
            type: type,
            sort: columns[i].sort,
            class: columns[i].class,
            cellClasses: cclass,
            align: "align" in columns[i] ? columns[i].align : "center",
        });
        if ("width" in columns[i]) {
            widths.push(columns[i].width);
        }
    }

    if (widths.length > 0) {
        this.setWidths(widths, disableBodyRedraw);
    }

    this.sortColumnsChanged = true;

    if (disableBodyRedraw != true) {
        this.headerRedraw();
    }
};

sqGrid.fn.getColumnLabel = function(columnIndex) {
    if (columnIndex >= this.columns.length) {
        return null;
    }

    return this.columns[columnIndex].title;
};

sqGrid.fn.setWidths = function(columnWidths, disableBodyRedraw) {
    if (!Array.isArray(columnWidths)) {
        console.error("Argument has to be array in specified format.");
        return;
        // bud obsahuje procenta a nebo je to jen cislo, to znamena, ze to je v pixelech
    }

    this.hasFlexibleColumn = false;

    if (columnWidths.length != this.columns.length) {
        console.error(
            "The number of items has to be identical to number of columns specified."
        );
        return;
    }
    for (var i = 0; i < columnWidths.length; i++) {
        if (columnWidths[i] == "*") {
            this.hasFlexibleColumn = true;
        }
        if (columnWidths[i].toString().indexOf("%") == -1) {
            columnWidths[i] = columnWidths[i].toString() + "px";
        }
    }

    if (this.hasFlexibleColumn) {
        this.gridElement.classList.add("sq-grid-with-flex-column");
    } else {
        this.gridElement.classList.remove("sq-grid-with-flex-column");
    }

    this.sortColumnsChanged = true;

    this.widths = columnWidths;
    if (disableBodyRedraw != true) {
        this.headerRedraw();
        this.bodyRedraw();
    }
};

sqGrid.fn.appendData = function(dataRows, disableBodyRedraw) {
    // array of rows
    if (!Array.isArray(dataRows)) {
        console.error("Argument has to be array in specified format.");
        return;
    }

    for (var i = 0; i < dataRows.length; i++) {
        var newRowParsed = this.parseOneRow(dataRows[i]);
        var newRow = {
            index: this.rows.length,
            cells: newRowParsed.cells,
            userData: {},
            icons: [],
            class: "",
            cellClass: newRowParsed.cellsClasses,
            disabled: false,
            styles: newRowParsed.cellsStyles,
        };

        this.rows.push(newRow);
    }

    var redrawAfterwards = true;
    if (
        this.sortedColumns.length == 0 &&
        this.rows.length > this.showItemsFrom + this.itemsWillFit
    ) {
        redrawAfterwards = false;
    }

    this.sortChanged = true;

    if (disableBodyRedraw != true) {
        this.sortAll(disableBodyRedraw);
    }

    if (redrawAfterwards && disableBodyRedraw != true) {
        this.debouncedBodyRedraw();
    }
};

sqGrid.fn.debouncedResort = function(disableBodyRedraw) {
    if (this.sortedColumns.length > 0) {
        var thisSqGrid = this;
        clearTimeout(this.appendSortDebouncerTimer);
        this.appendSortDebouncerTimer = setTimeout(function() {
            thisSqGrid.appendSortDebouncerTimer = null;
            thisSqGrid.sortAll(disableBodyRedraw);
        }, 1000);
    }
};

sqGrid.fn.debouncedBodyRedraw = function(forced) {
    var thisSqGrid = this;
    thisSqGrid.bodyRedraw(forced);
};

sqGrid.fn.debouncedBodyRedrawWithResort = function(forced) {
    var thisSqGrid = this;
    clearTimeout(this.redrawBodyDebouncerTimer);
    this.redrawBodyDebouncerTimer = setTimeout(function() {
        thisSqGrid.redrawBodyDebouncerTimer = null;
        if (thisSqGrid.sortedColumns.length > 0) {
            thisSqGrid.debouncedResort();
        } else {
            thisSqGrid.debouncedBodyRedraw(forced);
        }
    }, 200);
};

sqGrid.fn.removeSelectionAtIndex = function(i, disableShuffle) {
    var indexChecked = this.checkedRows.indexOf(i);
    if (indexChecked != -1) {
        this.checkedRows.splice(indexChecked, 1);
    }
    indexChecked = this.selectedRows.indexOf(i);
    if (indexChecked != -1) {
        this.selectedRows.splice(indexChecked, 1);
    }
};

sqGrid.fn.removeRowByIndex = function(index) {
    if (index < this.rows.length) {
        this.removeSelectionAtIndex(index);
        this.rows.splice(index, 1);
        for (var j = 0; j < this.checkedRows.length; j++) {
            if (this.checkedRows[j] >= index) {
                this.checkedRows[j] -= 1;
            }
        }

        if (this.onSelectionChanged !== null) {
            this.onSelectionChanged([index], [false]);
        }

        this.sortChanged = true;

        this.bodyRedraw();
    } else {
        console.error("Specified index " + index + " is not in the buffer.");
        return;
    }
};

sqGrid.fn.removeRowById = function(id, disableRedraw) {
    if (!this.firstColIsId) {
        console.error("ID column has not been set.");
        return;
    }
    for (var i = 0; i < this.rows.length; i++) {
        if (this.rows[i].cells[0] == id) {
            this.removeSelectionAtIndex(i, true);
            this.rows.splice(i, 1);
            for (var j = 0; j < this.checkedRows.length; j++) {
                if (this.checkedRows[j] >= i) {
                    this.checkedRows[j] -= 1;
                }
            }

            if (this.onSelectionChanged !== null) {
                this.onSelectionChanged([i], [false]);
            }

            this.sortChanged = true;

            if (disableRedraw != true) {
                this.bodyRedraw();
            }
            return;
        }
    }
};

sqGrid.fn.modifyRowByIndex = function(
    newValues,
    rowIndex,
    colIndex,
    disableBodyRedraw
) {
    var maxRows = this.rows.length;
    if (this.smartRenderingUrl) {
        rowIndex = rowIndex - this.showItemsFrom;
        maxRows = this.smartRenderTotalRows;
    }
    if (rowIndex < maxRows) {
        if (colIndex == undefined) {
            var newRows = this.parseOneRow(newValues);
            this.rows[rowIndex].cells = newRows.cells;
            this.rows[rowIndex].styles = newRows.cellsStyles;
            this.rows[rowIndex].class = newRows.cellsClasses;
        } else {
            var newRow = this.htmlCellFormat(newValues);
            this.rows[rowIndex].cells[colIndex] = newRow.text;
            this.rows[rowIndex].styles[colIndex] = newRow.styles;
            this.rows[rowIndex].class[colIndex] = newRow.classes;
        }
    } else {
        console.error("Specified index " + index + " is not in the buffer.");
        return;
    }

    this.sortChanged = true;

    if (this.showItemsFrom + 50 < this.rows.length || disableBodyRedraw) {} else {
        this.debouncedBodyRedraw();
    }
};

sqGrid.fn.modifyRowById = function(newValues, rowId, colIndex, disableRedraw) {
    if (!this.firstColIsId) {
        console.error("ID column has not been set.");
        return;
    }
    for (var i = 0; i < this.rows.length; i++) {
        if (this.rows[i].cells[0] == rowId) {
            if (colIndex == undefined) {
                var newRows = this.parseOneRow(newValues);
                this.rows[i].cells = newRows.cells;
                this.rows[i].styles = newRows.cellsStyles;
                this.rows[i].cellClass = newRows.cellsClasses;
            } else {
                var newRow = this.htmlCellFormat(newValues);
                this.rows[i].cells[colIndex] = newRow.text;
                this.rows[i].styles[colIndex] = newRow.styles;
                this.rows[i].cellClass[colIndex] = newRow.class;
            }
            this.sortChanged = true;
            if (
                (this.sortedColumns.length == 0 &&
                    this.showItemsFrom + 50 < this.rows.length) ||
                disableRedraw
            ) {} else {
                this.debouncedBodyRedraw();
            }
            return;
        }
    }
    console.error("Specified ID " + rowId + " is not in the buffer.");
};

sqGrid.fn.removeAllRows = function(
    disableRedraw,
    disableMovingScroll,
    doNotRemoveSort
) {
    this.rows = [];
    this.sortedRowsBuffer = [];
    this.sortChanged = true;
    if (doNotRemoveSort != true) {
        this.sortedColumns = [];
        this.sortColumnsChanged = true;
    } else {
        this.sortColumnsChanged = true;
        this.sortAll(true);
    }

    if (disableMovingScroll != true) {
        this.scrollerBoxOuter.scrollTop = 0;
        this.showItemsFrom = 0;
    }
    this.selectedRows = [];
    this.checkedRows = [];
    this.checkedAll = false;
    this.lastSelectedRow = null;
    if (this.onSelectionChanged !== null) {
        this.onSelectionChanged([], []);
    }
    if (disableRedraw != true) {
        this.headerRedraw();
        this.bodyRedraw();
    }
};

sqGrid.fn.addRow = function(row, disableBodyRedraw) {
    if (!Array.isArray(row)) {
        console.error("Argument has to be array in specified format.");
        return;
    }
    if (row.length != this.columns.length) {
        console.error(
            "The number of items has to be identical to number of columns specified."
        );
        return;
    }

    var newRowParsed = this.parseOneRow(row);
    this.rows.push({
        cells: newRowParsed.cells,
        userData: {},
        icons: [],
        class: "",
        cellClass: newRowParsed.cellsClasses,
        disabled: false,
        styles: newRowParsed.cellsStyles,
    });
    this.sortChanged = true;
    if (
        (this.sortedColumns.length == 0 &&
            this.showItemsFrom + 50 < this.rows.length) ||
        disableBodyRedraw
    ) {} else {
        this.bodyRedraw();
    }
    return this.rows.length - 1;
};

sqGrid.fn.getSelectedRows = function() {
    var selectedRowsData = [];
    var checkedRowsData = [];
    this.checkedRows = this.uniqueArray(this.checkedRows);
    this.selectedRows = this.uniqueArray(this.selectedRows);

    if (this.checkboxesOn && this.checkedRows.length > 0) {
        var checkedRowsDup = this.checkedRows.slice();
        for (var i = 0; i < this.checkedRows.length; i++) {
            if (this.rows[this.checkedRows[i]] !== undefined) {
                selectedRowsData.push(this.rows[this.checkedRows[i]].cells);
                checkedRowsData.push(this.rows[this.checkedRows[i]].cells);
            }
        }
        if (
            this.selectedRows.length > 0 &&
            this.checkedRows.indexOf(this.selectedRows[0]) == -1
        ) {
            if (this.rows.indexOf(this.selectedRows[0]) != -1) {
                checkedRowsDup.push(this.selectedRows[0]);
                selectedRowsData.push(this.rows[this.selectedRows[0]].cells);
            }
        }
        return {
            selectedRows: checkedRowsDup,
            selectedRowsData: selectedRowsData,
            checkedRows: this.checkedRows,
            checkedRowsData: checkedRowsData,
        };
    } else {
        for (var i = 0; i < this.selectedRows.length; i++) {
            if (this.smartRenderingUrl) {
                var relativeRowIndex = this.selectedRows[i] - this.showItemsFrom;
                if (this.rows[relativeRowIndex] !== undefined) {
                    selectedRowsData.push(this.rows[relativeRowIndex].cells);
                }
            } else {
                if (this.rows[this.selectedRows[i]] !== undefined) {
                    selectedRowsData.push(this.rows[this.selectedRows[i]].cells);
                }
            }
        }
        return {
            selectedRows: this.selectedRows,
            selectedRowsData: selectedRowsData,
            checkedRows: [],
            checkedRowsData: [],
        };
    }
};

sqGrid.fn.getSelectedRowOnly = function() {
    if (
        this.selectedRows.length > 0 &&
        this.rows[this.selectedRows[0]] !== undefined
    ) {
        return {
            index: this.selectedRows[0],
            data: this.rows[this.selectedRows[0]].cells,
        };
    } else {
        return false;
    }
};

sqGrid.fn.setUserData = function(rowIndex, userDataName, userDataValue) {
    if (rowIndex < this.rows.length) {
        if (!("userData" in this.rows[rowIndex])) {
            this.rows[rowIndex].userData = {};
        }
        this.rows[rowIndex].userData[userDataName] = userDataValue;
    }
};

sqGrid.fn.getUserData = function(rowIndex, userDataName) {
    if (
        "userData" in this.rows[rowIndex] &&
        userDataName in this.rows[rowIndex].userData
    ) {
        return this.rows[rowIndex].userData[userDataName];
    }

    return null;
};

sqGrid.fn.setRowClass = function(rowIndex, className) {
    if (this.smartRenderingUrl) {
        this.smartRenderAllRowsClasses[rowIndex] = className;
    } else {
        if (rowIndex < this.rows.length) {
            this.rows[rowIndex].class = className;
        }
    }
};

sqGrid.fn.setIcon = function(
    rowIndex,
    cellIndex,
    showIcon,
    iconAlign,
    iconClass,
    iconText,
    disableBodyRedraw
) {
    if (rowIndex < this.rows.length) {
        this.removeIconPrivate(rowIndex, cellIndex);
        this.rows[rowIndex].icons.push({
            cellIndex: cellIndex,
            showIcon: showIcon,
            iconAlign: iconAlign,
            iconClass: iconClass,
            iconText: iconText,
        });
    }
    if (disableBodyRedraw != true) {
        this.bodyRedraw();
    }
};

sqGrid.fn.getIconPrivate = function(rowIndex, cellIndex) {
    if (rowIndex < this.rows.length) {
        for (var i = 0; i < this.rows[rowIndex].icons.length; i++) {
            if (this.rows[rowIndex].icons[i].cellIndex == cellIndex) {
                return this.rows[rowIndex].icons[i];
            }
        }
    }
    return null;
};

sqGrid.fn.removeIconPrivate = function(rowIndex, cellIndex) {
    if (rowIndex < this.rows.length) {
        for (var i = this.rows[rowIndex].icons.length - 1; i >= 0; i--) {
            if (this.rows[rowIndex].icons[i].cellIndex == cellIndex) {
                this.rows[rowIndex].icons.splice(i, 1);
                return;
            }
        }
    }
};

sqGrid.fn.getNumberOfRows = function() {
    return this.rows.length;
};

sqGrid.fn.sortFunctionText = function(a, b) {
    if (a < b) return -1;
    if (a > b) return 1;
    return 0;
};

sqGrid.fn.sortFunctionNumber = function(a, b) {
    var aFloat = parseFloat(a);
    var bFloat = parseFloat(b);
    if (aFloat < bFloat) return -1;
    if (aFloat > bFloat) return 1;
    return 0;
};

sqGetDateDottedAsNumber = function(dateString) {
    var dateRegex = /(\d{4})\.(\d{1,2})\.(\d{1,2})/gm;
    var found = dateRegex.exec(dateString);
    if (found) {
        return Number(
            found[1] + ("00" + found[2]).slice(-2) + ("00" + found[3]).slice(-2)
        );
    } else {
        return 0;
    }
};

sqGrid.fn.sortFunctionDateDotted = function(a, b) {
    return sqGetDateDottedAsNumber(a) - sqGetDateDottedAsNumber(b);
};

sqGrid.fn.getRowIndex = function(rowId) {
    if (!this.firstColIsId) {
        console.error("ID column has not been set.");
        return -1;
    }
    for (var i = 0; i < this.rows.length; i++) {
        if (this.rows[i].cells[0] == rowId) {
            return i;
        }
    }
    // console.error("Specified ID " + rowId + " is not in the buffer.");
    return -1;
};

sqGrid.fn.getRowId = function(rowIndex) {
    if (!this.firstColIsId) {
        console.error("ID column has not been set.");
        return -1;
    }
    if (rowIndex < this.rows.length) {
        return this.rows[rowIndex].cells[0];
    } else {
        console.error("Specified index " + rowIndex + " is not in the buffer.");
        return -1;
    }
};

sqGrid.fn.getRowData = function(rowIndex) {
    if (rowIndex < this.rows.length) {
        return this.rows[rowIndex];
    } else {
        console.error("Specified index " + rowIndex + " is not in the buffer.");
        return -1;
    }
};

sqGrid.fn.getCellValue = function(rowIndex, cellIndex) {
    if (rowIndex < this.rows.length) {
        return this.rows[rowIndex].cells[cellIndex];
    } else {
        console.error("Specified index " + rowIndex + " is not in the buffer.");
        return -1;
    }
};

sqGrid.fn.rowExists = function(rowId) {
    if (!this.firstColIsId) {
        console.error("ID column has not been set.");
        return -1;
    }
    for (var i = 0; i < this.rows.length; i++) {
        if (this.rows[i].cells[0] == rowId) {
            return true;
        }
    }
    return false;
};

sqGrid.fn.loadFromUrl = function(url, callback, keepSort) {
    var thisSqGrid = this;
    this.makeHttpRequest(
        url,
        function(data) {
            var jsonData = JSON.parse(data);

            thisSqGrid.loadFromJSON(jsonData, keepSort);

            if (callback) {
                callback();
            }
        },
        true
    );
};

sqGrid.fn.loadFromJSON = function(jsonData, keepSort) {
    if ("error" in jsonData) {
        this.hadError = jsonData.error;
    } else {
        this.hadError = false;
        this.removeAllRows(false, false, keepSort);
        var totalRowsHeight =
            jsonData.total_count * this.rowHeight + this.rowHeight;
        this.rows = [];
        for (var i = 0; i < jsonData.rows.length; i++) {
            var newRowParsed = this.parseOneRow(jsonData.rows[i].data);
            var rowClass = "class" in jsonData.rows[i] ? jsonData.rows[i].class : "";
            var newRow = {
                index: this.rows.length,
                cells: newRowParsed.cells,
                userData: {},
                icons: [],
                class: rowClass,
                cellClass: newRowParsed.cellsClasses,
                disabled: false,
                styles: newRowParsed.cellsStyles,
            };

            if ("userdata" in jsonData.rows[i]) {
                newRow.userData = jsonData.rows[i].userdata;
            }

            if ("bgColor" in jsonData.rows[i] && jsonData.rows[i].bgColor != "") {
                newRow.userData.bgColor = jsonData.rows[i].bgColor;
            }

            this.rows.push(newRow);
        }
    }

    this.setScrollHeight(totalRowsHeight);

    this.headerRedraw();
    this.debouncedBodyRedrawWithResort();
};

sqGrid.fn.defineWidget = function(widgetName, widgetFunction) {
    this.widgets[widgetName] = widgetFunction;
};

sqGrid.fn.isRowChecked = function(rowIndex) {
    return this.checkedRows.indexOf(rowIndex) >= 0;
};

sqGrid.fn.setRowChecked = function(
    rowIndex,
    checked,
    callHandler,
    disableRedraw
) {
    if (checked) {
        if (this.checkboxesOn) {
            if (this.checkedRows.indexOf(rowIndex) < 0) {
                this.checkedRows.push(rowIndex);
            }
        } else {
            if (this.selectedRows.indexOf(rowIndex) < 0) {
                this.selectedRows.push(rowIndex);
            }
        }
    } else {
        if (this.checkboxesOn) {
            var index = this.checkedRows.indexOf(rowIndex);
            if (index != -1) {
                this.checkedRows.splice(index, 1);
            }
        } else {
            var index = this.selectedRows.indexOf(rowIndex);
            if (index != -1) {
                this.selectedRows.splice(index, 1);
            }
        }
    }

    if (this.onSelectionChanged !== null && callHandler) {
        this.onSelectionChanged([rowIndex], [checked]);
    }

    if (!disableRedraw) {
        this.bodyRedraw();
    }
};

sqGrid.fn.getCellCustomData = function(rowIndex, colIndex, userDataName) {
    var dataValue = null;
    if (this.smartRenderingUrl) {
        rowIndex = rowIndex - this.showItemsFrom;
    }
    if (
        userDataName in this.rows[rowIndex].userData &&
        colIndex in this.rows[rowIndex].userData[userDataName]
    ) {
        dataValue = this.rows[rowIndex].userData[userDataName][colIndex];
    }
    return dataValue;
};

sqGrid.fn.setCellCustomData = function(
    rowIndex,
    colIndex,
    userDataName,
    userDataValue
) {
    if (this.smartRenderingUrl) {
        rowIndex = rowIndex - this.showItemsFrom;
    }
    if (!(userDataName in this.rows[rowIndex].userData)) {
        this.rows[rowIndex].userData[userDataName] = {};
    }
    this.rows[rowIndex].userData[userDataName][colIndex] = userDataValue;
};

sqGrid.fn.getCellWidgetValue = function(rowIndex, colIndex, userDataName) {
    var gotValue = this.getCellCustomData(rowIndex, colIndex, userDataName);
    if (gotValue == null) {
        var widgetResult = this.parseWidget(this.getCellValue(rowIndex, colIndex));
        if (
            widgetResult != false &&
            "params" in widgetResult &&
            "value" in widgetResult.params
        ) {
            gotValue = widgetResult.params.value;
        }
    }
    gotValue = !isNaN(parseFloat(gotValue)) && isFinite(gotValue) ?
        parseFloat(gotValue) :
        gotValue;
    return gotValue;
};

sqGrid.fn.setRowDisabled = function(rowIndex, disabled) {
    this.rows[rowIndex].disabled = disabled;
    this.bodyRedraw();
};

sqGrid.fn.isRowDisabled = function(rowIndex) {
    return this.rows[rowIndex].disabled;
};

sqGrid.fn.setCellDisabled = function(
    rowIndex,
    colIndex,
    disabled,
    disableBodyRedraw
) {
    this.setCellCustomData(rowIndex, colIndex, "cellsDisabled", disabled);
    if (!disableBodyRedraw) {
        this.bodyRedraw();
    }
};

sqGrid.fn.isCellDisabled = function(rowIndex, colIndex) {
    return this.getCellCustomData(rowIndex, colIndex, "cellsDisabled");
};

sqGrid.fn.bodyRedrawThrottled = sqGridThrottle(function() {
    this.bodyRedraw();
}, 100);

sqGrid.fn.setSort = function(columnIndex, desc) {
    var thisNewObject = {
        columnIndex: columnIndex,
        isDesc: desc,
    };
    this.sortedColumns = [];
    if (columnIndex != -1) {
        this.sortedColumns.push(thisNewObject);
    }
    this.sortColumnsChanged = true;
    this.sortAll();
};

sqGrid.fn.uniqueArray = function(arrArg) {
    return arrArg.filter(function(elem, pos, arr) {
        return arr.indexOf(elem) == pos;
    });
};

sqGrid.fn.isSorted = function() {
    return this.sortedColumns.length > 0;
};

sqGrid.fn.hashCode = function(inputS) {
    var hash = 0,
        i,
        chr;
    if (inputS.length === 0) return hash;
    for (i = 0; i < inputS.length; i++) {
        chr = inputS.charCodeAt(i);
        hash = (hash << 5) - hash + chr;
        hash |= 0; // Convert to 32bit integer
    }
    return hash;
};

sqGrid.fn.parseDomICDom = function(inputDom) {
    if (inputDom.nodeType === Node.ELEMENT_NODE) {
        IncrementalDOM.elementOpenStart(
            inputDom.nodeName.toLowerCase(),
            String(Math.random() * 50000), ["value"]
        );
        for (var i = 0; i < inputDom.attributes.length; i++) {
            IncrementalDOM.attr(
                inputDom.attributes[i].nodeName,
                inputDom.attributes[i].value
            );
        }
        for (var attrName in inputDom) {
            if (attrName.indexOf("on") == 0) {
                if (!!inputDom[attrName]) {
                    IncrementalDOM.attr(attrName, inputDom[attrName]);
                }
            }
        }
        IncrementalDOM.elementOpenEnd();
        for (var i = 0; i < inputDom.childNodes.length; i++) {
            this.parseDomICDom(inputDom.childNodes[i]);
        }
        IncrementalDOM.elementClose(inputDom.nodeName.toLowerCase());
    } else if (inputDom.nodeType === Node.TEXT_NODE) {
        IncrementalDOM.text(inputDom.textContent);
    }
};

sqGrid.fn.parseStringICDom = function(inputString) {
    if (
        typeof inputString == "string" &&
        (inputString.indexOf(">") > -1 || inputString.indexOf("<") > -1)
    ) {
        var regex = /([^<>]*)<([^<>]*\/?[^<>]*)>([^<>]*)/gm;
        while ((m = regex.exec(inputString)) !== null) {
            if (m.index === regex.lastIndex) {
                regex.lastIndex++;
            }
            if (m[1] != "") {
                IncrementalDOM.text(m[1]);
            }
            if (m[2] != "") {
                var tagCon = m[2].trim();
                var sLen = tagCon.length;
                if (tagCon[sLen - 1] == "/") {
                    tagCon = tagCon.slice(0, sLen - 1).trim();
                    var regex2 = /(\S+="[^"]+")|(\S+)/gm;
                    var m2 = tagCon.match(regex2);
                    IncrementalDOM.elementOpenStart(m2[0]);
                    if (m2.length > 1) {
                        for (var i = 1; i < m2.length; i++) {
                            var attrValues = m2[i].split("=");
                            if (attrValues.length > 1) {
                                attrValues[1] = attrValues[1].slice(1, -1);
                            } else {
                                attrValues.push(attrValues[0]);
                            }
                            IncrementalDOM.attr(attrValues[0], attrValues[1]);
                        }
                    }
                    IncrementalDOM.elementOpenEnd(m2[0]);
                    IncrementalDOM.elementClose(m2[0]);
                } else if (tagCon[0] == "/") {
                    IncrementalDOM.elementClose(tagCon.slice(1));
                } else {
                    var regex2 = /(\S\s*)[^ ]*/gm;
                    var m2 = tagCon.match(regex2);
                    IncrementalDOM.elementOpenStart(m2[0]);
                    if (m2.length > 1) {
                        for (var i = 1; i < m2.length; i++) {
                            var attrValues = m2[i].split("=");
                            if (attrValues.length > 1) {
                                attrValues[1] = attrValues[1].slice(1, -1);
                            } else {
                                attrValues.push(attrValues[0]);
                            }
                            IncrementalDOM.attr(attrValues[0], attrValues[1]);
                        }
                    }
                    IncrementalDOM.elementOpenEnd(m2[0]);
                }
            }
            if (m[3] != "") {
                IncrementalDOM.text(m[3]);
            }
        }
    } else {
        IncrementalDOM.text(inputString);
    }
};

function ICDomHtmlBlob(content) {
    var el = IncrementalDOM.elementOpen("html-blob");
    if (el.__cachedInnerHtml !== content) {
        el.__cachedInnerHtml = content;
        el.innerHTML = content;
    }
    IncrementalDOM.skip();
    IncrementalDOM.elementClose("html-blob");
}

function sqGridThrottle(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this,
            args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        if (!timeout) timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
}

function whichChild(elem) {
    var i = 0;
    while ((elem = elem.previousSibling) != null) ++i;
    return i;
}

function sqGridSendEvent(e, eventName) {
    e.preventDefault();
    var grid = sqGridFindAncestorByClass(e.target, "sq-grid").sqgrid;
    if (grid.cellEventHandler != null) {
        var td = sqGridFindAncestorByClass(e.target, "sq-grid-cell");
        var tr = sqGridFindAncestorByClass(e.target, "sq-grid-row");
        var argumentsToPass = [];
        for (var i = 0; i < arguments.length; i++) {
            if (i > 1) {
                argumentsToPass.push(arguments[i]);
            }
        }
        grid.cellEventHandler(
            parseInt(tr.dataset.rowIndex),
            parseInt(td.dataset.cellIndex),
            eventName,
            argumentsToPass
        );
    }
}

function sqGridFindAncestorByClass(el, cls) {
    while ((el = el.parentElement) && !el.classList.contains(cls));
    return el;
}

function sqFormatNumberTo2Decimals(value) {
    let returnValue =
        value < 0 ?
        parseInt(value * 100 - 0.1) / 100 :
        parseInt(value * 100 + 0.1) / 100;
    if (returnValue == 0) {
        returnValue =
            value < 0 ?
            parseInt(value * 1000000 - 0.1) / 1000000 :
            parseInt(value * 1000000 + 0.1) / 1000000;
    }
    return returnValue;
}