Calculadora RPN

A Calculadora RPN é um app HTML/CSS/JS que provê uma calculadora funcional usando a Reverse Polish Notation, como as calculadoras HP clássicas. Além da funcionalidade básica de calculadora, esta aplicação provê a utilização opcional de temas, para demonstrar o poder do CSS3 para controlar a aparência de uma aplicação baseada em HTML.

O código fonte deste exemplo pode ser encontrado aqui: https://github.com/gomobile/sample-rpn-calc.

Características de Linguagens

Este app foi escrito totalmente em HTML5, CSS3 e JavaScript, e demonstra um número de características destas linguages:

Lista de Tecnologias

  • HTML5
    • classes
  • CSS3
    • flex box
    • alterando links css
    • transições, gradientes,
  • JavaScript
    • JS Orientado a Objetos

Layout

O layout é feito usando a funcionalidade flex box do CSS3. A calculadora é composta por um elemento <div> contando um elemento <div> Display e um KeyPad. O KeyPad é composto por uma tabela de botões, usando o flexbox para controlar a distribuição horizontal dos botões, cada um em um elemento <td>. Cada elemento <td> possui a propriedade display setada para box, e a propriedade box-flex setada para 1, garantindo uma distribuição igualitária dentro de 100% da tabela. Isso funciona bem no Chrome e em outros ambientes baseados no webkit, mas o Firefox executa o layout de forma diferente, onde a largura de cada elemento <td> é afetada pela largura do texto contido nele. A diferença pode ser vistas nas imagens abaixo:

Screenshot do Chrome Screenshot do Firefox

A especificação de quais botões vão em cada elemento de uma linha é feita através de uma estrutura de dados em JavaScript, uma lista de listas de objetos. A lista de fora corresponde ao KeyPad, e cada elemento desta lista corresponde a uma linha. Cada elemento da lista interna é um botão, contendo um texto (text) e um elemento fcn. O elemento text contém o texto que será apresentado no botão, enquando a fcn é chamada pelo atributo onclick daquele botão. De fato, uma inspeção mais cuidadosa mostra que mesmo no Chrome a altura das linhas varia levemente devido a certas combinações de caracteres (ex. exponenciação, +/-) mas não é algo que se note com facilidade.

Dois layouts são definidos, o layout primário e o secundário, que são alterados pressionando o botão Fn (que é apresentado em ambos os layouts). Seria possível usar html também, mas usando este método nos trouxe alguns atalhos, como usando construtores em alguns casos (ex. NumButton para botões correspondentes a digitos) para simplificar declarações repetitivas e garantir consistência.

Temas

"Temas" são definidos utilizando JavaScript para modificar as tags de link dentro do arquivo html. Cada tema é apresentado com a class=theme o que cria uma lista de temas disponíveis no DOM, lista que pode ser circulada quando o botão Theme for pressionado. O link para cada tema está no arquivo html raiz, index.html, como:

 



	<link class=theme rel="stylesheet" type="text/css" href="css/themes/shinybuttons.css">

	

 

A lista dos temas é criada do DOM:

 



	var l = document.getElementsByClassName('theme');

	

 

que então é usada pelo método toggleTheme que é usado pelo método onclick do botão Theme.

Cada tema é definido por um arquivo CSS separado, dentro do diretório css/themes. Uma variedade de técnicas de CSS são ilustrada nos diversos temas. Para o tema padrão, "shinybuttons," uma imagem de fundo padrão é utilizada em cada botão. Esta imagem foi originalmente criada como um arquivo SVG, que usa gradientes e filtros para obter os efeitos de iluminação e transparência. Infelizmente, estas imagens não funcionam em dispositivos Android, então elas foram convertidas para arquivos PNG, que funcionam em todas as plataformas testadas. Outros temas ilustram funcionalidades como caixas com cantos curvos, gradientes CSS e mesmo efeitos animados de transição. 

