Skip to content

Commit

Permalink
added length and temperature converters. created manual type of conve…
Browse files Browse the repository at this point in the history
…rting, if it is not usual (as temperature). added converter header with icon. optimised code a bit
  • Loading branch information
mhevyk committed Aug 6, 2022
1 parent 3eaa11e commit 517d494
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 46 deletions.
9 changes: 3 additions & 6 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

<script src="js/main.js" defer></script>
<script src="js/propotypeExtension.js"></script>
<script src="js/tabs.js"></script>
<script src="js/tabs.js" defer></script>
<script src="js/converter.js" defer></script>
<script src="js/precisionRange.js"></script>
</head>
Expand Down Expand Up @@ -55,8 +55,8 @@ <h3 class="header-subtitle">Handy enough to use it!</h3>
</div>
<section>
<div id="area-converter" class="menu-content"></div>
<div class="menu-content"></div>
<div class="menu-content"></div>
<div id="length-converter" class="menu-content"></div>
<div id="temperature-converter" class="menu-content"></div>
<div class="menu-content"></div>
<div class="menu-content"></div>
<div class="menu-content"></div>
Expand All @@ -70,8 +70,5 @@ <h3 class="header-subtitle">Handy enough to use it!</h3>
<a href="https://www.instagram.com/m.hevyk/" target="blank"><i class="fa fa-instagram" aria-hidden="true"></i></a>
</footer>
</div>
<script>
const menu = new Tabs("main-navigation-tabs");
</script>
</body>
</html>
129 changes: 109 additions & 20 deletions js/converter.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,75 @@
class Converter{
static measurementUnitGroups = {
area: {
icon: `<i class="fa fa-area-chart" aria-hidden="true"></i>`,
list: [
{id: "nm2", value: 1e18, names: {short: "nm&sup2", full: "square nanometer"}, group: "Metric system"},
{id: "mcm2", value: 1e12, names: {short: "mcm&sup2", full: "square micrometer"}, group: "Metric system"},
{id: "mm2", value: 1000000, names: {short: "mm&sup2", full: "square millimeter"}, group: "Metric system"},
{id: "sm2", value: 10000, names: {short: "cm&sup2", full: "square centimeter"}, group: "Metric system"},
{id: "dm2", value: 100, names: {short: "dm&sup2", full: "square decimeter"}, group: "Metric system"},
{id: "mm2", value: 1e6, names: {short: "mm&sup2", full: "square millimeter"}, group: "Metric system"},
{id: "cm2", value: 1e4, names: {short: "cm&sup2", full: "square centimeter"}, group: "Metric system"},
{id: "dm2", value: 1e2, names: {short: "dm&sup2", full: "square decimeter"}, group: "Metric system"},
{id: "m2", value: 1, names: {short: "m&sup2", full: "square meter"}, group: "Metric system"},
{id: "ar", value: 0.01, names: {short: "a", full: "ar/hectare"}, group: "Metric system"},
{id: "ha", value: 0.0001, names: {short: "ha", full: "hectare"}, group: "Metric system"},
{id: "km2", value: 0.000001, names: {short: "km&sup2", full: "square kilometer"}, group: "Metric system"},
{id: "ar", value: 1e-2, names: {short: "a", full: "ar/hectare"}, group: "Metric system"},
{id: "ha", value: 1e-4, names: {short: "ha", full: "hectare"}, group: "Metric system"},
{id: "km2", value: 1e-6, names: {short: "km&sup2", full: "square kilometer"}, group: "Metric system"},

{id: "township", value: 1.072506e-8, names: {short: "twsp", full: "township"}, group: "Anglo-american units"},
{id: "township", value: 1.072506e-8, names: {short: "", full: "township"}, group: "Anglo-american units"},
{id: "mi2", value: 3.861022e-7, names: {short: "mi&sup2", full: "square mile"}, group: "Anglo-american units"},
{id: "homestead", value: 0.000001544409, names: {short: "hmst", full: "homestead"}, group: "Anglo-american units"},
{id: "homestead", value: 0.000001544409, names: {short: "", full: "homestead"}, group: "Anglo-american units"},
{id: "acre", value: 0.0002471055, names: {short: "acre", full: "acre"}, group: "Anglo-american units"},
{id: "rod", value: 0.0009884220, names: {short: "rod", full: "rod"}, group: "Anglo-american units"},
{id: "rod2", value: 0.03953687, names: {short: "rod&sup2", full: "square rod"}, group: "Anglo-american units"},
{id: "yd2", value: 1.195990, names: {short: "yd&sup2", full: "square yard"}, group: "Anglo-american units"},
{id: "ft2", value: 10.76391, names: {short: "ft&sup2", full: "square foot"}, group: "Anglo-american units"},
{id: "in2", value: 1550, names: {short: "in&sup2", full: "square inch"}, group: "Anglo-american units"}
{id: "in2", value: 1550, names: {short: "in&sup2", full: "square inch"}, group: "Anglo-american units"},

],
},
length:{
list: []
icon: `<i class='fas fa-ruler'></i>`,
list: [
{id: "nm", value: 1e9, names: {short: "nm", full: "nanometer"}, group: "Metric system"},
{id: "mcm", value: 1e6, names: {short: "mcm", full: "micrometer"}, group: "Metric system"},
{id: "mm", value: 1e3, names: {short: "mm", full: "milimeter"}, group: "Metric system"},
{id: "cm", value: 1e2, names: {short: "cm", full: "centimeter"}, group: "Metric system"},
{id: "dm", value: 10, names: {short: "dm", full: "decimeter"}, group: "Metric system"},
{id: "m", value: 1, names: {short: "m", full: "meter"}, group: "Metric system"},
{id: "km", value: 1e-3, names: {short: "km", full: "kilometer"}, group: "Metric system"},

{id: "league", value: 2.0712373074577798987247806145444e-4, names: {short: "", full: "league"}, group: "Anglo-american units"},
{id: "mi", value: 6.2137119223733396961743418436332e-4, names: {short: "mi", full: "mile"}, group: "Anglo-american units"},
{id: "chain", value: 0.049709695378986717569394734749065, names: {short: "", full: "chain"}, group: "Anglo-american units"},
{id: "rd", value: 0.19883878151594687027757893899626, names: {short: "rd", full: "rod"}, group: "Anglo-american units"},
{id: "yd", value: 1.0936132983377077865266841644794, names: {short: "yd", full: "yard"}, group: "Anglo-american units"},
{id: "ft", value: 3.2808398950131233595800524934383, names: {short: "ft", full: "foot"}, group: "Anglo-american units"},
{id: "in", value: 39.37007874015748031496062992126, names: {short: "in", full: "inch"}, group: "Anglo-american units"},
]
},
temperature:{
icon: `<i class="fas fa-thermometer-half"></i>`,
manual: true,
list: [
{id: "K", names: {short: "K", full: "kelvin degree"}, group: "", manualConverts: {
C: K => K - 273.15,
F: K => ((K - 273.15) * 1.8) + 32,
R: K => (K - 273.15) * 0.8
}},
{id: "C", names: {short: "&degC", full: "celcius degree"}, group: "", manualConverts: {
K: C => C + 273.15,
F: C => (C * 1.8) + 32,
R: C => C * 0.8
}},
{id: "F", names: {short: "&degF", full: "fahrenheit degree"}, group: "", manualConverts: {
K: F => F + 273.15,
C: F => (F - 32) / 1.8,
R: F => (F - 32) * 0.44444
}},
{id: "R", names: {short: "&degR", full: "réaumur degree"}, group: "", manualConverts: {
C: R => R / 0.8,
F: R => (R * 2.25) + 32,
K: R => (R / 0.8) + 273.15
}}
]
}
};
constructor(props = {}){
Expand All @@ -38,12 +83,15 @@ class Converter{
}
appendTo(selector){
//avoid multiple appending one converter to page
if(this.appended) return;
if(this.appended) return "This converter was already appended!";
this.appended = true;

//create wrapper of converter
const wrapper = createContainerWithClasses("div", this.type, "converter");

const converterTitle = createContainerWithClasses("h2", "converter-main-title");
converterTitle.innerHTML = this.dataByType.icon + this.type + " converter";

//input for typing value
const valueInput = createConverterValueInput(this.type);

Expand Down Expand Up @@ -89,6 +137,7 @@ class Converter{
precisionRange.onclick = event => moveConverterRangeCaption(event.target.parentNode);

//appending elements to wrapper and then appending to block with parameter selector:
wrapper.appendChild(converterTitle);
wrapper.appendChild(fromTitle);
wrapper.appendChild(valueInput);
wrapper.appendChild(fromSelect);
Expand All @@ -102,33 +151,65 @@ class Converter{
getById(id, from){
return from.find(record => record.id === id);
}
convert(props = {}){
convert(props){
console.log(`Converting ${props.value} ${props.from} to ${props.to || "all"}`);
if(!props.value) return "Value is invalid!";

//deep copy of array
//deep copy of array. This kind of copy makes functions null and they dissapear
const unitsOfMeasurementList = this.dataByType.list.createDeepCopy();
console.log(JSON.stringify(unitsOfMeasurementList.map(a => a.value).join(", ")));

//value of unit, that we are converting from
const fromUnitOfMeasurement = this.getById(props.from, unitsOfMeasurementList);
if(!fromUnitOfMeasurement) return "From value is invalid!";

const toUnitOfMeasurement = this.getById(props.to, unitsOfMeasurementList);

this.convertAlgorithm({
...props,
list: unitsOfMeasurementList,
fromCopy: fromUnitOfMeasurement,
});
console.log(unitsOfMeasurementList)

return toUnitOfMeasurement || unitsOfMeasurementList;
}
convertAlgorithm(props){
return this.dataByType.manual ? this.specialConvert(props) : this.simpleConvert(props);
}
specialConvert(props = {}){
console.log(props)
const origFrom = this.getById(props.from, this.dataByType.list);
const manualConverts = origFrom.manualConverts;
for(const key of Object.keys(manualConverts)){
console.log(manualConverts[key](props.value));
}
props.list.forEach(unit => {
//if from and to value are equal, we set value of it 1, because there can be rounding issues
if(props.from === unit.id) unit.value = new Big(props.value);
else{
unit.value = new Big(manualConverts[unit.id](props.value));
}
});
}
simpleConvert(props = {}){
/*it is special coefitient, that makes first (props.from) unit of measurement value equal 1 (because a * (1/a) = 1)
* other units of measurement, multiplied by this value become value of conversion from 1 "from-unit" to 1 "to-unit"
* for example we want to convert km2 to m2. By default m2 is reset (because it is equal 1)
* firstly, we get km2 value, it is 0.000001. Then we multiply all by 1/0.000001. km becomes 1 (reset), m2 becomes 1000000.
* Now we know, that 1 km2 is 1000000 m2
*/
let reduceFraction = new Big(1 / fromUnitOfMeasurement.value);
let reduceFraction = new Big(1 / props.fromCopy.value);
//If we want to have (props.value) km2, we multiply it by reduceFraction
const convertedValue = reduceFraction.times(props.value);

//multiply unit value ny converted value
unitsOfMeasurementList.forEach(unit => unit.value = convertedValue.times(unit.value));

const toUnitOfMeasurement = this.getById(props.to, unitsOfMeasurementList);

return toUnitOfMeasurement || unitsOfMeasurementList;
props.list.forEach(unit => {
//if from and to value are equal, we set value of it 1, because there can be rounding issues
unit.value = (unit.id === props.from)
? unit.value = new Big(props.value)
: convertedValue.times(unit.value);
});
}
toHtmlList(props = {}){
const converted = props.converted;
Expand All @@ -137,7 +218,7 @@ class Converter{
return `
<li>
${record.value.toStandardForm(props.precision)}
<span class="primary-text">${record.names.short}</span>
<span class="primary-text">${record.names.short || record.names.full}</span>
</li>`;
};

Expand All @@ -153,3 +234,11 @@ const areaConverter = new Converter({
type: "area",
selector: "#area-converter"
});
const lengthConverter = new Converter({
type: "length",
selector: "#length-converter"
});
const temperatureConverter = new Converter({
type: "temperature",
selector: "#temperature-converter"
});
16 changes: 13 additions & 3 deletions js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,22 @@ const createToConverterSelect = type => createConverterSelect({
defaultOptionText: "All measurement units",
});

const createOptionsList = array => {
return array.map(record => {
const shortName = record.names.short ? `(${record.names.short})` : ``;
return `<option value="${record.id}">${record.names.full} ${shortName}</option>`;
});
};

const createOptgroups = list => {
const groups = list.map(record => record.group).getUniqueElements();
const groups = list.map(record => record.group).getUniqueElements().filter(Boolean);
if(!groups.length){
return createOptionsList(list);
}
console.log(groups)
const optgroups = groups.map(group => {
const groupContent = list.filter(record => record.group === group);
const optionsList = groupContent.map(record => `<option value="${record.id}">${record.names.full} (${record.names.short})</option>`);
return `<optgroup label="${group}">${optionsList.join("")}</optgroup>`;
return `<optgroup label="${group}">${createOptionsList(groupContent).join("")}</optgroup>`;
});
return optgroups.join("");
};
Expand Down
48 changes: 32 additions & 16 deletions js/propotypeExtension.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,55 @@ Array.prototype.getUniqueElements = function(){
Array.prototype.createDeepCopy = function(){
return JSON.parse(JSON.stringify(this));
}
Big.prototype.equals = function(number){
return !this.cmp(new Big(number));
};

Big.prototype.toStandardForm = function(precision){
//NORMAL DISPLAY CONSTANTS
//numbers in range from (10 ^ -ND_EXPONENT) to (10 ^ ND_EXPONENT) do not display in standard form
const ND_EXPONENT = 4;
//numbers, which mantissa`s length is less than ND_MANTISSA do not display in standard form
const ND_MANTISSA = ND_EXPONENT + 2;
//numbers in range from (10 ^ -MAX_NOT_STANDARD_EXPONENT) to (10 ^ MAX_NOT_STANDARD_EXPONENT) do not display in standard form
const MAX_SMALL_EXPONENT = 3;

const tenToPowerPart = exponent => `10 <sup>${exponent}</sup>`;
const isExponentSmallerThan = limit => Math.abs(intExponent) <= limit;

const exponential = this.toExponential();

//split exponential from using `e` symbol, because Big.toExponential() incudes only lowercase e
let [stringMantissa, stringExponent] = exponential.split("e");

//convert exponent to int to remove `+` sign before number
const numericExponent = parseInt(stringExponent);

//condition to show number without standard form
if(Math.abs(numericExponent) <= ND_EXPONENT && stringMantissa.length <= ND_MANTISSA){
return this.toNumber();
}
const intExponent = parseInt(stringExponent);
/*if number is written in form 1 x 10 ^ x, we skip 1 and return 10 ^ x
* we don`t use it in printing, because it can cause loss of data
* use parseFloat, because if we parseInt stringMantissa, 1.1313 and 1 will be equal to 1, but it is wrong because loss of data
*/
const numericMantissa = parseFloat(stringMantissa);
const decimalMantissa = parseFloat(stringMantissa);

if(numericMantissa === 1){
return `10 <sup>${numericExponent}</sup>`;
//if this number is one, we return 1 in order to avoid rounding problems
//Big.cmp() returns 0 if numbers are equal
if(this.equals(1)){
return 1;
}
//condition to show number without standard form
else if(isExponentSmallerThan(MAX_SMALL_EXPONENT) && this.c.length <= MAX_SMALL_EXPONENT){
console.log(this.c, stringMantissa);
return this.toNumber();
}
//if mantissa is 1, it means, that numbers contains only ten to power part
else if(decimalMantissa === 1){
return tenToPowerPart(intExponent);
}
//convert bigMantissa to Big in order to use Big.toFixed() method
if(precision && Number.isInteger(precision)){
else if(precision && Number.isInteger(precision)){
const bigMantissa = new Big(stringMantissa);
//if number has very small mantissa
if(isExponentSmallerThan(MAX_SMALL_EXPONENT - 1)){
//nultiply comma by exponent and set it to fixed
return (bigMantissa.times(10 ** intExponent)).toFixed(precision);
}
stringMantissa = bigMantissa.toFixed(precision);
}
//if numericExponent equals 0, we do not show it, because 10 ^ 0 = 1, and mantissa * 1 = mantissa
return `${stringMantissa} ${numericExponent ? `x 10 <sup>${numericExponent}` : ""}</sup>`;
//if intExponent equals 0, we do not show it, because 10 ^ 0 = 1, and mantissa * 1 = mantissa
return `${stringMantissa} ${intExponent ? ("x " + tenToPowerPart(intExponent)) : ""}</sup>`;
}
4 changes: 3 additions & 1 deletion js/tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ class Tabs{
isActive(index){
return this.headers[index].classList.contains("active");
}
}
}

const menu = new Tabs("main-navigation-tabs");
15 changes: 15 additions & 0 deletions styles/converter.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
display: flex;
flex-direction: column;
}
.converter-main-title{
text-transform: uppercase;
margin-top: 10px;
margin-bottom: 20px;
padding-bottom: 8px;
border-bottom: 2px solid var(--primary-blue);
}
.converter-main-title .fa, .converter-main-title .fas{
margin-right: 8px;
}
.converter .primary-text{
color: var(--secondary-blue);
}
Expand Down Expand Up @@ -76,4 +86,9 @@
.converter{
width: 100%;
}
}
@media only screen and (max-width: 700px){
.converter-main-title{
font-size: 1rem;
}
}

0 comments on commit 517d494

Please sign in to comment.