insertColumn = function(mixed, columnNumber, insertBefore, properties) {
    const obj = this;

    // Configuration
    if (obj.options.allowInsertColumn != false) {
        // Records
        var records = [];

        // Data to be insert
        let data = [];

        // The insert could be lead by number of rows or the array of data
        let numOfColumns;
        if (!Array.isArray(mixed)) {
            numOfColumns = typeof mixed === 'number' ? mixed : 1;
        } else {
            numOfColumns = 1;

            if (mixed) {
                data = mixed;
            }
        }

        // Direction
        insertBefore = insertBefore ? true : false;

        // Current column number
        const currentNumOfColumns = Math.max(
            obj.options.columns.length,
            ...obj.options.data.map(function(row) {
                return row.length;
            })
        );

        const lastColumn = currentNumOfColumns - 1;

        // Confirm position
        if (columnNumber == undefined || columnNumber >= parseInt(lastColumn) || columnNumber < 0) {
            columnNumber = lastColumn;
        }

        // Create default properties
        if (! properties) {
            properties = [];
        }

        for (let i = 0; i < numOfColumns; i++) {
            if (! properties[i]) {
                properties[i] = {};
            }
        }

        const columns = [];

        if (!Array.isArray(mixed)) {
            for (let i = 0; i < mixed; i++) {
                const column = {
                    column: columnNumber + i + (insertBefore ? 0 : 1),
                    options: Object.assign({}, properties[i]),
                };

                columns.push(column);
            }
        } else {
            const data = [];

            for (let i = 0; i < obj.options.data.length; i++) {
                data.push(i < mixed.length ? mixed[i] : '');
            }

            const column = {
                column: columnNumber + (insertBefore ? 0 : 1),
                options: Object.assign({}, properties[0]),
                data,
            };

            columns.push(column);
        }

        // Onbeforeinsertcolumn
        if (dispatch.call(obj, 'onbeforeinsertcolumn', obj, columns) === false) {
            return false;
        }

        // Merged cells
        if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {
            if (isColMerged.call(obj, columnNumber, insertBefore).length) {
                if (! confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {
                    return false;
                } else {
                    obj.destroyMerge();
                }
            }
        }

        // Insert before
        const columnIndex = (! insertBefore) ? columnNumber + 1 : columnNumber;
        obj.options.columns = injectArray(obj.options.columns, columnIndex, properties);

        // Open space in the containers
        const currentHeaders = obj.headers.splice(columnIndex);
        const currentColgroup = obj.cols.splice(columnIndex);

        // History
        const historyHeaders = [];
        const historyColgroup = [];
        const historyRecords = [];
        const historyData = [];
        const historyFooters = [];

        // Add new headers
        for (let col = columnIndex; col < (numOfColumns + columnIndex); col++) {
            createCellHeader.call(obj, col);
            obj.headerContainer.insertBefore(obj.headers[col], obj.headerContainer.children[col+1]);
            obj.colgroupContainer.insertBefore(obj.cols[col].colElement, obj.colgroupContainer.children[col+1]);

            historyHeaders.push(obj.headers[col]);
            historyColgroup.push(obj.cols[col]);
        }

        // Add new footer cells
        if (obj.options.footers) {
            for (let j = 0; j < obj.options.footers.length; j++) {
                historyFooters[j] = [];
                for (let i = 0; i < numOfColumns; i++) {
                    historyFooters[j].push('');
                }
                obj.options.footers[j].splice(columnIndex, 0, historyFooters[j]);
            }
        }

        // Adding visual columns
        for (let row = 0; row < obj.options.data.length; row++) {
            // Keep the current data
            const currentData = obj.options.data[row].splice(columnIndex);
            const currentRecord = obj.records[row].splice(columnIndex);

            // History
            historyData[row] = [];
            historyRecords[row] = [];

            for (let col = columnIndex; col < (numOfColumns + columnIndex); col++) {
                // New value
                const value = data[row] ? data[row] : '';
                obj.options.data[row][col] = value;
                // New cell
                const td = createCell.call(obj, col, row, obj.options.data[row][col]);
                obj.records[row][col] = {
                    element: td,
                    y: row,
                };
                // Add cell to the row
                if (obj.rows[row]) {
                    obj.rows[row].element.insertBefore(td, obj.rows[row].element.children[col+1]);
                }

                if (obj.options.columns && obj.options.columns[col] && typeof obj.options.columns[col].render === 'function') {
                    obj.options.columns[col].render(
                        td,
                        value,
                        parseInt(col),
                        parseInt(row),
                        obj,
                        obj.options.columns[col],
                    );
                }

                // Record History
                historyData[row].push(value);
                historyRecords[row].push({ element: td, x: col, y: row });
            }

            // Copy the data back to the main data
            Array.prototype.push.apply(obj.options.data[row], currentData);
            Array.prototype.push.apply(obj.records[row], currentRecord);
        }

        Array.prototype.push.apply(obj.headers, currentHeaders);
        Array.prototype.push.apply(obj.cols, currentColgroup);

        for (let i = columnIndex; i < obj.cols.length; i++) {
            obj.cols[i].x = i;
        }

        for (let j = 0; j < obj.records.length; j++) {
            for (let i = 0; i < obj.records[j].length; i++) {
                obj.records[j][i].x = i;
            }
        }

        // Adjust nested headers
        if (
            obj.options.nestedHeaders &&
            obj.options.nestedHeaders.length > 0 &&
            obj.options.nestedHeaders[0] &&
            obj.options.nestedHeaders[0][0]
        ) {
            for (let j = 0; j < obj.options.nestedHeaders.length; j++) {
                const colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) + numOfColumns;
                obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan;
                obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan);
                let o = obj.thead.children[j].children[obj.thead.children[j].children.length-1].getAttribute('data-column');
                o = o.split(',');
                for (let col = columnIndex; col < (numOfColumns + columnIndex); col++) {
                    o.push(col);
                }
                obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('data-column', o);
            }
        }

        // Keep history
        setHistory.call(obj, {
            action: 'insertColumn',
            columnNumber:columnNumber,
            numOfColumns:numOfColumns,
            insertBefore:insertBefore,
            columns:properties,
            headers:historyHeaders,
            cols:historyColgroup,
            records:historyRecords,
            footers:historyFooters,
            data:historyData,
        });

        // Remove table references
        updateTableReferences.call(obj);

        // Events
        dispatch.call(obj, 'oninsertcolumn', obj, columns);
    }
}
Insert a new column

@param mixed - num of columns to be added or data to be added in one single column
@param int columnNumber - number of columns to be created
@param bool insertBefore
@param object properties - column properties
@return void