Posts com Tag ‘javascript’

1. Por que da necessidade de um proxy?

Todos os navegadores modernos impõem uma restrição de segurança em conexões de rede, que inclui chamadas para XMLHttpRequest. Esta restrição impede que um script (ou aplicativo) de fazer uma conexão com qualquer servidor web que não seja o do próprio domínio de onde fora carregado (Internet Explorer permitirá solicitações entre domínios se a opção tiver sido ativada nas preferências). Se tanto a aplicação web e os dados XML que usa aplicativos vêm diretamente do mesmo domínio, então não haverá qualquer restrição e solicitações de dados podem ser realizadas livremente. Ou seja, quando um código JavaScript está carregando dados HTML, XML ou JSON de um recurso no mesmo domínio, tudo que é necessário é solicitar normalmente (de forma direta) estes dados. Observe a figura a seguir.

Solicitação de dados do mesmo servidor: sem necessidade de proxy.

Se, entretanto, a aplicação web é carregada de um determinado domínio web e esta aplicação faz solicitações de dados de serviços da Web de outro domínio, o navegador do usuário impedirá que a conexão seja aberta. Pode ser uma chatice a princípio, mas isto se justifica por questões de segurança. Observe a figura a seguir.

Solicitação de dados de outro host: necessidade de proxy.

Há uma série de soluções para esta situação, mas o mais comumente usado é a instalação de um proxy no próprio servidor web de onde a aplicação fora carregada. Em vez de fazer chamadas XMLHttpRequest diretamente para o serviço web, se faz as chamadas para o proxy do servidor web do domínio da aplicação. O proxy, em seguida, passa a chamada para o serviço web no host destino e, em contrapartida, passa os dados para o aplicativo cliente quando estes forem respondidos. Como a conexão é feita para o servidor da aplicação, e os dados de volta provêm do servidor da aplicação, o navegador não tem nada a reclamar. Observe a figura abaixo.

Solicitação de dados de outro servidor: proxy funcionando.

Conclusão: é necessário ter um web proxy funcionando para ultrapassar as limitações de segurança do Javascript quando se tem que requisitar dados de um servidor diferente daquele da aplicação.
OBS:
Mais rigorosamente, a origem de uma página é definida por seu protocolo, host e porta. Por exemplo, a origem da página deste post é (‘http’,’concani3.wordpress.com’, 80).

2. Um simples web proxy em Python para uso com a biblioteca OpenLayers
Vamos mostrar neste post a superação da restrição “cross-domain” através de um simples script com função Proxy, escrito em Python. Isto irá fazer com que o código JavaScript da aplicação possa acessar conteúdos de webpages de outros domínios, superando estas limitações de segurança “cross-domain”. É necessário para isto instalar este script no servidor de onde é carregado a aplicação JavaScript.

2.1 Disponibilizar o script que implementa o proxy
No caso da bilbioteca OpenLayers-2.12, esta traz uma sugestão de script em Python para funcionar como um proxy. Esse script é encontrado em OpenLayers-2.12/examples/proxy.cgi. Há a necessidade de alterar apenas uma instrução deste arquivo: incluir o nomes dos domínios destino (ou dos dominios destinos) no array “allowedHosts”. E nada mais. Por exemplo, se houvesse a necessidade de requisitar dados dos dominios http://www.exemplo1.com.br e de http://www.exemplo2.com.br, a configuração ficaria assim:
allowedHosts = ['www.exemplo1.com.br', 'www.exemplo2.com.br']

Vamos disponibilizar este arquivo numa pasta tradicionalmente comum para scripts cgi: em /usr/lib/cgi-bin. Assim, fazer:

# cp pasta_do_OpenLayers/OpenLayers-2.12/examples/proxy.cgi /usr/lib/cgi-bin/.

OBS:
– se a pasta destino cgi-bin não estiver criada, faça sua criação;
– por padrão, o interpretador Python em máquinas Linux já vem instalado. Portanto, nada a se preocupar quanto a este aspecto.

2.2 Código do web proxy
Por conveniência, vamos disponibilizar aqui este código do web proxy fornecido pela biblioteca OpenLayers:


#!/usr/bin/env python

import urllib2
import cgi
import sys, os

# Designed to prevent Open Proxy type stuff.

allowedHosts = ['www.openlayers.org', 'openlayers.org', 
                'labs.metacarta.com', 'world.freemap.in', 
                'prototype.openmnnd.org', 'geo.openplans.org',
                'sigma.openplans.org', 'demo.opengeo.org',
                'www.openstreetmap.org', 'sample.azavea.com',
                'v2.suite.opengeo.org', 'v-swe.uni-muenster.de:8080', 
                'vmap0.tiles.osgeo.org', 'www.openrouteservice.org',
                'maps.wien.gv.at','www.exemplo1.com.br','www.exemplo2.com.br']

method = os.environ["REQUEST_METHOD"]

if method == "POST":
    qs = os.environ["QUERY_STRING"]
    d = cgi.parse_qs(qs)
    if d.has_key("url"):
        url = d["url"][0]
    else:
        url = "http://www.openlayers.org"
else:
    fs = cgi.FieldStorage()
    url = fs.getvalue('url', "http://www.openlayers.org")

