KISS - Keep it simple, stupid!

JavaScript: buscar a posição de um elemento em uma matriz

postado por Gabriel Gilini em 30/12/2009 16:00:03
Não há tags para este post

Recebi uma pergunta curiosa no Aardvark há pouco, perguntando se eu não conhecia algum código JavaScript que buscasse a posição exata de um elemento dentro de um array multidimensional — matriz — qualquer.

Não é novidade pra ninguém que eu me divirto escrevendo código nessa linguagem, então resolvi bolar alguma coisa pra resolver o problema do rapaz em necessidade. Aí vai o código (gist):

Exemplo de uso:

No tags for this post.
Topo

Sem comentários

Criando thumbnails no shell do Linux de maneira simples

postado por Gabriel Gilini em 25/11/2009 19:59:08
Não há tags para este post

Hello, peeps.

Hoje precisei converter um bocado de imagens para um tamanho reduzido, e como um bom linuxer, fui procurar como fazer isso no shell.

Uma pesquisadinha rápida no Google, e trombei com esse “how to”. Simples e direto, resolveu minha dúvida. Chequei se o ImageMagick já estava instalado, e estava. Mas esse script que o cara colocou no tutorial tá meio pobre, não dá pra passar nada por parâmetro. Cada vez que eu precisar disso vou ter que abrir o script e modificar? Ah, não.

Dez minutinhos de vim aberto depois, surge o thumbalizr:

exemplo de uso do thumbalizr

Você tem que passar width OU height (eu sei, tá errada a marcação dos parâmetros no exemplo de uso, se alguém souber o certo me fala), e pode passar uma string pra ser inserida antes do nome original da imagem. Caso contrário, as imagens serão substituídas.

UPDATE:
Agora o script aceita a flag “-a string_pra_concatenar” que concatena uma string no final do nome do thumbnail. E também fiz algumas mudanças pra compatibilidade.

Para “thumbalizar” imagens em vários diretórios, faça algo como:

Aí vai o código do script:

Ou você pode baixar o script aqui.

No tags for this post.
Topo

Sem comentários

O IE6 é apenas mais um navegador

postado por Gabriel Gilini em 19/07/2009 16:06:54
Não há tags para este post

Em todo blog de desenvolvimento o assunto vem sendo o mesmo nos últimos dias: a morte do IE6. O Digg anunciou que não suporta mais o navegador, depois veio o Youtube, e tenho certeza que muitos outros estão seguindo a mesma estratégia dos gigantes.

Por todos os cantos da Internet se escutam exaltações de alegria: “hacks para PNG não mais”, “seletores CSS3!”, “HTML5″, e etc. E a cada vez que leio algo nesse sentido, sinto uma pontada de pesar por saber que essas pessoas não entendem o que é desenvolver pra web, elas acreditam que dando suporte às funcionalidades conhecidas de um punhado de navegadores que aparecem nas estatísticas é o necessário para dar suporte cross-browser. Então me pergunto: o que adianta bradar aos quatro ventos que desenvolve nos padrões de acessibilidade, sendo que na hora de escrever um script não leva em conta que os navegadores utilizados por deficientes visuais, por exemplo, são praticamente desconhecidos para os desenvolvedores. Eu não sei qual punhado de funcionalidades um navegador desses suporta, mas é meu papel garantir que pelo menos uma experiência minimalista de uso seja viável em qualquer navegador.

Não me vejo no direito de excluir algumas pessoas de utilizarem minha interface porque elas não usam um navegador que eu conheça ou goste, mas parece-me que muita gente não se importa com esses “excluídos”. Ontem, depois de ler o milésimo post falando como o IE6 é(era) a praga da web, resolvi dissertar brevemente sobre o assunto, e como gostei do que escrevi, vou reproduzir o texto do comentário aqui ipsis litteris.

Eu já cansei de falar isso, mas vamos lá.

