Usando IndexedDB - APIs da Web

Usando IndexedDB
================

IndexedDB é uma forma de armazenar dados no navegador do usuário. Com ele você pode criar aplicações web com possibilidade de fazer query sem necessidade de conexão, suas aplicações podem funcionar tanto online quanto offline.

Sobre esse documento
--------------------

Esse tutorial utiliza a API assíncrona do IndexedDB. Se você não está familiarizado com IndexedDB, você pode ler Conceitos básicos sobre IndexedDB (en-US).

Para a documentação de referência, veja o artigo sobre API IndexedDB, pois nele contém os tipos de objetos utilizados no IndexedDB, como também métodos da API, tanto síncrona como assíncrona.

Padrão básico
-------------

O IndexedDB encoraja o uso do seguinte padrão:

1. Abrir um banco de dados.
2. Criar um ObjectStore ao atualizar o banco.
3. Iniciar uma transação e e faz um request para fazer alguma operação no banco, como adicionar ou obter dados.
4. Esperar a operação ser completada ouvindo algum evento DOM.
5. Fazer algo com o resultado da query (que pode ser obtida pelo objeto request).

OK, então, agora com esses conceitos em mente, nós podemos fazer coisas mais concretas.

Criando e estruturando o banco
------------------------------

Pelo fato da especificação ainda estar evoluindo, as implementações do IndexedDB tem prefixos de navegadores. Os navegadores podem ter implementações diferentes da API IndexedDB até a especificação ser consolidada. Mas uma vez que tudo chegar a um consenso, os navegadores tirarão seus prefixos. Atualmente, algumas implementações removeram o prefixo: Internet Explorer 10, Firefox 16, Chrome 24. Quando eles usam prefixo, navegadores baseados no Gecko usam o prefixo `moz`, enquanto os navegadores baseados no WebKit usam o prefixo `webkit`.

### Usando uma versão experimental do IndexedDB

Se você quiser testar seu código em navegadores que usam prefixo, você pode usar o código abaixo:

```js
// Na linha abaixo, você deve incluir os prefixos do navegador que você vai testar.
window.indexedDB =
  window.indexedDB ||
  window.mozIndexedDB ||
  window.webkitIndexedDB ||
  window.msIndexedDB;
// Não use "var indexedDB = ..." se você não está numa function.
// Posteriormente, você pode precisar de referências de algum objeto window.IDB\*:
window.IDBTransaction =
  window.IDBTransaction ||
  window.webkitIDBTransaction ||
  window.msIDBTransaction;
window.IDBKeyRange =
  window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
// (Mozilla nunca usou prefixo nesses objetos, então não precisamos window.mozIDB\*)
```

Tome cuidado, implementações prefixadas podem estar com bugs ou implementando especificações antigas. Portanto, não é recomendado usar em produção. É preferível não usar IndexedDB em navegadores antigos:

```js
if (!window.indexedDB) {
  window.alert(
    "Seu navegador não suporta uma versão estável do IndexedDB. Alguns recursos não estarão disponíveis.",
  );
}
```

### Abrindo um banco

Começamos todo o processo assim:

```js
// Abrindo o banco de dados
var request = window.indexedDB.open("DBteste", 3);
```

Abrir um banco é como qualquer outra operação — Você tem que "requerer (request)".

A requisição de abertura não abre o banco ou inicia a transação. A chamada da função `open()` retorna um objeto `IDBOpenDBRequest` (en-US) com o resultado (success) ou um erro que você terá que tratar. Muitas outras funções assíncronas no IndexedDB fazem a mesma coisa - retornam um objeto `IDBRequest` (en-US) com o resultado ou erro. O resultado para a função open é uma instância de `IDBDatabase.`

O segundo parâmetro para o método open é a versão do banco. A versão do banco determina seu schema — os registros no banco e sua estrutura. Se o banco não existe ainda, ele é criado pela operação `open`, então o evento `onupgradeneeded` é chamado e você cria o schema do banco nesse evento. Se o banco existe mas você está fornecendo um novo número de versão, o evento `onupgradeneeded` é chamado imediatamente, permitindo você tratar a atualização do schema. Para mais informações sobre isso veja Updating the version of the database.

**Aviso:** O número de versão é um `unsigned long long`, o que significa que ele pode ver um inteiro muito grande. Isso também significa que você não pode usar float, pois ele será convertido em um inteiro pequeno e a transação pode não acontecer, ou o evento `upgradeneeded` pode não ser chamado. Então se você usar 2.4 como versão:

```js
var request = indexedDB.open("DBteste", 2.4); // não faça isso, pois a versão será convertida para 2.
```

#### Gerenciando handlers

A primeira coisa que você vai querer fazer em quase todos os requests é tratar os casos de success e error:

```js
request.onerror = function (event) {
  // Fazer algo com request.errorCode!
};
request.onsuccess = function (event) {
  // Fazer algo com request.result!
};
```

Qual das duas funções, `onsuccess()` ou `onerror()`, será chamada? Se tudo correr bem, o evento de sucesso (que é um evento DOM event com propriedade `type` setada `"success"`) é chamado com `request` como seu `target`. Uma vez chamado, a função `onsuccess()` no `request` é chamada com o evento de sucesso em seu contexto. Por outro lado, se acontecer algum problema, um evento de erro (que é um evento DOM com a propriedade `type` setada para `"error"`) é chamado no `request`. Então a função `onerror()` com o evento erro em seu contexto.

A API IndexedDB é feita para minimizar a necessidade de manipular erros, então você não fará muitos eventos de erro (ao menos, se você usar a API!) No caso de abrir um banco, portanto, existe algumas condições comuns para eventos de erro. O problema mais comum é o usuário não dar permissão para criar o banco. Um dos principais objetivos do IndexedDB é permitir muitos dados serem armazenados para uso offline. (Para aprender mais sobre o quanto cada navegador pode armazenar, veja Storage limits).

Obviamente, navegadores não querem armazenar dados que poluem seu computador, então o navegador mostra uma mensagem ao usuário na primeira vez que um aplicativo tenta abrir o IndexedDB. O usuário pode escolher permitir ou negar acesso. O IndexedDB também é desabilitado no modo privado dos navegadores (ctrl+shift+N no Chrome e ctrl+shift+P no Firefox). Isso acontece porque o intuito do modo privado é não deixar rastros na navegação.

Agora, assumindo que o usuário aprovou seu request para criar o banco, e você recebeu success; Qual é o próximo passo? O request foi gerado com a chamada de `indexedDB.open()`, e `request.result` é uma instância de `IDBDatabase`, e você definitivamente vai querer armazenar isso para usar depois. Veja abaixo:

```js
var db;
var request = indexedDB.open("DBteste");
request.onerror = function (event) {
  alert("Você não habilitou minha web app para usar IndexedDB?!");
};
request.onsuccess = function (event) {
  db = request.result;
};
```

#### Tratando Erros

Como mencionado acima, o evento de erro é chamado quando o request dá erro. Se você quer evitar manipuladores de erro a cada request, você pode adicionar um único manipulador de erro no objeto db, como abaixo:

```js
db.onerror = function (event) {
  // Função genérica para tratar os erros de todos os requests desse banco!
  alert("Database error: " + event.target.errorCode);
};
```

Um dos erros mais comuns ao abrir o banco é `VER_ERR`. Ele indica que a versão do banco existente é maior que a versão que você quer abrir.

### Criando ou atualizando a versão do banco

Quando você cria um novo banco ou aumenta sua versão, o evento `onupgradeneeded` será chamado. No manipulador deste evento, você deve criar o objectStore necessário para a versão do banco:

```js
// Este evento é implementado somente em navegadores mais recentes
request.onupgradeneeded = function (event) {
  var db = event.target.result;

  // cria um objectStore para esse banco
  var objectStore = db.createObjectStore("nome", { keyPath: "minhaChave" });
};
```

Neste caso, o banco já terá objectStores de suas versões anteriores, então você não terá que criar esses objectStores de novo. Você somente precisará criar um novo objectStore qualquer, ou deletar objectStores da versão anterior que não serão utilizados. Se você precisa mudar um objectStore existente (mudar o `keyPath, por exemplo`), então você precisa deletar o objectStore antigo e criá-lo novamente com as novas opções. (Note que isso irá deletar a informação no objectStore! Se você precisa salvar a informação, você deve ler isso e salvá-lo em algum lugar antes de atualizar o banco.)

Blink/Webkit suporta a versão atual da especificação, nas versões do Chrome 23+ e Opera 17+; IE10+ também suporta. Outros motores e versões antigas não implementam a versão atual da especificação e não suportam a assinatura `indexedDB.open(name, version).onupgradeneeded` ainda. Para mais informação sobre como atualizar a versão do banco em Webkit/Blink, veja IDBDatabase reference article.

### Estruturando o banco

Agora a estrutura do banco. IndexedDB usa "armazens de objetos" em vez de tabelas, e um único banco de dados pode conter qualquer número de "armazem de objetos". Sempre que um valor é armazenado num objectStore, ele é associado a uma chave. Existe várias maneiras diferentes de uma chave ser mostrada, dependendo do que o objectStore usa, um key path ou key generator.

A tabela abaixo mostra as direfentes chaves suportadas:

| Key Path (`keyPath`) | Key Generator (`autoIncrement`) | Description |
| --- | --- | --- |
| Não | Não | Este objectStore pode ter qualquer tipo de valor primitivo como número ou string. Você deve suprir uma chave separada sempre que adicionar um novo valor. |
| Sim | Não | Este objectStore pode simplesmente armazenar objetos JavaScript. O objeto deve ter uma propriedade com o mesmo nome do key path. |
| Não | Sim | Este objectStore pode possuir qualquer tipo de valor. A chave é gerada para você, automaticamente, ou você pode suprir uma chave separada, caso utilize uma chave específica. |
| Sim | Sim | Este objectStore suporta somente objetos JavaScript. Normalmente uma chave é gerada e o valor dela é armazenado no objeto em uma propriedade com o mesmo nome da key path. Portanto, se a propriedade já existe, o valor dela será usado como chave, em vez do valor gerado. |

Você também pode criar índices em qualquer objectStore. Um indice deixa você olhar os valores armazenados no objectStore usando o valor de uma propriedade do objectStore, em vez de sua chave.

Adicionalmente, indices tem a habilidade de forçar restrições simples nos dados armazenados. Setando uma flag única quando criar o índice, reforça que dois objetos são armazenados contendo o mesmo valor para o key path do índice. Então, por exemplo, se você tem um objeto armazenado que tem um conjunto de pessoas, e você quer ter certeza que ninguém tera o mesmo e-mail, você pode usar um índice com flag única para forçar isso.

Isso soa meio confuso, mas este exemplo pode iluminar o conceito. Primeiro, vamos definir alguns dados a serem usados no exemplo:

```js
// Isso é o que os dados de nossos clientes será.
const DadosClientes = [
  { ssn: "444-44-4444", nome: "Bill", idade: 35, email: "bill@company.com" },
  { ssn: "555-55-5555", nome: "Donna", idade: 32, email: "donna@home.org" },
];
```

Agora vamos ver ccomo criar um IndexedDB para armazenar nossos dados:

```js
const dbName = "clientes";

var request = indexedDB.open(dbName, 2);

request.onerror = function (event) {
  // Tratar erros.
};
request.onupgradeneeded = function (event) {
  var db = event.target.result;

  // Cria um objectStore para conter a informação sobre nossos clientes. Nós vamos
  // usar "ssn" como key path porque sabemos que é único;
  var objectStore = db.createObjectStore("clientes", { keyPath: "ssn" });

  // Cria um índice para buscar clientes pelo nome. Podemos ter nomes
  // duplicados, então não podemos usar como índice único.
  objectStore.createIndex("nome", "nome", { unique: false });

  // Cria um índice para buscar clientes por email. Queremos ter certeza
  // que não teremos 2 clientes com o mesmo e-mail;
  objectStore.createIndex("email", "email", { unique: true });

  // Usando transação oncomplete para afirmar que a criação do objectStore
  // é terminada antes de adicionar algum dado nele.
  objectStore.transaction.oncomplete = function (event) {
    // Armazenando valores no novo objectStore.
    var clientesObjectStore = db
      .transaction("clientes", "readwrite")
      .objectStore("clientes");
    for (var i in DadosClientes) {
      clientesObjectStore.add(DadosClientes[i]);
    }
  };
};
```

