/*
 * SDK Pullenti Unitext, version 4.33, fabruary 2026. Copyright (c) 2013-2026, Pullenti. All rights reserved. 
 * Non-Commercial Freeware and Commercial Software. 
 * This class is generated using the converter UniSharping (www.unisharping.ru) from Pullenti C# project. 
 * The latest version of the code is available on the site www.pullenti.ru
 */

const Utils = require("./../unisharp/Utils");
const RefOutArgWrapper = require("./../unisharp/RefOutArgWrapper");

const SizeunitConverter = require("./../util/SizeunitConverter");
const UnitextItem = require("./UnitextItem");
const UnitextTablecell = require("./UnitextTablecell");
const UnitextPagebreak = require("./UnitextPagebreak");
const UnitextStyledFragmentType = require("./UnitextStyledFragmentType");
const UnitextPlaintext = require("./UnitextPlaintext");
const UnitextHelper = require("./internal/uni/UnitextHelper");
const UnitextContainer = require("./UnitextContainer");
const UnitextNewline = require("./UnitextNewline");
const GetPlaintextParam = require("./GetPlaintextParam");
const UnitextImage = require("./UnitextImage");
const UnitextHyperlink = require("./UnitextHyperlink");

/**
 * Таблица, представляет собой матрицу из клеток. 
 * Ячейки могут заполнять прямоугольные области из клеток. 
 * Ячейки не могут пересекаться друг с другом.
 * Таблица
 */
class UnitextTable extends UnitextItem {
    
    constructor() {
        super();
        this.m_Cells = new Array();
        this.m_ColWidth = new Array();
        this.m_ColsCount = 0;
        this.width = null;
        this.hideBorders = false;
        this.tableHeaderPaginate = false;
        this.mayHasError = false;
        this.m_Map = null;
    }
    