Uma das vantagens de usar o layout flex box é ilustrada nos temas "bubbles". Nestes temas, um seletor hover é usado para aumentar o tamanho do elemento atual.  Em vez de simplesmente puxar para o lado os botões ao redor, eles são encolhidos, revelando um efeito de lente de aumento. Isto é diferente da transformação de escala utilizada no tema "green", onde o efeito de zoom do hover não afeta os botões vizinhos.

Bubbles - Efeito de aumento Green - Escalonamento

Semântica

A semântica da calculadora e da sua máquina de pilha é implementada em JavaScript. A máquina básica é composta por uma pilha de quatro elementos que são operados pelos diversos botões. Por exemplo, quando o botão '+' é pressionado, é feito um pop com os dois elementos do topo da pilha, eles são somados com o resultado e jogados de volta para o display. O display representa o topo atual da pilha. Editar o display é um caso especial, pois o display pode estar em um dos múltiplos estados, tanto em processo de ser editado ou com o push já realizado. Por exemplo, quando é feito um push no display, ele retém uma cópia de si próprio, que é substituída se a entrada de um novo número for realizada. De forma similar, quando é feito um pop na pilha a entrada de baixo da pilha é duplicada.

No geral, a matemática provida pela calculadora corresponde à matemática que o interpretador JavaScript provê. Além disso, o botão '+' simplesmente soma dois números em JavaScript, independente do seu tipo. Isso pode resultar em números como Infinity aparecendo no display; não típico para uma calculadora, mas sensato em JavaScript.

Testando, Especificidades e Soluções

O app foi testado em um número de dispositivos e plataformas, incluíndo Firefox and Chrome em um laptop e diversos dispositivos Android e iOS (smartphones e tablets). Se mostrou como uma boa demonstração da variedade de dificultades e inconsistências que podem ser encontradas nas diversas plataformas e navegadores: 

  • No Mozilla, a colunas de botões possuem larguras diferentes
  • O Webkit não suporta o Mathml

Originalmente, foi planejada a utilização de Mathml para gerar matematicamente os símbolos, como o símbolo de raiz quadrada e exponencial, entre outros. Infelizmente, alguns navegadores baseados no Webkit não suportam Mathml neste momento. Em particular, o Chrome e o navegador de dispositivos baseados no Android parecem não suportar Mathml. Para evitar isso, foi adotada a utilização de caracteres especiais do HTML como "&radic;", combinados com uma borda superior sobre o "x" subsequente. Isto tem a desvantagem de requerer muita tentativa e erro para se obter um resultado razoável, e é inconsistente através de fontes e dispositivos. Infelizmente isso também gera outras implicações, pois o símbolo de multiplicação  ('×') não parece ser bem suportado em dispositivos Android. Como resultado, foi utilizado um asterisco ('*') em seu lugar, o que apesar da clareza acaba sendo estéticamente menos apelativo.

Apesar de não implementada aqui, provavelmente a solução mais confiável teria sido a geração de imagens de fundo com os símbolos e textos apropriados. Isto garantiria maior consistência, mas traria alguma perda na flexibilidade (ex. não seria possível alterar as fontes).

Uma outra vantagem seria o tratar o que o Mozilla faz com a largura dos botões, tendo em vista que cada imagem de fundo teria o mesmo tamanho, eliminando as variações e encontradas devido à alura e largura do texto.

  • O Android não suporta as funcionalidades do SVG de forma adequada

Como mencionado acima, este problema foi solucionado pela conversão das imagens SVG em PNG.

  • Não existe hover em dispositivos com tela sensível ao toque

Alguns dos temas (green e bubble, entre outros) são baseados em um seletor hover para gerar as alterações, mas isso não se traduz bem para dispositivos com tela sensível ao toque. Uma solução melhor seria especializar, ou até mesmo remover, estes temas de acordo com o dispositivo utilizado.

Para obter informações mais completas sobre otimizações do compilador, consulte nosso aviso de otimização.