try:
    host = url.split("/")[2]
    if allowedHosts and not host in allowedHosts:
        print "Status: 502 Bad Gateway"
        print "Content-Type: text/plain"
        print
        print "This proxy does not allow you to access that location (%s)." % (host,)
        print
        print os.environ
  
    elif url.startswith("http://") or url.startswith("https://"):
    
        if method == "POST":
            length = int(os.environ["CONTENT_LENGTH"])
            headers = {"Content-Type": os.environ["CONTENT_TYPE"]}
            body = sys.stdin.read(length)
            r = urllib2.Request(url, body, headers)
            y = urllib2.urlopen(r)
        else:
            y = urllib2.urlopen(url)
        
        # print content type header
        i = y.info()
        if i.has_key("Content-Type"):
            print "Content-Type: %s" % (i["Content-Type"])
        else:
            print "Content-Type: text/plain"
        print
        
        print y.read()
        
        y.close()
    else:
        print "Content-Type: text/plain"
        print
        print "Illegal request."

except Exception, E:
    print "Status: 500 Unexpected Error"
    print "Content-Type: text/plain"
    print 
    print "Some unexpected error occurred. Error text was:", E

 
2.3 Configurar o Apache
Vamos fazer aqui uma configuração muito simples do Apache, sem lançar mão de criação de “Virtual Hosts”. Editar o host virtual default, criado quando da instalação do Apache, o 000-default:
Editar /etc/apache2/sites-enabled/000-default, alterando algumas de suas linhas:


    # Para poder executar a chamada proxy pela instrução:
    # OpenLayers.ProxyHost = "/cgi-bin/proxy.cgi?url=";
	ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
	<Directory "/usr/lib/cgi-bin">
		AllowOverride None
		Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
		Order allow,deny
		Allow from all
        </Directory>

 
Reiniciar o Apache:
# /etc/init.d/apache2 restart

2.4 Testar o funcionamento do web proxy
Para verificar o funcionamento do proxy, dois testes muito simples podem ser realizados.

a) Primeiro teste
Na barra de endereços do navegador, digitar o endereço do proxy passando como parâmetro um endereço que consta em “allowedHosts”. Por exemplo, o endereço openlayers.org. Fazer assim:
http://nome-servidor-do-proxy/cgi-bin/proxy.cgi?url=http://openlayers.org

Deve ser mostrado a página inicial do OpenLayers, solicitada através do proxy.

b) Segundo teste
Na barra de endereços do navegador, digitar o endereço do proxy passando como parâmetro um endereço que NÃO consta em “allowedHosts”. Por exemplo, o endereço uol.com.br. Fazer assim:
http://nome-servidor-do-proxy/cgi-bin/proxy.cgi?url=http://uol.com.br

Uma mensagem de negação do proxy deve ser mostrada na tela. Algo assim:


This proxy does not allow you to access that location (uol.com.br).

{'HTTP_COOKIE': '__utma=145174525.209261312.1401672948.1401672948.1401672948.1;
 __utmb=145174525.1.10.1401672948; __utmc=145174525; __utmz=145174525.1401672948.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)',
 'SERVER_SOFTWARE': 'Apache', 'SCRIPT_NAME': '/cgi-bin/proxy.cgi', 'SERVER_SIGNATURE': '', 'REQUEST_METHOD': 'GET',
 'SERVER_PROTOCOL': 'HTTP/1.1', 'QUERY_STRING': 'url=http://uol.com.br', 'PATH': '/usr/local/bin:/usr/bin:/bin',
 'HTTP_USER_AGENT': 'Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20131030 Firefox/17.0 Iceweasel/17.0.10',
 'HTTP_CONNECTION': 'keep-alive', 'SERVER_NAME': 'nome-servidor-do-proxy', 'REMOTE_ADDR': 'IP_do_navegador_usuario',
 'SERVER_PORT': '80', 'SERVER_ADDR': 'nome-servidor-do-proxy', 'DOCUMENT_ROOT': '/var/www',
 'SCRIPT_FILENAME': '/usr/lib/cgi-bin/proxy.cgi', 'SERVER_ADMIN': 'webmaster@localhost', 'HTTP_HOST': 'nome-servidor-do-proxy',
 'REQUEST_URI': '/cgi-bin/proxy.cgi?url=http://uol.com.br', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
 'GATEWAY_INTERFACE': 'CGI/1.1', 'REMOTE_PORT': '6315', 'HTTP_ACCEPT_LANGUAGE': 'pt-BR,pt;q=0.8,en-US;q=0.5,en;q=0.3',
 'HTTP_ACCEPT_ENCODING': 'gzip, deflate'
}

 
2.5 Definição do web proxy na aplicação
Estando o web proxy funcionando, na aplicação JavaScript que utiliza a bilbioteca OpenLayers basta inserir a definição do ProxyHost:

OpenLayers.ProxyHost = "/cgi-bin/proxy.cgi/?url=";

Referências:
1- JavaScript: use a Web Proxy for Cross-Domain XMLHttpRequest Calls
2- Same-origin policy
3- Cross-Domain requests in Javascript
4- Same-origin policy
5- In which ways could a javascript making a cross domain HEAD request be a threat?
6- W3C – Cross-Origin Resource Sharing

Anúncios