    optimize(isContent, pars) {
        if (this.m_ColsCount === 3 && this.m_Cells.length === 2) {
        }
        for (const ro of this.m_Cells) {
            for (const c of ro) {
                if (c !== null) 
                    c.optimize(false, pars);
            }
        }
        if (pars !== null && !pars.correctParams.removeEmptyTables) 
            return this;
        if (this.m_Cells.length === 0) 
            return null;
        if (this.ignorable) 
            return this;
        for (let r = 0; r < this.m_Cells.length; r++) {
            for (let c = 0; c < this.m_Cells[r].length; c++) {
                if (this.m_Cells[r][c] !== null) 
                    continue;
                let cc = 0;
                for (cc = c + 1; cc < this.m_Cells[r].length; cc++) {
                    if (this.m_Cells[r][cc] !== null) 
                        break;
                }
                this.addCell(r, r, c, cc - 1, null, false);
            }
            if (this.m_Cells[r].length < this.m_ColsCount) 
                this.addCell(r, r, this.m_Cells[r].length, this.m_ColsCount - 1, null, false);
        }
        let colsCount0 = this.colsCount;
        let rowCount0 = this.rowsCount;
        for (let i = this.m_ColsCount - 1; i >= 0; i--) {
            let r = 0;
            for (r = 0; r < this.m_Cells.length; r++) {
                let c = this.m_Cells[r][i];
                if (c === null) 
                    continue;
                if (c.content !== null) {
                    if (c.colBegin === i) 
                        break;
                }
            }
            if (r < this.m_Cells.length) 
                continue;
            for (r = 0; r < this.m_Cells.length; r++) {
                for (let c = 0; c < this.m_Cells[r].length; c++) {
                    let ce = this.m_Cells[r][c];
                    if (ce === null) 
                        continue;
                    if (ce.colBegin !== c || ce.rowBegin !== r) 
                        continue;
                    if (ce.colBegin >= i) 
                        ce.colBegin = ce.colBegin - 1;
                    if (ce.colEnd >= i) 
                        ce.colEnd = ce.colEnd - 1;
                }
            }
            for (r = 0; r < this.m_Cells.length; r++) {
                let col = this.m_Cells[r][i];
                if (col.m_StyledFrag !== null) {
                }
                this.m_Cells[r].splice(i, 1);
            }
            this.m_ColsCount--;
        }
        for (let r = this.m_Cells.length - 1; r >= 0; r--) {
            let i = 0;
            for (i = 0; i < this.m_Cells[r].length; i++) {
                let cel = this.m_Cells[r][i];
                if (cel === null) 
                    continue;
                if (cel.rowBegin === r && cel.colBegin === i) {
                    if (cel.content !== null) {
                        let cnt = Utils.as(cel.content, UnitextContainer);
                        if (cnt !== null && cnt.children.length > 0 && (cnt.children[0] instanceof UnitextPlaintext)) {
                            if (Utils.startsWithString(cnt.children[0].text, "КОНСУЛЬТАНТПЛЮС", true)) {
                                if (this.m_Cells.length < 4) 
                                    this.ignorable = true;
                                for (let j = 0; j < this.m_Cells[r].length; j++) {
                                    if (this.m_Cells[r][j] !== null) 
                                        this.m_Cells[r][j].ignorable = true;
                                }
                            }
                        }
                        break;
                    }
                }
            }
            if (i < this.m_Cells[r].length) 
                continue;
            for (let rr = 0; rr < this.m_Cells.length; rr++) {
                for (let c = 0; c < this.m_Cells[r].length; c++) {
                    let col = this.m_Cells[rr][c];
                    if (col === null) 
                        continue;
                    if (col.colBegin !== c || col.rowBegin !== rr) 
                        continue;
                    if (col.rowBegin >= r) 
                        col.rowBegin = col.rowBegin - 1;
                    if (col.rowEnd >= r) 
                        col.rowEnd = col.rowEnd - 1;
                    if (col.content !== null) 
                        continue;
                    if (col.m_StyledFrag !== null) {
                        if (col.m_StyledFrag.parent !== null && col.m_StyledFrag.parent.children.includes(col.m_StyledFrag)) 
                            Utils.removeItem(col.m_StyledFrag.parent.children, col.m_StyledFrag);
                    }
                }
            }
            this.m_Cells.splice(r, 1);
        }
        if (this.m_ColsCount === 1) {
            if (this.ignorable) 
                return this;
            let cnt = new UnitextContainer();
            cnt.isInline = false;
            for (let r = 0; r < this.m_Cells.length; r++) {
                if (this.m_Cells[r][0].content !== null && this.m_Cells[r][0].rowBegin === r) {
                    cnt.children.push(this.m_Cells[r][0].content);
                    cnt.children.push(UnitextNewline._new633(cnt));
                }
            }
            if (colsCount0 === 4 && rowCount0 === 1) {
                let str = cnt.getPlaintextString(GetPlaintextParam._new356(false));
                if (str !== null) {
                    if ((Utils.startsWithString(str, "ПО ВОПРОС", true) || str.includes("вступает в силу") || str.includes("вступают в силу")) || str.includes("утратил силу") || str.includes("утратила силу")) {
                        this.ignorable = true;
                        return this;
                    }
                }
            }
            cnt.children.splice(0, 0, UnitextNewline._new633(cnt));
            return cnt.optimize(false, pars);
        }
        if (this.m_ColsCount === 0) 
            return null;
        if (pars !== null && pars.splitTableRows) {
            for (let r = 0; r < this.rowsCount; r++) {
                let c = 0;
                let ppp = 0;
                let cou = 0;
                for (c = 0; c < this.colsCount; c++) {
                    let cel = this.getCell(r, c);
                    if (cel === null) 
                        break;
                    if (cel.colBegin !== cel.colEnd || cel.rowBegin !== cel.rowEnd) 
                        break;
                    if (cel.content === null) 
                        continue;
                    let cnt = Utils.as(cel.content, UnitextContainer);
                    if (cnt === null) 
                        break;
                    let i = 0;
                    let p = 0;
                    for (i = 0; i < cnt.children.length; i++) {
                        let ch = cnt.children[i];
                        if (((ch instanceof UnitextPlaintext) || (ch instanceof UnitextHyperlink) || (ch instanceof UnitextImage)) || (ch instanceof UnitextPagebreak)) {
                        }
                        else if (ch instanceof UnitextNewline) {
                            if (ch.count > 1) 
                                p++;
                        }
                        else 
                            break;
                    }
                    if (i < cnt.children.length) 
                        break;
                    if (p === 0) 
                        break;
                    if (ppp === 0) 
                        ppp = p;
                    else if (ppp !== p) 
                        break;
                    cou++;
                }
                if ((c < this.colsCount) || ppp === 0 || (cou < 2)) 
                    continue;
                let nrow = new Array();
                for (let rr = r + 1; rr < this.m_Cells.length; rr++) {
                    for (c = 0; c < this.colsCount; c++) {
                        if (this.m_Cells[rr][c] !== null) 
                            this.m_Cells[rr][c].tag = null;
                    }
                }
                for (let rr = r + 1; rr < this.m_Cells.length; rr++) {
                    for (c = 0; c < this.colsCount; c++) {
                        if (this.m_Cells[rr][c] !== null && this.m_Cells[rr][c].tag === null) {
                            this.m_Cells[rr][c].tag = this.m_Cells;
                            this.m_Cells[rr][c].rowBegin = this.m_Cells[rr][c].rowBegin + 1;
                            this.m_Cells[rr][c].rowEnd = this.m_Cells[rr][c].rowEnd + 1;
                        }
                    }
                }
                this.m_Cells.splice(r + 1, 0, nrow);
                for (c = 0; c < this.colsCount; c++) {
                    let cel = this.getCell(r, c);
                    let cel1 = UnitextTablecell._new636(true, c, c, r + 1, r + 1);
                    nrow.push(cel1);
                    if (cel.content === null) 
                        continue;
                    let cnt = Utils.as(cel.content, UnitextContainer);
                    let i = 0;
                    for (i = cnt.children.length - 1; i >= 0; i--) {
                        if (cnt.children[i] instanceof UnitextPagebreak) 
                            cnt.children.splice(i, 1);
                    }
                    for (i = 0; i < cnt.children.length; i++) {
                        if ((cnt.children[i] instanceof UnitextNewline) && cnt.children[i].count > 1) 
                            break;
                    }
                    if (i >= cnt.children.length) 
                        continue;
                    if (i === 1) {
                        cel1.content = cnt;
                        cel.content = cnt.children[0];
                        cnt.children.splice(0, 1);
                        cnt.children.splice(0, 1);
                        cel1.content = cel1.content.optimize(true, pars);
                    }
                    else {
                        let cnt1 = new UnitextContainer();
                        for (let ii = i + 1; ii < cnt.children.length; ii++) {
                            cnt1.children.push(cnt.children[ii]);
                        }
                        cnt.children.splice(i, cnt.children.length - i);
                        cel1.content = cnt1.optimize(true, pars);
                    }
                }
            }
        }
        if (this.m_ColsCount > 1 && this.rowsCount > 10) {
            let i = 0;
            let ok = false;
            for (i = 0; i < this.rowsCount; i++) {
                if ((this.m_Cells[i] !== null && this.m_Cells[i].length > 0 && this.m_Cells[i][0].colBegin === 0) && this.m_Cells[i][0].colEnd === (this.colsCount - 1)) {
                    if (i === 0 && this.m_Cells[i][0].content !== null) {
                        let str = this.m_Cells[i][0].content.getPlaintextString(null).trim();
                        if (Utils.startsWithString(str, "РАЗДЕЛ", true) || Utils.startsWithString(str, "ПОДРАЗДЕЛ", true) || Utils.startsWithString(str, "ГЛАВА", true)) 
                            ok = true;
                    }
                }
                else 
                    break;
            }
            if (i > (Utils.intDiv(this.rowsCount * 3, 4))) 
                ok = true;
            if (ok) {
                let cnt = new UnitextContainer();
                cnt.isInline = false;
                let tab = null;
                for (i = 0; i < this.rowsCount; i++) {
                    if ((this.m_Cells[i] !== null && this.m_Cells[i].length > 0 && this.m_Cells[i][0].colBegin === 0) && this.m_Cells[i][0].colEnd === (this.colsCount - 1)) {
                        tab = null;
                        cnt.children.push(this.m_Cells[i][0].content);
                        cnt.children.push(UnitextNewline._new633(cnt));
                    }
                    else {
                        if (tab === null) {
                            tab = Utils.as(this.clone(), UnitextTable);
                            tab.m_Cells.splice(0, tab.m_Cells.length);
                            cnt.children.push(tab);
                            cnt.children.push(UnitextNewline._new633(cnt));
                        }
                        let r = tab.rowsCount;
                        for (let j = 0; j < this.colsCount; j++) {
                            if (this.m_Cells[i][j] !== null) {
                                tab.addCell(r, r, this.m_Cells[i][j].colBegin, this.m_Cells[i][j].colEnd, this.m_Cells[i][j].content, false);
                                j = this.m_Cells[i][j].colEnd;
                            }
                        }
                    }
                }
                cnt.children.splice(0, 0, UnitextNewline._new633(cnt));
                return cnt.optimize(false, pars);
            }
        }
        for (let i = 1; i < (this.m_ColsCount - 1); i++) {
            if ((this.m_ColWidth !== null && (i < this.m_ColWidth.length) && this.m_ColWidth[i] !== null) && (this.m_ColWidth[i].length < 3) && this.m_ColWidth[i].endsWith("%")) {
                let first = 0;
                let last = 0;
                let nul = 0;
                let r = 0;
                for (r = 0; r < this.rowsCount; r++) {
                    let cel = this.getCell(r, i);
                    if (cel === null || cel.content === null) 
                        nul++;
                    else if (cel.colBegin === cel.colEnd) 
                        break;
                    else if (cel.colBegin === i) 
                        first++;
                    else if (cel.colEnd === i) 
                        last++;
                }
                if (r < this.rowsCount) 
                    continue;
                if (first === 0 || last === 0) 
                    continue;
                for (r = 0; r < this.rowsCount; r++) {
                    let row = this.m_Cells[r];
                    if (row === null) 
                        continue;
                    for (let c = 0; c < row.length; c++) {
                        let cel = row[c];
                        if (cel.rowBegin !== r || cel.colBegin !== c) 
                            continue;
                        if (cel.colBegin > i && c > 0 && cel === row[c - 1]) 
                            continue;
                        if (cel.colBegin > i) 
                            cel.colBegin = cel.colBegin - 1;
                        if (cel.colEnd >= i) 
                            cel.colEnd = cel.colEnd - 1;
                    }
                    if (i < row.length) 
                        row.splice(i, 1);
                }
                if (this.m_ColWidth !== null && (i < this.m_ColWidth.length)) 
                    this.m_ColWidth.splice(i, 1);
                this.m_ColsCount--;
                i--;
            }
        }
        if (this.m_Cells.length > 1 && this.m_Cells[this.m_Cells.length - 1].length > 1) {
            let cel0 = this.m_Cells[this.m_Cells.length - 1][0];
            let cel1 = this.m_Cells[this.m_Cells.length - 1][this.m_Cells[this.m_Cells.length - 1].length - 1];
            if (cel0 === cel1 && (cel0.content instanceof UnitextTable)) {
                this.m_Cells.splice(this.m_Cells.length - 1, 1);
                let cnt = new UnitextContainer();
                cnt.children.push(this);
                cnt.children.push(new UnitextNewline());
                cnt.children.push(cel0.content);
                return cnt.optimize(isContent, pars);
            }
        }
        return this;
    }
    
