var $;
var DataTable;
export function setJQuery(jq) {
    $ = jq;
    DataTable = $.fn.dataTable;
}
var FixedColumns = /** @class */ (function () {
    function FixedColumns(settings, opts) {
        var _this = this;
        // Check that the required version of DataTables is included
        if (!DataTable ||
            !DataTable.versionCheck ||
            !DataTable.versionCheck('2')) {
            throw new Error('FixedColumns requires DataTables 2 or newer');
        }
        var table = new DataTable.Api(settings);
        this.classes = $.extend(true, {}, FixedColumns.classes);
        // Get options from user
        this.c = $.extend(true, {}, FixedColumns.defaults, opts);
        this.s = {
            dt: table,
            rtl: $(table.table().node()).css('direction') === 'rtl'
        };
        // Backwards compatibility for deprecated options
        if (opts && opts.leftColumns !== undefined) {
            opts.left = opts.leftColumns;
        }
        if (opts && opts.left !== undefined) {
            this.c[this.s.rtl ? 'end' : 'start'] = opts.left;
        }
        if (opts && opts.rightColumns !== undefined) {
            opts.right = opts.rightColumns;
        }
        if (opts && opts.right !== undefined) {
            this.c[this.s.rtl ? 'start' : 'end'] = opts.right;
        }
        this.dom = {
            bottomBlocker: $('<div>').addClass(this.classes.bottomBlocker),
            topBlocker: $('<div>').addClass(this.classes.topBlocker),
            scroller: $('div.dt-scroll-body', this.s.dt.table().container())
        };
        if (this.s.dt.settings()[0]._bInitComplete) {
            // Fixed Columns Initialisation
            this._addStyles();
            this._setKeyTableListener();
        }
        else {
            table.one('init.dt.dtfc', function () {
                // Fixed Columns Initialisation
                _this._addStyles();
                _this._setKeyTableListener();
            });
        }
        // Lots or reasons to redraw the column styles
        table.on('column-sizing.dt.dtfc column-reorder.dt.dtfc draw.dt.dtfc', function () { return _this._addStyles(); });
        // Column visibility can trigger a number of times quickly, so we debounce it
        var debounced = DataTable.util.debounce(function () {
            _this._addStyles();
        }, 50);
        table.on('column-visibility.dt.dtfc', function () {
            debounced();
        });
        // Add classes to indicate scrolling state for styling
        this.dom.scroller.on('scroll.dtfc', function () { return _this._scroll(); });
        this._scroll();
        // Make class available through dt object
        table.settings()[0]._fixedColumns = this;
        table.on('destroy', function () { return _this._destroy(); });
        return this;
    }
    FixedColumns.prototype.end = function (newVal) {
        // If the value is to change
        if (newVal !== undefined) {
            if (newVal >= 0 && newVal <= this.s.dt.columns().count()) {
                // Set the new values and redraw the columns
                this.c.end = newVal;
                this._addStyles();
            }
            return this;
        }
        return this.c.end;
    };
    /**
     * Left fix - accounting for RTL
     *
     * @param count Columns to fix, or undefined for getter
     */
    FixedColumns.prototype.left = function (count) {
        return this.s.rtl
            ? this.end(count)
            : this.start(count);
    };
    /**
     * Right fix - accounting for RTL
     *
     * @param count Columns to fix, or undefined for getter
     */
    FixedColumns.prototype.right = function (count) {
        return this.s.rtl
            ? this.start(count)
            : this.end(count);
    };
    FixedColumns.prototype.start = function (newVal) {
        // If the value is to change
        if (newVal !== undefined) {
            if (newVal >= 0 && newVal <= this.s.dt.columns().count()) {
                // Set the new values and redraw the columns
                this.c.start = newVal;
                this._addStyles();
            }
            return this;
        }
        return this.c.start;
    };
    /**
     * Iterates over the columns, fixing the appropriate ones to the left and right
     */
    FixedColumns.prototype._addStyles = function () {
        var dt = this.s.dt;
        var that = this;
        var colCount = this.s.dt.columns(':visible').count();
        var headerStruct = dt.table().header.structure(':visible');
        var footerStruct = dt.table().footer.structure(':visible');
        var widths = dt.columns(':visible').widths().toArray();
        var wrapper = $(dt.table().node()).closest('div.dt-scroll');
        var scroller = $(dt.table().node()).closest('div.dt-scroll-body')[0];
        var rtl = this.s.rtl;
        var start = this.c.start;
        var end = this.c.end;
        var left = rtl ? end : start;
        var right = rtl ? start : end;
        var barWidth = dt.settings()[0].oBrowser.barWidth; // dt internal
        // Do nothing if no scrolling in the DataTable
        if (wrapper.length === 0) {
            return this;
        }
        // Bar not needed - no vertical scrolling
        if (scroller.offsetWidth === scroller.clientWidth) {
            barWidth = 0;
        }
        // Loop over the visible columns, setting their state
        dt.columns().every(function (colIdx) {
            var visIdx = dt.column.index('toVisible', colIdx);
            var offset;
            // Skip the hidden columns
            if (visIdx === null) {
                return;
            }
            if (visIdx < start) {
                // Fix to the start
                offset = that._sum(widths, visIdx);
                that._fixColumn(visIdx, offset, 'start', headerStruct, footerStruct, barWidth);
            }
            else if (visIdx >= colCount - end) {
                // Fix to the end
                offset = that._sum(widths, colCount - visIdx - 1, true);
                that._fixColumn(visIdx, offset, 'end', headerStruct, footerStruct, barWidth);
            }
            else {
                // Release
                that._fixColumn(visIdx, 0, 'none', headerStruct, footerStruct, barWidth);
            }
        });
        // Apply classes to table to indicate what state we are in
        $(dt.table().node())
            .toggleClass(that.classes.tableFixedStart, start > 0)
            .toggleClass(that.classes.tableFixedEnd, end > 0)
            .toggleClass(that.classes.tableFixedLeft, left > 0)
            .toggleClass(that.classes.tableFixedRight, right > 0);
        // Blocker elements for when scroll bars are always visible
        var headerEl = dt.table().header();
        var footerEl = dt.table().footer();
        var headerHeight = $(headerEl).outerHeight();
        var footerHeight = $(footerEl).outerHeight();
        this.dom.topBlocker
            .appendTo(wrapper)
            .css('top', 0)
            .css(this.s.rtl ? 'left' : 'right', 0)
            .css('height', headerHeight)
            .css('width', barWidth + 1)
            .css('display', barWidth ? 'block' : 'none');
        if (footerEl) {
            this.dom.bottomBlocker
                .appendTo(wrapper)
                .css('bottom', 0)
                .css(this.s.rtl ? 'left' : 'right', 0)
                .css('height', footerHeight)
                .css('width', barWidth + 1)
                .css('display', barWidth ? 'block' : 'none');
        }
    };
    /**
     * Clean up
     */
    FixedColumns.prototype._destroy = function () {
        this.s.dt.off('.dtfc');
        this.dom.scroller.off('.dtfc');
        $(this.s.dt.table().node())
            .removeClass(this.classes.tableScrollingEnd + ' ' +
            this.classes.tableScrollingLeft + ' ' +
            this.classes.tableScrollingStart + ' ' +
            this.classes.tableScrollingRight);
        this.dom.bottomBlocker.remove();
        this.dom.topBlocker.remove();
    };
    /**
     * Fix or unfix a column
     *
     * @param idx Column visible index to operate on
     * @param offset Offset from the start (pixels)
     * @param side start, end or none to unfix a column
     * @param header DT header structure object
     * @param footer DT footer structure object
     */
    FixedColumns.prototype._fixColumn = function (idx, offset, side, header, footer, barWidth) {
        var _this = this;
        var dt = this.s.dt;
        var applyStyles = function (jq, part) {
            if (side === 'none') {
                jq.css('position', '')
                    .css('left', '')
                    .css('right', '')
                    .removeClass(_this.classes.fixedEnd + ' ' +
                    _this.classes.fixedLeft + ' ' +
                    _this.classes.fixedRight + ' ' +
                    _this.classes.fixedStart);
            }
            else {
                var positionSide = side === 'start' ? 'left' : 'right';
                if (_this.s.rtl) {
                    positionSide = side === 'start' ? 'right' : 'left';
                }
                var off = offset;
                if (side === 'end' && (part === 'header' || part === 'footer')) {
                    off += barWidth;
                }
                jq.css('position', 'sticky')
                    .css(positionSide, off)
                    .addClass(side === 'start'
                    ? _this.classes.fixedStart
                    : _this.classes.fixedEnd)
                    .addClass(positionSide === 'left'
                    ? _this.classes.fixedLeft
                    : _this.classes.fixedRight);
            }
        };
        header.forEach(function (row) {
            if (row[idx]) {
                applyStyles($(row[idx].cell), 'header');
            }
        });
        applyStyles(dt.column(idx + ':visible', { page: 'current' }).nodes().to$(), 'body');
        if (footer) {
            footer.forEach(function (row) {
                if (row[idx]) {
                    applyStyles($(row[idx].cell), 'footer');
                }
            });
        }
    };
    /**
     * Update classes on the table to indicate if the table is scrolling or not
     */
    FixedColumns.prototype._scroll = function () {
        var scroller = this.dom.scroller[0];
        // Not a scrolling table
        if (!scroller) {
            return;
        }
        // Need to update the classes on potentially multiple table tags. There is the
        // main one, the scrolling ones and if FixedHeader is active, the holding
        // position ones! jQuery will deduplicate for us.
        var table = $(this.s.dt.table().node())
            .add(this.s.dt.table().header().parentNode)
            .add(this.s.dt.table().footer().parentNode)
            .add('div.dt-scroll-headInner table', this.s.dt.table().container())
            .add('div.dt-scroll-footInner table', this.s.dt.table().container());
        var scrollLeft = scroller.scrollLeft; // 0 when fully scrolled left
        var ltr = !this.s.rtl;
        var scrollStart = scrollLeft !== 0;
        var scrollEnd = scroller.scrollWidth > (scroller.clientWidth + Math.abs(scrollLeft) + 1); // extra 1 for Chrome
        table.toggleClass(this.classes.tableScrollingStart, scrollStart);
        table.toggleClass(this.classes.tableScrollingEnd, scrollEnd);
        table.toggleClass(this.classes.tableScrollingLeft, (scrollStart && ltr) || (scrollEnd && !ltr));
        table.toggleClass(this.classes.tableScrollingRight, (scrollEnd && ltr) || (scrollStart && !ltr));
    };
    FixedColumns.prototype._setKeyTableListener = function () {
        var _this = this;
        this.s.dt.on('key-focus.dt.dtfc', function (e, dt, cell) {
            var currScroll;
            var cellPos = $(cell.node()).offset();
            var scroller = _this.dom.scroller[0];
            var scroll = $($(_this.s.dt.table().node()).closest('div.dt-scroll-body'));
            // If there are fixed columns to the left
            if (_this.c.start > 0) {
                // Get the rightmost left fixed column header, it's position and it's width
                var rightMost = $(_this.s.dt.column(_this.c.start - 1).header());
                var rightMostPos = rightMost.offset();
                var rightMostWidth = rightMost.outerWidth();
                // If the current highlighted cell is left of the rightmost cell on the screen
                if ($(cell.node()).hasClass(_this.classes.fixedLeft)) {
                    // Fixed columns have the scrollbar at the start, always
                    scroll.scrollLeft(0);
                }
                else if (cellPos.left < rightMostPos.left + rightMostWidth) {
                    // Scroll it into view
                    currScroll = scroll.scrollLeft();
                    scroll.scrollLeft(currScroll -
                        (rightMostPos.left + rightMostWidth - cellPos.left));
                }
            }
            // If there are fixed columns to the right
            if (_this.c.end > 0) {
                // Get the number of columns and the width of the cell as doing right side calc
                var numCols = _this.s.dt.columns().data().toArray().length;
                var cellWidth = $(cell.node()).outerWidth();
                // Get the leftmost right fixed column header and it's position
                var leftMost = $(_this.s.dt.column(numCols - _this.c.end).header());
                var leftMostPos = leftMost.offset();
                // If the current highlighted cell is right of the leftmost cell on the screen
                if ($(cell.node()).hasClass(_this.classes.fixedRight)) {
                    scroll.scrollLeft(scroller.scrollWidth - scroller.clientWidth);
                }
                else if (cellPos.left + cellWidth > leftMostPos.left) {
                    // Scroll it into view
                    currScroll = scroll.scrollLeft();
                    scroll.scrollLeft(currScroll -
                        (leftMostPos.left - (cellPos.left + cellWidth)));
                }
            }
        });
    };
    /**
     * Sum a range of values from an array
     *
     * @param widths
     * @param index
     * @returns
     */
    FixedColumns.prototype._sum = function (widths, index, reverse) {
        if (reverse === void 0) { reverse = false; }
        if (reverse) {
            widths = widths.slice().reverse();
        }
        return widths.slice(0, index).reduce(function (accum, val) { return accum + val; }, 0);
    };
    FixedColumns.version = '5.0.4';
    FixedColumns.classes = {
        bottomBlocker: 'dtfc-bottom-blocker',
        fixedEnd: 'dtfc-fixed-end',
        fixedLeft: 'dtfc-fixed-left',
        fixedRight: 'dtfc-fixed-right',
        fixedStart: 'dtfc-fixed-start',
        tableFixedEnd: 'dtfc-has-end',
        tableFixedLeft: 'dtfc-has-left',
        tableFixedRight: 'dtfc-has-right',
        tableFixedStart: 'dtfc-has-start',
        tableScrollingEnd: 'dtfc-scrolling-end',
        tableScrollingLeft: 'dtfc-scrolling-left',
        tableScrollingRight: 'dtfc-scrolling-right',
        tableScrollingStart: 'dtfc-scrolling-start',
        topBlocker: 'dtfc-top-blocker'
    };
    FixedColumns.defaults = {
        i18n: {
            button: 'FixedColumns'
        },
        start: 1,
        end: 0
    };
    return FixedColumns;
}());
export default FixedColumns;