Como falamos antes, `onupgradeneeded` é o único lugar onde você pode alterar a estrutura do banco. Nele você pode criar e deletar objectStores e construir ou remover índices.

Armazens de objetos são criados com uma única chamada de `createObjectStore()`. O método pega o nome do armazem e um objeto parâmetro. Mesmo que o objeto parâmetro seja opcional, ele é muito importante porque ele deixa você definir propriedades importantes e ajustar tipos de dados que você quer criar. No nosso caso, nós obtemos um objectStore chamado "clientes" e definimos um `keyPath`, que é a propriedade que faz um objeto individual ser único no banco. Essa propriedade, nesse exemplo, é "ssn", que simboliza o cpf (social security number), que é único. O "ssn" deve ser apresentado em cada objeto armazenado no `objectStore`.

Nós também criamos um índice chamado "nome" ligado à propriedade `nome`. Assim como o `createObjectStore()`, o `createIndex()` tem um parâmetro opcional `options` que cuida do tipo de índice que você quer criar. Adicionando objetos que não tem a propriedade `nome` terá sucesso, porém esses objetos não aparecerão no índice "nome".

Nós podemos obter os objetos de clientes armazenados usando os `ssn` da objectStore diretamente, ou usando os nomes usados no índice. Para aprender como isso é feito, veja a seção usando um índice.

### Usando um key generator

Setando uma flag `autoIncrement` ao criar o objectStore habilitará o key generator. Por padrão ele não é setado.

Com o key generator, a chave será gerada automaticamente quando você adicionar algum valor no objectStore. O atual número do key generator é sempre setado 1 quando a primeira key generator do objectStore é criada. Basicamente a próxima chave recebe o incremento de 1. O número atual da key generator nunca decresce, a não ser se alguma operação do banco for revertida, como numa transação abortada, por exemplo. No entanto, deletar um registro ou limpar todos os registros nunca afeta o key generator dos objectStores.

Nós podemos criar outro objectStore com o key generator como abaixo:

```js
// Abrindo o indexedDB.
var request = indexedDB.open(dbName, 3);

request.onupgradeneeded = function (event) {
  var db = event.target.result;

  // Criando outro objeto chamado "names" com o autoIncrement setado.
  var objStore = db.createObjectStore("names", { autoIncrement: true });

  // Porque "names" tem o the key generator, a chave para o nome é gerada automaticamente.
  // Os registros adicionados serão assim:
  // key : 1 => value : "Bill"
  // key : 2 => value : "Donna"
  for (var i in DadosClientes) {
    objStore.add(DadosClientes[i].nome);
  }
};
```

Para mais detalhes veja "W3C Key Generators".

Adicionando, obtendo e removendo dados
--------------------------------------

Antes de fazer qualquer coisa em um novo banco, você precisa iniciar uma transação. Transações estão no objeto database, e você tem que especificar qual objectStore você quer na transaction. Uma vez que você está dentro da transação, você pode acessar os objectStores com seus dados e fazer os requests. Depois, você precisa decidir se você vai fazer mudanças no banco ou se você simplesmente quer ler esses dados. Transações tem três modos disponíveis: `readonly`, `readwrite`, and `versionchange`.

Para mudar o "schema" ou estrutura do banco — o que envolve criar ou deletar objectStores ou índices — a transação deve ser em modo `versionchange`. Esta transação é aberta chamando o método `IDBFactory.open` (en-US) especificando a `version`. (Em navegadores com WebKit que não tem a ultima especificação implementada, o método `IDBFactory.open` (en-US) tem apenas um parâmetro, o `nome` do banco; então você deve chamar `IDBVersionChangeRequest.setVersion` para estabelecer uma transação `versionchange`.)

Para ler os registros de um objectStore existente, a transação pode ser tanto `readonly` quanto `readwrite`. Para fazer mudanças em um objectStore existente, a transação deve ser em modo `readwrite`. Você abre essas transações usando `IDBDatabase.transaction` (en-US). O método aceita dois parâmetros: o `storeNames` (o escopo, definido como um array de objectStores que você quer acessar) e o `modo` (`readonly` or `readwrite`) da transação. O método retorna o objeto detransação contendo o método `IDBIndex.objectStore` (en-US), que você pode usar para acessar seu objectStore. Por padrão, quando nenhum modo é especificado, a transação é aberta no modo `readonly`.

Você pode deixar o acesso aos dados mais rápido usando o escopo correto e o modo correto de transação. Aqui vai algumas dicas:

* Quando definir o escopo, especifique apenas os objectStores que você precisa. Desse jeito você pode rodar multiplas transações sem que uma sobreponha a outra.
* Somente especifique uma transação `readwrite` quando necessário. Você pode rodar várias transações `readonly` com escopos sobreposts, mas você pode ter somente uma transação `readwrite` por objectStore. Para aprender mais sobre *transactions (en-US)* veja Basic Concepts (en-US).
### Adicionando dados no banco

Se você acabou de criar o banco, então você provavelmente quer escrever algo nele. Veja abaixo:

```js
var transaction = db.transaction(["clientes"], "readwrite");
// Nota: Implementações mais antigas usam uma versão IDBTransaction.READ\_WRITE antiga em vez de "readwrite".
// Então, para suportar versões antigas, escreva:
// var transaction = db.transaction(["clientes"], IDBTransaction.READ\_WRITE);
```

A função `transaction()` tem dois argumentos (opcionais) e retorna um objeto de transação. O primeiro argumento é uma lista de objectStores que serão trabalhados na transação. Você pode passar um array vazio se você quer uma transação com todos os objectStores, mas não faça isso pois a especificação diz que um array vazio pode gerar um erro InvalidAccessError. Se você não especificar nada no segundo parâmetro, você tem uma transação read-only. Se você quer escrever no banco, use `"readwrite"`.

Agora que você tem uma transação, você precisa entender seu tempo de uso. Transações são amarradas a um evento. Se você faz uma transação fora de um evento, ela ficará inativa. A única maneira de manter uma transação ativa é fazer um request nela. Quando o request acabar você terá a oportunidade de extender a transação durante o callback. Se você tentar extender uma transação dentro de um evento, então ela tornará inativa. Se existir requests pendentes, a transação continua ativa. O tempo de vida de uma transação é realmente simples mas deve ser usada em um curto espaço de tempo. Outros exemplos poderão ajudá-lo. Se você começar a ver `TRANSACTION_INACTIVE_ERR` error então você está fazendo algo errado.

Transações podem receber eventos DOM de três tipos diferentes: `error`, `abort`, e `complete`. Nós falamos sobre o `error`, ou seja, a transação recebe um error sempre que o request gerar erro. Um ponto mais sutil é que o comportamento padrão de um erro é abortar a transação na qual ele estava. A menos que você manipule o erro chamando `preventDefault()` e fazendo algo depois, a transaçaõ inteira será desfeita. Este design força você a pensar sobre manipulação de erros, mas você pode sempre adicionar um manipulador de todos os erros se a manipulação separada estiver complicada. Se você não tratar o erro ou chamar `abort()` na transação, então a transação é desfeita (roll back) e o evento `abort` é chamado. Por outro lado, depois de todo request completado, você tem um evento `complete`. Se você fazer várias operações no banco, então seguir as operações de transações pode ser um caminho a seguir.

Agora que você tem uma transação, você precisará obter um objectStore dela. Transações somente deixam você obter um objectStore citado na transação. Então você pode adicionar os dados que precisa.

```js
// Faz algo após a inserção dos dados.
transaction.oncomplete = function (event) {
  alert("Pronto!");
};

transaction.onerror = function (event) {
  // Não esquecer de tratar os erros!
};

var objectStore = transaction.objectStore("clientes");
for (var i in DadosClientes) {
  var request = objectStore.add(DadosClientes[i]);
  request.onsuccess = function (event) {
    // event.target.result == DadosClientes[i].ssn;
  };
}
```

O `result` de um request gerado de uma chamada de `add()` é a chave do valor que foi adicionado. Então neste caso, ele deve ser igual ao valor do `ssn` do objeto que foi adicionado, desde que o objeto use o `ssn` como key path. Note que a função `add()` não deixa nenhum objeto ser adicionado com a mesma chave. Se você está tentando modificar um registro existente, você deve usar o `put()`, como explica a seção Updating an entry in the database.

### Removendo dados do banco

Para remoção o código é parecido:

```js
var request = db
  .transaction(["clientes"], "readwrite")
  .objectStore("clientes")
  .delete("444-44-4444");
request.onsuccess = function (event) {
  // Pronto!
};
```

### Obtendo dados do banco

Agora que o banco tem algumas informações nele, você pode obtê-las de diferentes maneiras. Primeiro, um `get()` simples. Você precisa informar a chave do valor a ser obtido:

```js
var transaction = db.transaction(["clientes"]);
var objectStore = transaction.objectStore("clientes");
var request = objectStore.get("444-44-4444");
request.onerror = function (event) {
  // Tratar erro!
};
request.onsuccess = function (event) {
  // Fazer algo com request.result!
  alert("O nome do SSN 444-44-4444 é " + request.result.name);
};
```

Veja agora de maneira resumida:

```js
db
  .transaction("clientes")
  .objectStore("clientes")
  .get("444-44-4444").onsuccess = function (event) {
  alert("O nome do SSN 444-44-4444 é " + request.result.name);
};
```

Viu como funciona? Desde que exista um objectStore, você pode evitar passar uma lista de objectStores que precisa na transação e passar apenas o nome como string. Você também pode ler do banco, apenas, então não precisará de uma transação `"readwrite"`. Chamando `transaction()` com nenhum modo especificado, você terá uma transação `"readonly"`. Outra consideração é que você não necessita salvar o request em uma variável. Desde que o evento DOM tenha o target que você precisará para obter a propriedade `result`.

**Nota:** Você pode deixar o acesso aos dados mais rápido limitando o escopo e o modo de transação. Veja algumas dicas:

* Quando definir o escopo, especifique somente os objectStores que vai precisar. Assim você pode rodar multiplas transações sem sopreposições.
* Utilize uma transação `readwrite` somente quando necessário. Você pode rodar várias transações `readonly` simultâneas, mas apenas uma transação `readwrite` por objectStore. Para aprender mais sobre isso veja o artigo *transactions* in the Basic Concepts (en-US).

### Atualizando um registro no banco

Agora que obtemos algum dado, atualizá-ls é inserí-los novamente no IndexedDB é bem simples. Vamos atualizar o exemplo anterior:

```js
var objectStore = db
  .transaction(["clientes"], "readwrite")
  .objectStore("clientes");
var request = objectStore.get("444-44-4444");
request.onerror = function (event) {
  // Tratar erro
};
request.onsuccess = function (event) {
  // Obter os valores antigos
  var data = request.result;

  // atualizar algum dado
  data.age = 42;

  // Atulizar esse dado no banco
  var requestUpdate = objectStore.put(data);
  requestUpdate.onerror = function (event) {
    // Tratar erro
  };
  requestUpdate.onsuccess = function (event) {
    // Sucesso na atualização \o/
  };
};
```

Criamos uma `objectStore` e obtemos um cliente dele, identificado pelo ssn (`444-44-4444`). Nós atualizamos o objeto, passando-o como parâmetro de um método put de outro request (`requestUpdate`) sobrescrevendo o valor antigo.

