Manipulando classes com classList API

Manipular classes de CSS em elementos HTML é uma tarefa muito útil, e até corriqueira, para quem trabalha com JavaScript. Provavelmente, você já sabe como fazer isso com jQuery. Porém, o que nem todos sabem ainda, é que temos uma API nativa de JavaScript para executar essas tarefas.

Neste artigo vou lhe apresentar a classList API. Essa API é bem simples de ser utilizada, e é extremamente útil para diversas “brincadeiras”.

Se você ainda não a conhece, verá que não é nada complicado manipular elementos dessa forma, mesmo sem utilizar bibliotecas como jQuery. Por outro lado, se você é um desenvolvedor experiente que já utiliza essa API no dia a dia, fique a vontade para deixar críticas e sugestões através dos comentários.

Entendendo a API

Todo objeto Element possui um objeto classList que retorna um DOMTokenList representando seu atributo class definido no documento HTML. O objeto classList possui uma propriedade length e é somente para leitura, porém podemos modificá-lo através de seus métodos, como add e remove. É aí que as coisas ficam interessantes.

Os métodos disponíveis em classList são:

  • add
  • remove
  • toggle
  • contains

Vamos considerar agora cada um desses métodos.

Adicionando classes com classList.add

Para adicionar uma ou mais classes em um elemento HTML, basta selecioná-lo e chamar o método classList.add, passando uma String como argumento. É interessante notar que podemos adicionar multiplas classes de uma só vez. Para isso, informe os nomes das classes que deseja adicionar separados por vírgula. Exemplo:

// Selecionamos um elemento span no DOM:
var mySpan = document.querySelector( 'span' );

// Agora adicionamos a classe highlight para destaca-lo:
mySpan.classList.add( 'highlight' );

// Agora adicionamos mais 3 classes de uma so vez:
mySpan.classList.add( 'feature', 'label', 'label-primary' );

Removendo classes com classList.remove

Além de adicionar novas classes, vez por outra você vai precisar também remover classes já existentes. Para isso, temos o método classList.remove. Vale notar, que assim como o método add, podemos remover multiplas classes de uma só vez. Exemplo:

// Selecionamos um elemento div no DOM:
var myDiv = document.querySelector( 'div' );

// Supondo que esse div tenha a classe 'foo', 'bar' e 'baz',
// vamos remover a classe 'baz':
myDiv.classList.remove( 'baz' );

// Agora vamos remover as classes 'foo' e 'bar' ao mesmo tempo:
myDiv.classList.remove( 'foo', 'bar' );

Alternando classes com classList.toggle

Para dar um comportamento mais dinâmico em nossa interface, pode ser interessante alternar (inserir e remover) uma classe, respondendo a um clique, toque na tela ou outro evento. Com isso, podemos fazer coisas como por exemplo exibir/ocultar um bloco de conteúdo, exibir/ocultar um menu, destacar/remover destaque de um elemento, entre várias outras coisas.

É aí que o método toggle entra em ação. Passamos o nome de uma classe para o método toggle, e ele fica responsável por checar se um elemento tem ou não essa classe. Se já tiver: ele remove, se não tiver: ele adiciona. Tudo “automagicamente” sem precisar escrever instruções if/else, expressões regulares ou coisa do tipo.

Para ver o método toggle funcionando, o ideal é registrarmos uma rotina de tratamento de eventos como comentei acima. No final do artigo, você encontra um exemplo assim. Mas a sintaxe desse método é a seguinte:

// Alterna a classe 'is-visible' no elemento 'myDiv':
myDiv.classList.toggle( 'is-visible' );

Verificando classes com classList.contains

Talvez você precise checar se um elemento possui determinada classe e dependendo do resultado, executar uma ou outra tarefa. Se esse for o caso, utilize o método classList.contains. Exemplo:

if( myDiv.classList.contains( 'is-visible' ) ) {
	// Se o elemento 'myDiv' possui a classe 'is-visible':
	// Faca alguma coisa interessante aqui.
} else {
	// Se nao...
	// Faca alguma outra coisa aqui.
}

Exemplo dinâmico

Para que você possa visualizar melhor as coisas, fiz um exemplo bem simples onde você pode interagir com a classList API clicando nos botões. Ao testar o botão contains, repare a saída gerada no console.

classList API

Compatibilidade nos browsers

Ao aplicarmos a classList API (ou qualquer outro recurso) em nossos projetos, é sempre bom ficar atento em relação a quais browsers suportam ou não tal API ou recurso que queremos implementar, e então tomar as decisões corretas.

Para isso, gosto bastante do site Can I use.

Como podemos ver na tabela abaixo, o suporte à classList API é bem tranquilo em vários navegadores. Para variar um pouco, no IE só funciona a partir da versão 10+. Mas isso não é nenhuma surpresa né? :-P.

Can I Use classList API

Mas fique tranquilo. Qualquer browser moderno aceita essa API, e se você precisa dar suporte aos navegadores mais antigos, pode usar algum polyfill disponível na Internet. Um deles você encontra na documentação da Mozilla Developer Network.

Conclusão

Agora você já sabe como manipular classes em seus documentos HTML de maneira muito fácil e sem depender de bibliotecas de terceiros. O suporte à essa API é muito bom em todos os browsers modernos e podemos utilizar um polyfill para os que não oferecem suporte. API’s nativas podem facilitar bastante nossa vida como desenvolvedores Web, basta conhece-las e colocar em prática.

Referências:

Novo plugin para WordPress lançado: WP Like System

É com grande satisfação que venho através desse post, anunciar mais um novo plugin que desenvolvi para WordPress. O nome desse plugin é WP Like System, e você pode fazer o download aqui no repositório oficial do WordPress.org.

O que esse plugin faz?

O WP Like System adiciona um sistema de ‘curtir’, semelhante ao famoso recurso lançado pelo Facebook. Dessa forma, os usuários podem avaliar seus posts e interagir com seu site/blog, mas sem precisar estar logado na rede social, e não é necessário fazer integração com scripts de terceiros, pois as avaliações dos usuários ficam armazenadas em sua própria base de dados.

Principais recursos

  • Opções “Curtir” e “Desfazer”.
  • Exibe total de pessoas que já curtiram.
  • Fácil de usar: Nada de configurações complicadas e desnecessárias. Basta instalar, colar o snippet do plugin dentro do loop do seu tema, e tudo funciona como o esperado.
  • Fácil de personalizar: marcação HTML limpa e com classes CSS para que você possa estilizar a aparência do plugin da forma que achar melhor, caso ache necessário.
  • Pronto para traduções e já está disponível nos idiomas Português e Inglês.

Como instalar

O processo de instalação é feito como qualquer outro plugin para WordPress.

Após ter instalado, abra o arquivo content.php ou single.php do seu tema e cole o seguinte snippet dentro do loop:

<?php
    if( function_exists( 'has_wpls_show_likes' ) ) {
        has_wpls_show_likes( get_the_ID() );
    }
?>

Qualquer dúvida, fique a vontade para abrir um tópico no fórum de suporte.

Screenshots

Abaixo você pode ver algumas imagens que demonstram como o plugin funciona.

WP-Like-System-1

WP-Like-System-2

WP-Like-System-3

Gostou? Colabore.

Se você não é um programador, pode contribuir com o desenvolvimento do plugin por fazer um review lá no repositório do WordPress.org e deixar uma avaliação com 5 estrelas. Fazer isso não leva mais do que 3 minutos, não lhe custará nada, e irá me ajudar a manter esse plugin, bem como me incentivar a desenvolver outros plugins novos futuramente.

Se você é programador, o código fonte está disponível nesse repositório no Github. Fique a vontade para fazer um fork, e ajudar a melhorar o plugin.

JavaScript: Descobrir Menor e Maior valor em um Array

Neste artigo irei mostrar técnicas para resolver de forma muito simples, um problema que geralmente uma hora ou outra acabamos precisando solucionar (eu por exemplo, já me deparei com esse problema em uma entrevista de emprego). Essas técnicas podem não ser novidade para alguns, mas tenho certeza que poderá ser novidade (e de forte ajuda) para outros.

O problema

Digamos que você tenha um array com alguns números variados:

var random = [2, 3, 1, 4, 6, 5];

Agora você precisa:

  • Encontrar o menor valor dentro do array.
  • Encontrar o maior valor dentro do array.
  • Descobrir o índice de um valor específico.

E então, já sabe como fazer? Veja como é simples.

Menor e maior valor dentro do array

Olhando para o objeto Math nativo da linguagem JavaScript, podemos encontrar vários métodos úteis para realizar operações aritméticas mais complexas. Entre esses, temos os métodos Math.min e Math.max, que como você já deve ter imaginado, encontram respectivamente o menor e o maior valor entre um dado conjunto de números.

Maravilha! Problema resolvido. Será mesmo?

Bom, na verdade se você leu com atenção, deve ter notado que os métodos Math.min e Math.max encontram o menor e o maior valor entre um dado conjunto de números. Mas nós precisamos encontrar o menor e o maior valor de um array.

Vamos testar?

Abra o Chrome DevTools, Firefox Developer Tools, Firebug ou qualquer outra ferramenta que você use para brincar com JavaScript, e digite o código abaixo:

Math.min( [1, 2, 3] ); // Retorna: NaN
Math.max( [1, 2, 3] ); // Retorna: NaN

É, o resultado não foi o esperado. Mas isso pode ser resolvido com uma técnica bem simples.

O segredo da coisa é: podemos pegar funções que recebem um número indeterminado de argumentos (assim como Math.min e Math.max), e adaptá-las para serem utilizadas em um array, através do método .apply(). Não faz parte desse artigo explicar como o método .apply() funciona, mas você pode clicar no link anterior para ler mais sobre o ele.

Sacou a ideia? Ainda não? Após ver o exemplo abaixo tudo ficará mais claro.

// Funcao para retornar o menor valor de um array
Array.min = function(array) {
    return Math.min.apply(Math, array);
};

// Funcao para retornar o maior valor de um array
Array.max = function(array) {
    return Math.max.apply(Math, array);
};

// Agora e so pegar os resultados
var random = [2, 3, 1, 4, 6, 5];  // Nosso array
console.log( Array.min(random) ); // Menor valor
console.log( Array.max(random) ); // Maior valor

Ótimo! Agora sim temos boa parte do nosso problema resolvido.

Bom, em alguns casos você pode também receber um array que possui índices vazios ou que não possuem valores numéricos. Se esse for o seu caso, também é possível utilizar a técnica mostrada acima, basta fazer alguns ajustes para verificar os valores contidos no array antes de passá-lo como argumento para o método .apply(). Fazer essas verificações já foge do escopo desse artigo, mas caso você precise de ajuda para fazer isso, deixe um comentário abaixo.

Bônus: Descobrir o índice (posição) de um valor específico

Para finalizar, precisamos descobrir o índice (posição) de um dado valor específico dentro do nosso array.

Graças ao método nativo .indexOf(), fazer isso é muito, mas muito fácil. Basta digitar o seguinte código:

var random = [2, 3, 1, 4, 6, 5];  // Nosso array
console.log( random.indexOf(1) ); // Retorna a posicaoo do numero 1 no array

Pronto! Agora nosso problema foi resolvido por completo.

Referências

WP Upcoming Releases: meu novo plugin para WordPress

Pouco tempo atrás tive a oportunidade de lançar meu primeiro plugin para WordPress. Publiquei ele no repositório oficial e divulguei discretamente para alguns colegas. Agora gostaria de anunciá-lo aqui no blog para que todos possam conhecê-lo, e caso ache interessante, você poderá fazer o download e utilizá-lo em seu site/blog.

Pra que serve, e pra quem é esse plugin?

O WP Upcoming Releases é um plugin simples, mas que cumpre com seu objetivo: exibir uma lista com “próximos lançamentos” de forma dinâmica e fácil de gerenciar. Evitando um trabalho cansativo de manutenção para atualizar de forma manual o site/blog que necessita desse recurso.

Você pode usar o WP Upcoming Releases em vários tipos de sites/blogs. Tem um site sobre games e gostaria de manter seus visitantes informados sobre quais serão os próximos lançamentos na área? Ou um blog sobre música e quer mostrar os próximos álbuns de um ou mais cantores? Ah, talvez queira exibir os próximos livros que você prentede ler, filmes que assistiu/pretende assistir, que tal? Seja qual for o caso, você pode usar o WP Upcoming Releases para cuidar disso.

Principais recursos

Com ele você pode cadastrar quantos itens desejar com: foto, título, classificação indicativa e data de lançamento. Além disso, você pode organizar seus itens por categorias.

Para exibir seus itens no site, não é necessário ter conhecimento de programação. Basta “arrastar e soltar” o widget de “Próximos lançamentos” em sua barra lateral ou outra área de widgets que tenha disponível em seu tema.

Caso necessite de algo um pouco mais personalizado, basta conhecer um pouco de CSS e você poderá estilizar a exibição dos itens da maneira que lhe agrade melhor. Basta utilizar as classes do plugin no CSS do seu tema.

Vale lembrar também, que até o momento, o plugin está disponível nos idiomas Inglês e Português do Brasil.

Screenshots

Para que você tenha uma visão melhor sobre como o plugin funciona, separei algumas imagens tanto da parte administrativa, como a parte que é exibida para os usuários no site.

screenshot-1

screenshot-2

screenshot-3

screenshot-4

screenshot-5

Contribuindo

