@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.
- package/README.md +50 -12
- package/browser-dist/ucum-lhc.js +841 -1325
- package/data/ucumDefs.min.json +1 -1
- package/package.json +1 -1
- package/source/ucumLhcUtils.js +100 -66
- package/source/ucumXmlDocument.js +5 -2
- package/source/unit.js +2 -1
- package/source/unitString.js +159 -140
- package/source-cjs/config.js +1 -12
- package/source-cjs/config.js.map +1 -1
- package/source-cjs/dimension.js +20 -63
- package/source-cjs/dimension.js.map +1 -1
- package/source-cjs/jsonArrayPack.js +7 -25
- package/source-cjs/jsonArrayPack.js.map +1 -1
- package/source-cjs/prefix.js +12 -26
- package/source-cjs/prefix.js.map +1 -1
- package/source-cjs/prefixTables.js +10 -24
- package/source-cjs/prefixTables.js.map +1 -1
- package/source-cjs/ucumFunctions.js +35 -32
- package/source-cjs/ucumFunctions.js.map +1 -1
- package/source-cjs/ucumInternalUtils.js +5 -13
- package/source-cjs/ucumInternalUtils.js.map +1 -1
- package/source-cjs/ucumJsonDefs.js +1 -16
- package/source-cjs/ucumJsonDefs.js.map +1 -1
- package/source-cjs/ucumLhcUtils.js +117 -138
- package/source-cjs/ucumLhcUtils.js.map +1 -1
- package/source-cjs/ucumPkg.js +1 -6
- package/source-cjs/ucumPkg.js.map +1 -1
- package/source-cjs/ucumXmlDocument.js +162 -184
- package/source-cjs/ucumXmlDocument.js.map +1 -1
- package/source-cjs/unit.js +97 -181
- package/source-cjs/unit.js.map +1 -1
- package/source-cjs/unitString.js +537 -618
- package/source-cjs/unitString.js.map +1 -1
- package/source-cjs/unitTables.js +33 -139
- package/source-cjs/unitTables.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lhncbc/ucum-lhc",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"description": "Implements Unified Code for Units of Measure (UCUM) functions in a javascript library",
|
|
5
5
|
"main": "source-cjs/ucumPkg.js",
|
|
6
6
|
"homepage": "https://lhncbc.github.io/ucum-lhc/",
|
package/source/ucumLhcUtils.js
CHANGED
|
@@ -130,19 +130,12 @@ export class UcumLhcUtils {
|
|
|
130
130
|
|
|
131
131
|
let resp = this.getSpecifiedUnit(uStr, valConv, suggest);
|
|
132
132
|
let theUnit = resp['unit'];
|
|
133
|
-
let retObj = {}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
'
|
|
138
|
-
|
|
139
|
-
else {
|
|
140
|
-
retObj = {'status': resp['origString'] === uStr ? 'valid': 'invalid',
|
|
141
|
-
'ucumCode': resp['origString'],
|
|
142
|
-
'unit': {'code': theUnit.csCode_,
|
|
143
|
-
'name': theUnit.name_,
|
|
144
|
-
'guidance': theUnit.guidance_ }};
|
|
145
|
-
}
|
|
133
|
+
let retObj = !theUnit ? {'ucumCode': null} :
|
|
134
|
+
{'ucumCode': resp['origString'],
|
|
135
|
+
'unit': {'code': theUnit.csCode_,
|
|
136
|
+
'name': theUnit.name_,
|
|
137
|
+
'guidance': theUnit.guidance_ }};
|
|
138
|
+
retObj.status = resp.status;
|
|
146
139
|
if (resp['suggestions']) {
|
|
147
140
|
retObj['suggestions'] = resp['suggestions'];
|
|
148
141
|
}
|
|
@@ -228,12 +221,7 @@ export class UcumLhcUtils {
|
|
|
228
221
|
returnObj['status'] = 'error';
|
|
229
222
|
returnObj['msg'].push('No "from" unit expression specified.');
|
|
230
223
|
}
|
|
231
|
-
|
|
232
|
-
!intUtils_.isNumericString(fromVal))) {
|
|
233
|
-
returnObj['status'] = 'error';
|
|
234
|
-
returnObj['msg'].push('No "from" value, or an invalid "from" value, ' +
|
|
235
|
-
'was specified.');
|
|
236
|
-
}
|
|
224
|
+
this._checkFromVal(fromVal, returnObj);
|
|
237
225
|
if (toUnitCode) {
|
|
238
226
|
toUnitCode = toUnitCode.trim();
|
|
239
227
|
}
|
|
@@ -343,63 +331,77 @@ export class UcumLhcUtils {
|
|
|
343
331
|
* @param fromUnit the unit string to be converted to base units information
|
|
344
332
|
* @param fromVal the number of "from" units to be converted
|
|
345
333
|
* @returns an object with the properties:
|
|
346
|
-
* '
|
|
334
|
+
* 'status' indicates whether the result succeeded. The value will be one of:
|
|
335
|
+
* 'succeeded': the conversion was successfully calculated (which can be
|
|
336
|
+
* true even if it was already in base units);
|
|
337
|
+
* 'invalid': fromUnit is not a valid UCUM code;
|
|
338
|
+
* 'failed': the conversion could not be made (e.g., if it is an "arbitrary" unit);
|
|
339
|
+
* 'error': if an error occurred (an input or programming error)
|
|
340
|
+
* 'msg': an array of messages (possibly empty) if the string is invalid or
|
|
347
341
|
* an error occurred, indicating the problem, or a suggestion of a
|
|
348
342
|
* substitution such as the substitution of 'G' for 'Gauss', or
|
|
349
|
-
* an empty array if no messages were generated.
|
|
350
|
-
*
|
|
343
|
+
* an empty array if no messages were generated. There can also be a
|
|
344
|
+
* message that is just informational or warning.
|
|
351
345
|
* 'magnitude': the new value when fromVal units of fromUnits is expressed in the base units.
|
|
352
346
|
* 'fromUnitIsSpecial': whether the input unit fromUnit is a "special unit"
|
|
353
347
|
* as defined in UCUM. This means there is some function applied to convert
|
|
354
348
|
* between fromUnit and the base units, so the returned magnitude is likely not
|
|
355
349
|
* useful as a scale factor for other conversions (i.e., it only has validity
|
|
356
350
|
* and usefulness for the input values that produced it).
|
|
357
|
-
* 'unitToExp': a map of base units in
|
|
351
|
+
* 'unitToExp': a map of base units in fromUnit to their exponent
|
|
358
352
|
*/
|
|
359
353
|
convertToBaseUnits(fromUnit, fromVal) {
|
|
360
|
-
let inputUnitLookup = this.getSpecifiedUnit(fromUnit, 'validate');
|
|
361
354
|
let retObj = {};
|
|
362
|
-
|
|
363
|
-
retObj.
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
355
|
+
this._checkFromVal(fromVal, retObj);
|
|
356
|
+
if (!retObj.status) { // could be set to 'error' by _checkFromVal
|
|
357
|
+
let inputUnitLookup = this.getSpecifiedUnit(fromUnit, 'validate');
|
|
358
|
+
retObj = {status: inputUnitLookup.status == 'valid' ? 'succeeded' : inputUnitLookup.status};
|
|
359
|
+
let unit = inputUnitLookup.unit;
|
|
360
|
+
retObj.msg = inputUnitLookup.retMsg || [];
|
|
361
|
+
if (!unit) {
|
|
362
|
+
if (inputUnitLookup.retMsg?.length == 0)
|
|
363
|
+
retObj.msg.push('Could not find unit information for '+fromUnit);
|
|
364
|
+
}
|
|
365
|
+
else if (unit.isArbitrary_) {
|
|
366
|
+
retObj.msg.push('Arbitrary units cannot be converted to base units or other units.');
|
|
367
|
+
retObj.status = 'failed';
|
|
368
|
+
}
|
|
369
|
+
else if (retObj.status == 'succeeded') {
|
|
370
|
+
let unitToExp = {};
|
|
371
|
+
let dimVec = unit.dim_?.dimVec_
|
|
372
|
+
let baseUnitString = '1';
|
|
373
|
+
if (dimVec) {
|
|
374
|
+
let dimVecIndexToBaseUnit = UnitTables.getInstance().dimVecIndexToBaseUnit_;
|
|
375
|
+
for (let i=0, len=dimVec.length; i<len; ++i) {
|
|
376
|
+
let exp = dimVec[i];
|
|
377
|
+
if (exp) {
|
|
378
|
+
unitToExp[dimVecIndexToBaseUnit[i]] = exp;
|
|
379
|
+
baseUnitString += '.' + dimVecIndexToBaseUnit[i] + exp;
|
|
380
|
+
}
|
|
382
381
|
}
|
|
383
382
|
}
|
|
384
|
-
}
|
|
385
383
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
try {
|
|
395
|
-
retObj.magnitude = retUnit.convertFrom(fromVal, unit);
|
|
396
|
-
}
|
|
397
|
-
catch (e) {
|
|
398
|
-
retObj.msg.push(e.toString());
|
|
384
|
+
// The unit might have a conversion function, which has to be applied; we
|
|
385
|
+
// cannot just assume unit_.magnitude_ is the magnitude in base units.
|
|
386
|
+
let retUnitLookup = this.getSpecifiedUnit(baseUnitString, 'validate');
|
|
387
|
+
// There should not be any error in retUnitLookup, unless there is a bug.
|
|
388
|
+
let retUnit = retUnitLookup.unit;
|
|
389
|
+
if (retUnitLookup.status !== 'valid') {
|
|
390
|
+
retObj.msg.push('Unable construct base unit string; tried '+baseUnitString);
|
|
391
|
+
retObj.status = 'error';
|
|
399
392
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
393
|
+
else {
|
|
394
|
+
try {
|
|
395
|
+
retObj.magnitude = retUnit.convertFrom(fromVal, unit);
|
|
396
|
+
}
|
|
397
|
+
catch (e) {
|
|
398
|
+
retObj.msg.push(e.toString());
|
|
399
|
+
retObj.status = 'error';
|
|
400
|
+
}
|
|
401
|
+
if (retObj.status == 'succeeded') {
|
|
402
|
+
retObj.unitToExp = unitToExp;
|
|
403
|
+
retObj.fromUnitIsSpecial = unit.isSpecial_;
|
|
404
|
+
}
|
|
403
405
|
}
|
|
404
406
|
}
|
|
405
407
|
}
|
|
@@ -407,6 +409,26 @@ export class UcumLhcUtils {
|
|
|
407
409
|
}
|
|
408
410
|
|
|
409
411
|
|
|
412
|
+
/**
|
|
413
|
+
* Checks the given value as to whether it is suitable as a "from" value in a
|
|
414
|
+
* unit conversion. If it is not, the responseObj will have its status set
|
|
415
|
+
* to 'error' and a message added.
|
|
416
|
+
* @param fromVal The value to check
|
|
417
|
+
* @param responseObj the object that will be updated if the value is not
|
|
418
|
+
* usable.
|
|
419
|
+
*/
|
|
420
|
+
_checkFromVal(fromVal, responseObj) {
|
|
421
|
+
if (fromVal === null || isNaN(fromVal) || (typeof fromVal !== 'number' &&
|
|
422
|
+
!intUtils_.isNumericString(fromVal))) {
|
|
423
|
+
responseObj.status = 'error';
|
|
424
|
+
if (!responseObj.msg)
|
|
425
|
+
responseObj.msg = [];
|
|
426
|
+
responseObj.msg.push('No "from" value, or an invalid "from" value, ' +
|
|
427
|
+
'was specified.');
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
|
|
410
432
|
/**
|
|
411
433
|
* This method accepts a term and looks for units that include it as
|
|
412
434
|
* a synonym - or that include the term in its name.
|
|
@@ -450,13 +472,18 @@ export class UcumLhcUtils {
|
|
|
450
472
|
* true indicates suggestions are wanted; false indicates they are not,
|
|
451
473
|
* and is the default if the parameter is not specified;
|
|
452
474
|
* @returns a hash containing:
|
|
475
|
+
* 'status' will be 'valid' (uName is a valid UCUM code), 'invalid'
|
|
476
|
+
* (the uStr is not a valid UCUM code, and substitutions or
|
|
477
|
+
* suggestions may or may not be returned, depending on what was
|
|
478
|
+
* requested and found); or 'error' (an input or programming error
|
|
479
|
+
* occurred);
|
|
453
480
|
* 'unit' the unit object (or null if there were problems creating the
|
|
454
481
|
* unit);
|
|
455
482
|
* 'origString' the possibly updated unit string passed in;
|
|
456
483
|
* 'retMsg' an array of user messages (informational, error or warning) if
|
|
457
484
|
* any were generated (IF any were generated, otherwise will be an
|
|
458
485
|
* empty array); and
|
|
459
|
-
*
|
|
486
|
+
* 'suggestions' is an array of 1 or more hash objects. Each hash
|
|
460
487
|
* contains three elements:
|
|
461
488
|
* 'msg' which is a message indicating what unit expression the
|
|
462
489
|
* suggestions are for;
|
|
@@ -511,6 +538,17 @@ export class UcumLhcUtils {
|
|
|
511
538
|
} // end if the unit was not found as a unit name
|
|
512
539
|
} // end if a unit expression was specified
|
|
513
540
|
|
|
541
|
+
// Set the status field
|
|
542
|
+
if (!retObj.unit) {
|
|
543
|
+
// No unit was found; check whether origString has a value
|
|
544
|
+
retObj.status = !retObj.origString ? 'error' : 'invalid';
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
// Check whether substitutions were made to the unit string in order to
|
|
548
|
+
// find the unit
|
|
549
|
+
retObj.status = retObj.origString === uName ? 'valid': 'invalid';
|
|
550
|
+
}
|
|
551
|
+
|
|
514
552
|
return retObj;
|
|
515
553
|
|
|
516
554
|
} // end getSpecifiedUnit
|
|
@@ -582,7 +620,3 @@ export class UcumLhcUtils {
|
|
|
582
620
|
UcumLhcUtils.getInstance = function(){
|
|
583
621
|
return new UcumLhcUtils();
|
|
584
622
|
} ;
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
@@ -116,7 +116,10 @@ export class UcumXmlDocument {
|
|
|
116
116
|
attrs["exp_"] = pValNode.childNamed('sup');
|
|
117
117
|
if (attrs["exp_"] != null) {
|
|
118
118
|
attrs["exp_"] = attrs["exp_"].val;
|
|
119
|
-
|
|
119
|
+
// Use parseFloat('1eSOMETHING') instead of Math.pow() to avoid
|
|
120
|
+
// small number changes like 1.0000000000000001e-21. See LF-2830.
|
|
121
|
+
// attrs["value_"] = Math.pow(10, attrs["exp_"]);
|
|
122
|
+
attrs["value_"] = parseFloat(`1e${attrs["exp_"]}`);
|
|
120
123
|
}
|
|
121
124
|
else {
|
|
122
125
|
attrs["value_"] = pValNode.val;
|
|
@@ -519,4 +522,4 @@ UcumXmlDocument.getInstance = function(){
|
|
|
519
522
|
|
|
520
523
|
// Perform the first request for the document object, to get the
|
|
521
524
|
// getInstance method set.
|
|
522
|
-
UcumXmlDocument.getInstance();
|
|
525
|
+
UcumXmlDocument.getInstance();
|
package/source/unit.js
CHANGED
|
@@ -839,7 +839,8 @@ export class Unit {
|
|
|
839
839
|
invertString(theString) {
|
|
840
840
|
|
|
841
841
|
if (theString.length > 0) {
|
|
842
|
-
|
|
842
|
+
// replace('<!', '</') is here to make sure closing html tags like </sup> are intact. See LF-2830.
|
|
843
|
+
let stringRep = theString.replace('/', "!").replace('.', '/').replace('<!', '</').replace("!", '.');
|
|
843
844
|
switch(stringRep.charAt(0)) {
|
|
844
845
|
case '.' : theString = stringRep.substr(1); break;
|
|
845
846
|
case '/' : theString = stringRep; break;
|
package/source/unitString.js
CHANGED
|
@@ -878,11 +878,16 @@ export class UnitString {
|
|
|
878
878
|
else {
|
|
879
879
|
|
|
880
880
|
if (intUtils_.isNumericString(aftText)) {
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
881
|
+
retUnit = null;
|
|
882
|
+
let msg = `An exponent (${aftText}) following a parenthesis ` +
|
|
883
|
+
`is invalid as of revision 1.9 of the UCUM Specification.`;
|
|
884
|
+
// Add the suggestion only if the string in the parenthesis don't end with a number.
|
|
885
|
+
if (!pStr.match(/\d$/)) {
|
|
886
|
+
pStr += aftText;
|
|
887
|
+
msg += '\n ' + this.vcMsgStart_ + pStr + this.vcMsgEnd_;
|
|
888
|
+
}
|
|
889
|
+
this.retMsg_.push(msg);
|
|
890
|
+
endProcessing = true;
|
|
886
891
|
}
|
|
887
892
|
// else the text after the parentheses is neither a number nor
|
|
888
893
|
// an annotation. If suggestions were NOT requested, record an
|
|
@@ -1149,143 +1154,150 @@ export class UnitString {
|
|
|
1149
1154
|
origUnit = this.utabs_.getUnitByCode(uCode);
|
|
1150
1155
|
}
|
|
1151
1156
|
|
|
1157
|
+
// If an exponent is found but it's not a valid number, e.g. "2-1",
|
|
1158
|
+
// mark the unit invalid. Otherwise, the "-1" part will be ignored
|
|
1159
|
+
// because parseInt("2-1") results in 2. See LF-2870.
|
|
1160
|
+
if (exp && isNaN(exp)) {
|
|
1161
|
+
retUnit = null;
|
|
1162
|
+
this.retMsg_.push(`${origCode} is not a valid UCUM code.`);
|
|
1163
|
+
}
|
|
1164
|
+
else {
|
|
1165
|
+
// If we still don't have a unit, separate out the prefix, if any,
|
|
1166
|
+
// and try without it.
|
|
1167
|
+
if (!origUnit) {
|
|
1168
|
+
// Try for a single character prefix first.
|
|
1169
|
+
pfxCode = uCode.charAt(0);
|
|
1170
|
+
pfxObj = this.pfxTabs_.getPrefixByCode(pfxCode);
|
|
1171
|
+
|
|
1172
|
+
// if we got a prefix, get its info and remove it from the unit code
|
|
1173
|
+
if (pfxObj) {
|
|
1174
|
+
pfxVal = pfxObj.getValue();
|
|
1175
|
+
pfxExp = pfxObj.getExp();
|
|
1176
|
+
let pCodeLen = pfxCode.length;
|
|
1177
|
+
uCode = uCode.substr(pCodeLen);
|
|
1152
1178
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
if (!origUnit) {
|
|
1156
|
-
// Try for a single character prefix first.
|
|
1157
|
-
pfxCode = uCode.charAt(0);
|
|
1158
|
-
pfxObj = this.pfxTabs_.getPrefixByCode(pfxCode);
|
|
1159
|
-
|
|
1160
|
-
// if we got a prefix, get its info and remove it from the unit code
|
|
1161
|
-
if (pfxObj) {
|
|
1162
|
-
pfxVal = pfxObj.getValue();
|
|
1163
|
-
pfxExp = pfxObj.getExp();
|
|
1164
|
-
let pCodeLen = pfxCode.length;
|
|
1165
|
-
uCode = uCode.substr(pCodeLen);
|
|
1179
|
+
// try again for the unit
|
|
1180
|
+
origUnit = this.utabs_.getUnitByCode(uCode);
|
|
1166
1181
|
|
|
1167
|
-
|
|
1168
|
-
|
|
1182
|
+
// If we still don't have a unit, see if the prefix could be the
|
|
1183
|
+
// two character "da" (deka) prefix. That's the only prefix with
|
|
1184
|
+
// two characters, and without this check it's interpreted as "d"
|
|
1185
|
+
// (deci) and the "a" is considered part of the unit code.
|
|
1169
1186
|
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1187
|
+
if (!origUnit && pfxCode == 'd' && uCode.substr(0, 1) == 'a') {
|
|
1188
|
+
pfxCode = 'da';
|
|
1189
|
+
pfxObj = this.pfxTabs_.getPrefixByCode(pfxCode);
|
|
1190
|
+
pfxVal = pfxObj.getValue();
|
|
1191
|
+
uCode = uCode.substr(1);
|
|
1174
1192
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
pfxVal = pfxObj.getValue();
|
|
1179
|
-
uCode = uCode.substr(1);
|
|
1193
|
+
// try one more time for the unit
|
|
1194
|
+
origUnit = this.utabs_.getUnitByCode(uCode);
|
|
1195
|
+
}
|
|
1180
1196
|
|
|
1181
|
-
//
|
|
1182
|
-
|
|
1197
|
+
// Reject the unit we found if it might have another prefix.
|
|
1198
|
+
// Such things are in our tables through the LOINC source_
|
|
1199
|
+
// (ucum.csv) which has guidance and synonyms. I think it should be
|
|
1200
|
+
// safe to exclude anything whose source is LOINC from having a
|
|
1201
|
+
// prefix.
|
|
1202
|
+
if (origUnit && origUnit.source_ == 'LOINC')
|
|
1203
|
+
origUnit = null;
|
|
1204
|
+
} // end if we found a prefix
|
|
1205
|
+
} // end if we didn't get a unit after removing an exponent
|
|
1206
|
+
|
|
1207
|
+
// If we still haven't found anything, we're done looking.
|
|
1208
|
+
// (We tried with the full unit string, with the unit string
|
|
1209
|
+
// without the exponent, the unit string without a prefix,
|
|
1210
|
+
// common errors, etc. That's all we can try).
|
|
1211
|
+
if (!origUnit) {
|
|
1212
|
+
retUnit = null ;
|
|
1213
|
+
// BUT if the user asked for suggestions, at least look for them
|
|
1214
|
+
if (this.suggestions_) {
|
|
1215
|
+
let suggestStat = this._getSuggestions(origCode);
|
|
1216
|
+
}
|
|
1217
|
+
else {
|
|
1218
|
+
this.retMsg_.push(`${origCode} is not a valid UCUM code.`);
|
|
1183
1219
|
}
|
|
1184
|
-
|
|
1185
|
-
// Reject the unit we found if it might have another prefix.
|
|
1186
|
-
// Such things are in our tables through the LOINC source_
|
|
1187
|
-
// (ucum.csv) which has guidance and synonyms. I think it should be
|
|
1188
|
-
// safe to exclude anything whose source is LOINC from having a
|
|
1189
|
-
// prefix.
|
|
1190
|
-
if (origUnit && origUnit.source_ == 'LOINC')
|
|
1191
|
-
origUnit = null;
|
|
1192
|
-
} // end if we found a prefix
|
|
1193
|
-
} // end if we didn't get a unit after removing an exponent
|
|
1194
|
-
|
|
1195
|
-
// If we still haven't found anything, we're done looking.
|
|
1196
|
-
// (We tried with the full unit string, with the unit string
|
|
1197
|
-
// without the exponent, the unit string without a prefix,
|
|
1198
|
-
// common errors, etc. That's all we can try).
|
|
1199
|
-
if (!origUnit) {
|
|
1200
|
-
retUnit = null ;
|
|
1201
|
-
// BUT if the user asked for suggestions, at least look for them
|
|
1202
|
-
if (this.suggestions_) {
|
|
1203
|
-
let suggestStat = this._getSuggestions(origCode);
|
|
1204
1220
|
}
|
|
1205
1221
|
else {
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1222
|
+
// Otherwise we found a unit object. Clone it and then apply the
|
|
1223
|
+
// prefix and exponent, if any, to it. And remove the guidance.
|
|
1224
|
+
retUnit = origUnit.clone();
|
|
1225
|
+
// If we are here, this is only part of the full unit string, so it is
|
|
1226
|
+
// not a base unit, and the synonyms will mostly likely not be correct for the full
|
|
1227
|
+
// string.
|
|
1228
|
+
retUnit.resetFieldsForDerivedUnit();
|
|
1229
|
+
let theDim = retUnit.getProperty('dim_');
|
|
1230
|
+
let theMag = retUnit.getProperty('magnitude_');
|
|
1231
|
+
let theName = retUnit.getProperty('name_');
|
|
1232
|
+
let theCiCode = retUnit.getProperty('ciCode_');
|
|
1233
|
+
let thePrintSymbol = retUnit.getProperty('printSymbol_');
|
|
1234
|
+
// If there is an exponent for the unit, apply it to the dimension
|
|
1235
|
+
// and magnitude now
|
|
1236
|
+
if (exp) {
|
|
1237
|
+
exp = parseInt(exp);
|
|
1238
|
+
let expMul = exp;
|
|
1239
|
+
if (theDim)
|
|
1240
|
+
theDim = theDim.mul(exp);
|
|
1241
|
+
theMag = Math.pow(theMag, exp);
|
|
1242
|
+
retUnit.assignVals({'magnitude_': theMag});
|
|
1243
|
+
|
|
1244
|
+
// If there is also a prefix, apply the exponent to the prefix.
|
|
1245
|
+
if (pfxObj) {
|
|
1246
|
+
|
|
1247
|
+
// if the prefix base is 10 it will have an exponent. Multiply
|
|
1248
|
+
// the current prefix exponent by the exponent for the unit
|
|
1249
|
+
// we're working with. Then raise the prefix value to the level
|
|
1250
|
+
// defined by the exponent.
|
|
1251
|
+
if (pfxExp) {
|
|
1252
|
+
expMul *= pfxObj.getExp();
|
|
1253
|
+
pfxVal = Math.pow(10, expMul);
|
|
1254
|
+
}
|
|
1255
|
+
// If the prefix base is not 10, it won't have an exponent.
|
|
1256
|
+
// At the moment I don't see any units using the prefixes
|
|
1257
|
+
// that aren't base 10. But if we get one the prefix value
|
|
1258
|
+
// will be applied to the magnitude (below) if the unit does
|
|
1259
|
+
// not have a conversion function, and to the conversion prefix
|
|
1260
|
+
// if it does.
|
|
1261
|
+
} // end if there's a prefix as well as the exponent
|
|
1262
|
+
} // end if there's an exponent
|
|
1263
|
+
|
|
1264
|
+
// Now apply the prefix, if there is one, to the conversion
|
|
1265
|
+
// prefix or the magnitude
|
|
1233
1266
|
if (pfxObj) {
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
expMul *= pfxObj.getExp();
|
|
1241
|
-
pfxVal = Math.pow(10, expMul);
|
|
1267
|
+
if (retUnit.cnv_) {
|
|
1268
|
+
retUnit.assignVals({'cnvPfx_': pfxVal});
|
|
1269
|
+
}
|
|
1270
|
+
else {
|
|
1271
|
+
theMag *= pfxVal;
|
|
1272
|
+
retUnit.assignVals({'magnitude_': theMag})
|
|
1242
1273
|
}
|
|
1243
|
-
// If the prefix base is not 10, it won't have an exponent.
|
|
1244
|
-
// At the moment I don't see any units using the prefixes
|
|
1245
|
-
// that aren't base 10. But if we get one the prefix value
|
|
1246
|
-
// will be applied to the magnitude (below) if the unit does
|
|
1247
|
-
// not have a conversion function, and to the conversion prefix
|
|
1248
|
-
// if it does.
|
|
1249
|
-
} // end if there's a prefix as well as the exponent
|
|
1250
|
-
} // end if there's an exponent
|
|
1251
|
-
|
|
1252
|
-
// Now apply the prefix, if there is one, to the conversion
|
|
1253
|
-
// prefix or the magnitude
|
|
1254
|
-
if (pfxObj) {
|
|
1255
|
-
if (retUnit.cnv_) {
|
|
1256
|
-
retUnit.assignVals({'cnvPfx_': pfxVal});
|
|
1257
1274
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1275
|
+
// if we have a prefix and/or an exponent, add them to the unit
|
|
1276
|
+
// attributes - name, csCode, ciCode and print symbol
|
|
1277
|
+
let theCode = retUnit.csCode_;
|
|
1278
|
+
if (pfxObj) {
|
|
1279
|
+
theName = pfxObj.getName() + theName;
|
|
1280
|
+
theCode = pfxCode + theCode;
|
|
1281
|
+
theCiCode = pfxObj.getCiCode() + theCiCode;
|
|
1282
|
+
thePrintSymbol = pfxObj.getPrintSymbol() + thePrintSymbol;
|
|
1283
|
+
retUnit.assignVals({
|
|
1284
|
+
'name_': theName,
|
|
1285
|
+
'csCode_': theCode,
|
|
1286
|
+
'ciCode_': theCiCode,
|
|
1287
|
+
'printSymbol_': thePrintSymbol
|
|
1288
|
+
});
|
|
1261
1289
|
}
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
'csCode_': theCode,
|
|
1274
|
-
'ciCode_': theCiCode,
|
|
1275
|
-
'printSymbol_': thePrintSymbol
|
|
1276
|
-
});
|
|
1277
|
-
}
|
|
1278
|
-
if (exp) {
|
|
1279
|
-
let expStr = exp.toString();
|
|
1280
|
-
retUnit.assignVals({
|
|
1281
|
-
'name_': theName + '<sup>' + expStr + '</sup>',
|
|
1282
|
-
'csCode_': theCode + expStr,
|
|
1283
|
-
'ciCode_': theCiCode + expStr,
|
|
1284
|
-
'printSymbol_': thePrintSymbol + '<sup>' + expStr + '</sup>'
|
|
1285
|
-
});
|
|
1286
|
-
}
|
|
1287
|
-
} // end if an original unit was found (without prefix and/or exponent)
|
|
1288
|
-
|
|
1290
|
+
if (exp) {
|
|
1291
|
+
let expStr = exp.toString();
|
|
1292
|
+
retUnit.assignVals({
|
|
1293
|
+
'name_': theName + '<sup>' + expStr + '</sup>',
|
|
1294
|
+
'csCode_': theCode + expStr,
|
|
1295
|
+
'ciCode_': theCiCode + expStr,
|
|
1296
|
+
'printSymbol_': thePrintSymbol + '<sup>' + expStr + '</sup>'
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
} // end if an original unit was found (without prefix and/or exponent)
|
|
1300
|
+
} // end if an invalid exponent wasn't found
|
|
1289
1301
|
} // end if we didn't get a unit for the full unit code (w/out modifiers)
|
|
1290
1302
|
} // end if we didn't find the unit on the first try, before parsing
|
|
1291
1303
|
return [retUnit, origString];
|
|
@@ -1330,24 +1342,31 @@ export class UnitString {
|
|
|
1330
1342
|
let tryBrackets = '[' + annoText.substring(1, annoText.length - 1) + ']';
|
|
1331
1343
|
let mkUnitRet = this._makeUnit(tryBrackets, origString);
|
|
1332
1344
|
|
|
1333
|
-
//
|
|
1334
|
-
//
|
|
1345
|
+
// Nearly anything inside braces is valid, so we don't want to change the
|
|
1346
|
+
// unit, but we can put the found unit in the message as a sort of
|
|
1347
|
+
// warning.
|
|
1335
1348
|
if (mkUnitRet[0]) {
|
|
1336
|
-
retUnit =
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
`${tryBrackets} is.\n` + this.vcMsgStart_ +
|
|
1340
|
-
`${tryBrackets} (${retUnit.name_})${this.vcMsgEnd_}`);
|
|
1349
|
+
retUnit = uCode;
|
|
1350
|
+
this.retMsg_.push(`${annoText} is a valid unit expression, but ` +
|
|
1351
|
+
`did you mean ${tryBrackets} (${mkUnitRet[0].name_})?`);
|
|
1341
1352
|
}
|
|
1342
|
-
// Otherwise assume that this should be interpreted as a 1
|
|
1343
1353
|
else {
|
|
1344
1354
|
// remove error message generated for trybrackets
|
|
1345
1355
|
if (this.retMsg_.length > msgLen) {
|
|
1346
1356
|
this.retMsg_.pop();
|
|
1347
1357
|
}
|
|
1348
|
-
uCode = 1;
|
|
1349
|
-
retUnit = 1;
|
|
1350
1358
|
}
|
|
1359
|
+
|
|
1360
|
+
// This is the case where the string is only this annotation.
|
|
1361
|
+
// Create and return a unit object, as we do for numeric units in
|
|
1362
|
+
// parseString.
|
|
1363
|
+
retUnit = new Unit({
|
|
1364
|
+
'csCode_': annoText,
|
|
1365
|
+
'ciCode_': annoText,
|
|
1366
|
+
'magnitude_': 1,
|
|
1367
|
+
'name_': annoText
|
|
1368
|
+
});
|
|
1369
|
+
|
|
1351
1370
|
} // end if it's only an annotation
|
|
1352
1371
|
|
|
1353
1372
|
else {
|