**Nota:** que neste caso nós temos que especificar a transação `readwrite` porque nós queremos escrever no banco, não somente ler os dados dele.

### Usando um cursor

Ao usar o método `get()` você precisa saber a chave do objeto que deseja obter. Se você quer passear entre todos os valores do seu objectStore, então você pode usar um cursor. Veja:

```js
var objectStore = db.transaction("cliente").objectStore("cliente");

objectStore.openCursor().onsuccess = function (event) {
  var cursor = event.target.result;
  if (cursor) {
    alert("O nome do SSN " + cursor.key + " é " + cursor.value.name);
    cursor.continue();
  } else {
    alert("Não existe mais registros!");
  }
};
```

`A função openCursor()` tem vários argumentos. Primeiro, você pode limitar o número de itens obtidos usando uma chave que veremos logo abaixo. Segundo, você pode especificar a direção que deseja iterar. No exemplo acima, nós estamos iterando em todos os objetos em ordem ascendente. O callback de sucesso para cursor é um pouco especial. O objeto cursor já é o `result` do request (acima nós usamos `event.target.result`). Então a chave atual e o valor pode ser encontrado na propriedade `key` e `value` do objeto cursor. Se você quer manter adiante, então você usa o método `continue()`. Quando você chegar ao fim dos dados (ou se não existem registros encontrados no `openCursor()`) você ainda tem um callback de sucesso, mas a propriedade `result` fica `undefined`.

Um padrão comum para cursores é obter todos os objetos em um objectStore e adicioná-los a um array como este:

```js
var clientes = [];

objectStore.openCursor().onsuccess = function (event) {
  var cursor = event.target.result;
  if (cursor) {
    clientes.push(cursor.value);
    cursor.continue();
  } else {
    alert("Todos os clientes: " + clientes);
  }
};
```

**Nota:** Note: Mozilla também implementou o método `getAll()` para ser usado nesse caso (e `getAllKeys()`, que está atualmente dentro da preferência do `dom.indexedDB.experimental` em about:config). Estes métodos não são parte do padrão IndexedDB, então eles podem desaparecer no futuro. Nós adicionamos porque achamos útil. O código abaixo faz o mesmo que o código acima:

```js
objectStore.getAll().onsuccess = function (event) {
  alert("Todos os clientes: " + event.target.result);
};
```

Existe um custo de performance associado com a propriedade `value` do cursor, porque o objeto é criado de forma lenta. Quando você usa `getAll()` por exemplo, Gecko deve criar todos os objetos de uma vez. Se você está somente interessado em cada chave, é muito melhor usar o cursor do que usar o `getAll()`. Se você está tentando obter um array de todos os objetos, então é melhor usar o `getAll()`.

### Usando um índice

Armazenar dados de um cliente usando o SSN como chave é lógico pois o SSN identifica o cliente de forma única. Se você precisa obter um cliente pelo seu nome, portanto, você precisará iterar todos os registros no banco e comparar os nomes até achar o que você procura. Buscar dessa maneira é algo lento, então criamos um índice.

```js
var index = objectStore.index("nome");
index.get("John").onsuccess = function (event) {
  alert("O SSN de John é " + event.target.result.ssn);
};
```

O cursor "nome" não é único, então pode existir mais de um registro com o `nome` igual a `"John"`. Neste caso você sempre obtem o registro com a chave de menor valor.

Se você precisa acessar todos os registros retornados, você pode usar um cursor. Você pode abrir dois tipos de cursores. Um cursor normal mapeia o índice ao objeto na objectStore. Uma cursor-chave mapeia o a propriedade índice à chave usada para armazenar o objeto. As diferenças são ilustradas abaixo:

```js
// Usando um cursor normal para obter todos os objetos
index.openCursor().onsuccess = function (event) {
  var cursor = event.target.result;
  if (cursor) {
    // cursor.key é um nome, como "Bill", e cursor.value é o objeto inteiro.
    alert(
      "Nome: " +
        cursor.key +
        ", SSN: " +
        cursor.value.ssn +
        ", email: " +
        cursor.value.email,
    );
    cursor.continue();
  }
};

// Usando um cursor-chave para obter todos os objetos
index.openKeyCursor().onsuccess = function (event) {
  var cursor = event.target.result;
  if (cursor) {
    // cursor.key é o nome, como "Bill", e cursor.value é o SSN (chave).
    // Não tem como obter o resto do objeto
    alert("Nome: " + cursor.key + ", SSN: " + cursor.value);
    cursor.continue();
  }
};
```

### Especificando o número e a direção dos cursores

Se você gostaria de limitar o número de valores retornados pelo cursor, você pode usar um objeto `IDBKeyRange` e passar isso como o primeiro argumento ao `openCursor()` ou `openKeyCursor()`. Você pode fazer um key range que permite um único valor, ou valores acima ou abaixo do especificado. O limite pode ser fechado (o key range inclui os valores dados) ou aberto (o key range não inclue os valores dados). Veja como funciona:

```js
// Somente se for igual "Donna"
var singleKeyRange = IDBKeyRange.only("Donna");

// Combinações menores que "Bill", incluindo "Bill"
var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");

// Combinações menores que "Bill", sem incluir "Bill"
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);

// Combinações maiores que Donna, não incluindo "Donna"
var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);

// Combinações entre "Bill" e "Donna", sem incluir "Donna"
var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);

// Para usar qualquer um desses key ranges, basta passar como primeiro parâmetro de openCursor()/openKeyCursor()
index.openCursor(boundKeyRange).onsuccess = function (event) {
  var cursor = event.target.result;
  if (cursor) {
    // Faz algo com o que encontrar
    cursor.continue();
  }
};
```

As vezes você pode querer iterar em ordem decrescente, em vez de crescente, alterando o segundo parâmetro de `openCursor()`:

```js
objectStore.openCursor(boundKeyRange, "prev").onsuccess = function (event) {
  var cursor = event.target.result;
  if (cursor) {
    // Prev indica ordem decrescente
    cursor.continue();
  }
};
```

Se você apenas quer especificar a ordem sem key range, é só passar null no primeiro parâmetro:

```js
objectStore.openCursor(null, "prev").onsuccess = function (event) {
  var cursor = event.target.result;
  if (cursor) {
    // Faça algo com os resultados.
    cursor.continue();
  }
};
```

Uma vez que o índice "nome" não é único, pode existir várias entradas onde o `nome` é o mesmo. Isso não acontece com objectStores porque a chave deve ser sempre única. Se você deseja filtrar valores duplicados numa iteração do cursor, você pode passar `nextunique` (ou `prevunique` se quiser decrescer) como parâmetro de direção. Quando `nextunique` ou `prevunique` é usado, o registro com menor chave é retornado.

```js
index.openKeyCursor(null, "nextunique").onsuccess = function (event) {
  var cursor = event.target.result;
  if (cursor) {
    // Faça algo com os registros.
    cursor.continue();
  }
};
```

Veja "IDBCursor Constants" para parâmetros válidos.

Mudança de versão quando a web app está aberta em outra aba
-----------------------------------------------------------

Quando sua web app muda a versão você precisa considerar o que vai acontecer se o usuário está na versão antiga em uma aba, e carrega a versão nova na outra. Quando você chamar o `open()` com a versão mais nova, um evento `onblocked` é chamado até que a aba da versão antiga seja fechada ou recarregada. Veja abaixo:

```js
var openReq = mozIndexedDB.open("DBteste", 2);

openReq.onblocked = function (event) {
  // Se existe outra aba com a versão antiga
  alert(
    "Existe uma versão antiga da web app aberta em outra aba, feche-a por favor!",
  );
};

openReq.onupgradeneeded = function (event) {
  // Se estiver tudo fechado, então faça as devidas alterações
  db.createObjectStore(/\* ... \*/);
  useDatabase(db);
};

openReq.onsuccess = function (event) {
  var db = event.target.result;
  useDatabase(db);
  return;
};

function useDatabase(db) {
  // Esteja certo de que adicionou um evento para notificar se a página muda a versão
  // Nós devemos fechar o banco. Isso permite à outra página ser atualizada
  // Se você não fizer isso a atualização não acontecerá até fechar as abas.
  db.onversionchange = function (event) {
    db.close();
    alert("Uma nova versão desta web app está pronta. Atualiza, por favor!");
  };

  // Fazer algo com os bancos
}
```

Segurança
---------

IndexedDB usa o princípio de mesma origem, o que significa que o banco só será acessado pelo site que o criou.

