name:
src/Cell.ts
-rw-r--r--
5755
1const CELL_ID_ERROR = "CELL_ID_ERROR";
2
3/**
4 * Represents a cell id error, and is thrown when a cells id does not conform to A1 notation.
5 */
6class CellIdError extends Error {
7 constructor(msg: string) {
8 super();
9 this.message = msg;
10 this.name = CELL_ID_ERROR;
11 }
12}
13
14/**
15 * Cell represents a cell in the spreadsheet. It contains a nullable rawFormulaText, and a value, which is not nullable unless
16 * the parsing of the rawFormulaText results in an error.
17 */
18class Cell {
19 /**
20 * The raw formula text that can be parse, excluding the proceeding =
21 * E.g: SUM(A2:A4, 10)
22 */
23 private rawFormulaText: string = null;
24 private typedValue: any = null;
25 private dependencies: Array<string> = [];
26 private error: Error = null;
27 private id: string;
28 private row: number;
29 private col: number;
30
31 /**
32 * Creates an empty cell with an id.
33 * @param id key of the cell in A1-format.
34 */
35 constructor(id: string) {
36 if (!id.match(/^(?:[A-Za-z]+[0-9]+)$/)) {
37 throw new CellIdError("Cell id " + id + " not valid");
38 }
39 let key = parseKey(id);
40
41 this.id = id;
42 this.row = key.y;
43 this.col = key.x;
44 }
45
46 /**
47 * Update this cell's dependencies, where `dependencies` is a unique list of A1-format cell IDs.
48 * @param dependencies to merge with existing dependencies.
49 */
50 updateDependencies(dependencies: Array<string>) {
51 for (let index in dependencies) {
52 if (this.dependencies.indexOf(dependencies[index]) === -1) {
53 this.dependencies.push(dependencies[index]);
54 }
55 }
56 }
57
58 /**
59 * Return a list of dependencies in A1-format cell IDs, in no particular order, but likely in order of occurrence in
60 * rawFormulaText.
61 * @returns {Array<string>} list of dependencies in A1-format
62 */
63 getDependencies() : Array<string> {
64 return this.dependencies;
65 }
66
67 /**
68 * Return the zero-indexed column number of this cell.
69 * @returns {number} column
70 */
71 getColumn() : number {
72 return this.col;
73 }
74
75 /**
76 * Return the zero-indexed row number of this cell.
77 * @returns {number} row
78 */
79 getRow() : number {
80 return this.row;
81 }
82
83 /**
84 * Get the A1-format ID of this cell.
85 * @returns {string} cell ID
86 */
87 getId() : string {
88 return this.id;
89 }
90
91 /**
92 * Get the rawFormulaText of this cell if set. Defaults to null, so should be used in combination with hasFormula().
93 * @returns {string} rawFormulaText of this cell, if set. Nullable.
94 */
95 getFormula() : string {
96 return this.rawFormulaText;
97 }
98
99 /**
100 * Returns true if this cell has a formula to be parsed.
101 * @returns {boolean}
102 */
103 hasFormula() : boolean {
104 return this.rawFormulaText !== null;
105 }
106
107 /**
108 * Sets the value or rawFormulaText for this cell. If the input begins with =, then it is considered to be a rawFormulaText. If it
109 * is not, then it is a value, and set as the raw value for this cell.
110 * @param rawFormula
111 */
112 setValue(rawFormula: string) {
113 if (typeof rawFormula === "string" && rawFormula.charAt(0) === "=") {
114 this.rawFormulaText = rawFormula.substr(1);
115 } else {
116 this.typedValue = rawFormula;
117 }
118 }
119
120 /**
121 * Gets the rawFormulaText for this cell, which is either null or a string.
122 * @returns {string}
123 */
124 getRawFormulaText() : string | null {
125 return this.rawFormulaText;
126 }
127
128
129 /**
130 * Get the value of this cell if a value is present. If this cell was given a formula but not a value, this may return
131 * null.
132 * @returns {any}
133 */
134 getValue() : any {
135 return this.typedValue;
136 }
137
138 /**
139 * CLears a cells value.
140 */
141 clearValue() {
142 this.typedValue = null;
143 }
144
145 /**
146 * Set error for this cell. Usually in the case of a parse error when parsing the rawFormulaText.
147 * @param error to set.
148 */
149 setError(error: Error) {
150 this.error = error;
151 }
152
153 /**
154 * Get the error for this cell. If the rawFormulaText is not parsed properly, or is null, this could be null.
155 * @returns {Error} error to return, could be null.
156 */
157 getError() : Error {
158 return this.error;
159 }
160
161 /**
162 * Easier way to check if this cell has an error.
163 * @returns {boolean}
164 */
165 hasError() : boolean {
166 return this.error !== null;
167 }
168
169 /**
170 * A cell is deemed blank if it contains no value, no error, and no typed value.
171 * @returns {boolean}
172 */
173 isBlank(): boolean {
174 return this.error === null && this.rawFormulaText === null && this.typedValue === null;
175 }
176
177 /**
178 * Returns the human-readable string representation of this cell, omitting some obvious fields.
179 * @returns {string}
180 */
181 toString() : string {
182 return "id=" + this.id + ", value=" + this.typedValue + ", rawFormulaText=" + this.rawFormulaText + ", error=" + this.error;
183 }
184
185 /**
186 * Comparing two cells.
187 * @param other
188 * @returns {boolean}
189 */
190 equals(other : Cell) : boolean {
191 return this.toString() === other.toString();
192 }
193
194 /**
195 * Build a cell with an id and value.
196 * @param id - A1-notation id or key.
197 * @param value - value of the cell as a string
198 * @returns {Cell}
199 * @constructor
200 */
201 static BuildFrom(id: string, value: any) : Cell {
202 let cell = new Cell(id);
203 cell.setValue(value);
204 return cell;
205 }
206}
207
208function toNum(chr) {
209 chr = chr.replace(/\$/g, '');
210 let base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', i, j, result = 0;
211 for (i = 0, j = chr.length - 1; i < chr.length; i += 1, j -= 1) {
212 result += Math.pow(base.length, j) * (base.indexOf(chr[i]) + 1);
213 }
214 if (result) {
215 --result;
216 }
217 return result;
218}
219
220function parseKey(cell) {
221 let num = cell.match(/\d+$/),
222 alpha = cell.replace(num, '');
223
224 return {
225 x: toNum(alpha),
226 y: parseInt(num[0], 10) - 1
227 };
228}
229
230export {
231 Cell,
232 CellIdError,
233 CELL_ID_ERROR
234}