1) Introdução
Ext JS é uma biblioteca publicada sob licença GPL utilizada para a construção de aplicativos web interativos utilizando AJAX, DHTML e DOM. Este framework utiliza a arquitetura MVC como padrão de desenvolvimento, e oferece aos desenvolvedores diversos componentes de UI comuns às principais aplicações comerciais, como grid’s, formulários, botões, combobox, checkbox e outros.

Sencha Cmd é uma ferramenta de linha de comando multi-plataforma que fornece muitas tarefas automatizadas em todo o ciclo de vida completo de seus aplicativos desde a geração de um novo projeto até a sua implantação em produção. Sencha Cmd suporta Sencha Ext JS versão 4.1.1a ou superior e Sencha Touch versão 2.1 ou superior.

O presente post foi testado com as seguintes versões software:
– máquina com arquitetura 64 bits;
– S.O. Linux/Debian 7 (wheezy);
– Sun Java 8: jdk-8u5-linux-x64;
– ExtJS 4.2.1-gpl;
– SenchaCmd-4.0.4.84-linux-x64.

2) Instalar Java e Ruby
Para o java, seguir procedimento descrito no post para instalação Java.
# apt-get install ruby

3) Baixar e instalar ExtJS numa máquina Desktop com interface gráfica e Apache instalado
A versão GPL do ExtJS pode ser baixada de seu sítio oficial:
$ wget -c http://cdn.sencha.com/ext/gpl/ext-4.2.1-gpl.zip

Imediatamente já se pode ver o ExtJS em ação. Estando com o servidor de páginas Apache instalado (para instalar pode ser utilizado como base o post Instalar servidor LAMP (Linux + Apache + PHP + MySQL) no Debian), e supondo como diretório root default do Apache a pasta /var/www, instale o ExtJS a partir deste diretório:

/var/www# unzip ext-4.2.1-gpl.zip
/var/www# mv ext-4.2.1.883/ extjs/

Agora com o navegador, acesse a URL http://localhost/extjs/ para visualizar a documentação e vários exemplos. Alguns exemplos disponíveis que usam Ajax necessitarão estar em um servidor web verdadeiro a fim de funcionarem corretamente.

4) Baixar e instalar Sencha Cmd numa máquina Desktop com interface gráfica

$ unzip SenchaCmd-4.0.4.84-linux-x64.run.zip
$ chmod +x SenchaCmd-4.0.4.84-linux-x64.run
$ ./SenchaCmd-4.0.4.84-linux-x64.run

Será solicitado a pasta para se fazer a instalação. No caso, fiz a indicação de /home/usuario/bin/, fazendo com que a instalação ocorrece em /home/usuario/bin/Sencha/Cmd/4.0.4.84/
Ao iniciar a instalação uma primeira tela será apresentada conforme esta a seguir.

Tela inicial da instalação do Sencha Cmd

Ao final da instalação, a tela abaixo será mostrada.

Tela ao final da instalação do Sencha Cmd

Automaticamente, o processo de instalação gerou no arquivo /home/usuario/.bashrc as seguintes duas entradas:
export PATH=/home/usuario/bin/Sencha/Cmd/4.0.4.84:$PATH
export SENCHA_CMD_3_0_0="/home/usuario/bin/Sencha/Cmd/4.0.4.84"

Ou seja, o caminho já fora colocado na variável de ambiente PATH. Na próxima vez que uma sessão for iniciada (é só fechar e abrir o terminal novamente) este novo PATH passará a valer. Caso não queira fechar e abrir o terminal novamente, uma alternativa seria executar o comando a seguir:
$ source .bashrc

4.1) Verificar o funcionamento
$ sencha help
$ sencha which
$ sencha diag show

– o primeiro comando mostra um help dos comandos disponíveis
– o segundo comando mostra o caminho para a versão corrente instalada do Sencha Cmd
– o terceiro comando mostra as propriedades da configuração corrente.

4.2) Desinstalar

Se necessitar desinstalar o Sencha Cmd, basta executar o comando:
$ uninstall

As duas telas a seguir ilustram o processo: uma mostrada em seu início e a outra confirmando a desinstalação ao final.
Obs: é bom verificar se as duas entradas colocadas no arquivo .bashrc do usuário foram retiradas. Caso contrário, editar o arquivo e retirá-las manualmente. Da próxima vez que for iniciada uma sessão bash o PATH não contará mais com o apontamento para a pasta do Sencha Cmd.

Tela inicial para desinstalação do Sencha Cmd

Tela final de confirmação da desinstalação do Sencha Cmd

5) Arquivo de configuração do Sencha
O arquivo primeiro de configuração do Sencha Cmd é o /home/usuario/bin/Sencha/Cmd/4.0.4.84/sencha.cfg, que é o primeiro arquivo de configuração lido quando o Sencha Cmd é iniciado. Similar ao Apache Ant (no qual muitos dos aspectos do Sencha Cmd são baseados), a sua configuração é aplicada através do modelo “first-write-wins”. Isto é essencial para permitir que valores das propriedades possam ser reescritas através da linha de comando do usuário. O processo de carga da configuração começa pela busca no diretório corrente e continua até o Workspace. Em termos práticos, as propriedades de configuração mais importantes são aqueles passadas ​​na linha de comando. Estas irão substituir as propriedades provenientes de qualquer dos outros arquivos anteriores.
Para saber mais sobre os arquivos de configuração do Sencha Cmd vide documentação em Advanced Sencha Cmd.

