devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
704 lines • 56.1 kB
JavaScript
import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed';
import { ListUtils } from '@devexpress/utils/lib/utils/list';
import { SearchUtils } from '@devexpress/utils/lib/utils/search';
import { BoundaryInterval } from '@devexpress/utils/lib/intervals/boundary';
import { Calculator } from '../../../layout-formatter/table/grid-engine/calculators/column-width-engine/calculator';
import { BorderInfo } from '../../borders/border-info';
import { BorderLineStyle } from '../../borders/enums';
import { TableStyleChangedSubDocumentChange } from '../../changes/sub-document/style/table-style-changed';
import { TableCellInsertedSubDocumentChange } from '../../changes/sub-document/table/cell-inserted';
import { TableCellMergedHorizontallySubDocumentChange } from '../../changes/sub-document/table/cell-merged-horizontally';
import { TableCellRemovedSubDocumentChange } from '../../changes/sub-document/table/cell-removed';
import { TableCellSplittedHorizontallySubDocumentChange } from '../../changes/sub-document/table/cell-splitted-horizontally';
import { TableCreatedSubDocumentChange } from '../../changes/sub-document/table/created';
import { TableRemovedSubDocumentChange } from '../../changes/sub-document/table/removed';
import { TableRowInsertedSubDocumentChange } from '../../changes/sub-document/table/row-inserted';
import { TableRowRemovedSubDocumentChange } from '../../changes/sub-document/table/row-removed';
import { TableStartPositionShiftedSubDocumentChange } from '../../changes/sub-document/table/start-position-shifted';
import { ParagraphFirstLineIndentHistoryItem, ParagraphFirstLineIndentTypeHistoryItem, ParagraphLeftIndentHistoryItem } from '../../history/items/paragraph-properties-history-items';
import { RemoveIntervalHistoryItem } from '../../history/items/remove-interval-history-item';
import { ShiftTableStartPositionToTheRightHistoryItem } from '../../history/items/tables/change-table-cell-history-items';
import { CreateTableHistoryItem } from '../../history/items/tables/create-table-history-item';
import { RemoveTableCellHistoryItem } from '../../history/items/tables/remove-table-cell-history-item';
import { RemoveTableHistoryItem } from '../../history/items/tables/remove-table-history-item';
import { RemoveTableRowHistoryItem } from '../../history/items/tables/remove-table-row-history-item';
import { TableCellColumnSpanHistoryItem, TableCellPreferredWidthHistoryItem, TableCellVerticalMergingHistoryItem } from '../../history/items/tables/table-cell-properties-history-items';
import { TableBordersHistoryItem, TableLookTypesHistoryItem, TablePreferredWidthHistoryItem } from '../../history/items/tables/table-properties-history-items';
import { TableRowGridAfterHistoryItem, TableRowGridBeforeHistoryItem, TableRowHeightHistoryItem, TableRowWidthAfterHistoryItem, TableRowWidthBeforeHistoryItem } from '../../history/items/tables/table-row-properties-history-items';
import { ModelIterator } from '../../model-iterator';
import { ParagraphFirstLineIndent } from '../../paragraph/paragraph-properties';
import { SubDocumentInterval, SubDocumentPosition } from '../../sub-document';
import { Table, TablePosition } from '../../tables/main-structures/table';
import { TableCell } from '../../tables/main-structures/table-cell';
import { TableRow } from '../../tables/main-structures/table-row';
import { TableCellProperties } from '../../tables/properties/table-cell-properties';
import { TableProperties } from '../../tables/properties/table-properties';
import { TableRowProperties } from '../../tables/properties/table-row-properties';
import { TableCellMergingState, TableLookTypes } from '../../tables/secondary-structures/table-base-structures';
import { TableHeightUnit, TableHeightUnitType, TableWidthUnit, TableWidthUnitType } from '../../tables/secondary-structures/table-units';
import { TableCellUtils } from '../../tables/table-utils';
import { BaseManipulator } from '../base-manipulator';
import { InsertParagraphManipulatorParams } from '../paragraph-manipulator/insert-paragraph-manipulator-params';
import { InsertTableCellToTheLeftOperation, InsertTableCellToTheRightOperation } from './insert-table-cell-operation';
import { InsertTableRowAboveOperation, InsertTableRowBelowOperation } from './insert-table-row-operation';
import { MergeTwoTableCellsHorizontallyOperation, MergeTwoTableCellsVerticallyOperation } from './merge-table-cells-operation';
import { TableCellPropertiesManipulator } from './table-cell-properties-manipulator';
import { TablePropertiesManipulator } from './table-properties-manipulator';
import { TableRowPropertiesManipulator } from './table-row-properties-manipulator';
export class TablesManipulator extends BaseManipulator {
constructor(manipulator) {
super(manipulator);
this.cellProperties = new TableCellPropertiesManipulator(manipulator);
this.tableProperties = new TablePropertiesManipulator(manipulator);
this.rowProperties = new TableRowPropertiesManipulator(manipulator);
}
static removeTableCore(table, tables, tablesByLevels) {
const position = table.getStartPosition();
const tblLevel = tablesByLevels[table.nestedLevel];
tblLevel.splice(SearchUtils.binaryIndexOf(tblLevel, t => t.getStartPosition() - position), 1);
tables.splice(table.index, 1);
Table.advanceIndices(tables, table.index, -1);
}
createTable(subDocument, firstParagraphIndex, rowCount, cellCount) {
let paragraph = subDocument.paragraphs[firstParagraphIndex];
let parentCell = Table.getTableCellByPosition(subDocument.tables, paragraph.startLogPosition.value);
let newTable = new Table(new TableProperties(), subDocument.documentModel.getDefaultTableStyle());
this.createTableStructure(subDocument, firstParagraphIndex, newTable, rowCount, cellCount);
this.insertTableCore(newTable, subDocument.tables, subDocument.tablesByLevels, parentCell);
this.modelManipulator.notifyModelChanged(new TableCreatedSubDocumentChange(subDocument.id, newTable));
return newTable;
}
removeTable(subDocument, table) {
const startPosition = table.getStartPosition();
const endPosition = table.getEndPosition();
table.destructor(subDocument.positionManager);
const removedText = subDocument.getText(table.interval);
TablesManipulator.removeTableCore(table, subDocument.tables, subDocument.tablesByLevels);
this.modelManipulator.notifyModelChanged(new TableRemovedSubDocumentChange(subDocument.id, startPosition, endPosition, table.nestedLevel, removedText));
}
restoreRemovedTable(subDocument, table, cellsRanges) {
for (let rowIndex = 0, row; row = table.rows[rowIndex]; rowIndex++) {
for (let cellIndex = 0, cell; cell = row.cells[cellIndex]; cellIndex++) {
let cellRange = cellsRanges[rowIndex][cellIndex];
cell.startParagraphPosition = subDocument.positionManager.registerPosition(cellRange.start);
cell.endParagrapPosition = subDocument.positionManager.registerPosition(cellRange.end);
}
}
table.parentCell = Table.getTableCellByPosition(subDocument.tables, table.getStartPosition());
this.insertTableCore(table, subDocument.tables, subDocument.tablesByLevels, table.parentCell);
this.modelManipulator.notifyModelChanged(new TableCreatedSubDocumentChange(subDocument.id, table));
}
pasteTable(subDocument, patternTable, position) {
let patternCell = Table.getTableCellByPosition(subDocument.tables, position);
let newTable = new Table(patternTable.properties, this.model.stylesManager.addTableStyle(patternTable.style));
newTable.preferredWidth = patternTable.preferredWidth.clone();
newTable.lookTypes = patternTable.lookTypes;
for (let rowIndex = 0, patternRow; patternRow = patternTable.rows[rowIndex]; rowIndex++) {
let newRow = new TableRow(newTable, subDocument.documentModel.cache.tableRowPropertiesCache.getItem(patternRow.properties.clone()));
newRow.gridAfter = patternRow.gridAfter;
newRow.gridBefore = patternRow.gridBefore;
if (patternRow.tablePropertiesException)
newRow.tablePropertiesException = patternRow.tablePropertiesException.clone();
newRow.widthAfter = patternRow.widthAfter.clone();
newRow.widthBefore = patternRow.widthBefore.clone();
newRow.height = patternRow.height.clone();
if (patternRow.tablePropertiesException)
newRow.tablePropertiesException = patternRow.tablePropertiesException.clone();
for (let cellIndex = 0, patternCell; patternCell = patternRow.cells[cellIndex]; cellIndex++) {
let cellLength = patternCell.endParagrapPosition.value - patternCell.startParagraphPosition.value;
let newCell = new TableCell(newRow, subDocument.documentModel.cache.tableCellPropertiesCache.getItem(patternCell.properties.clone()));
newCell.startParagraphPosition = subDocument.positionManager.registerPosition(position);
position += cellLength;
newCell.endParagrapPosition = subDocument.positionManager.registerPosition(position);
newCell.columnSpan = patternCell.columnSpan;
newCell.conditionalFormatting = patternCell.conditionalFormatting;
newCell.verticalMerging = patternCell.verticalMerging;
newCell.preferredWidth = patternCell.preferredWidth.clone();
newRow.cells.push(newCell);
}
newTable.rows.push(newRow);
}
this.insertTableCore(newTable, subDocument.tables, subDocument.tablesByLevels, patternCell);
this.modelManipulator.notifyModelChanged(new TableCreatedSubDocumentChange(subDocument.id, newTable));
return newTable;
}
insertRow(subDocument, tableIndex, patternRow, targetRowIndex, cellIntervals) {
var table = subDocument.tables[tableIndex];
var row = new TableRow(table, patternRow.properties);
if (cellIntervals.length !== patternRow.cells.length)
throw new Error("cellIntervals.length should be equal to patternRow.cells.length");
row.gridAfter = patternRow.gridAfter;
row.gridBefore = patternRow.gridBefore;
row.height = patternRow.height.clone();
row.properties = patternRow.properties;
row.tablePropertiesException = patternRow.tablePropertiesException;
row.widthAfter = patternRow.widthAfter.clone();
row.widthBefore = patternRow.widthBefore.clone();
for (let i = 0, interval; interval = cellIntervals[i]; i++) {
let patternCell = patternRow.cells[i];
let cell = new TableCell(row, patternCell.properties);
cell.startParagraphPosition = subDocument.positionManager.registerPosition(interval.start);
cell.endParagrapPosition = subDocument.positionManager.registerPosition(interval.end);
cell.columnSpan = patternCell.columnSpan;
cell.conditionalFormatting = patternCell.conditionalFormatting;
cell.preferredWidth = patternCell.preferredWidth.clone();
cell.verticalMerging = patternCell.verticalMerging;
cell.style = patternCell.style;
row.cells.push(cell);
}
table.rows.splice(targetRowIndex, 0, row);
let nextRow = table.rows[targetRowIndex + 1];
if (nextRow) {
subDocument.positionManager.unregisterPosition(nextRow.cells[0].startParagraphPosition);
nextRow.cells[0].startParagraphPosition = subDocument.positionManager.registerPosition(row.getEndPosition());
}
this.modelManipulator.notifyModelChanged(new TableRowInsertedSubDocumentChange(subDocument.id, table, targetRowIndex));
}
removeRow(subDocument, tableIndex, rowIndex) {
const table = subDocument.tables[tableIndex];
TablesManipulator.removeRowCore(subDocument, table, rowIndex);
this.modelManipulator.notifyModelChanged(new TableRowRemovedSubDocumentChange(subDocument.id, table, rowIndex));
}
static removeRowCore(subDocument, table, rowIndex) {
const row = table.rows[rowIndex];
row.destructor(subDocument.positionManager);
var nextRow = table.rows[rowIndex + 1];
if (nextRow) {
var nextRowFirstCell = nextRow.cells[0];
subDocument.positionManager.unregisterPosition(nextRowFirstCell.startParagraphPosition);
nextRowFirstCell.startParagraphPosition = subDocument.positionManager.registerPosition(row.getStartPosition());
}
table.rows.splice(rowIndex, 1);
}
removeCell(subDocument, table, rowIndex, cellIndex) {
let row = table.rows[rowIndex];
let cell = row.cells[cellIndex];
let nextCell = row.cells[cellIndex + 1];
if (!nextCell && table.rows.length > rowIndex + 1)
nextCell = table.rows[rowIndex + 1].cells[0];
cell.destructor(subDocument.positionManager);
if (nextCell) {
subDocument.positionManager.unregisterPosition(nextCell.startParagraphPosition);
nextCell.startParagraphPosition = subDocument.positionManager.registerPosition(cell.startParagraphPosition.value);
}
row.cells.splice(cellIndex, 1);
this.modelManipulator.notifyModelChanged(new TableCellRemovedSubDocumentChange(subDocument.id, table, rowIndex, cellIndex));
}
insertCell(subDocument, table, rowIndex, cellIndex, patternCell, length) {
let row = table.rows[rowIndex];
var cell = new TableCell(row, patternCell.properties);
cell.columnSpan = patternCell.columnSpan;
cell.conditionalFormatting = patternCell.conditionalFormatting;
cell.preferredWidth = patternCell.preferredWidth.clone();
cell.style = patternCell.style;
cell.verticalMerging = patternCell.verticalMerging;
let startPosition = 0;
if (cellIndex > 0)
startPosition = row.cells[cellIndex - 1].endParagrapPosition.value;
else if (rowIndex > 0)
startPosition = table.rows[rowIndex - 1].getEndPosition();
else
startPosition = table.getStartPosition();
cell.startParagraphPosition = subDocument.positionManager.registerPosition(startPosition);
cell.endParagrapPosition = subDocument.positionManager.registerPosition(startPosition + length);
row.cells.splice(cellIndex, 0, cell);
let nextCell = row.cells[cellIndex + 1];
if (!nextCell && table.rows.length > rowIndex + 1)
nextCell = table.rows[rowIndex + 1].cells[0];
if (nextCell) {
subDocument.positionManager.unregisterPosition(nextCell.startParagraphPosition);
nextCell.startParagraphPosition = subDocument.positionManager.registerPosition(cell.endParagrapPosition.value);
}
this.modelManipulator.notifyModelChanged(new TableCellInsertedSubDocumentChange(subDocument.id, table, rowIndex, cellIndex));
}
insertParagraphToTheCellStartAndShiftContent(subDocument, cell, inpPos) {
this.modelManipulator.paragraph.insertParagraphViaHistory(InsertParagraphManipulatorParams.makeParamsByPosition(new SubDocumentPosition(subDocument, cell.startParagraphPosition.value), inpPos));
this.shiftContent(subDocument, cell);
}
shiftContent(subDocument, cell) {
const table = cell.parentRow.parentTable;
let nextTable = subDocument.tables[table.index + 1];
let shiftingTables = [];
while (nextTable && nextTable.nestedLevel > table.nestedLevel) {
let nextTableStartPosition = nextTable.getStartPosition();
if (nextTableStartPosition === cell.startParagraphPosition.value)
shiftingTables.push(nextTable);
else if (nextTableStartPosition > cell.startParagraphPosition.value)
break;
nextTable = subDocument.tables[nextTable.index + 1];
}
for (let i = shiftingTables.length - 1, shiftingTable; shiftingTable = shiftingTables[i]; i--) {
this.history.addAndRedo(new ShiftTableStartPositionToTheRightHistoryItem(this.modelManipulator, subDocument, shiftingTable.index));
}
}
changeTableStartPosition(subDocument, table, newPosition) {
let cell = table.rows[0].cells[0];
var oldPosition = cell.startParagraphPosition.value;
subDocument.positionManager.unregisterPosition(cell.startParagraphPosition);
cell.startParagraphPosition = subDocument.positionManager.registerPosition(newPosition);
this.modelManipulator.notifyModelChanged(new TableStartPositionShiftedSubDocumentChange(subDocument.id, table, oldPosition, newPosition));
}
shiftTableStartPositionToTheRight(subDocument, table) {
this.changeTableStartPosition(subDocument, table, table.rows[0].cells[0].startParagraphPosition.value + 1);
}
restoreShiftedTableStartPositionToTheRight(subDocument, table) {
this.changeTableStartPosition(subDocument, table, table.rows[0].cells[0].startParagraphPosition.value - 1);
}
splitTableCellHorizontally(subDocument, table, rowIndex, cellIndex, rightDirection, copyProperties) {
let row = table.rows[rowIndex];
if (rightDirection)
this.splitTableCellToTheRightCore(subDocument, row, cellIndex, copyProperties);
else
this.splitTableCellToTheLeftCore(subDocument, row, cellIndex, copyProperties);
this.modelManipulator.notifyModelChanged(new TableCellSplittedHorizontallySubDocumentChange(subDocument.id, table, rowIndex, rightDirection ? cellIndex : (cellIndex + 1), rightDirection));
}
restoreSplittedCellHorizontally(subDocument, table, rowIndex, cellIndex, rightDirection) {
let row = table.rows[rowIndex];
let targetCell = row.cells[cellIndex];
let removingCell = rightDirection ? row.cells[cellIndex + 1] : row.cells[cellIndex - 1];
if (rightDirection) {
subDocument.positionManager.unregisterPosition(targetCell.endParagrapPosition);
targetCell.endParagrapPosition = subDocument.positionManager.registerPosition(removingCell.endParagrapPosition.value);
removingCell.destructor(subDocument.positionManager);
row.cells.splice(cellIndex + 1, 1);
}
else {
subDocument.positionManager.unregisterPosition(targetCell.startParagraphPosition);
targetCell.startParagraphPosition = subDocument.positionManager.registerPosition(removingCell.startParagraphPosition.value);
removingCell.destructor(subDocument.positionManager);
row.cells.splice(cellIndex - 1, 1);
}
this.modelManipulator.notifyModelChanged(new TableCellMergedHorizontallySubDocumentChange(subDocument.id, table, rowIndex, rightDirection ? cellIndex : (cellIndex - 1), rightDirection));
}
splitTableCellToTheLeftCore(subDocument, row, splittingCellIndex, copyProperties) {
if (splittingCellIndex < 0)
throw new Error("splittingCellIndex should be > 0");
let splittingCell = row.cells[splittingCellIndex];
let movingParagraph = subDocument.getParagraphByPosition(splittingCell.startParagraphPosition.value);
let newTableCell = new TableCell(row, copyProperties ?
splittingCell.properties : subDocument.documentModel.cache.tableCellPropertiesCache.getItem(new TableCellProperties()));
newTableCell.preferredWidth = splittingCell.preferredWidth.clone();
row.cells.splice(splittingCellIndex, 0, newTableCell);
newTableCell.startParagraphPosition = subDocument.positionManager.registerPosition(movingParagraph.startLogPosition.value);
newTableCell.endParagrapPosition = subDocument.positionManager.registerPosition(movingParagraph.getEndPosition());
subDocument.positionManager.unregisterPosition(splittingCell.startParagraphPosition);
splittingCell.startParagraphPosition = subDocument.positionManager.registerPosition(movingParagraph.getEndPosition());
}
splitTableCellToTheRightCore(subDocument, row, splittingCellIndex, copyProperties) {
if (splittingCellIndex < 0)
throw new Error("splittingCellIndex should be > 0");
let splittingCell = row.cells[splittingCellIndex];
let movingParagraph = subDocument.getParagraphByPosition(splittingCell.endParagrapPosition.value - 1);
let newTableCell = new TableCell(row, copyProperties ?
splittingCell.properties : subDocument.documentModel.cache.tableCellPropertiesCache.getItem(new TableCellProperties()));
newTableCell.preferredWidth = splittingCell.preferredWidth.clone();
row.cells.splice(splittingCellIndex + 1, 0, newTableCell);
newTableCell.startParagraphPosition = subDocument.positionManager.registerPosition(movingParagraph.startLogPosition.value);
newTableCell.endParagrapPosition = subDocument.positionManager.registerPosition(movingParagraph.getEndPosition());
subDocument.positionManager.unregisterPosition(splittingCell.endParagrapPosition);
splittingCell.endParagrapPosition = subDocument.positionManager.registerPosition(movingParagraph.startLogPosition.value);
}
setTableStyle(subDocument, tableIndex, style) {
const table = subDocument.tables[tableIndex];
table.style = style;
this.resetParagraphCharacterMergedProperties(subDocument, tableIndex);
this.modelManipulator.notifyModelChanged(new TableStyleChangedSubDocumentChange(subDocument.id, table, style));
}
removeTableWithContent(subDocument, table) {
this.history.beginTransaction();
this.removeNestedTablesByParentTable(subDocument, table);
this.history.addAndRedo(new RemoveTableHistoryItem(this.modelManipulator, subDocument, table.index));
this.history.addAndRedo(new RemoveIntervalHistoryItem(this.modelManipulator, new SubDocumentInterval(subDocument, FixedInterval.fromPositions(table.getStartPosition(), table.getEndPosition())), false));
this.history.endTransaction();
}
removeTableCellWithContent(subDocument, table, rowIndex, cellIndex) {
let cell = table.rows[rowIndex].cells[cellIndex];
this.removeNestedTablesByParentCell(subDocument, cell);
this.history.addAndRedo(new RemoveTableCellHistoryItem(this.modelManipulator, subDocument, table.index, rowIndex, cellIndex));
this.history.addAndRedo(new RemoveIntervalHistoryItem(this.modelManipulator, new SubDocumentInterval(subDocument, cell.interval), false));
}
removeTableRowWithContent(subDocument, table, rowIndex) {
let row = table.rows[rowIndex];
this.removeNestedTables(subDocument, row);
this.updateVerticalMergingState(subDocument, table, rowIndex);
this.history.addAndRedo(new RemoveTableRowHistoryItem(this.modelManipulator, subDocument, table.index, rowIndex));
this.history.addAndRedo(new RemoveIntervalHistoryItem(this.modelManipulator, new SubDocumentInterval(subDocument, FixedInterval.fromPositions(row.getStartPosition(), row.getEndPosition())), false));
}
updateVerticalMergingState(subDocument, table, rowIndex) {
let row = table.rows[rowIndex];
for (let cellIndex = row.cells.length - 1, cell; cell = row.cells[cellIndex]; cellIndex--) {
if (cell.verticalMerging !== TableCellMergingState.None) {
let columnIndex = TableCellUtils.getStartColumnIndex(cell);
let nextRow = table.rows[rowIndex + 1];
let nextRowCellIndex = nextRow ? TableCellUtils.getCellIndexByColumnIndex(nextRow, columnIndex) : -1;
let nextRowCell = nextRow ? nextRow.cells[nextRowCellIndex] : null;
if (cell.verticalMerging == TableCellMergingState.Restart) {
if (nextRowCell) {
let afterNextRow = table.rows[rowIndex + 2];
let afterNextRowCell = afterNextRow ? afterNextRow.cells[TableCellUtils.getCellIndexByEndColumnIndex(afterNextRow, columnIndex)] : null;
if (afterNextRowCell && afterNextRowCell.verticalMerging === TableCellMergingState.Continue)
this.history.addAndRedo(new TableCellVerticalMergingHistoryItem(this.modelManipulator, subDocument, table.index, rowIndex + 1, nextRowCellIndex, TableCellMergingState.Restart));
else
this.history.addAndRedo(new TableCellVerticalMergingHistoryItem(this.modelManipulator, subDocument, table.index, rowIndex + 1, nextRowCellIndex, TableCellMergingState.None));
}
}
else if (cell.verticalMerging == TableCellMergingState.Continue && rowIndex > 0) {
let prevRow = table.rows[rowIndex - 1];
let prevRowCellIndex = TableCellUtils.getCellIndexByColumnIndex(prevRow, columnIndex);
let prevRowCell = prevRow.cells[prevRowCellIndex];
if (prevRowCell && prevRowCell.verticalMerging === TableCellMergingState.Restart) {
if (!nextRowCell || nextRowCell.verticalMerging !== TableCellMergingState.Continue)
this.history.addAndRedo(new TableCellVerticalMergingHistoryItem(this.modelManipulator, subDocument, table.index, rowIndex - 1, prevRowCellIndex, TableCellMergingState.None));
}
}
}
}
}
removeNestedTables(subDocument, row) {
let nextNestedTable = subDocument.tables[row.parentTable.index + 1];
let nestedTables = [];
while (nextNestedTable && nextNestedTable.nestedLevel > row.parentTable.nestedLevel) {
if (this.isNestedTableInRow(nextNestedTable, row))
nestedTables.push(nextNestedTable);
nextNestedTable = subDocument.tables[nextNestedTable.index + 1];
}
for (let i = nestedTables.length - 1, nestedTable; nestedTable = nestedTables[i]; i--)
this.history.addAndRedo(new RemoveTableHistoryItem(this.modelManipulator, subDocument, nestedTable.index));
}
isNestedTableInRow(table, parentRow) {
return table.parentCell && (table.parentCell.parentRow === parentRow || this.isNestedTableInRow(table.parentCell.parentRow.parentTable, parentRow));
}
removeNestedTablesByParentTable(subDocument, parentTable) {
let nextNestedTable = subDocument.tables[parentTable.index + 1];
let nestedTables = [];
while (nextNestedTable && nextNestedTable.nestedLevel > parentTable.nestedLevel) {
if (this.isNestedTableInTable(nextNestedTable, parentTable))
nestedTables.push(nextNestedTable);
nextNestedTable = subDocument.tables[nextNestedTable.index + 1];
}
for (let i = nestedTables.length - 1, nestedTable; nestedTable = nestedTables[i]; i--)
this.history.addAndRedo(new RemoveTableHistoryItem(this.modelManipulator, subDocument, nestedTable.index));
}
removeNestedTablesByParentCell(subDocument, parentCell) {
let nextNestedTable = subDocument.tables[parentCell.parentRow.parentTable.index + 1];
let nestedTables = [];
while (nextNestedTable && nextNestedTable.nestedLevel > parentCell.parentRow.parentTable.nestedLevel) {
if (this.isNestedTableInCell(nextNestedTable, parentCell))
nestedTables.push(nextNestedTable);
nextNestedTable = subDocument.tables[nextNestedTable.index + 1];
}
for (let i = nestedTables.length - 1, nestedTable; nestedTable = nestedTables[i]; i--)
this.history.addAndRedo(new RemoveTableHistoryItem(this.modelManipulator, subDocument, nestedTable.index));
}
isNestedTableInCell(table, parentCell) {
return table.parentCell && (table.parentCell === parentCell || this.isNestedTableInCell(table.parentCell.parentRow.parentTable, parentCell));
}
isNestedTableInTable(table, parentTable) {
return table.parentCell && (table.parentCell.parentRow.parentTable === parentTable || this.isNestedTableInTable(table.parentCell.parentRow.parentTable, parentTable));
}
normalizeVerticalSpans(subDocument, table) {
let rowCount = table.rows.length;
if (rowCount === 1) {
let row = table.rows[0];
for (let cellIndex = 0, cell; cell = row.cells[cellIndex]; cellIndex++) {
if (cell.verticalMerging !== TableCellMergingState.None)
this.history.addAndRedo(new TableCellVerticalMergingHistoryItem(this.modelManipulator, subDocument, table.index, 0, cellIndex, TableCellMergingState.None));
}
return;
}
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
let row = table.rows[rowIndex];
let columnIndex = row.gridBefore;
for (let cellIndex = 0, cell; cell = row.cells[cellIndex]; cellIndex++) {
switch (cell.verticalMerging) {
case TableCellMergingState.Restart:
if (rowIndex == rowCount - 1)
this.history.addAndRedo(new TableCellVerticalMergingHistoryItem(this.modelManipulator, subDocument, table.index, rowIndex, cellIndex, TableCellMergingState.None));
else {
let bottomCellIndex = TableCellUtils.getCellIndexByColumnIndex(table.rows[rowIndex + 1], columnIndex);
let bottomCell = table.rows[rowIndex + 1].cells[bottomCellIndex];
if (!bottomCell || bottomCell.verticalMerging !== TableCellMergingState.Continue)
this.history.addAndRedo(new TableCellVerticalMergingHistoryItem(this.modelManipulator, subDocument, table.index, rowIndex, cellIndex, TableCellMergingState.None));
}
break;
case TableCellMergingState.Continue:
if (rowIndex === 0)
this.history.addAndRedo(new TableCellVerticalMergingHistoryItem(this.modelManipulator, subDocument, table.index, rowIndex, cellIndex, TableCellMergingState.None));
else {
let topCellIndex = TableCellUtils.getCellIndexByColumnIndex(table.rows[rowIndex - 1], columnIndex);
let topCell = table.rows[rowIndex - 1].cells[topCellIndex];
if (!topCell || topCell.verticalMerging === TableCellMergingState.None) {
if (rowIndex == rowCount - 1)
this.history.addAndRedo(new TableCellVerticalMergingHistoryItem(this.modelManipulator, subDocument, table.index, rowIndex, cellIndex, TableCellMergingState.None));
else {
let bottomCellIndex = TableCellUtils.getCellIndexByColumnIndex(table.rows[rowIndex + 1], columnIndex);
let bottomCell = table.rows[rowIndex + 1].cells[bottomCellIndex];
if (bottomCell && bottomCell.verticalMerging == TableCellMergingState.Continue)
this.history.addAndRedo(new TableCellVerticalMergingHistoryItem(this.modelManipulator, subDocument, table.index, rowIndex, cellIndex, TableCellMergingState.Restart));
else
this.history.addAndRedo(new TableCellVerticalMergingHistoryItem(this.modelManipulator, subDocument, table.index, rowIndex, cellIndex, TableCellMergingState.None));
}
}
}
break;
}
columnIndex += cell.columnSpan;
}
}
}
normalizeTableGrid(subDocument, table) {
var maxEndColumnIndex = -1;
for (let i = 0, row; row = table.rows[i]; i++) {
maxEndColumnIndex = Math.max(maxEndColumnIndex, row.getTotalCellsInRowConsiderGrid() - 1);
}
for (let i = 0, row; row = table.rows[i]; i++) {
let currentEndColumnIndex = row.getTotalCellsInRowConsiderGrid() - 1;
let gridAfterDelta = maxEndColumnIndex - currentEndColumnIndex;
if (gridAfterDelta != 0)
this.history.addAndRedo(new TableRowGridAfterHistoryItem(this.modelManipulator, subDocument, table.index, i, row.gridAfter + gridAfterDelta));
}
}
normalizeCellColumnSpans(subDocument, table, canNormalizeWidthBeforeAndWidthAfter) {
TablesManipulator.normalizeRowsGridBefore(table, canNormalizeWidthBeforeAndWidthAfter, (t, ri, val) => this.history.addAndRedo(new TableRowGridBeforeHistoryItem(this.modelManipulator, subDocument, t.index, ri, val)), (t, ri, val) => this.history.addAndRedo(new TableRowGridAfterHistoryItem(this.modelManipulator, subDocument, t.index, ri, val)), (t, ri, val) => this.history.addAndRedo(new TableRowWidthBeforeHistoryItem(this.modelManipulator, subDocument, t.index, ri, val)), (t, ri, val) => this.history.addAndRedo(new TableRowWidthAfterHistoryItem(this.modelManipulator, subDocument, t.index, ri, val)));
const intervals = Calculator.getIntervals(table);
for (let i = table.rows.length - 1; i >= 0; i--) {
TablesManipulator.normalizeTableRow(table, i, intervals.slice(0), (t, ri, val) => this.history.addAndRedo(new TableRowGridBeforeHistoryItem(this.modelManipulator, subDocument, t.index, ri, val)), (t, ri, val) => this.history.addAndRedo(new TableRowGridAfterHistoryItem(this.modelManipulator, subDocument, t.index, ri, val)), (t, ri, ci, val) => this.history.addAndRedo(new TableCellColumnSpanHistoryItem(this.modelManipulator, subDocument, t.index, ri, ci, val)));
}
}
static normalizeCellColumnSpansWithoutHistory(table, canNormalizeWidthBeforeAndWidthAfter) {
TablesManipulator.normalizeRowsGridBefore(table, canNormalizeWidthBeforeAndWidthAfter, (t, ri, val) => t.rows[ri].gridBefore = val, (t, ri, val) => t.rows[ri].gridAfter = val, (t, ri, val) => t.rows[ri].widthBefore = val, (t, ri, val) => t.rows[ri].widthAfter = val);
const intervals = Calculator.getIntervals(table);
for (let i = table.rows.length - 1; i >= 0; i--) {
TablesManipulator.normalizeTableRow(table, i, intervals.slice(0), (t, ri, val) => t.rows[ri].gridBefore = val, (t, ri, val) => t.rows[ri].gridAfter = val, (t, ri, ci, val) => t.rows[ri].cells[ci].columnSpan = val);
}
}
static normalizeTableRow(table, rowIndex, intervals, setGridBefore, setGridAfter, setColumnSpan) {
let row = table.rows[rowIndex];
let span = TablesManipulator.calculateNewSpan(row.gridBefore, intervals);
if (row.gridBefore != span)
setGridBefore(table, rowIndex, span);
for (let i = 0, cell; cell = row.cells[i]; i++) {
span = TablesManipulator.calculateNewSpan(cell.columnSpan, intervals);
if (cell.columnSpan !== span)
setColumnSpan(table, rowIndex, i, span);
}
span = TablesManipulator.calculateNewSpan(row.gridAfter, intervals);
if (row.gridAfter !== span)
setGridAfter(table, rowIndex, span);
}
static calculateNewSpan(oldSpan, intervals) {
let result = 0;
let totalSum = 0;
while (totalSum < oldSpan) {
totalSum += intervals[0].colSpan;
result++;
intervals.splice(0, 1);
}
return result;
}
static normalizeRowsGridBefore(table, canNormalizeWidthBeforeAndWidthAfter, setGridBefore, setGridAfter, setWidthBefore, setWidthAfter) {
let minGridBefore = table.rows[0].gridBefore;
let minGridAfter = table.rows[0].gridAfter;
for (let i = 1, row; row = table.rows[i]; i++) {
minGridBefore = Math.min(minGridBefore, row.gridBefore);
minGridAfter = Math.min(minGridAfter, row.gridAfter);
}
if (minGridBefore == 0 && minGridAfter == 0)
return;
for (let i = 0, row; row = table.rows[i]; i++) {
if (minGridBefore !== 0)
setGridBefore(table, i, row.gridBefore - minGridBefore);
if (row.gridBefore === 0 && (row.widthBefore.type != TableWidthUnitType.Nil || row.widthBefore.value != 0) && canNormalizeWidthBeforeAndWidthAfter)
setWidthBefore(table, i, TableWidthUnit.createDefault());
if (minGridAfter != 0)
setGridAfter(table, i, row.gridAfter - minGridAfter);
if (row.gridAfter == 0 && (row.widthAfter.type != TableWidthUnitType.Nil || row.widthAfter.value != 0) && canNormalizeWidthBeforeAndWidthAfter)
setWidthAfter(table, i, TableWidthUnit.createDefault());
}
}
normalizeTableCellWidth(subDocument, table) {
const maxWidth = 5000;
for (let i = 0, row; row = table.rows[i]; i++) {
let totalWidth = 0;
let tableCellPreferredWidths = [];
for (let j = 0, cell; cell = row.cells[j]; j++) {
let tableCellPreferredWidth = cell.preferredWidth;
tableCellPreferredWidths.push(tableCellPreferredWidth);
if (tableCellPreferredWidth.type === TableWidthUnitType.FiftiethsOfPercent)
totalWidth += tableCellPreferredWidth.value;
else {
totalWidth = 0;
break;
}
}
if (totalWidth <= maxWidth)
continue;
for (let j = 0; j < row.cells.length; j++) {
let newValue = tableCellPreferredWidths[j].value * maxWidth / totalWidth;
let newWidth = tableCellPreferredWidths[j].clone();
newWidth.value = newValue;
this.history.addAndRedo(new TableCellPreferredWidthHistoryItem(this.modelManipulator, subDocument, table.index, i, j, newWidth));
}
}
}
createTableStructure(subDocument, paragraphIndex, newTable, rowCount, cellCount) {
let paragraph = subDocument.paragraphs[paragraphIndex];
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
let row = new TableRow(newTable, subDocument.documentModel.cache.tableRowPropertiesCache.getItem(new TableRowProperties()));
newTable.rows.push(row);
for (let cellIndex = 0; cellIndex < cellCount; cellIndex++) {
let cell = new TableCell(row, subDocument.documentModel.cache.tableCellPropertiesCache.getItem(new TableCellProperties()));
row.cells.push(cell);
cell.startParagraphPosition = subDocument.positionManager.registerPosition(paragraph.startLogPosition.value);
cell.endParagrapPosition = subDocument.positionManager.registerPosition(paragraph.getEndPosition());
paragraph = subDocument.paragraphs[++paragraphIndex];
}
}
}
fullCellWidth(widths, startSpan, cellSpan) {
return ListUtils.accumulate(widths, 0, (acc, w) => acc + w, startSpan, startSpan + cellSpan);
}
initializeColumnWidthsWhenInsertTable(subDocument, table, avaliableSpace) {
this.history.addAndRedo(new TablePreferredWidthHistoryItem(this.modelManipulator, subDocument, table.index, TableWidthUnit.create(0, TableWidthUnitType.Auto)));
const widths = this.distributeWidthsToAllColumns(table, avaliableSpace);
this.forEachCell(table, (pos, cellSpan) => {
this.history.addAndRedo(new TableCellPreferredWidthHistoryItem(this.modelManipulator, subDocument, table.index, pos.rowIndex, pos.cellIndex, TableWidthUnit.create(this.fullCellWidth(widths, cellSpan, pos.cell.columnSpan), TableWidthUnitType.ModelUnits)));
});
}
validateTableIndent(subDocument, table) {
let paragraphIndex = subDocument.getParagraphIndexByPosition(table.getStartPosition());
let paragraph = subDocument.paragraphs[paragraphIndex];
let leftIndent = paragraph.getParagraphMergedProperties().leftIndent;
let firstLineIndent = paragraph.getParagraphMergedProperties().firstLineIndent;
let firstLineIndentType = paragraph.getParagraphMergedProperties().firstLineIndentType;
if (leftIndent === 0 && firstLineIndent === 0 && firstLineIndentType === ParagraphFirstLineIndent.None)
return;
let endParagraphIndex = subDocument.getParagraphIndexByPosition(table.getEndPosition() - 1);
for (; paragraphIndex <= endParagraphIndex; paragraphIndex++) {
paragraph = subDocument.paragraphs[paragraphIndex];
this.history.addAndRedo(new ParagraphLeftIndentHistoryItem(this.modelManipulator, new SubDocumentInterval(subDocument, paragraph.interval), 0, true));
this.history.addAndRedo(new ParagraphFirstLineIndentHistoryItem(this.modelManipulator, new SubDocumentInterval(subDocument, paragraph.interval), 0, true));
this.history.addAndRedo(new ParagraphFirstLineIndentTypeHistoryItem(this.modelManipulator, new SubDocumentInterval(subDocument, paragraph.interval), ParagraphFirstLineIndent.None, true));
}
}
tryJoinTables(subDocument, table) {
let paragraphIndex = subDocument.getParagraphIndexByPosition(table.getStartPosition());
if (paragraphIndex === 0)
return;
let previousParagraph = subDocument.paragraphs[paragraphIndex - 1];
let previousParagraphCell = Table.getTableCellByPosition(subDocument.tables, previousParagraph.startLogPosition.value);
if (!previousParagraphCell || table.nestedLevel !== previousParagraphCell.parentRow.parentTable.nestedLevel)
return;
this.joinTablesCore(subDocument, [previousParagraphCell.parentRow.parentTable, table]);
}
joinTablesCore(_subDocument, tables) {
if (tables.length < 2)
throw new Error("tables.length should be > 2");
}
forEachCell(table, func) {
const pos = new TablePosition(table, -1, -1);
while (pos.moveToNextRow()) {
let cellSpan = pos.row.gridBefore;
while (pos.moveToNextCell()) {
func(pos, cellSpan);
cellSpan += pos.cell.columnSpan;
}
}
}
distributeWidthsToAllColumns(table, width) {
const count = table.getTotalVirtualColumnsCount();
const colWidth = Math.max(Math.ceil(width / count), 1);
const result = ListUtils.initByValue(count - 1, colWidth);
result.push(Math.max(1, Math.ceil(width - colWidth * (count - 1))));
return result;
}
insertTableCore(table, tables, tablesByLevels, parentCell) {
table.nestedLevel = parentCell ? parentCell.parentRow.parentTable.nestedLevel + 1 : 0;
table.parentCell = parentCell;
let position = table.getStartPosition();
if (!tablesByLevels[table.nestedLevel])
tablesByLevels[table.nestedLevel] = [];
let indexInNestedLevel = SearchUtils.normedInterpolationIndexOf(tablesByLevels[table.nestedLevel], t => t.getStartPosition(), position);
tablesByLevels[table.nestedLevel].splice(indexInNestedLevel + 1, 0, table);
if (!parentCell)
table.index = Math.max(0, SearchUtils.normedInterpolationIndexOf(tables, t => t.getStartPosition(), position) + 1);
else {
let parentTable = parentCell.parentRow.parentTable;
let index = parentTable.index + 1;
for (let nextTable; nextTable = tables[index]; index++) {
if (nextTable.getStartPosition() >= position)
break;
}
table.index = index;
}
tables.splice(table.index, 0, table);
Table.advanceIndices(tables, table.index + 1, 1);
}
normalizeVerticalMerging(subDocument, table, rowIndex, cellIndex) {
let row = table.rows[rowIndex];
for (let nextCellIndex = cellIndex + 1, nextCell; nextCell = row.cells[nextCellIndex]; nextCellIndex++) {
if (nextCell.verticalMerging === TableCellMergingState.None)
continue;
let nextCellVerticalMerging = nextCell.verticalMerging;
let firstCellInMergingGroupPosition = Table.getFirstCellPositionInVerticalMergingGroup(TablePosition.createAndInit(table, rowIndex, nextCellIndex).init());
let startColumnIndex = TableCellUtils.getStartColumnIndex(firstCellInMergingGroupPosition.cell);
let verticalSpanCellPositions = TableCellUtils.getVerticalSpanCellPositions(firstCellInMergingGroupPosition, startColumnIndex);
this.history.addAndRedo(new TableCellVerticalMergingHistoryItem(this.modelManipulator, subDocument, table.index, rowIndex, nextCellIndex, TableCellMergingState.None));
if (nextCellVerticalMerging === TableCellMergingState.Restart) {
if (verticalSpanCellPositions.length > 2)
this.history.addAndRedo(TableCellVerticalMergingHistoryItem.fromPosition(this.modelManipulator, subDocument, verticalSpanCellPositions[1], TableCellMergingState.Restart));
else {
this.history.addAndRedo(TableCellVerticalMergingHistoryItem.fromPosition(this.modelManipulator, subDocument, verticalSpanCellPositions[0], TableCellMergingState.None));
this.history.addAndRedo(TableCellVerticalMergingHistoryItem.fromPosition(this.modelManipulator, subDocument, verticalSpanCellPositions[1], TableCellMergingState.None));
}
}
else {
let nextCellIndexInMergingGroup = TablePosition.indexOfCell(verticalSpanCellPositions, nextCell);
if (nextCellIndexInMergingGroup === 1)
this.history.addAndRedo(TableCellVerticalMergingHistoryItem.fromPosition(this.modelManipulator, subDocument, verticalSpanCellPositions[0], TableCellMergingState.None));
if (verticalSpanCellPositions.length - 2 === nextCellIndexInMergingGroup)
this.history.addAndRedo(TableCellVerticalMergingHistoryItem.fromPosition(this.modelManipulator, subDocument, verticalSpanCellPositions[verticalSpanCellPositions.length - 1], TableCellMergingState.None));
else if (verticalSpanCellPositions.length - 1 !== nextCellIndexInMergingGroup)
this.history.addAndRedo(TableCellVerticalMergingHistoryItem.fromPosition(this.modelManipulator, subDocument, verticalSpanCellPositions[nextCellIndexInMergingGroup + 1], TableCellMergingState.Restart));
}
}
}
normalizeRows(subDocument, table) {
for (let i = table.rows.length - 1, row; row = table.rows[i]; i--) {
if (this.areAllCellsHasVerticalMerge(row)) {
let height = row.height;
if (height.type !== TableHeightUnitType.Auto && i > 0) {
let prevRowHeight = table.rows[i - 1].height;
this.history.addAndRedo(new TableRowHeightHistoryItem(this.modelManipulator, subDocument, table.index, i - 1, TableHeightUnit.create(prevRowHeight.value + height.value, height.type)));
}
this.removeTableRowWithContent(subDocument, table, i);
}
}
}
areAllCellsHasVerticalMerge(row) {
for (let i = 0, cell; cell = row.cells[i]; i++) {
if (cell.verticalMerging !== TableCellMergingState.Continue)
return false;
}
return true;
}
mergeSelectedTableCellsHorizontally(subDocument, tableInfo, inputPosition) {
tableInfo.extendedData.rows.forEach(rowInfo => {
const modelCellInterval = new BoundaryInterval(rowInfo.cells[0].cellIndex, ListUtils.last(rowInfo.cells).cellIndex + 1);
for (let modelCellInd = modelCellInterval.end - 2; modelCellInd >= modelCellInterval.start; modelCellInd--) {
const tablePosition = new TablePosition(tableInfo.table, rowInfo.rowIndex, modelCellInd).init();
this.modelManipulator.table.mergeTwoTableCellsHorizontally(subDocument, tablePosition, inputPosition);
}
});
}
mergeSelectedTableCellsVertically(subDocument, tableInfo, inputPosition) {
ListUtils.reverseForEach(tableInfo.extendedData.rows, (rowInfo) => {
const tablePosition = new TablePosition(tableInfo.table, rowInfo.rowIndex, rowInfo.cells[0].cellIndex).init();
this.modelManipulator.table.mergeTwoTableCellsVertically(subDocument, tablePosition, inputPosition);
}, tableInfo.extendedData.numRows - 2);
}
mergeTwoTableCellsHorizontally(subDocument, cellPosition, inpPos) {
new MergeTwoTableCellsHorizontallyOperation(this.modelManipulator.modelManager, subDocument).execute(cellPosition, true, inpPos);
}
mergeTwoTableCellsVertically(subDocument, cellPosition, inpPos) {
new MergeTwoTableCel