Todo esse chororô dos desenvolvedores no que concerne ao IE6 é, no mínimo, “overrated”. Eu me sinto na obrigação de escrever código que funcione (mesmo que de maneira básica) em qualquer navegador. E sabem o que é mais interessante? Pra 99,9% dos projetos, não é nem um pouco difícil dar suporte ao IE6, o problema é que as pessoas desistiram de aprender JavaScript para usar cegamente essa aberração chamada jQuery, que até pouco tempo atrás abusou de user-agent sniffing, e que atualmente trocou pelos piores exemplos de feature testing que já vi na vida, se é que posso chamar aquilo de feature testing, tá mais pra object inference. Aí quando o cara testa no IE6 e vê as coisas dando errado, começa a meter a boca no navegador, mas mal sabe ele que o script que ele confia tão cegamente está fazendo tudo errado.
O meu ponto é que não é tão dificil escrever código cross-browser quando os John Resigs da internet pintam a todo momento.

Quanto ao CSS, sim o IE6 tem um suporte bem fraco a CSS2.1, mas existem os comentários condicionais. É simples fazer uma versão ‘lo-fi’ do design para IE6 utilizando um stylesheet alternativo. E pra quem ainda não sabe, esses fixes de PNG introduzem vários crashes totalmente imprevisíveis. O que você prefere, utilizar PNG 8bit ou CRASHAR o navegador de seus usuários?

Por fim, gostaria de dizer que sim, o IE6 está deixando de ser utilizado pela maioria, mas sempre existem pessoas que não podem atualizar, ou pessoas que utilizam um browser que nem entra nas estatísticas, algum dos muitos ‘flavors’ do IE6 que saíram ao longo dos anos. Acho que dá pra entender como é pointless ficar malhando um navegador, certo? Boas práticas de desenvolvimento contornam todos esses problemas.
O que falta na web são pessoas que realmente saibam o que estão fazendo e não navegadores com melhores capacidades.

E você, o que acha?

No tags for this post.
Topo

26 Comentários

Referências Circulares e Memory Leaks

postado por Gabriel Gilini em 15/07/2009 15:04:42
Não há tags para este post

Bom, o Lucas já me cobrou e eu nem tinha percebido que não postei a apresentação aqui hehe, mas aí vai! Uma breve apresentação sobre referências circulares no DOM e como elas podem vazar muita memória no IE 6 e anteriores.

No começo também falo sobre resolução de identificadores e como funciona o contexto de execução quando uma função é criada. Isso é necessário pro entendimento a fundo do que causa esses memory leaks, mas o mais importante para todos os desenvolvedores está nos últimos slides, onde mostro o que é uma closure, como as referências circulares más são formadas e como fazer para evitá-las.

Qualquer dúvida que tiverem, digam nos comentários e ficarei feliz em responder.

Aproveito também para convidar novamente todos que forem de Londrina a participar dessas apresentações, vamos tentar realizá-las a cada uma ou duas semanas.

No tags for this post.
Topo

3 Comentários

jshash – Implementando um Hash em JavaScript

postado por Gabriel Gilini em 21/04/2009 03:42:14
Tags: ,

Recentemente escrevi algum código javascript que realizava algumas requisições, e a fim de não buscar informações redundantes, armazenava um cache desses dados que chegavam. Simplificadamente, algo como:

var cache = {};
 
function buscaEProcessaDados(id){
	var dados;
	if(!cache[id]){
		// faz a requisição e retorna dados
		cache[id] = dados;
	}
	// Processa
}

E estava funcionando tudo bem. Mas pensando bem, isso não está muito seguro. Vamos supor que outro script na página execute o seguinte código:

Object.prototype.foo = function(){alert('te peguei!')};

e eu quisesse buscar algum dado que tenha id = foo.

buscaEProcessaDados('foo'); // :(

Mesmo não havendo nenhum dado armazenado na propriedade 'foo' daquele objeto cache que foi criado vazio mais acima, o teste

!!cache[id] //-> true

será interpretado como true, já que o JavaScript encontrará essa propriedade ao subir a cadeia de prototypes (escreverei sobre isso em breve) em busca de algo com nome 'foo'.

A maneira que encontrei para resolver este problema foi implementar um hash simples, com métodos de put, get e remove. O objeto que conterá as propriedades fica inacessível para código fora do objeto construído e as chaves são concatenadas a uma string gerada a partir do timestamp no momento da instanciação.

