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;
 |