@lhncbc/ucum-lhc 4.2.0 → 5.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +50 -12
  2. package/browser-dist/ucum-lhc.js +841 -1325
  3. package/data/ucumDefs.min.json +1 -1
  4. package/package.json +1 -1
  5. package/source/ucumLhcUtils.js +100 -66
  6. package/source/ucumXmlDocument.js +5 -2
  7. package/source/unit.js +2 -1
  8. package/source/unitString.js +159 -140
  9. package/source-cjs/config.js +1 -12
  10. package/source-cjs/config.js.map +1 -1
  11. package/source-cjs/dimension.js +20 -63
  12. package/source-cjs/dimension.js.map +1 -1
  13. package/source-cjs/jsonArrayPack.js +7 -25
  14. package/source-cjs/jsonArrayPack.js.map +1 -1
  15. package/source-cjs/prefix.js +12 -26
  16. package/source-cjs/prefix.js.map +1 -1
  17. package/source-cjs/prefixTables.js +10 -24
  18. package/source-cjs/prefixTables.js.map +1 -1
  19. package/source-cjs/ucumFunctions.js +35 -32
  20. package/source-cjs/ucumFunctions.js.map +1 -1
  21. package/source-cjs/ucumInternalUtils.js +5 -13
  22. package/source-cjs/ucumInternalUtils.js.map +1 -1
  23. package/source-cjs/ucumJsonDefs.js +1 -16
  24. package/source-cjs/ucumJsonDefs.js.map +1 -1
  25. package/source-cjs/ucumLhcUtils.js +117 -138
  26. package/source-cjs/ucumLhcUtils.js.map +1 -1
  27. package/source-cjs/ucumPkg.js +1 -6
  28. package/source-cjs/ucumPkg.js.map +1 -1
  29. package/source-cjs/ucumXmlDocument.js +162 -184
  30. package/source-cjs/ucumXmlDocument.js.map +1 -1
  31. package/source-cjs/unit.js +97 -181
  32. package/source-cjs/unit.js.map +1 -1
  33. package/source-cjs/unitString.js +537 -618
  34. package/source-cjs/unitString.js.map +1 -1
  35. package/source-cjs/unitTables.js +33 -139
  36. package/source-cjs/unitTables.js.map +1 -1
@@ -4,26 +4,18 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.UnitString = void 0;
7
-
8
7
  var intUtils_ = _interopRequireWildcard(require("./ucumInternalUtils.js"));
9
-
10
8
  function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
11
-
12
9
  function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
13
-
14
10
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
15
-
16
11
  /**
17
12
  * This class handles the parsing of a unit string into a unit object
18
13
  */
19
- var Ucum = require('./config.js').Ucum;
20
14
 
15
+ var Ucum = require('./config.js').Ucum;
21
16
  var Unit = require('./unit.js').Unit;
22
-
23
17
  var UnitTables = require('./unitTables.js').UnitTables;
24
-
25
18
  var PrefixTables = require('./prefixTables.js').PrefixTables;