6) Estrutura do comando no Sencha Cmd
Todas as facilidades proporcionadas pelo Sencha Cmd estão organizadas em categorias (ou módulos) e comandos:

$ sencha [category] [command] [options...] [arguments...]
$ sencha help [command] [category]

Exemplos de comandos básicos:
$ sencha help app
$ sencha help generate
$ sencha help config
$ sencha help generate app
$ sencha -sdk extjs generate app -p p_gerar -n nova myapp

Obs: neste último comando, as pastas “p_extjs” e “p_gerar” estão abaixo da pasta corrente.

Funções em JavaScript

Publicado: 15/11/2013 em Programação
Tags:

1. Introdução
São características das funções em JavaScript (JS):

  • serem objetos de primeira classe. Tratadas como qualquer outro objeto da linguagem, inclusive tendo propriedades e métodos próprios;
  • definem escopo;
  • poderem ser invocadas na forma de funções construtoras;
  • terem propriedades:
    • a propriedade “prototype” aponta para um novo objeto em branco, que por sua vez possui uma propriedade “constructor”. O “constructor” aponta para a função criada;
    • a propriedade “name” armazena o nome da função como uma string. Em funções sem nome, esta propriedade terá uma string vazia;
    • a propriedade “this”, que se refere a um objeto que está implicitamente associado à invocação da função e que recebe o nome de “contexto da função”. O parâmetro “this” aponta para algo que depende de como a função é invocada;
    • outras propriedade criadas pelo usuário, a qualquer tempo.

Funções em JS são declaradas utilizando um “literal de função” que cria um valor de função. Literais de função são compostos de quatro partes:

  • a palavera-chave “function”;
  • um nome, opcional (se omitido será uma “função anônima”) que, se especificado, deve ser um identificador JS válido;
  • uma lista separada por vírgulas de nomes de parâmetros entre parênteses;
  • o corpo da função, como uma série de instruções JS entre chaves.

OBS:
a) se não houver a necessidade de uma função ser chamada por seu nome, não há necessidade de dar um nome a ela (será uma função anônima);
b) quando uma função é nomeada, esse nome é válido ao longo do escopo dentro do qual a função é declarada.

Exemplos de declarações de funções:


function calcular() {return true;} // declaração de uma função nomeada
var xyz = function() {return true;} // declaração de uma função anônima atribuída a variável xyz

function fixar() {        // declaração de uma função nomeada
    var a = 0;
    return a;
}

var laranja = {
    cortar: function(){  // declaração de uma função anônima como método
        alert('cortou');
    }
};

setTimeout(
    function() { alert('fim temporização'); },   // declaração de uma função anônima como callback
    500);

2. Escopo
Em JS, escopos são declarados por “funções”, e não por blocos. Exemplo:

if (a) {
    var x = 10;
}
alert(x);

No exemplo acima, o JS não encerra o escopo da variável x ao final do bloco if. O escopo funciona da seguinte forma em JS:

  • declaração de variável está em escopo a partir do ponto de sua declaração até o final da função dentro da qual fora declarada;
  • função nomeada está em escopo dentro da função inteira na qual fora declarada (mecanismo de içamento, ou “hoisting”).

2.1 Parâmetro “this”
Sempre que uma função é invocada, além dos parâmetros que representam os argumentos que foram fornecidos na chamada da função, um parâmetro implícito chamado “this” também é transmitido a ela. O parâmetro “this” se refere a um objeto que está implicitamente associado à invocação da função e que recebe o nome de “contexto da função”. O parâmetro “this” aponta para algo que depende de como a função é invocada.

3. Invocações
Há 4 (quatro) maneiras diferentes de invocarmos uma função:

  • como uma função, na qual a função é invocada de modo direto;
  • como um método, o que associa a invocação a um objeto;
  • como um construtor, no qual um novo objeto é trazido à existência;
  • por meio de seus métodos apply() e call() (aplicação/chamada).

Todas as invocações de funções também recebem dois parâmetros implícitos: “arguments” e “this”. Eles podem ser referenciados dentro da função da mesma forma que qualquer outro parâmetro nomeado explicitamente.

3.1 Invocação como função
Ocorre quando uma função é invocada utilizando o operador () e a expressão para o qual o operador () é aplicado não referencia a função como uma propriedade de um objeto. Exemplos:

function calcular() {};
calcular();

var xyz = function() {};
xyz();

OBS: observe que o contexto da função será seu contexto externo, e o parâmetro this apontará para esse contexto externo à função.

3.2 Invocação como método
Ocorre quando uma função é atribuída a uma propriedade de um objeto e a invocação ocorre referenciado-se a função que utiliza essa propriedade. A função será invocada como um método desse objeto. Exemplo:


var xyz = {};
xyz.calcular = function() {};
xyz.calcular();

OBS:

  • observe que o contexto da função será o objeto, e o parâmetro this aponta para este objeto. Esta, junto com construtores, são as principais formas pelas quais o JS permite que códigos orientados a objetos sejam escritos;
  • notar que o contexto de funções para cada invocação da função muda dependendo de como ela é invocada, e não de acordo como foi declarada.

3.3 Invocação como construtor
Para invocar a função como um construtor, precedemos sua invocação com a palavra-chave “new”. Exemplo:

function Especialistas() {	// define um construtor que cria um a propriedade "oculto" em qualquer objeto que seja o contexto da função.
	this.ocultar = function() { return this;};
}
var especialista1 = new Especialistas();
var especialista2 = new Especialistas();
var contexto1 = especialista1.ocultar();
var contexto2 = especialista2.ocultar();

OBS: a codificação como construtor traz a característica de se evitar a repetição de código. O código comum é escrito uma só vez, como o corpo do cnstrutor.

4. O padrão de função imediata

O padrão de função imediata é uma sintaxe que permite executar uma função tão logo ela seja definida. Veja o exemplo:

(funtion () {
	alert('Olá');
}());

Cuidados com a sintaxe:

  • definir a função usando uma expressão de função;
  • adicionar um par de parênteses no final, o que faz a função ser executada imediatamente;
  • colcoar toda a função entre parênteses.

Outro exemplo:

var resultado = (function () {
	return 2 + 2;
}());	// resultado = 4

5. Herança e Funções Construtoras do Usuário
O JavaScript possui herança, podendo ser realizada de várias formas. A forma mais comum é através do uso de protótipos, onde um “protótipo” é um objeto. Toda função que é criada recebe automaticamente uma propriedade “prototype” que aponta para um novo objeto em branco. Por sua vez, este objeto tem uma propriedade “constructor” que aponta para a função que fora criada. E aqui está a mágica da herança: pode ser adicionado membros a esse objeto em branco e, quando se fizer a sua instanciação, estes outros objetos criados herdarão suas propriedades e métodos. Vejamos o caso prático a mostrado a seguir onde se faz a criação de um novo objeto através de uma função construtora, o que permite herdar o método identificar() do objeto original:

function Identificacao(p_name) {
	this.name = p_name;
};
Identificar.prototype.identificar = function () {
	return "Eu sou " + this.name;
};

Para utilizar esta função construtora, instanciando novos objetos:
var maria = new Identificacao("Maria");
maria.identificar(); // "Eu sou Maria"

OBS:
a) Construtores não deixam de ser funções, mas são invocados com new. “Identificacao” parece muito com uma classe, mas não há classes no JavaScript;
b) Adicionamos o método identificar() ao protótipo da função construtora Identificacao. Isso é mais eficiente que definir este método através de this, pois identificar() não se altera de uma instância para outra. Caso se utilizasse “this”, a cada nova chamada de “new Identificacao()” uma nova função identificar() seria criada desnecessariamente, não reaproveitando código. No exemplo acima, cada nova instância de Pessoa herda o método identificar() sem precisar fazer sua recriação.

Links:
Padrões de desenvolvimento JavaScript que utilizo

1. Introdução
Utilizar padrões ajuda a escrever códigos melhores, utilizando as melhores práticas. Melhora a comunicação entre os desenvolvedores e equipes.

Tem-se em JavaScript:

  • uma linguagem orientada a objetos;
  • uma linguagem funcional;
  • a implementação do conceito de herança (normalmente fazendo uso de protótipos);
  • funções:
    • são objetos de primeira classe. Tratadas como qualquer outro objeto da linguagem, podendo inclusive terem propriedades e métodos próprios;
    • definem um escopo;
    • existem funções construtoras;
    • toda função tem uma propriedade “prototype”;
  • arrays são objetos;
  • inexistência de “classes”. JavaScript lida apenas com objetos.

2. Aspectos importantes a observar em JavaScript

Norma JavaScript
O núcleo (core) da linguagem de programação JavaScript é baseado na norma ECMAScript (ES, de forma abreviada). A versão 3 da norma foi aceita oficialmente em 1999. A versão 5 foi aprovada em dezembro de 2009. A inclusão mais importante na versão 5 foi o modo estrito. Para ativar este modo, basta uma vez por escopo fazer:

function init() {
         'use strict';
         // o resto da função...
}

A função
Em toda função que é criada, esta recebe automaticamente uma propriedade “prototype”. A propriedade “prototype” aponta para um novo objeto em branco, que por sua vez possui uma propriedade “constructor”. O “constructor” aponta para a função criada.

3. Padrões que utilizo no desenvolvimento de código JavaScript
3.1- Sobre variáveis:
a) Utilizar a menor quantidade de variáveis globais possíveis;
b) Sempre declarar variáveis usando “var”;
c) Usar uma única declaração “var” no topo de cada função para declarar as variáveis criadas.

3.2- Convenções de nomeações:
a) Ter como base o padrão camel case, adotando:

construtores (invocadas por new) camel case iniciando por maiúsculas, palavras sem separação. Preferencialmente nomeados como um “substantivo”;
funções/métodos camel case iniciando por minúsculas, palavras sem separação. Preferencialmente, a palavra é um “verbo”;
variáveis completamente em caixa baixa, palavras separadas por underscores;
constantes completamente em caixa alta, palavras separadas por underscores;
parâmetros recebidos por uma função/método começam com p_;
variáveis ou métodos privados ou “protegidos” (protected) começam com _ (underline);

