Calculadora em JavaScript – PT2

Se arrependimento matasse…

Parte 2 de 2;

Código bruto

Montada a GUI e engatilhados os eventos, agora ela tem de fazer algo. Com javascript, dedicação e um pouco de fé, vai funcionar.

Nosso objetivo final é algo assim: http://ddona.free0host.com/calcjs/

Parecia algo tão simples programar uma calculadora. Mas deu muitos problemas deixá-la como eu queria sem o uso de eval. Tudo começou pois a amiga de uma amiga tinha de fazer uma para a faculdade de castigo e eu acabei sendo escalado pra programar a desgrama.

Ela levou uma versão com cerca de 100 linhas e alguns bugs. A versão final está com 192 xD.

Sinceramente não sei se cometi uma “manuelzada” no algoritmo, mas não encontrei nenhuma outra para comparar.

A teoria é esta:

  • O usuário pode digitar qualquer número racional (inteiros, quebrados)
  • O usuário deve escolher uma única operação (se clicar na subtração e depois em multiplicação, prevalece sempre última)
  • O usuário deve digitar um novo número, se for o primeiro cálculo (12 * o quê?).
  • O botão de igual só vai dar resultados se tivermos uma operação válida a avaliar (12 * 2). Se tivermos (12 / 0) ou alguma coisa do gênero, o resultado sempre será um display limpo e o reinício do processo (como se voltassemos ao primeiro numero)
  • Ao pressionar um botão de operação pela segunda vez, o display já se atualiza com o valor da operação anterior

Segue o código fonte comentado e, até onde eu sei, funcionando!

var num2,resultado;
var vez=0;
var operacaoLocal,operacaoGlobal=null;
var statusOperacao=0;

/*******************

statusOperacao:
Verifica se estamos com alguma operação aritmética ativada,
evitando que o usuario faça N divisões antes de trocar o numero

----
vez:
Da vez "0" que fazemos uma operação, deixamos o valor como resultado

15 +... -> o 15 será nosso primeiro resultado

Da vez "1", pegamos o numero digitado

... + 25 -> 25 será o num2

Da vez "2", fazemos a conta, resultado + num2

Evitamos a volta a vez 0 pois o resultado com que lidaremos é sempre o que está no display
----

operacaoGlobal:
Detecta se o usuário está escolhendo operações diferentes em sequencia
operacaoLocal:
Serve para podermos atualizar o display com a operação anterior

*******************/

function display(numero)
{
	x = document.getElementById("display");

	//NaN = Not a Number (!NaN = é um número)
	//isFinite é para evitar divisão por 0
	if(!(isNaN(numero)) && isFinite(numero))
	{
		x.value = resultado.toString();
		statusOperacao = 1;
	}
	else
	{
		limpar();
	}
}

function executarCalc(op)
{
	switch (op)
	{
		case "+":
			//ADIÇÃO
			resultado = resultado + num2;
			break;

		case "-":
			//SUBTRAÇÃO
			resultado = resultado - num2;
			break;

		case "*":
			//MULTIPLICAÇÃO
			resultado = resultado * num2;
			break;

		case "/":
			//DIVISÃO
			resultado = resultado / num2;
			break;
	}
}

function acres(numero)
{
	var i,count=0;
	x = document.getElementById("display");

	//conta quantos pontos temos no display
	for(i in x.value)
	{
		if(x.value[i] == '.')
		{
			count++;
		}
	}

	if(numero == '.' && (count > 0 || x.value.length == 0))
	{
		return(0);
	}

	if(statusOperacao == 1)
	{
		statusOperacao =  0;
		//corrigimos a operação global para a última escolhida
		operacaoGlobal = operacaoLocal;
		x.value = "";
		vez = 1;
	}

	x.value = x.value + numero;	

}

function limpar()
{
	x = document.getElementById("display");

	x.value = "";
	resultado = 0;
	num2 = 0;
	vez=0;
	statusOperacao=0;
	operacaoGlobal = null;
	operacaoAux = null;
	operacaoLocal = null;
}

function calc(operacao)
{
	operacaoLocal = operacao;
	x = document.getElementById("display");
	valor = parseFloat(x.value);
	//alert("global: "+operacaoGlobal);
	//alert("op: "+operacao);

	if(statusOperacao == 1)
	{
		return(0);
	}

	if(vez == 0)
	{
		resultado = valor;
		vez=1;
		x.value = "";
		//alert("atribuindo "+resultado+" para resultado");
		statusOperacao = 1;
		operacaoGlobal = operacaoLocal;
		return(0);
	}

	if(vez == 1)
	{
		num2 = valor;

		//vemos se o usuário pressionou soma, digitou um valor
		//e depois mudou a operacão
		if(operacaoGlobal != operacaoLocal)
		{
			executarCalc(operacaoGlobal);
			//re-ajustamos o valor
			num2 = valor;
			//passamos pra próxima vez
			vez=2;
			//atualizamos o display
			display(resultado);
			//estamos numa operação...
			statusOperacao = 1;
			//igualamos a operação
			operacaoGlobal = operacaoLocal;
			//paramos a função, se não fará a próxima conta sem o input do usuário
			return(0);
		}
		else
		{
			vez = 2;
		}
	}

	if(vez == 2)
	{
		executarCalc(operacaoLocal);
		display(resultado);
		operacaoGlobal = operacaoLocal;
	}
}

