305 lines
7.4 KiB
JavaScript
305 lines
7.4 KiB
JavaScript
var convert
|
|
, keys = require('./lodash/lodash.keys')
|
|
, each = require('./lodash/lodash.foreach')
|
|
, measures = {
|
|
length: require('./definitions/length')
|
|
, area: require('./definitions/area')
|
|
, mass: require('./definitions/mass')
|
|
, volume: require('./definitions/volume')
|
|
, each: require('./definitions/each')
|
|
, temperature: require('./definitions/temperature')
|
|
, time: require('./definitions/time')
|
|
, digital: require('./definitions/digital')
|
|
, partsPer: require('./definitions/partsPer')
|
|
, speed: require('./definitions/speed')
|
|
, pace: require('./definitions/pace')
|
|
, pressure: require('./definitions/pressure')
|
|
, current: require('./definitions/current')
|
|
, voltage: require('./definitions/voltage')
|
|
, power: require('./definitions/power')
|
|
, reactivePower: require('./definitions/reactivePower')
|
|
, apparentPower: require('./definitions/apparentPower')
|
|
, energy: require('./definitions/energy')
|
|
, reactiveEnergy: require('./definitions/reactiveEnergy')
|
|
, volumeFlowRate: require('./definitions/volumeFlowRate')
|
|
, illuminance: require('./definitions/illuminance')
|
|
, frequency: require('./definitions/frequency')
|
|
, angle : require('./definitions/angle')
|
|
}
|
|
, Converter;
|
|
|
|
Converter = function (numerator, denominator) {
|
|
if(denominator)
|
|
this.val = numerator / denominator;
|
|
else
|
|
this.val = numerator;
|
|
};
|
|
|
|
/**
|
|
* Lets the converter know the source unit abbreviation
|
|
*/
|
|
Converter.prototype.from = function (from) {
|
|
if(this.destination)
|
|
throw new Error('.from must be called before .to');
|
|
|
|
this.origin = this.getUnit(from);
|
|
|
|
if(!this.origin) {
|
|
this.throwUnsupportedUnitError(from);
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Converts the unit and returns the value
|
|
*/
|
|
Converter.prototype.to = function (to) {
|
|
if(!this.origin)
|
|
throw new Error('.to must be called after .from');
|
|
|
|
this.destination = this.getUnit(to);
|
|
|
|
var result
|
|
, transform;
|
|
|
|
if(!this.destination) {
|
|
this.throwUnsupportedUnitError(to);
|
|
}
|
|
|
|
// Don't change the value if origin and destination are the same
|
|
if (this.origin.abbr === this.destination.abbr) {
|
|
return this.val;
|
|
}
|
|
|
|
// You can't go from liquid to mass, for example
|
|
if(this.destination.measure != this.origin.measure) {
|
|
throw new Error('Cannot convert incompatible measures of '
|
|
+ this.destination.measure + ' and ' + this.origin.measure);
|
|
}
|
|
|
|
/**
|
|
* Convert from the source value to its anchor inside the system
|
|
*/
|
|
result = this.val * this.origin.unit.to_anchor;
|
|
|
|
/**
|
|
* For some changes it's a simple shift (C to K)
|
|
* So we'll add it when convering into the unit (later)
|
|
* and subtract it when converting from the unit
|
|
*/
|
|
if (this.origin.unit.anchor_shift) {
|
|
result -= this.origin.unit.anchor_shift
|
|
}
|
|
|
|
/**
|
|
* Convert from one system to another through the anchor ratio. Some conversions
|
|
* aren't ratio based or require more than a simple shift. We can provide a custom
|
|
* transform here to provide the direct result
|
|
*/
|
|
if(this.origin.system != this.destination.system) {
|
|
transform = measures[this.origin.measure]._anchors[this.origin.system].transform;
|
|
if (typeof transform === 'function') {
|
|
result = transform(result)
|
|
}
|
|
else {
|
|
result *= measures[this.origin.measure]._anchors[this.origin.system].ratio;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This shift has to be done after the system conversion business
|
|
*/
|
|
if (this.destination.unit.anchor_shift) {
|
|
result += this.destination.unit.anchor_shift;
|
|
}
|
|
|
|
/**
|
|
* Convert to another unit inside the destination system
|
|
*/
|
|
return result / this.destination.unit.to_anchor;
|
|
};
|
|
|
|
/**
|
|
* Converts the unit to the best available unit.
|
|
*/
|
|
Converter.prototype.toBest = function(options) {
|
|
if(!this.origin)
|
|
throw new Error('.toBest must be called after .from');
|
|
|
|
var options = Object.assign({
|
|
exclude: [],
|
|
cutOffNumber: 1,
|
|
}, options)
|
|
|
|
var best;
|
|
/**
|
|
Looks through every possibility for the 'best' available unit.
|
|
i.e. Where the value has the fewest numbers before the decimal point,
|
|
but is still higher than 1.
|
|
*/
|
|
each(this.possibilities(), function(possibility) {
|
|
var unit = this.describe(possibility);
|
|
var isIncluded = options.exclude.indexOf(possibility) === -1;
|
|
|
|
if (isIncluded && unit.system === this.origin.system) {
|
|
var result = this.to(possibility);
|
|
if (!best || (result >= options.cutOffNumber && result < best.val)) {
|
|
best = {
|
|
val: result,
|
|
unit: possibility,
|
|
singular: unit.singular,
|
|
plural: unit.plural
|
|
};
|
|
}
|
|
}
|
|
}.bind(this));
|
|
|
|
return best;
|
|
}
|
|
|
|
/**
|
|
* Finds the unit
|
|
*/
|
|
Converter.prototype.getUnit = function (abbr) {
|
|
var found;
|
|
|
|
each(measures, function (systems, measure) {
|
|
each(systems, function (units, system) {
|
|
if(system == '_anchors')
|
|
return false;
|
|
|
|
each(units, function (unit, testAbbr) {
|
|
if(testAbbr == abbr) {
|
|
found = {
|
|
abbr: abbr
|
|
, measure: measure
|
|
, system: system
|
|
, unit: unit
|
|
};
|
|
return false;
|
|
}
|
|
});
|
|
|
|
if(found)
|
|
return false;
|
|
});
|
|
|
|
if(found)
|
|
return false;
|
|
});
|
|
|
|
return found;
|
|
};
|
|
|
|
var describe = function(resp) {
|
|
return {
|
|
abbr: resp.abbr
|
|
, measure: resp.measure
|
|
, system: resp.system
|
|
, singular: resp.unit.name.singular
|
|
, plural: resp.unit.name.plural
|
|
};
|
|
}
|
|
|
|
/**
|
|
* An alias for getUnit
|
|
*/
|
|
Converter.prototype.describe = function (abbr) {
|
|
var resp = Converter.prototype.getUnit(abbr);
|
|
var desc = null;
|
|
|
|
try {
|
|
desc = describe(resp);
|
|
} catch(err) {
|
|
this.throwUnsupportedUnitError(abbr);
|
|
}
|
|
|
|
return desc;
|
|
};
|
|
|
|
/**
|
|
* Detailed list of all supported units
|
|
*/
|
|
Converter.prototype.list = function (measure) {
|
|
var list = [];
|
|
|
|
each(measures, function (systems, testMeasure) {
|
|
if(measure && measure !== testMeasure)
|
|
return;
|
|
|
|
each(systems, function (units, system) {
|
|
if(system == '_anchors')
|
|
return false;
|
|
|
|
each(units, function (unit, abbr) {
|
|
list = list.concat(describe({
|
|
abbr: abbr,
|
|
measure: testMeasure
|
|
, system: system
|
|
, unit: unit
|
|
}));
|
|
});
|
|
});
|
|
});
|
|
|
|
return list;
|
|
};
|
|
|
|
Converter.prototype.throwUnsupportedUnitError = function (what) {
|
|
var validUnits = [];
|
|
|
|
each(measures, function (systems, measure) {
|
|
each(systems, function (units, system) {
|
|
if(system == '_anchors')
|
|
return false;
|
|
|
|
validUnits = validUnits.concat(keys(units));
|
|
});
|
|
});
|
|
|
|
throw new Error('Unsupported unit ' + what + ', use one of: ' + validUnits.join(', '));
|
|
}
|
|
|
|
/**
|
|
* Returns the abbreviated measures that the value can be
|
|
* converted to.
|
|
*/
|
|
Converter.prototype.possibilities = function (measure) {
|
|
var possibilities = [];
|
|
if(!this.origin && !measure) {
|
|
each(keys(measures), function (measure){
|
|
each(measures[measure], function (units, system) {
|
|
if(system == '_anchors')
|
|
return false;
|
|
|
|
possibilities = possibilities.concat(keys(units));
|
|
});
|
|
});
|
|
} else {
|
|
measure = measure || this.origin.measure;
|
|
each(measures[measure], function (units, system) {
|
|
if(system == '_anchors')
|
|
return false;
|
|
|
|
possibilities = possibilities.concat(keys(units));
|
|
});
|
|
}
|
|
|
|
return possibilities;
|
|
};
|
|
|
|
/**
|
|
* Returns the abbreviated measures that the value can be
|
|
* converted to.
|
|
*/
|
|
Converter.prototype.measures = function () {
|
|
return keys(measures);
|
|
};
|
|
|
|
convert = function (value) {
|
|
return new Converter(value);
|
|
};
|
|
|
|
module.exports = convert;
|