    /**
     * Добавить ячейку
     * @param rowBegin начальная строка
     * @param rowEnd конечная строка
     * @param colBegin начальный столбец
     * @param colEnd конечный столбец
     * @param content возможное содержимое
     * @param overwrite полностью перезаписать то, что было на том месте (если было)
     * @return если null, то значит область пересекается с существующей ячейкой
     */
    addCell(rowBegin, rowEnd, colBegin, colEnd, content = null, overwrite = false) {
        if ((rowBegin < 0) || rowBegin > rowEnd) 
            return null;
        if ((colBegin < 0) || colBegin > colEnd) 
            return null;
        if ((colEnd + 1) > this.m_ColsCount) 
            this.m_ColsCount = colEnd + 1;
        while (this.m_Cells.length <= rowEnd) {
            this.m_Cells.push(new Array());
        }
        if (!overwrite) {
            for (let r = rowEnd; r >= rowBegin; r--) {
                while (this.m_Cells[r].length <= colEnd) {
                    this.m_Cells[r].push(null);
                }
                for (let c = colBegin; c <= colEnd; c++) {
                    if (this.m_Cells[r][c] !== null) {
                        if (r > rowBegin) {
                            rowEnd = r - 1;
                            break;
                        }
                        if (c > colBegin) {
                            colEnd = c - 1;
                            break;
                        }
                        return null;
                    }
                }
            }
        }
        let cel = UnitextTablecell._new640(content, colBegin, colEnd, rowBegin, rowEnd);
        for (let r = rowBegin; r <= rowEnd; r++) {
            for (let c = colBegin; c <= colEnd; c++) {
                while (this.m_Cells[r].length <= colEnd) {
                    this.m_Cells[r].push(null);
                }
                this.m_Cells[r][c] = cel;
            }
        }
        return cel;
    }
    