Lancei o WP Upcoming Releases com código open source. Logo, você pode fazer o download gratuito para utilizar em seu site/blog, e caso tenha conhecimentos em HTML/CSS/PHP, poderá contribuir com o desenvolvimento dele, seja criando novos recursos ou corrigindo bugs.

O código fonte está disponível em: github.com/HenriqueSilverio/wp-upcoming-releases. Fique a vontade para forkar o repositório e contribuir.

Ah! Se você não é um programador, mas gostou do plugin, você pode contribuir deixando uma avaliação lá na página de download.

Download

Para baixar e instalar o WP Upcoming Releases em seu site, basta acessar essa página.

Espero que goste do plugin e que ele lhe seja útil. Qualquer dúvida, abra um tópico na área de suporte ou deixe um comentário abaixo.

Font Icons para Custom Post Types e Temas ~ WordPress 3.8

Se você conhece o WordPress há um bom tempo e tem acompanhado de perto sua evolução, ao instalar a versão 3.8, sem dúvida deve ter se surpreendido com a grande mudança feita na interface do painel administrativo. O design ficou muito mais limpo e moderno, coisa linda.

Entre as várias mudanças que ocorreram, uma delas é que agora foram adicionados Font Icons (chamados de Dashicons) no lugar das imagens que eram usadas anteriormente. Os ícones seguem um estilo flat, e combinam muito bem com o novo estilo dado à dashboard, e trazem vários benefícios, como por exemplo: são preparados para as poderosas telas de retina.

Bom, após conhecer os novos ícones, é claro que você vai querer utilizá-los ao desenvolver seus plugins e temas. Neste artigo, trago uma dica rápida para você que deseja fazer isso. Veja como é bem tranquilo.

Dashicons para Custom Post Types

Um dos parâmetros que você deve informar ao registrar um post type é o menu_icon. E para inserir o Font Icon (Dashicon) que você desejar, basta informar o nome de sua respectiva classe CSS.

Para descobrir qual é o nome da classe CSS que corresponde ao ícone desejado, você pode acessar o link: melchoyce.github.io/dashicons/. Nessa página você verá uma lista com todos os ícones disponíveis. Basta clicar no ícone para que ele seja selecionado e no topo do site você verá o nome da classe CSS, aí é só copiar esse nome e informá-lo ao parâmetro menu_icon.

Font-Icon-Dashicon-WordPress-3.8

register_post_type( 'lancamentos', array(
	// ...
	'menu_icon' => 'dashicons-list-view',
	// ...
) );

Font-Icons-Painel-WP

Dashicons para Temas no Front-End

Se você deseja utilizar os novos ícones no Front-End de um tema, poderá fazer isso também de forma muito simples. Basta passar o dashicons como uma dependência de sua folha de estilos na chamada da função wp_enqueue_style, como no exemplo abaixo:

function my_theme_enqueue_style() {
	wp_enqueue_style( 
		'my-theme-style', 
		get_stylesheet_uri(), 
		array( 'dashicons' )
	);
} 

add_Action( 'wp_enqueue_scripts', 'my_theme_enqueue_style' );

O código mostrado acima, irá se encarregar de chamar o CSS para os Dashicons sempre junto com o CSS do seu tema. Feito isso, basta voltar ao site do Dashicons, clicar no ícone que deseja utilizar, e depois clicar em “Copy CSS” para obter o código referente ao ícone selecionado, como mostrado na figura abaixo:

Copy-Font-Icon-CSS

Com o código do ícone em mãos, agora basta utilizá-lo em um pseudo-elemento :before ou :after no CSS do seu tema, seguindo o exemplo:

.my-item:before {
	content: "\f163";
	 font-family: "dashicons";
}

Conclusão

Não é de hoje que muitos encaram o WordPress como uma “espécie de framework” completo para desenvolver os mais diferentes tipos de aplicações Web. Neste artigo consideramos um recurso simples, mas que faz uma grande diferença na experiência do usuário, inclusive tornando seu tema ou plugin preparado para dispositivos com telas de alta definição. E vimos como o WordPress lida com isso, tornando o processo de desenvolvimento bem mais tranquilo, mesmo nesses “pequenos detalhes”.

Espero que essas informações lhe sejam úteis. Qualquer dúvida, crítica ou sugestão, é só deixar um comentário abaixo.

JavaScript para iniciantes: O que são Objetos?

Escrevi este artigo com o objetivo de ajudar quem está começando estudar JavaScript. Como futuro programador JavaScript, é fundamental que você entenda o que são, e como funcionam os objetos nessa poderosa linguagem. É claro que existem muitos outros detalhes sobre Objetos em JavaScript, mas espero que esse artigo lhe sirva de ajuda como um ponto de partida em suas pesquisas.

Let’s go! O que são Objetos?

Objetos são como uma espécie de “super variáveis” que armazenam uma “coleção de valores” referenciados por nome, e que podem ser recuperados para serem utilizados em diversas outras partes de um programa. Em JavaScript praticamente qualquer tipo de dado é um objeto.

Cada item dessa “coleção de valores”, é chamado de propriedade. Cada propriedade é composta por um par de “nome: valor”. Quando uma propriedade armazena uma função, ela se torna o que chamamos de método.

Criando objetos

Agora que já sabemos o que são objetos, vamos ver um pouco sobre como trabalhar com eles. Primeiramente vamos conhecer duas maneiras de se criar objetos.

Notação literal

A maneira mais simples (e recomendável) de se criar objetos em JavaScript é usando o que chamamos de notação literal. Um objeto literal é composto por um par de chaves “{ }“, que envolve uma ou mais propriedades. Cada propriedade segue o formato “nome: valor” e devem ser separadas por vírgula.

Para entender bem, nada melhor que um exemplo. Imagine que você vai criar um programa para organizar álbuns de vários cantores e bandas. Aqui vamos criar um objeto para armazenar informações sobre um álbum da banda Metallica, depois você pode praticar criando objetos com suas bandas favoritas. Então, mãos a obra!

var album = {
    title: "Metallica (Black Album)",
    released: 1991,
    showInfo: function() {
        alert("Titulo do album: " + this.title + "Lancado em: " + this.released);
    }
};

Conseguiu entender o que o código acima faz? Veja, é bem simples: primeiro criamos uma variável chamada “album”. Depois criamos um objeto – note a abertura e fechamento das chaves: { e }. Então adicionamos duas propriedades e um método ao nosso objeto, que são: “title”, “released” e “showInfo”. Nas propriedades nós armazenamos o título e ano de lançamento do álbum, e no método temos uma função que irá exibir as informações sobre o álbum em uma “caixa de alerta” para o usuário. Mais fácil do que parece, não é mesmo?

Função construtora

Outra maneira de criar objetos em JavaScript é utilizando uma função construtora. Se quisermos criar o mesmo objeto que criamos anteriormente, só que usando uma função construtora para isso, basta escrever o seguinte código:

var album = new Object();
    album.title = "Metallica (Black Album)";
    album.released = 1991;
    album.showInfo = function() {
    alert("Titulo do album: " + this.title + "Lancado em: " + this.released);
};

Como você pôde notar, a sintaxe ficou um pouco diferente. Aqui devemos utilizar a palavra-chave new seguida pela função construtora Object() ao invés de abrir e fechar chaves. Depois nós adicionamos as propriedades e métodos utilizando album.title, album.released e album.showInfo e atribuimos os valores à elas ao invés de colocar os pares de “nome: valor”.

Acessando propriedades e métodos

Após ter criado um objeto, você vai precisar acessar os valores que ele armazena. Podemos acessar (ou se preferir: “recuperar”) os valores guardados em um objeto, de duas maneiras: utilizando notação de ponto ou notação de colchetes. Veja um exemplo:

// notacao de ponto
album.title // Retorna: Metallica (Black Album)

// notacaoo de colchetes
album["title"] // Retorna: Metallica (Black Album)

Repare que no código acima, acessamos a mesma propriedade de duas maneiras diferentes. Geralmente é recomendável que você utilize a notação de ponto – album.title – por ser mais simples de ler e escrever.

Como os métodos são funções, você deve adicionar um par de parênteses – () – quando for acessá-los. Fora isso, nada de diferente. Veja no exemplo abaixo:

// notacao de ponto
album.showInfo() // Exibe alerta:
 // Titulo do album: Metallica (Black Album) Lancado em: 1991

// notacao de colchetes
album["showInfo"]() // Exibe alerta:
 // Titulo do album: Metallica (Black Album) Lancado em: 1991

Alterando e adicionando propriedades

Alterando

Vez por outra vamos precisar alterar os valores armazenados nas propriedades de nossos objetos. Fazer isso também é bem tranquilo. Basta acessar a propriedade que deseja alterar, utilizando a notação de ponto que acabamos de conhecer, e atribuir o novo valor à ela. Quer um exemplo?

album.title = "Powerslave";
album.released = 1984;

O que aconteceu no código acima? Isso mesmo. Alteramos o título do álbum e o ano de lançamento. Agora nosso objeto armazena informações sobre um outro álbum de outra banda.

Para fixar, antes de prosseguir a leitura (supondo que você esteja lendo e digitando os códigos para treinar), altere os valores de title e released para Metallica (Black Album) e 1991 novamente.

Adicionando

Bom, agora que o título do nosso álbum voltou a ser “Metallica (Black Album)”, que tal adicionar uma lista com os títulos das faixas do álbum? Sim, nós podemos adicionar novas propriedades e métodos aos nossos objetos mesmo após ter criado eles. A sintaxe é a mesma utilizada para alterar valores, que nós acabamos de ver.

Objetos podem armazenar qualquer tipo de dado válido em JavaScript, então, para criar uma lista com os títulos das faixas de nosso álbum, basta seguir o exemplo abaixo:

// Aqui adicionamos um array com os nomes de algumas faixas do album.
// Para praticar voce pode adicionar todas as 12 faixas.
album.tracks = ["Enter Sandman", "Sad but True", "Holier Than Thou", "The Unforgiven"];

Deletando propriedades

Você pode deletar uma propriedade ou método de um objeto utilizando o operador delete seguido pelo nome da propriedade. Vamos testar?

typeof album.showInfo // "function"

delete album.showInfo // deleta o metodo showInfo

typeof album.showInfo // "undefined"

Leitura adicional

Agora você já sabe o básico sobre objetos em JavaScript. Para uma abordagem mais profunda sobre o assunto, consultar bons livros pode ser de grande ajuda. Seguem algumas recomendações:

  • “O Melhor do JavaScript”, por Douglas Crockford. Capítulo 4, Página 26.
  • “JavaScript – O Guia Definitivo” – 6ª Edição, por David Flanagan. Capítulo 6, Página 112.

<dialog>: Janela modal nativa com HTML5

Um recurso bastante utilizado em diversos tipos de aplicações são as famosas “janelas modais”. Provavelmente você já deve ter utilizado esse recurso em algum de seus projetos. Caso já tenha utilizado, como você desenvolveu essa feature? Adicionou elementos extras no DOM e aplicou algumas técnicas de CSS? JavaScript “puro” ou talvez um plugin jQuery, certo?

A verdade é que existem “milhares” de técnicas para implementar uma janela modal. Mas em breve poderemos utilizar um elemento nativo do HTML5 para criar caixas de diálogo e janelas modais. Uma especificação do grupo WHATWG está saindo do forno, e nos traz o novo elemento dialog. Nesse artigo, irei apresentar como ele funciona.

Com vocês, o novo elemento dialog!

O elemento dialog representa uma parte da aplicação onde o usuário interage com a execução de uma tarefa, por exemplo uma caixa de diálogo ou uma janela modal. Esse elemento existe no DOM, e pode ser estilizado com CSS, assim como qualquer outro elemento de bloco.

Junto com este novo elemento, temos algumas APIs de JavaScript para manipular seus comportamentos, como por exemplo os métodos .show(), .close(), .showModal() e também o atributo .returnValue. E para personalizar a experiência de utilização temos o pseudo-elemento ::backdrop, além de que a caixa/janela pode ser estilizada por CSS como já comentado.

Vamos criar então o nosso dialog:

<dialog>
	<h3>Caixa de dialogo simples</h3>
	<p>Lorem ipsum dolor sit amet consectetur.</p>
	<button>Fechar</button>
</dialog>
<button>Abrir modal</button>

Compatibilidade

O elemento dialog ainda está em processo de desenvolvimento e por enquanto não foi especificado como um padrão nos browsers. Em breve os navegadores modernos deverão passar a implementá-lo, mas até que isso aconteça, para testar essa feature, você pode usar o Chrome Canary, acessando chrome://flags e ativando os “recursos experimentais da plataforma Web”.

ativar-flag-experimental-web-platform

Para os demais navegadores já existe também um fallback/polyfill criado pela equipe do Google Chrome. Você pode ver mais detalhes sobre como implementar esse fallback acessando o repositório do projeto no Github.

API .show(), .close() e .returnValue

Para que nossa “caixa de diálogo” ou “janela modal” funcione, basta utilizarmos as novas APIs de JavcaScript. Um exemplo básico é feito utilizando os métodos .show() e .close().

Como é de se esperar, quando o método .show() é chamado, ele checa se o elemento dialog já está sendo exibido, se já estiver, então nada acontece, caso contrário, ele adiciona um atributo open em nosso elemento dialog e exibe o mesmo para o usuário. É interessante notar que quando exibimos a “janela modal” através desse método .show(), o usuário ainda pode selecionar textos e interagir com os elementos da página que ficarão por trás dela.