3.3- Sobre indentação de blocos de código:
a) Usar sempre indentação do código;
b) Estilo de indentação 1TBS (The One True Brace Style);
c) Toda indentação deve ser feita utilizando tabulação, ao invés de espaços.
d) Usar sempre “{“, mesmo quando não obrigatório;
e) Usar “{” a cada if, else, while, function, etc., mesmo que estes só tenham uma linha de instrução;
f) A “{” deve ficar na mesma linha da instrução anterior (início do comando);
g) Todos os fins de instrução devem terminar com ; (ponto e vírgula).

3.4- Outros aspectos sobre estilos:
a) Aspas simples ‘ (ou seja, apóstrofo) para strings com código JS; e aspas duplas ” para o código HTML;
b) Usar espaços em branco:

Situação Exemplo
após “;” que separa as partes de um loop for (var j = 0; j < 50; i += 1) {...}
na iniciação de múltiplas variáveis em um loop for (var j = 0, max = 50; j < max; j += 1) {...}
após as vírgulas que delimitam itens de array var a = [1, 2, 3];
após vírgulas em propriedades de objeto e após os sinais de “:” que dividam nomes de propriedades e seus valores var o = {a: 1, b: 2};
após argumentos de funções function myFunc(a, b, c)
antes das chaves em declarações de função function myFunc() {}
após Function em expressões de função anônima var myFunc = Function () {}
para separar todos os operadores e seus operandos colocar um espaço antes e depois de + , – , * , = , < , > , <= , >= , ===  e assim por diante.

3.5- Preferir:
a) i += 1; (em vez de i = i + 1 ou i++)
b) loops “for normais” com arrays e loops “for-in” com outros objetos;
c) prealocar propriedade “length” usada em loops
d) usar o método hasOwnProperty() quando for iterar propriedades de um objeto;

Links
1- Funções em JavaScript
2- Padrões de desenvolvimento do Javascript

1. Um recorte de código
Veja o recorte de código abaixo:


    var saveStrategy = new OpenLayers.Strategy.Save();   
    wfs = new OpenLayers.Layer.Vector("Editable Features", {
        strategies: [new OpenLayers.Strategy.BBOX(), saveStrategy],
        projection: new OpenLayers.Projection("EPSG:4326"),
        protocol: new OpenLayers.Protocol.WFS({
            version: "1.1.0",
            srsName: "EPSG:4326",
            url: "endereço-servidor:8080/geoserver/wfs/",
            featureNS :  "endereço-servidor/catalogteste",
            featureType: "escolas",
            geometryName: "the_geom",
            schema: "endereço-servidor:8080/geoserver/wfs/DescribeFeatureType?version=1.1.0&typename=teste:escolas"
        })
    });    
    mapa.addLayer(wfs);

a)Onde:

  • featureNS : {String} Feature namespace (opcional).
  • featureType : {String} feature typeName local (sem prefixo) (obrigatório).
  • geometryName: {String} Opcional, nome do atributo geometry. Se não fornecido, será utilizado o nome default que é ‘the_geom’.

b)Como identificar os parâmetros?
Basta acessar o Geoserver como administrador e verificar os parâmetros dos layers criados:

  • featureNS: no Geoserver –> Edit Workspace –> Namespace URI. O que será colocado em featureNS deve ser igual ao que temos neste campo do Geoserver.
  • geometryName: não fizemos citação porque utilizamos o nome default. Para verificar isto, no Geoserver –> Edit Layer (no caso o layer escolas) –> Feature Type Details (que está no final da página). São encontradas aí o nome dos campos do layer. Para a geometria, vemos que o que está no geoserver é “the_geom”.

c) Para entender um pouco mais

  • Strategy Class: vide post OpenLayers: compreendendo melhor um “Layer Vetorial”. Em síntese, a classe Strategy é utilizada com dois objetivos: construir a requisição e determinar o que fazer com os dados.
    • Strategy.Save: uma estratégia que “commit” as features recém-criadas ou modificadas. Por padrão, antes de persistir as mudanças a “strategy” espera por uma chamada de “salvar”. Ao configurar a estratégia com a opção “auto”, as mudanças podem ser salvas automaticamente.
    • Função Save: diz ao “protocol” para realizar o “commit” das features não salvas. Se a projeção do layer difere da projeção do mapa, as features serão transformadas na projeção do layer antes do “commit”.
  • Protocol Class: vide post OpenLayers: compreendendo melhor um “Layer Vetorial”. Em síntese, a classe Protocolo se refere a comunicação, ou seja, controla como o layer vetorial se comunica com o servidor de dados. Existem 2 (duas) subclasses: Protocol.HTTP e Protocol.WFS.

Observação: Para carregar/salvar dados de alguma fonte de dados externa ao script, necessita-se no mínimo definir as classes Protocol e Strategy.
Referências:
1- OpenLayers: utilizando o protocolo WFS para requisitar features de servidor remoto Geoserver
2- OpenLayers: compreendendo melhor um “Layer Vetorial”
3- OpenLayers.Strategy.Save

O primeiro desafio é saber especificar os parâmetros exigidos pela classe OpenLayers.Protocol.WFS para se fazer a requisição. Dois parâmetros são essenciais:

  • featureNS : {String} Feature namespace (opcional).
  • featureType : {String} feature typeName local (sem prefixo) (obrigatório).

Parâmetros adicionais:

  • geometryName: {String} Opcional, nome do atributo geometry. Se não fornecido, será utilizado o nome default que é ‘the_geom’.
  • outputFormat:  {String} Opcional, formato de saída para uso do WFS GetFeature requests. Pode ser de qualquer formato presente na WFS’s GetCapabilities response.