    /**
     * Получить ячейку таблицы, накрывающую указанную клетку
     * @param row строка клетки
     * @param col столбец клетки
     * @return ячейка (null - отсутствует)
     * 
     */
    getCell(row, col) {
        if ((row < 0) || row >= this.m_Cells.length) 
            return null;
        if ((col < 0) || col >= this.m_Cells[row].length) 
            return null;
        return this.m_Cells[row][col];
    }
    
    /**
     * Получить текст для ячейки, накрывающую указанную клетку
     * @param row строка клетки
     * @param col столбец клетки
     * @return плоский текст, полученный функцией GetPlaintextString для содержимого ячейки или null при пустой ячейке
     */
    getCellPlaintextString(row, col) {
        let cel = this.getCell(row, col);
        if (cel === null || cel.content === null) 
            return null;
        return cel.content.getPlaintextString(null);
    }
    
    /**
     * Получить ширину столбца (если таковая была задана во входном файле)
     * @param col номер столбца
     * @return ширина (как она была задана в Html или Doc) или null
     */
    getColWidth(col) {
        if (col >= 0 && (col < this.m_ColWidth.length)) 
            return this.m_ColWidth[col];
        else 
            return null;
    }
    
    setColWidth(col, val) {
        if (col < 0) 
            return;
        while (col >= this.m_ColWidth.length) {
            this.m_ColWidth.push(null);
        }
        this.m_ColWidth[col] = val;
    }
    
    get colsCount() {
        return this.m_ColsCount;
    }
    
    get rowsCount() {
        return this.m_Cells.length;
    }
    