E o método .close()? Se você adivinhar ganha um doce! Tá você não vai ganhar um doce, mas sim, o método .close() faz exatamente oque o nome sugere. Após chamar esse método, a nossa “janela modal” é fechada. Esse método também aceita um parâmetro opcional, que se for passado, é usado como valor de retorno e será gravado na propriedade .returnValue.

O código ficaria mais ou menos assim:

var dialog = document.getElementById('my-dialog'),
	btOpen = document.getElementById('btn-open'),
	btClose = document.getElementById('btn-close');

btOpen.addEventListener('click' function() {
	dialog.show();
}, false);

btClose.addEventListener('click' function() {
	dialog.close();
}, false);

API .showModal() e pseudo-elemento ::backdrop

Em alguns casos pode ser necessário “bloquear” o restante da página enquanto a “janela modal” estiver sendo exibida na tela. Para este efeito existe o método .showModal(). Basta chamar .showModal() ao invés de .show() para que a “janela modal” seja exibida na tela e o usuário não possa interagir com o restante do conteúdo enquanto não fechá-la.

/* ... */

btOpen.addEventListener('click' function() {
	dialog.showModal();
}, false);

/* ... */

Muito legal não é mesmo? Mas ainda tem mais. Outra feature muito comum, é “escurecer” a página por trás da “janela modal”, aplicando um background com um pouco de “transparência”. Fazer isso é muito fácil: basta utilizar o pseudo-elemento ::backdrop no CSS.

dialog::backdrop {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.6);
}

Demonstração

Para finalizar, você pode acessar a demonstração que preparei. Não esqueça de abrir o link no Chrome Canary e ativar a flag “recursos experimentais da plataforma Web” em chrome://flags. Espero que tenha gostado do artigo. Deixe suas opiniões nos comentários. =]

Como utilizar API de Geolocalização HTML5

Nesse artigo irei mostrar como utilizar geolocalização com HTML5. O uso dessa API é muito simples, e as possibilidades de aplicações que ela nos traz são fantásticas, sendo assim um recurso muito bacana para sites e aplicações web modernas.

html5-geolocation-api

Geolo… quê?

Entre as diversas novas API’s que chegaram com o HTML5, está a Geolocation API. O que essa API faz? Simples. Com ela nós podemos descobrir onde o usuário está localizado de maneira bem precisa, e capturar alguns dados como suas coordenadas de latitude e longitude.

Com esses dados em “mãos” por exemplo, você pode usar a Google Maps API para exibir um mapa mostrando onde o usuário está, traçar uma rota para ajudá-lo a chegar em determinado local, exibir locais úteis como restaurantes, lojas, etc. que estão por perto do usuário e podem interessá-lo, enfim, o limite será sua criatividade. O legal é explorar as possibilidades e não ficar apenas no “creme de papaia de sempre“.

Que browsers suportam a API de Geolocalização?

O suporte à API de Geolocalização é bem amplo, funciona na grande maioria dos browsers, com exceção apenas o IEca8- e o Opera Mini 5.0 – 7.0. Você pode conferir esses dados na tabela abaixo, retirada do Can I Use:

Geolocation-HTML5-Support

Para verificar se o browser suporta a API de geolocalização HTML5, podemos utilizar a biblioteca Modernizr ou fazer uma checagem simples com JavaScript “puro”:

if( ! "geolocation" in navigator ) {
	// Nao suporta Geolocation HTML5...
} else {
	// Suporta Geolocation HTML5. Vamos em frente!
}

Mergulhando mais a fundo

Agora vamos realmente entender como as coisas funcionam nessa API. Se você abrir o console Firebug ou outra ferramenta de inspeção em seu browser favorito, verá que o objeto geolocation é composto por três métodos, que são: clearWatch(), getCurrentPosition() e watchPosition(). Veremos como trabalhar com cada um desses métodos.

HTML5-Geolocation-Firebug

O método getCurrentPosition()

Para fins didáticos, vamos começar analisando o método getCurrentPosition(). Esse método pode receber três argumentos que são:

  • successCallback: Uma função que será executada caso a posição do usuário seja localizada com sucesso;
  • errorCallback: Uma função que será utilizada para tratar eventuais erros ao se tentar obter a localização do usuário. Esse parâmetro é opcional;
  • PositionOptions: Um objeto com propriedades de configurações adicionais. Esse parâmetro também é opcional.

getCurrentPosition() é chamado da seguinte forma:

navigator.geolocation.getCurrentPosition( geoSuccess );

Quando esse método é chamado, ele exibe uma notificação solicitando permissão do usuário para detectar sua posição, e caso o usuário concorde (não, você não pode acessar a localização do usuário sem que ele permita), ele executa uma requisição assíncrona para detectar sua posição atual e chama um callback para tratar os dados retornados. Por isso você sempre deve especificar o primeiro argumento de getCurrentPosition().

A função callback de sucesso, recebe um único argumento e retorna um objeto com duas propriedades: coords e timestamp. A propriedade timestamp serve apenas para armazenar a data e o horário de quando a localização foi calculada. E a propriedade coords é um objeto com propriedades interessantes como latitude e longitude.

Após implementar a função callback geoSuccess() nosso código ficou assim:

function geoSuccess( pos ) {
	// armazena as coordenadas de latitude e longitude
	var lat = pos.coords.latitude,
		lng = pos.coords.longitude;
	
	// crie qualquer coisa legal usando as coordenadas
};

navigator.geolocation.getCurrentPosition( geoSuccess );

Tratando erros

Durante o ciclo de uso da geolocalização alguns erros podem ocorrer, e a API nos fornece recursos para tratar esses possíveis erros de maneira bem simples. Para isso, basta usar o segundo parâmetro que é uma função callback para tratar erros, conforme já citado anteriormente. Cada erro retornado é referenciado por um código. Os erros que podem ser retornados e seus respectivos códigos são:

  • 1 – PERMISSION_DENIED: O usuário não aceitou compartilhar sua posição. O ideal nesse caso é não incomodar o usuário com mensagens de erro.
  • 2 – POSITION_UNAVAILABLE: O usuário está desconectado, não foi possível alcancar os satélites de GPS ou algo do tipo.
  • 3 – TIMEOUT: A requisição demorou demais para retornar a posição do usuário. Você pode determinar qual é o tempo limite máximo, veremos como fazer isso.
  • 0 – UNKNOWN_ERROR: Ocorreu um erro desconhecido.
function geoSuccess( pos ) {
	// armazena as coordenadas de latitude e longitude
	var lat = pos.coords.latitude,
		lng = pos.coords.longitude;
	
	// crie qualquer coisa legal usando as coordenadas
};

function geoError( err ) {
	switch( err.code ) {
		case 1:
			// permissao negada pelo usuario
			break;

		case 2:
			// nao foi possivel alcancar os satelites GPS
			break;

		case 3:
			// a requisicao demorou demais para retornar
			break;

		case 0:
			// ocorreu um erro desconhecido...
			break;
	}	
};

