devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
306 lines (305 loc) • 14.3 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 { TableAlignmentApplier } from '../../../layout-formatter/table/table-alignment-applier';
import { TablePropertiesMergerBorderBottom, TablePropertiesMergerBorderHorizontal, TablePropertiesMergerBorderLeft, TablePropertiesMergerBorderRight, TablePropertiesMergerBorderTop, TablePropertiesMergerBorderVertical, TablePropertiesMergerIndent, TablePropertiesMergerLayoutType, TablePropertiesMergerMarginBottom, TablePropertiesMergerMarginLeft, TablePropertiesMergerMarginRight, TablePropertiesMergerMarginTop, TablePropertiesMergerShadingInfo } from '../properties-mergers/table-properties-merger';
import { ConditionalTableStyleFormatting, TableCellMargins, TableCellMergingState, TableLookTypes, TableRowAlignment, } from '../secondary-structures/table-base-structures';
import { TableWidthUnit } from '../secondary-structures/table-units';
import { TableBorders } from '../../borders/table-borders';
export class Table {
constructor(properties, style) {
this.rows = [];
this.preferredWidth = TableWidthUnit.createDefault();
this.lookTypes = TableLookTypes.None;
this.properties = properties;
this.style = style;
}
getTotalVirtualColumnsCount() {
return ListUtils.maxExtended(this.rows, row => row.getTotalCellsInRowConsiderGrid()).maxValue;
}
destructor(positionManager) {
for (var rowIndex = 0, tableRow; tableRow = this.rows[rowIndex]; rowIndex++)
tableRow.destructor(positionManager);
}
get interval() { return FixedInterval.fromPositions(this.getStartPosition(), this.getEndPosition()); }
getParentTable() {
const parentCell = this.parentCell;
return parentCell ? this.parentCell.parentRow.parentTable : null;
}
getTopLevelParent() {
let currTable = this;
while (currTable.parentCell)
currTable = currTable.getParentTable();
return currTable;
}
getStartPosition() {
return this.rows[0].getStartPosition();
}
getEndPosition() {
return this.rows[this.rows.length - 1].getEndPosition();
}
getLastCell() {
let lastRow = this.rows[this.rows.length - 1];
return lastRow.cells[lastRow.cells.length - 1];
}
getFirstCell() {
return this.rows[0].cells[0];
}
getActualLeftBorder(defaultTableProperties, isTableOuterBorder = true) {
return this.getActualBorderCore(new TablePropertiesMergerBorderLeft(isTableOuterBorder), defaultTableProperties);
}
getActualRightBorder(defaultTableProperties, isTableOuterBorder = true) {
return this.getActualBorderCore(new TablePropertiesMergerBorderRight(isTableOuterBorder), defaultTableProperties);
}
getActualBottomBorder(defaultTableProperties, isTableOuterBorder = true) {
return this.getActualBorderCore(new TablePropertiesMergerBorderBottom(isTableOuterBorder), defaultTableProperties);
}
getActualTopBorder(defaultTableProperties, isTableOuterBorder = true) {
return this.getActualBorderCore(new TablePropertiesMergerBorderTop(isTableOuterBorder), defaultTableProperties);
}
getActualHorizontalBorder(defaultTableProperties) {
return this.getActualBorderCore(new TablePropertiesMergerBorderHorizontal(), defaultTableProperties);
}
getActualVerticalBorder(defaultTableProperties) {
return this.getActualBorderCore(new TablePropertiesMergerBorderVertical(), defaultTableProperties);
}
getActualBorders(defaultTableProperties) {
const top = this.getActualTopBorder(defaultTableProperties);
const right = this.getActualRightBorder(defaultTableProperties);
const bottom = this.getActualBottomBorder(defaultTableProperties);
const left = this.getActualLeftBorder(defaultTableProperties);
const insideHorizontal = this.getActualHorizontalBorder(defaultTableProperties);
const insideVertical = this.getActualVerticalBorder(defaultTableProperties);
return TableBorders.create(top, right, bottom, left, insideHorizontal, insideVertical);
}
getActualBorderCore(tablePropertiesMerger, defaultTableProperties) {
return tablePropertiesMerger.getProperty(this.properties, this.style, ConditionalTableStyleFormatting.WholeTable, defaultTableProperties);
}
getActualLeftMargin(defaultTableProperties) {
return this.getActualMarginCore(new TablePropertiesMergerMarginLeft(), defaultTableProperties);
}
getActualRightMargin(defaultTableProperties) {
return this.getActualMarginCore(new TablePropertiesMergerMarginRight(), defaultTableProperties);
}
getActualTopMargin(defaultTableProperties) {
return this.getActualMarginCore(new TablePropertiesMergerMarginTop(), defaultTableProperties);
}
getActualBottomMargin(defaultTableProperties) {
return this.getActualMarginCore(new TablePropertiesMergerMarginBottom(), defaultTableProperties);
}
getActualMarginCore(merger, defaultTableProperties) {
return merger.getProperty(this.properties, this.style, ConditionalTableStyleFormatting.WholeTable, defaultTableProperties);
}
getActualMargins(defaultTableProperties) {
const top = this.getActualTopMargin(defaultTableProperties);
const right = this.getActualRightMargin(defaultTableProperties);
const bottom = this.getActualBottomMargin(defaultTableProperties);
const left = this.getActualLeftMargin(defaultTableProperties);
return TableCellMargins.create(top, right, bottom, left);
}
getActualTableAlignment() {
return TableAlignmentApplier.getTableAlignment(this);
}
getActualTableIndent(defaultTableProperties) {
const aligment = this.getActualTableAlignment();
if (aligment != TableRowAlignment.Left)
return TableWidthUnit.createDefault();
return new TablePropertiesMergerIndent().getProperty(this.properties, this.style, ConditionalTableStyleFormatting.WholeTable, defaultTableProperties);
}
getActualTableLayout(defaultTableProperties) {
return new TablePropertiesMergerLayoutType().getProperty(this.properties, this.style, ConditionalTableStyleFormatting.WholeTable, defaultTableProperties);
}
getActualShadingInfo(defaultTableProperties) {
return new TablePropertiesMergerShadingInfo().getProperty(this.properties, this.style, ConditionalTableStyleFormatting.WholeTable, defaultTableProperties);
}
static comparer(a, b) {
const cmpPos = a.getStartPosition() - b.getStartPosition();
return cmpPos == 0 ? a.nestedLevel - b.nestedLevel : cmpPos;
}
static sort(tables) {
tables.sort(Table.comparer);
for (let tableIndex = 0, table; table = tables[tableIndex]; tableIndex++)
table.index = tableIndex;
}
static advanceIndices(tables, startIndex, shift) {
for (let i = startIndex, table; table = tables[i]; i++)
table.index += shift;
}
static fillTableByLevels(subDocument) {
const tableByLevels = subDocument.tablesByLevels;
let tableByLevelsLength = tableByLevels.length;
for (let tableIndex = 0, table; table = subDocument.tables[tableIndex]; tableIndex++) {
table.index = tableIndex;
if (table.nestedLevel >= tableByLevelsLength) {
tableByLevels.push([]);
tableByLevelsLength++;
}
tableByLevels[table.nestedLevel].push(table);
}
}
static getTableCellByPosition(tables, position) {
const table = Table.getTableByPosition(tables, position, true);
return table ? Table.getTableCellByPositionFromTable(table, position) : null;
}
static getTableCellByPositionFromTable(table, position) {
const rowIndex = SearchUtils.normedInterpolationIndexOf(table.rows, r => r.getStartPosition(), position);
const row = table.rows[rowIndex];
const cellIndex = SearchUtils.normedInterpolationIndexOf(row.cells, c => c.startParagraphPosition.value, position);
return row.cells[cellIndex];
}
static getTableByPosition(tables, position, maxNestedLevel, tableIndex = SearchUtils.normedInterpolationIndexOf(tables, (t) => t.getStartPosition(), position)) {
if (tableIndex < 0)
return null;
let table = tables[tableIndex];
while (position >= table.getEndPosition()) {
if (table.nestedLevel === 0)
return null;
table = table.getParentTable();
}
return Table.correctBoundTable(tables, table.index, position, maxNestedLevel ? (index) => ++index : (index) => --index);
}
static correctBoundTable(tables, tableIndex, position, indexIterator) {
let table = tables[tableIndex];
let tablePos = table.getStartPosition();
tableIndex = indexIterator(tableIndex);
for (let neighborTable; neighborTable = tables[tableIndex]; tableIndex = indexIterator(tableIndex)) {
const neighborTablePos = neighborTable.getStartPosition();
if (tablePos != neighborTablePos || position >= neighborTable.getEndPosition())
break;
tablePos = neighborTablePos;
table = neighborTable;
}
return table;
}
static getFirstCellPositionInVerticalMergingGroup(tablePosition) {
if (tablePosition.cell.verticalMerging != TableCellMergingState.Continue)
return tablePosition;
const tblPos = tablePosition.clone().init();
const cellColumnIndex = tblPos.row.getCellColumnIndex(tblPos.cellIndex);
while (tblPos.moveToPrevRow()) {
let columnIndex = tblPos.row.gridBefore;
tblPos.setCell(0);
do {
if (cellColumnIndex <= columnIndex) {
if (tblPos.cell.verticalMerging != TableCellMergingState.Continue || tblPos.rowIndex == 0)
return tblPos;
else
break;
}
columnIndex += tblPos.cell.columnSpan;
} while (tblPos.moveToNextCell());
}
return null;
}
clone(subDocument) {
const result = new Table(this.properties.clone(), subDocument.documentModel.stylesManager.getTableStyleByName(this.style.styleName));
result.index = this.index;
result.nestedLevel = this.nestedLevel;
const tablesOnLevel = subDocument.tablesByLevels[result.nestedLevel];
if (!tablesOnLevel)
subDocument.tablesByLevels.push([result]);
else
tablesOnLevel.push(result);
if (this.parentCell)
result.parentCell = Table.getTableCellByPositionFromTable(subDocument.tables[this.parentCell.parentRow.parentTable.index], this.parentCell.startParagraphPosition.value);
result.rows = ListUtils.map(this.rows, r => r.clone(subDocument, result));
result.preferredWidth = this.preferredWidth.clone();
result.lookTypes = this.lookTypes;
return result;
}
}
export class TablePositionIndexes {
constructor(rowIndex, cellIndex) {
this.rowIndex = rowIndex;
this.cellIndex = cellIndex;
}
equals(obj) {
return obj &&
this.cellIndex == obj.cellIndex &&
this.rowIndex == obj.rowIndex;
}
copyFrom(obj) {
this.rowIndex = obj.rowIndex;
this.cellIndex = obj.cellIndex;
}
clone() {
return new TablePositionIndexes(this.rowIndex, this.cellIndex);
}
}
export class TablePosition extends TablePositionIndexes {
constructor(table, rowIndex, cellIndex) {
super(rowIndex, cellIndex);
this.table = table;
}
initIndexes(rowIndex, cellIndex) {
this.rowIndex = rowIndex;
this.cellIndex = cellIndex;
return this;
}
init() {
this.row = this.table.rows[this.rowIndex];
this.cell = this.row.cells[this.cellIndex];
return this;
}
setRow(rowIndex) {
this.rowIndex = rowIndex;
this.row = this.table.rows[this.rowIndex];
return this;
}
setCell(cellIndex) {
this.cellIndex = cellIndex;
this.cell = this.row.cells[cellIndex];
}
static createAndInit(table, rowIndex, cellIndex) {
const position = new TablePosition(table, rowIndex, cellIndex);
position.init();
return position;
}
static indexOfCell(positions, cell) {
for (let i = 0, pos; pos = positions[i]; i++) {
if (pos.cell === cell)
return i;
}
return -1;
}
moveToPrevRow() {
if (!this.rowIndex)
return false;
this.rowIndex--;
this.row = this.table.rows[this.rowIndex];
return true;
}
moveToNextRow() {
if (this.rowIndex == this.table.rows.length - 1)
return false;
this.rowIndex++;
this.row = this.table.rows[this.rowIndex];
this.cellIndex = -1;
return true;
}
moveToNextCell() {
if (this.cellIndex == this.row.cells.length - 1)
return false;
this.cellIndex++;
this.cell = this.row.cells[this.cellIndex];
return true;
}
copyFrom(obj) {
super.copyFrom(obj);
this.table = obj.table;
this.row = obj.row;
this.cell = obj.cell;
}
clone() {
const pos = new TablePosition(this.table, this.rowIndex, this.cellIndex);
pos.row = this.row;
pos.cell = this.cell;
return pos;
}
equals(obj) {
return obj &&
this.table == obj.table &&
this.rowIndex == obj.rowIndex &&
this.cellIndex == obj.cellIndex;
}
}