    clone() {
        let res = new UnitextTable();
        res._cloneFrom(this);
        res.m_ColsCount = this.m_ColsCount;
        res.tableHeaderPaginate = this.tableHeaderPaginate;
        if (this.m_ColWidth !== null) 
            res.m_ColWidth = Array.from(this.m_ColWidth);
        res.width = this.width;
        res.hideBorders = this.hideBorders;
        res.mayHasError = this.mayHasError;
        for (const r of this.m_Cells) {
            let rr = new Array();
            res.m_Cells.push(rr);
            for (let i = 0; i < r.length; i++) {
                if (r[i] === null) 
                    rr.push(null);
                else {
                    let cc = Utils.as(r[i].clone(), UnitextTablecell);
                    cc.tag = r[i];
                    cc.parent = this;
                    rr.push(cc);
                }
            }
        }
        return res;
    }
    
    get startsOnNewline() {
        return true;
    }
    
    /**
     * Добавить строки второй таблицы (количество колонок должно быть одинаковым, иначе не добавит)
     * @param tab таблица с добавляемыми строками
     * @param dontClone при true строки будут не клонироваться, а вставляться напрямую
     * @param checkWidth при true будет проверяться ширина колонок, при этом возможно, что в добавляемой таблице одной колонки не будет
     * @return удалось ли добавить
     */
    appendTable(tab, dontClone, checkWidth) {
        if (tab === null) 
            return false;
        if (tab.colsCount > this.colsCount) 
            return false;
        if ((tab.colsCount + 1) < this.colsCount) 
            return false;
        if (tab.colsCount !== this.colsCount && !checkWidth) 
            return false;
        let mergeCol = -1;
        if (checkWidth) {
            let k0 = 0;
            for (let k = 0; k < tab.colsCount; k++,k0++) {
                let w1 = this.getColWidth(k0);
                let w2 = tab.getColWidth(k);
                if (w1 === w2) 
                    continue;
                if (w1.endsWith("%")) 
                    w1 = w1.substring(0, 0 + w1.length - 1);
                if (w2.endsWith("%")) 
                    w2 = w2.substring(0, 0 + w2.length - 1);
                let n1 = 0;
                let n2 = 0;
                let wrapn1643 = new RefOutArgWrapper();
                let inoutres644 = Utils.tryParseInt(w1, wrapn1643);
                let wrapn2645 = new RefOutArgWrapper();
                let inoutres646 = Utils.tryParseInt(w2, wrapn2645);
                n1 = wrapn1643.value;
                n2 = wrapn2645.value;
                if (!inoutres644 || !inoutres646) 
                    return false;
                let d = n1 - n2;
                if (d < 0) 
                    d = -d;
                if (d < 2) 
                    continue;
                if (((k0 + 1) < this.colsCount) && (mergeCol < 0)) {
                    let w3 = tab.getColWidth(k0 + 1);
                    if (w3 !== null && w3.endsWith("%")) 
                        w3 = w3.substring(0, 0 + w3.length - 1);
                    let n3 = 0;
                    let wrapn3641 = new RefOutArgWrapper();
                    let inoutres642 = Utils.tryParseInt((w3 != null ? w3 : ""), wrapn3641);
                    n3 = wrapn3641.value;
                    if (inoutres642) {
                        d = n2 - n3;
                        if (d < 0) 
                            d = -d;
                        if (d < 2) {
                            mergeCol = k0;
                            k0++;
                            continue;
                        }
                    }
                }
                return false;
            }
        }
        let rcou0 = this.m_Cells.length;
        for (const r of tab.m_Cells) {
            let rr = new Array();
            this.m_Cells.push(rr);
            for (let i = 0; i < r.length; i++) {
                if (r[i] === null) 
                    rr.push(null);
                else if (dontClone) {
                    rr.push(r[i]);
                    r[i].rowBegin = r[i].rowBegin + rcou0;
                    r[i].rowEnd = r[i].rowEnd + rcou0;
                }
                else {
                    let cc = Utils.as(r[i].clone(), UnitextTablecell);
                    cc.tag = r[i];
                    cc.parent = this;
                    rr.push(cc);
                    cc.rowBegin = cc.rowBegin + rcou0;
                    cc.rowEnd = cc.rowEnd + rcou0;
                }
                if (i === mergeCol) 
                    rr.push(null);
            }
        }
        if (tab.endChar > this.endChar) 
            this.endChar = tab.endChar;
        return true;
    }
    
    removeRows(ind, cou) {
        for (let r = (ind + cou) - 1; r >= ind; r--) {
            this.removeRow(r);
        }
    }
    
    removeLastColumn() {
        this.m_ColsCount--;
        if (this.m_ColWidth !== null && this.m_ColWidth.length > this.m_ColsCount) 
            this.m_ColWidth.splice(this.m_ColsCount, this.m_ColWidth.length - this.m_ColsCount);
        for (const r of this.m_Cells) {
            if (r.length > this.m_ColsCount) 
                r.splice(this.m_ColsCount, r.length - this.m_ColsCount);
        }
    }
    
