gecko-dev/accessible/tests/mochitest/table.js

1034 строки
26 KiB
JavaScript

/**
* This file provides set of helper functions to test nsIAccessibleTable
* interface.
*
* Required:
* common.js
* role.js
* states.js
*/
/* import-globals-from common.js */
/* import-globals-from role.js */
/* import-globals-from states.js */
/**
* Constants used to describe cells array.
*/
const kDataCell = 1; // Indicates the cell is origin data cell
const kRowHeaderCell = 2; // Indicates the cell is row header cell
const kColHeaderCell = 4; // Indicated the cell is column header cell
const kOrigin = kDataCell | kRowHeaderCell | kColHeaderCell;
const kRowSpanned = 8; // Indicates the cell is not origin and row spanned
const kColSpanned = 16; // Indicates the cell is not origin and column spanned
const kSpanned = kRowSpanned | kColSpanned;
/**
* Constants to define column header type.
*/
const kNoColumnHeader = 0;
const kListboxColumnHeader = 1;
const kTreeColumnHeader = 2;
/**
* Constants to define table type.
*/
const kTable = 0;
const kTreeTable = 1;
const kMathTable = 2;
/**
* Test table structure and related methods.
*
* @param aIdentifier [in] table accessible identifier
* @param aCellsArray [in] two dimensional array (row X columns) of
* cell types (see constants defined above).
* @param aColHeaderType [in] specifies wether column header cells are
* arranged into the list.
* @param aCaption [in] caption text if any
* @param aSummary [in] summary text if any
* @param aTableType [in] specifies the table type.
* @param aRowRoles [in] array of row roles.
*/
function testTableStruct(
aIdentifier,
aCellsArray,
aColHeaderType,
aCaption,
aSummary,
aTableType,
aRowRoles
) {
var tableNode = getNode(aIdentifier);
var isGrid =
tableNode.getAttribute("role") == "grid" ||
tableNode.getAttribute("role") == "treegrid" ||
tableNode.localName == "tree";
var rowCount = aCellsArray.length;
var colsCount = aCellsArray[0] ? aCellsArray[0].length : 0;
// Test table accessible tree.
var tableObj = {
children: [],
};
switch (aTableType) {
case kTable:
tableObj.role = ROLE_TABLE;
break;
case kTreeTable:
tableObj.role = ROLE_TREE_TABLE;
break;
case kMathTable:
tableObj.role = ROLE_MATHML_TABLE;
break;
}
// caption accessible handling
if (aCaption) {
var captionObj = {
role: ROLE_CAPTION,
children: [
{
role: ROLE_TEXT_LEAF,
name: aCaption,
},
],
};
tableObj.children.push(captionObj);
}
// special types of column headers handling
if (aColHeaderType) {
var headersObj = {
role: ROLE_LIST,
children: [],
};
for (let idx = 0; idx < colsCount; idx++) {
var headerCellObj = {
role: ROLE_COLUMNHEADER,
};
headersObj.children.push(headerCellObj);
}
if (aColHeaderType == kTreeColumnHeader) {
var columnPickerObj = {
role: ROLE_PUSHBUTTON,
};
headersObj.children.push(columnPickerObj);
}
tableObj.children.push(headersObj);
}
// rows and cells accessibles
for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) {
let rowObj = {
role: aRowRoles ? aRowRoles[rowIdx] : ROLE_ROW,
children: [],
};
for (let colIdx = 0; colIdx < colsCount; colIdx++) {
let celltype = aCellsArray[rowIdx][colIdx];
var role = ROLE_NOTHING;
switch (celltype) {
case kDataCell:
role =
aTableType == kMathTable
? ROLE_MATHML_CELL
: isGrid
? ROLE_GRID_CELL
: ROLE_CELL;
break;
case kRowHeaderCell:
role = ROLE_ROWHEADER;
break;
case kColHeaderCell:
role = ROLE_COLUMNHEADER;
break;
}
if (role != ROLE_NOTHING) {
var cellObj = { role };
rowObj.children.push(cellObj);
}
}
tableObj.children.push(rowObj);
}
testAccessibleTree(aIdentifier, tableObj);
// Test table table interface.
var table = getAccessible(aIdentifier, [nsIAccessibleTable]);
// summary
if (aSummary) {
is(
table.summary,
aSummary,
"Wrong summary of the table " + prettyName(aIdentifier)
);
}
// rowCount and columnCount
is(
table.rowCount,
rowCount,
"Wrong rows count of " + prettyName(aIdentifier)
);
is(
table.columnCount,
colsCount,
"Wrong columns count of " + prettyName(aIdentifier)
);
// rows and columns extents
for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) {
for (let colIdx = 0; colIdx < colsCount; colIdx++) {
let celltype = aCellsArray[rowIdx][colIdx];
if (celltype & kOrigin) {
// table getRowExtentAt
var rowExtent = table.getRowExtentAt(rowIdx, colIdx);
let idx;
/* eslint-disable no-empty */
for (
idx = rowIdx + 1;
idx < rowCount && aCellsArray[idx][colIdx] & kRowSpanned;
idx++
) {}
/* eslint-enable no-empty */
var expectedRowExtent = idx - rowIdx;
is(
rowExtent,
expectedRowExtent,
"getRowExtentAt: Wrong number of spanned rows at (" +
rowIdx +
", " +
colIdx +
") for " +
prettyName(aIdentifier)
);
// table getColumnExtentAt
var colExtent = table.getColumnExtentAt(rowIdx, colIdx);
/* eslint-disable no-empty */
for (
idx = colIdx + 1;
idx < colsCount && aCellsArray[rowIdx][idx] & kColSpanned;
idx++
) {}
/* eslint-enable no-empty */
var expectedColExtent = idx - colIdx;
is(
colExtent,
expectedColExtent,
"getColumnExtentAt: Wrong number of spanned columns at (" +
rowIdx +
", " +
colIdx +
") for " +
prettyName(aIdentifier)
);
// cell rowExtent and columnExtent
var cell = getAccessible(table.getCellAt(rowIdx, colIdx), [
nsIAccessibleTableCell,
]);
is(
cell.rowExtent,
expectedRowExtent,
"rowExtent: Wrong number of spanned rows at (" +
rowIdx +
", " +
colIdx +
") for " +
prettyName(aIdentifier)
);
is(
cell.columnExtent,
expectedColExtent,
"columnExtent: Wrong number of spanned column at (" +
rowIdx +
", " +
colIdx +
") for " +
prettyName(aIdentifier)
);
}
}
}
}
/**
* Test table indexes.
*
* @param aIdentifier [in] table accessible identifier
* @param aIdxes [in] two dimensional array of cell indexes
*/
function testTableIndexes(aIdentifier, aIdxes) {
var tableAcc = getAccessible(aIdentifier, [nsIAccessibleTable]);
if (!tableAcc) {
return;
}
var obtainedRowIdx, obtainedColIdx, obtainedIdx;
var cellAcc;
var id = prettyName(aIdentifier);
var rowCount = aIdxes.length;
for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
var colCount = aIdxes[rowIdx].length;
for (var colIdx = 0; colIdx < colCount; colIdx++) {
var idx = aIdxes[rowIdx][colIdx];
// getCellAt
try {
cellAcc = null;
cellAcc = tableAcc.getCellAt(rowIdx, colIdx);
} catch (e) {}
ok(
(idx != -1 && cellAcc) || (idx == -1 && !cellAcc),
id +
": Can't get cell accessible at row = " +
rowIdx +
", column = " +
colIdx
);
if (idx != -1) {
// getRowIndexAt
var origRowIdx = rowIdx;
while (
origRowIdx > 0 &&
aIdxes[rowIdx][colIdx] == aIdxes[origRowIdx - 1][colIdx]
) {
origRowIdx--;
}
try {
obtainedRowIdx = tableAcc.getRowIndexAt(idx);
} catch (e) {
ok(
false,
id + ": can't get row index for cell index " + idx + "," + e
);
}
is(
obtainedRowIdx,
origRowIdx,
id + ": row for index " + idx + " is not correct (getRowIndexAt)"
);
// getColumnIndexAt
var origColIdx = colIdx;
while (
origColIdx > 0 &&
aIdxes[rowIdx][colIdx] == aIdxes[rowIdx][origColIdx - 1]
) {
origColIdx--;
}
try {
obtainedColIdx = tableAcc.getColumnIndexAt(idx);
} catch (e) {
ok(
false,
id + ": can't get column index for cell index " + idx + "," + e
);
}
is(
obtainedColIdx,
origColIdx,
id +
": column for index " +
idx +
" is not correct (getColumnIndexAt)"
);
// getRowAndColumnIndicesAt
var obtainedRowIdxObj = {},
obtainedColIdxObj = {};
try {
tableAcc.getRowAndColumnIndicesAt(
idx,
obtainedRowIdxObj,
obtainedColIdxObj
);
} catch (e) {
ok(
false,
id +
": can't get row and column indices for cell index " +
idx +
"," +
e
);
}
is(
obtainedRowIdxObj.value,
origRowIdx,
id +
": row for index " +
idx +
" is not correct (getRowAndColumnIndicesAt)"
);
is(
obtainedColIdxObj.value,
origColIdx,
id +
": column for index " +
idx +
" is not correct (getRowAndColumnIndicesAt)"
);
if (cellAcc) {
var cellId = prettyName(cellAcc);
cellAcc = getAccessible(cellAcc, [nsIAccessibleTableCell]);
// cell: 'table-cell-index' attribute
var attrs = cellAcc.attributes;
var strIdx = "";
try {
strIdx = attrs.getStringProperty("table-cell-index");
} catch (e) {
ok(
false,
cellId +
": no cell index from object attributes on the cell accessible at index " +
idx +
"."
);
}
if (strIdx) {
is(
parseInt(strIdx),
idx,
cellId +
": cell index from object attributes of cell accessible isn't corrent."
);
}
// cell: table
try {
is(
cellAcc.table,
tableAcc,
cellId + ": wrong table accessible for the cell."
);
} catch (e) {
ok(false, cellId + ": can't get table accessible from the cell.");
}
// cell: getRowIndex
try {
obtainedRowIdx = cellAcc.rowIndex;
} catch (e) {
ok(
false,
cellId +
": can't get row index of the cell at index " +
idx +
"," +
e
);
}
is(
obtainedRowIdx,
origRowIdx,
cellId + ": row for the cell at index " + idx + " is not correct"
);
// cell: getColumnIndex
try {
obtainedColIdx = cellAcc.columnIndex;
} catch (e) {
ok(
false,
cellId +
": can't get column index of the cell at index " +
idx +
"," +
e
);
}
is(
obtainedColIdx,
origColIdx,
id + ": column for the cell at index " + idx + " is not correct"
);
}
}
// getCellIndexAt
try {
obtainedIdx = tableAcc.getCellIndexAt(rowIdx, colIdx);
} catch (e) {
obtainedIdx = -1;
}
is(
obtainedIdx,
idx,
id +
": row " +
rowIdx +
" /column " +
colIdx +
" and index " +
obtainedIdx +
" aren't inconsistent."
);
}
}
}
/**
* Test table getters selection methods.
*
* @param aIdentifier [in] table accessible identifier
* @param aCellsArray [in] two dimensional array (row X columns) of cells
* states (either boolean (selected/unselected) if cell is
* origin, otherwise kRowSpanned or kColSpanned constant).
* @param aMsg [in] text appended before every message
*/
function testTableSelection(aIdentifier, aCellsArray, aMsg) {
var msg = aMsg ? aMsg : "";
var acc = getAccessible(aIdentifier, [nsIAccessibleTable]);
if (!acc) {
return;
}
var rowCount = aCellsArray.length;
var colsCount = aCellsArray[0].length;
// Columns selection tests.
var selCols = [];
// isColumnSelected test
for (let colIdx = 0; colIdx < colsCount; colIdx++) {
var isColSelected = true;
for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) {
if (
!aCellsArray[rowIdx][colIdx] ||
aCellsArray[rowIdx][colIdx] == undefined
) {
isColSelected = false;
break;
}
}
is(
acc.isColumnSelected(colIdx),
isColSelected,
msg +
"Wrong selection state of " +
colIdx +
" column for " +
prettyName(aIdentifier)
);
if (isColSelected) {
selCols.push(colIdx);
}
}
// selectedColsCount test
is(
acc.selectedColumnCount,
selCols.length,
msg + "Wrong count of selected columns for " + prettyName(aIdentifier)
);
// getSelectedColumns test
var actualSelCols = acc.getSelectedColumnIndices();
var actualSelColsCount = actualSelCols.length;
is(
actualSelColsCount,
selCols.length,
msg +
"Wrong count of selected columns for " +
prettyName(aIdentifier) +
"from getSelectedColumns."
);
for (let i = 0; i < actualSelColsCount; i++) {
is(
actualSelCols[i],
selCols[i],
msg + "Column at index " + selCols[i] + " should be selected."
);
}
// Rows selection tests.
var selRows = [];
// isRowSelected test
for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) {
var isRowSelected = true;
for (let colIdx = 0; colIdx < colsCount; colIdx++) {
if (
!aCellsArray[rowIdx][colIdx] ||
aCellsArray[rowIdx][colIdx] == undefined
) {
isRowSelected = false;
break;
}
}
is(
acc.isRowSelected(rowIdx),
isRowSelected,
msg +
"Wrong selection state of " +
rowIdx +
" row for " +
prettyName(aIdentifier)
);
if (isRowSelected) {
selRows.push(rowIdx);
}
}
// selectedRowCount test
is(
acc.selectedRowCount,
selRows.length,
msg + "Wrong count of selected rows for " + prettyName(aIdentifier)
);
// getSelectedRows test
var actualSelRows = acc.getSelectedRowIndices();
var actualSelrowCount = actualSelRows.length;
is(
actualSelrowCount,
selRows.length,
msg +
"Wrong count of selected rows for " +
prettyName(aIdentifier) +
"from getSelectedRows."
);
for (let i = 0; i < actualSelrowCount; i++) {
is(
actualSelRows[i],
selRows[i],
msg + "Row at index " + selRows[i] + " should be selected."
);
}
// Cells selection tests.
var selCells = [];
// isCellSelected test
for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) {
for (let colIdx = 0; colIdx < colsCount; colIdx++) {
if (aCellsArray[rowIdx][colIdx] & kSpanned) {
continue;
}
var isSelected = !!aCellsArray[rowIdx][colIdx];
is(
acc.isCellSelected(rowIdx, colIdx),
isSelected,
msg +
"Wrong selection state of cell at " +
rowIdx +
" row and " +
colIdx +
" column for " +
prettyName(aIdentifier)
);
if (aCellsArray[rowIdx][colIdx]) {
selCells.push(acc.getCellIndexAt(rowIdx, colIdx));
}
}
}
// selectedCellCount tests
is(
acc.selectedCellCount,
selCells.length,
msg + "Wrong count of selected cells for " + prettyName(aIdentifier)
);
// getSelectedCellIndices test
var actualSelCells = acc.getSelectedCellIndices();
var actualSelCellsCount = actualSelCells.length;
is(
actualSelCellsCount,
selCells.length,
msg +
"Wrong count of selected cells for " +
prettyName(aIdentifier) +
"from getSelectedCells."
);
for (let i = 0; i < actualSelCellsCount; i++) {
is(
actualSelCells[i],
selCells[i],
msg +
"getSelectedCellIndices: Cell at index " +
selCells[i] +
" should be selected."
);
}
// selectedCells and isSelected tests
var actualSelCellsArray = acc.selectedCells;
for (let i = 0; i < actualSelCellsCount; i++) {
var actualSelCellAccessible = actualSelCellsArray.queryElementAt(
i,
nsIAccessibleTableCell
);
let colIdx = acc.getColumnIndexAt(selCells[i]);
let rowIdx = acc.getRowIndexAt(selCells[i]);
var expectedSelCellAccessible = acc.getCellAt(rowIdx, colIdx);
is(
actualSelCellAccessible,
expectedSelCellAccessible,
msg +
"getSelectedCells: Cell at index " +
selCells[i] +
" should be selected."
);
ok(
actualSelCellAccessible.isSelected(),
"isSelected: Cell at index " + selCells[i] + " should be selected."
);
}
// selected states tests
for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) {
for (let colIdx = 0; colIdx < colsCount; colIdx++) {
if (aCellsArray[rowIdx][colIdx] & kSpanned) {
continue;
}
var cell = acc.getCellAt(rowIdx, colIdx);
var isSel = aCellsArray[rowIdx][colIdx];
if (isSel == undefined) {
testStates(cell, 0, 0, STATE_SELECTABLE | STATE_SELECTED);
} else if (isSel) {
testStates(cell, STATE_SELECTED);
} else {
testStates(cell, STATE_SELECTABLE, 0, STATE_SELECTED);
}
}
}
}
/**
* Test unselectColumn method of accessible table.
*/
function testUnselectTableColumn(aIdentifier, aColIdx, aCellsArray) {
var acc = getAccessible(aIdentifier, [nsIAccessibleTable]);
if (!acc) {
return;
}
var rowCount = aCellsArray.length;
for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
// Unselect origin cell.
var [origRowIdx, origColIdx] = getOrigRowAndColumn(
aCellsArray,
rowIdx,
aColIdx
);
aCellsArray[origRowIdx][origColIdx] = false;
}
acc.unselectColumn(aColIdx);
testTableSelection(
aIdentifier,
aCellsArray,
"Unselect " + aColIdx + " column: "
);
}
/**
* Test selectColumn method of accessible table.
*/
function testSelectTableColumn(aIdentifier, aColIdx, aCellsArray) {
var acc = getAccessible(aIdentifier, [nsIAccessibleTable]);
if (!acc) {
return;
}
var rowCount = aCellsArray.length;
var colsCount = aCellsArray[0].length;
for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
var cellState = aCellsArray[rowIdx][colIdx];
if (colIdx == aColIdx) {
// select target column
if (!(cellState & kSpanned)) {
// Select the cell if it is origin.
aCellsArray[rowIdx][colIdx] = true;
} else {
// If the cell is spanned then search origin cell and select it.
var [origRowIdx, origColIdx] = getOrigRowAndColumn(
aCellsArray,
rowIdx,
colIdx
);
aCellsArray[origRowIdx][origColIdx] = true;
}
} else if (!(cellState & kSpanned)) {
// unselect other columns
if (colIdx > aColIdx) {
// Unselect the cell if traversed column index is greater than column
// index of target cell.
aCellsArray[rowIdx][colIdx] = false;
} else if (!(aCellsArray[rowIdx][aColIdx] & kColSpanned)) {
// Unselect the cell if the target cell is not row spanned.
aCellsArray[rowIdx][colIdx] = false;
} else {
// Unselect the cell if it is not spanned to the target cell.
for (
var spannedColIdx = colIdx + 1;
spannedColIdx < aColIdx;
spannedColIdx++
) {
var spannedCellState = aCellsArray[rowIdx][spannedColIdx];
if (!(spannedCellState & kRowSpanned)) {
aCellsArray[rowIdx][colIdx] = false;
break;
}
}
}
}
}
}
acc.selectColumn(aColIdx);
testTableSelection(
aIdentifier,
aCellsArray,
"Select " + aColIdx + " column: "
);
}
/**
* Test unselectRow method of accessible table.
*/
function testUnselectTableRow(aIdentifier, aRowIdx, aCellsArray) {
var acc = getAccessible(aIdentifier, [nsIAccessibleTable]);
if (!acc) {
return;
}
var colsCount = aCellsArray[0].length;
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
// Unselect origin cell.
var [origRowIdx, origColIdx] = getOrigRowAndColumn(
aCellsArray,
aRowIdx,
colIdx
);
aCellsArray[origRowIdx][origColIdx] = false;
}
acc.unselectRow(aRowIdx);
testTableSelection(
aIdentifier,
aCellsArray,
"Unselect " + aRowIdx + " row: "
);
}
/**
* Test selectRow method of accessible table.
*/
function testSelectTableRow(aIdentifier, aRowIdx, aCellsArray) {
var acc = getAccessible(aIdentifier, [nsIAccessibleTable]);
if (!acc) {
return;
}
var rowCount = aCellsArray.length;
var colsCount = aCellsArray[0].length;
for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
var cellState = aCellsArray[rowIdx][colIdx];
if (rowIdx == aRowIdx) {
// select the given row
if (!(cellState & kSpanned)) {
// Select the cell if it is origin.
aCellsArray[rowIdx][colIdx] = true;
} else {
// If the cell is spanned then search origin cell and select it.
var [origRowIdx, origColIdx] = getOrigRowAndColumn(
aCellsArray,
rowIdx,
colIdx
);
aCellsArray[origRowIdx][origColIdx] = true;
}
} else if (!(cellState & kSpanned)) {
// unselect other rows
if (rowIdx > aRowIdx) {
// Unselect the cell if traversed row index is greater than row
// index of target cell.
aCellsArray[rowIdx][colIdx] = false;
} else if (!(aCellsArray[aRowIdx][colIdx] & kRowSpanned)) {
// Unselect the cell if the target cell is not row spanned.
aCellsArray[rowIdx][colIdx] = false;
} else {
// Unselect the cell if it is not spanned to the target cell.
for (
var spannedRowIdx = rowIdx + 1;
spannedRowIdx < aRowIdx;
spannedRowIdx++
) {
var spannedCellState = aCellsArray[spannedRowIdx][colIdx];
if (!(spannedCellState & kRowSpanned)) {
aCellsArray[rowIdx][colIdx] = false;
break;
}
}
}
}
}
}
acc.selectRow(aRowIdx);
testTableSelection(aIdentifier, aCellsArray, "Select " + aRowIdx + " row: ");
}
/**
* Test columnHeaderCells and rowHeaderCells of accessible table.
*/
function testHeaderCells(aHeaderInfoMap) {
for (var testIdx = 0; testIdx < aHeaderInfoMap.length; testIdx++) {
var dataCellIdentifier = aHeaderInfoMap[testIdx].cell;
var dataCell = getAccessible(dataCellIdentifier, [nsIAccessibleTableCell]);
// row header cells
var rowHeaderCells = aHeaderInfoMap[testIdx].rowHeaderCells;
var rowHeaderCellsCount = rowHeaderCells.length;
var actualRowHeaderCells = dataCell.rowHeaderCells;
var actualRowHeaderCellsCount = actualRowHeaderCells.length;
is(
actualRowHeaderCellsCount,
rowHeaderCellsCount,
"Wrong number of row header cells for the cell " +
prettyName(dataCellIdentifier)
);
if (actualRowHeaderCellsCount == rowHeaderCellsCount) {
for (let idx = 0; idx < rowHeaderCellsCount; idx++) {
var rowHeaderCell = getAccessible(rowHeaderCells[idx]);
var actualRowHeaderCell = actualRowHeaderCells.queryElementAt(
idx,
nsIAccessible
);
isObject(
actualRowHeaderCell,
rowHeaderCell,
"Wrong row header cell at index " +
idx +
" for the cell " +
dataCellIdentifier
);
}
}
// column header cells
var colHeaderCells = aHeaderInfoMap[testIdx].columnHeaderCells;
var colHeaderCellsCount = colHeaderCells.length;
var actualColHeaderCells = dataCell.columnHeaderCells;
var actualColHeaderCellsCount = actualColHeaderCells.length;
is(
actualColHeaderCellsCount,
colHeaderCellsCount,
"Wrong number of column header cells for the cell " +
prettyName(dataCellIdentifier)
);
if (actualColHeaderCellsCount == colHeaderCellsCount) {
for (let idx = 0; idx < colHeaderCellsCount; idx++) {
var colHeaderCell = getAccessible(colHeaderCells[idx]);
var actualColHeaderCell = actualColHeaderCells.queryElementAt(
idx,
nsIAccessible
);
isObject(
actualColHeaderCell,
colHeaderCell,
"Wrong column header cell at index " +
idx +
" for the cell " +
dataCellIdentifier
);
}
}
}
}
// //////////////////////////////////////////////////////////////////////////////
// private implementation
/**
* Return row and column of orig cell for the given spanned cell.
*/
function getOrigRowAndColumn(aCellsArray, aRowIdx, aColIdx) {
var cellState = aCellsArray[aRowIdx][aColIdx];
var origRowIdx = aRowIdx,
origColIdx = aColIdx;
if (cellState & kRowSpanned) {
for (var prevRowIdx = aRowIdx - 1; prevRowIdx >= 0; prevRowIdx--) {
let prevCellState = aCellsArray[prevRowIdx][aColIdx];
if (!(prevCellState & kRowSpanned)) {
origRowIdx = prevRowIdx;
break;
}
}
}
if (cellState & kColSpanned) {
for (var prevColIdx = aColIdx - 1; prevColIdx >= 0; prevColIdx--) {
let prevCellState = aCellsArray[aRowIdx][prevColIdx];
if (!(prevCellState & kColSpanned)) {
origColIdx = prevColIdx;
break;
}
}
}
return [origRowIdx, origColIdx];
}