function Hash(){
    var _hash = {};
    var pre = '__' + (new Date()).getTime() + '__';
 
    function put(key, value){
        _hash[pre + key] = value;
        return this;
    }
 
    function get(key){
        return _hash[pre + key];
    }
 
    function remove(key){
        delete _hash[pre + key];
    }
 
    return {
        'put': put,
        'get': get,
        'remove': remove
    }
}

Utilizando o Hash, nosso código anterior fica assim:

var cache = new Hash();
 
function buscaEProcessaDados(id){
	var dados,
cached = cache.get(id);
	if(!cached){
		// faz a requisição e retorna dados
		cache.put(id, dados);
	}
	// Processa
}

e mesmo que a propriedade 'foo' esteja sendo herdada do Object.prototype, podemos ficar seguros que ela não interferirá em nossa hash:

typeof cache.foo == 'function' &&
typeof cache.get('foo') == 'undefined'; //-> true

É claro que esta Hash é bem primitiva e muitos métodos ainda podem ser implementados. Ainda assim ela funciona perfeitamente para propósitos simples como o demonstrado acima.

Como de costume, o código está hospedado no GitHub, e o download pode ser feito aqui.

Tags: ,
Topo

Sem comentários

Fazendo deploy de uma aplicação CakePHP com Git

postado por Gabriel Gilini em 02/03/2009 15:35:47
Tags: , ,

Deploy é sempre um problema sério. Estamos sempre buscando soluções que minimizem o trabalho manual na hora de subir os arquivos para o servidor e deixá-los prontos para produção.

Em um projeto que temos, utilizamos o próprio Git para fazer esta tarefa, afinal o repositório central já fica lá no servidor mesmo, nada mais óbvio.

Então sempre que temos uma versão que está estável para produção, nos logamos por ssh e mandamos um git pull no diretório onde fica o site. Geralmente o merge ocorre sem problemas e fica tudo atualizado. Mas o problema é que os arquivos de configuração sempre são substituidos, e toda vez o trabalho de consertar as configurações de banco, colocar o debug pra 0, entre outras coisinhas (que sempre passam desapercebidas) é feito à mão.

Hoje, depois de fazer isso pelo menos umas 3 vezes, me lembrei que o Git tem um comando que poderia servir perfeitamente pra evitar toda essa edição manual na hora do deploy. É o git stash.

Agora, antes de executar o git pull, eu digito git stash save “Salvando configurações de produção”; com isso o git guarda os arquivos modificados em um local “invisível”, e executa um git reset –hard, que retorna todos os arquivos à versão HEAD. Aí o diretório fica limpo para receber o merge sem problemas.

Depois que o git pull for completo, é só digitar git stash apply que ele retorna todas as mudanças que você tinha guardado, e sua aplicação está pronta pra receber visitas.

Update: eu não havia dito pois não tinha certeza, mas acabei de verificar que o git stash apply não simplesmente recupera o arquivo “stashado”, mas aplica somente as mudanças que você havia guardado, preservando o que mudar com o git pull. Nice!

Tags: , ,
Topo

4 Comentários

Arrastando elementos com JavaScript (ou JavaScript Drag)

postado por Gabriel Gilini em 27/02/2009 20:21:10
Tags: , ,

Na minha incessante busca por aprender mais sobre JavaScript e DOM, escrevi meu próprio código para arrastar elementos pela página – ou drag se preferir. Escolhi justamente o drag porque, além precisar lidar bastante com as inconsistências da implementação do DOM nos diferentes navegadores, é uma funçãozinha muito utilizada e que me fazia carregar uma lib gigantesca pra usar só essa funcionalidade.

Durante o desenvolvimento desse script aprendi a lidar com o problema de perda de escopo em funções de callback (vale a leitura), algo que eu nunca tinha focado minha atenção por utilizar facilidades existentes nas bibliotecas famosas.

Minhas principais fontes de pesquisa foram o newsgroup comp.lang.javascript e seu respectivo FAQ. Tem um pessoal por lá que sabe do que tá falando, tipo um tal de Douglas Crockford :).

Como funciona

Vou pular as funções de utilidades que estão no objeto Drag, como evtObserve, callback, entre outras; e vou explicar só o funcionamento do drag propriamente dito.

