spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← All files
name: src/Formulas/Info.ts
-rw-r--r--
11688
  1import {
  2  ArgsChecker
  3} from "../Utilities/ArgsChecker";
  4import {
  5  NAError, NullError, DivZeroError, ValueError, RefError, NameError, NumError, NULL_ERROR, DIV_ZERO_ERROR, VALUE_ERROR,
  6  REF_ERROR, NAME_ERROR, NUM_ERROR, NA_ERROR
  7} from "../Errors";
  8import {
  9  TypeConverter
 10} from "../Utilities/TypeConverter";
 11import {
 12  Cell
 13} from "../Cell";
 14import {Filter} from "../Utilities/Filter";
 15
 16
 17/**
 18 * Returns the "value not available" error, "#N/A".
 19 * @constructor
 20 */
 21let NA = function () {
 22  ArgsChecker.checkLength(arguments, 0, "NA");
 23  throw new NAError("NA Error thrown.");
 24};
 25
 26
 27/**
 28 * Returns true if a value is text.
 29 * @param value - value or reference to check.
 30 * @returns {boolean}.
 31 * @constructor
 32 */
 33let ISTEXT =  function (value) {
 34  ArgsChecker.checkLength(arguments, 1, "ISTEXT");
 35  return typeof TypeConverter.firstValue(value) === "string";
 36};
 37
 38
 39/**
 40 * Returns true if a value is not text.
 41 * @param value - value or reference to check.
 42 * @returns {boolean}.
 43 * @constructor
 44 */
 45let ISNONTEXT = function (value) {
 46  ArgsChecker.checkLength(arguments, 1, "ISNONTEXT");
 47  return typeof TypeConverter.firstValue(value) !== "string";
 48};
 49
 50
 51/**
 52 * Returns true if value is a boolean (FALSE, or TRUE). Numerical and text values return false.
 53 * @param value - value or reference to check.
 54 * @returns {boolean}
 55 * @constructor
 56 */
 57let ISLOGICAL = function (value) {
 58  ArgsChecker.checkLength(arguments, 1, "ISLOGICAL");
 59  return typeof TypeConverter.firstValue(value) === "boolean";
 60};
 61
 62
 63/**
 64 * Returns true if value or reference is a number.
 65 * @param value - value or reference to check.
 66 * @returns {boolean}
 67 * @constructor
 68 */
 69let ISNUMBER = function (value) {
 70  ArgsChecker.checkLength(arguments, 1, "ISNUMBER");
 71  return typeof TypeConverter.firstValue(value) === "number";
 72};
 73
 74
 75/**
 76 * Returns true if input is a valid email. Valid domains are Original top-level domains and Country code top-level
 77 * domains.
 78 * @param value - Value to check whether it is an email or not.
 79 * @returns {boolean}
 80 * @constructor
 81 */
 82let ISEMAIL = function (value) {
 83  ArgsChecker.checkLength(arguments, 1, "ISEMAIL");
 84  if (typeof value !== "string") {
 85    return false;
 86  }
 87  const EMAIL_REGEX = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[A-Z]{2}|com|org|net|gov|mil|edu|int|biz|info|mobi|name|aero|jobs|museum|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bl|bm|bn|bo|bq|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mf|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)\b/;
 88  return EMAIL_REGEX.test(TypeConverter.firstValueAsString(value));
 89};
 90
 91
 92/**
 93 * Returns true if the input is a valid URL.
 94 * @param value - Value to check
 95 * @returns {boolean}
 96 * @constructor
 97 */
 98let ISURL = function (value) {
 99  ArgsChecker.checkLength(arguments, 1, "ISURL");
100  value = TypeConverter.firstValueAsString(value);
101  let matches = value.match(/(?:([^:\/?#]+):)?(?:\/\/([^\/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/);
102  if (/[^a-z0-9\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=\.\-\_\~\%]/i.test(value)) {
103    return false;
104  }
105  if (/%[^0-9a-f]/i.test(value)) {
106    return false;
107  }
108  if (/%[0-9a-f](:?[^0-9a-f]|$)/i.test(value)) {
109    return false;
110  }
111  let authority = matches[2];
112  let path = matches[3];
113  if (!(path.length >= 0)) {
114    return false;
115  }
116  if (authority && authority.length) {
117    if (!(path.length === 0 || /^\//.test(path))) {
118      return false;
119    }
120  } else {
121    if (/^\/\//.test(path)) {
122      return false;
123    }
124  }
125  return true;
126};
127
128/**
129 * Returns the value as a number.
130 * @param value - value to return.
131 * @returns {number}
132 * @constructor
133 */
134let N = function (value) {
135  ArgsChecker.checkLength(arguments, 1, "N");
136  return TypeConverter.firstValueAsNumber(value);
137};
138
139
140/**
141 * Tests if the content of one or several cells is a reference. Verifies the type of references in a cell or a range of
142 * cells. If an error occurs, the function returns a logical or numerical value.
143 * @param value - The value to be tested, to determine whether it is a reference.
144 * @returns {boolean}
145 * @constructor
146 */
147let ISREF = function (value) {
148  ArgsChecker.checkLength(arguments, 1, "ISREF");
149  return TypeConverter.firstValue(value) instanceof Cell;
150};
151
152
153/**
154 * Returns the number corresponding to an error value occurring in a different cell. With the aid of this number, an
155 * error message text can be generated. If an error occurs, the function returns a logical or numerical value.
156 * @param value - Contains either the address/reference of the cell in which the error occurs, or the error directly.
157 * Eg: `=ERRORTYPE(NA())`
158 * @constructor
159 */
160let ERRORTYPE = function (value) {
161  ArgsChecker.checkLength(arguments, 1, "ERRORTYPE");
162  try {
163    value = TypeConverter.firstValue(value);
164  } catch (e) {
165    value = e;
166  }
167  if (value instanceof Cell) {
168    if (value.hasError()) {
169      value = value.getError();
170    } else {
171      throw new NAError("Function ERROR.TYPE parameter 1 value is not an error.");
172    }
173  }
174  if (value instanceof Error) {
175    switch (value.name) {
176      case NULL_ERROR:
177        return 1;
178      case DIV_ZERO_ERROR:
179        return 2;
180      case VALUE_ERROR:
181        return 3;
182      case REF_ERROR:
183        return 4;
184      case NAME_ERROR:
185        return 5;
186      case NUM_ERROR:
187        return 6;
188      case NA_ERROR:
189        return 7;
190      default:
191        return 8;
192    }
193  } else {
194    throw new NAError("Function ERROR.TYPE parameter 1 value is not an error.");
195  }
196};
197
198
199/**
200 * Returns TRUE if the reference to a cell is blank. This function is used to determine if the content of a cell is
201 * empty. A cell with a formula inside is not empty. If an error occurs, the function returns a logical or numerical
202 * value.
203 * @param value - The content to be tested.
204 * @returns {boolean}
205 * @constructor
206 */
207let ISBLANK = function (value) {
208  ArgsChecker.checkLength(arguments, 1, "ISBLANK");
209  if (value instanceof Cell) {
210    return value.isBlank();
211  }
212  return value === undefined;
213};
214
215
216/**
217 * Returns TRUE if the value refers to any error value except #N/A. You can use this function to control error values
218 * in certain cells. If an error occurs, the function returns a logical or numerical value.
219 * @param value - Any value or expression in which a test is performed to determine whether an error value not equal to
220 * #N/A is present.
221 * @returns {boolean}
222 * @constructor
223 */
224let ISERR = function (value) {
225  ArgsChecker.checkLength(arguments, 1, "ISERR");
226  try {
227    value = TypeConverter.firstValue(value);
228  } catch (e) {
229    return true;
230  }
231  if (value instanceof Cell) {
232    if (value.hasError()) {
233      return value.getError().name !== NA_ERROR;
234    }
235    return false;
236  }
237  if (value instanceof Error) {
238    return value.name !== NA_ERROR;
239  }
240  return false;
241};
242
243/**
244 * Tests if the cells contain general error values. ISERROR recognizes the #N/A error value. If an error occurs, the
245 * function returns a logical or numerical value.
246 * @param value - is any value where a test is performed to determine whether it is an error value.
247 * @returns {boolean}
248 * @constructor
249 */
250let ISERROR = function (value) {
251  ArgsChecker.checkLength(arguments, 1, "ISERROR");
252  try {
253    value = TypeConverter.firstValue(value);
254  } catch (e) {
255    return true;
256  }
257  if (value instanceof Cell) {
258    return value.hasError();
259  }
260  return (value instanceof Error);
261};
262
263
264/**
265 * Returns TRUE if a cell contains the #N/A (value not available) error value. If an error occurs, the function returns
266 * a logical or numerical value.
267 * @param value - The value or expression to be tested.
268 * @returns {boolean}
269 * @constructor
270 */
271let ISNA = function (value) {
272  ArgsChecker.checkLength(arguments, 1, "ISNA");
273  try {
274    value = TypeConverter.firstValue(value);
275  } catch (e) {
276    return false;
277  }
278  if (value instanceof Cell) {
279    if (value.hasError()) {
280      return value.getError().name === NA_ERROR;
281    }
282  }
283  if (value instanceof Error) {
284    return value.name === NA_ERROR;
285  }
286  return false;
287};
288
289
290/**
291 * Returns the first argument if no error value is present, otherwise returns the second argument if provided, or a
292 * blank if the second argument is absent. Blank value is `null`.
293 * @param value - Value to check for error.
294 * @param valueIfError - [OPTIONAL] - Value to return if no error is present in the first argument.
295 * @returns {any}
296 * @constructor
297 */
298let IFERROR = function (value, valueIfError?) {
299  valueIfError = valueIfError || null;
300  ArgsChecker.checkLengthWithin(arguments, 1, 2, "IFERROR");
301  if (value instanceof Cell) {
302    if (value.hasError()) {
303      return valueIfError;
304    }
305    return value;
306  }
307  if (ISERROR(value)) {
308    return valueIfError;
309  }
310  return value;
311};
312
313
314/**
315 * Returns a number corresponding to the type of data passed into the function. 1 = number, 2 = text, 4 = boolean,
316 * 16 = error, 64 = array/range, 128 = any other type of cell.
317 * @param value - Value for which the type will be determined.
318 * @returns {number}
319 * @constructor
320 */
321let TYPE = function (value) {
322  ArgsChecker.checkLengthWithin(arguments, 1, 2, "TYPE");
323  if (value instanceof Cell) {
324    if (value.hasError()) {
325      return 16;
326    }
327    value = value.getValue();
328  }
329  if (value === null) {
330    return 1;
331  }
332  if (typeof value === "number") {
333    return 1;
334  }
335  if (typeof value === "string") {
336    return 2;
337  }
338  if (typeof value === "boolean") {
339    return 4;
340  }
341  if (value instanceof Error) {
342    return 16;
343  }
344  if (value instanceof Array) {
345    return 64;
346  }
347  return 128;
348};
349
350
351/**
352 * Returns the column number of a specified cell, starting with column 1 for A.
353 * @param cell - Cell, defaults to the cell calling this formula, when used in the context of a spreadsheet.
354 * @constructor
355 */
356let COLUMN =  function (cell) {
357  ArgsChecker.checkLength(arguments, 1, "COLUMN");
358  if (!(cell instanceof Cell)) {
359    throw new NAError("Argument must be a range or reference.");
360  }
361  return cell.getColumn() + 1;
362};
363
364
365/**
366 * Returns the row number of a specified cell, starting with row 1 for A1.
367 * @param cell - Cell, defaults to the cell calling this formula, when used in the context of a spreadsheet.
368 * @constructor
369 */
370let ROW =  function (cell) {
371  ArgsChecker.checkLength(arguments, 1, "ROW");
372  if (!(cell instanceof Cell)) {
373    throw new NAError("Argument must be a range or reference.");
374  }
375  return cell.getRow() + 1;
376};
377
378
379/**
380 * Returns TRUE if a cell is a formula cell. Must be given a reference.
381 * @param value - To check.
382 * @returns {boolean}
383 * @constructor
384 */
385let ISFORMULA = function (value) {
386  ArgsChecker.checkLength(arguments, 1, "ISFORMULA");
387  if (value instanceof Array) {
388    if (value.length === 0) {
389      throw new RefError("Reference does not exist.");
390    }
391  }
392  if (!(value instanceof Cell)) {
393    throw new NAError("Argument must be a range");
394  }
395  return value.hasFormula();
396};
397
398
399export {
400  NA,
401  ISTEXT,
402  ISLOGICAL,
403  ISNUMBER,
404  ISNONTEXT,
405  ISEMAIL,
406  ISURL,
407  N,
408  ISREF,
409  ERRORTYPE,
410  ISBLANK,
411  ISERR,
412  ISERROR,
413  ISNA,
414  IFERROR,
415  TYPE,
416  COLUMN,
417  ROW,
418  ISFORMULA
419}