No nosso caso, utilizaremos a seguinte chamada:

var wfs = new OpenLayers.Layer.Vector("States", {
strategies: [new OpenLayers.Strategy.BBOX()],
protocol: new OpenLayers.Protocol.WFS({
version: "1.1.0",
url: "HTTP://endereço-servidor:8080/geoserver/wfs",
featureType: "ra31_df",
featureNS: "http://endereço-servidor/catalogcdes",
srsName: "EPSG:4326"
})
});

Como identificar os parâmetros? Basta acessar o Geoserver como administrador e verificar os parâmetros dos layers criados:
a)  featureNS: no Geoserver –> Edit Workspace –> Namespace URI. O que será colocado em featureNS deve ser igual ao que temos neste campo do Geoserver.
b) geometryName: não fizemos citação porque utilizamos o nome default. Para verificar isto, no Geoserver –> Edit Layer (no caso o layer ra31_df) –> Feature Type Details (que está no final da página). São encontradas aí o nome dos campos do layer. Para a geometria, vemos que o que está no geoserver é “the_geom”.

<!DOCTYPE html> 
<html lang='pt'> 
<head> 
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <script type='text/javascript' src='http://localhost/js/OpenLayers-2.12/OpenLayers.js'></script>
    <title>WFS</title>
    <script type='text/javascript'>
    var map;
    const HOSTNAME = "HTTP://nome-servidor:8080/geoserver/wms";
    //
    function init() {
        map = new OpenLayers.Map('map', {
            controls: [
                new OpenLayers.Control.PanZoom(),
                new OpenLayers.Control.MousePosition({}),
                new OpenLayers.Control.LayerSwitcher(),
                new OpenLayers.Control.Navigation()
            ]
        });
    //WMS Layer - baseLayer
    layer = new OpenLayers.Layer.WMS(
    'DF:RIDE',
    HOSTNAME,
    {layers: 'cdes:ride_df', transparent:true},{attribution:'Ride',visibility: true, 
      isBaseLayer:true,opacity:1,displayInLayerSwitcher:true}
    );
    //
    map.addLayer(layer);
    //
    map.setCenter(new OpenLayers.LonLat(-47.85,-15.80),10);
//
 var wfs = new OpenLayers.Layer.Vector("RAs", {
        strategies: [new OpenLayers.Strategy.BBOX()],
        protocol: new OpenLayers.Protocol.WFS({
            version: "1.1.0",
            url: "HTTP://nome-servidor:8080/geoserver/wfs",
            featureType: "ra31_df",
            featureNS: "http://nome-servidor/catalogcdes",
            srsName: "EPSG:4326"
        })
 }); 
 map.addLayer(wfs);
//
    }
    </script>
  </head>
  <body onload="init()">
<h1 id="title">WFS: Protocol.WFS</h1>
<p id="shortdesc">
    Demostra como usar o protocol WFS para buscar features em um servidor remoto e colocar estas informações
 em um layer vetorial tornando-as visiveis em um mapa.
</p>
<div id="map" style='width: 800px; height: 400px;'></div>
</body>
</html>

Referências:
OpenLayers: utilizando o protocolo WFS para salvar features no servidor remoto Geoserver

1. Introdução
Eventos são o coração do JavaScript. São impulsos que desencadeiam reações. Cada classe capaz de emitir eventos é responsável por gerenciar seus “listeners” (aquelas funções que devem ser notificadas quando um evento é disparado) e também por emitir eventos sob certas circunstâncias. Por exemplo, é possível disparar eventos para quando o zoom do mapa mudar, quando um layer for carregado ou quando uma feature for adicionada ao layer. Eventos tem o significado de “algo está acontecendo” e deve ter um tratamento.

Mas como funciona mesmo os eventos no OpenLayers?
Para explicar de uma maneira fácil, vamos exemplificar através do tipo de evento “zoomend” da classe OpenLayers.Map. Para este tipo de evento, todas as vezes que o mapa muda seu zoom, ele é reponsável por disparar (trigger) o evento do tipo “zoomend”, e daí todos seus listeners serão notificados deste novo evento.

Para ajudar em todos estes processos, o OpenLayers possui a classe OpenLayers.Event, que cuida dos “registering listeners” e simplifica a ação de acionamento do evento para todos estes listeners (obs: um evento pode acionar vários listeners). Simplificadamente, esta classe permite:

  • definir o evento;
  • registrar os listeners;
  • acionar os eventos para notificar-se todos os listeners.

Quando um evento acontece, o mecanismo de gerenciamento de eventos do OpenLayers sempre chama a função listener passando um parâmetro “event”. Este objeto passado contém as informações que foram escritas pela classe que disparou o evento, mais as seguintes 3 propriedades que são sempre adicionadas automaticamente:

  •  type: contém o tipo do evento (exemplos: move, zoomend, etc);
  • object: aponta para o objeto que disparou o evento;
  •  element: o elemento DOM relacionado ao evento.

A construção é a seguinte:

register: function ( type, obj, func_listener, priority )

