insertRow = function(mixed, rowNumber, insertBefore) {
    const obj = this;

    // Configuration
    if (obj.options.allowInsertRow != 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 numOfRows;

        if (!Array.isArray(mixed)) {
            numOfRows = typeof mixed !== 'undefined' ? mixed : 1;
        } else {
            numOfRows = 1;

            if (mixed) {
                data = mixed;
            }
        }

        // Direction
        insertBefore = insertBefore ? true : false;

        // Current column number
        const lastRow = obj.options.data.length - 1;

        if (rowNumber == undefined || rowNumber >= parseInt(lastRow) || rowNumber < 0) {
            rowNumber = lastRow;
        }

        const onbeforeinsertrowRecords = [];

        for (let row = 0; row < numOfRows; row++) {
            const newRow = [];

            for (let col = 0; col < obj.options.columns.length; col++) {
                newRow[col] = data[col] ? data[col] : '';
            }

            onbeforeinsertrowRecords.push({
                row: row + rowNumber + (insertBefore ? 0 : 1),
                data: newRow,
            });
        }

        // Onbeforeinsertrow
        if (dispatch.call(obj, 'onbeforeinsertrow', obj, onbeforeinsertrowRecords) === false) {
            return false;
        }

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

        // Clear any search
        if (obj.options.search == true) {
            if (obj.results && obj.results.length != obj.rows.length) {
                if (confirm(jSuites.translate('This action will clear your search results. Are you sure?'))) {
                    obj.resetSearch();
                } else {
                    return false;
                }
            }

            obj.results = null;
        }

        // Insertbefore
        const rowIndex = (! insertBefore) ? rowNumber + 1 : rowNumber;

        // Keep the current data
        const currentRecords = obj.records.splice(rowIndex);
        const currentData = obj.options.data.splice(rowIndex);
        const currentRows = obj.rows.splice(rowIndex);

        // Adding lines
        const rowRecords = [];
        const rowData = [];
        const rowNode = [];

        for (let row = rowIndex; row < (numOfRows + rowIndex); row++) {
            // Push data to the data container
            obj.options.data[row] = [];
            for (let col = 0; col < obj.options.columns.length; col++) {
                obj.options.data[row][col]  = data[col] ? data[col] : '';
            }
            // Create row
            const newRow = createRow.call(obj, row, obj.options.data[row]);
            // Append node
            if (currentRows[0]) {
                if (Array.prototype.indexOf.call(obj.tbody.children, currentRows[0].element) >= 0) {
                    obj.tbody.insertBefore(newRow.element, currentRows[0].element);
                }
            } else {
                if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[rowNumber].element) >= 0) {
                    obj.tbody.appendChild(newRow.element);
                }
            }
            // Record History
            rowRecords.push([...obj.records[row]]);
            rowData.push([...obj.options.data[row]]);
            rowNode.push(newRow);
        }

        // Copy the data back to the main data
        Array.prototype.push.apply(obj.records, currentRecords);
        Array.prototype.push.apply(obj.options.data, currentData);
        Array.prototype.push.apply(obj.rows, currentRows);

        for (let j = rowIndex; j < obj.rows.length; j++) {
            obj.rows[j].y = j;
        }

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

        // Respect pagination
        if (obj.options.pagination > 0) {
            obj.page(obj.pageNumber);
        }

        // Keep history
        setHistory.call(obj, {
            action: 'insertRow',
            rowNumber: rowNumber,
            numOfRows: numOfRows,
            insertBefore: insertBefore,
            rowRecords: rowRecords,
            rowData: rowData,
            rowNode: rowNode,
        });

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

        // Events
        dispatch.call(obj, 'oninsertrow', obj, onbeforeinsertrowRecords);
    }
}
Insert a new row

@param mixed - number of blank lines to be insert or a single array with the data of the new row
@param rowNumber
@param insertBefore
@return void