You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							197 lines
						
					
					
						
							5.1 KiB
						
					
					
				
			
		
		
	
	
							197 lines
						
					
					
						
							5.1 KiB
						
					
					
				'use strict';
 | 
						|
 | 
						|
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;
 |