function resFinal()
{
	x = document.getElementById("display");

	if(statusOperacao == 0)
	num2 = parseFloat(x.value);

	executarCalc(operacaoLocal);
	display(resultado);
	//re-inicia o ciclo
	vez=1;
	//demos o resultado, precisamos pedir que o usuário efetue uma nova operação
	statusOperacao = 1;
}

Qualquer coisa, basta entrar em contato.

cya!

Calculadora em JavaScript – PT1

Calculadora do Windows:

A melhor coisa do Sistema Operacional. :D

Parte 1 de 2;

Criação da interface e engatilhar eventos.

Este post é um tutorial mais simples e detalhista para a interface, se você já sabe como lidar com o DreamWeaver e algo de CSS, pode pular direto para a parte 2. Apenas dê uma olhada na parte final, pois vamos engatilhar os eventos nos botões e etc.

Nosso objetivo final é algo assim: http://ddona.free0host.com/calcjs/

Parte 2, da codificação, aqui.

Já tentou criar uma calculadora de verdade com programação? Existem muitos exemplos de calculadoras com 2 campos de valores, botões de operação e um campo de resultado.

Esse é um modelo que facilita muito a codificação, pois você deposita no usuário o problema de organização das operações.

O bixo pega quando você tenta fazer somente um campo, que abrigará N valores e compete com a mostra de resultados. Estilo uma calculadora de verdade.

Quando mexia com Delphi, fiz uma calculadora nesse estilo. Porém, após alguns testes, percebi que ela fazia apenas 2 termos e resultado (2+2 = 4, 2 / 2 = 1…), me enrolando muito para deixar no modelo de N termos.

O grande problema é o seguinte, em minha visão: Você precisa saber quando o que está no display é resultado ou um valor de operação.

Existem algumas calculadoras em JS na internet que usam a função eval. Basicamente, ela considera tudo que é digitado é faz as operações ou executa os códigos.

Se eu entrar com uma string “2*2 + 5 * 7”, o retorno será 39. O problema é que se o usuário conseguir entrar com algum código javascript, este também será executado.

So what?

Sentei e fiz uma sem eval. Me surpreendi pois foi um código bem extenso, lidando com alguns problemas que nunca havia notado. Por isso opto por dividir este artigo em 2 partes, esta para a criação da interface e “engatilhar” as funções e o próximo para codificar.

Componentes e estrutura

O layout de nossa calculadora será numa tabela.

Esboço

Esboço

A primeira linha será ocupada pelo visor, o campo de operações.

Nos campos cinza-claro, teremos os números, o botão de ponto e uma tecla de “clear”. Nos cinza-escuro, os botões de operação (adição, subtração…).

A última terá o botão de igual.

Se não sabe como montar essa tabela,

table_01Basta selecionar as linhas e clicar no botão indicado, para uni-las.

Coisa linda.

Estamos com a estrutura preparada.

Vá até a aba FORMS.

btnTxtClique na primeira linha de sua tabela e clique sobre o ícone do Textfield.

caixaInput

Se esta tela aparecer, maravilha.

Em ID, preencha com Display, é a identificação do objeto na sua página HTML. Na parte de style, marque a opção “No label tag”. A label tag serve para deixar um texto ao lado do objeto que você inseriu, sendo neste projeto inútil.

Pressione OK e mais uma tela surgirá:

caixaInput2

Geralmente quando usamos textfields, botões e outros objetos da aba Forms, o dreamweaver pensa que estamos criando um formulário. Não é o nosso caso, quantas vezes esta tela aparecer neste projeto, clique não.

Agora sua tabela deve estar assim:

table_02

Clique sobre a primeira célula cinza-claro e vamos inserir um botão.

btnTxt

As mesmas telas de antes surgirão. Dessa vez não é necessário preencher o ID, basta clicar em OK.

Agora uma jogadinha:

table_03

Clique sobre o botão que você acabou de inserir. Se a aba “Properties”(propriedades) não estiver aparecendo, pressione CTRL+F3. Em value, preencha com o número que estaria no botão.

Não se preocupe se ela ficar deformada, depois consertaremos a interface. Repita o processo do botão com todos os espaços.

Se lembre de adicionar os numeros de 0 a 9, o ponto, os sinais de operação e o botão de igual, que deve ficar na última linha.

Díca: Se você clicar sob um botão já inserido, der um CTRL+C e CTRL+V em outra célula, você só tem que editar o value dele.

No final, deve ser algo assim:

table_04

Próximo passo, clique no canto da tabela, selecionando-a:table_05

Primeiramente, clique sobre a ‘esponjinha’ para resetar o tamanho da tabela. Depois, na marcação verde (o campo de ‘id’), digite calc.

CSS e interface

Agora, vamos acertar a interface. Sabe o painel CSS Styles? Não? Pressione Shift+f11:

css_panel

Ele deve surgir na direita. Precisamos dele pois vamos estilizar nossa calculadora usando CSS.

CSS é um acrônimo para Cascading Style Sheet (Folhas de Estilo em Casca). Com essa tecnologia, é possível formatar com extrema precisão qualquer elemento de uma página web, um por um ou em lote.

Mais informações sobre CSS.

Após tomar coragem, clique sobre a pequena folha com um sinal de “+” (canto inferior direito).

table_06

Selector Type = Especifica o que vamos criar (uma classe, se vamos definir um id, se vamos mexer com uma tag específica…)

Selector Name = o nome que você dará a regra.

Rule Definition = Se criaremos no próprio documento ou num arquivo novo.

Defina o Type como TAG. Perceba que o campo de NAME já será preenchido com algo. Clicando na setinha do combo box, você verá uma lista de todos os elementos que você pode brincar. Digite td, que é o elemento correspondente a cada célula da tabela. Em Rule Definition, escolha new style sheet file. Pressione OK.

Um diálogo de salvar arquivo surgirá. Dê um nome e clique em salvar.

table_07

Nesta tela de cima, você pode colocar todas as definições de CSS que desejar. Todas estão divididas por categoria.

Escolha a categoria BOX. Vá até padding e digite 5 na caixa de texto. Aperte OK.

Perceba que sua tabela foi atualizada instantâneamente. E o painel de Css também:

table_08

Caso queira mudar o valor, basta clicar no número, apagar e digitar outro (mantendo o px no fim). Caso queira mudar outra coisa, clique no pequeno lápis (canto inferior direito, ao lado da folha com +).

Clique novamente na folha e crie uma nova regra, type tag e de name input. Vá até a categoria Positioning e defina width e height como 40 e 25, respectivamente.

table_09

Clique para criar uma nova regra CSS. Dessa vez o type será CLASS. Se é class, o nome deve sempre iniciar com um ponto. Ex:

.meuNome, .blabla, .asd

Sugestão: .display

Novamente clique na aba de positioning. Em width, digite 100. Note que a caixa ao lado está mostrando o valor em px, ou seja, pixels. Clique nela e escola a medida em porcentagem (%).

Clique sob o textbox de display.

table_10

Note que na barra de propriedades existe uma propriedade CLASS. Clique na caixa e veja que a sua classe recem criada está lá. Se você tivesse criado mais de uma, ela também estaria ali, podendo ser aplicada livremente.

Aplique a classe para o display e também no botão de =.

Opa, epa, ei! Chamamos a classe de .display e aplicamos num botão??

Classes podem ser aplicadas livremente em N objetos de N categorias. Obviamente, estamos cometendo um erro ao aplicar esta classe com este nome, isto pode nos confundir e é um mau-hábito.

Mudemos o nome da classe para… .areaMaior

table_11_1

table_11_2

table_11_3

Escolha Sim (ou Yes… ou Sí… Sei lá, qualquer coisa que signifique Sim)

Se o find e replace não mudar automaticamente a classe do seu display e botão, faça isso manualmente (clique sobre eles, escolha a classe com o novo nome).

Beleza! Acabamos a estrutura da interface gráfica. Com seus conhecimentos em CSS e um pouco de estudo, você pode enfeitá-la ainda mais, porém paro por aqui. Mexemos com uns 3% do que o CSS pode fazer. Pesquise, arrisque  e pergunte para descobrir mais!

Engatilhar eventos

Até agora tudo bonitinho, certo? Só mexemos com botões e interface. Agora vamos ver o código.

table_12

Repare que estamos em Design. Clique em Code.

Está vendo todo o código? O DreamWeaver gerou tudo por você. Clique novamente em Design. Clique sob um dos botões da interface e em seguida sobre code.

table_13A parte azul é a seleção direta do que você tinha clicado.

Posicione o cursor ao lado da segunda aspa de value e pressione espaço. Se uma lista não aparecer, tente ctrl+barra de espaço.

table_14Procure o “onclick” e dê um duplo clique. Ele vai gerar o evento de clique para você. Poderíamos ter digitado, mas achei conveniente mostrar essa funcionalidade.

Dentro das aspas criadas, digite acres(this.value);

Isto é uma chamada de função que será enviada ao nosso arquivo de código (aliás, ainda inexistente :)).

acres = nome da função

this.value = parâmetro, dado que estamos passando

Adianto que “acres” vai inserir um número no textbox de display.

“Ué, vai aparecer esse texto this.value então?”

Não. this.value é um comando javascript. Quem sabe inglês já deve ter matado: este.valor. Estamos passando o valor do objeto atual. Se estamos no botão 7, e ele tem o value como 7… Passamos 7 para a função.

Acrescente o onclick e a função acres em todos os numeros.

Nos botões de operação, faça exatamente o mesmo, mas ao invés de acres, digite calc(this.value);

No clear, limpar();

No igual, resFinal();

É isso ai. Engatilhamos todos os eventos necessários. No próximo artigo veremos como fazer essa bagaça funfar.

Qualquer erro, dúvida, basta entrar em contato.

Cya!