navigator.geolocation.getCurrentPosition( geoSuccess, geoError );

Configurações adicionais

Agora que já temos uma função para capturar a posição do usuário e outra para tratar os possíveis erros, podemos também opicionalmente utilizar um terceiro parâmetro, que é o objeto PositionOptions. Esse objeto possui três proriedades que são:

  • enableHighAccuracy: Um valor boolean, por padrão false. Se definir como true, e o dispositivo do usuário suportar tal funcionalidade, irá tentar utilizar GPS para aumentar a precisão da localização, por exemplo.
  • timeout: Aqui definimos o tempo máximo em milisegundos que a requisição poderá demorar antes de disparar um erro TIMEOUT que vimos acima.
  • maximumAge: O tempo máximo em milisegundos que o dispositivo pode fazer um cache da localização do usuário.
function geoSuccess( pos ) {
	// armazena as coordenadas de latitude e longitude
	var lat = pos.coords.latitude,
		lng = pos.coords.longitude;
	
	// crie qualquer coisa legal usando as coordenadas
};

function geoError( err ) {
	switch( err.code ) {
		case 1:
			// permissao negada pelo usuario
			break;

		case 2:
			// nao foi possivel alcancar os satelites GPS
			break;

		case 3:
			// a requisicao demorou demais para retornar
			break;

		case 0:
			// ocorreu um erro desconhecido...
			break;
	}	
};

var geoOptions = {
	enableHighAccuracy: true,
	timeout: 30000,
	maximumAge: 3000
};

navigator.geolocation.getCurrentPosition( geoSuccess, geoError, geoOptions );

watchPosition() e clearWatch()

Ao invés de apenas pegar a localização do usuário, nós podemos também acompanhar continuamente sua posição, atualizando as coordenadas sempre que o usuário se mover. Para isso nós utilizamos o método watchPosition(). Esse método funciona da mesma forma que o getCurrentPosition(), a única diferença é que a função geoSuccess() do nosso exemplo será chamada toda vez que o usuário se locomover, e o método watchPosition() retorna um número, para que possamos parar de rastrear a posição do usuário quando desejarmos.

Para parar de rastrear a posição do usuário, basta utilizarmos o método clearWatch() passando à ele como argumento o número retornado pelo método watchPosition(). Veja agora um exemplo:

// callback para tratar posicao do usuario
function geoSuccess( pos ) {
	// armazena as coordenadas de latitude e longitude
	var lat = pos.coords.latitude,
		lng = pos.coords.longitude;
	
	// crie qualquer coisa legal usando as coordenadas
};

// callback para tratar erros
function geoError( err ) {
	switch( err.code ) {
		case 1:
			// permissao negada pelo usuario
			break;

		case 2:
			// nao foi possivel alcancar os satelites GPS
			break;

		case 3:
			// a requisicao demorou demais para retornar
			break;

		case 0:
			// ocorreu um erro desconhecido...
			break;
	}	
};

// objeto de configuracoes adicionais
var geoOptions = {
	enableHighAccuracy: true,
	timeout: 30000,
	maximumAge: 3000
};

	// um botao "stop"
var btnStop = document.getElementById( "btn-stop" ),

	// rastreia posicao do usuario continuamente
	watch = navigator.geolocation.watchPosition( geoSuccess, geoError, geoOptions );

// para de rastrear posicao do usuario ao clicar no botao "stop"
btnStop.addEventListener( "click", function() {
	navigator.geolocation.clearWatch( watch );
}, false );

Finalizando

Neste artigo você conheceu a API de Geolocalização HTML5 e viu alguns exemplos de código para se trabalhar com ela. Essa API sem dúvida será de grande ajuda para tornar suas aplicações mais ricas e interativas. Conforme já mencionado no artigo, você pode integrar o uso dessa API com outros serviços, API’s sociais etc., para criar coisas muito legais.

Você já usou a API de Geolocalização HTML5 em algum projeto? Gostou do artigo e pretende fazer algum experimento? Não gostou do artigo ou tem dúvidas? Fique a vontade para compartilhar suas experiências nos comentários.

Referências:

Grunt JS: Automatize tarefas e otimize o seu workflow

GruntJS-JavaScript-Task-Runner

Desenvolver um site ou aplicativo web moderno, é algo que está se tornando cada vez mais complexo. Por esse e outros motivos, todo bom desenvolvedor está sempre procurando novas maneiras de otimizar seu workflow visando tornar as coisas mais divertidas e aumentar sua produtividade sem perder a qualidade de seu trabalho.

Neste artigo irei apresentar o Grunt JS, uma excelente ferramenta criada por Ben Alman. Mostrarei  também um exemplo prático de uso, e espero que após lê-lo, você se sinta mais confortável com o Grunt JS e passe a adiciona-lo na “mistura” de seus próximos projetos.

Grunt JS… o que é isso, afinal?

O Grunt JS é um poderoso task runner (automatizador de tarefas), que roda no terminal, e é gerenciado pelo NPM, o gerenciador de pacotes para Node.js.

Legal, mas… por que eu devo aprender sobre Grunt JS?

Pelo “simples” fato da automatização de tarefas.

Já parou para pensar quanto tempo você perde toda vez que vai lançar um projeto em produção por exemplo? Você concatena e minifica (ou pelo menos deveria) seus arquivos de CSS e JavaScript, executa testes unitários e linting nos arquivos de JavaScript, otimiza imagens com ferramentas como Smush.it, JPEGMini, TinyPNG etc., separa os arquivos gerados e finalmente faz deploy da aplicação. E que dizer se você estiver usando um pré-processador de CSS como Sass? Mais uma tarefa na lista: Compilar arquivos .scss ou .sass.

Quanto menos tempo você perder executando tarefas repetitivas como essas citadas, mais fácil e produtivo se torna o seu trabalho. É aí que um task runner como o Grunt JS entra em ação. Uma vez configurado, o Grunt JS fará todo o “trabalho sujo” para você e/ou sua equipe, com praticamente nenhum esforço extra.

Primeiros passos

Como você já deve ter percebido, para usar o Grunt JS é necessário ter o Node.js com o NPM instalados em sua máquina. Caso você ainda não tenha, o processo de instalação é muito simples. Acesse http://nodejs.org/, clique no botão “Install” para fazer o download e execute o instalador. Feito isso você já terá o Node.js + NPM funcionando.

Agora abra seu console/terminal e vamos intalar o Grunt CLI (Grunt Command Line Interface). Se você trabalha em ambiente Windows, recomendo que utilize o Windows PowerShell, rodando como administrador. Em Mac ou sistemas Unix, talvez você precise usar o sudo.

npm install -g grunt-cli

O comando acima irá instalar o Grunt CLI globalmente, e agora você pode usar o comando grunt em seu terminal. O Grunt CLI permite que você rode versões diferentes do Grunt em uma mesma máquina simultaneamente.