    get isInline() {
        return false;
    }
    
    get innerTag() {
        return "tbl";
    }
    
    findById(_id) {
        if (this.id === _id) 
            return this;
        for (let r = 0; r < this.m_Cells.length; r++) {
            for (let c = 0; c < this.m_Cells[r].length; c++) {
                if (this.m_Cells[r][c] !== null) {
                    let res = this.m_Cells[r][c].findById(_id);
                    if (res !== null) 
                        return res;
                }
            }
        }
        return null;
    }
    
    _findCellByPos(plainPos) {
        if (this.m_Map === null && (this.beginChar < this.endChar)) {
            this.m_Map = Array((this.endChar + 1) - this.beginChar).fill(null);
            for (let r = 0; r < this.m_Cells.length; r++) {
                for (let c = 0; c < this.m_Cells[r].length; c++) {
                    if (this.m_Cells[r][c] !== null) {
                        let cel = this.m_Cells[r][c];
                        for (let i = cel.beginChar; i <= cel.endChar; i++) {
                            if (i >= this.beginChar && i <= this.endChar) 
                                this.m_Map[i - this.beginChar] = cel;
                        }
                    }
                }
            }
        }
        if ((this.m_Map !== null && plainPos >= this.beginChar && plainPos <= this.endChar) && ((plainPos - this.beginChar) < this.m_Map.length)) 
            return this.m_Map[plainPos - this.beginChar];
        for (let r = 0; r < this.m_Cells.length; r++) {
            for (let c = 0; c < this.m_Cells[r].length; c++) {
                if (this.m_Cells[r][c] !== null) {
                    let cel = this.m_Cells[r][c];
                    if (cel.beginChar <= plainPos && plainPos <= cel.endChar) 
                        return cel;
                }
            }
        }
        return null;
    }
    
    tryAppend(tab) {
        if (tab.colsCount !== this.m_ColsCount) 
            return false;
        if (this.pageSectionId !== tab.pageSectionId) 
            return false;
        if (tab.rowsCount < 1) 
            return false;
        let k = 0;
        for (k = 0; k < tab.colsCount; k++) {
            let w1 = this.getColWidth(k);
            let w2 = tab.getColWidth(k);
            if (w1 === null && w2 === null) 
                continue;
            if (w1 === null || w2 === null) 
                return false;
            if (w1 === w2) 
                continue;
            let su1 = new SizeunitConverter(w1);
            let su2 = new SizeunitConverter(w2);
            if (su1.unit !== su2.unit) 
                return false;
            let d = Math.abs(su1.val - su2.val);
            if (d > 1) 
                return false;
        }
        for (k = 0; k < 2; k++) {
            let r = (k === 0 ? 0 : this.m_Cells.length - 1);
            let c = 0;
            for (c = 0; c < this.m_Cells[r].length; c++) {
                if (this.m_Cells[r][c] === null) 
                    break;
                let cel = tab.getCell(0, c);
                if (cel === null) 
                    break;
                if (cel.colBegin !== this.m_Cells[r][c].colBegin || cel.colEnd !== this.m_Cells[r][c].colEnd) 
                    break;
            }
            if (c >= this.m_Cells[r].length) 
                break;
        }
        if (k >= 2) 
            return false;
        let delt = this.m_Cells.length;
        for (let r = tab.rowsCount - 1; r >= 0; r--) {
            for (let c = 0; c < tab.colsCount; c++) {
                let cel = tab.getCell(r, c);
                if (cel === null) 
                    continue;
                if (cel.colBegin === c && cel.rowBegin === r) {
                    cel.rowBegin = cel.rowBegin + delt;
                    cel.rowEnd = cel.rowEnd + delt;
                }
            }
        }
        this.m_Cells.splice(this.m_Cells.length, 0, ...tab.m_Cells);
        tab.m_Cells.splice(0, tab.m_Cells.length);
        return true;
    }
    
    toString() {
        return ("Table [" + this.rowsCount + "x" + this.colsCount + "]" + (this.mayHasError ? " may be error" : "") + (this.ignorable ? " ignorable" : ""));
    }
    
