diff --git a/lib/type/unit/Unit.js b/lib/type/unit/Unit.js index b6f4a41c1..7035c2fd8 100644 --- a/lib/type/unit/Unit.js +++ b/lib/type/unit/Unit.js @@ -370,11 +370,13 @@ function factory (type, config, load, typed, math) { } // Replace the unit into the auto unit system - var baseDim = res.unit.base.key; - UNIT_SYSTEMS.auto[baseDim] = { - unit: res.unit, - prefix: res.prefix - }; + if(res.unit.base) { + var baseDim = res.unit.base.key; + UNIT_SYSTEMS.auto[baseDim] = { + unit: res.unit, + prefix: res.prefix + }; + } } // Has the string been entirely consumed? @@ -538,6 +540,17 @@ function factory (type, config, load, typed, math) { * @private */ function _findUnit(str) { + + // First, match units names exactly. For example, a user could define 'mm' as 10^-4 m, which is silly, but then we would want 'mm' to match the user-defined unit. + if(UNITS.hasOwnProperty(str)) { + var unit = UNITS[str]; + var prefix = unit.prefixes['']; + return { + unit: unit, + prefix: prefix + } + } + for (var name in UNITS) { if (UNITS.hasOwnProperty(name)) { if (endsWith(str, name)) { @@ -2863,6 +2876,78 @@ function factory (type, config, load, typed, math) { } } + /** + * Create a user-defined unit and register it with the Unit type. + * Example: + * createUnit('knot', '0.514444444 m/s') + * createUnit('acre', new Unit(43560, 'ft^2')) + * + * @param {string} name The name of the new unit. Must be unique. Example: 'knot' + * @param {string, Unit} definition Definition of the unit in terms of existing units. For example, '0.514444444 m / s'. + * @param {Object} options (optional) An object containing any of the following properties: + * prefixes {string} "none", "short", "long", "binary_short", or "binary_long". The default is "none". + * aliases {Array} Array of strings. Example: ['knots', 'kt', 'kts'] + * offset {Numeric} An offset to apply when converting from the unit. For example, the offset for celsius is 273.15 and the offset for farhenheit is 459.67. Default is 0. + * + * @return {Unit} + */ + Unit.createUnit = function(name, definition, options) { + + if(typeof(name) !== 'string') { + throw new TypeError("createUnit expects first parameter to be of type 'string'"); + } + + // Check collisions with existing units + if(UNITS.hasOwnProperty(name)) { + throw new Error("Cannot create unit '" + name + "': a unit with that name already exists"); + } + + // TODO: Validate name for collisions with other built-in functions (like abs or cos, for example), and for acceptable variable names. For example, '42' is probably not a valid unit. Nor is '%', since it is also an operator. + + var defUnit; + if(typeof(definition) === 'string') { + defUnit = Unit.parse(definition); + } + else if(definition && definition.type === 'Unit') { + defUnit = definition.clone(); + } + else { + throw new TypeError("createUnit expects second parameter to be of type 'string' or 'Unit'"); + } + + var prefixes = PREFIXES.NONE; + var aliases = []; + var offset = 0; + if(options) { + if(options.prefixes) { + prefixes = PREFIXES[options.prefixes.toUpperCase()] || PREFIXES.NONE; + } + aliases = options.aliases || []; + offset = options.offset || 0; + } + + var newUnit = { + name: name, + value: defUnit.value, + dimensions: JSON.parse(JSON.stringify(defUnit.dimensions)), + prefixes: prefixes, + offset: offset + } + + Unit.UNITS[name] = newUnit; + + for (var i=0; i