exceljs
Version:
Excel Workbook Manager - Read and Write xlsx and csv Files.
193 lines (164 loc) • 5.17 kB
JavaScript
const _ = require('../utils/under-dash');
const colCache = require('../utils/col-cache');
const CellMatrix = require('../utils/cell-matrix');
const Range = require('./range');
const rangeRegexp = /[$](\w+)[$](\d+)(:[$](\w+)[$](\d+))?/;
class DefinedNames {
constructor() {
this.matrixMap = {};
}
getMatrix(name) {
const matrix = this.matrixMap[name] || (this.matrixMap[name] = new CellMatrix());
return matrix;
}
// add a name to a cell. locStr in the form SheetName!$col$row or SheetName!$c1$r1:$c2:$r2
add(locStr, name) {
const location = colCache.decodeEx(locStr);
this.addEx(location, name);
}
addEx(location, name) {
const matrix = this.getMatrix(name);
if (location.top) {
for (let col = location.left; col <= location.right; col++) {
for (let row = location.top; row <= location.bottom; row++) {
const address = {
sheetName: location.sheetName,
address: colCache.n2l(col) + row,
row,
col,
};
matrix.addCellEx(address);
}
}
} else {
matrix.addCellEx(location);
}
}
remove(locStr, name) {
const location = colCache.decodeEx(locStr);
this.removeEx(location, name);
}
removeEx(location, name) {
const matrix = this.getMatrix(name);
matrix.removeCellEx(location);
}
removeAllNames(location) {
_.each(this.matrixMap, matrix => {
matrix.removeCellEx(location);
});
}
forEach(callback) {
_.each(this.matrixMap, (matrix, name) => {
matrix.forEach(cell => {
callback(name, cell);
});
});
}
// get all the names of a cell
getNames(addressStr) {
return this.getNamesEx(colCache.decodeEx(addressStr));
}
getNamesEx(address) {
return _.map(this.matrixMap, (matrix, name) => matrix.findCellEx(address) && name).filter(Boolean);
}
_explore(matrix, cell) {
cell.mark = false;
const {sheetName} = cell;
const range = new Range(cell.row, cell.col, cell.row, cell.col, sheetName);
let x;
let y;
// grow vertical - only one col to worry about
function vGrow(yy, edge) {
const c = matrix.findCellAt(sheetName, yy, cell.col);
if (!c || !c.mark) {
return false;
}
range[edge] = yy;
c.mark = false;
return true;
}
for (y = cell.row - 1; vGrow(y, 'top'); y--);
for (y = cell.row + 1; vGrow(y, 'bottom'); y++);
// grow horizontal - ensure all rows can grow
function hGrow(xx, edge) {
const cells = [];
for (y = range.top; y <= range.bottom; y++) {
const c = matrix.findCellAt(sheetName, y, xx);
if (c && c.mark) {
cells.push(c);
} else {
return false;
}
}
range[edge] = xx;
for (let i = 0; i < cells.length; i++) {
cells[i].mark = false;
}
return true;
}
for (x = cell.col - 1; hGrow(x, 'left'); x--);
for (x = cell.col + 1; hGrow(x, 'right'); x++);
return range;
}
getRanges(name, matrix) {
matrix = matrix || this.matrixMap[name];
if (!matrix) {
return {name, ranges: []};
}
// mark and sweep!
matrix.forEach(cell => {
cell.mark = true;
});
const ranges = matrix
.map(cell => cell.mark && this._explore(matrix, cell))
.filter(Boolean)
.map(range => range.$shortRange);
return {
name,
ranges,
};
}
normaliseMatrix(matrix, sheetName) {
// some of the cells might have shifted on specified sheet
// need to reassign rows, cols
matrix.forEachInSheet(sheetName, (cell, row, col) => {
if (cell) {
if (cell.row !== row || cell.col !== col) {
cell.row = row;
cell.col = col;
cell.address = colCache.n2l(col) + row;
}
}
});
}
spliceRows(sheetName, start, numDelete, numInsert) {
_.each(this.matrixMap, matrix => {
matrix.spliceRows(sheetName, start, numDelete, numInsert);
this.normaliseMatrix(matrix, sheetName);
});
}
spliceColumns(sheetName, start, numDelete, numInsert) {
_.each(this.matrixMap, matrix => {
matrix.spliceColumns(sheetName, start, numDelete, numInsert);
this.normaliseMatrix(matrix, sheetName);
});
}
get model() {
// To get names per cell - just iterate over all names finding cells if they exist
return _.map(this.matrixMap, (matrix, name) => this.getRanges(name, matrix)).filter(definedName => definedName.ranges.length);
}
set model(value) {
// value is [ { name, ranges }, ... ]
const matrixMap = (this.matrixMap = {});
value.forEach(definedName => {
const matrix = (matrixMap[definedName.name] = new CellMatrix());
definedName.ranges.forEach(rangeStr => {
if (rangeRegexp.test(rangeStr.split('!').pop() || '')) {
matrix.addCell(rangeStr);
}
});
});
}
}
module.exports = DefinedNames;
;