Hace mucho tiempo en mis inicios en la programación hice un conversor de unidades usando JavaScript, HTML, CSS y los frameworks Bootstrap y jQuery. Era para mi clase de física. Hoy vengo a presentarlo y compartirlo por si a alguien más le sirve.
Es un simple convertidor de unidades web que transforma de una unidad a otra. Soporta:
- Longitud
- Masa
- Tiempo
- Energía
- Frecuencia
- Presión
- Tamaño de datos
- Temperatura
- Velocidad
- Volumen
- Área
A través de este post te mostraré cómo funciona, cómo descargarlo y cómo está hecho.
Unidades soportadas
Soporta todos los grupos de unidades de medidas listadas anteriormente. Por ejemplo en longitud tiene metros, kilómetros, etcétera. En masa tiene gramo, kilogramo, y así para cada categoría.
He escrito todas las unidades y sus equivalencias en un diccionario. Es un archivo grande pues tiene todas las equivalencias como si fuera la base de datos. Se ve así (aunque no es el completo):
/**
* Created by Luis Cabrera Benito on 27/01/2016.
*/
var unidades = {
'Longitud': {
'grupo': 'Longitud',
'unidades': [
'kilometro',
'metro',
'centimetro',
'milimetro',
'micrometro',
'nanometro',
'milla',
'yarda',
'pie',
'pulgada'
],
'equivalencias': {
'kilometro': {
'kilometro': 1,
'metro': 1000,
'centimetro': 100000,
'milimetro': 1e+6,
'micrometro': 1e+9,
'nanometro': 1e+12,
'milla': 0.621371,
'yarda': 1093.61,
'pie': 3280.84,
'pulgada': 39370.1
},
}
};
Ahora ese archivo es leído desde el verdadero conversor cuya plantilla HTML es la siguiente en donde cargamos los scripts incluyendo a jQuery.
<!doctype html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="./css/bootstrap.css">
<link rel="stylesheet" href="./css/fa.css">
<link rel="stylesheet" href="./css/styles.css">
<title>Conversor de unidades - Dennis</title>
</head>
<body>
<div class="container-fluid">
<div class="row "><br>
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<div class="form-group">
<label for="principal">¿Qué quieres convertir?
<button class="btn btn-info" id="mostrarEjemplo">Ver un ejemplo</button>
</label>
<input type="text" class="form-control" id="principal" placeholder="256 bits a megabytes"
autocomplete="off">
</div>
<br>
<div class="alert" id="resultado">
</div>
<br>
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 ocultable" hidden="hidden">
<div class="row ">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<div class="form-group">
<label for="grupoUnidades">Unidad de medida</label>
<select name="grupoUnidades" id="grupoUnidades" class="form-control"></select>
</div>
</div>
<br><br>
<div class="col-xs-6 col-sm-6 col-md-6 col-lg-6">
<label for="primeraUnidad">De</label>
<input id="inputPrimeraUnidad" type="text" class="form-control">
<select name="primeraUnidad" id="primeraUnidad" class="form-control select"></select>
</div>
<div class="col-xs-6 col-sm-6 col-md-6 col-lg-6">
<label for="segundaUnidad">A</label>
<input id="inputSegundaUnidad" type="text" class="form-control">
<select name="segundaUnidad" id="segundaUnidad" class="form-control"></select>
</div>
</div>
</div>
<br><br><br>
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<button id="btn-about" class="btn btn-primary form-control"><i
class="material-icons fa fa-info-circle"></i> Acerca de
</button>
</div>
</div>
<div id="acercaDe" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="text-center">¡Hola!, me llamo Dennis y puedo convertir unidades.<br>
<strong>Nota:</strong>
Por el momento no entiendo las abreviaciones como "km" o "m",
pero mejoraré día a día. <i class="fa fa-smile-o"></i></h4>
</div>
<div class="modal-body">
<strong>Unidades que puedo convertir hasta el momento:</strong>
<ul class="list-group">
<li class="list-group-item list-group-item-success">Longitud</li>
<li class="list-group-item list-group-item-success">Masa</li>
<li class="list-group-item list-group-item-success">Tiempo - Gracias a <strong><a
href="https://twitter.com/MariJose961128">Marijo Sacramento</a></strong> por
escribir las equivalencias
</li>
<li class="list-group-item list-group-item-success">Energía - Gracias a <strong><a
href="https://twitter.com/MariJose961128">Marijo Sacramento</a></strong> por
escribir las equivalencias
</li>
<li class="list-group-item list-group-item-success">Frecuencia - Gracias a <strong><a
href="https://twitter.com/MariJose961128">Marijo Sacramento</a></strong> por
escribir las equivalencias
</li>
<li class="list-group-item list-group-item-success">Presión - Gracias a <strong><a
href="https://twitter.com/MariJose961128">Marijo Sacramento</a></strong> por
escribir las equivalencias
</li>
<li class="list-group-item list-group-item-success">Tamaño de datos - Gracias a <strong><a
href="https://twitter.com/MariJose961128">Marijo Sacramento</a></strong> por
escribir las equivalencias
</li>
<li class="list-group-item list-group-item-success">Temperatura</li>
<li class="list-group-item list-group-item-success">Velocidad</li>
<li class="list-group-item list-group-item-success">Volumen</li>
<li class="list-group-item list-group-item-success">Área - Gracias a <strong><a
href="https://twitter.com/MariJose961128">Marijo Sacramento</a></strong> por
escribir las equivalencias
</li>
</ul>
<br>
<p>Más unidades vienen en camino...</p>
<p>Creado y mantenido por <a href="https://www.facebook.com/cabrerabenito">Luis Cabrera
Benito</a></p>
<p>Programado en Javascript utilizando jQuery. Los estilos y algunas funcionalidades son gracias
a Bootstrap.</p>
<p>Código fuente disponible en GitHub. <a
href="https://github.com/parzibyte/dennis-conversor">Ver código</a></p>
<hr>
<p>Bautizado como Dennis en honor a <strong><a
href="https://es.wikipedia.org/wiki/Dennis_Ritchie">Dennis Ritchie</a></strong></p>
<blockquote>
<p>Vacía tu memoria con un <i>free()</i>, como un puntero.
<br>
Si casteas un puntero a un <i>entero</i>, se convierte en un <i>entero</i>,
<br>
si casteas un puntero a un <i>struct</i>, se convierte en un <i>struct</i>.
<br>
El puntero puede estallar,
<br>
el puntero se puede sobrecargar,
<br>
¡Sé un puntero, amigo mío!
</p>
<footer>Dennis Ritchie</footer>
</blockquote>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
</div>
</div>
</div>
</div>
<div id="manual" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="text-center">Cómo hablarle a Dennis</h4>
</div>
<div class="modal-body text-center">
<p>Dennis aceptará que le hables como a una persona con frases como:</p><br>
<ul class="list-group">
<li class="list-group-item list-group-item-success">1 milla a metros</li>
<li class="list-group-item list-group-item-success">15.05 libra por pulgada cuadrada en
atmosfera
</li>
<li class="list-group-item list-group-item-success">28 año natural a siglo</li>
<li class="list-group-item list-group-item-success">11 kilohertz a hercios</li>
<li class="list-group-item list-group-item-success">96 bits en kilobits</li>
</ul>
<br>
<p><strong>Nota: </strong>Dennis acepta palabras en singular, y en plural (por ejemplo,
"metros") pero solamente si
la unidad no tiene espacios. Ya que por ejemplo "grados celsius" no será detectado
correctamente. En ese caso
es mejor escribir "grado celsius".</p>
<br>
<p>Conforme vayas escribiendo, Dennis te avisará si es que tienes un error</p>
<img src="./img/1.png" alt="Imagen 1" class="img img-responsive img-thumbnail">
<br>
<p>Si Dennis no reconoce una unidad (a veces es porque no reconoce los plurales, por ejemplo
"años naturales") te lo dirá.</p>
<img src="./img/2.png" alt="Imagen 2" class="img img-responsive img-thumbnail"><br>
<br>
<p>Una vez que Dennis haya reconocido lo que le dijiste, lo convertirá y te mostrará unos
formularios</p>
<img src="./img/3.png" alt="Imagen 3" class="img img-responsive img-thumbnail"><br>
<p>Dichos formularios son para que interactúes mejor con Dennis, y no tengas que estar
escribiendo.</p>
<p>Puedes mover y cambiar valores a tu gusto.</p>
<img src="./img/4.png" alt="Imagen 4" class="img img-responsive img-thumbnail"><br>
<img src="./img/5.png" alt="Imagen 5" class="img img-responsive img-thumbnail"><br>
<img src="./img/6.png" alt="Imagen 6" class="img img-responsive img-thumbnail"><br>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" data-dismiss="modal">Entendido</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="./js/jquery.js"></script>
<script type="text/javascript" src="./js/bootstrap.js"></script>
<script type="text/javascript" src="./js/unidades.js"></script>
<script type="text/javascript" src="./js/conversor.js"></script>
</body>
</html>
Escuchamos ya sea que cambie el input o unos de los seleccionadores y se hace la conversión, quedando el código así:
/**
* Created by Luis Cabrera Benito on 25/01/2016.
*/
$(document).ready(function () {
principal();
$selectPrimerGrupo.val('metro');
});
var $inputPrincipal = $('input#principal'),
$resultado = $('div#resultado'),
$selectGrupoUnidades = $('select#grupoUnidades'),
$selectPrimerGrupo = $('select#primeraUnidad'),
$selectSegundoGrupo = $('select#segundaUnidad'),
$inputPrimerGrupo = $('input#inputPrimeraUnidad'),
$inputSegundoGrupo = $('input#inputSegundaUnidad'),
$btnEjemplo = $('button#mostrarEjemplo'),
$ventanaAcercaDe = $('div#acercaDe'),
$btnAcercaDe = $('button#btn-about'),
contador = 0,
segundosCambiaPlaceholder = 1,
limiteLongitudNumeros = 16,
palabrasPermitidas = ["en", "a"],
sugerencias = [
"20 hercios a kilohertz",
"1 milimetro en pies",
"14.6 miligramos en onzas",
"256 bits a megabytes",
"15 mes a horas",
"256 bits a megabytes"
];
function aLetraCapital(palabra) {
return palabra[0].toUpperCase() + palabra.slice(1, palabra.length);
}
function cambiaPlaceholder() {
if (contador < sugerencias.length) {
$inputPrincipal.attr('placeholder', sugerencias[contador]);
contador++;
} else {
contador = 0;
}
}
function compruebaValores(oracion) {
if (oracion) {
var oracionCortada = cortaOracion(oracion);
if (isNaN(oracionCortada)) {
var numero = oracionCortada.numero,
unidad1 = oracionCortada.u1.toLowerCase(),
unidad2 = oracionCortada.u2.toLowerCase();
if (!isNaN(numero)) {
if (numero.length <= limiteLongitudNumeros - 1) {
if (dameGrupo(unidad1) === false) {
unidad1 = unidad1.slice(0, -1);
}
if (dameGrupo(unidad2) === false) {
unidad2 = unidad2.slice(0, -1);
}
var grupoUnidad1 = dameGrupo(unidad1),
grupoUnidad2 = dameGrupo(unidad2);
if (grupoUnidad1 !== false) {
if (grupoUnidad2 !== false) {
if (grupoUnidad1 === grupoUnidad2) {
convierte(grupoUnidad1, grupoUnidad2, numero, unidad1, unidad2, false);
} else {
escribeError('Los grupos de unidades no son compatibles: <strong>' + unidad1 + ' y ' + unidad2 + '</strong>');
}
} else {
escribeError('La segunda unidad no existe: <strong>' + unidad2 + '</strong>');
}
} else {
escribeError('La primera unidad no existe: <strong>' + unidad1 + '</strong>');
}
} else {
escribeError('Lo siento, solamente se permiten números de <strong>16 cifras</strong> sin incluir el punto decimal');
}
} else {
escribeError('La primera parte de la oración no es un número: <strong>' + numero + '</strong>');
}
} else {
switch (oracionCortada) {
case 0:
escribeError('No entiendo lo que quieres. Recuerda que debes unir con "en" o "a", por ejemplo "1 milla <strong>a</strong> metros"');
break;
case 1:
escribeError('No entiendo lo que quieres. Recuerda que no debes <strong>incluir espacios</strong> al inicio');
break;
}
}
} else {
$('.ocultable').hide();
}
}
function convierte(grupo, grupo2, numero, u1, u2, esDelSegundoInput) {
var resultado = dameEquivalencia(grupo, u1, u2) * numero;
if (grupo === "Temperatura") {
resultado = grados(grupo, u1, u2, numero);
}
llenaPrimerGrupo($selectPrimerGrupo, grupo);
llenaSegundoGrupo($selectSegundoGrupo, grupo2);
$selectGrupoUnidades.val(grupo);
if (esDelSegundoInput) {
$inputPrimerGrupo.val(resultado);
$inputSegundoGrupo.val(numero);
$selectPrimerGrupo.val(u2);
$selectSegundoGrupo.val(u1);
} else {
$inputPrimerGrupo.val(numero);
$inputSegundoGrupo.val(resultado);
$selectPrimerGrupo.val(u1);
$selectSegundoGrupo.val(u2);
}
escribeResultado(numero, u1, u2, resultado);
}
function cortaOracion(o) {
var posicionPrimerNumero = o.indexOf(" ");
if (posicionPrimerNumero > 0) {
var primerNumero = o.slice(0, posicionPrimerNumero);
var posicionNexo;
var nexo;
var x;
for (x in palabrasPermitidas) {
posicionNexo = o.indexOf(" " + palabrasPermitidas[x] + " ", posicionPrimerNumero + 1);
if (posicionNexo !== -1) {
break;
}
}
if (posicionNexo !== -1) {
nexo = palabrasPermitidas[x];
var unidad1 = o.slice(posicionPrimerNumero + 1, posicionNexo);
var unidad2 = o.slice(posicionNexo + 2 + nexo.length, o.length);
return {
'numero': primerNumero,
'u1': unidad1,
'u2': unidad2
};
} else {
return 0;
}
} else {
return 1;
}
}
function dameEquivalencia(grupo, u1, u2) {
return unidades[grupo].equivalencias[u1][u2];
}
function dameGrupo(unidad) {
for (var x in unidades) {
if (unidades[x].unidades.indexOf(unidad) !== -1) {
return unidades[x].grupo;
}
}
return false;
}
function escribeError(mensaje) {
$('.ocultable').hide();
$resultado
.removeClass('alert-success')
.addClass('alert-danger');
$resultado.html('<h5>' + mensaje + '</h5>');
}
function escribeResultado(numero, u1, u2, resultado) {
var primeraUnidadFinal = (numero === 1) ? u1 : u1 + 's';
var cadenaSegundaUnidad = (resultado === 1) ? u2 : u2 + 's';
var cadenaEquivalencia = (numero === 1) ? " equivale " : " equivalen ";
var mensaje = '<h5>'
+ '<strong>' + numero + '</strong>'
+ ' '
+ primeraUnidadFinal
+ cadenaEquivalencia + 'a '
+ '<strong>' + resultado + '</strong>'
+ ' '
+ cadenaSegundaUnidad
+ '</h5>';
$resultado
.removeClass('alert-danger')
.addClass('alert-success');
$resultado.html(mensaje);
$('.ocultable').show();
}
function escuchaElementos() {
$selectGrupoUnidades.change(function () {
llenaPrimerGrupo($selectPrimerGrupo, $(this).val());
llenaSegundoGrupo($selectSegundoGrupo, $(this).val());
$inputPrimerGrupo.val(1);
$inputPrimerGrupo.keyup();
convierte(
$(this).val(),
$(this).val(),
$inputPrimerGrupo.val(),
$selectPrimerGrupo.val(),
$selectSegundoGrupo.val(), false);
});
$selectPrimerGrupo.change(function () {
convierte(
$selectGrupoUnidades.val(),
$selectGrupoUnidades.val(),
$inputPrimerGrupo.val(),
$(this).val(),
$selectSegundoGrupo.val(), false);
});
$selectSegundoGrupo.change(function () {
convierte(
$selectGrupoUnidades.val(),
$selectGrupoUnidades.val(),
$inputPrimerGrupo.val(),
$selectPrimerGrupo.val(),
$(this).val(), false);
});
$inputPrimerGrupo.keyup(function () {
if (!isNaN($(this).val())) {
if ($(this).val()) {
if ($(this).val().length <= limiteLongitudNumeros - 1) {
convierte(
$selectGrupoUnidades.val(),
$selectGrupoUnidades.val(),
$(this).val(),
$selectPrimerGrupo.val(),
$selectSegundoGrupo.val(), false);
} else {
escribeError('Lo siento, solamente se permiten números de <strong>16 cifras</strong> sin incluir el punto decimal');
$('.ocultable').show();
}
}
} else {
escribeError('Ingresa un número en la caja de texto número 1');
$('.ocultable').show();
}
});
$inputSegundoGrupo.keyup(function () {
if (!isNaN($(this).val())) {
if ($(this).val()) {
if ($(this).val().length <= limiteLongitudNumeros - 1) {
convierte(
$selectGrupoUnidades.val(),
$selectGrupoUnidades.val(),
$(this).val(),
$selectSegundoGrupo.val(),
$selectPrimerGrupo.val(), true);
} else {
escribeError('Lo siento, solamente se permiten números de <strong>16 cifras</strong> sin incluir el punto decimal');
$('.ocultable').show();
}
}
} else {
escribeError('Ingresa un número en la caja de texto número 2');
$('.ocultable').show();
}
});
$btnAcercaDe.click(function () {
$ventanaAcercaDe.modal('show');
});
$inputPrincipal.keyup(function () {
compruebaValores($(this).val());
});
$btnEjemplo.click(function () {
$('div#manual').modal('show');
});
}
function grados(grupo, u1, u2, numero) {
if (grupo === "Temperatura") {
switch (u1) {
case "grado fahrenheit":
switch (u2) {
case "grado fahrenheit":
return numero;
break;
case "grado celsius":
return (numero - 32) * (5 / 9);
break;
case "kelvin":
return ((5 * (numero - 32)) / 9) + 273.15;
break;
}
break;
case "grado celsius":
switch (u2) {
case "grado fahrenheit":
return 32 + ((9 / 5) * numero );
break;
case "grado celsius":
return numero;
break;
case "kelvin":
return numero + 273.15;
break;
}
break;
case "kelvin":
switch (u2) {
case "grado fahrenheit":
return ((9 * (numero - 273.15)) / 5) + 32;
break;
case "grado celsius":
return numero - 273.15;
break;
case "kelvin":
return numero;
break;
}
break;
}
}
}
function llenaPrimerGrupo(selector, grupo) {
selector.empty();
for (var x in unidades[grupo].unidades) {
selector.append($('<option>', {
value: unidades[grupo].unidades[x],
text: aLetraCapital(unidades[grupo].unidades[x])
}));
}
}
function llenaSegundoGrupo(selector, grupo) {
selector.empty();
for (var x in unidades[grupo].unidades) {
selector.append($('<option>', {
value: unidades[grupo].unidades[x],
text: aLetraCapital(unidades[grupo].unidades[x])
}));
}
}
function llenaSelectUnidades(selector) {
for (var opcion in unidades) {
selector.append($('<option>', {
value: opcion,
text: opcion
}));
}
}
function principal() {
$inputPrincipal.focus();
setInterval(cambiaPlaceholder, segundosCambiaPlaceholder * 1000);
llenaSelectUnidades($selectGrupoUnidades);
escuchaElementos();
$('.ocultable').hide();
}
Poniendo todo junto
Todo este conversor está hecho con Bootstrap (tanto estilos como funcionamiento de modals) y jQuery. Sé que jQuery ya casi no se usa pero recuerda que este programa fue hecho hace 5 años y apenas lo he publicado.
Si quieres el código fuente completo lo dejo en GitHub. También puedes acceder a una demostración aquí.