26
-
27
19
  class UnitString {
28
20
  /**
29
21
  * Constructor
@@ -31,43 +23,52 @@ class UnitString {
31
23
  constructor() {
32
24
  // Get instances of the unit and prefix tables and the utilities
33
25
  this.utabs_ = UnitTables.getInstance();
34
- this.pfxTabs_ = PrefixTables.getInstance(); // Set emphasis characters to defaults. These are used to emphasize
26
+ this.pfxTabs_ = PrefixTables.getInstance();
27
+
28
+ // Set emphasis characters to defaults. These are used to emphasize
35
29
  // certain characters or strings in user messages. They can be reset in
36
30
  // the useHTMLInMessages method.
37
-
38
31
  this.openEmph_ = Ucum.openEmph_;
39
- this.closeEmph_ = Ucum.closeEmph_; // Set the braces message to blank. This message is displayed for each
32
+ this.closeEmph_ = Ucum.closeEmph_;
33
+
34
+ // Set the braces message to blank. This message is displayed for each
40
35
  // validation request on the web page, but is included separately as
41
36
  // a note on the validation spreadsheet. The useBraceMsgForEachString
42
37
  // method should be used to set the message to be displayed for each
43
38
  // unit string.
39
+ this.bracesMsg_ = '';
44
40
 
45
- this.bracesMsg_ = ''; // Set the flags used, with indices, as place holders in unit strings
41
+ // Set the flags used, with indices, as place holders in unit strings
46
42
  // for parenthetical strings and strings within braces.
47
-
48
43
  this.parensFlag_ = "parens_placeholder"; // in lieu of Jehoshaphat
49
-
50
44
  this.pFlagLen_ = this.parensFlag_.length;
51
45
  this.braceFlag_ = "braces_placeholder"; // in lieu of Nebuchadnezzar
46
+ this.bFlagLen_ = this.braceFlag_.length;
52
47
 
53
- this.bFlagLen_ = this.braceFlag_.length; // Initialize the message start/end strings, which will be set when
48
+ // Initialize the message start/end strings, which will be set when
54
49
  // parseString is called.
55
-
56
50
  this.vcMsgStart_ = null;
57
- this.vcMsgEnd_ = null; // Arrays used by multiple methods within this class to hold persistent
51
+ this.vcMsgEnd_ = null;
52
+
53
+ // Arrays used by multiple methods within this class to hold persistent
58
54
  // data. Just gets too bulky to pass these guys around.
59
- // Messages to be returned to the calling function
60
55
 
61
- this.retMsg_ = []; // Units for parenthetical unit strings
56
+ // Messages to be returned to the calling function
57
+ this.retMsg_ = [];
62
58
 
63
- this.parensUnits_ = []; // annotation text for annotations found in unit strings
59
+ // Units for parenthetical unit strings
60
+ this.parensUnits_ = [];
64
61
 
65
- this.annotations_ = []; // suggestions for unit strings that for which no unit was found
62
+ // annotation text for annotations found in unit strings
63
+ this.annotations_ = [];
66
64
 
65
+ // suggestions for unit strings that for which no unit was found
67
66
  this.suggestions = [];
68
67
  } // end constructor
68
+
69
69
  // The start of an error message about an invalid annotation character.
70
70
 
71
+ // A regular expression for validating annotation strings.
71
72
 
72
73
  /**
73
74
  * Sets the emphasis strings to the HTML used in the webpage display - or
@@ -93,11 +94,10 @@ class UnitString {
93
94
  * @param use flag indicating whether or not to use the braces message;
94
95
  * defaults to true
95
96
  */
96
-
97
-
98
97
  useBraceMsgForEachString(use) {
99
98
  if (use === undefined || use) this.bracesMsg_ = Ucum.bracesMsg_;else this.bracesMsg_ = '';
100
99
  }
100
+
101
101
  /**
102
102
  * Parses a unit string, returns a unit, a possibly updated version of
103
103
  * the string passed in, and messages and suggestions where appropriate.
@@ -136,15 +136,12 @@ class UnitString {
136
136
  * was found or if suggestions were not requested.
137
137
  * @throws an error if nothing was specified.
138
138
  */
139
-
140
-
141
139
  parseString(uStr, valConv, suggest) {
142
- uStr = uStr.trim(); // Make sure we have something to work with
143
-
140
+ uStr = uStr.trim();
141
+ // Make sure we have something to work with
144
142
  if (uStr === '' || uStr === null) {
145
143
  throw new Error('Please specify a unit expression to be validated.');
146
144
  }
147
-
148
145
  if (valConv === 'validate') {
149
146
  this.vcMsgStart_ = Ucum.valMsgStart_;
150
147
  this.vcMsgEnd_ = Ucum.valMsgEnd_;
@@ -152,18 +149,18 @@ class UnitString {
152
149
  this.vcMsgStart_ = Ucum.cnvMsgStart_;
153
150
  this.vcMsgEnd_ = Ucum.cnvMsgEnd_;
154
151
  }
155
-
156
152
  if (suggest === undefined || suggest === false) {
157
153
  this.suggestions_ = null;
158
154
  } else {
159
155
  this.suggestions_ = [];
160
156
  }
161
-
162
157
  this.retMsg_ = [];
163
158
  this.parensUnits_ = [];
164
159
  this.annotations_ = [];
165
160
  let origString = uStr;
166
- let retObj = []; // Extract any annotations, i.e., text enclosed in braces ({}) from the
161
+ let retObj = [];
162
+
163
+ // Extract any annotations, i.e., text enclosed in braces ({}) from the
167
164
  // string before further processing. Store each one in this.annotations_
168
165
  // array and put a placeholder in the string for the annotation. Do
169
166
  // this before other processing in case an annotation contains characters
@@ -171,41 +168,40 @@ class UnitString {
171
168
  // subsequent processing.
172
169
 
173
170
  uStr = this._getAnnotations(uStr);
174
-
175
171
  if (this.retMsg_.length > 0) {
176
172
  retObj[0] = null;
177
173
  retObj[1] = null;
178
174
  } else {
179
175
  // Flag used to block further processing on an unrecoverable error
180
- let endProcessing = this.retMsg_.length > 0; // First check for one of the "special" units. If it's one of those, put
176
+ let endProcessing = this.retMsg_.length > 0;
177
+
178
+ // First check for one of the "special" units. If it's one of those, put
181
179
  // in a substitution phrase for it to avoid having it separated on its
182
180
  // embedded operator. This will only happen, by the way, if it is
183
181
  // preceded by a prefix or followed by an operator and another unit.
184
-
185
182
  let sUnit = null;
186
-
187
183
  for (sUnit in Ucum.specUnits_) {
188
184
  while (uStr.indexOf(sUnit) !== -1) uStr = uStr.replace(sUnit, Ucum.specUnits_[sUnit]);
189
- } // Check for spaces and throw an error if any are found. The spec
185
+ }
186
+
187
+ // Check for spaces and throw an error if any are found. The spec
190
188
  // explicitly forbids spaces except in annotations, which is why any
191
189
  // annotations are extracted before this check is made.
192
-
193
-
194
190
  if (uStr.indexOf(' ') > -1) {
195
191
  throw new Error('Blank spaces are not allowed in unit expressions.');
196
192
  } // end if blanks were found in the string
193
+
197
194
  // assign the array returned to retObj. It will contain 2 elements:
198
195
  // the unit returned in position 0; and the origString (possibly
199
196
  // modified) in position 1. The origString in position 1 will not
200
197
  // be changed by subsequent processing.
201
-
202
-
203
198
  retObj = this._parseTheString(uStr, origString);
204
- let finalUnit = retObj[0]; // Do a final check to make sure that finalUnit is a unit and not
199
+ let finalUnit = retObj[0];
200
+
201
+ // Do a final check to make sure that finalUnit is a unit and not
205
202
  // just a number. Something like "8/{HCP}" will return a "unit" of 8
206
203
  // - which is not a unit. Hm - evidently it is. So just create a unit
207
204
  // object for it.
208
-
209
205
  if (intUtils_.isIntegerUnit(finalUnit) || typeof finalUnit === 'number') {
210
206
  finalUnit = new Unit({
211
207
  'csCode_': origString,
@@ -215,10 +211,8 @@ class UnitString {
215
211
  });
216
212
  retObj[0] = finalUnit;
217
213
  } // end final check
218
-
219
214
  } // end if no annotation errors were found
220
215
 
221
-
222
216
  retObj[2] = this.retMsg_;
223
217
  if (this.suggestions_ && this.suggestions_.length > 0) retObj[3] = this.suggestions_;
224
218
  return retObj;
@@ -251,86 +245,83 @@ class UnitString {
251
245
  * the this.suggestions_ array may be populated by methods called within
252
246
  * this one
253
247
  */
254
-
255
-
256
248
  _parseTheString(uStr, origString) {
257
249
  // Unit to be returned
258
- let finalUnit = null; // Flag used to block further processing on an unrecoverable error
250
+ let finalUnit = null;
251
+
252
+ // Flag used to block further processing on an unrecoverable error
253
+ let endProcessing = this.retMsg_.length > 0;
259
254
 
260
- let endProcessing = this.retMsg_.length > 0; // Call _processParens to search for and process any/all parenthetical
255
+ // Call _processParens to search for and process any/all parenthetical
261
256
  // strings in uStr. Units created for parenthetical strings will be
262
257
  // stored in the this.parensUnits_ array.
263
-
264
258
  let parensResp = this._processParens(uStr, origString);
259
+ endProcessing = parensResp[2];
265
260
 
266
- endProcessing = parensResp[2]; // The array used to hold the units and their operators.
267
-
268
- let uArray = []; // Continue if we didn't hit a problem
261
+ // The array used to hold the units and their operators.
262
+ let uArray = [];
269
263
 
264
+ // Continue if we didn't hit a problem
270
265
  if (!endProcessing) {
271
266
  uStr = parensResp[0];
272
- origString = parensResp[1]; // Call _makeUnitsArray to convert the string to an array of unit
273
- // descriptors with operators.
267
+ origString = parensResp[1];
274
268
 
269
+ // Call _makeUnitsArray to convert the string to an array of unit
270
+ // descriptors with operators.
275
271
  let mkUArray = this._makeUnitsArray(uStr, origString);
276
-
277
272
  endProcessing = mkUArray[2];
278
-
279
273
  if (!endProcessing) {
280
274
  uArray = mkUArray[0];
281
- origString = mkUArray[1]; // Create a unit object out of each un element
282
-
275
+ origString = mkUArray[1];
276
+ // Create a unit object out of each un element
283
277
  let uLen = uArray.length;
284
-
285
278
  for (let u1 = 0; u1 < uLen; u1++) {
286
279
  //for (let u1 = 0; u1 < uLen && !endProcessing; u1++) {
287
- let curCode = uArray[u1]['un']; // Determine the type of the "un" attribute of the current array element
280
+ let curCode = uArray[u1]['un'];
281
+
282
+ // Determine the type of the "un" attribute of the current array element
283
+
288
284
  // Check to see if it's a number. If so write the number version of
289
285
  // the number back to the "un" attribute and move on
290
-
291
286
  if (intUtils_.isIntegerUnit(curCode)) {
292
287
  uArray[u1]['un'] = Number(curCode);
293
288
  } else {
294
289
  // The current unit array element is a string. Check now to see
295
290
  // if it is or contains a parenthesized unit from this.parensUnits_.
296
291
  // If so, call _getParens to process the string and get the unit.
292
+
297
293
  if (curCode.indexOf(this.parensFlag_) >= 0) {
298
- let parenUnit = this._getParensUnit(curCode, origString); // if we couldn't process the string, set the end flag and bypass
294
+ let parenUnit = this._getParensUnit(curCode, origString);
295
+ // if we couldn't process the string, set the end flag and bypass
299
296
  // further processing.
297
+ if (!endProcessing) endProcessing = parenUnit[1];
300
298
 
301
-
302
- if (!endProcessing) endProcessing = parenUnit[1]; // If we're good, put the unit in the uArray and replace the
299
+ // If we're good, put the unit in the uArray and replace the
303
300
  // curCode, which contains the parentheses placeholders, etc.,
304
301
  // with the unit's code - including any substitutions.
305
-
306
302
  if (!endProcessing) {
307
303
  uArray[u1]['un'] = parenUnit[0];
308
304
  }
309
305
  } // end if the curCode contains a parenthesized unit
306
+
310
307
  // Else it's not a parenthetical unit and not a number. Call
311
308
  // _makeUnit to create a unit for it.
312
309
  else {
313
- let uRet = this._makeUnit(curCode, origString); // If we didn't get a unit, set the endProcessing flag.
314
-
315
-
316
- if (uRet[0] === null) {
317
- endProcessing = true;
318
- } else {
319
- uArray[u1]['un'] = uRet[0];
320
- origString = uRet[1];
321
- }
322
- } // end if the curCode is not a parenthetical expression
323
-
310
+ let uRet = this._makeUnit(curCode, origString);
311
+ // If we didn't get a unit, set the endProcessing flag.
312
+ if (uRet[0] === null) {
313
+ endProcessing = true;
314
+ } else {
315
+ uArray[u1]['un'] = uRet[0];
316
+ origString = uRet[1];
317
+ }
318
+ } // end if the curCode is not a parenthetical expression
324
319
  } // end if the "un" array is a not a number
325
-
326
320
  } // end do for each element in the units array
327
-
328
321
  } // end if _makeUnitsArray did not return an error
329
-
330
322
  } // end if _processParens did not find an error that causes a stop
331
- // If we're still good, continue
332
-
333
323
 
324
+ // If we're still good, continue
334
325
  if (!endProcessing) {
335
326
  // Process the units (and numbers) to create one final unit object
336
327
  if ((uArray[0] === null || uArray[0] === ' ' || uArray[0]['un'] === undefined || uArray[0]['un'] === null) && this.retMsg_.length === 0) {
@@ -339,11 +330,9 @@ class UnitString {
339
330
  endProcessing = true;
340
331
  }
341
332
  }
342
-
343
333
  if (!endProcessing) {
344
334
  finalUnit = this._performUnitArithmetic(uArray, origString);
345
335
  }
346
-
347
336
  return [finalUnit, origString];
348
337
  } // end _parseTheString
349
338
 
@@ -360,20 +349,16 @@ class UnitString {
360
349
  * (informational, error or warning) generated by this or called methods
361
350
  * the this.annotations_ array is populated by this method
362
351
  */
363
-
364
-
365
352
  _getAnnotations(uString) {
366
353
  let openBrace = uString.indexOf('{');
367
-
368
354
  while (openBrace >= 0) {
369
355
  let closeBrace = uString.indexOf('}');
370
-
371
356
  if (closeBrace < 0) {
372
357
  this.retMsg_.push('Missing closing brace for annotation starting at ' + this.openEmph_ + uString.substr(openBrace) + this.closeEmph_);
373
358
  openBrace = -1;
374
359
  } else {
375
- let braceStr = uString.substring(openBrace, closeBrace + 1); // Check for valid characters in the annotation.
376
-
360
+ let braceStr = uString.substring(openBrace, closeBrace + 1);
361
+ // Check for valid characters in the annotation.
377
362
  if (!UnitString.VALID_ANNOTATION_REGEX.test(braceStr)) {
378
363
  this.retMsg_.push(UnitString.INVALID_ANNOTATION_CHAR_MSG + this.openEmph_ + braceStr + this.closeEmph_);
379
364
  openBrace = -1; // end search for annotations
@@ -385,15 +370,13 @@ class UnitString {
385
370
  }
386
371
  }
387
372
  } // end do while we have an opening brace
388
- // check for a stray/unmatched closing brace
389
-
390
373
 
374
+ // check for a stray/unmatched closing brace
391
375
  if (this.retMsg_.length == 0) {
392
376
  // if there were no other errors above
393
377
  let closeBrace = uString.indexOf('}');
394
378
  if (closeBrace >= 0) this.retMsg_.push('Missing opening brace for closing brace found at ' + this.openEmph_ + uString.substring(0, closeBrace + 1) + this.closeEmph_);
395
379
  }
396
-
397
380
  return uString;
398
381
  } // end _getAnnotations
399
382
 
@@ -427,108 +410,101 @@ class UnitString {
427
410
  * this this.parensUnits_ array will be populated with units found for
428
411
  * parenthetical unit strings
429
412
  */
430
-
431
-
432
413
  _processParens(uString, origString) {
433
414
  // Unit strings array and index
434
415
  let uStrArray = [];
435
416
  let uStrAryPos = 0;
436
417
  let stopProcessing = false;
437
- let pu = this.parensUnits_.length; // Count of characters trimmed off the beginning of the unit string (uString)
418
+ let pu = this.parensUnits_.length;
419
+
420
+ // Count of characters trimmed off the beginning of the unit string (uString)
438
421
  // as units are removed from it; used for error messages to provide
439
422
  // context.
423
+ let trimmedCt = 0;
440
424
 
441
- let trimmedCt = 0; // Break the unit string into pieces that consist of text outside of
425
+ // Break the unit string into pieces that consist of text outside of
442
426
  // parenthetical strings and placeholders for the parenthetical units.
443
427
  // This method is called recursively for parenthetical strings and the units
444
428
  // returned are stored in the this.parensUnits_ array.
445
-
446
429
  while (uString !== "" && !stopProcessing) {
447
430
  let openCt = 0;
448
431
  let closeCt = 0;
449
- let openPos = uString.indexOf('('); // If an opening parenthesis was not found, check for an unmatched
432
+ let openPos = uString.indexOf('(');
433
+
434
+ // If an opening parenthesis was not found, check for an unmatched
450
435
  // close parenthesis. If one was found report the error and end
451
436
  // processing.
452
-
453
437
  if (openPos < 0) {
454
438
  let closePos = uString.indexOf(')');
455
-
456
439
  if (closePos >= 0) {
457
440
  let theMsg = `Missing open parenthesis for close ` + `parenthesis at ${uString.substring(0, closePos + trimmedCt)}` + `${this.openEmph_}${uString.substr(closePos, 1)}${this.closeEmph_}`;
458
-
459
441
  if (closePos < uString.length - 1) {
460
442
  theMsg += `${uString.substr(closePos + 1)}`;
461
443
  }
462
-
463
444
  this.retMsg_.push(theMsg);
464
445
  uStrArray[uStrAryPos] = uString;
465
446
  stopProcessing = true;
466
447
  } // end if a close parenthesis was found
448
+
467
449
  // If no parentheses were found in the current unit string, transfer
468
450
  // it to the units array and blank out the string, which will end
469
451
  // the search for parenthetical units.
470
452
  else {
471
- uStrArray[uStrAryPos] = uString;
472
- uString = "";
473
- } // end if no close parenthesis was found
474
-
453
+ uStrArray[uStrAryPos] = uString;
454
+ uString = "";
455
+ } // end if no close parenthesis was found
475
456
  } // end if no open parenthesis was found
457
+
476
458
  // Otherwise an open parenthesis was found. Process the string that
477
459
  // includes the parenthetical group
478
460
  else {
479
- openCt += 1; // Write the text before the parentheses (if any) to the unit strings array
480
-
481
- let uLen = uString.length;
482
-
483
- if (openPos > 0) {
484
- uStrArray[uStrAryPos++] = uString.substr(0, openPos);
485
- } // Find the matching closePos, i.e., the one that closes the
486
- // parenthetical group that this one opens. Look also for
487
- // another open parenthesis, in case this includes nested parenthetical
488
- // strings. This continues until it finds the same number of close
489
- // parentheses as open parentheses, or runs out of string to check.
490
- // In the case of nested parentheses this will identify the outer set
491
- // of parentheses.
492
-
493
-
494
- let closePos = 0;
495
- let c = openPos + 1;
496
-
497
- for (; c < uLen && openCt != closeCt; c++) {
498
- if (uString[c] === '(') openCt += 1;else if (uString[c] === ')') closeCt += 1;
499
- } // Put a placeholder for the group in the unit strings array and recursively
500
- // call this method for the parenthetical group. Put the unit returned
501
- // in this.parensUnits_. Set the unit string to whatever follows
502
- // the position of the closing parenthesis for this group, to be
503
- // processed by the next iteration of this loop. If there's nothing
504
- // left uString is set to "".
505
-
506
-
507
- if (openCt === closeCt) {
508
- closePos = c;
509
- uStrArray[uStrAryPos++] = this.parensFlag_ + pu.toString() + this.parensFlag_;
461
+ openCt += 1;
462
+ // Write the text before the parentheses (if any) to the unit strings array
463
+ let uLen = uString.length;
464
+ if (openPos > 0) {
465
+ uStrArray[uStrAryPos++] = uString.substr(0, openPos);
466
+ }
510
467
 
511
- let parseResp = this._parseTheString(uString.substring(openPos + 1, closePos - 1), origString);
468
+ // Find the matching closePos, i.e., the one that closes the
469
+ // parenthetical group that this one opens. Look also for
470
+ // another open parenthesis, in case this includes nested parenthetical
471
+ // strings. This continues until it finds the same number of close
472
+ // parentheses as open parentheses, or runs out of string to check.
473
+ // In the case of nested parentheses this will identify the outer set
474
+ // of parentheses.
475
+ let closePos = 0;
476
+ let c = openPos + 1;
477
+ for (; c < uLen && openCt != closeCt; c++) {
478
+ if (uString[c] === '(') openCt += 1;else if (uString[c] === ')') closeCt += 1;
479
+ }
512
480
 
513
- if (parseResp[0] === null) stopProcessing = true;else {
514
- origString = parseResp[1];
515
- this.parensUnits_[pu++] = parseResp[0];
516
- uString = uString.substr(closePos);
517
- trimmedCt = closePos;
518
- }
519
- } // end if the number of open and close parentheses matched
520
- // If the number of open and close parentheses doesn't match, indicate
521
- // an error.
522
- else {
523
- uStrArray.push(origString.substr(openPos));
524
- this.retMsg_.push(`Missing close parenthesis for open parenthesis at ` + `${origString.substring(0, openPos + trimmedCt)}` + `${this.openEmph_}${origString.substr(openPos, 1)}` + `${this.closeEmph_}${origString.substr(openPos + 1)}`);
525
- stopProcessing = true;
526
- }
527
- } // end if an open parenthesis was found
481
+ // Put a placeholder for the group in the unit strings array and recursively
482
+ // call this method for the parenthetical group. Put the unit returned
483
+ // in this.parensUnits_. Set the unit string to whatever follows
484
+ // the position of the closing parenthesis for this group, to be
485
+ // processed by the next iteration of this loop. If there's nothing
486
+ // left uString is set to "".
487
+ if (openCt === closeCt) {
488
+ closePos = c;
489
+ uStrArray[uStrAryPos++] = this.parensFlag_ + pu.toString() + this.parensFlag_;
490
+ let parseResp = this._parseTheString(uString.substring(openPos + 1, closePos - 1), origString);
491
+ if (parseResp[0] === null) stopProcessing = true;else {
492
+ origString = parseResp[1];
493
+ this.parensUnits_[pu++] = parseResp[0];
494
+ uString = uString.substr(closePos);
495
+ trimmedCt = closePos;
496
+ }
497
+ } // end if the number of open and close parentheses matched
528
498
 
499
+ // If the number of open and close parentheses doesn't match, indicate
500
+ // an error.
501
+ else {
502
+ uStrArray.push(origString.substr(openPos));
503
+ this.retMsg_.push(`Missing close parenthesis for open parenthesis at ` + `${origString.substring(0, openPos + trimmedCt)}` + `${this.openEmph_}${origString.substr(openPos, 1)}` + `${this.closeEmph_}${origString.substr(openPos + 1)}`);
504
+ stopProcessing = true;
505
+ }
506
+ } // end if an open parenthesis was found
529
507
  } // end do while the input string is not empty
530
-
531
-
532
508
  if (stopProcessing) this.parensUnits_ = [];
533
509
  return [uStrArray.join(''), origString, stopProcessing];
534
510
  } // end _processParens
@@ -552,47 +528,44 @@ class UnitString {
552
528
  * the this.retMsg_ array will be updated with any user messages
553
529
  * (informational, error or warning) generated by this or called methods
554
530
  */
555
-
556
-
557
531
  _makeUnitsArray(uStr, origString) {
558
532
  // Separate the string into pieces based on delimiters / (division) and .
559
533
  // (multiplication). The idea is to get an array of units on which we
560
534
  // can then perform any operations (prefixes, multiplication, division).
535
+
561
536
  let uArray1 = uStr.match(/([./]|[^./]+)/g);
562
537
  let endProcessing = false;
563
538
  let uArray = [];
564
- let startNumCheck = /(^[0-9]+)(\[?[a-zA-Z\_0-9a-zA-Z\_]+\]?$)/; // If the first element in the array is the division operator (/), the
539
+ let startNumCheck = /(^[0-9]+)(\[?[a-zA-Z\_0-9a-zA-Z\_]+\]?$)/;
540
+
541
+ // If the first element in the array is the division operator (/), the
565
542
  // string started with '/'. Add a first element containing 1 to the
566
543
  // array, which will cause the correct computation to be performed (inversion).
567
-
568
544
  if (uArray1[0] === "/") {
569
545
  uArray1.unshift("1");
570
- } // If the first element in the array is the multiplication operator (.)
546
+ }
547
+ // If the first element in the array is the multiplication operator (.)
571
548
  // return an error.
572
549
  else if (uArray1[0] === '.') {
573
- this.retMsg_.push(`${origString} is not a valid UCUM code. ` + `The multiplication operator at the beginning of the expression is ` + `not valid. A multiplication operator must appear only between ` + `two codes.`);
574
- endProcessing = true;
575
- }
576
-
550
+ this.retMsg_.push(`${origString} is not a valid UCUM code. ` + `The multiplication operator at the beginning of the expression is ` + `not valid. A multiplication operator must appear only between ` + `two codes.`);
551
+ endProcessing = true;
552
+ }
577
553
  if (!endProcessing) {
578
554
  // Check to see if there is a number preceding a unit code, e.g., 2mg
579
555
  // If so, update the first element to remove the number (2mg -> mg) and
580
556
  // add two elements to the beginning of the array - the number and the
581
557
  // multiplication operator.
558
+
582
559
  if (!intUtils_.isNumericString(uArray1[0])) {
583
560
  let numRes = uArray1[0].match(startNumCheck);
584
-
585
561
  if (numRes && numRes.length === 3 && numRes[1] !== '' && numRes[2] !== '' && numRes[2].indexOf(this.braceFlag_) !== 0) {
586
562
  let dispVal = numRes[2];
587
-
588
563
  if (!endProcessing && numRes[2].indexOf(this.parensFlag_) !== -1) {
589
564
  let parensback = this._getParensUnit(numRes[2], origString);
590
-
591
565
  numRes[2] = parensback[0]['csCode_'];
592
566
  dispVal = `(${numRes[2]})`;
593
567
  endProcessing = parensback[1];
594
568
  }
595
-
596
569
  if (!endProcessing) {
597
570
  this.retMsg_.push(`${numRes[1]}${dispVal} is not a valid UCUM code.` + ` ${this.vcMsgStart_}${numRes[1]}.${dispVal}${this.vcMsgEnd_}`);
598
571
  origString = origString.replace(`${numRes[1]}${dispVal}`, `${numRes[1]}.${dispVal}`);
@@ -601,6 +574,7 @@ class UnitString {
601
574
  }
602
575
  }
603
576
  } // end if the first element is not a number (only)
577
+
604
578
  // Create an array of unit/operator objects. The unit is, for now, the
605
579
  // string containing the unit code (e.g., Hz for hertz) including
606
580
  // a possible prefix and exponent. The operator is the operator to be
@@ -608,21 +582,18 @@ class UnitString {
608
582
  // us two objects. The first will have a unit of a, and a blank operator
609
583
  // (because it's the first unit). The second would have a unit of b
610
584
  // and the multiplication operator (.).
611
-
612
-
613
585
  if (!endProcessing) {
614
586
  let u1 = uArray1.length;
615
587
  uArray = [{
616
588
  op: "",
617
589
  un: uArray1[0]
618
590
  }];
619
-
620
591
  for (let n = 1; n < u1; n++) {
621
592
  // check to make sure that we don't have two operators together, e.g.,
622
593
  // mg./K. If so, let the user know the problem.
623
- let theOp = uArray1[n++]; // oh wait - check to make sure something is even there, that the
594
+ let theOp = uArray1[n++];
595
+ // oh wait - check to make sure something is even there, that the
624
596
  // user didn't end the expression with an operator.
625
-
626
597
  if (!uArray1[n]) {
627
598
  this.retMsg_.push(`${origString} is not a valid UCUM code. ` + `It is terminated with the operator ${this.openEmph_}` + `${theOp}${this.closeEmph_}.`);
628
599
  n = u1;
@@ -642,63 +613,52 @@ class UnitString {
642
613
  // 2.kJ.
643
614
  if (!intUtils_.isNumericString(uArray1[n])) {
644
615
  let numRes2 = uArray1[n].match(startNumCheck);
645
-
646
616
  if (numRes2 && numRes2.length === 3 && numRes2[1] !== '' && numRes2[2] !== '' && numRes2[2].indexOf(this.braceFlag_) !== 0) {
647
617
  let invalidString = numRes2[0];
648
-
649
618
  if (!endProcessing && numRes2[2].indexOf(this.parensFlag_) !== -1) {
650
619
  let parensback = this._getParensUnit(numRes2[2], origString);
651
-
652
620
  numRes2[2] = parensback[0]['csCode_'];
653
621
  invalidString = `(${numRes2[2]})`;
654
622
  endProcessing = parensback[1];
655
-
656
623
  if (!endProcessing) {
657
624
  this.retMsg_.push(`${numRes2[1]}${invalidString} is not a ` + `valid UCUM code. ${this.vcMsgStart_}${numRes2[1]}.${invalidString}` + `${this.vcMsgEnd_}`);
658
625
  let parensString = `(${numRes2[1]}.${invalidString})`;
659
626
  origString = origString.replace(`${numRes2[1]}${invalidString}`, parensString);
660
-
661
627
  let nextParens = this._processParens(parensString, origString);
662
-
663
628
  endProcessing = nextParens[2];
664
-
665
629
  if (!endProcessing) {
666
630
  uArray.push({
667
631
  op: theOp,
668
632
  un: nextParens[0]
669
633
  });
670
- } //uArray.push({op: '.', un: numRes2[2]});
671
-
634
+ }
635
+ //uArray.push({op: '.', un: numRes2[2]});
672
636
  }
673
637
  } // end if the string represents a parenthesized unit
674
638
  else {
675
- let parensStr = '(' + numRes2[1] + '.' + numRes2[2] + ')';
676
-
677
- let parensResp = this._processParens(parensStr, origString); // if a "stop processing" flag was returned, set the n index to end
678
- // the loop and set the endProcessing flag
679
-
680
-
681
- if (parensResp[2]) {
682
- n = u1;
683
- endProcessing = true;
684
- } else {
685
- this.retMsg_.push(`${numRes2[0]} is not a ` + `valid UCUM code. ${this.vcMsgStart_}${numRes2[1]}.${numRes2[2]}` + `${this.vcMsgEnd_}`);
686
- origString = origString.replace(numRes2[0], parensStr);
687
- uArray.push({
688
- op: theOp,
689
- un: parensResp[0]
690
- });
691
- } // end if no error on the processParens call
692
-
693
- } // end if the string does not represent a parenthesized unit
694
-
639
+ let parensStr = '(' + numRes2[1] + '.' + numRes2[2] + ')';
640
+ let parensResp = this._processParens(parensStr, origString);
641
+ // if a "stop processing" flag was returned, set the n index to end
642
+ // the loop and set the endProcessing flag
643
+ if (parensResp[2]) {
644
+ n = u1;
645
+ endProcessing = true;
646
+ } else {
647
+ this.retMsg_.push(`${numRes2[0]} is not a ` + `valid UCUM code. ${this.vcMsgStart_}${numRes2[1]}.${numRes2[2]}` + `${this.vcMsgEnd_}`);
648
+ origString = origString.replace(numRes2[0], parensStr);
649
+ uArray.push({
650
+ op: theOp,
651
+ un: parensResp[0]
652
+ });
653
+ } // end if no error on the processParens call
654
+ } // end if the string does not represent a parenthesized unit
695
655
  } // end if the string is a number followed by a string
696
656
  else {
697
- uArray.push({
698
- op: theOp,
699
- un: uArray1[n]
700
- });
701
- }
657
+ uArray.push({
658
+ op: theOp,
659
+ un: uArray1[n]
660
+ });
661
+ }
702
662
  } else {
703
663
  uArray.push({
704
664
  op: theOp,
@@ -706,14 +666,9 @@ class UnitString {
706
666
  });
707
667
  }
708
668
  } // end if there isn't a missing operator or unit code
709
-
710
669
  } // end do for each element in uArray1
711
-
712
670
  } // end if a processing error didn't occur in getParensUnit
713
-
714
671
  } // end if the string did not begin with a '.' with no following digit
715
-
716
-
717
672
  return [uArray, origString, endProcessing];
718
673
  } // end _makeUnitsArray
719
674
 
@@ -745,53 +700,50 @@ class UnitString {
745
700
  * @throws an error if an invalid parensUnit index was found. This is
746
701
  * a processing error.
747
702
  */
748
-
749
-
750
703
  _getParensUnit(pStr, origString) {
751
704
  let endProcessing = false;
752
705
  let retAry = [];
753
706
  let retUnit = null;
754
707
  let befAnnoText = null;
755
- let aftAnnoText = null; // Get the location of the flags. We're assuming there are only two
708
+ let aftAnnoText = null;
709
+
710
+ // Get the location of the flags. We're assuming there are only two
756
711
  // because _processParens takes care of nesting. By the time we get
757
712
  // here we should not be looking a nested parens. Also get any text
758
713
  // before and after the parentheses. Once we get the unit we update
759
714
  // the input string with the unit's csCode_, which will wipe out any
760
715
  // before and after text
761
-
762
716
  let psIdx = pStr.indexOf(this.parensFlag_);
763
717
  let befText = null;
764
-
765
718
  if (psIdx > 0) {
766
719
  befText = pStr.substr(0, psIdx - 1);
767
720
  }
768
-
769
721
  let peIdx = pStr.lastIndexOf(this.parensFlag_);
770
722
  let aftText = null;
771
-
772
723
  if (peIdx + this.pFlagLen_ < pStr.length) {
773
724
  aftText = pStr.substr(peIdx + this.pFlagLen_);
774
- } // Get the text between the flags
725
+ }
775
726
 
727
+ // Get the text between the flags
728
+ let pNumText = pStr.substring(psIdx + this.pFlagLen_, peIdx);
776
729
 
777
- let pNumText = pStr.substring(psIdx + this.pFlagLen_, peIdx); // Make sure the index is a number, and if it is, get the unit from the
730
+ // Make sure the index is a number, and if it is, get the unit from the
778
731
  // this.parensUnits_ array
779
-
780
732
  if (intUtils_.isNumericString(pNumText)) {
781
733
  retUnit = this.parensUnits_[Number(pNumText)];
782
-
783
734
  if (!intUtils_.isIntegerUnit(retUnit)) {
784
735
  pStr = retUnit.csCode_;
785
736
  } else {
786
737
  pStr = retUnit;
787
738
  }
788
- } // If it's not a number, it's a programming error. Throw a fit.
739
+ }
740
+ // If it's not a number, it's a programming error. Throw a fit.
789
741
  else {
790
- throw new Error(`Processing error - invalid parens number ${pNumText} ` + `found in ${pStr}.`);
791
- } // If there's something in front of the starting parentheses flag, check to
792
- // see if it's a number or an annotation.
793
-
742
+ throw new Error(`Processing error - invalid parens number ${pNumText} ` + `found in ${pStr}.`);
743
+ }
794
744
 
745
+ // If there's something in front of the starting parentheses flag, check to
746
+ // see if it's a number or an annotation.
795
747
  if (befText) {
796
748
  // If it's a number, assume that multiplication was assumed
797
749
  if (intUtils_.isNumericString(befText)) {
@@ -804,81 +756,79 @@ class UnitString {
804
756
  this.retMsg_.push(`${befText}${pStr} is not a valid UCUM code.\n` + this.vcMsgStart_ + pStr + this.vcMsgEnd_);
805
757
  } else {
806
758
  if (befText.indexOf(this.braceFlag_) >= 0) {
807
- let annoRet = this._getAnnoText(befText, origString); // if we found not only an annotation, but text before or after
759
+ let annoRet = this._getAnnoText(befText, origString);
760
+ // if we found not only an annotation, but text before or after
808
761
  // the annotation (remembering that this is all before the
809
762
  // parentheses) throw an error - because we don't know what
810
763
  // to do with it. Could it be missing an operator?
811
-
812
-
813
764
  if (annoRet[1] || annoRet[2]) {
814
765
  throw new Error(`Text found before the parentheses (` + `${befText}) included an annotation along with other text ` + `for parenthetical unit ${retUnit.csCode_}`);
815
- } // Otherwise put the annotation after the unit string and note
766
+ }
767
+ // Otherwise put the annotation after the unit string and note
816
768
  // the misplacement.
817
-
818
-
819
769
  pStr += annoRet[0];
820
770
  this.retMsg_.push(`The annotation ${annoRet[0]} before the unit ` + `code is invalid.\n` + this.vcMsgStart_ + pStr + this.vcMsgEnd_);
821
- } // else the text before the parentheses is neither a number nor
771
+ }
772
+ // else the text before the parentheses is neither a number nor
822
773
  // an annotation. If suggestions were NOT requested, record an
823
774
  // error.
824
775
  else if (!this.suggestions_) {
825
- this.retMsg_.push(`${befText} preceding the unit code ${pStr} ` + `is invalid. Unable to make a substitution.`);
826
- endProcessing = true;
827
- } // otherwise try for suggestions
828
- else {
829
- let suggestStat = this._getSuggestions(befText);
830
-
831
- endProcessing = suggestStat !== 'succeeded';
832
- } // end if a brace was found or, if not, suggestions were not or
776
+ this.retMsg_.push(`${befText} preceding the unit code ${pStr} ` + `is invalid. Unable to make a substitution.`);
777
+ endProcessing = true;
778
+ }
779
+ // otherwise try for suggestions
780
+ else {
781
+ let suggestStat = this._getSuggestions(befText);
782
+ endProcessing = suggestStat !== 'succeeded';
783
+ } // end if a brace was found or, if not, suggestions were not or
833
784
  // were requested
834
-
835
785
  } // end if text preceding the parentheses was not a number
836
-
837
786
  } // end if there was text before the parentheses
838
- // Process any text after the parentheses
839
-
840
787
 
788
+ // Process any text after the parentheses
841
789
  if (aftText) {
842
790
  // if it's an annotation, get it and add it to the pStr
843
791
  if (aftText.indexOf(this.braceFlag_) >= 0) {
844
- let annoRet = this._getAnnoText(aftText, origString); // if we found not only an annotation, but text before or after
792
+ let annoRet = this._getAnnoText(aftText, origString);
793
+ // if we found not only an annotation, but text before or after
845
794
  // the annotation (remembering that this is all after the
846
795
  // parentheses) throw an error - because we don't know what
847
796
  // to do with it. Could it be missing an operator?
848
-
849
-
850
797
  if (annoRet[1] || annoRet[2]) {
851
798
  throw new Error(`Text found after the parentheses (` + `${aftText}) included an annotation along with other text ` + `for parenthetical unit ${retUnit.csCode_}`);
852
- } // Otherwise put the annotation after the unit string - no message
799
+ }
800
+ // Otherwise put the annotation after the unit string - no message
853
801
  // needed.
854
-
855
-
856
802
  pStr += annoRet[0];
857
- } // Otherwise check to see if it's an exponent. If so, warn the
803
+ }
804
+ // Otherwise check to see if it's an exponent. If so, warn the
858
805
  // user that it's not valid - but try it anyway
859
806
  else {
860
- if (intUtils_.isNumericString(aftText)) {
807
+ if (intUtils_.isNumericString(aftText)) {
808
+ retUnit = null;
809
+ let msg = `An exponent (${aftText}) following a parenthesis ` + `is invalid as of revision 1.9 of the UCUM Specification.`;
810
+ // Add the suggestion only if the string in the parenthesis don't end with a number.
811
+ if (!pStr.match(/\d$/)) {
861
812
  pStr += aftText;
862
- retUnit = retUnit.power(Number(aftText));
863
- this.retMsg_.push(`An exponent (${aftText}) following a parenthesis ` + `is invalid as of revision 1.9 of the UCUM Specification.\n ` + this.vcMsgStart_ + pStr + this.vcMsgEnd_);
864
- } // else the text after the parentheses is neither a number nor
865
- // an annotation. If suggestions were NOT requested, record an
866
- // error.
867
- else if (!this.suggestions_) {
868
- this.retMsg_.push(`Text ${aftText} following the unit code ${pStr} ` + `is invalid. Unable to make a substitution.`);
869
- endProcessing = true;
870
- } // otherwise try for suggestions
871
- else {
872
- let suggestStat = this._getSuggestions(befText);
873
-
874
- endProcessing = suggestStat !== 'succeeded';
875
- } // end if text following the parentheses not an exponent
876
-
877
- } // end if text following the parentheses is not an annotation
878
-
813
+ msg += '\n ' + this.vcMsgStart_ + pStr + this.vcMsgEnd_;
814
+ }
815
+ this.retMsg_.push(msg);
816
+ endProcessing = true;
817
+ }
818
+ // else the text after the parentheses is neither a number nor
819
+ // an annotation. If suggestions were NOT requested, record an
820
+ // error.
821
+ else if (!this.suggestions_) {
822
+ this.retMsg_.push(`Text ${aftText} following the unit code ${pStr} ` + `is invalid. Unable to make a substitution.`);
823
+ endProcessing = true;
824
+ }
825
+ // otherwise try for suggestions
826
+ else {
827
+ let suggestStat = this._getSuggestions(befText);
828
+ endProcessing = suggestStat !== 'succeeded';
829
+ } // end if text following the parentheses not an exponent
830
+ } // end if text following the parentheses is not an annotation
879
831
  } // end if there is text following the parentheses
