Browse Source

Merge branch 'master' into async

Caleb Porzio 5 years ago
parent
commit
b7381425f4
10 changed files with 1238 additions and 406 deletions
  1. 12 3
      README.md
  2. 789 0
      README.pt.md
  3. 387 388
      dist/alpine-ie11.js
  4. 1 1
      dist/alpine.js
  5. 1 1
      package-lock.json
  6. 0 9
      src/component.js
  7. 3 3
      src/utils.js
  8. 14 0
      test/bind.spec.js
  9. 19 0
      test/on.spec.js
  10. 12 1
      test/utils.spec.js

+ 12 - 3
README.md

@@ -12,9 +12,14 @@ Think of it like [Tailwind](https://tailwindcss.com/) for JavaScript.
 
 > Note: This tool's syntax is almost entirely borrowed from [Vue](https://vuejs.org/) (and by extension [Angular](https://angularjs.org/)). I am forever grateful for the gift they are to the web.
 
-[**日本語ドキュメント**](./README.ja.md)
-[**繁體中文使用文件**](./README_zh-TW.md)
-[**Документация на русском**](./README.ru.md)
+## Translated documentation
+
+| Language | Link for documentation |
+| --- | --- |
+| Japanese | [**日本語ドキュメント**](./README.ja.md) | 
+| Chinese | [**繁體中文使用文件**](./README_zh-TW.md) | 
+| Russian | [**Документация на русском**](./README.ru.md) | 
+| Portuguese | [**Documentação em Português**](./README.pt.md) | 
 
 ## Install
 
@@ -457,6 +462,8 @@ It's important that `x-if` is used on a `<template></template>` tag because Alpi
 
 > Note: `x-if` must have a single element root inside the `<template></template>` tag.
 
+> Note: When using `template` in a `svg` tag, you need to add a [polyfill](https://github.com/alpinejs/alpine/issues/637#issuecomment-654856538) that should be run before Alpine.js is initialized.
+
 ---
 
 ### `x-for`
@@ -482,6 +489,8 @@ If you want to access the current index of the iteration, use the following synt
 
 > Note: `x-for` must have a single element root inside of the `<template></template>` tag.
 
+> Note: When using `template` in a `svg` tag, you need to add a [polyfill](https://github.com/alpinejs/alpine/issues/637#issuecomment-654856538) that should be run before Alpine.js is initialized.
+
 #### Nesting `x-for`s
 You can nest `x-for` loops, but you MUST wrap each loop in an element. For example:
 

+ 789 - 0
README.pt.md

@@ -0,0 +1,789 @@
+# Alpine.js
+
+![npm bundle size](https://img.shields.io/bundlephobia/minzip/alpinejs)
+![npm version](https://img.shields.io/npm/v/alpinejs)
+[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true)](https://alpinejs.codewithhugo.com/chat/)
+
+O Alpine.js oferece a natureza reativa e declarativa de grandes estruturas, como Vue ou React, a um custo muito menor.
+
+Podemos manter a DOM e aperfeiçoar o comportamento como acharmos melhor.
+
+Pensem nisso como o [Tailwind](https://tailwindcss.com/) para JavaScript.
+
+> Nota: A sintaxe desta ferramenta é quase totalmente inspirada no [Vue](https://vuejs.org/) (e por extensão [Angular](https://angularjs.org/)). Estou eternamente agradecido pelo presente que estas são para a web.
+
+## Instalação
+
+**Via CDN:** Adicionem o seguinte script no final da seção `<head>`.
+
+```html
+<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
+```
+
+E é isso. Ele vai se inicializar.
+
+Para ambiente de produção, é recomendado fixar o número da versão específico no link para evitar problemas inesperadas das versões mais recentes.
+Por exemplo, para usar a versão `2.3.5`:
+
+```html
+<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.3.5/dist/alpine.min.js" defer></script>
+```
+
+**Via NPM:** Instale o pacote pelo NPM.
+
+```js
+npm i alpinejs
+```
+
+Incluir no script.
+
+```js
+import 'alpinejs';
+```
+
+**Para suportar IE11** Usar os seguintes scripts.
+
+```html
+<script type="module" src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js"></script>
+<script nomodule src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine-ie11.min.js" defer></script>
+```
+
+O padrão acima é o [padrão de módulo/nomodule](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/) que resulta num pacote moderno carregado automaticamente em browsers modernos e o pacote IE11 carregado automaticamente no IE11 e em outros browsers herdados.
+
+## Usar
+
+_Dropdown/Modal_
+
+```html
+<div x-data="{ open: false }">
+    <button @click="open = true">Open Dropdown</button>
+
+    <ul x-show="open" @click.away="open = false">
+        Corpo do Dropdown
+    </ul>
+</div>
+```
+
+_Tabs_
+
+```html
+<div x-data="{ tab: 'foo' }">
+    <button :class="{ 'active': tab === 'foo' }" @click="tab = 'foo'">
+        Foo
+    </button>
+    <button :class="{ 'active': tab === 'bar' }" @click="tab = 'bar'">
+        Bar
+    </button>
+
+    <div x-show="tab === 'foo'">Tab Foo</div>
+    <div x-show="tab === 'bar'">Tab Bar</div>
+</div>
+```
+
+Podemos até usá-lo para coisas não triviais:
+_Pré pedido de conteudo para o HTML da dropdown ao passar com o rato_
+
+```html
+<div x-data="{ open: false }">
+    <button
+        @mouseenter.once="
+            fetch('/dropdown-partial.html')
+                .then(response => response.text())
+                .then(html => { $refs.dropdown.innerHTML = html })
+        "
+        @click="open = true"
+    >
+        Mostrar Dropdown
+    </button>
+
+    <div x-ref="dropdown" x-show="open" @click.away="open = false">
+        Carregando Spinner...
+    </div>
+</div>
+```
+
+## Aprenda
+
+Existem 14 diretrizes disponíveis:
+
+| Directiva                       | Descrição                                                                                                                     |
+| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
+| [`x-data`](#x-data)             | Declara um novo scope do componente.                                                                                          |
+| [`x-init`](#x-init)             | Executa uma expressão quando um componente é inicializado.                                                                    |
+| [`x-show`](#x-show)             | Alterna `display: none;` no elemento dependendo da expressão (verdadeiro ou falso).                                           |
+| [`x-bind`](#x-bind)             | Define o valor de um atributo para o resultado de uma expressão JS.                                                            |
+| [`x-on`](#x-on)                 | Anexa um evento de escuta ao elemento. Executa a expressão JS quando emitida.                                                 |
+| [`x-model`](#x-model)           | Adiciona "ligação de dados bidirecional" a um elemento. Mantém o elemento de entrada sincronizado com os dados do componente. |
+| [`x-text`](#x-text)             | Funciona da mesma forma que o `x-bind`, mas atualiza o `innerText` de um elemento.                                             |
+| [`x-html`](#x-html)             | Funciona de maneira semelhante ao `x-bind`, mas atualiza o `innerHTML` de um elemento.                                         |
+| [`x-ref`](#x-ref)               | Maneira conveniente de recuperar elementos DOM fora do seu componente.                                                        |
+| [`x-if`](#x-if)                 | Remove um elemento completamente na DOM. Precisa de usar uma tag `<template>`.                                           |
+| [`x-for`](#x-for)               | Crie novos nós DOM para cada item em uma matriz. Precisa de usar uma tag `<template>`.                                   |
+| [`x-transition`](#x-transition) | Diretrizes para aplicar classes a vários estágios da transição de um elemento.                                                 |
+| [`x-spread`](#x-spread)         | Permite definir um objeto de diretivas Alpine, a um elemento para melhor reutilização.                                         |
+| [`x-cloak`](#x-cloak)           | Este atributo é removido quando o Alpine é inicializado. Útil para ocultar a pré-inicialização da DOM.                       |
+
+E 6 propriedades mágicas:
+
+| Propriedades Mágicas     | Descrição                                                                                                        |
+| ------------------------ | ---------------------------------------------------------------------------------------------------------------- |
+| [`$el`](#el)             | Recupere o nó DOM do componente raiz.                                                                            |
+| [`$refs`](#refs)         | Recupera elementos DOM marcados com `x-ref` dentro do componente.                                                |
+| [`$event`](#event)       | Recupera o objeto "Evento" do browser nativo em um evento que estejamos a escuta.                                   |
+| [`$dispatch`](#dispatch) | Cria um `CustomEvent` e envio-o usando `.dispatchEvent ()` internamente.                                           |
+| [`$nextTick`](#nexttick) | Execute uma determinada expressão APÓS o Alpine fazer suas atualizações reativas na DOM.                         |
+| [`$watch`](#watch)       | Disparará um callback fornecida quando uma propriedade do componente que está a "escuta" for alterada. |
+
+## Patrocinadores
+
+<img width="33%" src="https://refactoringui.nyc3.cdn.digitaloceanspaces.com/tailwind-logo.svg" alt="Tailwind CSS">
+
+**Queres o teu logótipo aqui? [Mensagem pelo Twitter](https://twitter.com/calebporzio)**
+
+## Colaboradores VIP
+
+<table>
+  <tr>
+    <td align="center"><a href="http://calebporzio.com"><img src="https://avatars2.githubusercontent.com/u/3670578?v=4" width="100px;" alt="Caleb Porzio"/><br /><sub><b>Caleb Porzio</b></sub></a><br /><sub>(Creator)</sub></td>
+    <td align="center"><a href="https://github.com/HugoDF"><img src="https://avatars2.githubusercontent.com/u/6459679?v=4" width="100px;" alt="Hugo"/><br /><sub><b>Hugo</b></sub></a></td>
+    <td align="center"><a href="https://github.com/ryangjchandler"><img src="https://avatars2.githubusercontent.com/u/41837763?v=4" width="100px;" alt="Ryan Chandler"/><br /><sub><b>Ryan Chandler</b></sub></a></td>
+    <td align="center"><a href="https://github.com/SimoTod"><img src="https://avatars2.githubusercontent.com/u/8427737?v=4" width="100px;" alt="Simone Todaro"/><br /><sub><b>Simone Todaro</b></sub></a></td>
+  </tr>
+</table>
+
+### Diretivas
+
+---
+
+### `x-data`
+
+**Exemplo:** `<div x-data="{ foo: 'bar' }">...</div>`
+
+**Estrutura:** `<div x-data="[JSON data object]">...</div>`
+
+`x-data` declara um novo scope do componente. Diz à estrutura para inicializar um novo componente com o seguinte objeto de dados.
+
+Pensem nisso como a propriedade `data` de um componente Vue.
+
+**Extrair Lógica dos Componentes**
+
+Podemos extrair dados (e comportamentos) em funções reutilizáveis:
+
+```html
+<div x-data="dropdown()">
+    <button x-on:click="open">Open</button>
+
+    <div x-show="isOpen()" x-on:click.away="close">
+        // Dropdown
+    </div>
+</div>
+
+<script>
+    function dropdown() {
+        return {
+            show: false,
+            open() {
+                this.show = true;
+            },
+            close() {
+                this.show = false;
+            },
+            isOpen() {
+                return this.show === true;
+            },
+        };
+    }
+</script>
+```
+
+> **Para utilizadores do bundler**, observem que o Alpine.js assede a funções que estão no scope global (`window`), vamos necessitar atribuir explicitamente as suas funções à `window` para usá-las com `x- data`, por exemplo `window.dropdown = function () {}` (isso ocorre com Webpack, Rollup, Parcel etc. `function`'s que defenir serão padronizados para o scope do módulo, e não para `window`).
+
+Também podemos misturar vários objetos de dados usando a desestruturação de objetos:
+
+```html
+<div x-data="{...dropdown(), ...tabs()}"></div>
+```
+
+---
+
+### `x-init`
+
+**Exemplo:** `<div x-data="{ foo: 'bar' }" x-init="foo = 'baz'"></div>`
+
+**Estrutura:** `<div x-data="..." x-init="[expressão]"></div>`
+
+`x-init` executa uma expressão quando um componente é inicializado.
+
+Caso desejem executar o código ANTES do Alpine fazer as atualizações iniciais na DOM (algo como um gancho `mounted ()` no VueJS), podemos retornar um callback do `x-init`, e é executado após:
+
+`x-init="() => { // temos acesso ao estado de pós-inicialização aqui // }"`
+
+---
+
+### `x-show`
+
+**Exemplo:** `<div x-show="open"></div>`
+
+**Estrutura:** `<div x-show="[expressão]"></div>`
+
+`x-show` alterna o estilo `display: none;` no elemento, dependendo se a expressão for resolvida como `verdadeiro` ou `falso`.
+
+**x-show.transition**
+
+`x-show.transition` é uma API de conveniência para tornar o seu `x-show` mais agradável usando transições CSS.
+
+```html
+<div x-show.transition="open">
+    Esses conteúdos serão transferidos para dentro e para fora.
+</div>
+```
+
+| Diretivas                                               | Descrição                                                                                                                                           |
+| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `x-show.transition`                                     | Desvanecer e escala em simultâneos. (opacity, scale: 0.95, timing-function: cubic-bezier(0.4, 0.0, 0.2, 1), duration-in: 150ms, duration-out: 75ms) |
+| `x-show.transition.in`                                  | Apenas transição de entrada.                                                                                                                        |
+| `x-show.transition.out`                                 | Apenas transição de saída.                                                                                                                          |
+| `x-show.transition.opacity`                             | Apenas transição de desvanecer.                                                                                                                     |
+| `x-show.transition.scale`                               | Apenas transição de escala.                                                                                                                         |
+| `x-show.transition.scale.75`                            | Personalizar a transformação de escala CSS `transform: scale(.75)`.                                                                                 |
+| `x-show.transition.duration.200ms`                      | Define a transição "entrada" para 200ms. A saída é ajustada para metade disso (100ms).                                                           |
+| `x-show.transition.origin.top.right`                    | Personalizar a origem da transformação CSS `transform-origin: top right`.                                                                           |
+| `x-show.transition.in.duration.200ms.out.duration.50ms` | Durações diferentes para "entrada" e "saída".                                                                                                       |
+
+> Nota: Todos esses modificadores de transição podem ser usados em conjunto. Isso é possível (apesar de não fazer sentido): `x-show.transition.in.duration.100ms.origin.top.right.opacity.scale.85.out.duration.200ms.origin.bottom.left.opacity.scale.95`
+
+> Nota: `x-show` espera que todas os filhos terminem a transição. Caso desejem ignorar esse comportamento, adicionem o modificador `.immediate`:
+
+```html
+<div x-show.immediate="open">
+    <div x-show.transition="open"></div>
+</div>
+```
+
+---
+
+### `x-bind`
+
+> Nota: Podemos usar uma sintaxe ":" mais curta: `:type =" ... "`
+
+**Exemplo:** `<input x-bind:type="inputType">`
+
+**Estrutura:** `<input x-bind:[attribute]="[expressão]">`
+
+`x-bind` define o valor de um atributo para o resultado de uma expressão JavaScript. A expressão tem acesso a todas as chaves do objeto de dados do componente e é atualizada sempre que os dados forem atualizados.
+
+> Nota: as ligações de atributo APENAS são atualizadas quando as dependências são atualizadas. A estrutura é inteligente o suficiente para observar alterações nos dados e detectar quais ligações se importam com elas.
+
+**`x-bind` para atributos de classes**
+
+`x-bind` comporta-se de maneira um pouco diferente ao definir o atributo`class`.
+
+Para classes, passamos um objeto cujas as chaves são nomes de classe e valores são expressões booleanas para determinar se esses nomes de classe são aplicados ou não.
+
+Por exemplo: `<div x-bind:class="{ 'hidden': foo }"></div>`
+
+Neste exemplo, a classe "hidden" é aplicada apenas quando o valor do atributo de dados `foo` for `verdadeiro`.
+
+**`x-bind` para atributos booleanos**
+
+O `x-bind` suporta atributos booleanos da mesma maneira que os atributos de valor, usando uma variável como a condição ou qualquer expressão JavaScript que resolva como `verdadeiro` ou `falso`.
+
+Por exemplo:
+
+```html
+<!-- Given: -->
+<button x-bind:disabled="myVar">Clique em mim</button>
+
+<!-- Quando myVar == true: -->
+<button disabled="disabled">Clique em mim</button>
+
+<!-- Quando myVar == false: -->
+<button>Clique em mim</button>
+```
+
+Isso adicionará ou removerá o atributo `disabled` quando`myVar` for verdadeiro ou falso, respectivamente.
+
+Os atributos booleanos são suportados de acordo com a [especificação HTML](https://html.spec.whatwg.org/multipage/indices.html#attributes-3:boolean-attribute), por exemplo `disabled`,`readonly`, `required`, `checked`,`hidden`, `selected`,`open` etc.
+
+**`.camel` modificador**
+**Exemplo:** `<svg x-bind:view-box.camel="viewBox">`
+
+O modificador `camel` se ligará ao equivalente em maiúsculas e minúsculas do nome do atributo. No exemplo acima, o valor de `viewBox` é definido ao atributo`viewBox` em oposição ao atributo `viewbox`.
+
+---
+
+### `x-on`
+
+> Nota: podemos usar a sintaxe "@" mais curta: `@click =" ... "
+
+**Exemplo:** `<button x-on:click="foo = 'bar'"></button>`
+
+**Estrutura:** `<button x-on:[event]="[expressão]"></button>`
+
+O `x-on` anexa um evento de escuta ao elemento em que está declarado. Quando esse evento é emitido, a expressão JavaScript definida como seu valor é executada.
+
+Caso algum dado for modificado na expressão, outros atributos do elemento "definidos" a esses dados serão atualizados.
+
+> Nota: Também podemos especificar um nome de função JavaScript
+
+**Exemplo:** `<button x-on:click="myFunction"></button>`
+
+O equivalente é: `<button x-on:click="myFunction($event)"></button>`
+
+**`keydown` modificadores**
+
+**Exemplo:** `<input type="text" x-on:keydown.escape="open = false">`
+
+Podemos especificar chaves específicas para escutar usando modificadores de keydown anexados à diretiva `x-on: keydown`. Observem que os modificadores são versões em kebab dos valores do `Event.key`.
+
+Exemplos: `enter`, `escape`, `arrow-up`, `arrow-down`
+
+> Nota: Também podemos ouvir a combinações de teclas do sistema como: `x-on:keydown.cmd.enter="foo"`
+
+**`.away` modificador**
+
+**Exemplo:** `<div x-on:click.away="showModal = false"></div>`
+
+Quando o modificador `.away` estiver presente, o evento handler é executado apenas quando o evento se originar de uma fonte que não seja ela própria ou seus filhos.
+
+Isso é útil para ocultar dropdowns e modals quando um utilizador clicar longe deles.
+
+**`.prevent` modificador**
+**Exemplo:** `<input type="checkbox" x-on:click.prevent>`
+
+Adicionar `.prevent` a um evento de escuta ira chamar o ` preventDefault` no evento acionado. No exemplo acima, isso significa que a caixa de seleção não é realmente verificada quando um utilizador clicar nela.
+
+**`.stop` modificador**
+**Exemplo:** `<div x-on:click="foo = 'bar'"><button x-on:click.stop></button></div>`
+
+Adicionar `.stop` a um evento de escuta ira chamar o ` stopPropagation` no evento acionado. No exemplo acima, isso significa que o evento "click" não borbulha do botão para o exterior `<div>`. Ou seja, quando um utilizador clicar no botão, `foo` não é definido como 'bar'.
+
+**`.self` modificador**
+**Exemplo:** `<div x-on:click.self="foo = 'bar'"><button></button></div>`
+
+Adicionar `.self` a um evento de escuta só vai acionar o handler quando o `$event.target` for o próprio elemento. No exemplo acima, isso significa que o evento "click" que borbulha do botão para a `<div>` externo **não** executa o handler.
+
+**`.window` modificador**
+**Exemplo:** `<div x-on:resize.window="isOpen = window.outerWidth > 768 ? false : open"></div>`
+
+Adicionar `.window` a um evento de escuta instalará a escutas no objeto na window global em vez do nó DOM no qual está declarado. Isso é útil para quando desejamos modificar o estado do componente quando algo muda com a window, como o evento de redimensionamento. Neste exemplo, quando a janela tiver mais de 768 pixels de largura, fechamos a modal/dropdown, caso contrário, manteremos o mesmo estado.
+
+> Nota: Também podemos usar o modificador `.document` para anexar escutas ao` document` em vez de `window`
+
+**`.once` modificador**
+**Exemplo:** `<button x-on:mouseenter.once="fetchSomething()"></button>`
+
+Adicionar o modificador `.once` a um evento de escuta vai garantir que a escuta seja tratado apenas uma vez. Isso é útil para coisas que desejamos fazer apenas uma vez, como ir procurar parciais HTML e outras coisas.
+
+**`.passive` modificador**
+**Exemplo:** `<button x-on:mousedown.passive="interactive = true"></button>`
+
+Adicionar o modificador `.passive` a um evento de escuta fará com que a escuta seja passiva, o que significa que o `preventDefault()` não vai funcionar em nenhum evento sendo processado, isso pode ajudar, por exemplo, com o desempenho do scroll em dispositivos touch.
+
+**`.debounce` modificador**
+**Exemplo:** `<input x-on:input.debounce="fetchSomething()">`
+
+O modificador `debounce` permite fazer "debounce" a um evento handler. Em outras palavras, o evento handler NÃO será executado até que tenha decorrido um certo tempo desde o último evento que foi disparado. Quando o handler estiver pronto para ser chamado, a última chamada do handler será executada.
+
+O tempo de espera de debounce padrão é de 250 milissegundos.
+
+Caso desejem personalizar isso, pode especificar um tempo de espera personalizado da seguinte maneira:
+
+```
+<input x-on:input.debounce.750="fetchSomething()">
+<input x-on:input.debounce.750ms="fetchSomething()">
+```
+
+**`.camel` modificador**
+**Exemplo:** `<input x-on:event-name.camel="doSomething()">`
+
+O modificador `camel` anexa um evento de escuta ao nome em camel case do evento equivalente. No exemplo acima, a expressão é avaliada quando o evento `eventName` for disparado no elemento
+
+---
+
+### `x-model`
+
+**Exemplo:** `<input type="text" x-model="foo">`
+
+**Estrutura:** `<input type="text" x-model="[data item]">`
+
+O `x-model` adiciona "ligação de dados bidirecional" a um elemento. Em outras palavras, o valor do elemento de entrada é mantido sincronizado com o valor do item de dados do componente.
+
+> Nota: `x-model` é inteligente o suficiente para detectar alterações nos inputs, checkboxes, radio buttons, textareas, selects e multiplo selects. Devem comportar-se [como o Vue] (https://vuejs.org/v2/guide/forms.html) nesses casos.
+
+**`.debounce` modificador**
+**Exemplo:** `<input x-model.debounce="search">`
+
+O modificador `debounce` permite adicionar um "debounce" a uma atualização de valor. Em outras palavras, o evento handler NÃO é executado até que tenha decorrido um certo tempo desde o último evento que foi disparado. Quando o handler estiver pronto para ser chamado, a última chamada do handler é executada.
+
+O tempo de espera de debounce padrão é de 250 milissegundos.
+
+Caso desejem personalizar isso, pode especificar um tempo de espera personalizado da seguinte maneira:
+
+```
+<input x-model.debounce.750="search">
+<input x-model.debounce.750ms="search">
+```
+
+---
+
+### `x-text`
+
+**Exemplo:** `<span x-text="foo"></span>`
+
+**Estrutura:** `<span x-text="[expressão]"`
+
+O `x-text` funciona da mesma forma que o` x-bind`, exceto que, em vez de atualizar o valor de um atributo, ele atualiza o `innerText` de um elemento.
+
+---
+
+### `x-html`
+
+**Exemplo:** `<span x-html="foo"></span>`
+
+**Estrutura:** `<span x-html="[expressão]"`
+
+O `x-html` funciona de maneira semelhante ao` x-bind`, exceto que, em vez de atualizar o valor de um atributo, ele atualiza o `innerHTML` de um elemento.
+
+> :warning: **Usar apenas em conteúdo de confiança e nunca em conteúdo fornecido pelo utilizador.** :warning:
+>
+> A renderização dinâmica do HTML de terceiros pode levar facilmente às vulnerabilidades de [XSS] (https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting).
+---
+
+### `x-ref`
+
+**Exemplo:** `<div x-ref="foo"></div><button x-on:click="$refs.foo.innerText = 'bar'"></button>`
+
+**Estrutura:** `<div x-ref="[ref name]"></div><button x-on:click="$refs.[ref name].innerText = 'bar'"></button>`
+
+O `x-ref` fornece uma maneira conveniente de recuperar elementos DOM fora do seu componente. Ao definir um atributo `x-ref` em um elemento, torna-o disponível para todos os eventos handlers dentro de um objeto chamando `$refs`.
+
+Esta é uma alternativa útil para definir ID's e usar o `document.querySelector` em todo o lago.
+
+> Nota: também podemos definir valores dinâmicos no x-ref: `<span: x-ref =" item.id "> </span>` se necessário.
+
+---
+
+### `x-if`
+
+**Exemplo:** `<template x-if="true"><div>Algum elemento</div></template>`
+
+**Estrutura:** `<template x-if="[expressão]"><div>Algum elemento</div></template>`
+
+Nos casos em que `x-show` não é suficiente (`x-show` define um elemento para `display: none` se for falso),`x-if` pode ser usado para remover um elemento completamente na DOM.
+
+É importante que o `x-if` seja usado em uma tag `<template> </template>` porque o Alpine não usa um DOM virtual. Essa implementação permite que o Alpine permaneça robusto e use o DOM real para fazer sua mágia.
+
+> Nota: `x-if` deve ter uma raiz de elemento único dentro da tag` <template> </template> `.
+
+---
+
+### `x-for`
+
+**Exemplo:**
+
+```html
+<template x-for="item in items" :key="item">
+    <div x-text="item"></div>
+</template>
+```
+
+> Nota: a ligação `:key` é opcional, mas ALTAMENTE recomendada.
+
+O `x-for` está disponível para casos em que desejem criar novos nós DOM para cada item em uma matriz. Isso deve parecer semelhante ao `v-for` no Vue, com uma exceção da necessidade de existir em uma tag`template`, e não em um elemento DOM comum.
+
+Caso desejem aceder ao índice atual da iteração, usem a seguinte sintaxe:
+
+```html
+<template x-for="(item, index) in items" :key="index">
+    <!-- You can also reference "index" inside the iteration if you need. -->
+    <div x-text="index"></div>
+</template>
+```
+
+> Nota: `x-for` deve ter uma raiz de elemento único dentro da tag`<template> </template>`.
+
+#### Encadeamento de `x-for`s
+
+Podemos ter encadeamento de ciclos `x-for`, mas DEVEMOS envolver cada ciclo em um elemento. Por exemplo:
+
+```html
+<template x-for="item in items">
+    <div>
+        <template x-for="subItem in item.subItems">
+            <div x-text="subItem"></div>
+        </template>
+    </div>
+</template>
+```
+
+---
+
+### `x-transition`
+
+**Exemplo:**
+
+```html
+<div
+    x-show="open"
+    x-transition:enter="transition ease-out duration-300"
+    x-transition:enter-start="opacity-0 transform scale-90"
+    x-transition:enter-end="opacity-100 transform scale-100"
+    x-transition:leave="transition ease-in duration-300"
+    x-transition:leave-start="opacity-100 transform scale-100"
+    x-transition:leave-end="opacity-0 transform scale-90"
+>
+    ...
+</div>
+```
+
+```html
+<template x-if="open">
+    <div
+        x-transition:enter="transition ease-out duration-300"
+        x-transition:enter-start="opacity-0 transform scale-90"
+        x-transition:enter-end="opacity-100 transform scale-100"
+        x-transition:leave="transition ease-in duration-300"
+        x-transition:leave-start="opacity-100 transform scale-100"
+        x-transition:leave-end="opacity-0 transform scale-90"
+    >
+        ...
+    </div>
+</template>
+```
+
+> O exemplo acima usa classes de [Tailwind CSS](https://tailwindcss.com)
+
+Alpine oferece 6 diretivas de transição diferentes para aplicar classes a vários estágios da transição de um elemento entre os estados "oculto" e "mostrado". Essas diretivas funcionam tanto com `x-show` E`x-if`.
+
+Elas se comportam exatamente como as diretivas de transição do VueJs, exceto que têm nomes diferentes e mais sensíveis:
+
+| Directiva      | Descrição                                                                                                                                            |
+| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `:enter`       | Aplicado durante toda a fase de entrada.                                                                                                             |
+| `:enter-start` | Adicionado antes que o elemento seja inserido, removido um frame após o elemento ser inserido.                                                                       |
+| `:enter-end`   | Adicionado um frame após a inserção do elemento (ao mesmo tempo em que o `enter-start` é removido), removido quando a transição/animação termina.                   |
+| `:leave`       | Aplicado durante toda a fase de partida.                                                                                                             |
+| `:leave-start` | Adicionado imediatamente quando uma transição de saída é acionada, removida após um frame.                                                                   |
+| `:leave-end`   | Adicionado um frame depois que uma transição de saída é acionada (ao mesmo tempo em que o `leave-start` é removido), removido quando a transição/animação termina. |
+
+---
+
+### `x-spread`
+
+**Exemplo:**
+
+```html
+<div x-data="dropdown()">
+    <button x-spread="trigger">Dropdown Aberto</button>
+
+    <span x-spread="dialogue">Conteúdo da Dropdown</span>
+</div>
+
+<script>
+    function dropdown() {
+        return {
+            open: false,
+            trigger: {
+                ['@click']() {
+                    this.open = true;
+                },
+            },
+            dialogue: {
+                ['x-show']() {
+                    return this.open;
+                },
+                ['@click.away']() {
+                    this.open = false;
+                },
+            },
+        };
+    }
+</script>
+```
+
+O `x-spread` permite extrair as ligações de um elemento Alpine em um objeto reutilizável.
+
+As chaves do objeto são as diretivas (pode ser qualquer diretiva, incluindo modificadores), e os valores são callback's a serem avaliados pelo Alpine.
+
+> Nota: A única anomalia com propagação x é quando usada com `x-for`. Quando a diretiva "spread" é `x-for`, devemos retornar uma string de expressão normal a partir de um callback. Por exemplo: `['x-for'] () {return 'item in items'}`.
+---
+
+### `x-cloak`
+
+**Exemplo:** `<div x-data="{}" x-cloak></div>`
+
+Os atributos `x-cloak` são removidos dos elementos quando o Alpine é inicializado. Isso é útil para ocultar o DOM pré-inicializado. É típico adicionar o seguinte estilo global para que isso funcione:
+
+```html
+<style>
+    [x-cloak] {
+        display: none;
+    }
+</style>
+```
+
+### Propriedades Mágicas
+
+> Com exceção de `$el`, as propriedades mágicas **não estão disponíveis no` x-data`**, pois o componente ainda não foi inicializado.
+---
+
+### `$el`
+
+**Exemplo:**
+
+```html
+<div x-data>
+    <button @click="$el.innerHTML = 'foo'">Substitua-me por "foo"</button>
+</div>
+```
+
+`$el` é uma propriedade mágica que pode ser usada para recuperar o nó DOM do componente raiz.
+
+### `$refs`
+
+**Exemplo:**
+
+```html
+<span x-ref="foo"></span>
+
+<button x-on:click="$refs.foo.innerText = 'bar'"></button>
+```
+
+`$refs` é uma propriedade mágica que pode ser usada para recuperar elementos DOM marcados com `x-ref` dentro do componente. Isso é útil quando necessitamos manipular manualmente os elementos na DOM.
+
+---
+
+### `$event`
+
+**Exemplo:**
+
+```html
+<input x-on:input="alert($event.target.value)" />
+```
+
+`$event` é uma propriedade mágica que pode ser usada dentro de um evento de escuta para recuperar o objeto "Event" do browser nativo.
+
+> Nota: A propriedade $event está disponível apenas nas expressões DOM.
+
+Caso necessitem aceder ao $event dentro de uma função JavaScript, podemos passa-lo diretamente:
+
+`<button x-on:click="myFunction($event)"></button>`
+
+---
+
+### `$dispatch`
+
+**Exemplo:**
+
+```html
+<div @custom-event="console.log($event.detail.foo)">
+    <button @click="$dispatch('custom-event', { foo: 'bar' })">
+    <!-- Quando clicado, console.log "bar" ->
+</div>
+```
+
+**Nota sobre a propagação de eventos**
+
+Observem que, devido ao [evento com bolhas](https://en.wikipedia.org/wiki/Event_bubbling), quando for preciso capturar eventos enviados pelos nós que estão sob a mesma hierarquia de encadeamento, usem o modificador [`.window`](https://github.com/alpinejs/alpine#x-on):
+
+**Exemplo:**
+
+```html
+<div x-data>
+    <span @custom-event="console.log($event.detail.foo)"></span>
+    <button @click="$dispatch('custom-event', { foo: 'bar' })">
+        <div></div>
+    </button>
+</div>
+```
+
+> Isso não vai funcionar porque, quando o `custom-event` for executado, ele é propagado para seu ancestral comum, a `div`.
+
+**Expedição para componentes**
+
+Também podemos tirar proveito da técnica anterior para fazer os componentes comunicarem entre si:
+
+**Exemplo:**
+
+```html
+<div x-data @custom-event.window="console.log($event.detail)"></div>
+
+<button x-data @click="$dispatch('custom-event', 'Olá Mundo!')">
+    <!-- Quando clicado, o console.log "Olá Mundo!". -->
+</button>
+```
+
+`$dispatch` é um atalho para criar um`CustomEvent` e enviá-lo internamente usando `.dispatchEvent ()`. Existem muitos casos de uso bons para transmitir dados entre componentes usando eventos personalizados. [Leia aqui](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events) para obter mais informações sobre o sistema subjacente `CustomEvent` nos browsers.
+
+Notarão que todos os dados passados como o segundo parâmetro para `$dispatch('some-event', {some: 'data'})` ficam disponíveis através da nova propriedade "detail" de eventos: `$event.detail.some`. Anexar dados de eventos personalizados à propriedade `.detail` é uma prática padrão para o `CustomEvent`s nos browsers. [Leia aqui](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) para obter mais informações.
+
+Também podemos usar `$dispatch()` para acionar atualizações de dados para ligações `x-model`. Por exemplo:
+
+```html
+<div x-data="{ foo: 'bar' }">
+    <span x-model="foo">
+        <button @click="$dispatch('input', 'baz')">
+        <!-- Depois que o botão é clicado, o `x-model` irá capturar o evento "input" e atualizar foo para "baz". -->
+    </span>
+</div>
+```
+
+> Nota: A propriedade $dispatch está disponível apenas nas expressões DOM.
+
+Caso necessitem aceder ao $dispatch dentro de uma função JavaScript, poderão transmiti-la diretamente:
+
+`<button x-on:click="myFunction($dispatch)"></button>`
+
+---
+
+### `$nextTick`
+
+**Exemplo:**
+
+```html
+<div x-data="{ fruit: 'apple' }">
+    <button
+        x-on:click="
+            fruit = 'pear';
+            $nextTick(() => { console.log($event.target.innerText) });
+        "
+        x-text="fruit"
+    ></button>
+</div>
+```
+
+`$ nextTick` é uma propriedade mágica que permite executar apenas uma determinada expressão APÓS o Alpine fazer suas atualizações a DOM. Isso é útil nos momentos em que desejam interagir com o estado da DOM, após refletir as atualizações de dados que fizemos.
+
+---
+
+### `$watch`
+
+**Exemplo:**
+
+```html
+<div
+    x-data="{ open: false }"
+    x-init="$watch('open', value => console.log(value))"
+>
+    <button @click="open = ! open">Alternar Abrir</button>
+</div>
+```
+
+Podemos "assistir" uma propriedade de componente com o método mágico `$watch`. No exemplo acima, quando o botão é clicado e o valor do `open` é alterado, e o callback fornecida é executada e o novo valor mostrado num `console.log`.
+
+## Segurança
+
+Caso encontrarem uma vulnerabilidade de segurança, envie um email para [calebporzio@gmail.com](mailto:calebporzio@gmail.com)
+
+O Alpine conta com uma implementação personalizada usando o objeto `Function` para avaliar suas diretivas. Apesar de ser mais seguro que o `eval()`, o seu uso é proibido em alguns ambientes, como o Google Chrome App, usando a Política de Segurança de Conteúdo restritiva (CSP).
+
+Caso usem o Alpine em uma página web que lida com dados confidenciais e exige [CSP](https://csp.withgoogle.com/docs/strict-csp.html), necessitam incluir `unsafe-eval` na sua política. Uma política robusta configurada corretamente ajudará a proteger os utilizadores ao usar dados pessoais ou financeiros.
+
+Como uma política se aplica a todos os scripts da sua página, é importante que outras bibliotecas externas incluídas na página web estejam cuidadosamente revisadas para garantir que sejam confiáveis e não apresentem nenhuma vulnerabilidade de Cross Site Scripting usando a função `eval()` ou manipular o DOM para injetar código malicioso na sua página.
+
+## Licença
+
+Copyright © 2019-2020 Caleb Porzio e colaboradores
+
+Licenciado sob a licença MIT, consulte [LICENSE.md](LICENSE.md) para obter detalhes.

+ 387 - 388
dist/alpine-ie11.js

@@ -23,11 +23,10 @@
   	return module = { exports: {} }, fn(module, module.exports), module.exports;
   }
 
-  (function(){function n(){function v(){return null}function l(a){return a?"object"===typeof a||"function"===typeof a:!1}function p(a){if(null!==a&&!l(a))throw new TypeError("Object prototype may only be an Object or null: "+a);}var q=null,e=Object,w=!!e.create||!({__proto__:null}instanceof e),A=e.create||(w?function(a){p(a);return {__proto__:a}}:function(a){function c(){}p(a);if(null===a)throw new SyntaxError("Native Object.create is required to create objects with null prototype");c.prototype=a;return new c}),
-  B=e.getPrototypeOf||([].__proto__===Array.prototype?function(a){a=a.__proto__;return l(a)?a:null}:v);var m=function(a,c){function k(){}if(void 0===(this&&this instanceof m?this.constructor:void 0))throw new TypeError("Constructor Proxy requires 'new'");if(!l(a)||!l(c))throw new TypeError("Cannot create proxy with a non-object as target or handler");q=function(){a=null;k=function(b){throw new TypeError("Cannot perform '"+b+"' on a proxy that has been revoked");};};setTimeout(function(){q=null;},0);var g=
-  c;c={get:null,set:null,apply:null,construct:null};for(var h in g){if(!(h in c))throw new TypeError("Proxy polyfill does not support trap '"+h+"'");c[h]=g[h];}"function"===typeof g&&(c.apply=g.apply.bind(g));g=B(a);var r=!1,t=!1;if("function"===typeof a){var f=function(){var b=this&&this.constructor===f,d=Array.prototype.slice.call(arguments);k(b?"construct":"apply");return b&&c.construct?c.construct.call(this,a,d):!b&&c.apply?c.apply(a,this,d):b?(d.unshift(a),new (a.bind.apply(a,d))):a.apply(this,
-  d)};r=!0;}else a instanceof Array?(f=[],t=!0):f=w||null!==g?A(g):{};var x=c.get?function(b){k("get");return c.get(this,b,f)}:function(b){k("get");return this[b]},C=c.set?function(b,d){k("set");c.set(this,b,d,f);}:function(b,d){k("set");this[b]=d;},y={};e.getOwnPropertyNames(a).forEach(function(b){if(!((r||t)&&b in f)){var d=e.getOwnPropertyDescriptor(a,b);e.defineProperty(f,b,{enumerable:!!d.enumerable,get:x.bind(a,b),set:C.bind(a,b)});y[b]=!0;}});h=!0;if(r||t){var D=e.setPrototypeOf||([].__proto__===
-  Array.prototype?function(b,d){p(d);b.__proto__=d;return b}:v);g&&D(f,g)||(h=!1);}if(c.get||!h)for(var u in a)y[u]||e.defineProperty(f,u,{get:x.bind(a,u)});e.seal(a);e.seal(f);return f};m.revocable=function(a,c){return {proxy:new m(a,c),revoke:q}};return m}var z="undefined"!==typeof process&&"[object process]"==={}.toString.call(process)||"undefined"!==typeof navigator&&"ReactNative"===navigator.product?commonjsGlobal:self;z.Proxy||(z.Proxy=n(),z.Proxy.revocable=z.Proxy.revocable);})();
+  (function(){function k(){function p(a){return a?"object"===typeof a||"function"===typeof a:!1}var l=null;var n=function(a,c){function g(){}if(!p(a)||!p(c))throw new TypeError("Cannot create proxy with a non-object as target or handler");l=function(){a=null;g=function(b){throw new TypeError("Cannot perform '"+b+"' on a proxy that has been revoked");};};setTimeout(function(){l=null;},0);var f=c;c={get:null,set:null,apply:null,construct:null};for(var h in f){if(!(h in c))throw new TypeError("Proxy polyfill does not support trap '"+
+  h+"'");c[h]=f[h];}"function"===typeof f&&(c.apply=f.apply.bind(f));var d=this,q=!1,r=!1;"function"===typeof a?(d=function(){var b=this&&this.constructor===d,e=Array.prototype.slice.call(arguments);g(b?"construct":"apply");return b&&c.construct?c.construct.call(this,a,e):!b&&c.apply?c.apply(a,this,e):b?(e.unshift(a),new (a.bind.apply(a,e))):a.apply(this,e)},q=!0):a instanceof Array&&(d=[],r=!0);var t=c.get?function(b){g("get");return c.get(this,b,d)}:function(b){g("get");return this[b]},w=c.set?function(b,
+  e){g("set");c.set(this,b,e,d);}:function(b,e){g("set");this[b]=e;},u={};Object.getOwnPropertyNames(a).forEach(function(b){if(!((q||r)&&b in d)){var e={enumerable:!!Object.getOwnPropertyDescriptor(a,b).enumerable,get:t.bind(a,b),set:w.bind(a,b)};Object.defineProperty(d,b,e);u[b]=!0;}});f=!0;Object.setPrototypeOf?Object.setPrototypeOf(d,Object.getPrototypeOf(a)):d.__proto__?d.__proto__=a.__proto__:f=!1;if(c.get||!f)for(var m in a)u[m]||Object.defineProperty(d,m,{get:t.bind(a,m)});Object.seal(a);Object.seal(d);
+  return d};n.revocable=function(a,c){return {proxy:new n(a,c),revoke:l}};return n}var v="undefined"!==typeof process&&"[object process]"==={}.toString.call(process)||"undefined"!==typeof navigator&&"ReactNative"===navigator.product?commonjsGlobal:self;v.Proxy||(v.Proxy=k(),v.Proxy.revocable=v.Proxy.revocable);})();
 
   !function(e){var t=e.Element.prototype;"function"!=typeof t.matches&&(t.matches=t.msMatchesSelector||t.mozMatchesSelector||t.webkitMatchesSelector||function(e){for(var t=(this.document||this.ownerDocument).querySelectorAll(e),o=0;t[o]&&t[o]!==this;)++o;return Boolean(t[o])}),"function"!=typeof t.closest&&(t.closest=function(e){for(var t=this;t&&1===t.nodeType;){if(t.matches(e))return t;t=t.parentNode;}return null});}(window);
 
@@ -885,394 +884,394 @@
 
   })();
 
-  var ApplyThisPrototype = (function() {
-    return function ApplyThisPrototype(event, target) {
-      if ((typeof target === 'object') && (target !== null)) {
-        var proto = Object.getPrototypeOf(target);
-        var property;
-
-        for (property in proto) {
-          if (!(property in event)) {
-            var descriptor = Object.getOwnPropertyDescriptor(proto, property);
-            if (descriptor) {
-              Object.defineProperty(event, property, descriptor);
-            }
-          }
-        }
-
-        for (property in target) {
-          if (!(property in event)) {
-            event[property] = target[property];
-          }
-        }
-      }
-    }
+  var ApplyThisPrototype = (function() {
+    return function ApplyThisPrototype(event, target) {
+      if ((typeof target === 'object') && (target !== null)) {
+        var proto = Object.getPrototypeOf(target);
+        var property;
+
+        for (property in proto) {
+          if (!(property in event)) {
+            var descriptor = Object.getOwnPropertyDescriptor(proto, property);
+            if (descriptor) {
+              Object.defineProperty(event, property, descriptor);
+            }
+          }
+        }
+
+        for (property in target) {
+          if (!(property in event)) {
+            event[property] = target[property];
+          }
+        }
+      }
+    }
   })();
 
-  (function(ApplyThisPrototype) {
-    /**
-     * Polyfill CustomEvent
-     */
-    try {
-      var event = new window.CustomEvent('event', { bubbles: true, cancelable: true });
-    } catch (error) {
-      var CustomEventOriginal = window.CustomEvent || window.Event;
-      var CustomEvent = function(eventName, params) {
-        params = params || {};
-        var event = document.createEvent('CustomEvent');
-        event.initCustomEvent(
-          eventName,
-          (params.bubbles === void 0) ? false : params.bubbles,
-          (params.cancelable === void 0) ? false : params.cancelable,
-          (params.detail === void 0) ? {} : params.detail
-        );
-        ApplyThisPrototype(event, this);
-        return event;
-      };
-      CustomEvent.prototype = CustomEventOriginal.prototype;
-      window.CustomEvent = CustomEvent;
-    }
+  (function(ApplyThisPrototype) {
+    /**
+     * Polyfill CustomEvent
+     */
+    try {
+      var event = new window.CustomEvent('event', { bubbles: true, cancelable: true });
+    } catch (error) {
+      var CustomEventOriginal = window.CustomEvent || window.Event;
+      var CustomEvent = function(eventName, params) {
+        params = params || {};
+        var event = document.createEvent('CustomEvent');
+        event.initCustomEvent(
+          eventName,
+          (params.bubbles === void 0) ? false : params.bubbles,
+          (params.cancelable === void 0) ? false : params.cancelable,
+          (params.detail === void 0) ? {} : params.detail
+        );
+        ApplyThisPrototype(event, this);
+        return event;
+      };
+      CustomEvent.prototype = CustomEventOriginal.prototype;
+      window.CustomEvent = CustomEvent;
+    }
   })(ApplyThisPrototype);
 
-  var EventListenerInterceptor = (function() {
-
-    if(typeof EventTarget === 'undefined') {
-      window.EventTarget = Node;
-    }
-
-    /**
-     * Event listener interceptor
-     */
-
-    var EventListenerInterceptor = {
-      interceptors: [] // { target: EventTarget, interceptors: [{ add: Function, remove: Function }, ...] }
-    };
-
-
-    /**
-     * Returns if exists a previously registered listener from a target and the normalized arguments
-     * @param target
-     * @param normalizedArguments
-     * @return {*}
-     */
-    EventListenerInterceptor.getRegisteredEventListener = function(target, normalizedArguments) {
-      var key = normalizedArguments.type + '-' + (normalizedArguments.options.capture ? '1' : '0');
-      if(
-        (target.__eventListeners !== void 0) &&
-        (target.__eventListeners[key] !== void 0)
-      ) {
-        var map = target.__eventListeners[key];
-        for(var i = 0; i < map.length; i++) {
-          if(map[i].listener === normalizedArguments.listener) {
-            return map[i];
-          }
-        }
-      }
-      return null;
-    };
-
-    /**
-     * Registers a listener on a target with some options
-     * @param target
-     * @param normalizedArguments
-     */
-    EventListenerInterceptor.registerEventListener = function(target, normalizedArguments) {
-      var key = normalizedArguments.type + '-' + (normalizedArguments.options.capture ? '1' : '0');
-
-      if(target.__eventListeners === void 0) {
-        target.__eventListeners = {};
-      }
-
-      if(target.__eventListeners[key] === void 0) {
-        target.__eventListeners[key] = [];
-      }
-
-      target.__eventListeners[key].push(normalizedArguments);
-    };
-
-    /**
-     * Unregisters a listener on a target with some options
-     * @param target
-     * @param normalizedArguments
-     */
-    EventListenerInterceptor.unregisterEventListener = function(target, normalizedArguments) {
-      var key = normalizedArguments.type + '-' + (normalizedArguments.options.capture ? '1' : '0');
-      if(
-        (target.__eventListeners !==  void 0) &&
-        (target.__eventListeners[key] !== void 0)
-      ) {
-        var map = target.__eventListeners[key];
-        for(var i = 0; i < map.length; i++) {
-          if(map[i].listener === normalizedArguments.listener) {
-            map.splice(i, 1);
-          }
-        }
-
-        if(map.length === 0) {
-          delete target.__eventListeners[key];
-        }
-      }
-    };
-
-
-
-    EventListenerInterceptor.normalizeListenerCallback = function(listener) {
-      if((typeof listener === 'function') || (listener === null) || (listener === void 0)) {
-        return listener;
-      } else if((typeof listener === 'object') && (typeof listener.handleEvent === 'function')) {
-        return listener.handleEvent;
-      } else {
-        // to support Symbol
-        return function(event) {
-          listener(event);
-        };
-      }
-    };
-
-    EventListenerInterceptor.normalizeListenerOptions = function(options) {
-      switch(typeof options) {
-        case 'boolean':
-          options = { capture: options };
-          break;
-        case 'undefined':
-          options = { capture: false };
-          break;
-        case 'object':
-          if (options === null) {
-            options = { capture: false };
-          }
-          break;
-        default:
-          throw new Error('Unsupported options type for addEventListener');
-      }
-
-      options.once      = Boolean(options.once);
-      options.passive   = Boolean(options.passive);
-      options.capture   = Boolean(options.capture);
-
-      return options;
-    };
-
-    EventListenerInterceptor.normalizeListenerArguments = function(type, listener, options) {
-      return {
-        type: type,
-        listener: this.normalizeListenerCallback(listener),
-        options: this.normalizeListenerOptions(options)
-      };
-    };
-
-
-
-    EventListenerInterceptor.intercept = function(target, interceptors) {
-      // get an interceptor with this target or null
-      var interceptor = null;
-      for (var i = 0; i < this.interceptors.length; i++) {
-        if(this.interceptors[i].target === target) {
-          interceptor = this.interceptors[i];
-        }
-      }
-
-      // if no interceptor already set
-      if (interceptor === null) {
-        interceptor = { target: target, interceptors: [interceptors] };
-        this.interceptors.push(interceptor);
-
-        this.interceptAddEventListener(target, interceptor);
-        this.interceptRemoveEventListener(target, interceptor);
-      } else { // if an interceptor already set, simply add interceptors to the list
-        interceptor.interceptors.push(interceptors);
-      }
-
-      // var release = function() {
-      //   target.prototype.addEventListener = addEventListener;
-      //   target.prototype.removeEventListener = removeEventListener;
-      // };
-      // this.interceptors.push(release);
-      // return release;
-    };
-
-    EventListenerInterceptor.interceptAddEventListener = function(target, interceptor) {
-      var _this = this;
-
-      var addEventListener = target.prototype.addEventListener;
-      target.prototype.addEventListener = function(type, listener, options) {
-        var normalizedArguments = _this.normalizeListenerArguments(type, listener, options);
-        var registeredEventListener = _this.getRegisteredEventListener(this, normalizedArguments);
-
-        if (!registeredEventListener) {
-
-          normalizedArguments.polyfilled = {
-            type: normalizedArguments.type,
-            listener: normalizedArguments.listener,
-            options: {
-              capture: normalizedArguments.options.capture,
-              once: normalizedArguments.options.once,
-              passive: normalizedArguments.options.passive
-            }
-          };
-
-          for (var i = 0; i < interceptor.interceptors.length; i++) {
-            var interceptors = interceptor.interceptors[i];
-            if (typeof interceptors.add === 'function') {
-              interceptors.add(normalizedArguments);
-            }
-          }
-
-          // console.log('normalizedArguments', normalizedArguments.polyfilled);
-
-          _this.registerEventListener(this, normalizedArguments);
-
-          addEventListener.call(
-            this,
-            normalizedArguments.polyfilled.type,
-            normalizedArguments.polyfilled.listener,
-            normalizedArguments.polyfilled.options
-          );
-        }
-      };
-
-      return function() {
-        target.prototype.addEventListener = addEventListener;
-      };
-    };
-
-    EventListenerInterceptor.interceptRemoveEventListener = function(target, interceptor) {
-      var _this = this;
-
-      var removeEventListener = target.prototype.removeEventListener;
-      target.prototype.removeEventListener = function(type, listener, options) {
-        var normalizedArguments = _this.normalizeListenerArguments(type, listener, options);
-        var registeredEventListener = _this.getRegisteredEventListener(this, normalizedArguments);
-
-        if (registeredEventListener) {
-          _this.unregisterEventListener(this, normalizedArguments);
-          removeEventListener.call(
-            this,
-            registeredEventListener.polyfilled.type,
-            registeredEventListener.polyfilled.listener,
-            registeredEventListener.polyfilled.options
-          );
-        } else {
-          removeEventListener.call(this, type, listener, options);
-        }
-      };
-
-      return function() {
-        target.prototype.removeEventListener = removeEventListener;
-      };
-    };
-
-    EventListenerInterceptor.interceptAll = function(interceptors) {
-      this.intercept(EventTarget, interceptors);
-      if(!(window instanceof EventTarget)) {
-        this.intercept(Window, interceptors);
-      }
-    };
-
-    EventListenerInterceptor.releaseAll = function() {
-      for(var i = 0, l = this.interceptors.length; i < l; i++) {
-        this.interceptors();
-      }
-    };
-
-
-    EventListenerInterceptor.error = function(error) {
-      // throw error;
-      console.error(error);
-    };
-
-    return EventListenerInterceptor;
+  var EventListenerInterceptor = (function() {
+
+    if(typeof EventTarget === 'undefined') {
+      window.EventTarget = Node;
+    }
+
+    /**
+     * Event listener interceptor
+     */
+
+    var EventListenerInterceptor = {
+      interceptors: [] // { target: EventTarget, interceptors: [{ add: Function, remove: Function }, ...] }
+    };
+
+
+    /**
+     * Returns if exists a previously registered listener from a target and the normalized arguments
+     * @param target
+     * @param normalizedArguments
+     * @return {*}
+     */
+    EventListenerInterceptor.getRegisteredEventListener = function(target, normalizedArguments) {
+      var key = normalizedArguments.type + '-' + (normalizedArguments.options.capture ? '1' : '0');
+      if(
+        (target.__eventListeners !== void 0) &&
+        (target.__eventListeners[key] !== void 0)
+      ) {
+        var map = target.__eventListeners[key];
+        for(var i = 0; i < map.length; i++) {
+          if(map[i].listener === normalizedArguments.listener) {
+            return map[i];
+          }
+        }
+      }
+      return null;
+    };
+
+    /**
+     * Registers a listener on a target with some options
+     * @param target
+     * @param normalizedArguments
+     */
+    EventListenerInterceptor.registerEventListener = function(target, normalizedArguments) {
+      var key = normalizedArguments.type + '-' + (normalizedArguments.options.capture ? '1' : '0');
+
+      if(target.__eventListeners === void 0) {
+        target.__eventListeners = {};
+      }
+
+      if(target.__eventListeners[key] === void 0) {
+        target.__eventListeners[key] = [];
+      }
+
+      target.__eventListeners[key].push(normalizedArguments);
+    };
+
+    /**
+     * Unregisters a listener on a target with some options
+     * @param target
+     * @param normalizedArguments
+     */
+    EventListenerInterceptor.unregisterEventListener = function(target, normalizedArguments) {
+      var key = normalizedArguments.type + '-' + (normalizedArguments.options.capture ? '1' : '0');
+      if(
+        (target.__eventListeners !==  void 0) &&
+        (target.__eventListeners[key] !== void 0)
+      ) {
+        var map = target.__eventListeners[key];
+        for(var i = 0; i < map.length; i++) {
+          if(map[i].listener === normalizedArguments.listener) {
+            map.splice(i, 1);
+          }
+        }
+
+        if(map.length === 0) {
+          delete target.__eventListeners[key];
+        }
+      }
+    };
+
+
+
+    EventListenerInterceptor.normalizeListenerCallback = function(listener) {
+      if((typeof listener === 'function') || (listener === null) || (listener === void 0)) {
+        return listener;
+      } else if((typeof listener === 'object') && (typeof listener.handleEvent === 'function')) {
+        return listener.handleEvent;
+      } else {
+        // to support Symbol
+        return function(event) {
+          listener(event);
+        };
+      }
+    };
+
+    EventListenerInterceptor.normalizeListenerOptions = function(options) {
+      switch(typeof options) {
+        case 'boolean':
+          options = { capture: options };
+          break;
+        case 'undefined':
+          options = { capture: false };
+          break;
+        case 'object':
+          if (options === null) {
+            options = { capture: false };
+          }
+          break;
+        default:
+          throw new Error('Unsupported options type for addEventListener');
+      }
+
+      options.once      = Boolean(options.once);
+      options.passive   = Boolean(options.passive);
+      options.capture   = Boolean(options.capture);
+
+      return options;
+    };
+
+    EventListenerInterceptor.normalizeListenerArguments = function(type, listener, options) {
+      return {
+        type: type,
+        listener: this.normalizeListenerCallback(listener),
+        options: this.normalizeListenerOptions(options)
+      };
+    };
+
+
+
+    EventListenerInterceptor.intercept = function(target, interceptors) {
+      // get an interceptor with this target or null
+      var interceptor = null;
+      for (var i = 0; i < this.interceptors.length; i++) {
+        if(this.interceptors[i].target === target) {
+          interceptor = this.interceptors[i];
+        }
+      }
+
+      // if no interceptor already set
+      if (interceptor === null) {
+        interceptor = { target: target, interceptors: [interceptors] };
+        this.interceptors.push(interceptor);
+
+        this.interceptAddEventListener(target, interceptor);
+        this.interceptRemoveEventListener(target, interceptor);
+      } else { // if an interceptor already set, simply add interceptors to the list
+        interceptor.interceptors.push(interceptors);
+      }
+
+      // var release = function() {
+      //   target.prototype.addEventListener = addEventListener;
+      //   target.prototype.removeEventListener = removeEventListener;
+      // };
+      // this.interceptors.push(release);
+      // return release;
+    };
+
+    EventListenerInterceptor.interceptAddEventListener = function(target, interceptor) {
+      var _this = this;
+
+      var addEventListener = target.prototype.addEventListener;
+      target.prototype.addEventListener = function(type, listener, options) {
+        var normalizedArguments = _this.normalizeListenerArguments(type, listener, options);
+        var registeredEventListener = _this.getRegisteredEventListener(this, normalizedArguments);
+
+        if (!registeredEventListener) {
+
+          normalizedArguments.polyfilled = {
+            type: normalizedArguments.type,
+            listener: normalizedArguments.listener,
+            options: {
+              capture: normalizedArguments.options.capture,
+              once: normalizedArguments.options.once,
+              passive: normalizedArguments.options.passive
+            }
+          };
+
+          for (var i = 0; i < interceptor.interceptors.length; i++) {
+            var interceptors = interceptor.interceptors[i];
+            if (typeof interceptors.add === 'function') {
+              interceptors.add(normalizedArguments);
+            }
+          }
+
+          // console.log('normalizedArguments', normalizedArguments.polyfilled);
+
+          _this.registerEventListener(this, normalizedArguments);
+
+          addEventListener.call(
+            this,
+            normalizedArguments.polyfilled.type,
+            normalizedArguments.polyfilled.listener,
+            normalizedArguments.polyfilled.options
+          );
+        }
+      };
+
+      return function() {
+        target.prototype.addEventListener = addEventListener;
+      };
+    };
+
+    EventListenerInterceptor.interceptRemoveEventListener = function(target, interceptor) {
+      var _this = this;
+
+      var removeEventListener = target.prototype.removeEventListener;
+      target.prototype.removeEventListener = function(type, listener, options) {
+        var normalizedArguments = _this.normalizeListenerArguments(type, listener, options);
+        var registeredEventListener = _this.getRegisteredEventListener(this, normalizedArguments);
+
+        if (registeredEventListener) {
+          _this.unregisterEventListener(this, normalizedArguments);
+          removeEventListener.call(
+            this,
+            registeredEventListener.polyfilled.type,
+            registeredEventListener.polyfilled.listener,
+            registeredEventListener.polyfilled.options
+          );
+        } else {
+          removeEventListener.call(this, type, listener, options);
+        }
+      };
+
+      return function() {
+        target.prototype.removeEventListener = removeEventListener;
+      };
+    };
+
+    EventListenerInterceptor.interceptAll = function(interceptors) {
+      this.intercept(EventTarget, interceptors);
+      if(!(window instanceof EventTarget)) {
+        this.intercept(Window, interceptors);
+      }
+    };
+
+    EventListenerInterceptor.releaseAll = function() {
+      for(var i = 0, l = this.interceptors.length; i < l; i++) {
+        this.interceptors();
+      }
+    };
+
+
+    EventListenerInterceptor.error = function(error) {
+      // throw error;
+      console.error(error);
+    };
+
+    return EventListenerInterceptor;
   })();
 
-  (function(EventListenerInterceptor) {
-    /**
-     * Event listener options support
-     */
-
-    EventListenerInterceptor.detectSupportedOptions = function() {
-      var _this = this;
-
-      this.supportedOptions = {
-        once: false,
-        passive: false,
-        capture: false,
-
-        all: false,
-        some: false
-      };
-
-      document.createDocumentFragment().addEventListener('test', function() {}, {
-        get once() {
-          _this.supportedOptions.once = true;
-          return false;
-        },
-        get passive() {
-          _this.supportedOptions.passive = true;
-          return false;
-        },
-        get capture() {
-          _this.supportedOptions.capture = true;
-          return false;
-        }
-      });
-
-      // useful shortcuts to detect if options are all/some supported
-      this.supportedOptions.all  = this.supportedOptions.once && this.supportedOptions.passive && this.supportedOptions.capture;
-      this.supportedOptions.some = this.supportedOptions.once || this.supportedOptions.passive || this.supportedOptions.capture;
-    };
-
-    EventListenerInterceptor.polyfillListenerOptions = function() {
-      this.detectSupportedOptions();
-      if (!this.supportedOptions.all) {
-        var _this = this;
-
-        this.interceptAll({
-          add: function(normalizedArguments) {
-            // console.log('intercepted', normalizedArguments);
-
-            var once = normalizedArguments.options.once && !_this.supportedOptions.once;
-            var passive = normalizedArguments.options.passive && !_this.supportedOptions.passive;
-
-            if (once || passive) {
-              var listener = normalizedArguments.polyfilled.listener;
-
-              normalizedArguments.polyfilled.listener = function(event) {
-                if(once) {
-                  this.removeEventListener(normalizedArguments.type, normalizedArguments.listener, normalizedArguments.options);
-                }
-
-                if(passive) {
-                  event.preventDefault = function() {
-                    throw new Error('Unable to preventDefault inside passive event listener invocation.');
-                  };
-                }
-
-                return listener.call(this, event);
-              };
-            }
-
-            if (!_this.supportedOptions.some) {
-              normalizedArguments.polyfilled.options = normalizedArguments.options.capture;
-            }
-          }
-        });
-      }
-    };
-
-
-    EventListenerInterceptor.polyfillListenerOptions();
-
-
-    // var onclick = function() {
-    //   console.log('click');
-    // };
-
-    // document.body.addEventListener('click', onclick, false);
-    // document.body.addEventListener('click', onclick, { once: true });
-    // document.body.addEventListener('click', onclick, { once: true });
-    // document.body.addEventListener('click', onclick, false);
-    // document.body.addEventListener('click', onclick, false);
-
+  (function(EventListenerInterceptor) {
+    /**
+     * Event listener options support
+     */
+
+    EventListenerInterceptor.detectSupportedOptions = function() {
+      var _this = this;
+
+      this.supportedOptions = {
+        once: false,
+        passive: false,
+        capture: false,
+
+        all: false,
+        some: false
+      };
+
+      document.createDocumentFragment().addEventListener('test', function() {}, {
+        get once() {
+          _this.supportedOptions.once = true;
+          return false;
+        },
+        get passive() {
+          _this.supportedOptions.passive = true;
+          return false;
+        },
+        get capture() {
+          _this.supportedOptions.capture = true;
+          return false;
+        }
+      });
+
+      // useful shortcuts to detect if options are all/some supported
+      this.supportedOptions.all  = this.supportedOptions.once && this.supportedOptions.passive && this.supportedOptions.capture;
+      this.supportedOptions.some = this.supportedOptions.once || this.supportedOptions.passive || this.supportedOptions.capture;
+    };
+
+    EventListenerInterceptor.polyfillListenerOptions = function() {
+      this.detectSupportedOptions();
+      if (!this.supportedOptions.all) {
+        var _this = this;
+
+        this.interceptAll({
+          add: function(normalizedArguments) {
+            // console.log('intercepted', normalizedArguments);
+
+            var once = normalizedArguments.options.once && !_this.supportedOptions.once;
+            var passive = normalizedArguments.options.passive && !_this.supportedOptions.passive;
+
+            if (once || passive) {
+              var listener = normalizedArguments.polyfilled.listener;
+
+              normalizedArguments.polyfilled.listener = function(event) {
+                if(once) {
+                  this.removeEventListener(normalizedArguments.type, normalizedArguments.listener, normalizedArguments.options);
+                }
+
+                if(passive) {
+                  event.preventDefault = function() {
+                    throw new Error('Unable to preventDefault inside passive event listener invocation.');
+                  };
+                }
+
+                return listener.call(this, event);
+              };
+            }
+
+            if (!_this.supportedOptions.some) {
+              normalizedArguments.polyfilled.options = normalizedArguments.options.capture;
+            }
+          }
+        });
+      }
+    };
+
+
+    EventListenerInterceptor.polyfillListenerOptions();
+
+
+    // var onclick = function() {
+    //   console.log('click');
+    // };
+
+    // document.body.addEventListener('click', onclick, false);
+    // document.body.addEventListener('click', onclick, { once: true });
+    // document.body.addEventListener('click', onclick, { once: true });
+    // document.body.addEventListener('click', onclick, false);
+    // document.body.addEventListener('click', onclick, false);
+
   })(EventListenerInterceptor);
 
   // For the IE11 build.
@@ -5786,7 +5785,7 @@
   function camelCase(subject) {
     var _this2 = this;
 
-    return subject.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, function (match, _char) {
+    return subject.toLowerCase().replace(/-(\w)/g, function (match, _char) {
       _newArrowCheck(this, _this2);
 
       return _char.toUpperCase();

+ 1 - 1
dist/alpine.js

@@ -81,7 +81,7 @@
     return subject.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[_\s]/, '-').toLowerCase();
   }
   function camelCase(subject) {
-    return subject.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (match, char) => char.toUpperCase());
+    return subject.toLowerCase().replace(/-(\w)/g, (match, char) => char.toUpperCase());
   }
   function walk(el, callback) {
     if (callback(el) === false) return;

+ 1 - 1
package-lock.json

@@ -1,6 +1,6 @@
 {
     "name": "alpinejs",
-    "version": "2.4.1",
+    "version": "2.5.0",
     "lockfileVersion": 1,
     "requires": true,
     "dependencies": {

+ 0 - 9
src/component.js

@@ -266,15 +266,6 @@ export default class Component {
 
     resolveBoundAttributes(el, initialUpdate = false, extraVars) {
         let attrs = getXAttrs(el, this)
-        if (el.type !== undefined && el.type === 'radio') {
-            // If there's an x-model on a radio input, move it to end of attribute list
-            // to ensure that x-bind:value (if present) is processed first.
-            const modelIdx = attrs.findIndex((attr) => attr.type === 'model')
-            if (modelIdx > -1) {
-                attrs.push(attrs.splice(modelIdx, 1)[0])
-            }
-        }
-
         attrs.forEach(({ type, value, modifiers, expression }) => {
             switch (type) {
                 case 'model':

+ 3 - 3
src/utils.js

@@ -33,7 +33,7 @@ export function kebabCase(subject) {
 }
 
 export function camelCase(subject) {
-    return subject.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (match, char) => char.toUpperCase())
+    return subject.toLowerCase().replace(/-(\w)/g, (match, char) => char.toUpperCase())
 }
 
 export function walk(el, callback) {
@@ -138,11 +138,11 @@ function sortDirectives(directives) {
     })
 }
 
-function parseHtmlAttribute({ name, value }) {
+export function parseHtmlAttribute({ name, value }) {
     const normalizedName = replaceAtAndColonWithStandardSyntax(name)
 
     const typeMatch = normalizedName.match(xAttrRE)
-    const valueMatch = normalizedName.match(/:([a-zA-Z\-:]+)/)
+    const valueMatch = normalizedName.match(/:([a-zA-Z0-9\-:]+)/)
     const modifiers = normalizedName.match(/\.[^.\]]+(?=[^\]]*$)/g) || []
 
     return {

+ 14 - 0
test/bind.spec.js

@@ -495,3 +495,17 @@ test('.camel modifier correctly sets name of attribute', async () => {
 
     expect(document.querySelector('svg').getAttribute('viewBox')).toEqual('0 0 42 42')
 })
+
+
+test('attribute binding names can contain numbers', async () => {
+    document.body.innerHTML = `
+        <svg x-data>
+            <line x1="1" y1="2" :x2="3" x-bind:y2="4" />
+        </svg>
+    `;
+
+    Alpine.start();
+
+    expect(document.querySelector('line').getAttribute('x2')).toEqual('3');
+    expect(document.querySelector('line').getAttribute('y2')).toEqual('4');
+})

+ 19 - 0
test/on.spec.js

@@ -538,3 +538,22 @@ test('.camel modifier correctly binds event listener', async () => {
         expect(document.querySelector('p').innerText).toEqual('bob');
     });
 })
+
+test('.camel modifier correctly binds event listener with namespace', async () => {
+    document.body.innerHTML = `
+        <div x-data="{ foo: 'bar' }" x-on:ns:event-name.camel.window="foo = 'bob'">
+            <button x-on:click="$dispatch('ns:eventName')"></button>
+            <p x-text="foo"></p>
+        </div>
+    `
+
+    Alpine.start()
+
+    expect(document.querySelector('p').innerText).toEqual('bar')
+
+    document.querySelector('button').click();
+
+    await wait(() => {
+        expect(document.querySelector('p').innerText).toEqual('bob');
+    });
+})

+ 12 - 1
test/utils.spec.js

@@ -1,7 +1,18 @@
-import { arrayUnique } from '../src/utils'
+import { arrayUnique, parseHtmlAttribute } from '../src/utils'
 
 test('utils/arrayUnique', () => {
     const arrayMock = [1, 1, 2, 3, 1, 'a', 'b', 'c', 'b']
     const expected = arrayUnique(arrayMock)
     expect(expected).toEqual([1, 2, 3, 'a', 'b', 'c'])
 })
+
+test('utils/parseHtmlAttribute', () => {
+    const attribute = { name: ':x1', value: 'x' };
+    const expected = parseHtmlAttribute(attribute);
+    expect(expected).toEqual({
+        type: 'bind',
+        value: 'x1',
+        modifiers: [],
+        expression: 'x'
+    });
+})