Configurando um novo projeto com o Grunt

Todo projeto Grunt precisa de dois arquivos básicos: package.json e o Gruntfile.js. O package.json é um arquivo utilizado para guardar metadados de projetos que são publicados como módulos para o NPM, e o Gruntfile é um arquivo JavaScript ou CoffeeScript, utilizado para configurar tarefas e carregar os plugins do Grunt. Esses dois arquivos devem ser criados dentro da raiz de seu projeto, e devem sempre estar juntos no mesmo diretório.

Aprendendo na prática

Para começar nosso exemplo prático, vamos criar um diretório chamado grunt-project e dentro dele iremos criar um subdiretório chamado src, que é onde iremos salvar o package.json e o Gruntfile.js. Nosso package.json terá um estrutura mais ou menos assim:

{
	"name": "project-name",
	"version": "0.1.0",
	"description": "Project description"
}

Você pode iniciar seguindo o modelo acima e edita-lo conforme suas necessidades. Para maiores informações sobre quais propriedades você pode usar, consulte a documentação. Você pode também dar uma olhada nesse guia interativo, e conhecer diversas propriedades interessantes para acrescentar em sua configuração.

Com o package.json configurado, agora crie um novo arquivo de JavaScript e salve-o dentro do mesmo diretório src, ao lado de seu package.json, com o nome de Gruntfile.js.

O Gruntfile.js é composto por uma função que engloba todo o código, as configurações das tarefas do projeto, carregamento dos “grunt plugins” instalados (Veremos mais sobre como instalar plugins do grunt daqui a pouco) e o(s) registro(s) de sua(s) tarefas personalizadas.

Um Gruntfile.js básico pode ser feito seguindo essa estrutura:

"use strict";

module.exports = function( grunt ) {

	grunt.initConfig({

		// configuracoes das tarefas

	});

	// carregando plugins
	grunt.loadNpmTasks( 'plugin-name' );

	// registrando tarefas
	grunt.registerTask( 'default', [ 'watch' ] );

};

Dentro da função que engloba todo nosso código, temos alguns métodos sendo chamados, como: initConfig(), loadNpmTasks() e registerTask(). Vamos entendê-las melhor.

O método initConfig(), recebe um objeto JSON que será responsável por armazenar as configurações das tarefas de nosso projeto.

O loadNpmTaks() carrega um grunt plugin e permite que você use-o em seu Gruntfile. Você deve chamar esse método para todos os plugins que for utilizar. Carregar plugins vai se tornar algo muito chato conforme você for utiliando o Grunt com mais frequência, por isso mostrarei como você pode automatizar essa tarefa também.

E por fim o registerTask(), recebe dois argumentos: uma string e um array com o nome de uma ou mais tarefas que devem ser realizadas ao executar esse comando. Em nosso exemplo, ao rodar o comando grunt no terminal, ele irá chamar a tarefa default, executando as configurações definidas para o plugin watch.

Instalando o Grunt e plugins úteis

Antes de fazermos nossas configurações, vamos instalar o Grunt e os plugins que iremos utilizar. Para instalar o Grunt, e marcá-lo como uma das dependências de seu projeto é simples:

npm install grunt --save-dev

Após rodar esse comando, você verá que um diretório node_modules foi criado, e visto que usamos ––save-dev, também foi adicionada uma propriedade devDependencies dentro do package.json. Isso é muito importante, pois assim, caso esteja trabalhando com controle de versões, você não precisará incluir o diretório node_modules em seus commits, basta rodar o comando npm install e todas as dependências do projeto serão instaladas.

O processo de instalação de plugins é o mesmo. Geralmente existe um plugin específico para qualquer tarefa que você queira realizar. Literalmente são milhares de plugins que já foram criados para o Grunt. Você pode consultar essa lista extensa de plugins.

Vamos agora instalar os plugins que iremos utilizar:

npm install matchdep --save-dev
npm install grunt-contrib-watch --save-dev
npm install grunt-contrib-compass --save-dev
npm install grunt-contrib-uglify --save-dev
npm install grunt-ftp-deploy --save-dev

Com o Grunt e os plugins que precisaremos instalados, agora vamos configurar nossas tasks e finalmente ver a “mágica” funcionando. Explicarei as configurações uma a uma dos plugins instalados acima, e no final você poderá consultar o código fonte completo.

Carregando todas as taks do Grunt automaticamente

O primeiro plugin que iremos configurar é o matchdep.

Conforme comentei anteriormente, cada vez que você for usar um novo plugin, será necessário adicionar uma chamada ao método loadNpmTasks(). Isso traz um trabalho chato e demanda manutenção desnecessária sempre que for adicionar ou remover um plugin.

Felizmente com o plugin matchdep nós cortamos esse problema pela raiz. O que esse plugin faz é o seguinte: Ele extrai informações gravadas em seu package.json, e chama o método loadNpmTasks() para cada plugin instalado.

require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks);

Pronto. Você nunca mais precisará adicionar ou remover chamadas ao método loadNpmTasks() manualmente de novo.

Executando tarefas após alterar arquivos

Com o plugin grunt-contribu-watch, criamos uma task chamada “watch”. Ao rodar essa tarefa, o Grunt irá “observar” os arquivos do nosso projeto, e quando fizermos qualquer alteração em um dos arquivos especificados, ele irá executar outras tarefas que foram atreladas a ela automaticamente. Isso é fantástico, pois dessa forma você pode executar várias tarefas automaticamente com apenas um único comando.

// Watch
watch: {
	css: {
		files: [ '../assets/scss/**/*' ],
		tasks: [ 'compass' ]
	},
	js: {
		files: '../assets/js/**/*',
		tasks: [ 'uglify' ]
	}
},

Nesse exemplo, sempre que algum arquivo dentro do diretório ‘assets/scss’ for alterado, o Grunt irá executar a tarefa ‘compass’ para compilar o código Sass. E sempre que editarmos um arquivo dentro do diretório ‘assets/js’ o Grunt irá executar a tarefa ‘uglify’ para concatenar e minificar os arquivos JavaScript.

Veremos como configurar essas tarefas logo abaixo.

Compilar Sass usando Compass

Se você trabalha com Sass + Compass, compilar seu código para CSS é muito simples usando o plugin grunt-contrib-compass. Em nosso exemplo, definimos a propriedade force como true, para permitir que o Compass sobrescreva arquivos, indicamos o caminho para um arquivo config.rb que contém as demais configurações do Sass e determinamos que o código compilado sairá minificado.

Se você não está familiarizado com o arquivo config.rb que foi citado, leia a documentação para obter maiores informações. Então criamos nosso arquivo config.rb e adicionamos o seguinte código em nosso Gruntfile.js:

// Compile scss
compass: {
	dist: {
		options: {
			force: true,
			config: 'config.rb',
			outputStyle: 'compressed'
		}
	}
},