880
-
881
-
882
832
  if (!endProcessing) {
883
833
  if (!retUnit) {
884
834
  retUnit = new Unit({
@@ -896,7 +846,6 @@ class UnitString {
896
846
  retUnit.csCode_ = pStr;
897
847
  }
898
848
  }
899
-
900
849
  return [retUnit, endProcessing];
901
850
  } // end _getParensUnit
902
851
 
@@ -922,31 +871,28 @@ class UnitString {
922
871
  * the this.annotations_ array is used as the source for the annotations text
923
872
  * @throws an error if for a processing error - an invalid annotation index.
924
873
  */
925
-
926
-
927
874
  _getAnnoText(pStr, origString) {
928
875
  // if the starting braces flag is not at index 0, get the starting
929
876
  // text and the adjust the pStr to omit it.
930
877
  let asIdx = pStr.indexOf(this.braceFlag_);
931
878
  let startText = asIdx > 0 ? pStr.substring(0, asIdx) : null;
932
-
933
879
  if (asIdx !== 0) {
934
880
  pStr = pStr.substr(asIdx);
935
- } // Get the location of the end flag and, if text follows it, get the text
936
-
881
+ }
937
882
 
883
+ // Get the location of the end flag and, if text follows it, get the text
938
884
  let aeIdx = pStr.indexOf(this.braceFlag_, 1);
939
- let endText = aeIdx + this.bFlagLen_ < pStr.length ? pStr.substr(aeIdx + this.bFlagLen_) : null; // Get the index of the annotation in this.annotations_.
940
- // Check it to make sure it's valid, and if not, throw an error
885
+ let endText = aeIdx + this.bFlagLen_ < pStr.length ? pStr.substr(aeIdx + this.bFlagLen_) : null;
941
886
 
887
+ // Get the index of the annotation in this.annotations_.
888
+ // Check it to make sure it's valid, and if not, throw an error
942
889
  let idx = pStr.substring(this.bFlagLen_, aeIdx);
943
890
  let idxNum = Number(idx);
944
-
945
891
  if (!intUtils_.isNumericString(idx) || idxNum >= this.annotations_.length) {
946
892
  throw new Error(`Processing Error - invalid annotation index ${idx} found ` + `in ${pStr} that was created from ${origString}`);
947
- } // Replace the flags and annotation index with the annotation expression
948
-
893
+ }
949
894
 
895
+ // Replace the flags and annotation index with the annotation expression
950
896
  pStr = this.annotations_[idxNum];
951
897
  return [pStr, startText, endText];
952
898
  } // end _getAnnoText
@@ -976,29 +922,23 @@ class UnitString {
976
922
  * Each array will contain the unit code, the unit name and the
977
923
  * unit guidance (if any).
978
924
  */
979
-
980
-
981
925
  _getSuggestions(pStr) {
982
926
  let retObj = intUtils_.getSynonyms(pStr);
983
-
984
927
  if (retObj['status'] === 'succeeded') {
985
928
  let suggSet = {};
986
929
  suggSet['msg'] = `${pStr} is not a valid UCUM code. We found possible ` + `units that might be what was meant:`;
987
930
  suggSet['invalidUnit'] = pStr;
988
931
  let synLen = retObj['units'].length;
989
932
  suggSet['units'] = [];
990
-
991
933
  for (let s = 0; s < synLen; s++) {
992
934
  let unit = retObj['units'][s];
993
935
  let unitArray = [unit['code'], unit['name'], unit['guidance']];
994
936
  suggSet['units'].push(unitArray);
995
937
  }
996
-
997
938
  this.suggestions_.push(suggSet);
998
939
  } else {
999
940
  this.retMsg_.push(`${pStr} is not a valid UCUM code. No alternatives ` + `were found.`);
1000
941
  }
1001
-
1002
942
  return retObj['status'];
1003
943
  } // end getSuggestions
1004
944
 
@@ -1023,255 +963,245 @@ class UnitString {
1023
963
  * the this.suggestions_ array will be populated if no unit (with or without
1024
964
  * substitutions) could be found and suggestions were requested
1025
965
  */
1026
-
1027
-
1028
966
  _makeUnit(uCode, origString) {
1029
967
  // First try the code just as is, without looking for annotations,
1030
968
  // prefixes, exponents, or elephants.
1031
969
  let retUnit = this.utabs_.getUnitByCode(uCode);
1032
-
1033
970
  if (retUnit) {
1034
971
  retUnit = retUnit.clone();
1035
- } // If we found it, we're done. No need to parse for those elephants (or
972
+ }
973
+
974
+ // If we found it, we're done. No need to parse for those elephants (or
1036
975
  // other stuff).
1037
976
  else if (uCode.indexOf(this.braceFlag_) >= 0) {
1038
- let getAnnoRet = this._getUnitWithAnnotation(uCode, origString);
1039
-
1040
- retUnit = getAnnoRet[0];
977
+ let getAnnoRet = this._getUnitWithAnnotation(uCode, origString);
978
+ retUnit = getAnnoRet[0];
979
+ if (retUnit) {
980
+ origString = getAnnoRet[1];
981
+ }
982
+ // If a unit is not found, retUnit will be returned null and
983
+ // the this.retMsg_ array will contain a message describing the problem.
984
+ // If a unit is found, of course, all is good. So ... nothing left
985
+ // to see here, move along.
986
+ } // end if the uCode includes an annotation
987
+ else {
988
+ // So we didn't find a unit for the full uCode or for one with
989
+ // annotations. Try looking for a unit that uses a carat (^)
990
+ // instead of an asterisk (*)
1041
991
 
992
+ if (uCode.indexOf('^') > -1) {
993
+ let tryCode = uCode.replace('^', '*');
994
+ retUnit = this.utabs_.getUnitByCode(tryCode);
1042
995
  if (retUnit) {
1043
- origString = getAnnoRet[1];
1044
- } // If a unit is not found, retUnit will be returned null and
1045
- // the this.retMsg_ array will contain a message describing the problem.
1046
- // If a unit is found, of course, all is good. So ... nothing left
1047
- // to see here, move along.
1048
-
1049
- } // end if the uCode includes an annotation
1050
- else {
1051
- // So we didn't find a unit for the full uCode or for one with
1052
- // annotations. Try looking for a unit that uses a carat (^)
1053
- // instead of an asterisk (*)
1054
- if (uCode.indexOf('^') > -1) {
1055
- let tryCode = uCode.replace('^', '*');
1056
- retUnit = this.utabs_.getUnitByCode(tryCode);
1057
-
1058
- if (retUnit) {
1059
- retUnit = retUnit.clone();
1060
- retUnit.csCode_ = retUnit.csCode_.replace('*', '^');
1061
- retUnit.ciCode_ = retUnit.ciCode_.replace('*', '^');
1062
- }
1063
- } // If that didn't work, check to see if it should have brackets
1064
- // around it (uCode = degF when it should be [degF]
1065
-
1066
-
1067
- if (!retUnit) {
1068
- let addBrackets = '[' + uCode + ']';
1069
- retUnit = this.utabs_.getUnitByCode(addBrackets);
1070
-
1071
- if (retUnit) {
1072
- retUnit = retUnit.clone();
1073
- origString = origString.replace(uCode, addBrackets);
1074
- this.retMsg_.push(`${uCode} is not a valid unit expression, but ` + `${addBrackets} is.\n` + this.vcMsgStart_ + `${addBrackets} (${retUnit.name_})${this.vcMsgEnd_}`);
1075
- } // end if we found the unit after adding brackets
1076
-
1077
- } // end trying to add brackets
1078
- // If we didn't find it, try it as a name
1079
-
1080
-
1081
- if (!retUnit) {
1082
- let retUnitAry = this.utabs_.getUnitByName(uCode);
1083
-
1084
- if (retUnitAry && retUnitAry.length > 0) {
1085
- retUnit = retUnitAry[0].clone();
1086
- let mString = 'The UCUM code for ' + uCode + ' is ' + retUnit.csCode_ + '.\n' + this.vcMsgStart_ + retUnit.csCode_ + this.vcMsgEnd_;
1087
- let dupMsg = false;
1088
-
1089
- for (let r = 0; r < this.retMsg_.length && !dupMsg; r++) dupMsg = this.retMsg_[r] === mString;
1090
-
1091
- if (!dupMsg) this.retMsg_.push(mString);
1092
- let rStr = new RegExp('(^|[.\/({])(' + uCode + ')($|[.\/)}])');
1093
- let res = origString.match(rStr);
1094
- origString = origString.replace(rStr, res[1] + retUnit.csCode_ + res[3]);
1095
- uCode = retUnit.csCode_;
1096
- }
1097
- } // If we still don't have a unit, try assuming a modifier (prefix and/or
1098
- // exponent) and look for a unit without the modifier
1099
-
1100
-
1101
- if (!retUnit) {
1102
- // Well, first see if it's one of the special units. If so,
1103
- // replace the placeholder text with the actual unit string, keeping
1104
- // whatever text (probably a prefix) goes with the unit string.
1105
- let sUnit = null;
996
+ retUnit = retUnit.clone();
997
+ retUnit.csCode_ = retUnit.csCode_.replace('*', '^');
998
+ retUnit.ciCode_ = retUnit.ciCode_.replace('*', '^');
999
+ }
1000
+ }
1001
+ // If that didn't work, check to see if it should have brackets
1002
+ // around it (uCode = degF when it should be [degF]
1003
+ if (!retUnit) {
1004
+ let addBrackets = '[' + uCode + ']';
1005
+ retUnit = this.utabs_.getUnitByCode(addBrackets);
1006
+ if (retUnit) {
1007
+ retUnit = retUnit.clone();
1008
+ origString = origString.replace(uCode, addBrackets);
1009
+ this.retMsg_.push(`${uCode} is not a valid unit expression, but ` + `${addBrackets} is.\n` + this.vcMsgStart_ + `${addBrackets} (${retUnit.name_})${this.vcMsgEnd_}`);
1010
+ } // end if we found the unit after adding brackets
1011
+ } // end trying to add brackets
1106
1012
 
1107
- for (sUnit in Ucum.specUnits_) {
1108
- if (uCode.indexOf(Ucum.specUnits_[sUnit]) !== -1) uCode = uCode.replace(Ucum.specUnits_[sUnit], sUnit);
1109
- }
1013
+ // If we didn't find it, try it as a name
1014
+ if (!retUnit) {
1015
+ let retUnitAry = this.utabs_.getUnitByName(uCode);
1016
+ if (retUnitAry && retUnitAry.length > 0) {
1017
+ retUnit = retUnitAry[0].clone();
1018
+ let mString = 'The UCUM code for ' + uCode + ' is ' + retUnit.csCode_ + '.\n' + this.vcMsgStart_ + retUnit.csCode_ + this.vcMsgEnd_;
1019
+ let dupMsg = false;
1020
+ for (let r = 0; r < this.retMsg_.length && !dupMsg; r++) dupMsg = this.retMsg_[r] === mString;
1021
+ if (!dupMsg) this.retMsg_.push(mString);
1022
+ let rStr = new RegExp('(^|[.\/({])(' + uCode + ')($|[.\/)}])');
1023
+ let res = origString.match(rStr);
1024
+ origString = origString.replace(rStr, res[1] + retUnit.csCode_ + res[3]);
1025
+ uCode = retUnit.csCode_;
1026
+ }
1027
+ }
1110
1028
 
1111
- retUnit = this.utabs_.getUnitByCode(uCode);
1112
- if (retUnit) retUnit = retUnit.clone();
1113
- }
1029
+ // If we still don't have a unit, try assuming a modifier (prefix and/or
1030
+ // exponent) and look for a unit without the modifier
1031
+ if (!retUnit) {
1032
+ // Well, first see if it's one of the special units. If so,
1033
+ // replace the placeholder text with the actual unit string, keeping
1034
+ // whatever text (probably a prefix) goes with the unit string.
1035
+ let sUnit = null;
1036
+ for (sUnit in Ucum.specUnits_) {
1037
+ if (uCode.indexOf(Ucum.specUnits_[sUnit]) !== -1) uCode = uCode.replace(Ucum.specUnits_[sUnit], sUnit);
1038
+ }
1039
+ retUnit = this.utabs_.getUnitByCode(uCode);
1040
+ if (retUnit) retUnit = retUnit.clone();
1041
+ }
1042
+ if (!retUnit) {
1043
+ let origCode = uCode;
1044
+ let origUnit = null;
1045
+ let exp = null;
1046
+ let pfxCode = null;
1047
+ let pfxObj = null;
1048
+ let pfxVal = null;
1049
+ let pfxExp = null;
1050
+
1051
+ // Look first for an exponent. If we got one, separate it out and
1052
+ // try to get the unit again
1053
+ let codeAndExp = this._isCodeWithExponent(uCode);
1054
+ if (codeAndExp) {
1055
+ uCode = codeAndExp[0];
1056
+ exp = codeAndExp[1];
1057
+ origUnit = this.utabs_.getUnitByCode(uCode);
1058
+ }
1114
1059
 
1115
- if (!retUnit) {
1116
- let origCode = uCode;
1117
- let origUnit = null;
1118
- let exp = null;
1119
- let pfxCode = null;
1120
- let pfxObj = null;
1121
- let pfxVal = null;
1122
- let pfxExp = null; // Look first for an exponent. If we got one, separate it out and
1123
- // try to get the unit again
1124
-
1125
- let codeAndExp = this._isCodeWithExponent(uCode);
1126
-
1127
- if (codeAndExp) {
1128
- uCode = codeAndExp[0];
1129
- exp = codeAndExp[1];
1060
+ // If an exponent is found but it's not a valid number, e.g. "2-1",
1061
+ // mark the unit invalid. Otherwise, the "-1" part will be ignored
1062
+ // because parseInt("2-1") results in 2. See LF-2870.
1063
+ if (exp && isNaN(exp)) {
1064
+ retUnit = null;
1065
+ this.retMsg_.push(`${origCode} is not a valid UCUM code.`);
1066
+ } else {
1067
+ // If we still don't have a unit, separate out the prefix, if any,
1068
+ // and try without it.
1069
+ if (!origUnit) {
1070
+ // Try for a single character prefix first.
1071
+ pfxCode = uCode.charAt(0);
1072
+ pfxObj = this.pfxTabs_.getPrefixByCode(pfxCode);
1073
+
1074
+ // if we got a prefix, get its info and remove it from the unit code
1075
+ if (pfxObj) {
1076
+ pfxVal = pfxObj.getValue();
1077
+ pfxExp = pfxObj.getExp();
1078
+ let pCodeLen = pfxCode.length;
1079
+ uCode = uCode.substr(pCodeLen);
1080
+
1081
+ // try again for the unit
1130
1082
  origUnit = this.utabs_.getUnitByCode(uCode);
1131
- } // If we still don't have a unit, separate out the prefix, if any,
1132
- // and try without it.
1133
-
1134
1083
 
1135
- if (!origUnit) {
1136
- // Try for a single character prefix first.
1137
- pfxCode = uCode.charAt(0);
1138
- pfxObj = this.pfxTabs_.getPrefixByCode(pfxCode); // if we got a prefix, get its info and remove it from the unit code
1084
+ // If we still don't have a unit, see if the prefix could be the
1085
+ // two character "da" (deka) prefix. That's the only prefix with
1086
+ // two characters, and without this check it's interpreted as "d"
1087
+ // (deci) and the "a" is considered part of the unit code.
1139
1088
 
1140
- if (pfxObj) {
1089
+ if (!origUnit && pfxCode == 'd' && uCode.substr(0, 1) == 'a') {
1090
+ pfxCode = 'da';
1091
+ pfxObj = this.pfxTabs_.getPrefixByCode(pfxCode);
1141
1092
  pfxVal = pfxObj.getValue();
1142
- pfxExp = pfxObj.getExp();
1143
- let pCodeLen = pfxCode.length;
1144
- uCode = uCode.substr(pCodeLen); // try again for the unit
1145
-
1146
- origUnit = this.utabs_.getUnitByCode(uCode); // If we still don't have a unit, see if the prefix could be the
1147
- // two character "da" (deka) prefix. That's the only prefix with
1148
- // two characters, and without this check it's interpreted as "d"
1149
- // (deci) and the "a" is considered part of the unit code.
1150
-
1151
- if (!origUnit && pfxCode == 'd' && uCode.substr(0, 1) == 'a') {
1152
- pfxCode = 'da';
1153
- pfxObj = this.pfxTabs_.getPrefixByCode(pfxCode);
1154
- pfxVal = pfxObj.getValue();
1155
- uCode = uCode.substr(1); // try one more time for the unit
1156
-
1157
- origUnit = this.utabs_.getUnitByCode(uCode);
1158
- } // Reject the unit we found if it might have another prefix.
1159
- // Such things are in our tables through the LOINC source_
1160
- // (ucum.csv) which has guidance and synonyms. I think it should be
1161
- // safe to exclude anything whose source is LOINC from having a
1162
- // prefix.
1163
-
1164
-
1165
- if (origUnit && origUnit.source_ == 'LOINC') origUnit = null;
1166
- } // end if we found a prefix
1093
+ uCode = uCode.substr(1);
1167
1094
 
1168
- } // end if we didn't get a unit after removing an exponent
1169
- // If we still haven't found anything, we're done looking.
1170
- // (We tried with the full unit string, with the unit string
1171
- // without the exponent, the unit string without a prefix,
1172
- // common errors, etc. That's all we can try).
1173
-
1174
-
1175
- if (!origUnit) {
1176
- retUnit = null; // BUT if the user asked for suggestions, at least look for them
1177
-
1178
- if (this.suggestions_) {
1179
- let suggestStat = this._getSuggestions(origCode);
1180
- } else {
1181
- this.retMsg_.push(`${origCode} is not a valid UCUM code.`);
1095
+ // try one more time for the unit
1096
+ origUnit = this.utabs_.getUnitByCode(uCode);
1182
1097
  }
1183
- } else {
1184
- // Otherwise we found a unit object. Clone it and then apply the
1185
- // prefix and exponent, if any, to it. And remove the guidance.
1186
- retUnit = origUnit.clone(); // If we are here, this is only part of the full unit string, so it is
1187
- // not a base unit, and the synonyms will mostly likely not be correct for the full
1188
- // string.
1189
-
1190
- retUnit.resetFieldsForDerivedUnit();
1191
- let theDim = retUnit.getProperty('dim_');
1192
- let theMag = retUnit.getProperty('magnitude_');
1193
- let theName = retUnit.getProperty('name_');
1194
- let theCiCode = retUnit.getProperty('ciCode_');
1195
- let thePrintSymbol = retUnit.getProperty('printSymbol_'); // If there is an exponent for the unit, apply it to the dimension
1196
- // and magnitude now
1197
-
1198
- if (exp) {
1199
- exp = parseInt(exp);
1200
- let expMul = exp;
1201
- if (theDim) theDim = theDim.mul(exp);
1202
- theMag = Math.pow(theMag, exp);
1203
- retUnit.assignVals({
1204
- 'magnitude_': theMag
1205
- }); // If there is also a prefix, apply the exponent to the prefix.
1206
-
1207
- if (pfxObj) {
1208
- // if the prefix base is 10 it will have an exponent. Multiply
1209
- // the current prefix exponent by the exponent for the unit
1210
- // we're working with. Then raise the prefix value to the level
1211
- // defined by the exponent.
1212
- if (pfxExp) {
1213
- expMul *= pfxObj.getExp();
1214
- pfxVal = Math.pow(10, expMul);
1215
- } // If the prefix base is not 10, it won't have an exponent.
1216
- // At the moment I don't see any units using the prefixes
1217
- // that aren't base 10. But if we get one the prefix value
1218
- // will be applied to the magnitude (below) if the unit does
1219
- // not have a conversion function, and to the conversion prefix
1220
- // if it does.
1221
-
1222
- } // end if there's a prefix as well as the exponent
1223
-
1224
- } // end if there's an exponent
1225
- // Now apply the prefix, if there is one, to the conversion
1226
- // prefix or the magnitude
1227
1098
 
1099
+ // Reject the unit we found if it might have another prefix.
1100
+ // Such things are in our tables through the LOINC source_
1101
+ // (ucum.csv) which has guidance and synonyms. I think it should be
1102
+ // safe to exclude anything whose source is LOINC from having a
1103
+ // prefix.
1104
+ if (origUnit && origUnit.source_ == 'LOINC') origUnit = null;
1105
+ } // end if we found a prefix
1106
+ } // end if we didn't get a unit after removing an exponent
1107
+
1108
+ // If we still haven't found anything, we're done looking.
1109
+ // (We tried with the full unit string, with the unit string
1110
+ // without the exponent, the unit string without a prefix,
1111
+ // common errors, etc. That's all we can try).
1112
+ if (!origUnit) {
1113
+ retUnit = null;
1114
+ // BUT if the user asked for suggestions, at least look for them
1115
+ if (this.suggestions_) {
1116
+ let suggestStat = this._getSuggestions(origCode);
1117
+ } else {
1118
+ this.retMsg_.push(`${origCode} is not a valid UCUM code.`);
1119
+ }
1120
+ } else {
1121
+ // Otherwise we found a unit object. Clone it and then apply the
1122
+ // prefix and exponent, if any, to it. And remove the guidance.
1123
+ retUnit = origUnit.clone();
1124
+ // If we are here, this is only part of the full unit string, so it is
1125
+ // not a base unit, and the synonyms will mostly likely not be correct for the full
1126
+ // string.
1127
+ retUnit.resetFieldsForDerivedUnit();
1128
+ let theDim = retUnit.getProperty('dim_');
1129
+ let theMag = retUnit.getProperty('magnitude_');
1130
+ let theName = retUnit.getProperty('name_');
1131
+ let theCiCode = retUnit.getProperty('ciCode_');
1132
+ let thePrintSymbol = retUnit.getProperty('printSymbol_');
1133
+ // If there is an exponent for the unit, apply it to the dimension
1134
+ // and magnitude now
1135
+ if (exp) {
1136
+ exp = parseInt(exp);
1137
+ let expMul = exp;
1138
+ if (theDim) theDim = theDim.mul(exp);
1139
+ theMag = Math.pow(theMag, exp);
1140
+ retUnit.assignVals({
1141
+ 'magnitude_': theMag
1142
+ });
1228
1143
 
1144
+ // If there is also a prefix, apply the exponent to the prefix.
1229
1145
  if (pfxObj) {
1230
- if (retUnit.cnv_) {
1231
- retUnit.assignVals({
1232
- 'cnvPfx_': pfxVal
1233
- });
1234
- } else {
1235
- theMag *= pfxVal;
1236
- retUnit.assignVals({
1237
- 'magnitude_': theMag
1238
- });
1146
+ // if the prefix base is 10 it will have an exponent. Multiply
1147
+ // the current prefix exponent by the exponent for the unit
1148
+ // we're working with. Then raise the prefix value to the level
1149
+ // defined by the exponent.
1150
+ if (pfxExp) {
1151
+ expMul *= pfxObj.getExp();
1152
+ pfxVal = Math.pow(10, expMul);
1239
1153
  }
1240
- } // if we have a prefix and/or an exponent, add them to the unit
1241
- // attributes - name, csCode, ciCode and print symbol
1242
-
1243
-
1244
- let theCode = retUnit.csCode_;
1245
-
1246
- if (pfxObj) {
1247
- theName = pfxObj.getName() + theName;
1248
- theCode = pfxCode + theCode;
1249
- theCiCode = pfxObj.getCiCode() + theCiCode;
1250
- thePrintSymbol = pfxObj.getPrintSymbol() + thePrintSymbol;
1154
+ // If the prefix base is not 10, it won't have an exponent.
1155
+ // At the moment I don't see any units using the prefixes
1156
+ // that aren't base 10. But if we get one the prefix value
1157
+ // will be applied to the magnitude (below) if the unit does
1158
+ // not have a conversion function, and to the conversion prefix
1159
+ // if it does.
1160
+ } // end if there's a prefix as well as the exponent
1161
+ } // end if there's an exponent
1162
+
1163
+ // Now apply the prefix, if there is one, to the conversion
1164
+ // prefix or the magnitude
1165
+ if (pfxObj) {
1166
+ if (retUnit.cnv_) {
1251
1167
  retUnit.assignVals({
1252
- 'name_': theName,
1253
- 'csCode_': theCode,
1254
- 'ciCode_': theCiCode,
1255
- 'printSymbol_': thePrintSymbol
1168
+ 'cnvPfx_': pfxVal
1256
1169
  });
1257
- }
1258
-
1259
- if (exp) {
1260
- let expStr = exp.toString();
1170
+ } else {
1171
+ theMag *= pfxVal;
1261
1172
  retUnit.assignVals({
1262
- 'name_': theName + '<sup>' + expStr + '</sup>',
1263
- 'csCode_': theCode + expStr,
1264
- 'ciCode_': theCiCode + expStr,
1265
- 'printSymbol_': thePrintSymbol + '<sup>' + expStr + '</sup>'
1173
+ 'magnitude_': theMag
1266
1174
  });
1267
1175
  }
1268
- } // end if an original unit was found (without prefix and/or exponent)
1269
-
1270
- } // end if we didn't get a unit for the full unit code (w/out modifiers)
1271
-
1272
- } // end if we didn't find the unit on the first try, before parsing
1273
-
1274
-
1176
+ }
1177
+ // if we have a prefix and/or an exponent, add them to the unit
1178
+ // attributes - name, csCode, ciCode and print symbol
1179
+ let theCode = retUnit.csCode_;
1180
+ if (pfxObj) {
1181
+ theName = pfxObj.getName() + theName;
1182
+ theCode = pfxCode + theCode;
1183
+ theCiCode = pfxObj.getCiCode() + theCiCode;
1184
+ thePrintSymbol = pfxObj.getPrintSymbol() + thePrintSymbol;
1185
+ retUnit.assignVals({
1186
+ 'name_': theName,
1187
+ 'csCode_': theCode,
1188
+ 'ciCode_': theCiCode,
1189
+ 'printSymbol_': thePrintSymbol
1190
+ });
1191
+ }
1192
+ if (exp) {
1193
+ let expStr = exp.toString();
1194
+ retUnit.assignVals({
1195
+ 'name_': theName + '<sup>' + expStr + '</sup>',
1196
+ 'csCode_': theCode + expStr,
1197
+ 'ciCode_': theCiCode + expStr,
1198
+ 'printSymbol_': thePrintSymbol + '<sup>' + expStr + '</sup>'
1199
+ });
1200
+ }
1201
+ } // end if an original unit was found (without prefix and/or exponent)
1202
+ } // end if an invalid exponent wasn't found
1203
+ } // end if we didn't get a unit for the full unit code (w/out modifiers)
1204
+ } // end if we didn't find the unit on the first try, before parsing
1275
1205
  return [retUnit, origString];
1276
1206
  } // end _makeUnit
1277
1207
 
@@ -1288,101 +1218,109 @@ class UnitString {
1288
1218
  * the this.retMsg_ array will be updated with any user messages
1289
1219
  * (informational, error or warning) generated by this or called methods
1290
1220
  */
1291
-
1292
-
1293
1221
  _getUnitWithAnnotation(uCode, origString) {
1294
- let retUnit = null; // Get the annotation and anything that precedes or follows it.
1222
+ let retUnit = null;
1295
1223
 
1224
+ // Get the annotation and anything that precedes or follows it.
1296
1225
  let annoRet = this._getAnnoText(uCode, origString);
1297
-
1298
1226
  let annoText = annoRet[0];
1299
1227
  let befAnnoText = annoRet[1];
1300
- let aftAnnoText = annoRet[2]; // Add the warning about annotations - just once.
1228
+ let aftAnnoText = annoRet[2];
1301
1229
 
1302
- if (this.bracesMsg_ && this.retMsg_.indexOf(this.bracesMsg_) === -1) this.retMsg_.push(this.bracesMsg_); // If there's no text before or after the annotation, it's probably
1230
+ // Add the warning about annotations - just once.
1231
+
1232
+ if (this.bracesMsg_ && this.retMsg_.indexOf(this.bracesMsg_) === -1) this.retMsg_.push(this.bracesMsg_);
1233
+
1234
+ // If there's no text before or after the annotation, it's probably
1303
1235
  // something that should be interpreted as a 1, e.g., {KCT'U}.
1304
1236
  // HOWEVER, it could also be a case where someone used braces instead
1305
1237
  // of brackets, e.g., {degF} instead of [degF]. Check for that before
1306
1238
  // we assume it should be a 1.
1307
-
1308
1239
  let msgLen = this.retMsg_.length;
1309
-
1310
1240
  if (!befAnnoText && !aftAnnoText) {
1311
1241
  let tryBrackets = '[' + annoText.substring(1, annoText.length - 1) + ']';
1242
+ let mkUnitRet = this._makeUnit(tryBrackets, origString);
1312
1243
 
1313
- let mkUnitRet = this._makeUnit(tryBrackets, origString); // If we got back a unit, assign it to the returned unit, and add
1314
- // a message to advise the user that brackets should enclose the code
1315
-
1316
-
1244
+ // Nearly anything inside braces is valid, so we don't want to change the
1245
+ // unit, but we can put the found unit in the message as a sort of
1246
+ // warning.
1317
1247
  if (mkUnitRet[0]) {
1318
- retUnit = mkUnitRet[0];
1319
- origString = origString.replace(annoText, tryBrackets);
1320
- this.retMsg_.push(`${annoText} is not a valid unit expression, but ` + `${tryBrackets} is.\n` + this.vcMsgStart_ + `${tryBrackets} (${retUnit.name_})${this.vcMsgEnd_}`);
1321
- } // Otherwise assume that this should be interpreted as a 1
1322
- else {
1323
- // remove error message generated for trybrackets
1324
- if (this.retMsg_.length > msgLen) {
1325
- this.retMsg_.pop();
1326
- }
1327
-
1328
- uCode = 1;
1329
- retUnit = 1;
1248
+ retUnit = uCode;
1249
+ this.retMsg_.push(`${annoText} is a valid unit expression, but ` + `did you mean ${tryBrackets} (${mkUnitRet[0].name_})?`);
1250
+ } else {
1251
+ // remove error message generated for trybrackets
1252
+ if (this.retMsg_.length > msgLen) {
1253
+ this.retMsg_.pop();
1330
1254
  }
1255
+ }
1256
+
1257
+ // This is the case where the string is only this annotation.
1258
+ // Create and return a unit object, as we do for numeric units in
1259
+ // parseString.
1260
+ retUnit = new Unit({
1261
+ 'csCode_': annoText,
1262
+ 'ciCode_': annoText,
1263
+ 'magnitude_': 1,
1264
+ 'name_': annoText
1265
+ });
1331
1266
  } // end if it's only an annotation
1332
1267
  else {
1333
- // if there's text before and no text after, assume the text before
1334
- // the annotation is the unit code (with an annotation following it).
1335
- // Call _makeUnit for the text before the annotation.
1336
- if (befAnnoText && !aftAnnoText) {
1337
- // make sure that what's before the annoText is not a number, e.g.,
1338
- // /100{cells}. But f it is a number, just set the return unit to
1339
- // the number.
1340
- if (intUtils_.isIntegerUnit(befAnnoText)) {
1341
- retUnit = befAnnoText;
1342
- } // Otherwise try to find a unit
1343
- else {
1344
- let mkUnitRet = this._makeUnit(befAnnoText, origString); // if a unit was returned
1345
-
1268
+ // if there's text before and no text after, assume the text before
1269
+ // the annotation is the unit code (with an annotation following it).
1270
+ // Call _makeUnit for the text before the annotation.
1271
+ if (befAnnoText && !aftAnnoText) {
1272
+ // make sure that what's before the annoText is not a number, e.g.,
1273
+ // /100{cells}. But f it is a number, just set the return unit to
1274
+ // the number.
1275
+ if (intUtils_.isIntegerUnit(befAnnoText)) {
1276
+ retUnit = befAnnoText;
1277
+ }
1278
+ // Otherwise try to find a unit
1279
+ else {
1280
+ let mkUnitRet = this._makeUnit(befAnnoText, origString);
1346
1281
 
1347
- if (mkUnitRet[0]) {
1348
- retUnit = mkUnitRet[0];
1349
- retUnit.csCode_ += annoText;
1350
- origString = mkUnitRet[1];
1351
- } // Otherwise add a not found message
1352
- else {
1353
- this.retMsg_.push(`Unable to find a unit for ${befAnnoText} that ` + `precedes the annotation ${annoText}.`);
1354
- }
1355
- }
1356
- } // else if there's only text after the annotation, try for a unit
1357
- // from the after text and assume the user put the annotation in
1358
- // the wrong place (and tell them)
1359
- else if (!befAnnoText && aftAnnoText) {
1360
- // Again, test for a number and if it is a number, set the return
1361
- // unit to the number.
1362
- if (intUtils_.isIntegerUnit(aftAnnoText)) {
1363
- retUnit = aftAnnoText + annoText;
1364
- this.retMsg_.push(`The annotation ${annoText} before the ``${aftAnnoText} is invalid.\n` + this.vcMsgStart_ + retUnit + this.vcMsgEnd_);
1365
- } else {
1366
- let mkUnitRet = this._makeUnit(aftAnnoText, origString);
1367
-
1368
- if (mkUnitRet[0]) {
1369
- retUnit = mkUnitRet[0];
1370
- retUnit.csCode_ += annoText;
1371
- origString = retUnit.csCode_;
1372
- this.retMsg_.push(`The annotation ${annoText} before the unit ` + `code is invalid.\n` + this.vcMsgStart_ + retUnit.csCode_ + this.vcMsgEnd_);
1373
- } // Otherwise add a not found message
1374
- else {
1375
- this.retMsg_.push(`Unable to find a unit for ${befAnnoText} that ` + `follows the annotation ${annoText}.`);
1376
- }
1377
- }
1378
- } // else it's got text before AND after the annotation. Now what?
1379
- // For now this is an error. This may be a case of a missing
1380
- // operator but that is not handled yet.
1282
+ // if a unit was returned
1283
+ if (mkUnitRet[0]) {
1284
+ retUnit = mkUnitRet[0];
1285
+ retUnit.csCode_ += annoText;
1286
+ origString = mkUnitRet[1];
1287
+ }
1288
+ // Otherwise add a not found message
1381
1289
  else {
1382
- this.retMsg_.push(`Unable to find a unit for ${befAnnoText}${annoText}` + `${aftAnnoText}.\nWe are not sure how to interpret text both before ` + `and after the annotation. Sorry`);
1383
- }
1384
- } // else if there's text before/and or after the annotation
1385
-
1290
+ this.retMsg_.push(`Unable to find a unit for ${befAnnoText} that ` + `precedes the annotation ${annoText}.`);
1291
+ }
1292
+ }
1293
+ }
1294
+ // else if there's only text after the annotation, try for a unit
1295
+ // from the after text and assume the user put the annotation in
1296
+ // the wrong place (and tell them)
1297
+ else if (!befAnnoText && aftAnnoText) {
1298
+ // Again, test for a number and if it is a number, set the return
1299
+ // unit to the number.
1300
+ if (intUtils_.isIntegerUnit(aftAnnoText)) {
1301
+ retUnit = aftAnnoText + annoText;
1302
+ this.retMsg_.push(`The annotation ${annoText} before the ``${aftAnnoText} is invalid.\n` + this.vcMsgStart_ + retUnit + this.vcMsgEnd_);
1303
+ } else {
1304
+ let mkUnitRet = this._makeUnit(aftAnnoText, origString);
1305
+ if (mkUnitRet[0]) {
1306
+ retUnit = mkUnitRet[0];
1307
+ retUnit.csCode_ += annoText;
1308
+ origString = retUnit.csCode_;
1309
+ this.retMsg_.push(`The annotation ${annoText} before the unit ` + `code is invalid.\n` + this.vcMsgStart_ + retUnit.csCode_ + this.vcMsgEnd_);
1310
+ }
1311
+ // Otherwise add a not found message
1312
+ else {
1313
+ this.retMsg_.push(`Unable to find a unit for ${befAnnoText} that ` + `follows the annotation ${annoText}.`);
1314
+ }
1315
+ }
1316
+ }
1317
+ // else it's got text before AND after the annotation. Now what?
1318
+ // For now this is an error. This may be a case of a missing
1319
+ // operator but that is not handled yet.
1320
+ else {
1321
+ this.retMsg_.push(`Unable to find a unit for ${befAnnoText}${annoText}` + `${aftAnnoText}.\nWe are not sure how to interpret text both before ` + `and after the annotation. Sorry`);
1322
+ }
1323
+ } // else if there's text before/and or after the annotation
1386
1324
 
1387
1325
  return [retUnit, origString];
1388
1326
  } // end _getUnitWithAnnotations
@@ -1403,11 +1341,8 @@ class UnitString {
1403
1341
  * the this.retMsg_ array will be updated with any user messages
1404
1342
  * (informational, error or warning) generated by this or called methods
1405
1343
  */
1406
-
1407
-
1408
1344
  _performUnitArithmetic(uArray, origString) {
1409
1345
  let finalUnit = uArray[0]['un'];
1410
-
1411
1346
  if (intUtils_.isIntegerUnit(finalUnit)) {
1412
1347
  finalUnit = new Unit({
1413
1348
  'csCode_': finalUnit,
@@ -1416,14 +1351,12 @@ class UnitString {
1416
1351
  'name_': finalUnit
1417
1352
  });
1418
1353
  }
1419
-
1420
1354
  let uLen = uArray.length;
1421
- let endProcessing = false; // Perform the arithmetic for the units, starting with the first 2 units.
1355
+ let endProcessing = false;
1356
+ // Perform the arithmetic for the units, starting with the first 2 units.
1422
1357
  // We only need to do the arithmetic if we have more than one unit.
1423
-
1424
1358
  for (let u2 = 1; u2 < uLen && !endProcessing; u2++) {
1425
1359
  let nextUnit = uArray[u2]['un'];
1426
-
1427
1360
  if (intUtils_.isIntegerUnit(nextUnit)) {
1428
1361
  nextUnit = new Unit({
1429
1362
  'csCode_': nextUnit,
@@ -1432,14 +1365,11 @@ class UnitString {
1432
1365
  'name_': nextUnit
1433
1366
  });
1434
1367
  }
1435
-
1436
1368
  if (nextUnit === null || typeof nextUnit !== 'number' && !nextUnit.getProperty) {
1437
1369
  let msgString = `Unit string (${origString}) contains unrecognized ` + 'element';
1438
-
1439
1370
  if (nextUnit) {
1440
1371
  msgString += ` (${this.openEmph_}${nextUnit.toString()}` + `${this.closeEmph_})`;
1441
1372
  }
1442
-
1443
1373
  msgString += '; could not parse full string. Sorry';
1444
1374
  this.retMsg_.push(msgString);
1445
1375
  endProcessing = true;
@@ -1447,9 +1377,10 @@ class UnitString {
1447
1377
  try {
1448
1378
  // Is the operation division?
1449
1379
  let thisOp = uArray[u2]['op'];
1450
- let isDiv = thisOp === '/'; // Perform the operation. Both the finalUnit and nextUnit
1451
- // are unit objects.
1380
+ let isDiv = thisOp === '/';
1452
1381
 
1382
+ // Perform the operation. Both the finalUnit and nextUnit
1383
+ // are unit objects.
1453
1384
  isDiv ? finalUnit = finalUnit.divide(nextUnit) : finalUnit = finalUnit.multiplyThese(nextUnit);
1454
1385
  } catch (err) {
1455
1386
  this.retMsg_.unshift(err.message);
@@ -1457,10 +1388,7 @@ class UnitString {
1457
1388
  finalUnit = null;
1458
1389
  }
1459
1390
  } // end if we have another valid unit/number to process
1460
-
1461
1391
  } // end do for each unit after the first one
1462
-
1463
-
1464
1392
  return finalUnit;
1465
1393
  } // end _performUnitArithmetic
1466
1394
 
@@ -1488,25 +1416,21 @@ class UnitString {
1488
1416
  * if there is no trailing number or something follows the trailing
1489
1417
  * number, or if the first part is not characters.
1490
1418
  */
1491
-
1492
-
1493
1419
  _isCodeWithExponent(uCode) {
1494
1420
  let ret = [];
1495
- let res = uCode.match(/(^[^\-\+]+?)([\-\+\d]+)$/); // If we got a return with an exponent, separate the exponent from the
1496
- // unit and return both (as separate values)
1421
+ let res = uCode.match(/(^[^\-\+]+?)([\-\+\d]+)$/);
1497
1422
 
1423
+ // If we got a return with an exponent, separate the exponent from the
1424
+ // unit and return both (as separate values)
1498
1425
  if (res && res[2] && res[2] !== "") {
1499
1426
  ret.push(res[1]);
1500
1427
  ret.push(res[2]);
1501
1428
  } // end if we got an exponent
1502
1429
  else {
1503
- ret = null;
1504
- }
1505
-
1430
+ ret = null;
1431
+ }
1506
1432
  return ret;
1507
1433
  } // end _isCodeWithExponent
1508
-
1509
-
1510
1434
  } // end class UnitString
1511
1435
 
1512
1436
  /**
@@ -1520,18 +1444,13 @@ class UnitString {
1520
1444
  *
1521
1445
  * @return the singleton UnitString object.
1522
1446
  */
1523
-
1524
-
1525
1447
  exports.UnitString = UnitString;
1526
-
1527
1448
  _defineProperty(UnitString, "INVALID_ANNOTATION_CHAR_MSG", 'An invalid character was found in the annotation ');
1528
-
1529
- // A regular expression for validating annotation strings.
1530
1449
  _defineProperty(UnitString, "VALID_ANNOTATION_REGEX", /^\{[!-z|~]*\}$/);
1531
-
1532
1450
  UnitString.getInstance = function () {
1533
1451
  return new UnitString();
1534
1452
  };
1453
+
1535
1454
  /*
1536
1455
  // Perform the first request for the object, to set the getInstance method.
1537
1456
  UnitString.getInstance();