    getPlaintext(res, pars = null) {
        if (pars !== null && pars.setPositions) 
            this.beginChar = res.length;
        let nullPars = false;
        if (pars === null) {
            nullPars = true;
            pars = UnitextItem.m_DefParams;
        }
        let pos0 = res.length;
        if (pars.tableStart !== null) 
            res.append(pars.tableStart);
        for (let r = 0; r < this.m_Cells.length; r++) {
            for (let c = 0; c < this.m_Cells[r].length; c++) {
                let cel = this.m_Cells[r][c];
                if (cel === null) 
                    continue;
                if (r === cel.rowBegin && c === cel.colBegin) 
                    cel.getPlaintext(res, pars);
            }
            if (pars === null) 
                res.append("\r\n").append((String.fromCharCode(7)));
            else {
                res.append((pars.newLine != null ? pars.newLine : ""));
                res.append((pars.tableRowEnd != null ? pars.tableRowEnd : ""));
            }
            if (pars !== null && pars.maxTextLength > 0 && res.length > pars.maxTextLength) 
                break;
        }
        if (pars.tableEnd !== null) 
            res.append(pars.tableEnd);
        if (pars !== null && pars.setPositions) 
            this.endChar = res.length - 1;
        if (this.ignorable && ((nullPars || !pars.keepIgnored))) {
            for (let i = pos0; i < res.length; i++) {
                res.setCharAt(i, ' ');
            }
            if (pars.newLine !== null) {
                for (let j = 0; j < pars.newLine.length; j++) {
                    res.setCharAt((res.length - pars.newLine.length) + j, pars.newLine[j]);
                }
            }
        }
    }
    
    getHtml(res, par) {
        if (!par.callBefore(this, res)) 
            return;
        if (this.ignorable) 
            return;
        res.append("\r\n<table border=\"").append((this.mayHasError ? 3 : (this.hideBorders ? 0 : 1))).append("\" width=\"").append(((this.width != null ? this.width : "100%"))).append("\"").append((this.mayHasError ? " style=\"border-color:red\"" : ""));
        if (this.id !== null) {
            res.append(" id=\"").append(this.id).append("\"");
            if (par !== null && par.styles.containsKey(this.id)) 
                res.append(" style=\"").append(par.styles.get(this.id)).append("\"");
            let fr = this.getStyledFragment(-1);
            if (fr !== null && fr.typ !== UnitextStyledFragmentType.TABLE) 
                fr = fr.parent;
            if ((fr !== null && fr.typ === UnitextStyledFragmentType.TABLE && fr.styleId > 0) && fr.style !== null) {
                res.append(" style=\"");
                fr.style.getHtml(res);
                res.append("\"");
            }
        }
        res.append(">");
        if (this.m_ColWidth.length > 0) {
            res.append("<colgroup>");
            for (let i = 0; i < this.colsCount; i++) {
                res.append("<col");
                if ((i < this.m_ColWidth.length) && this.m_ColWidth[i] !== null) 
                    res.append(" width=\"").append(this.m_ColWidth[i]).append("\"");
                res.append("/>");
            }
            res.append("</colgroup>");
        }
        for (let r = 0; r < this.m_Cells.length; r++) {
            res.append("\r\n <tr>");
            for (let c = 0; c < this.m_Cells[r].length; c++) {
                let cel = this.m_Cells[r][c];
                if (cel === null) 
                    continue;
                if (r === cel.rowBegin && c === cel.colBegin) {
                    res.append("\r\n   ");
                    cel.getHtml(res, par);
                }
            }
            res.append("\r\n </tr>");
            let stop = false;
            if (par !== null && res.length > par.maxHtmlSize) 
                stop = true;
            if (par !== null && par.maxTableRowCount > 0 && (r + 1) >= par.maxTableRowCount) 
                stop = true;
            if (stop) {
                res.append("<tr><td colspan=\"").append(this.colsCount).append("\" style=\"background:lightpink\">Остальные ").append((this.m_Cells.length - r)).append(" строк не выведены из-за ограничений html</td></tr>");
                break;
            }
        }
        res.append("\r\n</table>\r\n");
        if (par !== null) 
            par._outFootnotes(res);
        par.callAfter(this, res);
    }
    
    getXml(xml) {
        xml.writeStartElement("table");
        this._writeXmlAttrs(xml);
        xml.writeAttributeString("rows", this.rowsCount.toString());
        xml.writeAttributeString("columns", this.colsCount.toString());
        if (this.width !== null) 
            xml.writeAttributeString("width", this.width);
        if (this.hideBorders) 
            xml.writeAttributeString("border", "hide");
        if (this.tableHeaderPaginate) 
            xml.writeAttributeString("headerpaginate", "true");
        for (let i = 0; i < this.m_ColWidth.length; i++) {
            if (this.m_ColWidth[i] !== null) {
                xml.writeStartElement("col");
                xml.writeAttributeString("num", i.toString());
                xml.writeAttributeString("width", this.m_ColWidth[i]);
                xml.writeEndElement();
            }
        }
        for (let r = 0; r < this.m_Cells.length; r++) {
            xml.writeStartElement("row");
            for (let c = 0; c < this.m_Cells[r].length; c++) {
                let cel = this.m_Cells[r][c];
                if (cel === null) 
                    continue;
                if (r === cel.rowBegin && c === cel.colBegin) 
                    cel.getXml(xml);
            }
            xml.writeEndElement();
        }
        xml.writeEndElement();
    }
    