2. Tipos de eventos e forma de utilização
Os eventos são o que conduz as interações no OpenLayers, que, como dito anteriormente, possui sua própria classe Openlayers.Event. No OpenLayers existem dois tipos de eventos utilizados:

  • Browser Events: por exemplo, clicking do mouse no mapa.
  • Map Events: por exemplo, zooming do mapa para alguma coordenada.

Neste post estaremos interessados em explorar este segundo tipo de evento, onde existem duas formas de se utilizar os “Map Events” (que ao final realizam a mesma coisa):

  • no instante da criação do mapa já se adicionar os eventos;
  • definir os eventos em um instante posterior àquele da criação do mapa.

a) no instante da criação do mapa já se adicionar os eventos.
Para isto utilizaremos a propriedade eventListeners, tanto na classe OpenLayers.Map como na classe OpenLayers.Layer. Esta é a forma apropriada de se utilizar um Map Event desde que já se saiba no instante da criação do objeto mapa quais os eventos que se deseja adicionar ao mapa.
Exemplo:


// Criar as funções que serão chamadas pelos eventListeners
function movimento_listener(e){alert(
                   'Tipo do evento= ' + e.type + 
                   '\nClasse que disparou o evento= ' + e.object.CLASS_NAME +
                   '\nElement DOM= ' + e.element);};
function layer_listener(e){alert(
                   'Tipo do evento= ' + e.type + 
                   '\nClasse que disparou o evento= ' + e.object.CLASS_NAME +
                   '\nElement DOM= ' + e.element);};                  
// Criar o objeto mapa informando a propriedade eventListener. Neste caso, dois listeners.
map = new OpenLayers.Map('map_id',{
      eventListeners:{
                      'moveend': movimento_listener,
                      'changelayer': layer_listener
}});

 
OBS:
i) A propriedade eventListener é estruturada através de par key:value (eventType:listenerFunction);
ii) Os objetos eventListeners serão registrados com a classe OpenLayers.Events.on.

b) definir os eventos em um instante posterior àquele da criação do mapa.
Para isto utilizaremos a propriedade “events”. Esta forma de se utilizar o Map Event tem a diferença em relação a primeira forma pelo fato que definimos o tipo de evento em um instante posterior àquele da criação do objeto mapa. Existem dois métodos construtivos, mas que proporcionam o mesmo resultado:

Método-1: utilizando o método “register”
Exemplo:


// As funções listeners são as mesmas do último exemplo acima. 
// Não foram replicadas aqui. 
// Adicionar os eventos ao mapa que fora criado anteriormente,
//  uma instrução por tipo de evento.
map_name.events.register('moveend',map_name,movimento_listener);
map_name.events.register('changelayer',map_name,layer_listener);

 

OBS: no campo da propriedade object poderiamos utilizar “null” ou “this”, e o OpenLayers assumiria o próprio escopo de execução da instância (“this”).

Método-2: utilizando o método “on”
É um conveniete método para registrar listeners dentro de um escopo comum. Internamente, este método chama o método “register”.


Exemplo:
  // As funções listeners são as mesmas do último exemplo acima. Não copiadas aqui. 
  // Adicionar os eventos ao mapa que fora criado anteriormente, todos os eventos de uma única vez.
map_name.events.on({
	'moveend':movimento_listener,
	'changelayer':layer_listener
});

 

3. Map event Types
Temos falado em tipos de eventos mais ainda não expomos quais os tipos de eventos suportados. Na verdade cada classe que tem a propriedade “events” define um array com os tipos de eventos possíveis para aquela classe. Por exemplo, os tipos de eventos suportados pela classe OpenLayers.Map versão 2.12 são:
EVENT_TYPES: [“preaddlayer”, “addlayer”, “preremovelayer”, “removelayer”, “changelayer”, “movestart”, “move”, “moveend”, “zoomend”, “mouseover”, “mouseout”, “mousemove”, “changebaselayer”]

Já para a classe OpenLayers.Layer versão 2.12 temos:
EVENT_TYPES: [“loadstart”, “loadend”, “visibilitychanged”, “move”, “moveend”, “added”, “removed”]

Para a classe OpenLayers.Layer.Vector versão 2.12, além dos tipos suportados para a classe OpenLayers.Layer, adicionalmente também suporta:
EVENT_TYPES: [“beforefeatureadded”, “beforefeaturesadded”, “featureadded”, “featuresadded”, “beforefeatureremoved”, “beforefeaturesremoved”, “featureremoved”, “featuresremoved”, “beforefeatureselected”, “featureselected”, “featureunselected”, “beforefeaturemodified”, “featuremodified”, “afterfeaturemodified”, “vertexmodified”, “vertexremoved”, “sketchstarted”, “sketchmodified”, “sketchcomplete”, “refresh”]

Uma forma outra forma prática de ver os eventos suportados pelas classes do OpenLayers é utilizando o Firebug executando comandos através de seu console:
– para a classe OpenLayers.Map: “map.events.listeners”;
– para a classe Openlayers.Layer: “nome_layer.events.BROWSER_EVENTS” (neste caso encontra-se os tipos de eventos “Browser Events”).
Obs: vale salientar que este array de tipos de eventos suportados pode mudar a cada versão de OpenLayers, o que trará impacto sobre os programas desenvolvidos.

Referências:
1- OpenLayers: API Documentation
2- OpenLayers Library Documentation
Veja Também:
3- Um pouco de OpenLayers, Geoserver e PostGis