Concatenando e minificando JavaScript

Para concatenar e minificar nossos arquivos de JavaScript, vamos utilizar o plugin grunt-contrib-uglify. As configurações dessa task também são muito simples. Na propriedade files, passamos um objeto com local onde o arquivo minificado será gravado, que recebe um array de arquivos a serem concatenados e minificados:

// Concat and minify javascripts
uglify: {
	options: {
		mangle: false
	},
	dist: {
		files: {
			'../build/js/app.min.js': [
				'../assets/js/app.js'
			]
		}
	}
},

Fazendo deploy do projeto

Conforme comentei no inicio do artigo, o Grunt também pode ajudar você na hora de fazer deploy de seus projetos em um servidor. Se o seu host lhe dá acesso via SSH, você pode utilizar o plugin grunt-rsync para isso.

Mas em alguns casos você terá acesso apenas via FTP. Não se preocupe, para isso iremos utilizar o plugin grunt-ftp-deploy.

Para que o grunt-ftp-deploy funcione, vamos precisar adicionar um arquivo chamado .ftppass dentro do diretório src do nosso projeto, junto com Gruntfile.js, package.json e o config.rb. Este arquivo é um objeto JSON que armazena os dados de login do seu FTP. Tome cuidado com esse arquivo quando estiver usando Git ou outro sistema de versionamento de arquivos. O .ftppass segue essa estrutura:

{
	"key1": {
		"username": "your-ftp-user",
		"password": "your-ftp-pass"
	}
}

Agora que já temos nosso nome de usuário e senha guardados no arquivo .ftppass, vamos voltar ao nosso Gruntfile.js e configurar a task para fazer o deploy em nosso servidor.

Basta trocar o valor da propriedade ‘host’ para o host do seu site e o valor de ‘dest’ para o caminho onde você irá upar seus arquivos. Em ‘exclusions’ você pode especificar que arquivos e diretórios não devem ser enviados para o servidor junto com sua aplicação.

// FTP deployment
'ftp-deploy': {
	build: {
		auth: {
			host: 'ftp.yoursite.com',
			port: 21,
			authKey: 'key1'
		},
		src: '../',
		dest: '/www/my-app/',
		exclusions: [
			'../**/.DS_Store',
			'../**/Thumbs.db',
			'../.git',
			'../.gitignore',
			'../README.md',
			'../src',
			'../assets'
		]
	}
}

Juntando tudo e testando

Se você está acompanhando o artigo com atenção, nesse ponto você já terá todas as tarefas do Grunt configuradas. Vamos adicionar as duas últimas linhas de código em nosso Gruntfile.js para registrar duas tarefas personalizadas: watch e deploy.

// registrando tarefa default
grunt.registerTask( 'default', [ 'watch' ] );

// registrando tarefa para deploy
grunt.registerTask( 'deploy', [ 'ftp-deploy' ] );

Registrando essas tarefas, agora quando usarmos o comando “grunt” no terminal, será executada a task “watch” conforme já foi explicado anteriormente, e quando usarmos o comando “deploy”, irá rodar nossa task que fará deploy para o servidor.

Então a versão final do nosso Gruntfile.js ficou assim:

"use strict";

module.exports = function( grunt ) {

	// Load all tasks
	require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks);

	grunt.initConfig({

		// Watch
		watch: {
			css: {
				files: [ '../assets/scss/**/*' ],
				tasks: [ 'compass' ]
			},
			js: {
				files: '../assets/js/**/*',
				tasks: [ 'uglify' ]
			}
		},

		// Compile scss
		compass: {
			dist: {
				options: {
					force: true,
					config: 'config.rb',
					outputStyle: 'compressed'
				}
			}
		},

		// Concat and minify javascripts
		uglify: {
			options: {
				mangle: false
			},
			dist: {
				files: {
					'../build/js/app.min.js': [
						'../assets/js/app.js'
					]
				}
			}
		},

		// FTP deployment
		'ftp-deploy': {
			build: {
				auth: {
					host: 'ftp.yoursite.com',
					port: 21,
					authKey: 'key1'
				},
				src: '../',
				dest: '/www/my-app/',
				exclusions: [
					'../**/.DS_Store',
					'../**/Thumbs.db',
					'../.git',
					'../.gitignore',
					'../README.md',
					'../src',
					'../assets'
				]
			}
		}

	});

	// registrando tarefa default
	grunt.registerTask( 'default', [ 'watch' ] );

	// registrando tarefa para deploy
	grunt.registerTask( 'deploy', [ 'ftp-deploy' ] );

};

Em seu terminal acesse o diretório src e rode o comando:

grunt

Altere um arquivo do diretório assets/scss e assets/js e você verá que será gerado o diretório build com os arquivos minificados prontos para deploy.

Volte ao terminal e pressione CTRL + C para encerrar a task watch.

Agora para fazer o deploy, rode o comando abaixo e aguarde os arquivos serem enviados para seu servidor conforme especificado em suas configurações:

grunt deploy

Muito bem! Se você acompanhou tudo com atenção, com apenas um único comando você compilou arquivos scss, concatenou e minificou arquivos de JavaScript e gerou uma build do seu projeto. E com apenas mais um simples comando, você fez o deploy dessa build.

Eu disse… parece mágica não é mesmo? =D

Conclusão

Ferramentas como o Grunt JS só nos trazem grandes beneficios e estão aí disponíveis para quem quiser usar. Além das opções ensinadas nesse artigo, existem muitas outras coisas que o Grunt pode fazer, e você deve pesquisar sobre elas.

Espero que você tenha se interessado e pesquise mais para adaptar o Grunt as suas necessidades. Sem dúvida você não irá se arrepender se tirar um tempo para estudar e integrar esse tipo de ferramenta em seu workflow.

O código fonte do projeto desenvolvido nesse artigo está disponível no Github. Você pode clonar o repositório e personaliza-lo para seus projetos. Sinta-se a vontade para enviar sugestões e tirar suas dúvidas nos comentários.

Outros links sobre Grunt:

Como vender o valor do mobile para seus clientes

Recentemente conheci um pessoal da comunidade de Front-end’s do Brasil que resolveram criar um blog dedicado para traduções de artigos sobre desenvolvimento web, que foi chamado de Code in Brasil. Achei a idéia bacana e decidi colaborar, pois todos sabemos o quanto é difícil encontrar  conteúdo de qualidade sobre web em nossa língua.

Foi então que resolvi traduzir um artigo do famoso blog Smashing Magazine, sobre o tema “How To Sell The Value Of Mobile To Clients“. Neste artigo, o autor Mark Reeves fala sobre algumas estratégias que nós desenvolvedores podemos adotar ao conversar com nossos clientes para transmiti-los o real valor de se investir em uma solução mobile para seus negócios. Leia o artigo completo traduzido acessando este link.