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.
		
		
		
		
		
			
		
			
				
					
					
						
							416 lines
						
					
					
						
							10 KiB
						
					
					
				
			
		
		
	
	
							416 lines
						
					
					
						
							10 KiB
						
					
					
				| 'use strict';
 | |
| 
 | |
| const _ = require('../utils/under-dash');
 | |
| 
 | |
| const Enums = require('./enums');
 | |
| const colCache = require('../utils/col-cache');
 | |
| const Cell = require('./cell');
 | |
| 
 | |
| class Row {
 | |
|   constructor(worksheet, number) {
 | |
|     this._worksheet = worksheet;
 | |
|     this._number = number;
 | |
|     this._cells = [];
 | |
|     this.style = {};
 | |
|     this.outlineLevel = 0;
 | |
|   }
 | |
| 
 | |
|   // return the row number
 | |
|   get number() {
 | |
|     return this._number;
 | |
|   }
 | |
| 
 | |
|   get worksheet() {
 | |
|     return this._worksheet;
 | |
|   }
 | |
| 
 | |
|   // Inform Streaming Writer that this row (and all rows before it) are complete
 | |
|   // and ready to write. Has no effect on Worksheet document
 | |
|   commit() {
 | |
|     this._worksheet._commitRow(this); // eslint-disable-line no-underscore-dangle
 | |
|   }
 | |
| 
 | |
|   // helps GC by breaking cyclic references
 | |
|   destroy() {
 | |
|     delete this._worksheet;
 | |
|     delete this._cells;
 | |
|     delete this.style;
 | |
|   }
 | |
| 
 | |
|   findCell(colNumber) {
 | |
|     return this._cells[colNumber - 1];
 | |
|   }
 | |
| 
 | |
|   // given {address, row, col}, find or create new cell
 | |
|   getCellEx(address) {
 | |
|     let cell = this._cells[address.col - 1];
 | |
|     if (!cell) {
 | |
|       const column = this._worksheet.getColumn(address.col);
 | |
|       cell = new Cell(this, column, address.address);
 | |
|       this._cells[address.col - 1] = cell;
 | |
|     }
 | |
|     return cell;
 | |
|   }
 | |
| 
 | |
|   // get cell by key, letter or column number
 | |
|   getCell(col) {
 | |
|     if (typeof col === 'string') {
 | |
|       // is it a key?
 | |
|       const column = this._worksheet.getColumnKey(col);
 | |
|       if (column) {
 | |
|         col = column.number;
 | |
|       } else {
 | |
|         col = colCache.l2n(col);
 | |
|       }
 | |
|     }
 | |
|     return (
 | |
|       this._cells[col - 1] ||
 | |
|       this.getCellEx({
 | |
|         address: colCache.encodeAddress(this._number, col),
 | |
|         row: this._number,
 | |
|         col,
 | |
|       })
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   // remove cell(s) and shift all higher cells down by count
 | |
|   splice(start, count, ...inserts) {
 | |
|     const nKeep = start + count;
 | |
|     const nExpand = inserts.length - count;
 | |
|     const nEnd = this._cells.length;
 | |
|     let i;
 | |
|     let cSrc;
 | |
|     let cDst;
 | |
| 
 | |
|     if (nExpand < 0) {
 | |
|       // remove cells
 | |
|       for (i = start + inserts.length; i <= nEnd; i++) {
 | |
|         cDst = this._cells[i - 1];
 | |
|         cSrc = this._cells[i - nExpand - 1];
 | |
|         if (cSrc) {
 | |
|           cDst = this.getCell(i);
 | |
|           cDst.value = cSrc.value;
 | |
|           cDst.style = cSrc.style;
 | |
|           // eslint-disable-next-line no-underscore-dangle
 | |
|           cDst._comment = cSrc._comment;
 | |
|         } else if (cDst) {
 | |
|           cDst.value = null;
 | |
|           cDst.style = {};
 | |
|           // eslint-disable-next-line no-underscore-dangle
 | |
|           cDst._comment = undefined;
 | |
|         }
 | |
|       }
 | |
|     } else if (nExpand > 0) {
 | |
|       // insert new cells
 | |
|       for (i = nEnd; i >= nKeep; i--) {
 | |
|         cSrc = this._cells[i - 1];
 | |
|         if (cSrc) {
 | |
|           cDst = this.getCell(i + nExpand);
 | |
|           cDst.value = cSrc.value;
 | |
|           cDst.style = cSrc.style;
 | |
|           // eslint-disable-next-line no-underscore-dangle
 | |
|           cDst._comment = cSrc._comment;
 | |
|         } else {
 | |
|           this._cells[i + nExpand - 1] = undefined;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // now add the new values
 | |
|     for (i = 0; i < inserts.length; i++) {
 | |
|       cDst = this.getCell(start + i);
 | |
|       cDst.value = inserts[i];
 | |
|       cDst.style = {};
 | |
|       // eslint-disable-next-line no-underscore-dangle
 | |
|       cDst._comment = undefined;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Iterate over all non-null cells in this row
 | |
|   eachCell(options, iteratee) {
 | |
|     if (!iteratee) {
 | |
|       iteratee = options;
 | |
|       options = null;
 | |
|     }
 | |
|     if (options && options.includeEmpty) {
 | |
|       const n = this._cells.length;
 | |
|       for (let i = 1; i <= n; i++) {
 | |
|         iteratee(this.getCell(i), i);
 | |
|       }
 | |
|     } else {
 | |
|       this._cells.forEach((cell, index) => {
 | |
|         if (cell && cell.type !== Enums.ValueType.Null) {
 | |
|           iteratee(cell, index + 1);
 | |
|         }
 | |
|       });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // ===========================================================================
 | |
|   // Page Breaks
 | |
|   addPageBreak(lft, rght) {
 | |
|     const ws = this._worksheet;
 | |
|     const left = Math.max(0, lft - 1) || 0;
 | |
|     const right = Math.max(0, rght - 1) || 16838;
 | |
|     const pb = {
 | |
|       id: this._number,
 | |
|       max: right,
 | |
|       man: 1,
 | |
|     };
 | |
|     if (left) pb.min = left;
 | |
| 
 | |
|     ws.rowBreaks.push(pb);
 | |
|   }
 | |
| 
 | |
|   // return a sparse array of cell values
 | |
|   get values() {
 | |
|     const values = [];
 | |
|     this._cells.forEach(cell => {
 | |
|       if (cell && cell.type !== Enums.ValueType.Null) {
 | |
|         values[cell.col] = cell.value;
 | |
|       }
 | |
|     });
 | |
|     return values;
 | |
|   }
 | |
| 
 | |
|   // set the values by contiguous or sparse array, or by key'd object literal
 | |
|   set values(value) {
 | |
|     // this operation is not additive - any prior cells are removed
 | |
|     this._cells = [];
 | |
|     if (!value) {
 | |
|       // empty row
 | |
|     } else if (value instanceof Array) {
 | |
|       let offset = 0;
 | |
|       if (value.hasOwnProperty('0')) {
 | |
|         // contiguous array - start at column 1
 | |
|         offset = 1;
 | |
|       }
 | |
|       value.forEach((item, index) => {
 | |
|         if (item !== undefined) {
 | |
|           this.getCellEx({
 | |
|             address: colCache.encodeAddress(this._number, index + offset),
 | |
|             row: this._number,
 | |
|             col: index + offset,
 | |
|           }).value = item;
 | |
|         }
 | |
|       });
 | |
|     } else {
 | |
|       // assume object with column keys
 | |
|       this._worksheet.eachColumnKey((column, key) => {
 | |
|         if (value[key] !== undefined) {
 | |
|           this.getCellEx({
 | |
|             address: colCache.encodeAddress(this._number, column.number),
 | |
|             row: this._number,
 | |
|             col: column.number,
 | |
|           }).value = value[key];
 | |
|         }
 | |
|       });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // returns true if the row includes at least one cell with a value
 | |
|   get hasValues() {
 | |
|     return _.some(this._cells, cell => cell && cell.type !== Enums.ValueType.Null);
 | |
|   }
 | |
| 
 | |
|   get cellCount() {
 | |
|     return this._cells.length;
 | |
|   }
 | |
| 
 | |
|   get actualCellCount() {
 | |
|     let count = 0;
 | |
|     this.eachCell(() => {
 | |
|       count++;
 | |
|     });
 | |
|     return count;
 | |
|   }
 | |
| 
 | |
|   // get the min and max column number for the non-null cells in this row or null
 | |
|   get dimensions() {
 | |
|     let min = 0;
 | |
|     let max = 0;
 | |
|     this._cells.forEach(cell => {
 | |
|       if (cell && cell.type !== Enums.ValueType.Null) {
 | |
|         if (!min || min > cell.col) {
 | |
|           min = cell.col;
 | |
|         }
 | |
|         if (max < cell.col) {
 | |
|           max = cell.col;
 | |
|         }
 | |
|       }
 | |
|     });
 | |
|     return min > 0
 | |
|       ? {
 | |
|           min,
 | |
|           max,
 | |
|         }
 | |
|       : null;
 | |
|   }
 | |
| 
 | |
|   // =========================================================================
 | |
|   // styles
 | |
|   _applyStyle(name, value) {
 | |
|     this.style[name] = value;
 | |
|     this._cells.forEach(cell => {
 | |
|       if (cell) {
 | |
|         cell[name] = value;
 | |
|       }
 | |
|     });
 | |
|     return value;
 | |
|   }
 | |
| 
 | |
|   get numFmt() {
 | |
|     return this.style.numFmt;
 | |
|   }
 | |
| 
 | |
|   set numFmt(value) {
 | |
|     this._applyStyle('numFmt', value);
 | |
|   }
 | |
| 
 | |
|   get font() {
 | |
|     return this.style.font;
 | |
|   }
 | |
| 
 | |
|   set font(value) {
 | |
|     this._applyStyle('font', value);
 | |
|   }
 | |
| 
 | |
|   get alignment() {
 | |
|     return this.style.alignment;
 | |
|   }
 | |
| 
 | |
|   set alignment(value) {
 | |
|     this._applyStyle('alignment', value);
 | |
|   }
 | |
| 
 | |
|   get protection() {
 | |
|     return this.style.protection;
 | |
|   }
 | |
| 
 | |
|   set protection(value) {
 | |
|     this._applyStyle('protection', value);
 | |
|   }
 | |
| 
 | |
|   get border() {
 | |
|     return this.style.border;
 | |
|   }
 | |
| 
 | |
|   set border(value) {
 | |
|     this._applyStyle('border', value);
 | |
|   }
 | |
| 
 | |
|   get fill() {
 | |
|     return this.style.fill;
 | |
|   }
 | |
| 
 | |
|   set fill(value) {
 | |
|     this._applyStyle('fill', value);
 | |
|   }
 | |
| 
 | |
|   get hidden() {
 | |
|     return !!this._hidden;
 | |
|   }
 | |
| 
 | |
|   set hidden(value) {
 | |
|     this._hidden = value;
 | |
|   }
 | |
| 
 | |
|   get outlineLevel() {
 | |
|     return this._outlineLevel || 0;
 | |
|   }
 | |
| 
 | |
|   set outlineLevel(value) {
 | |
|     this._outlineLevel = value;
 | |
|   }
 | |
| 
 | |
|   get collapsed() {
 | |
|     return !!(
 | |
|       this._outlineLevel && this._outlineLevel >= this._worksheet.properties.outlineLevelRow
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   // =========================================================================
 | |
|   get model() {
 | |
|     const cells = [];
 | |
|     let min = 0;
 | |
|     let max = 0;
 | |
|     this._cells.forEach(cell => {
 | |
|       if (cell) {
 | |
|         const cellModel = cell.model;
 | |
|         if (cellModel) {
 | |
|           if (!min || min > cell.col) {
 | |
|             min = cell.col;
 | |
|           }
 | |
|           if (max < cell.col) {
 | |
|             max = cell.col;
 | |
|           }
 | |
|           cells.push(cellModel);
 | |
|         }
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     return this.height || cells.length
 | |
|       ? {
 | |
|           cells,
 | |
|           number: this.number,
 | |
|           min,
 | |
|           max,
 | |
|           height: this.height,
 | |
|           style: this.style,
 | |
|           hidden: this.hidden,
 | |
|           outlineLevel: this.outlineLevel,
 | |
|           collapsed: this.collapsed,
 | |
|         }
 | |
|       : null;
 | |
|   }
 | |
| 
 | |
|   set model(value) {
 | |
|     if (value.number !== this._number) {
 | |
|       throw new Error('Invalid row number in model');
 | |
|     }
 | |
|     this._cells = [];
 | |
|     let previousAddress;
 | |
|     value.cells.forEach(cellModel => {
 | |
|       switch (cellModel.type) {
 | |
|         case Cell.Types.Merge:
 | |
|           // special case - don't add this types
 | |
|           break;
 | |
|         default: {
 | |
|           let address;
 | |
|           if (cellModel.address) {
 | |
|             address = colCache.decodeAddress(cellModel.address);
 | |
|           } else if (previousAddress) {
 | |
|             // This is a <c> element without an r attribute
 | |
|             // Assume that it's the cell for the next column
 | |
|             const {row} = previousAddress;
 | |
|             const col = previousAddress.col + 1;
 | |
|             address = {
 | |
|               row,
 | |
|               col,
 | |
|               address: colCache.encodeAddress(row, col),
 | |
|               $col$row: `$${colCache.n2l(col)}$${row}`,
 | |
|             };
 | |
|           }
 | |
|           previousAddress = address;
 | |
|           const cell = this.getCellEx(address);
 | |
|           cell.model = cellModel;
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     if (value.height) {
 | |
|       this.height = value.height;
 | |
|     } else {
 | |
|       delete this.height;
 | |
|     }
 | |
| 
 | |
|     this.hidden = value.hidden;
 | |
|     this.outlineLevel = value.outlineLevel || 0;
 | |
| 
 | |
|     this.style = (value.style && JSON.parse(JSON.stringify(value.style))) || {};
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports = Row;
 |