    fromXml(xml) {
        super.fromXml(xml);
        let rows = 0;
        let cols = 0;
        if (xml.attributes !== null) {
            for (const a of xml.attributes) {
                if (a.local_name === "rows") 
                    rows = Utils.parseInt(a.value);
                else if (a.local_name === "columns") 
                    this.m_ColsCount = (cols = Utils.parseInt(a.value));
                else if (a.local_name === "width") 
                    this.width = a.value;
                else if (a.local_name === "border") {
                    if (a.value === "hide") 
                        this.hideBorders = true;
                }
                else if (a.local_name === "headerpaginate") 
                    this.tableHeaderPaginate = a.value === "true";
            }
        }
        for (let r = 0; r < rows; r++) {
            let row = new Array();
            for (let c = 0; c < cols; c++) {
                row.push(null);
            }
            this.m_Cells.push(row);
        }
        for (const x of xml.child_nodes) {
            if (x.local_name === "row") {
                for (const xx of x.child_nodes) {
                    let cel = Utils.as(UnitextHelper.createItem(xx, this), UnitextTablecell);
                    if (cel === null) 
                        continue;
                    if (cel.colEnd >= cols || cel.rowEnd >= rows) 
                        continue;
                    for (let r = cel.rowBegin; r <= cel.rowEnd; r++) {
                        for (let c = cel.colBegin; c <= cel.colEnd; c++) {
                            this.m_Cells[r][c] = cel;
                        }
                    }
                }
            }
            else if (x.local_name === "col") {
                let num = 0;
                let val = null;
                if (x.attributes !== null) {
                    for (const a of x.attributes) {
                        if (a.local_name === "width") 
                            val = a.value;
                        else if (a.local_name === "num") {
                            let wrapnum647 = new RefOutArgWrapper();
                            Utils.tryParseInt(Utils.notNull(a.value, ""), wrapnum647);
                            num = wrapnum647.value;
                        }
                    }
                }
                if (val !== null) 
                    this.setColWidth(num, val);
            }
        }
    }
    
    getAllItems(res, lev) {
        if (res !== null) 
            res.push(this);
        for (let r = 0; r < this.m_Cells.length; r++) {
            for (let c = 0; c < this.m_Cells[r].length; c++) {
                let cel = this.m_Cells[r][c];
                if (cel === null) 
                    continue;
                if (r === cel.rowBegin && c === cel.colBegin) {
                    cel.parent = this;
                    cel.getAllItems(res, lev + 1);
                }
            }
        }
    }
    
    addPlainTextPos(d) {
        super.addPlainTextPos(d);
        for (let r = 0; r < this.m_Cells.length; r++) {
            for (let c = 0; c < this.m_Cells[r].length; c++) {
                let cel = this.m_Cells[r][c];
                if (cel === null) 
                    continue;
                if (r === cel.rowBegin && c === cel.colBegin) 
                    cel.addPlainTextPos(d);
            }
        }
    }
    
    correct(typ, data) {
        for (let r = 0; r < this.m_Cells.length; r++) {
            for (let c = 0; c < this.m_Cells[r].length; c++) {
                let cel = this.m_Cells[r][c];
                if (cel === null) 
                    continue;
                if (r === cel.rowBegin && c === cel.colBegin) 
                    cel.correct(typ, data);
            }
        }
    }
    
    setDefaultTextPos(cp, res) {
        this.beginChar = cp.value;
        for (let r = 0; r < this.m_Cells.length; r++) {
            for (let c = 0; c < this.m_Cells[r].length; c++) {
                let cel = this.m_Cells[r][c];
                if (cel === null) 
                    continue;
                if (r === cel.rowBegin && c === cel.colBegin) {
                    cel.setDefaultTextPos(cp, res);
                    cel.parent = this;
                }
            }
        }
        this.endChar = cp.value - 1;
    }
    
    /**
     * Удалить всю строку
     * @param r номер строки
     * @return true, если удалилась
     */
    removeRow(r) {
        if ((r < 0) || r >= this.m_Cells.length) 
            return false;
        for (let rr = 0; rr < this.rowsCount; rr++) {
            for (let cc = 0; cc < this.colsCount; cc++) {
                let cel = this.getCell(rr, cc);
                if (cel === null) 
                    break;
                if (cel.rowBegin !== rr || cel.colBegin !== cc) 
                    continue;
                if (cel.rowBegin >= r) 
                    cel.rowBegin = cel.rowBegin - 1;
                if (cel.rowEnd >= r) 
                    cel.rowEnd = cel.rowEnd - 1;
                cc = cel.colEnd;
            }
        }
        this.m_Cells.splice(r, 1);
        return true;
    }
}


module.exports = UnitextTable