É importante notar que o IndexedDB não funciona para conteúdo carregado em um frame de outro site (seja `<frame>` (en-US) ou `<iframe>`. Esta é uma política de segurança e privacidade análoga ao bloqueio de cookies de terceiros. Para mais detalhes, veja Firefox bug 595307.

Alerta sobre fechar o navegador
-------------------------------

Quando o navegador é fechado, qualquer transação pendente no IndexedDB será abortada (silenciosamente) — ele não vai completar, nem chamar o evento de erro. Uma vez que o usuário pode sair do navegador, em qualquer momento, isto significa que você não pode confiar em qualquer transação específica para completar ou para saber que ela não foi concluída. Existem várias implicações nesse comportamento.

Primeiro, você deve ter o cuidado de sempre deixar seu banco de dados em um estado consistente, no final de cada transação. Por exemplo, suponha que você está usando IndexedDB para armazenar uma lista de itens que permitem ao usuário editar. Você salvar a lista após a edição, limpando o armazenamento de objetos e, em seguida, escrever a nova lista. Se você limpar o armazenamento de objetos em uma transação e escrever a nova lista em outra transação, há um perigo de que o navegador irá fechar após a limpeza de dados e antes da gravação, deixando-o com um banco de dados vazio. Para evitar isso, você deve combinar tanto a limpeza quanto a gravação em uma única transação.

Em segundo lugar, você nunca deve amarrar as operações de banco de dados ao evento unload. Se o evento unload é acionado pelo fechamento do navegador, todas as transações criadas no unload nunca serão concluídas. Uma abordagem intuitiva para manter algumas informações em sessões do navegador é lê-la a partir do banco de dados quando o navegador (ou uma determinada página) é aberta, atualizá-la assim que o usuário interagir com o navegador, e depois salvá-lo para o banco de dados quando o navegador (ou página) será fechada. No entanto, isso não vai funcionar. As transações de banco de dados será criado no unload, mas como elas são assíncronas, serão abortadas antes que eles possam executar.

De fato, não existe uma maneira de garantir que as transações no IndexedDBserão completadas, mesmo com o fechamento padrão do navegador. Ver Firefox bug 870645.

Exemplo de IndexedDB
--------------------

### HTML

```html
<script
 type="text/javascript"
 src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>

<h1>IndexedDB Demo: storing blobs, e-publication example</h1>
<div class="note">
  <p>Works and tested with:</p>
  <div id="compat"></div>
</div>

<div id="msg"></div>

<form id="register-form">
  <table>
    <tbody>
      <tr>
        <td>
          <label for="pub-title" class="required"> Title: </label>
        </td>
        <td>
          <input type="text" id="pub-title" name="pub-title" />
        </td>
      </tr>
      <tr>
        <td>
          <label for="pub-biblioid" class="required">
            Bibliographic ID:<br />
            <span class="note">(ISBN, ISSN, etc.)</span>
          </label>
        </td>
        <td>
          <input type="text" id="pub-biblioid" name="pub-biblioid" />
        </td>
      </tr>
      <tr>
        <td>
          <label for="pub-year"> Year: </label>
        </td>
        <td>
          <input type="number" id="pub-year" name="pub-year" />
        </td>
      </tr>
    </tbody>
    <tbody>
      <tr>
        <td>
          <label for="pub-file"> File image: </label>
        </td>
        <td>
          <input type="file" id="pub-file" />
        </td>
      </tr>
      <tr>
        <td>
          <label for="pub-file-url">
            Online-file image URL:<br />
            <span class="note">(same origin URL)</span>
          </label>
        </td>
        <td>
          <input type="text" id="pub-file-url" name="pub-file-url" />
        </td>
      </tr>
    </tbody>
  </table>

  <div class="button-pane">
    <input type="button" id="add-button" value="Add Publication" />
    <input type="reset" id="register-form-reset" />
  </div>
</form>

<form id="delete-form">
  <table>
    <tbody>
      <tr>
        <td>
          <label for="pub-biblioid-to-delete">
            Bibliographic ID:<br />
            <span class="note">(ISBN, ISSN, etc.)</span>
          </label>
        </td>
        <td>
          <input
 type="text"
 id="pub-biblioid-to-delete"
 name="pub-biblioid-to-delete" />
        </td>
      </tr>
      <tr>
        <td>
          <label for="key-to-delete">
            Key:<br />
            <span class="note">(for example 1, 2, 3, etc.)</span>
          </label>
        </td>
        <td>
          <input type="text" id="key-to-delete" name="key-to-delete" />
        </td>
      </tr>
    </tbody>
  </table>
  <div class="button-pane">
    <input type="button" id="delete-button" value="Delete Publication" />
    <input
 type="button"
 id="clear-store-button"
 value="Clear the whole store"
 class="destructive" />
  </div>
</form>

<form id="search-form">
  <div class="button-pane">
    <input
 type="button"
 id="search-list-button"
 value="List database content" />
  </div>
</form>

<div>
  <div id="pub-msg"></div>
  <div id="pub-viewer"></div>
  <ul id="pub-list"></ul>
</div>
```

### CSS Content

```css
body {
  font-size: 0.8em;
  font-family: Sans-Serif;
}

form {
  background-color: #cccccc;
  border-radius: 0.3em;
  display: inline-block;
  margin-bottom: 0.5em;
  padding: 1em;
}

table {
  border-collapse: collapse;
}

input {
  padding: 0.3em;
  border-color: #cccccc;
  border-radius: 0.3em;
}

.required:after {
  content: "\*";
  color: red;
}

.button-pane {
  margin-top: 1em;
}

#pub-viewer {
  float: right;
  width: 48%;
  height: 20em;
  border: solid #d092ff 0.1em;
}
#pub-viewer iframe {
  width: 100%;
  height: 100%;
}

#pub-list {
  width: 46%;
  background-color: #eeeeee;
  border-radius: 0.3em;
}
#pub-list li {
  padding-top: 0.5em;
  padding-bottom: 0.5em;
  padding-right: 0.5em;
}

#msg {
  margin-bottom: 1em;
}

.action-success {
  padding: 0.5em;
  color: #00d21e;
  background-color: #eeeeee;
  border-radius: 0.2em;
}

.action-failure {
  padding: 0.5em;
  color: #ff1408;
  background-color: #eeeeee;
  border-radius: 0.2em;
}

.note {
  font-size: smaller;
}

.destructive {
  background-color: orange;
}
.destructive:hover {
  background-color: #ff8000;
}
.destructive:active {
  background-color: red;
}
```

### JavaScript Content

```js
(function () {
  var COMPAT\_ENVS = [
    ["Firefox", ">= 16.0"],
    [
      "Google Chrome",
      ">= 24.0 (you may need to get Google Chrome Canary), NO Blob storage support",
    ],
  ];
  var compat = $("#compat");
  compat.empty();
  compat.append('<ul id="compat-list"></ul>');
  COMPAT\_ENVS.forEach(function (val, idx, array) {
    $("#compat-list").append("<li>" + val[0] + ": " + val[1] + "</li>");
  });

  const DB\_NAME = "mdn-demo-indexeddb-epublications";
  const DB\_VERSION = 1; // Use a long long for this value (don't use a float)
  const DB\_STORE\_NAME = "publications";

  var db;

  // Used to keep track of which view is displayed to avoid uselessly reloading it
  var current_view_pub_key;

  function openDb() {
    console.log("openDb ...");
    var req = indexedDB.open(DB\_NAME, DB\_VERSION);
    req.onsuccess = function (evt) {
      // Better use "this" than "req" to get the result to avoid problems with
      // garbage collection.
      // db = req.result;
      db = this.result;
      console.log("openDb DONE");
    };
    req.onerror = function (evt) {
      console.error("openDb:", evt.target.errorCode);
    };

    req.onupgradeneeded = function (evt) {
      console.log("openDb.onupgradeneeded");
      var store = evt.currentTarget.result.createObjectStore(DB\_STORE\_NAME, {
        keyPath: "id",
        autoIncrement: true,
      });

      store.createIndex("biblioid", "biblioid", { unique: true });
      store.createIndex("title", "title", { unique: false });
      store.createIndex("year", "year", { unique: false });
    };
  }

  /\*\*
 \* @param {string} store\_name
 \* @param {string} mode either "readonly" or "readwrite"
 \*/
  function getObjectStore(store\_name, mode) {
    var tx = db.transaction(store_name, mode);
    return tx.objectStore(store_name);
  }

  function clearObjectStore(store\_name) {
    var store = getObjectStore(DB\_STORE\_NAME, "readwrite");
    var req = store.clear();
    req.onsuccess = function (evt) {
      displayActionSuccess("Store cleared");
      displayPubList(store);
    };
    req.onerror = function (evt) {
      console.error("clearObjectStore:", evt.target.errorCode);
      displayActionFailure(this.error);
    };
  }

  function getBlob(key, store, success\_callback) {
    var req = store.get(key);
    req.onsuccess = function (evt) {
      var value = evt.target.result;
      if (value) success\_callback(value.blob);
    };
  }

  /\*\*
 \* @param {IDBObjectStore=} store
 \*/
  function displayPubList(store) {
    console.log("displayPubList");

    if (typeof store == "undefined")
      store = getObjectStore(DB\_STORE\_NAME, "readonly");

    var pub_msg = $("#pub-msg");
    pub_msg.empty();
    var pub_list = $("#pub-list");
    pub_list.empty();
    // Resetting the iframe so that it doesn't display previous content
    newViewerFrame();

    var req;
    req = store.count();
    // Requests are executed in the order in which they were made against the
    // transaction, and their results are returned in the same order.
    // Thus the count text below will be displayed before the actual pub list
    // (not that it is algorithmically important in this case).
    req.onsuccess = function (evt) {
      pub_msg.append(
        "<p>There are <strong>" +
          evt.target.result +
          "</strong> record(s) in the object store.</p>",
      );
    };
    req.onerror = function (evt) {
      console.error("add error", this.error);
      displayActionFailure(this.error);
    };

    var i = 0;
    req = store.openCursor();
    req.onsuccess = function (evt) {
      var cursor = evt.target.result;

      // If the cursor is pointing at something, ask for the data
      if (cursor) {
        console.log("displayPubList cursor:", cursor);
        req = store.get(cursor.key);
        req.onsuccess = function (evt) {
          var value = evt.target.result;
          var list_item = $(
            "<li>" +
              "[" +
              cursor.key +
              "] " +
              "(biblioid: " +
              value.biblioid +
              ") " +
              value.title +
              "</li>",
          );
          if (value.year != null) list_item.append(" - " + value.year);

          if (
            value.hasOwnProperty("blob") &&
            typeof value.blob != "undefined"
          ) {
            var link = $('<a href="' + cursor.key + '">File</a>');
            link.on("click", function () {
              return false;
            });
            link.on("mouseenter", function (evt) {
              setInViewer(evt.target.getAttribute("href"));
            });
            list_item.append(" / ");
            list_item.append(link);
          } else {
            list_item.append(" / No attached file");
          }
          pub_list.append(list_item);
        };

        // Move on to the next object in store
        cursor.continue();

        // This counter serves only to create distinct ids
        i++;
      } else {
        console.log("No more entries");
      }
    };
  }

  function newViewerFrame() {
    var viewer = $("#pub-viewer");
    viewer.empty();
    var iframe = $("<iframe />");
    viewer.append(iframe);
    return iframe;
  }

  function setInViewer(key) {
    console.log("setInViewer:", arguments);
    key = Number(key);
    if (key == current_view_pub_key) return;

    current_view_pub_key = key;

    var store = getObjectStore(DB\_STORE\_NAME, "readonly");
    getBlob(key, store, function (blob) {
      console.log("setInViewer blob:", blob);
      var iframe = newViewerFrame();

      // It is not possible to set a direct link to the
      // blob to provide a mean to directly download it.
      if (blob.type == "text/html") {
        var reader = new FileReader();
        reader.onload = function (evt) {
          var html = evt.target.result;
          iframe.load(function () {
            $(this).contents().find("html").html(html);
          });
        };
        reader.readAsText(blob);
      } else if (blob.type.indexOf("image/") == 0) {
        iframe.load(function () {
          var img_id = "image-" + key;
          var img = $('<img id="' + img_id + '"/>');
          $(this).contents().find("body").html(img);
          var obj_url = window.URL.createObjectURL(blob);
          $(this)
            .contents()
            .find("#" + img_id)
            .attr("src", obj_url);
          window.URL.revokeObjectURL(obj_url);
        });
      } else if (blob.type == "application/pdf") {
        $("\*").css("cursor", "wait");
        var obj_url = window.URL.createObjectURL(blob);
        iframe.load(function () {
          $("\*").css("cursor", "auto");
        });
        iframe.attr("src", obj_url);
        window.URL.revokeObjectURL(obj_url);
      } else {
        iframe.load(function () {
          $(this).contents().find("body").html("No view available");
        });
      }
    });
  }

  /\*\*
 \* @param {string} biblioid
 \* @param {string} title
 \* @param {number} year
 \* @param {string} url the URL of the image to download and store in the local
 \* IndexedDB database. The resource behind this URL is subjected to the
 \* "Same origin policy", thus for this method to work, the URL must come from
 \* the same origin as the web site/app this code is deployed on.
 \*/
  function addPublicationFromUrl(biblioid, title, year, url) {
    console.log("addPublicationFromUrl:", arguments);

    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    // Setting the wanted responseType to "blob"
    // https://www.w3.org/TR/XMLHttpRequest2/#the-response-attribute
    xhr.responseType = "blob";
    xhr.onload = function (evt) {
      if (xhr.status == 200) {
        console.log("Blob retrieved");
        var blob = xhr.response;
        console.log("Blob:", blob);
        addPublication(biblioid, title, year, blob);
      } else {
        console.error(
          "addPublicationFromUrl error:",
          xhr.responseText,
          xhr.status,
        );
      }
    };
    xhr.send();

    // We can't use jQuery here because as of jQuery 1.8.3 the new "blob"
    // responseType is not handled.
    // http://bugs.jquery.com/ticket/11461
    // http://bugs.jquery.com/ticket/7248
    // $.ajax({
    // url: url,
    // type: 'GET',
    // xhrFields: { responseType: 'blob' },
    // success: function(data, textStatus, jqXHR) {
    // console.log("Blob retrieved");
    // console.log("Blob:", data);
    // // addPublication(biblioid, title, year, data);
    // },
    // error: function(jqXHR, textStatus, errorThrown) {
    // console.error(errorThrown);
    // displayActionFailure("Error during blob retrieval");
    // }
    // });
  }

  /\*\*
 \* @param {string} biblioid
 \* @param {string} title
 \* @param {number} year
 \* @param {Blob=} blob
 \*/
  function addPublication(biblioid, title, year, blob) {
    console.log("addPublication arguments:", arguments);
    var obj = { biblioid: biblioid, title: title, year: year };
    if (typeof blob != "undefined") obj.blob = blob;

    var store = getObjectStore(DB\_STORE\_NAME, "readwrite");
    var req;
    try {
      req = store.add(obj);
    } catch (e) {
      if (e.name == "DataCloneError")
        displayActionFailure(
          "This engine doesn't know how to clone a Blob, " + "use Firefox",
        );
      throw e;
    }
    req.onsuccess = function (evt) {
      console.log("Insertion in DB successful");
      displayActionSuccess();
      displayPubList(store);
    };
    req.onerror = function () {
      console.error("addPublication error", this.error);
      displayActionFailure(this.error);
    };
  }

  /\*\*
 \* @param {string} biblioid
 \*/
  function deletePublicationFromBib(biblioid) {
    console.log("deletePublication:", arguments);
    var store = getObjectStore(DB\_STORE\_NAME, "readwrite");
    var req = store.index("biblioid");
    req.get(biblioid).onsuccess = function (evt) {
      if (typeof evt.target.result == "undefined") {
        displayActionFailure("No matching record found");
        return;
      }
      deletePublication(evt.target.result.id, store);
    };
    req.onerror = function (evt) {
      console.error("deletePublicationFromBib:", evt.target.errorCode);
    };
  }

  /\*\*
 \* @param {number} key
 \* @param {IDBObjectStore=} store
 \*/
  function deletePublication(key, store) {
    console.log("deletePublication:", arguments);

    if (typeof store == "undefined")
      store = getObjectStore(DB\_STORE\_NAME, "readwrite");

    // As per spec https://www.w3.org/TR/IndexedDB/#object-store-deletion-operation
    // the result of the Object Store Deletion Operation algorithm is
    // undefined, so it's not possible to know if some records were actually
    // deleted by looking at the request result.
    var req = store.get(key);
    req.onsuccess = function (evt) {
      var record = evt.target.result;
      console.log("record:", record);
      if (typeof record == "undefined") {
        displayActionFailure("No matching record found");
        return;
      }
      // Warning: The exact same key used for creation needs to be passed for
      // the deletion. If the key was a Number for creation, then it needs to
      // be a Number for deletion.
      req = store.delete(key);
      req.onsuccess = function (evt) {
        console.log("evt:", evt);
        console.log("evt.target:", evt.target);
        console.log("evt.target.result:", evt.target.result);
        console.log("delete successful");
        displayActionSuccess("Deletion successful");
        displayPubList(store);
      };
      req.onerror = function (evt) {
        console.error("deletePublication:", evt.target.errorCode);
      };
    };
    req.onerror = function (evt) {
      console.error("deletePublication:", evt.target.errorCode);
    };
  }

  function displayActionSuccess(msg) {
    msg = typeof msg != "undefined" ? "Success: " + msg : "Success";
    $("#msg").html('<span class="action-success">' + msg + "</span>");
  }
  function displayActionFailure(msg) {
    msg = typeof msg != "undefined" ? "Failure: " + msg : "Failure";
    $("#msg").html('<span class="action-failure">' + msg + "</span>");
  }
  function resetActionStatus() {
    console.log("resetActionStatus ...");
    $("#msg").empty();
    console.log("resetActionStatus DONE");
  }

  function addEventListeners() {
    console.log("addEventListeners");

    $("#register-form-reset").click(function (evt) {
      resetActionStatus();
    });

    $("#add-button").click(function (evt) {
      console.log("add ...");
      var title = $("#pub-title").val();
      var biblioid = $("#pub-biblioid").val();
      if (!title || !biblioid) {
        displayActionFailure("Required field(s) missing");
        return;
      }
      var year = $("#pub-year").val();
      if (year != "") {
        // Better use Number.isInteger if the engine has EcmaScript 6
        if (isNaN(year)) {
          displayActionFailure("Invalid year");
          return;
        }
        year = Number(year);
      } else {
        year = null;
      }

      var file_input = $("#pub-file");
      var selected_file = file_input.get(0).files[0];
      console.log("selected\_file:", selected_file);
      // Keeping a reference on how to reset the file input in the UI once we
      // have its value, but instead of doing that we rather use a "reset" type
      // input in the HTML form.
      //file\_input.val(null);
      var file_url = $("#pub-file-url").val();
      if (selected_file) {
        addPublication(biblioid, title, year, selected_file);
      } else if (file_url) {
        addPublicationFromUrl(biblioid, title, year, file_url);
      } else {
        addPublication(biblioid, title, year);
      }
    });

    $("#delete-button").click(function (evt) {
      console.log("delete ...");
      var biblioid = $("#pub-biblioid-to-delete").val();
      var key = $("#key-to-delete").val();

      if (biblioid != "") {
        deletePublicationFromBib(biblioid);
      } else if (key != "") {
        // Better use Number.isInteger if the engine has EcmaScript 6
        if (key == "" || isNaN(key)) {
          displayActionFailure("Invalid key");
          return;
        }
        key = Number(key);
        deletePublication(key);
      }
    });

    $("#clear-store-button").click(function (evt) {
      clearObjectStore();
    });

    var search_button = $("#search-list-button");
    search_button.click(function (evt) {
      displayPubList();
    });
  }

  openDb();
  addEventListeners();
})(); // Immediately-Invoked Function Expression (IIFE)
```

Test the online live demoVer também
----------

Uma leitura adicional para você encontrar mais informações.

### Refências

* IndexedDB API Reference
* Indexed Database API Specification
* Using IndexedDB in chrome
* Using JavaScript generators in Firefox
* IndexedDB interface files in the Firefox source code
### Guias e tutoriais

* Databinding UI Elements with IndexedDB
* IndexedDB — The Store in Your Browser


AnimationEvent.elapsedTime - APIs da Web

AnimationEvent.elapsedTime
==========================

**Experimental:** **Esta é uma tecnologia experimental (en-US)**
Verifique a tabela de compatibilidade entre Navegadores cuidadosamente antes de usar essa funcionalidade em produção.

Sumário
-------

O **`AnimationEvent.elapsedTime`** é uma propriedade só de leitura com um ponto flutuante dando a quantidade de tempo que animação esteve rodando, em segundos, quando o evento termina, excluirá o tempo em que ela esteve em pausa. Para um evento `"animationstart"`, `elapsedTime` é `0.0` a não ser que tenha um valor negativo para `animation-delay`, nesse caso o evento irá terminar com `elapsedTime` contendo `(-1 * delay)`.

Síntaxe
-------
```

time = AnimationEvent.elapsedTime
```

Especificações
--------------

| Specification |
| --- |
| CSS Animations Level 1 # dom-animationevent-elapsedtime |

Compatibilidade com navegadores
-------------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Veja também
-----------

* Usando animações CSS.
* Animation-related CSS properties and at-rules: `animation`, `animation-delay`, `animation-direction` (en-US), `animation-duration` (en-US), `animation-fill-mode` (en-US), `animation-iteration-count` (en-US), `animation-name` (en-US), `animation-play-state` (en-US), `animation-timing-function` (en-US), `@keyframes` (en-US).
* The `AnimationEvent` interface it belongs to.


AnimationEvent.pseudoElement - APIs da Web

AnimationEvent.pseudoElement
============================

**Experimental:** **Esta é uma tecnologia experimental (en-US)**
Verifique a tabela de compatibilidade entre Navegadores cuidadosamente antes de usar essa funcionalidade em produção.

Sumário
-------

O **`AnimationEvent.pseudoElement`** é uma propriedade só de leitura do `DOMString`, começando com `'::'`, contendo o nome do pseudo-element em que a animação roda. Se a animação não roda em um pseudo-elemento mas em um elemento, então temos uma *string* vazia : `''`.

Síntaxe
-------
```

name = AnimationEvent.pseudoElement
```

Especificações
--------------

| Specification |
| --- |
| CSS Animations Level 1 # dom-animationevent-pseudoelement |

Compatibilidade com navegadores
-------------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* Using CSS animations
* `animation`, `animation-delay`, `animation-direction` (en-US), `animation-duration` (en-US), `animation-fill-mode` (en-US), `animation-iteration-count` (en-US), `animation-name` (en-US), `animation-play-state` (en-US), `animation-timing-function` (en-US), `@keyframes` (en-US).
* The `AnimationEvent` interface it belongs to.


AnimationEvent.animationName - APIs da Web

AnimationEvent.animationName
============================

`**Esta é uma tecnologia experimental (en-US)**
Verifique a tabela de compatibilidade entre Navegadores cuidadosamente antes de usar essa funcionalidade em produção.`
O `AnimationEvent.animationName` é uma propriedade só de leitura do`DOMString` contendo o valor de `animation-name` (en-US) propriedade CSS relacionada com a trasição.

Síntaxe
-------
```

name = AnimationEvent.animationName
```

Especificações
--------------

| Specification |
| --- |
| CSS Animations Level 1 # dom-animationevent-animationname |

Compatibilidade com navegadores
-------------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* Usando animações CSS
* `animation`, `animation-delay`, `animation-direction` (en-US), `animation-duration` (en-US), `animation-fill-mode` (en-US), `animation-iteration-count` (en-US), `animation-name` (en-US), `animation-play-state` (en-US), `animation-timing-function` (en-US), `@keyframes` (en-US).
* The `AnimationEvent` interface it belongs to.


AnimationEvent() - APIs da Web

AnimationEvent()
================

**Experimental:** **Esta é uma tecnologia experimental (en-US)**
Verifique a tabela de compatibilidade entre Navegadores cuidadosamente antes de usar essa funcionalidade em produção.

`O construtor AnimationEvent()`retorna o recente criado `AnimationEvent`, representando um evento em relação a animação.

Síntaxe
-------
```

animationEvent = new AnimationEvent(type, {animationName: aPropertyName,
                                           elapsedTime  : aFloat,
                                           pseudoElement: aPseudoElementName});
```

### Argumentos

*O construtor `AnimationEvent()`* *também herda argumentos do `Event()`.*

`type`

Um `DOMString` representando o nome do tipo de `AnimationEvent`. É caso sensitivo e pode ser: `'animationstart'`, `'animationend'`, ou `'animationiteration'`.

`animationName` Optional

Um`DOMString` contendo o valor do `animation-name` (en-US) propriedade CSS associada com a transição. É por padrão `""`.

`elapsedTime` Optional

Um ponto flutuante dando a quantidade de tempo que a animação esteve rodando, em segundos, quando o evento termina, excluindo qualquer tempo que ela passou pausada. Para um evento `"animationstart"`, `elapsedTime` é `0.0` a não ser que haja um valor negativo para `animation-delay`, nesse caso o evento terminará com `elapsedTime contendo` `(-1 *`*delay*`)`. É por padrão `0.0`.

`pseudoElement` Optional

É um ``DOMString``, começando com`"::"`, contendo o nome do pseudo-element que a animação roda. Se a animação não roda em um pseudo-elementomas em um elemento, então temos um *string* vazio `"" .`É por padrão `""`.

Especificações
--------------

| Specification |
| --- |
| CSS Animations Level 1 # dom-animationevent-animationevent |

Compatibilidade com navegadores
-------------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Veja também
-----------

* Usando animações CSS.
* Tópicos relacionados a animação CSS, propriedades e regras: `animation`, `animation-delay`, `animation-direction` (en-US), `animation-duration` (en-US), `animation-fill-mode` (en-US), `animation-iteration-count` (en-US), `animation-name` (en-US), `animation-play-state` (en-US), `animation-timing-function` (en-US), `@keyframes` (en-US)
* The `AnimationEvent` interface it belongs to.


Blob.size - APIs da Web

Blob.size
=========

A propriedade **`Blob.size`** retorna o tamanho em bytes de `Blob` ou um `File`.

Syntaxe
-------
```

var sizeInBytes = blob.size
```

### Valor

Um número.

Exempl0
-------

```js
// fileInput é um HTMLInputElement: <input type="file" multiple id="myfileinput">
var fileInput = document.getElementById("myfileinput");

// files é um objeto FileList (similiar ao NodeList)
var files = fileInput.files;

for (var i = 0; i < files.length; i++) {
  console.log(files[i].name + " has a size of " + files[i].size + " Bytes");
}
```

Especificações
--------------

| Specification |
| --- |
| File API # dfn-size |

Compatibilidade com navegadores
-------------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Veja também
-----------

* `Blob`


Blob - APIs da Web

Blob
====

Blob()

Retorna um novo objeto Blob criado, cujo o conteúdo consiste na concatenação de um array de valores estabelecidos no parâmetro da função.

Syntaxe
-------
```

var aBlob = new Blob( array[, options]);
```

### Parâmetros

* *array* é um `Array` de objetos `ArrayBuffer`, `ArrayBufferView` (en-US), `Blob`, `DOMString`, ou uma combinação de quaisquer um destes objetos, que serão colocados dentro do `Blob`. DOMStrings são codificadas como UTF-8.
* *options* é um dicionário opcional `BlobPropertyBag` que pode especificar os dois atributos seguintes:
    + `type`, com um valor padrão de `""`, que representa o MIME type do conteúdo do array que será inserido no blob.
    + `endings`, com um valor padrão de `"transparent"`, especifica como strings contendo o caracter de final de linha `\n` deverão ser escritos. Tem um dos dois valores: `"native"`, indicando que caracteres de final de linha são modificados para coincidir com a convenção do Sistema Operacional, ou `"transparent"`, indicando que finais são armazenados no blob sem quaisquer modificações.
    Non-standard
Exemplo
-------

```js
var aFileParts = ['<a id="a"><b id="b">hey!</b></a>']; // an array consisting of a single DOMString
var oMyBlob = new Blob(aFileParts, { type: "text/html" }); // the blob
```

Especificação
-------------

| Specification |
| --- |
| File API # constructorBlob |

Compatibilidade com navegadores
-------------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Veja também
-----------

* A versão descontinuada que o construtor `BlobBuilder` (en-US) substitui.


Blob.slice() - APIs da Web

Blob.slice()
============

O método **`Blob.slice()`** é usado para criar um novo `Blob` object contendo os dados no intervalo especificado de bytes da fonte `Blob`.

**Nota:** Nota: Esteja ciente de que o método `slice ()` tem prefixos de fornecedores em alguns navegadores e versões: `blob.mozSlice ()` para Firefox 12 e anteriores e `blob.webkitSlice ()` no Safari. Uma versão antiga do método `slice ()`, sem prefixos de fornecedor, tinha uma semântica diferente e é obsoleta

Sintaxe
-------
```

let blob = instanceOfBlob.slice([start [, end [, contentType]]]);
```

### Parâmetros

`start` Optional

An index into the `Blob` indicating the first byte to include in the new `Blob`. If you specify a negative value, it's treated as an offset from the end of the string toward the beginning. For example, -10 would be the 10th from last byte in the `Blob`. The default value is 0. If you specify a value for `start` that is larger than the size of the source `Blob`, the returned `Blob` has size 0 and contains no data.

`end` Optional

An index into the `Blob` indicating the first byte that will \*not\* be included in the new `Blob` (i.e. the byte exactly at this index is not included). If you specify a negative value, it's treated as an offset from the end of the string toward the beginning. For example, -10 would be the 10th from last byte in the `Blob`. The default value is `size`.

`contentType` Optional

The content type to assign to the new `Blob`; this will be the value of its `type` property. The default value is an empty string.

### Return value

A new `Blob` object containing the specified data from the source `Blob`.

Specifications
--------------

| Specification |
| --- |
| File API # dfn-slice |

Compatibilidade com navegadores
-------------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `Blob`
* `BlobBuilder` (en-US)


Blob.type - APIs da Web

Blob.type
=========

A propriedade **`type`** de um objeto `Blob` fornece MIME type (en-US) do arquivo. Ela retorna uma string vazia se o tipo não puder ser determinado.

Syntaxe
-------
```

var mimetype = instanceOfFile.type
```

Valor
-----

Uma string

Exemplo
-------

```js
var i, fileInput, files, allowedFileTypes;

// fileInput é um HTMLInputElement: <input type="file" multiple id="myfileinput">
fileInput = document.getElementById("myfileinput");

// files é um objeto FileList (similiar ao NodeList)
files = fileInput.files;

// nossa aplicação permite apenas imagens dos tipos \*.png, \*.jpeg and \*.gif
allowedFileTypes = ["image/png", "image/jpeg", "image/gif"];

for (i = 0; i < files.length; i++) {
  // Testa se file.type é um tipo de arquivo permitido.
  if (allowedFileTypes.indexOf(files[i].type) > -1) {
    // file type é um dos tipos permitidos. Código aqui.
  }
});
```

Especificações
--------------

| Specification |
| --- |
| File API # dfn-type |

Compatibilidade com navegadores
-------------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Veja também
-----------

* `Blob`


EventSource.onerror - APIs da Web

EventSource.onerror
===================

A propriedade **`onerror`** da interface `EventSource` é um `event handler` chamado quando um erro ocorre e um evento `error (en-US)` é despachado para o objeto `EventSource`.

Sintaxe
-------
```

eventSource.onerror = function
```

Exemplos
--------

```js
evtSource.onerror = function () {
  console.log("EventSource failed.");
};
```

**Nota:** Você pode encontrar um exemplo completo no GitHub — veja Simple SSE demo using PHP.

Especificações
--------------

| Specification |
| --- |
| HTML Standard # event-error |
| HTML Standard # handler-eventsource-onerror |

Compatibilidade com navegadores
-------------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Veja também
-----------

* `EventSource`


EventSource() - APIs da Web

EventSource()
=============

O construtor **`EventSource()`** retorna uma nova `EventSource`, que representa um recurso remoto.

Sintaxe
-------
```

eventSource = new EventSource(url, configuration);
```

### Parâmetros

`url`

Uma `USVString` (en-US) que representa a localização de um recurso remoto servindo os eventos/mensagens.

`configuration` Optional

Fornece opções para configurar a nova conexão. Os atributos possíveis são:

* `withCredentials`, valor padrão `false`, indicando se o CORS deve ser instruído a `incluir` credenciais.

Exemplos
--------

```js
var evtSource = new EventSource("sse.php");
var eventList = document.querySelector("ul");

evtSource.onmessage = function (e) {
  var newElement = document.createElement("li");

  newElement.textContent = "message: " + e.data;
  eventList.appendChild(newElement);
};
```

**Nota:** Você pode encontrar um exemplo completo no GitHub — veja Simple SSE demo using PHP.

Especificações
--------------

| Specification |
| --- |
| HTML Standard # dom-eventsource-dev |

Compatibilidade com navegadores
-------------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `EventSource`


Contêiner de Alinhamento - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Contêiner de Alinhamento
========================

O **contêiner de alinhamento** é um retângulo dentro do qual os tópicos de alinhamento são alinhados, isto é, definidos. Isso é definido pelo modelo de layout; geralmente é o bloco que contém o tópico de alinhamento, e assume o modo de escrita do box estabelecendo o bloco de conteúdo.

Saiba mais
----------

* CSS Alinhamento de Box (en-US)


Python - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Python
======

**Python** é uma linguagem de programação de alto nível e de uso geral. Ele usa uma abordagem multi-paradigma, o que significa que ele suporta procedural, orientação a objetos, e algumas construções de programação funcional.

Ele foi criado por Guido van Rossum como sucessor de outra linguagem (chamado ABC) entre 1985 e 1990, e atualmente é usado em uma grande variedade de domínios, como desenvolvimento web, como linguagem de script para outras aplicações e para construir aplicações reais.

Saiba Mais
----------

* Python na Wikipedia
* Um tutorial python


Atributo HTML - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Atributo HTML
=============

*Atributos* estendem uma tag ("etiqueta"), modificando o comportamento dela ou fornecendo meta dados.
 Um atributo sempre tem a forma `nome=valor` (especificando o identificador do atributo e o valor associado a ele).

Exemplo:

<div id="principal" class="principal"> Neste cado o `id` e `class` são o atributo e o nome pricipal o valor.

Aprenda mais
------------

### Referência Técnica

* Referência de atributos de HTML
* Informações sobre atributos globais de HTML.


Função First-class - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Função First-class
==================

Entende-se que uma linguagem de programação tem **Função First-class** quando suas funções são tratadas como qualquer outra variável. Por exemplo, numa linguagem desse tipo, a função pode ser passada como argumento pra outras funções, ser retornada por outra função e pode ser atribuída como um valor à uma variável.

Exemplo | Atribuir uma função à uma variável
--------------------------------------------

### JavaScript

```js
const foo = function () {
  console.log("foobar");
};
// Chamar a função usando a variável
foo();
```

Nós atribuímos uma `Função Anônima` à uma `Variável`, então usamos a variável pra chamar a função adicionando parênteses `()` no fim.

**Nota:** **Mesmo se sua função for nomeada,** você pode usar o nome da variável pra chamá-la. Nomeá-la será útil quando for debugar seu código. *Mas não afetará a maneira que à chamamos.*

Exemplo | Passar uma função como um Argumento
---------------------------------------------

### JavaScript

```js
function sayHello() {
  return "Hello, ";
}
function greeting(helloMessage, name) {
  console.log(helloMessage() + name);
}
// Passar `sayHello` como um argumento pra função `greeting`
greeting(sayHello, "JavaScript!");
```

Nós estamos passando a função `sayHello()` como um argumento pra função `greeting()`, isso explica como estamos tratando a função como um `valor`.

**Nota:** A função que passamos como um argumento pra outra função, chamou uma **Função Callback.** *`sayHello` é uma Função Callback.*

Exemplo | Retornar uma função
-----------------------------

### JavaScript

```js
function sayHello() {
  return function () {
    console.log("Hello!");
  };
}
```

Neste exemplo; Precisamos retornar uma função de outra função - *Podemos retornar uma função porque tratamos função em JavaScript como um **`valor`**.*

**Nota:** Uma função que retorna uma função é chamada de **Higher-Order Function**

De volta ao nosso exemplo; Agora, precisamos chamar a função `sayHello` e a `Função anônima` retornada. Existem duas maneiras para fazermos isso:

### 1- Usando uma variável

```js
const sayHello = function () {
  return function () {
    console.log("Hello!");
  };
};
const myFunc = sayHello();
myFunc();
```

Dessa maneira, ela retorna a mensagem `Hello!`.

**Nota:** Você tem que usar outra variável. Se você fosse chamar `sayHello` diretamente, ela retornaria a função em si **sem chamar a função retornada**.

### 2- Usando parênteses duplo

```js
function sayHello() {
  return function () {
    console.log("Hello!");
  };
}
sayHello()();
```

Estamos usando parênteses duplo `()()` pra chamar também a função retornada.

Saiba mais
----------

### Conhecimento geral

* First-class functions na Wikipedia


CORS - Glossário do MDN Web Docs: Definições de termos relacionados à Web

CORS
====

**CORS** (Cross-Origin Resource Sharing) é um sistema que consiste na transmissão de HTTP headers (en-US), que determina se navegadores vão bloquear código JavaScript de acessarem respostas provindas de requisições entre origens.

A same-origin security policy (en-US) proíbe acesso aos recursos entre origens. Mas CORS dá aos servidores web a habilidade de dizer quando eles querem optar em permitir o acesso aos seus recursos entre origens.

Aprenda mais
------------

### Conhecimento geral

* Cross-Origin Resource Sharing (CORS) na MDN
* Cross-origin resource sharing na Wikipedia
### Cabeçalhos CORS

`Access-Control-Allow-Origin`

Indica quando uma resposta pode ser compartilhada.

`Access-Control-Allow-Credentials`

Indica quando uma resposta a uma requisição pode ou não ser exposta quando a bandeira de credenciais é verdadeira.

`Access-Control-Allow-Headers`

Usado na resposta a uma requisição *on-the-fly* que indica quais cabeçalhos HTTP podem ser usados quando a requisição verdadeira for feita.

`Access-Control-Allow-Methods`

Especifica o método ou métodos de acesso ao recurso na requisição *on-the-fly.*

`Access-Control-Expose-Headers`

Indica quais cabeçalhos podem ser expostos como parte da resposta listando seus nomes.

`Access-Control-Max-Age`

Indica quanto tempo os resultados da requisição *on-the-fly* podem ser cacheados.

`Access-Control-Request-Headers`

Usado ao emitir uma requisição *on-the-fly* para deixar o servidor sabendo quais cabeçalhos HTTP serão utilizados quando a requisição verdadeira for feita.

`Access-Control-Request-Method`

Usado ao emitir uma requisição *on-the-fly* para deixar o servidor sabendo qual método HTTP será usado quando a requisição verdadeira for feita.

`Origin`

Indica de onde uma busca se origina.

### Referência técnica

* Especificação Fetch


Arpanet - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Arpanet
=======

A **ARPAnet** (*Advanced Research Projects Agency Network*, em português, Rede da Agência de Pesquisas em Projetos Avançados) foi a primeira rede de computadores, construída em 1969 como um meio robusto para transmitir dados militares sigilosos e para interligar os departamentos de pesquisa por todo os Estados Unidos. ARPAnet primeiro executou NCP (*Network Control Protocol*, em português, Protocolo de Controle de Rede) e posteriormente a primeira versão do protocolo de internet ou o conjunto de protocolos TCP/IP (*Transmission Control Protocol/Internet Protocol*, em português, Protocolo de Controle de Transmissão/Protocolo de Internet), fazendo da ARPAnet uma parte proeminente da nascente Internet. ARPAnet foi fechado no início de 1990.

Saiba mais
----------

### Conhecimento Geral

* ARPANET na Wikipédia


Mozilla Firefox - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Mozilla Firefox
===============

Mozilla Firefox é um browser open-source cujo desenvolvimento é supervisionado pela Mozilla Corporation. Firefox roda em Windows, OS X, Linux, e Android.

O primeiro lançamento foi em Novembro de 2004, Firefox é completamente customizável com temas, plug-ins, e complementos. Firefox usa o Gecko para renderizar páginas web, e implementa ambos padrões corrente e a vir da Web.

Aprenda mais
------------

### Conhecimento geral

* Site oficial do Mozilla Firefox
### Referência técnica

* Informações de documentação/desenvolvimento na MDN


WHATWG - Glossário do MDN Web Docs: Definições de termos relacionados à Web

WHATWG
======

O GRUTTAH (WHATWG - sigla em inglês) (Grupo de Trabalho da Tecnologia de Aplicação Hipertexto da Web tradução livre de *Web Hypertext Application Working Group*) é uma organização que mantém e desenvolve o HTML e as APIs para aplicações Web. Antigos funcionários da Apple, Mozilla e Opera, estabeleceram o GRUTTAH em 2004.

 Os editores de especificações na GRUTTAH pesquisam e colhem feedbacks para a especificação de documentos. O grupo també tem um pequeno comitê de membros convidados e autorizados a sobrescrever ou substituir as especificações dos editores.
 Você também pode se juntar como contribuidor, assinando a lista de correspondência.

De acordo com o site deles, GRUTTAH é uma resposta ao lento progresso de padronização da Web da W3C's, especialmente o HTML, que teve seu desenvolvimento parado para a W3C concentrar no XHTML.

O GRUTTAH mantém especificações para HTML, DOM e JavaScript.

Saiba mais
----------

### Conhecimentos gerais

* WHATWG na Wikipedia
* Site Oficial da WHATWG.org


HTTP - Glossário do MDN Web Docs: Definições de termos relacionados à Web

HTTP
====

HTTP (HyperText Transfer Protocol) é o protocolo básico que habilita a transferência de arquivos na Web (en-US), normalmente entre um navegador e um servidor para que os humanos possam lê-los. A versão atual da especificação HTTP é chamada HTTP / 2.

Como parte de um URI, o "http: //" é chamado de "esquema" e geralmente fica no início de um endereço, por exemplo em "https://developer.mozilla.org" para indicar ao navegador que solicite o documento usando o protocolo HTTP. O https neste caso refere-se à versão segura do protocolo HTTP, SSL (en-US) (também chamado TLS).

O HTTP é textual (toda a comunicação é feita em texto simples) e sem estado (nenhuma comunicação está ciente das comunicações anteriores). Esta propriedade torna ideal para humanos ler documentos (sites) na web. No entanto, o HTTP também pode ser usado como base para os serviços da Web REST de servidor para servidor ou solicitações AJAX dentro de sites para torná-los mais dinâmicos.

Leia mais
---------

* HTTP no MDN
* HTTP na Wikipedia


Palavra-chave - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Palavra-chave
=============

Uma **palavra-chave** é a palavra ou a frase que descreve conteúdo. Palavras-chave on-line são utilizadas como pistas para buscadores ou como um conjunto de palavras que identifica e sintetiza o conteúdo dos sites.

Quando você usa uma máquina de busca, utiliza palavras ou expressões para especificar o conteúdo que está procurando e, como resultado, a máquina de busca retorna endereços e páginas relevantes para os termos apresentados. Para resultados mais apurados, tente utilizar palavras mais específicas, a exemplo "Blue Mustang GTO" ao invés de simplesmente "Mustang". Páginas de internet também utilizam palavras na forma de meta tags (na seção `<head>` para descrever o conteúdo da página, assim os buscadores podem identificar e organizar de forma melhorada as páginas da internet.

Aprenda Mais
------------

### Conhecimento Geral

* Keyword on Wikipedia
* Motor de Busca no Wikipedia


CDN - Glossário do MDN Web Docs: Definições de termos relacionados à Web

CDN
===

CDN é a sigla para (Content Delivery Network) ou seja, Rede de Fornecimento de Conteúdo na tradução livre. É um grupo de servidores espalhados em muitos locais. Esses servidores armazenam cópias duplicadas de dados para que os servidores possam atender às solicitações de dados com base nos quais servidores estão mais próximos dos respectivos usuários finais. CDNs deixa os servidores menos sobrecarregados quando possuem alto tráfego de dados.


Three js - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Three js
========

three.js é um motor WebGL (en-US) baseado em JavaScript que pode executar jogos movidos com GPU ou outros aplicativos gráficos diretamente do navegador. A biblioteca three.js fornece várias funções e APIs para desenhar cenas 3D em seu navegador.

Saiba mais
----------

### Conhecimento Geral

* Three.js na Wikipédia
* Site oficial do three.js


SDP - Glossário do MDN Web Docs: Definições de termos relacionados à Web

SDP
===

**SDP** (Session Description Protocol) é o padrão que descreve uma conexão ponto a ponto (en-US). SDP contém o codec (en-US), o endereço de origem e as informações de tempo de áudio e vídeo.

Aqui está uma mensagem SDP típica:
```

v=0
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com
s=
c=IN IP4 host.anywhere.com
t=0 0
m=audio 49170 RTP/AVP 0
a=rtpmap:0 PCMU/8000
m=video 51372 RTP/AVP 31
a=rtpmap:31 H261/90000
m=video 53000 RTP/AVP 32
a=rtpmap:32 MPV/90000
```

O SDP nunca é usado sozinho, mas por protocolos como RTP (en-US) e RTSP (en-US). O SDP também é um componente do WebRTC, que usa o SDP como forma de descrever uma sessão.

Leia mais
---------

### Conhecimentos gerais

* Protocolos WebRTC (en-US)
* Session Description Protocol no Wikipedia


Apple Safari - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Apple Safari
============

Safari é um navegador Web desenvolvido pela Apple e fornecido com ambos Mac OS X e iOS. Baseia-se com o mecanismo de código aberto WebKit.

Saiba mais
----------

### Conhecimento geral

* Safari na Wikipedia
* Safari on apple.com
### Informação técnica

* The WebKit project
* WebKit nightly build
* Reporting a bug for Safari


Pixel - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Pixel
=====

Um pixel é o menor elemento de um dispositivo de exibição como, por exemplo, um monitor.

A resolução de exibição pode ser expressada utilizando-se pixels como unidade. Por exemplo, uma resolução de "800x600" pixels significa que podem ser exibidos 800 pixels na largura e 600 pixels na altura.

Aprenda mais
------------

### Referência técnica

* Pixel na Wikipedia


Character encoding - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Character encoding
==================

Uma codificação define um mapeamento entre bytes e texto. Uma sequência de bytes permite diferentes interpretações textuais. Ao especificar uma codificação específica (como UTF-8), especificamos como a sequência de bytes deve ser interpretada.

Por exemplo, em HTML, normalmente declaramos uma codificação de caracteres UTF-8, usando a seguinte linha:

```html
<meta charset="utf-8" />
```

Isso garante que você possa usar caracteres de praticamente qualquer idioma humano em seu documento HTML e eles serão exibidos com de forma confiável.

Leia mais
---------

### Conhecimento Geral

* Character encoding on W3C
* Codificação de caracteres no Wikipedia


Progressive web apps - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Progressive web apps
====================

Progressive web apps é um termo usado para descrever o estado moderno do desenvolvimento de aplicações web. Isso envolve pegar web sites/apps que aproveitam de todas as melhores partes da Web - como descobertas por mecanismos de busca, tornando-se linkaveis via URLs, e trabalho em vários fatores de formas - e sobrecarregá-los com APIs modernas (como Service Works e Push) e funcionalidades que conferem outros benefícios comumente atribuídos para apps nativos.

Essas funcionalidades incluem possibilidade de instalação, funcionamento offline e ser de fácil sincronização e reconexão do usuário com o servidor.

Saiba mais
----------

* O App Center (en-US) na MDN
* Progressive web apps no Google Developers


Objeto - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Objeto
======

Objeto refere-se a uma estrutura de dados contendo dados e instruções para se trabalhar com estes dados. Objetos algumas vezes se referem a coisas do mundo real, por exemplo um objeto de carro ou um mapa em um jogo de corrida. JavaScript, Java, C++, Python, e Ruby são exemplos de Linguagens orientadas à objeto (object-oriented programming).

Aprenda mais
------------

### Conhecimentos gerais

* Object-oriented programming on Wikipedia
* `Object` in the JavaScript reference
* Object data structures in JavaScript


Web Design Responsivo - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Web Design Responsivo
=====================

*Web Design Responsivo* (**RWD em Inglês**) é um conceito de desenvolvimento Web focado em fazer a experiência e comportamento de websites mais otimizada para todos os dispositivos, desde o desktop até um dispositivo móvel.

Aprenda mais
------------

### Conhecimento geral

* Progressive Web Apps (en-US)
* Responsive Web Design


Identificador (Identifier) - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Identificador (Identifier)
==========================

Uma sequência de caracteres no código, que identifica uma **variável, função, ou propriedade**.

Em JavaScript, identificadores podem conter somente caracteres alfanuméricos (ou "$" ou "\_"), e não podem iniciar com um dígito. Um identificador difere de uma **string** no sentido de que uma string é informação, enquanto um identificador é parte do código. Em JavaScript, não existe uma forma de converter identificadores para strings, mas as vezes é possível converter strings em identificadores.

Saiba mais
----------

### Conhecimento Geral

* Identificador no Wikipédia


HSTS - Glossário do MDN Web Docs: Definições de termos relacionados à Web

HSTS
====

**O HTTP Strict Transport Security** permite que um site informe ao navegador que ele nunca deve carregar o site usando HTTP e deve converter automaticamente todas as tentativas de acessar o site usando HTTP para solicitações HTTPS. Consiste em um cabeçalho HTTP `Strict-Transport-Security`, enviado de volta pelo servidor com o recurso.

Em outras palavras, ele diz ao navegador que apenas altere o protocolo de HTTP para HTTPS em uma url (e será mais seguro) e solicitará que o navegador faça isso para cada solicitação.

Leia Mais
---------

* `Strict-Transport-Security`
* Artigo OWASP: HTTP Strict Transport Security
* Wikipedia: HTTP Strict Transport Security


Cache - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Cache
=====

O **cache** (web cache ou HTTP cache) é uma forma de armazenar dados que foram recebidos por respostas HTTP temporariamente para que possam ser usados em requisições HTTP subsequentes enquanto as condições de seu uso forem satisfeitas.

O **cache** ajuda a reduzir o tempo de resposta de uma página após a primeira requisição à mesma. Arquivos estáticos como o HTML e o CSS que não se modificam com frequência, scripts e imagens, ao serem salvos no cache, não precisam ser baixados do servidor novamente, sendo assim, só informações dinâmicas vão precisar ser requeridas. Lembrando que, qualquer modificação dos arquivos estáticos, o navegador do cliente terá de baixar novamente para receber as atualizações deste arquivo.

Aprenda mais
------------

### Conhecimento geral

* Web cache on Wikipedia


Viewport - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Viewport
========

Uma viewport representa uma área poligonal (normalmente retangular) que está sendo exibida no momento. Em termos de navegador web, se refere a parte do documento que é exibida na janela (ou tela, se o documento estiver exibido em modo tela-cheia). Conteúdos fora da viewport não são visíveis na janela/tela até que seja rolado para sua área de visualização.

A porção da viewport que está visível no momento é chamada de **visual viewport** . Esta pode ser menor que a viewport de layout, assim é quando o usuário efetua uma pinçada/zoom. O **viewport** **de** **layout** permanece o mesmo, mas a visual viewport se torna menor.

1. Conhecimentos gerais
    1. Viewport na Wikipedia (em inglês)
    2. O que é viewport ? em Stackoverflow (em português)
    3. Como obter a largura e altura da viewport com Javascript (em inglês)
    4. O conto de duas viewports (Quirksmode - em inglês)
    5. VisualViewport API (en-US)
2. MDN Web Docs Glossary
    1. Visual Viewport (en-US)


Reflow - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Reflow
======

**Reflow** acontece quando o browser deve processar e desenhar parte ou toda a página novamente, como após uma atualização em um site interativo.

Aprenda mais
------------

* Artigo do Google "Minimizing browser reflow"


Elemento - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Elemento
========

Um **elemento** é parte de uma página web. Em XML e HTML, um elemento pode conter um item de dados, um bloco de texto, uma imagem ou talvez nada. Um elemento típico inclui uma tag de abertura com alguns atributos, o conteúdo de texto incluído e uma tag de fechamento.

![Example: in
Hello world!
, '
' is an opening tag, 'class="nice"' is an attribute and its value, 'Hello world!' is enclosed text content, and '
' is a closing tag.](/pt-BR/docs/Glossary/Element/anatomy-of-an-html-element.png)

Elementos e tags *não* são a mesma coisa. As tags começam ou terminam um elemento no código fonte, enquanto os elementos são parte do DOM, o modelo do documento para exibir a página no navegador.

Veja também
-----------

* Começando com HTML
* Definindo Custom Elements
* A interface `Element` representando um elemento no DOM.


Árvore de Acessibilidade - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Árvore de Acessibilidade
========================

A **árvore de acessibilidade** contém informação relacionada à acessibilidade para a maioria dos elementos HTML.

Navegadores convertem a marcação em uma representação interna chamada árvore do DOM. A árvore do DOM contém objetos representando todas as marcações de elementos, atributos e nós de texto. Os navegadores, então, criam uma árvore de acessibilidade baseada na árvore do DOM, a qual é usada por APIs específicas de plataforma para fornecer uma representação que possa ser entendida por tecnologias assistivas, como leitores de tela.

Há quatro coisas em um objeto da árvore de acessibilidade:

**nome**

Como podemos nos referir a esse objeto? Por exemplo, um link com o texto "Leia mais" terá "Leia mais" como seu atributo *name* (saiba mais como os nomes são computados em Accessible Name and Description Computation spec).

**descrição**

Como podemos descrever esse objeto se quisermos fornecer mais detalhes além do nome? A descrição de uma tabela pode explicar que tipo de informação ela contém.

**função**

Que tipo de objeto é esse? Por exemplo, é um botão, uma barra de navegação ou uma lista de items?

**estado**

Tem um estado? Os exemplos incluem caixas de seleção marcadas ou desmarcadas, e recolhido ou expandido para o elemento `<summary>`.

Adicionalmente, a árvore de acessibilidade frequentemente contém informação sobre o que pode ser feito com um elemento: um link pode ser seguido, uma entrada de texto pode ser digitada, entre outras coisas.

Enquanto continua em forma de rascunho na Web Incubator Community Group, o Modelo de Objeto de Acessibilidade (**Accessibility Object Model,** AOM), tem a intenção de incubar APIs que facilitam expressar a semântica de acessibilidade e potencialmente permitir acesso de leitura à árvore de acessibilidade computada.

1. Glossário
    1. Acessibilidade
    2. ARIA


SVG - Glossário do MDN Web Docs: Definições de termos relacionados à Web

SVG
===

*Scalable Vector Graphics* (**SVG**) é um formato de imagem vetorial 2D baseado em uma sintaxe XML.

A W3C iniciou o trabalho no SVG no final dos anos 1990, mas o SVG só se tornou popular quando o Internet Explorer (en-US) 9 foi lançado com suporte a SVG. Atualmente a maioria dos navegadores suportam SVG.

Baseado na sintaxe de XML, SVG pode ser estilizado com CSS e apresentar interatividade usando JavaScript. Em HTML5 é possível embutir diretamente tags SVG em um documento HTML.

Sendo um formato para gráficos vetoriais, imagens SVG podem ser redimensionadas infinitamente, tendo um valor inestimável em design responsivo (en-US), já que você pode criar elementos de interface e gráficos que se comportam bem em qualquer tamanho de tela. SVG também traz um conjunto de funcionalidades úteis, como clipping, máscaras, filtros e animações.

Exemplo
-------

Abaixo você pode conferir a renderização de dois círculos de tamanhos e cores diferentes utilizando SVG.

```xml
<svg width="100" height="300">
   <circle cx="50" cy="50" r="40" stroke="blue" stroke-width="10" fill="red" />
   <circle cx="50" cy="150" r="20" stroke="green" stroke-width="10" fill="yellow" />
</svg>
```

Repare que `cx` e `cy` são para definir as coordenadas x e y do círculo, `r` é para definir o raio do círculo, o preenchimento é definido com `fill`, a largura do contorno é definida com `stroke-width` e a cor do contorno é definida com `stroke`. Lembrando que a largura e altura do `svg` em si são definidas no próprio elemento.

Saiba mais
----------

### Conhecimento Geral

* SVG na Wikipedia
### Aprendendo SVG

* W3.org's SVG Primer (em inglês)
### Informação técnica

* Documentação sobre SVG na MDN
* Especificação SVG mais recente (em inglês)


Metadata - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Metadata
========

Os **metadados** são — em sua definição mais simples — dados que descrevem dados. Por exemplo, um documento HTML é um dado, mas o HTML, também, pode conter metadados em seu elemento `<head>` que descreve o documento — por exemplo, quem o escreveu e seu resumo.

Saiba mais
----------

### Conhecimento geral

* metadata na Wikipedia
### Metadados HTML

* O elelmento `<meta>` no MDN


AJAX - Glossário do MDN Web Docs: Definições de termos relacionados à Web

AJAX
====

AJAX (Asynchronous JavaScript e XML) é uma prática de programação da combinação de HTML, CSS, JavaScript, o DOM, e o `XMLHttpRequest` object para a construção de páginas web mais complexas.

O que o AJAX permite é que você faça apenas atualizações em partes de uma página web ao invés de recarregá-la inteira novamente. O AJAX também permite que você trabalhe de forma assíncrona, o que significa que o seu código continua rodando enquanto uma parte da sua página está em processo de carregamento (em comparação com síncrono, que irá bloquear a execução do seu código até que toda a sua página web seja recarregada).

Com websites interativos e padrões web modernos, o AJAX está sendo substituído gradualmente por funcões presentes em frameworks JavaScript e o padrão oficial Fetch API.

Saiba mais
----------

### Conhecimentos gerais

* AJAX na Wikipedia
### Informações técnicas

* O objeto `XMLHttpRequest` .
* A documentação AJAX no MDN
* Fetch API no MDN


Degradação graciosa - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Degradação graciosa
===================

**Degradação graciosa** é uma filosofia de design centrada na tentativa de criar um site/aplicativo moderno que funcione nos navegadores mais recentes, mas recorre a uma experiência que, embora não tão boa, ainda oferece conteúdo e funcionalidade essenciais para os navegadores antigos.

Polyfills podem ser usados para criar com JavaScript recursos ausentes, mas alternativas aceitáveis para recursos como estilo e layout devem ser fornecidas sempre que possível, por exemplo, usando a cascata CSS ou o comportamento de HTML substituto. Alguns bons exemplos podem ser encontrados em Handling common HTML and CSS problems (en-US).

 É uma técnica útil que permite aos desenvolvedores da Web se concentrarem no desenvolvimento dos melhores sites possíveis, uma vez que esses sites são acessados por vários user-agents desconhecidos.
 Progressive enhancement (en-US) está relacionado, mas é diferente - geralmente visto como na direção oposta à degradação graciosa. Na realidade, ambas as abordagens são válidas e geralmente podem se complementar.

1. Graceful degradation on Wikipedia
2. Handling common HTML and CSS problems (en-US)
3. Implementing feature detection (en-US)
4. MDN Web Docs Glossary
    1. Graceful degradation
    2. Polyfill
    3. Progressive enhancement (en-US)


Nome de cabeçalho proibido - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Nome de cabeçalho proibido
==========================

Um *nome de cabeçalho proibido* é o nome de qualquer cabeçalho HTTP que não pode ser modificado programaticamente; especificamente, um nome de cabeçalho de **solicitação** HTTP (em contraste com um Forbidden response header name).

Modificar esses cabeçalhos é proibido porque o agente do usuário retém o controle total sobre eles. Nomes começando com `Sec-` são reservados para criar novos cabeçalhos seguros de APIs usando Fetch que concedem aos desenvolvedores controle sobre cabeçalhos, como `XMLHttpRequest`.

Nomes de cabeçalho proibidos começam com `Proxy-`ou `Sec-`, ou são um dos seguintes nomes:

* `Accept-Charset`
* `Accept-Encoding`
* `Access-Control-Request-Headers`
* `Access-Control-Request-Method`
* `Connection`
* `Content-Length`
* `Cookie`
* `Cookie2`
* `Date`
* `DNT`
* `Expect`
* `Host`
* `Keep-Alive`
* `Origin`
* `Proxy-`
* `Sec-`
* `Referer`
* `TE`
* `Trailer`
* `Transfer-Encoding`
* `Upgrade`
* `Via`

**Nota:** O cabeçalho `User-Agent` não é mais proibido, de acordo com a especificação - consulte a lista de nomes de cabeçalhos proibidos (isso foi implementado no Firefox 43) - agora ele pode ser definido em um objeto e busca de Headers (en-US), ou via XHR setRequestHeader().


Node.js - Glossário do MDN Web Docs: Definições de termos relacionados à Web

Node.js
=======

Node.js é um ambiente de execução multi-plataforma em JavaScript que permite aos desenvolvedores produzirem aplicações para rede e server-side usando o JavaScript.

Aprenda mais
------------

### Conhecimentos gerais

* Node.js no Wikipédia
* Site do Node.js
### Referência técnica

* Documentação da API
* Tutoriais