function Drag(elm, handler){
    this.isDragging = false;
    this.draggable = (typeof elm == 'string')?
        document.getElementById(elm):
        elm;
    this.handler = (typeof handler == 'undefined')?
        this.draggable:
        (typeof handler == 'string')?
            document.getElementById(handler):
            handler;

A propriedade ‘isDragging’ é quem vai ser responsável pelo flow control do script, ela que vai dizer quando a função _drag deve parar de ser chamada. O resto é auto-explicativo, se passar uma string eu uso gEBI para encontrá-lo, senão é bom que você tenha passado um elemento! A mesma coisa pro handler, que é o elemento que vai iniciar o evento de drag. Com a diferença de que se você não especificar um, o elemento arrastável vai fazer esse papel.

this.initDrag = this.callback(function(){
        this.isDragging = true;
        this.lastMouseCoords = [
            this.mouseCoords[0],
            this.mouseCoords[1]
        ];
        if(!this.draggable.style.left || !this.draggable.style.top){
            var offsets = this.getOffsets();
            this.draggable.style.left = offsets[0] + 'px';
            this.draggable.style.top = offsets[1] + 'px';
        }
        this.draggable.style.zIndex = this.handler.style.zIndex = '1000';
        this._drag();
    }, {bind: this});

Essa é a função que é chamada quando você clica no handler, ela vai preparar o gramado pra função que vai mover o elemento pela tela, iniciando propriedades como a posição atual do mouse e do elemento arrastável na tela. A função callback coloca a initDrag em uma closure para que ‘this’ signifique o objeto instanciado a partir do construtor Drag.

this._drag = this.callback(function(){
        this.newLeft = parseInt(this.draggable.style.left, 10) -
            (this.lastMouseCoords[0] - this.mouseCoords[0]);
        this.newTop = parseInt(this.draggable.style.top, 10) -
            (this.lastMouseCoords[1] - this.mouseCoords[1]);
 
        if(this.newLeft < 0)
            this.draggable.style.left = 0 + 'px';
        else
            this.draggable.style.left = this.newLeft + 'px';
 
        if(this.newTop < 0)
            this.draggable.style.top = 0 + 'px';
        else
            this.draggable.style.top = this.newTop + 'px';
 
        this.lastMouseCoords = this.mouseCoords;
 
        if(this.isDragging){
            setTimeout(this._drag, 10);
        }
    }, {bind: this});

Esse cara que realiza a mágica, mas é um truque bem simples. A cada vez que esta função é chamada, ela pega as últimas coordenadas do mouse armazenadas no objeto, e subtrai das coordenadas atuais, para descobrir quanto ela vai ter que mover o objeto na página. Depois disso, só armazena as coordenadas atuais como últimas lidas e, se a propriedade isDragging ainda for verdadeira, chama a si própria depois de 0,01 segundos. “Mas por que você não coloca um evento de mousemove?”, você deve estar se perguntando. Simples: performance. Em um evento de mousemove, a função de callback é chamada a cada pixel que o ponteiro se move, o que deixa o movimento de arrasto bem travado se você mover bastante o mouse. Você pode brincar com o valor desse Timeout e ver o que funciona melhor no seu caso, mas 10ms deixa com uma movimentação bem suave.

this.endDrag = this.callback(function(){
        this.isDragging = false;
        this.draggable.style.zIndex = this.handler.style.zIndex = '';
    }, {bind: this});

E esta é a função chamada quando você solta o botão do mouse. Com a propriedade isDragging setada para false, o _drag não é mais chamado e a animação pára.

Como podem ver, o conceito é muito simples, o que complica pra variar é o DOM. Olhando o código todo você pode encontrar todas os malabarismos necessários pra achar a dimensão da tela, do elemento e mais algumas coisinhas místicas das implementações de DOM soltas por aí.

O código todo se encontra disponível no GitHub, e você pode conferir também o script em funcionamento.

Tags: , ,
Topo

Sem comentários

Creative Commons License
Sou Ágil: KISS em http://kiss.souagil.com.br está licenciado sobre
Creative Commons Attribution-Share Alike 2.5 Brazil License.

souÁgil