main 4408fa004406 cached
371 files
3.5 MB
946.5k tokens
5307 symbols
1 requests
Download .txt
Showing preview only (3,777K chars total). Download the full file or copy to clipboard to get everything.
Repository: phoenixframework/phoenix_live_view
Branch: main
Commit: 4408fa004406
Files: 371
Total size: 3.5 MB

Directory structure:
gitextract_3104ppvg/

├── .formatter.exs
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── config.yml
│   ├── extract-changelog.sh
│   ├── single-file-samples/
│   │   ├── main.exs
│   │   └── test.exs
│   └── workflows/
│       ├── assets.yml
│       ├── ci.yml
│       ├── docs.yml
│       ├── github_release.yml
│       └── npm-publish.yml
├── .gitignore
├── .igniter.exs
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── assets/
│   ├── .prettierignore
│   ├── .prettierrc
│   ├── js/
│   │   └── phoenix_live_view/
│   │       ├── aria.js
│   │       ├── browser.js
│   │       ├── constants.js
│   │       ├── dom.js
│   │       ├── dom_patch.js
│   │       ├── dom_post_morph_restorer.js
│   │       ├── element_ref.js
│   │       ├── entry_uploader.js
│   │       ├── global.d.ts
│   │       ├── hooks.js
│   │       ├── index.ts
│   │       ├── js.js
│   │       ├── js_commands.ts
│   │       ├── live_socket.js
│   │       ├── live_uploader.js
│   │       ├── rendered.js
│   │       ├── upload_entry.js
│   │       ├── utils.js
│   │       ├── view.js
│   │       └── view_hook.ts
│   └── test/
│       ├── browser_test.ts
│       ├── debounce_test.ts
│       ├── dom_test.ts
│       ├── event_test.ts
│       ├── globals.d.ts
│       ├── hook_types_test.ts
│       ├── index_test.ts
│       ├── integration/
│       │   ├── event_test.ts
│       │   ├── metadata_test.ts
│       │   └── portal_test.ts
│       ├── js_test.ts
│       ├── live_socket_test.ts
│       ├── modify_root_test.ts
│       ├── rendered_test.ts
│       ├── test_helpers.ts
│       ├── tsconfig.json
│       ├── utils_test.ts
│       └── view_test.ts
├── babel.config.json
├── config/
│   ├── config.exs
│   ├── dev.exs
│   ├── docs.exs
│   ├── e2e.exs
│   └── test.exs
├── eslint.config.js
├── guides/
│   ├── cheatsheets/
│   │   └── html-attrs.cheatmd
│   ├── client/
│   │   ├── bindings.md
│   │   ├── external-uploads.md
│   │   ├── form-bindings.md
│   │   ├── js-interop.md
│   │   └── syncing-changes.md
│   ├── introduction/
│   │   └── welcome.md
│   └── server/
│       ├── assigns-eex.md
│       ├── deployments.md
│       ├── error-handling.md
│       ├── gettext.md
│       ├── live-layouts.md
│       ├── live-navigation.md
│       ├── security-model.md
│       ├── telemetry.md
│       └── uploads.md
├── jest.config.js
├── lib/
│   ├── mix/
│   │   └── tasks/
│   │       ├── compile/
│   │       │   └── phoenix_live_view.ex
│   │       └── phoenix_live_view.upgrade.ex
│   ├── phoenix_component/
│   │   ├── declarative.ex
│   │   └── macro_component.ex
│   ├── phoenix_component.ex
│   ├── phoenix_live_component.ex
│   ├── phoenix_live_view/
│   │   ├── application.ex
│   │   ├── async.ex
│   │   ├── async_result.ex
│   │   ├── channel.ex
│   │   ├── colocated_hook.ex
│   │   ├── colocated_js.ex
│   │   ├── controller.ex
│   │   ├── debug.ex
│   │   ├── diff.ex
│   │   ├── engine.ex
│   │   ├── helpers.ex
│   │   ├── html_algebra.ex
│   │   ├── html_engine.ex
│   │   ├── html_formatter/
│   │   │   └── tag_formatter.ex
│   │   ├── html_formatter.ex
│   │   ├── igniter/
│   │   │   └── upgrade_to_1_1.ex
│   │   ├── js.ex
│   │   ├── lifecycle.ex
│   │   ├── live_stream.ex
│   │   ├── logger.ex
│   │   ├── plug.ex
│   │   ├── renderer.ex
│   │   ├── route.ex
│   │   ├── router.ex
│   │   ├── session.ex
│   │   ├── socket.ex
│   │   ├── static.ex
│   │   ├── tag_engine/
│   │   │   ├── compiler.ex
│   │   │   ├── parser.ex
│   │   │   └── tokenizer.ex
│   │   ├── tag_engine.ex
│   │   ├── test/
│   │   │   ├── client_proxy.ex
│   │   │   ├── diff.ex
│   │   │   ├── dom.ex
│   │   │   ├── live_view_test.ex
│   │   │   ├── structs.ex
│   │   │   ├── tree_dom.ex
│   │   │   ├── upload_client.ex
│   │   │   └── utils.ex
│   │   ├── upload.ex
│   │   ├── upload_channel.ex
│   │   ├── upload_config.ex
│   │   ├── upload_tmp_file_writer.ex
│   │   ├── upload_writer.ex
│   │   └── utils.ex
│   ├── phoenix_live_view.ex
│   └── prettier.ex
├── mix.exs
├── package.json
├── priv/
│   └── static/
│       ├── phoenix_live_view.cjs.js
│       ├── phoenix_live_view.esm.js
│       └── phoenix_live_view.js
├── setupTests.js
├── test/
│   ├── e2e/
│   │   ├── .prettierignore
│   │   ├── README.md
│   │   ├── merge-coverage.js
│   │   ├── playwright.config.js
│   │   ├── support/
│   │   │   ├── colocated_live.ex
│   │   │   ├── components_live.ex
│   │   │   ├── error_live.ex
│   │   │   ├── form_dynamic_inputs_live.ex
│   │   │   ├── form_feedback.ex
│   │   │   ├── form_live.ex
│   │   │   ├── issues/
│   │   │   │   ├── issue_2787.ex
│   │   │   │   ├── issue_2965.ex
│   │   │   │   ├── issue_3026.ex
│   │   │   │   ├── issue_3040.ex
│   │   │   │   ├── issue_3047.ex
│   │   │   │   ├── issue_3083.ex
│   │   │   │   ├── issue_3107.ex
│   │   │   │   ├── issue_3117.ex
│   │   │   │   ├── issue_3169.ex
│   │   │   │   ├── issue_3194.ex
│   │   │   │   ├── issue_3200.ex
│   │   │   │   ├── issue_3378.ex
│   │   │   │   ├── issue_3448.ex
│   │   │   │   ├── issue_3496.ex
│   │   │   │   ├── issue_3529.ex
│   │   │   │   ├── issue_3530.ex
│   │   │   │   ├── issue_3612.ex
│   │   │   │   ├── issue_3636.ex
│   │   │   │   ├── issue_3647.ex
│   │   │   │   ├── issue_3651.ex
│   │   │   │   ├── issue_3656.ex
│   │   │   │   ├── issue_3658.ex
│   │   │   │   ├── issue_3681.ex
│   │   │   │   ├── issue_3684.ex
│   │   │   │   ├── issue_3686.ex
│   │   │   │   ├── issue_3709.ex
│   │   │   │   ├── issue_3719.ex
│   │   │   │   ├── issue_3814.ex
│   │   │   │   ├── issue_3819.ex
│   │   │   │   ├── issue_3919.ex
│   │   │   │   ├── issue_3931.ex
│   │   │   │   ├── issue_3941.ex
│   │   │   │   ├── issue_3953.ex
│   │   │   │   ├── issue_3979.ex
│   │   │   │   ├── issue_4027.ex
│   │   │   │   ├── issue_4066.ex
│   │   │   │   ├── issue_4078.ex
│   │   │   │   ├── issue_4088.ex
│   │   │   │   ├── issue_4094.ex
│   │   │   │   ├── issue_4095.ex
│   │   │   │   ├── issue_4102.ex
│   │   │   │   ├── issue_4107.ex
│   │   │   │   ├── issue_4121.ex
│   │   │   │   └── issue_4147.ex
│   │   │   ├── js_live.ex
│   │   │   ├── keyed_comprehension_live.ex
│   │   │   ├── navigation.ex
│   │   │   ├── portal.ex
│   │   │   ├── select_live.ex
│   │   │   └── upload_live.ex
│   │   ├── teardown.js
│   │   ├── test-fixtures.js
│   │   ├── test_helper.exs
│   │   ├── tests/
│   │   │   ├── colocated.spec.js
│   │   │   ├── components.spec.js
│   │   │   ├── errors.spec.js
│   │   │   ├── forms.spec.js
│   │   │   ├── issues/
│   │   │   │   ├── 2787.spec.js
│   │   │   │   ├── 2965.spec.js
│   │   │   │   ├── 3026.spec.js
│   │   │   │   ├── 3040.spec.js
│   │   │   │   ├── 3047.spec.js
│   │   │   │   ├── 3083.spec.js
│   │   │   │   ├── 3107.spec.js
│   │   │   │   ├── 3117.spec.js
│   │   │   │   ├── 3169.spec.js
│   │   │   │   ├── 3194.spec.js
│   │   │   │   ├── 3200.spec.js
│   │   │   │   ├── 3378.spec.js
│   │   │   │   ├── 3448.spec.js
│   │   │   │   ├── 3496.spec.js
│   │   │   │   ├── 3529.spec.js
│   │   │   │   ├── 3530.spec.js
│   │   │   │   ├── 3612.spec.js
│   │   │   │   ├── 3636.spec.js
│   │   │   │   ├── 3647.spec.js
│   │   │   │   ├── 3651.spec.js
│   │   │   │   ├── 3656.spec.js
│   │   │   │   ├── 3658.spec.js
│   │   │   │   ├── 3681.spec.js
│   │   │   │   ├── 3684.spec.js
│   │   │   │   ├── 3686.spec.js
│   │   │   │   ├── 3709.spec.js
│   │   │   │   ├── 3719.spec.js
│   │   │   │   ├── 3814.spec.js
│   │   │   │   ├── 3819.spec.js
│   │   │   │   ├── 3919.spec.js
│   │   │   │   ├── 3931.spec.js
│   │   │   │   ├── 3941.spec.js
│   │   │   │   ├── 3953.spec.js
│   │   │   │   ├── 3979.spec.js
│   │   │   │   ├── 4027.spec.js
│   │   │   │   ├── 4066.spec.js
│   │   │   │   ├── 4078.spec.js
│   │   │   │   ├── 4088.spec.js
│   │   │   │   ├── 4094.spec.js
│   │   │   │   ├── 4095.spec.js
│   │   │   │   ├── 4102.spec.js
│   │   │   │   ├── 4107.spec.js
│   │   │   │   ├── 4121.spec.js
│   │   │   │   └── 4147.spec.js
│   │   │   ├── js.spec.js
│   │   │   ├── keyed-comprehension.spec.js
│   │   │   ├── navigation.spec.js
│   │   │   ├── portal.spec.js
│   │   │   ├── select.spec.js
│   │   │   ├── streams.spec.js
│   │   │   └── uploads.spec.js
│   │   └── utils.js
│   ├── phoenix_component/
│   │   ├── components_test.exs
│   │   ├── declarative_assigns_test.exs
│   │   ├── macro_component_integration_test.exs
│   │   ├── macro_component_test.exs
│   │   ├── pages/
│   │   │   ├── about_page.html.heex
│   │   │   ├── another_root/
│   │   │   │   ├── root.html.heex
│   │   │   │   └── root.text.eex
│   │   │   └── welcome_page.html.heex
│   │   ├── rendering_test.exs
│   │   └── verify_test.exs
│   ├── phoenix_component_test.exs
│   ├── phoenix_live_view/
│   │   ├── async_result_test.exs
│   │   ├── async_test.exs
│   │   ├── colocated_hook_test.exs
│   │   ├── colocated_js_test.exs
│   │   ├── controller_test.exs
│   │   ├── debug_test.exs
│   │   ├── diff_test.exs
│   │   ├── engine_test.exs
│   │   ├── heex_extension_test.exs
│   │   ├── hooks_test.exs
│   │   ├── html_engine_test.exs
│   │   ├── html_formatter_test.exs
│   │   ├── igniter/
│   │   │   └── upgrade_to_1_1_test.exs
│   │   ├── integrations/
│   │   │   ├── assign_async_test.exs
│   │   │   ├── assigns_test.exs
│   │   │   ├── collocated_test.exs
│   │   │   ├── connect_test.exs
│   │   │   ├── elements_test.exs
│   │   │   ├── event_test.exs
│   │   │   ├── expensive_runtime_checks_test.exs
│   │   │   ├── flash_test.exs
│   │   │   ├── hooks_test.exs
│   │   │   ├── html_formatter_test.exs
│   │   │   ├── layout_test.exs
│   │   │   ├── live_components_test.exs
│   │   │   ├── live_reload_test.exs
│   │   │   ├── live_view_test.exs
│   │   │   ├── live_view_test_warnings_test.exs
│   │   │   ├── navigation_test.exs
│   │   │   ├── nested_test.exs
│   │   │   ├── params_test.exs
│   │   │   ├── start_async_test.exs
│   │   │   ├── stream_async_test.exs
│   │   │   ├── stream_test.exs
│   │   │   ├── telemetry_test.exs
│   │   │   └── update_test.exs
│   │   ├── js_test.exs
│   │   ├── live_stream_test.exs
│   │   ├── plug_test.exs
│   │   ├── router_test.exs
│   │   ├── socket_test.exs
│   │   ├── tag_engine/
│   │   │   └── tokenizer_test.exs
│   │   ├── test/
│   │   │   ├── diff_test.exs
│   │   │   ├── dom_test.exs
│   │   │   └── tree_dom_test.exs
│   │   ├── upload/
│   │   │   ├── channel_test.exs
│   │   │   ├── config_test.exs
│   │   │   └── external_test.exs
│   │   └── utils_test.exs
│   ├── phoenix_live_view_test.exs
│   ├── support/
│   │   ├── async_sync.ex
│   │   ├── controller.ex
│   │   ├── endpoint.ex
│   │   ├── layout_view.ex
│   │   ├── live_views/
│   │   │   ├── assign_async.ex
│   │   │   ├── cids_destroyed.ex
│   │   │   ├── collocated.ex
│   │   │   ├── collocated_component.html.heex
│   │   │   ├── collocated_live.html.heex
│   │   │   ├── component_and_nested_in_live.ex
│   │   │   ├── component_in_live.ex
│   │   │   ├── components.ex
│   │   │   ├── connect.ex
│   │   │   ├── debug_anno.exs
│   │   │   ├── debug_anno_opt_out.exs
│   │   │   ├── elements.ex
│   │   │   ├── events.ex
│   │   │   ├── expensive_runtime_checks.ex
│   │   │   ├── flash.ex
│   │   │   ├── general.ex
│   │   │   ├── host.ex
│   │   │   ├── layout.ex
│   │   │   ├── lifecycle.ex
│   │   │   ├── live_in_component.ex
│   │   │   ├── params.ex
│   │   │   ├── reload_live.ex
│   │   │   ├── render_with.ex
│   │   │   ├── root_tag_attr.exs
│   │   │   ├── start_async.ex
│   │   │   ├── stream_async.ex
│   │   │   ├── streams.ex
│   │   │   ├── test_warnings.ex
│   │   │   ├── update.ex
│   │   │   └── upload_live.ex
│   │   ├── router.ex
│   │   ├── telemetry_test_helpers.ex
│   │   └── templates/
│   │       ├── heex/
│   │       │   ├── dead_with_function_component.html.heex
│   │       │   ├── dead_with_function_component_with_inner_content.html.heex
│   │       │   ├── dead_with_live.html.eex
│   │       │   ├── inner_dead.html.eex
│   │       │   ├── inner_live.html.heex
│   │       │   ├── live_with_comprehension.html.heex
│   │       │   ├── live_with_dead.html.heex
│   │       │   └── live_with_live.html.heex
│   │       └── leex/
│   │           ├── dead_with_live.html.eex
│   │           ├── inner_dead.html.eex
│   │           ├── inner_live.html.leex
│   │           ├── live_with_comprehension.html.leex
│   │           ├── live_with_dead.html.leex
│   │           └── live_with_live.html.leex
│   └── test_helper.exs
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .formatter.exs
================================================
[
  import_deps: [:phoenix],
  plugins: [Phoenix.LiveView.HTMLFormatter],
  inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"],
  tag_formatters: %{script: Prettier}
]


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

### Environment

* Elixir version (please paste the output of `elixir -v`):
```

```
* Phoenix and LiveView versions (`mix deps | grep -w 'phoenix\|phoenix_live_view'`):
```

```
* Operating system:
- [ ] Windows
- [ ] MacOS
- [ ] Linux
- [ ] Other (please specify): 
* Browsers (including version) you attempted to reproduce this bug on (the more the merrier):
```

```

### Actual behavior

<!--
Describe the actual behaviour. If you are seeing an error, include the full message and stacktrace. 

Also please consider providing a single file app that reproduces the behaviour, you can start here:
https://github.com/phoenixframework/phoenix_live_view/blob/main/.github/single-file-samples/main.exs

For more examples, you can have a look at the directory:
https://github.com/phoenixframework/phoenix_live_view/blob/main/.github/single-file-samples/
-->

### Expected behavior



================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
---
blank_issues_enabled: true

contact_links:
  - name: Ask questions, support, and general discussions
    url: https://elixirforum.com/c/phoenix-forum
    about: Ask questions, provide support, and more on Elixir Forum

  - name: Propose new features
    url: https://elixirforum.com/c/phoenix-forum
    about: Propose new features on Elixir Forum


================================================
FILE: .github/extract-changelog.sh
================================================
#!/usr/bin/env bash
#
# Extract changelog for a specific version from CHANGELOG.md
#
# Usage: ./extract-changelog.sh <version>
# Example: ./extract-changelog.sh v1.1.22

set -euo pipefail

VERSION="${1:-}"

if [[ -z "$VERSION" ]]; then
    echo "Usage: $0 <version>" >&2
    echo "Example: $0 v1.1.22" >&2
    exit 1
fi

# Normalize version to include 'v' prefix
VERSION="${VERSION#v}"  # Remove 'v' if present
VERSION="v${VERSION}"   # Add 'v' prefix

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CHANGELOG_PATH="${SCRIPT_DIR}/../CHANGELOG.md"

if [[ ! -f "$CHANGELOG_PATH" ]]; then
    echo "Error: CHANGELOG.md not found at $CHANGELOG_PATH" >&2
    exit 1
fi

# Extract the section for the specified version
# Match from "## vX.Y.Z" until the next "## v" header
awk -v version="$VERSION" '
    BEGIN { found = 0; printing = 0 }

    # Match the start of our target version section
    /^## v[0-9]/ {
        if (printing) {
            # We hit the next version, stop printing
            exit
        }
        # Check if this line contains our version
        if (index($0, "## " version " ") > 0) {
            found = 1
            printing = 1
            next  # Skip the version header line
        }
    }

    printing { print }

    END {
        if (!found) {
            print "Error: Version " version " not found in changelog" > "/dev/stderr"
            exit 1
        }
    }
' "$CHANGELOG_PATH"


================================================
FILE: .github/single-file-samples/main.exs
================================================
Application.put_env(:sample, Example.Endpoint,
  http: [ip: {127, 0, 0, 1}, port: 5001],
  adapter: Bandit.PhoenixAdapter,
  server: true,
  live_view: [signing_salt: "aaaaaaaa"],
  secret_key_base: String.duplicate("a", 64)
)

Mix.install(
  [
    {:bandit, "~> 1.8"},
    {:jason, "~> 1.0"},
    {:phoenix, "~> 1.8"},
    {:phoenix_html, "~> 4.0"},
    # please test your issue using the latest version of LV from GitHub!
    {:phoenix_live_view,
     github: "phoenixframework/phoenix_live_view", branch: "main", override: true}
  ]
)

# if you're trying to test a specific LV commit, it may be necessary to manually build
# the JS assets. To do this, uncomment the following lines:
# this needs mix and npm available in your path!
#
# path = Phoenix.LiveView.__info__(:compile)[:source] |> Path.dirname() |> Path.join("../")
# System.cmd("mix", ["deps.get"], cd: path, into: IO.binstream())
# System.cmd("npm", ["install"], cd: Path.join(path, "./assets"), into: IO.binstream())
# System.cmd("mix", ["assets.build"], cd: path, into: IO.binstream())

defmodule Example.ErrorView do
  def render(template, _), do: Phoenix.Controller.status_message_from_template(template)
end

defmodule Example.HomeLive do
  use Phoenix.LiveView, layout: {__MODULE__, :live}

  def mount(_params, _session, socket) do
    {:ok, assign(socket, :count, 0)}
  end

  def render("live.html", assigns) do
    ~H"""
    <script src="/assets/phoenix/phoenix.js">
    </script>
    <script src="/assets/phoenix_live_view/phoenix_live_view.js">
    </script>
    <script src="/assets/phoenix_html/phoenix_html.js">
    </script>
    <%!-- uncomment to use enable tailwind --%>
    <%!-- <script src="https://cdn.tailwindcss.com"></script> --%>
    <script>
      let liveSocket = new window.LiveView.LiveSocket("/live", window.Phoenix.Socket)
      liveSocket.connect()
    </script>
    <style>
      * { font-size: 1.1em; }
    </style>
    {@inner_content}
    """
  end

  def render(assigns) do
    ~H"""
    {@count}
    <button phx-click="inc">+</button>
    <button phx-click="dec">-</button>
    """
  end

  def handle_event("inc", _params, socket) do
    {:noreply, assign(socket, :count, socket.assigns.count + 1)}
  end

  def handle_event("dec", _params, socket) do
    {:noreply, assign(socket, :count, socket.assigns.count - 1)}
  end
end

defmodule Example.Router do
  use Phoenix.Router
  import Phoenix.LiveView.Router

  pipeline :browser do
    plug(:accepts, ["html"])
  end

  scope "/", Example do
    pipe_through(:browser)

    live("/", HomeLive, :index)
  end
end

defmodule Example.Endpoint do
  use Phoenix.Endpoint, otp_app: :sample
  socket("/live", Phoenix.LiveView.Socket)

  plug Plug.Static, from: {:phoenix, "priv/static"}, at: "/assets/phoenix"
  plug Plug.Static, from: {:phoenix_live_view, "priv/static"}, at: "/assets/phoenix_live_view"
  plug Plug.Static, from: {:phoenix_html, "priv/static"}, at: "/assets/phoenix_html"

  plug(Example.Router)
end

{:ok, _} = Supervisor.start_link([Example.Endpoint], strategy: :one_for_one)
Process.sleep(:infinity)


================================================
FILE: .github/single-file-samples/test.exs
================================================
Application.put_env(:phoenix, Example.Endpoint,
  http: [ip: {127, 0, 0, 1}, port: 5001],
  adapter: Bandit.PhoenixAdapter,
  server: true,
  live_view: [signing_salt: "aaaaaaaa"],
  secret_key_base: String.duplicate("a", 64)
)

Mix.install(
  [
    {:bandit, "~> 1.8"},
    {:jason, "~> 1.0"},
    {:phoenix, "~> 1.8"},
    # please test your issue using the latest version of LV from GitHub!
    {:phoenix_live_view,
     github: "phoenixframework/phoenix_live_view", branch: "main", override: true},
    {:lazy_html, ">= 0.1.0"}
  ]
)

ExUnit.start()

defmodule Example.ErrorView do
  def render(template, _), do: Phoenix.Controller.status_message_from_template(template)
end

defmodule Example.HomeLive do
  use Phoenix.LiveView, layout: {__MODULE__, :live}

  def mount(_params, _session, socket) do
    socket
    |> then(&{:ok, &1})
  end

  def render("live.html", assigns) do
    ~H"""
    <script src="/assets/phoenix/phoenix.js">
    </script>
    <script src="/assets/phoenix_live_view/phoenix_live_view.js">
    </script>
    <script src="/assets/phoenix_html/phoenix_html.js">
    </script>
    <script>
      let liveSocket = new window.LiveView.LiveSocket("/live", window.Phoenix.Socket)
      liveSocket.connect()
    </script>
    <style>
      * { font-size: 1.1em; }
    </style>
    {@inner_content}
    """
  end

  def render(assigns) do
    ~H"""
    <p>The LiveView content goes here</p>
    """
  end
end

defmodule Example.Router do
  use Phoenix.Router
  import Phoenix.LiveView.Router

  pipeline :browser do
    plug(:accepts, ["html"])
  end

  scope "/", Example do
    pipe_through(:browser)

    live("/", HomeLive, :index)
  end
end

defmodule Example.Endpoint do
  use Phoenix.Endpoint, otp_app: :phoenix
  socket("/live", Phoenix.LiveView.Socket)
  plug Plug.Static, from: {:phoenix, "priv/static"}, at: "/assets/phoenix"
  plug Plug.Static, from: {:phoenix_live_view, "priv/static"}, at: "/assets/phoenix_live_view"
  plug Plug.Static, from: {:phoenix_html, "priv/static"}, at: "/assets/phoenix_html"
  plug(Example.Router)
end

defmodule Example.HomeLiveTest do
  use ExUnit.Case

  import Phoenix.ConnTest
  import Plug.Conn
  import Phoenix.LiveViewTest

  @endpoint Example.Endpoint

  test "works properly" do
    conn = Phoenix.ConnTest.build_conn()

    {:ok, _view, html} = live(conn, "/")

    assert html =~ "The LiveView content goes here"
  end
end

{:ok, _} = Supervisor.start_link([Example.Endpoint], strategy: :one_for_one)
ExUnit.run()
Process.sleep(:infinity)


================================================
FILE: .github/workflows/assets.yml
================================================
name: Assets

on:
  push:
    branches:
      - main
      - "v*.*"

jobs:
  build:
    runs-on: ubuntu-22.04
    env:
      elixir: 1.18.1
      otp: 27.2
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Elixir
        uses: erlef/setup-beam@v1
        with:
          elixir-version: ${{ env.elixir }}
          otp-version: ${{ env.otp }}

      - name: Restore deps and _build cache
        uses: actions/cache@v4
        with:
          path: |
            deps
            _build
          key: ${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-${{ hashFiles('**/mix.lock') }}-dev
          restore-keys: |
            ${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-
      - name: Install Dependencies
        run: mix deps.get --only dev

      - name: Set up Node.js 20.x
        uses: actions/setup-node@v4
        with:
          node-version: 20.x

      - name: Restore npm cache
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install npm dependencies
        run: npm ci

      - name: Build assets
        run: mix assets.build

      - name: Push updated assets
        id: push_assets
        uses: stefanzweifel/git-auto-commit-action@v5
        with:
          commit_message: Update assets
          file_pattern: priv/static


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  push:
  pull_request:
    branches:
      - main

jobs:
  mix_test:
    name: mix test (OTP ${{matrix.otp}} | Elixir ${{matrix.elixir}})

    strategy:
      fail-fast: false
      matrix:
        include:
          - elixir: 1.15.4
            otp: 25.3

          - elixir: 1.16.3
            otp: 26.2

          - elixir: 1.18.4
            otp: 27.3

          # update coverage report as well
          - elixir: 1.19.1
            otp: 28.1
            lint: lint

          # run against latest Elixir to catch warnings early
          - elixir: main-otp-28
            otp: maint-28

    runs-on: ubuntu-latest

    steps:
      - name: Install inotify-tools
        run: |
          sudo apt update
          sudo apt install -y inotify-tools
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Elixir
        uses: erlef/setup-beam@v1
        with:
          elixir-version: ${{ matrix.elixir }}
          otp-version: ${{ matrix.otp }}

      - name: Restore deps and _build cache
        uses: actions/cache@v4
        with:
          path: |
            deps
            _build
          key: deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }}
          restore-keys: |
            deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}

      - name: Install dependencies
        run: mix deps.get

      - name: Remove compiled application files
        run: mix clean

      - name: Compile dependencies
        run: mix compile
        if: ${{ !matrix.lint }}
        env:
          MIX_ENV: test

      - name: Compile & lint dependencies
        run: mix compile --warnings-as-errors
        if: ${{ matrix.lint }} or ${{ matrix.elixir == 'main' }}
        env:
          MIX_ENV: test

      - name: Compile without optional deps
        run: mix compile --no-optional-deps
        env:
          MIX_ENV: dev

      - name: Check if formatted
        run: mix format --check-formatted
        if: ${{ matrix.lint }}
        env:
          MIX_ENV: test

      - name: Run tests
        run: mix test --cover --export-coverage default --warnings-as-errors

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: mix-test-coverage-${{ matrix.otp }}-${{ matrix.elixir }}
          path: cover/default.coverdata
          retention-days: 7

  npm_test:
    name: npm test

    strategy:
      matrix:
        include:
          - elixir: 1.19.1
            otp: 28.1

    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Elixir
        uses: erlef/setup-beam@v1
        with:
          elixir-version: ${{ matrix.elixir }}
          otp-version: ${{ matrix.otp }}

      - name: Restore deps and _build cache
        uses: actions/cache@v4
        with:
          path: |
            deps
            _build
          key: deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }}
          restore-keys: |
            deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}

      - name: Install dependencies
        run: mix deps.get --only test

      - name: Set up Node.js 20.x
        uses: actions/setup-node@v4
        with:
          node-version: 20.x

      - name: Restore npm cache
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: setup JS
        run: npm run setup

      - name: typecheck
        run: npm run build && npm run typecheck:tests

      - name: check lint and format
        run: npm run js:lint && npm run js:format.check

      - name: test
        run: npm run js:test

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: js-unit-coverage
          path: coverage/
          retention-days: 7

  e2e_test:
    name: e2e test

    strategy:
      matrix:
        include:
          - elixir: 1.19.1
            otp: 28.1

    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/playwright:v1.58.2-noble
    env:
      ImageOS: ubuntu22
      HOME: /root
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: install unzip
        run: apt update && apt -y install unzip

      - name: Set up Elixir
        uses: erlef/setup-beam@v1
        with:
          elixir-version: ${{ matrix.elixir }}
          otp-version: ${{ matrix.otp }}

      - name: Restore deps and _build cache
        uses: actions/cache@v4
        with:
          path: |
            deps
            _build
          key: deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }}
          restore-keys: |
            deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}

      - name: Install dependencies
        run: mix deps.get

      - name: Restore npm cache
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: setup
        run: npm run setup

      - name: Run e2e tests
        run: npm run e2e:test

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 7

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: e2e-test-results
          path: test/e2e/test-results/
          retention-days: 7

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: mix-e2e-coverage
          path: cover/e2e.coverdata
          retention-days: 7

  coverage_report:
    name: coverage report

    runs-on: ubuntu-latest
    needs: [mix_test, npm_test, e2e_test]

    strategy:
      matrix:
        include:
          - elixir: 1.19.1
            otp: 28.1

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Elixir
        uses: erlef/setup-beam@v1
        with:
          elixir-version: ${{ matrix.elixir }}
          otp-version: ${{ matrix.otp }}

      - name: Download mix unit coverage
        uses: actions/download-artifact@v4
        with:
          # This needs to be updated when changing the test matrix
          name: mix-test-coverage-${{ matrix.otp }}-${{ matrix.elixir }}
          path: cover/

      - name: Download mix e2e coverage
        uses: actions/download-artifact@v4
        with:
          name: mix-e2e-coverage
          path: cover/

      - name: Restore deps and _build cache
        uses: actions/cache@v4
        with:
          path: |
            deps
            _build
          key: deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }}
          restore-keys: |
            deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}

      - name: Generate mix coverage report
        run: mix test.coverage

      - name: Download js-unit-coverage artifact
        uses: actions/download-artifact@v4
        with:
          name: js-unit-coverage
          path: coverage/

      - name: Download e2e-test-results artifact
        uses: actions/download-artifact@v4
        with:
          name: e2e-test-results
          path: test/e2e/test-results/

      - name: Set up Node.js 20.x
        uses: actions/setup-node@v4
        with:
          node-version: 20.x

      - name: Merge coverage reports
        run: npm install && npm run cover:merge

      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: overall-coverage
          path: cover/
          retention-days: 7


================================================
FILE: .github/workflows/docs.yml
================================================
name: Docs

on:
  push:
    branches:
      - main

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  build:
    runs-on: ubuntu-22.04
    env:
      elixir: 1.18.1
      otp: 27.2
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Elixir
        uses: erlef/setup-beam@v1
        with:
          elixir-version: ${{ env.elixir }}
          otp-version: ${{ env.otp }}

      - name: Restore deps and _build cache
        uses: actions/cache@v4
        with:
          path: |
            deps
            _build
          key: ${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-${{ hashFiles('**/mix.lock') }}-docs
          restore-keys: |
            ${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-
      - name: Install Dependencies
        run: mix deps.get --only docs

      - name: Build docs
        run: mix docs

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: doc/

  # Deployment job
  deploy:
    environment:
      name: github-pages
      url: ${{steps.deployment.outputs.page_url}}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4


================================================
FILE: .github/workflows/github_release.yml
================================================
name: Creates a GitHub Release

on:
  push:
    tags:
      - "v*"

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    env:
      GITHUB_TOKEN: ${{ github.token }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Extract version from tag
        id: version
        run: echo "version=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"

      - name: Extract changelog for version
        run: bash .github/extract-changelog.sh "${{ steps.version.outputs.version }}" > release_notes.md

      - name: Create GitHub Release
        run: |
          PRERELEASE_FLAG=""
          if [[ "${{ steps.version.outputs.version }}" == *-rc* ]]; then
            PRERELEASE_FLAG="--prerelease"
          fi
          gh release create "${{ steps.version.outputs.version }}" \
            --title "${{ steps.version.outputs.version }}" \
            --notes-file release_notes.md \
            $PRERELEASE_FLAG


================================================
FILE: .github/workflows/npm-publish.yml
================================================
# https://docs.npmjs.com/trusted-publishers
name: NPM Publish

on:
  push:
    tags:
      - "v*"

permissions:
  id-token: write  # Required for OIDC
  contents: read

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          registry-url: "https://registry.npmjs.org"

      # Ensure npm 11.5.1 or later is installed
      - name: Update npm
        run: npm install -g npm@latest

      - name: Determine npm tag
        id: npm-tag
        run: |
          TAG=${GITHUB_REF#refs/tags/}
          # Update this condition when bumping the major version!
          if [[ $TAG == v1.0.* ]]; then
            echo "tag=old-version" >> $GITHUB_OUTPUT
          elif [[ $TAG == *-rc* ]]; then
            echo "tag=rc" >> $GITHUB_OUTPUT
          else
            echo "tag=latest" >> $GITHUB_OUTPUT
          fi

      - name: Publish to npm
        run: npm publish --tag ${{ steps.npm-tag.outputs.tag }}

================================================
FILE: .gitignore
================================================
# The directory Mix will write compiled artifacts to.
/_build/

# If you run "mix test --cover", coverage assets end up here.
/cover/

# The directory Mix downloads your dependencies sources to.
/deps/

# Where 3rd-party dependencies like ExDoc output generated docs.
/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Ignore package tarball (built via "mix hex.build").
phoenix_live_view-*.tar

node_modules

/test/e2e/test-results/
/playwright-report/
/coverage/
/assets/js/types/


================================================
FILE: .igniter.exs
================================================
# This is a configuration file for igniter.
# For option documentation, see https://hexdocs.pm/igniter/Igniter.Project.IgniterConfig.html
# To keep it up to date, use `mix igniter.setup`
[
  module_location: :outside_matching_folder,
  extensions: [],
  deps_location: :last_list_literal,
  source_folders: ["lib", "test/support"],
  dont_move_files: [~r"lib/mix"]
]


================================================
FILE: CHANGELOG.md
================================================
# Changelog for v1.2

## v1.2.0-rc.0 (Unreleased)

### Enhancements

* Add `phx-no-unused-field` to prevent sending `_unused` parameters to the server ([#3577](https://github.com/phoenixframework/phoenix_live_view/issues/3577))
* Add `Phoenix.LiveView.JS.to_encodable/1` pushing JS commands via events ([#4060](https://github.com/phoenixframework/phoenix_live_view/pull/4060))
  * `%JS{}` now also implements the `JSON.Encoder` and `Jason.Encoder` protocols
* HTMLFormatter: Better preserve whitespace around tags and inside inline elements ([#3718](https://github.com/phoenixframework/phoenix_live_view/issues/3718))
* HEEx: Allow to opt out of debug annotations for a module ([#4119](https://github.com/phoenixframework/phoenix_live_view/pull/4119))
* HEEx: warn when missing a space between attributes ([#3999](https://github.com/phoenixframework/phoenix_live_view/issues/3999))
* HTMLFormatter: Add `TagFormatter` behaviour for formatting `<style>` and `<script>` tags ([#4140](https://github.com/phoenixframework/phoenix_live_view/pull/4140))

## v1.1

The CHANGELOG for v1.1 releases can be found [in the v1.1 branch](https://github.com/phoenixframework/phoenix_live_view/blob/v1.1/CHANGELOG.md).


================================================
FILE: LICENSE.md
================================================
# MIT License

Copyright (c) 2018 Chris McCord

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
# Phoenix LiveView

[![Actions Status](https://github.com/phoenixframework/phoenix_live_view/workflows/CI/badge.svg)](https://github.com/phoenixframework/phoenix_live_view/actions?query=workflow%3ACI) [![Hex.pm](https://img.shields.io/hexpm/v/phoenix_live_view.svg)](https://hex.pm/packages/phoenix_live_view) [![Documentation](https://img.shields.io/badge/documentation-gray)](https://hexdocs.pm/phoenix_live_view)

Phoenix LiveView enables rich, real-time user experiences with server-rendered HTML.

Visit the [https://livebeats.fly.dev](https://livebeats.fly.dev/) demo to see
the kinds of applications you can build, or see a sneak peek below:

https://user-images.githubusercontent.com/576796/162234098-31b580fe-e424-47e6-b01d-cd2cfcf823a9.mp4

<br />

LiveView ships by default in new Phoenix applications. After you
[install Elixir](https://elixir-lang.org/install.html) on your machine,
you can create your first LiveView app in two steps:

    $ mix archive.install hex phx_new
    $ mix phx.new demo

> If you have an older existing Phoenix app and you wish to add LiveView,
> see [the previous installation guide](https://github.com/phoenixframework/phoenix_live_view/blob/v0.20.1/guides/introduction/installation.md).

## Feature highlights

LiveView brings a unified experience to building web applications. You no longer
have to split work between client and server, across different toolings, layers, and
abstractions. Instead, LiveView enriches the server with a declarative and powerful
model while keeping your code closer to your data (and ultimately your source of truth):

  * **Declarative rendering:** Render HTML on the server over WebSockets with a declarative model, including an optional LongPolling fallback.

  * **Rich templating language:** Enjoy HEEx: a templating language that supports function components, slots, HTML validation, verified routes, and more.

  * **Diffs over the wire:** Instead of sending "HTML over the wire", LiveView knows exactly which parts of your templates change, sending minimal diffs over the wire after the initial render, reducing latency and bandwidth usage. The client leverages this information and optimizes the browser with 5-10x faster updates, compared to solutions that replace whole HTML fragments.

  * **Live form validation:** LiveView supports real-time form validation out of the box. Create rich user interfaces with features like uploads, nested inputs, and [specialized recovery](https://hexdocs.pm/phoenix_live_view/form-bindings.html#recovery-following-crashes-or-disconnects).

  * **File uploads:** Real-time file uploads with progress indicators and image previews. Process your uploads on the fly or submit them to your desired cloud service.

  * **Rich integration API:** Use the rich integration API to interact with the client, with `phx-click`, `phx-focus`, `phx-blur`, `phx-submit`, and `phx-hook` included for cases where you have to write JavaScript.

  * **Optimistic updates and transitions:** Perform optimistic updates and transitions with JavaScript commands via `Phoenix.LiveView.JS`.

  * **Loose coupling:** Reuse more code via stateful components with loosely-coupled templates, state, and event handling — a must for enterprise application development.

  * **Live navigation:** Enriched links and redirects are just more ways LiveView keeps your app light and performant. Clients load the minimum amount of content needed as users navigate around your app without any compromise in user experience.

  * **Latency simulator:** Emulate how slow clients will interact with your application with the latency simulator.

  * **Robust test suite:** Write tests with confidence alongside Phoenix LiveView built-in testing tools. No more running a whole browser alongside your tests.

## Learning

Check our [comprehensive docs](https://hexdocs.pm/phoenix_live_view) to get started.

The Phoenix framework documentation also keeps a list of [community resources](https://hexdocs.pm/phoenix/community.html), including books, videos, and other materials, and some include LiveView too.

Also follow these announcements from the Phoenix team on LiveView for more examples and rationale:

  * [LiveBeats: Building a Social Music App With Phoenix LiveView](https://fly.io/blog/livebeats/)

  * [Build a real-time Twitter clone with LiveView](https://www.phoenixframework.org/blog/build-a-real-time-twitter-clone-in-15-minutes-with-live-view-and-phoenix-1-5)

  * [Build a real-time Twitch clone with LiveView and Elixir WebRTC](https://www.youtube.com/watch?v=jziOb2Edfzk)

  * [Initial announcement](https://dockyard.com/blog/2018/12/12/phoenix-liveview-interactive-real-time-apps-no-need-to-write-javascript)

## Component systems

When you create a new Phoenix project, it comes with a minimal component system to power Phoenix generators.
In case you want to enrich your developer experience, there are several component systems provided by the
community at different stages of development:

* [Bloom](https://github.com/chrisgreg/bloom): The opinionated, open-source extension to Phoenix Core Components

* [Doggo](https://github.com/woylie/doggo): Headless UI components for Phoenix

* [Petal Components](https://github.com/petalframework/petal_components): Phoenix + Live View HEEX Components

* [PrimerLive](https://github.com/ArthurClemens/primer_live): An implementation of GitHub's Primer Design System using Phoenix LiveView

* [SaladUI](https://github.com/bluzky/salad_ui): Phoenix Liveview component library inspired by shadcn UI

* [Mishka Chelekom](https://github.com/mishka-group/mishka_chelekom): Phoenix + LiveView UI kit and HEEx components

* [Fluxon UI](https://fluxonui.com): Elegant and accessible UI components for Phoenix LiveView

## LiveDebugger

[LiveDebugger](https://github.com/software-mansion/live-debugger) is a debugging tool built specifically for Phoenix LiveView applications. It provides a real-time view into your LiveView processes, making it easier to understand the component hierarchy, inspect assigns, trace lifecycle callbacks, and troubleshoot issues more efficiently.

To get started, follow the setup instructions in the [LiveDebugger repository](https://github.com/software-mansion/live-debugger?tab=readme-ov-file#getting-started).

## What makes LiveView unique?

LiveView is server-centric. You no longer have to worry about managing
both client and server to keep things in sync. LiveView automatically
updates the client as changes happen on the server.

LiveView is first rendered statically as part of regular HTTP requests,
which provides quick times for "First Meaningful Paint", in addition to
helping search and indexing engines.

Then LiveView uses a persistent connection between client and server.
This allows LiveView applications to react faster to user events as
there is less work to be done and less data to be sent compared to
stateless requests that have to authenticate, decode, load, and encode
data on every request.

When LiveView was first announced, many developers from different
backgrounds got inspired by the potential unlocked by LiveView to
build rich, real-time user experiences. We believe LiveView is built
on top of a solid foundation that makes LiveView hard to replicate
anywhere else:

  * LiveView is built on top of the Elixir programming language and
    functional programming, which provides a great model for reasoning
    about your code and how your LiveView changes over time.

  * By building on top of a [scalable platform](https://dockyard.com/blog/2016/08/09/phoenix-channels-vs-rails-action-cable),
    LiveView scales well vertically (from small to large instances)
    and horizontally (by adding more instances). This allows you to
    continue shipping features when more and more users join your
    application, instead of dealing with performance issues.

  * LiveView applications are *distributed and real-time*. A LiveView
    app can push events to users as those events happen anywhere in
    the system. Do you want to notify a user that their best friend
    just connected? This is easily done without a single line of
    custom JavaScript and with no extra external dependencies
    (no extra databases, no Redis, no extra message queues, etc.).

  * LiveView performs change tracking: whenever you change a value on
    the server, LiveView will send to the client only the values that
    changed, drastically reducing the latency and the amount of data
    sent over the wire. This is achievable thanks to Elixir's
    immutability and its ability to treat code as data.

## Browser Support

All current Chrome, Safari, Firefox, and MS Edge are supported.
IE11 support is available with the following polyfills:

```shell
$ npm install --save --prefix assets mdn-polyfills url-search-params-polyfill formdata-polyfill child-replace-with-polyfill classlist-polyfill new-event-polyfill @webcomponents/template shim-keyboard-event-key core-js
```

Note: The `shim-keyboard-event-key` polyfill is also required for [MS Edge 12-18](https://caniuse.com/#feat=keyboardevent-key).

Note: The `event-submitter-polyfill` package is also required for [MS Edge 12-80 &amp; Safari &lt; 15.4](https://caniuse.com/mdn-api_submitevent_submitter).

```javascript
// assets/js/app.js
import "mdn-polyfills/Object.assign"
import "mdn-polyfills/CustomEvent"
import "mdn-polyfills/String.prototype.startsWith"
import "mdn-polyfills/Array.from"
import "mdn-polyfills/Array.prototype.find"
import "mdn-polyfills/Array.prototype.some"
import "mdn-polyfills/NodeList.prototype.forEach"
import "mdn-polyfills/Element.prototype.closest"
import "mdn-polyfills/Element.prototype.matches"
import "mdn-polyfills/Node.prototype.remove"
import "child-replace-with-polyfill"
import "url-search-params-polyfill"
import "formdata-polyfill"
import "classlist-polyfill"
import "new-event-polyfill"
import "@webcomponents/template"
import "shim-keyboard-event-key"
import "event-submitter-polyfill"
import "core-js/features/set"
import "core-js/features/url"

import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
...
```

## Contributing

We appreciate any contribution to LiveView.

Please see the Phoenix [Code of Conduct](https://github.com/phoenixframework/phoenix/blob/master/CODE_OF_CONDUCT.md) and [Contributing](https://github.com/phoenixframework/phoenix/blob/master/CONTRIBUTING.md) guides.

Running the Elixir tests:

```bash
$ mix deps.get
$ mix test
```

Running all JavaScript tests:
```bash
$ npm install
$ npm run setup
$ npm run test
```

Running the JavaScript unit tests:

```bash
$ npm run setup
$ npm run js:test
# to automatically run tests for files that have been changed
$ npm run js:test.watch
```

Running the JavaScript end-to-end tests:

```bash
$ npm run setup
$ npm run e2e:test
```

Checking test coverage:

```bash
$ npm run cover
$ npm run cover:report
```

Format the files:

```bash
$ mix format
$ npm run js:format
```

JS contributions are very welcome, but please do not include an updated `priv/static/phoenix_live_view.js` in pull requests. The maintainers will update it as part of the release process.


================================================
FILE: assets/.prettierignore
================================================
js/types/


================================================
FILE: assets/.prettierrc
================================================


================================================
FILE: assets/js/phoenix_live_view/aria.js
================================================
const ARIA = {
  anyOf(instance, classes) {
    return classes.find((name) => instance instanceof name);
  },

  isFocusable(el, interactiveOnly) {
    return (
      (el instanceof HTMLAnchorElement && el.rel !== "ignore") ||
      (el instanceof HTMLAreaElement && el.href !== undefined) ||
      (!el.disabled &&
        this.anyOf(el, [
          HTMLInputElement,
          HTMLSelectElement,
          HTMLTextAreaElement,
          HTMLButtonElement,
        ])) ||
      el instanceof HTMLIFrameElement ||
      (el.tabIndex >= 0 && el.getAttribute("aria-hidden") !== "true") ||
      (!interactiveOnly &&
        el.getAttribute("tabindex") !== null &&
        el.getAttribute("aria-hidden") !== "true")
    );
  },

  attemptFocus(el, interactiveOnly) {
    if (this.isFocusable(el, interactiveOnly)) {
      try {
        el.focus();
      } catch {
        // that's fine
      }
    }
    return !!document.activeElement && document.activeElement.isSameNode(el);
  },

  focusFirstInteractive(el) {
    let child = el.firstElementChild;
    while (child) {
      if (this.attemptFocus(child, true) || this.focusFirstInteractive(child)) {
        return true;
      }
      child = child.nextElementSibling;
    }
  },

  focusFirst(el) {
    let child = el.firstElementChild;
    while (child) {
      if (this.attemptFocus(child) || this.focusFirst(child)) {
        return true;
      }
      child = child.nextElementSibling;
    }
  },

  focusLast(el) {
    let child = el.lastElementChild;
    while (child) {
      if (this.attemptFocus(child) || this.focusLast(child)) {
        return true;
      }
      child = child.previousElementSibling;
    }
  },
};
export default ARIA;


================================================
FILE: assets/js/phoenix_live_view/browser.js
================================================
const Browser = {
  canPushState() {
    return typeof history.pushState !== "undefined";
  },

  dropLocal(localStorage, namespace, subkey) {
    return localStorage.removeItem(this.localKey(namespace, subkey));
  },

  updateLocal(localStorage, namespace, subkey, initial, func) {
    const current = this.getLocal(localStorage, namespace, subkey);
    const key = this.localKey(namespace, subkey);
    const newVal = current === null ? initial : func(current);
    localStorage.setItem(key, JSON.stringify(newVal));
    return newVal;
  },

  getLocal(localStorage, namespace, subkey) {
    return JSON.parse(localStorage.getItem(this.localKey(namespace, subkey)));
  },

  updateCurrentState(callback) {
    if (!this.canPushState()) {
      return;
    }
    history.replaceState(
      callback(history.state || {}),
      "",
      window.location.href,
    );
  },

  pushState(kind, meta, to) {
    if (this.canPushState()) {
      if (to !== window.location.href) {
        if (meta.type == "redirect" && meta.scroll) {
          // If we're redirecting store the current scrollY for the current history state.
          const currentState = history.state || {};
          currentState.scroll = meta.scroll;
          history.replaceState(currentState, "", window.location.href);
        }

        delete meta.scroll; // Only store the scroll in the redirect case.
        history[kind + "State"](meta, "", to || null); // IE will coerce undefined to string

        // when using navigate, we'd call pushState immediately before patching the DOM,
        // jumping back to the top of the page, effectively ignoring the scrollIntoView;
        // therefore we wait for the next frame (after the DOM patch) and only then try
        // to scroll to the hashEl
        window.requestAnimationFrame(() => {
          const hashEl = this.getHashTargetEl(window.location.hash);

          if (hashEl) {
            hashEl.scrollIntoView();
          } else if (meta.type === "redirect") {
            window.scroll(0, 0);
          }
        });
      }
    } else {
      this.redirect(to);
    }
  },

  setCookie(name, value, maxAgeSeconds) {
    const expires =
      typeof maxAgeSeconds === "number" ? ` max-age=${maxAgeSeconds};` : "";
    document.cookie = `${name}=${value};${expires} path=/`;
  },

  getCookie(name) {
    return document.cookie.replace(
      new RegExp(`(?:(?:^|.*;\s*)${name}\s*\=\s*([^;]*).*$)|^.*$`),
      "$1",
    );
  },

  deleteCookie(name) {
    document.cookie = `${name}=; max-age=-1; path=/`;
  },

  redirect(
    toURL,
    flash,
    navigate = (url) => {
      window.location.href = url;
    },
  ) {
    if (flash) {
      this.setCookie("__phoenix_flash__", flash, 60);
    }
    navigate(toURL);
  },

  localKey(namespace, subkey) {
    return `${namespace}-${subkey}`;
  },

  getHashTargetEl(maybeHash) {
    const hash = maybeHash.toString().substring(1);
    if (hash === "") {
      return;
    }
    return (
      document.getElementById(hash) ||
      document.querySelector(`a[name="${hash}"]`)
    );
  },
};

export default Browser;


================================================
FILE: assets/js/phoenix_live_view/constants.js
================================================
export const CONSECUTIVE_RELOADS = "consecutive-reloads";
export const MAX_RELOADS = 10;
export const RELOAD_JITTER_MIN = 5000;
export const RELOAD_JITTER_MAX = 10000;
export const FAILSAFE_JITTER = 30000;
export const PHX_EVENT_CLASSES = [
  "phx-click-loading",
  "phx-change-loading",
  "phx-submit-loading",
  "phx-keydown-loading",
  "phx-keyup-loading",
  "phx-blur-loading",
  "phx-focus-loading",
  "phx-hook-loading",
];
export const PHX_DROP_TARGET_ACTIVE_CLASS = "phx-drop-target-active";
export const PHX_COMPONENT = "data-phx-component";
export const PHX_VIEW_REF = "data-phx-view";
export const PHX_LIVE_LINK = "data-phx-link";
export const PHX_TRACK_STATIC = "track-static";
export const PHX_LINK_STATE = "data-phx-link-state";
export const PHX_REF_LOADING = "data-phx-ref-loading";
export const PHX_REF_SRC = "data-phx-ref-src";
export const PHX_REF_LOCK = "data-phx-ref-lock";
export const PHX_PENDING_REFS = "phx-pending-refs";
export const PHX_TRACK_UPLOADS = "track-uploads";
export const PHX_UPLOAD_REF = "data-phx-upload-ref";
export const PHX_PREFLIGHTED_REFS = "data-phx-preflighted-refs";
export const PHX_DONE_REFS = "data-phx-done-refs";
export const PHX_DROP_TARGET = "drop-target";
export const PHX_ACTIVE_ENTRY_REFS = "data-phx-active-refs";
export const PHX_LIVE_FILE_UPDATED = "phx:live-file:updated";
export const PHX_SKIP = "data-phx-skip";
export const PHX_MAGIC_ID = "data-phx-id";
export const PHX_PRUNE = "data-phx-prune";
export const PHX_CONNECTED_CLASS = "phx-connected";
export const PHX_LOADING_CLASS = "phx-loading";
export const PHX_ERROR_CLASS = "phx-error";
export const PHX_CLIENT_ERROR_CLASS = "phx-client-error";
export const PHX_SERVER_ERROR_CLASS = "phx-server-error";
export const PHX_PARENT_ID = "data-phx-parent-id";
export const PHX_MAIN = "data-phx-main";
export const PHX_ROOT_ID = "data-phx-root-id";
export const PHX_VIEWPORT_TOP = "viewport-top";
export const PHX_VIEWPORT_BOTTOM = "viewport-bottom";
export const PHX_VIEWPORT_OVERRUN_TARGET = "viewport-overrun-target";
export const PHX_TRIGGER_ACTION = "trigger-action";
export const PHX_HAS_FOCUSED = "phx-has-focused";
export const FOCUSABLE_INPUTS = [
  "text",
  "textarea",
  "number",
  "email",
  "password",
  "search",
  "tel",
  "url",
  "date",
  "time",
  "datetime-local",
  "color",
  "range",
];
export const CHECKABLE_INPUTS = ["checkbox", "radio"];
export const PHX_HAS_SUBMITTED = "phx-has-submitted";
export const PHX_SESSION = "data-phx-session";
export const PHX_VIEW_SELECTOR = `[${PHX_SESSION}]`;
export const PHX_STICKY = "data-phx-sticky";
export const PHX_STATIC = "data-phx-static";
export const PHX_READONLY = "data-phx-readonly";
export const PHX_DISABLED = "data-phx-disabled";
export const PHX_DISABLE_WITH = "disable-with";
export const PHX_DISABLE_WITH_RESTORE = "data-phx-disable-with-restore";
export const PHX_HOOK = "hook";
export const PHX_DEBOUNCE = "debounce";
export const PHX_THROTTLE = "throttle";
export const PHX_UPDATE = "update";
export const PHX_STREAM = "stream";
export const PHX_STREAM_REF = "data-phx-stream";
export const PHX_PORTAL = "data-phx-portal";
export const PHX_TELEPORTED_REF = "data-phx-teleported";
export const PHX_TELEPORTED_SRC = "data-phx-teleported-src";
export const PHX_RUNTIME_HOOK = "data-phx-runtime-hook";
export const PHX_LV_PID = "data-phx-pid";
export const PHX_KEY = "key";
export const PHX_PRIVATE = "phxPrivate";
export const PHX_AUTO_RECOVER = "auto-recover";
export const PHX_NO_UNUSED_FIELD = "no-unused-field";
export const PHX_LV_DEBUG = "phx:live-socket:debug";
export const PHX_LV_PROFILE = "phx:live-socket:profiling";
export const PHX_LV_LATENCY_SIM = "phx:live-socket:latency-sim";
export const PHX_LV_HISTORY_POSITION = "phx:nav-history-position";
export const PHX_PROGRESS = "progress";
export const PHX_MOUNTED = "mounted";
export const PHX_RELOAD_STATUS = "__phoenix_reload_status__";
export const LOADER_TIMEOUT = 1;
export const MAX_CHILD_JOIN_ATTEMPTS = 3;
export const BEFORE_UNLOAD_LOADER_TIMEOUT = 200;
export const DISCONNECTED_TIMEOUT = 500;
export const BINDING_PREFIX = "phx-";
export const PUSH_TIMEOUT = 30000;
export const LINK_HEADER = "x-requested-with";
export const RESPONSE_URL_HEADER = "x-response-url";
export const DEBOUNCE_TRIGGER = "debounce-trigger";
export const THROTTLED = "throttled";
export const DEBOUNCE_PREV_KEY = "debounce-prev-key";
export const DEFAULTS = {
  debounce: 300,
  throttle: 300,
};
export const PHX_PENDING_ATTRS = [PHX_REF_LOADING, PHX_REF_SRC, PHX_REF_LOCK];
// Rendered
export const STATIC = "s";
export const ROOT = "r";
export const COMPONENTS = "c";
export const KEYED = "k";
export const KEYED_COUNT = "kc";
export const EVENTS = "e";
export const REPLY = "r";
export const TITLE = "t";
export const TEMPLATES = "p";
export const STREAM = "stream";


================================================
FILE: assets/js/phoenix_live_view/dom.js
================================================
import {
  CHECKABLE_INPUTS,
  DEBOUNCE_PREV_KEY,
  DEBOUNCE_TRIGGER,
  FOCUSABLE_INPUTS,
  PHX_COMPONENT,
  PHX_VIEW_REF,
  PHX_TELEPORTED_REF,
  PHX_HAS_FOCUSED,
  PHX_HAS_SUBMITTED,
  PHX_MAIN,
  PHX_PARENT_ID,
  PHX_PRIVATE,
  PHX_REF_SRC,
  PHX_REF_LOCK,
  PHX_PENDING_ATTRS,
  PHX_ROOT_ID,
  PHX_SESSION,
  PHX_STATIC,
  PHX_UPLOAD_REF,
  PHX_VIEW_SELECTOR,
  PHX_STICKY,
  PHX_EVENT_CLASSES,
  THROTTLED,
  PHX_PORTAL,
  PHX_STREAM,
} from "./constants";

import { logError } from "./utils";

const DOM = {
  byId(id) {
    return document.getElementById(id) || logError(`no id found for ${id}`);
  },

  removeClass(el, className) {
    el.classList.remove(className);
    if (el.classList.length === 0) {
      el.removeAttribute("class");
    }
  },

  all(node, query, callback) {
    if (!node) {
      return [];
    }
    const array = Array.from(node.querySelectorAll(query));
    if (callback) {
      array.forEach(callback);
    }
    return array;
  },

  childNodeLength(html) {
    const template = document.createElement("template");
    template.innerHTML = html;
    return template.content.childElementCount;
  },

  isUploadInput(el) {
    return el.type === "file" && el.getAttribute(PHX_UPLOAD_REF) !== null;
  },

  isAutoUpload(inputEl) {
    return inputEl.hasAttribute("data-phx-auto-upload");
  },

  findUploadInputs(node) {
    const formId = node.id;
    const inputsOutsideForm = this.all(
      document,
      `input[type="file"][${PHX_UPLOAD_REF}][form="${formId}"]`,
    );
    return this.all(node, `input[type="file"][${PHX_UPLOAD_REF}]`).concat(
      inputsOutsideForm,
    );
  },

  findComponentNodeList(viewId, cid, doc = document) {
    return this.all(
      doc,
      `[${PHX_VIEW_REF}="${viewId}"][${PHX_COMPONENT}="${cid}"]`,
    );
  },

  isPhxDestroyed(node) {
    return node.id && DOM.private(node, "destroyed") ? true : false;
  },

  wantsNewTab(e) {
    const wantsNewTab =
      e.ctrlKey || e.shiftKey || e.metaKey || (e.button && e.button === 1);
    const isDownload =
      e.target instanceof HTMLAnchorElement &&
      e.target.hasAttribute("download");
    const isTargetBlank =
      e.target.hasAttribute("target") &&
      e.target.getAttribute("target").toLowerCase() === "_blank";
    const isTargetNamedTab =
      e.target.hasAttribute("target") &&
      !e.target.getAttribute("target").startsWith("_");
    return wantsNewTab || isTargetBlank || isDownload || isTargetNamedTab;
  },

  isUnloadableFormSubmit(e) {
    // Ignore form submissions intended to close a native <dialog> element
    // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog#usage_notes
    const isDialogSubmit =
      (e.target && e.target.getAttribute("method") === "dialog") ||
      (e.submitter && e.submitter.getAttribute("formmethod") === "dialog");

    if (isDialogSubmit) {
      return false;
    } else {
      return !e.defaultPrevented && !this.wantsNewTab(e);
    }
  },

  isNewPageClick(e, currentLocation) {
    const href =
      e.target instanceof HTMLAnchorElement
        ? e.target.getAttribute("href")
        : null;
    let url;

    if (e.defaultPrevented || href === null || this.wantsNewTab(e)) {
      return false;
    }
    if (href.startsWith("mailto:") || href.startsWith("tel:")) {
      return false;
    }
    if (e.target.isContentEditable) {
      return false;
    }

    try {
      url = new URL(href);
    } catch {
      try {
        url = new URL(href, currentLocation);
      } catch {
        // bad URL, fallback to let browser try it as external
        return true;
      }
    }

    if (
      url.host === currentLocation.host &&
      url.protocol === currentLocation.protocol
    ) {
      if (
        url.pathname === currentLocation.pathname &&
        url.search === currentLocation.search
      ) {
        return url.hash === "" && !url.href.endsWith("#");
      }
    }
    return url.protocol.startsWith("http");
  },

  markPhxChildDestroyed(el) {
    if (this.isPhxChild(el)) {
      el.setAttribute(PHX_SESSION, "");
    }
    this.putPrivate(el, "destroyed", true);
  },

  findPhxChildrenInFragment(html, parentId) {
    const template = document.createElement("template");
    template.innerHTML = html;
    return this.findPhxChildren(template.content, parentId);
  },

  isIgnored(el, phxUpdate) {
    return (
      (el.getAttribute(phxUpdate) || el.getAttribute("data-phx-update")) ===
      "ignore"
    );
  },

  isPhxUpdate(el, phxUpdate, updateTypes) {
    return (
      el.getAttribute && updateTypes.indexOf(el.getAttribute(phxUpdate)) >= 0
    );
  },

  findPhxSticky(el) {
    return this.all(el, `[${PHX_STICKY}]`);
  },

  findPhxChildren(el, parentId) {
    return this.all(el, `${PHX_VIEW_SELECTOR}[${PHX_PARENT_ID}="${parentId}"]`);
  },

  findExistingParentCIDs(viewId, cids) {
    // we only want to find parents that exist on the page
    // if a cid is not on the page, the only way it can be added back to the page
    // is if a parent adds it back, therefore if a cid does not exist on the page,
    // we should not try to render it by itself (because it would be rendered twice,
    // one by the parent, and a second time by itself)
    const parentCids = new Set();
    const childrenCids = new Set();

    cids.forEach((cid) => {
      this.all(
        document,
        `[${PHX_VIEW_REF}="${viewId}"][${PHX_COMPONENT}="${cid}"]`,
      ).forEach((parent) => {
        parentCids.add(cid);
        this.all(parent, `[${PHX_VIEW_REF}="${viewId}"][${PHX_COMPONENT}]`)
          .map((el) => parseInt(el.getAttribute(PHX_COMPONENT)))
          .forEach((childCID) => childrenCids.add(childCID));
      });
    });

    childrenCids.forEach((childCid) => parentCids.delete(childCid));

    return parentCids;
  },

  private(el, key) {
    return el[PHX_PRIVATE] && el[PHX_PRIVATE][key];
  },

  deletePrivate(el, key) {
    el[PHX_PRIVATE] && delete el[PHX_PRIVATE][key];
  },

  putPrivate(el, key, value) {
    if (!el[PHX_PRIVATE]) {
      el[PHX_PRIVATE] = {};
    }
    el[PHX_PRIVATE][key] = value;
  },

  updatePrivate(el, key, defaultVal, updateFunc) {
    const existing = this.private(el, key);
    if (existing === undefined) {
      this.putPrivate(el, key, updateFunc(defaultVal));
    } else {
      this.putPrivate(el, key, updateFunc(existing));
    }
  },

  syncPendingAttrs(fromEl, toEl) {
    if (!fromEl.hasAttribute(PHX_REF_SRC)) {
      return;
    }
    PHX_EVENT_CLASSES.forEach((className) => {
      fromEl.classList.contains(className) && toEl.classList.add(className);
    });
    PHX_PENDING_ATTRS.filter((attr) => fromEl.hasAttribute(attr)).forEach(
      (attr) => {
        toEl.setAttribute(attr, fromEl.getAttribute(attr));
      },
    );
  },

  copyPrivates(target, source) {
    if (source[PHX_PRIVATE]) {
      target[PHX_PRIVATE] = source[PHX_PRIVATE];
    }
  },

  putTitle(str) {
    const titleEl = document.querySelector("title");
    if (titleEl) {
      const { prefix, suffix, default: defaultTitle } = titleEl.dataset;
      const isEmpty = typeof str !== "string" || str.trim() === "";
      if (isEmpty && typeof defaultTitle !== "string") {
        return;
      }

      const inner = isEmpty ? defaultTitle : str;
      document.title = `${prefix || ""}${inner || ""}${suffix || ""}`;
    } else {
      document.title = str;
    }
  },

  debounce(
    el,
    event,
    phxDebounce,
    defaultDebounce,
    phxThrottle,
    defaultThrottle,
    asyncFilter,
    callback,
  ) {
    let debounce = el.getAttribute(phxDebounce);
    let throttle = el.getAttribute(phxThrottle);

    if (debounce === "") {
      debounce = defaultDebounce;
    }
    if (throttle === "") {
      throttle = defaultThrottle;
    }
    const value = debounce || throttle;
    switch (value) {
      case null:
        return callback();

      case "blur":
        this.incCycle(el, "debounce-blur-cycle", () => {
          if (asyncFilter()) {
            callback();
          }
        });
        if (this.once(el, "debounce-blur")) {
          el.addEventListener("blur", () =>
            this.triggerCycle(el, "debounce-blur-cycle"),
          );
        }
        return;

      default:
        const timeout = parseInt(value);
        const trigger = () =>
          throttle ? this.deletePrivate(el, THROTTLED) : callback();
        const currentCycle = this.incCycle(el, DEBOUNCE_TRIGGER, trigger);
        if (isNaN(timeout)) {
          return logError(`invalid throttle/debounce value: ${value}`);
        }
        if (throttle) {
          let newKeyDown = false;
          if (event.type === "keydown") {
            const prevKey = this.private(el, DEBOUNCE_PREV_KEY);
            this.putPrivate(el, DEBOUNCE_PREV_KEY, event.key);
            newKeyDown = prevKey !== event.key;
          }

          if (!newKeyDown && this.private(el, THROTTLED)) {
            return false;
          } else {
            callback();
            const t = setTimeout(() => {
              if (asyncFilter()) {
                this.triggerCycle(el, DEBOUNCE_TRIGGER);
              }
            }, timeout);
            this.putPrivate(el, THROTTLED, t);
          }
        } else {
          setTimeout(() => {
            if (asyncFilter()) {
              this.triggerCycle(el, DEBOUNCE_TRIGGER, currentCycle);
            }
          }, timeout);
        }

        const form = el.form;
        if (form && this.once(form, "bind-debounce")) {
          form.addEventListener("submit", () => {
            Array.from(new FormData(form).entries(), ([name]) => {
              const namedItem = form.elements.namedItem(name);
              const input =
                namedItem instanceof RadioNodeList ? namedItem[0] : namedItem;
              if (input) {
                this.incCycle(input, DEBOUNCE_TRIGGER);
                this.deletePrivate(input, THROTTLED);
              }
            });
          });
        }
        if (this.once(el, "bind-debounce")) {
          el.addEventListener("blur", () => {
            // because we trigger the callback here,
            // we also clear the throttle timeout to prevent the callback
            // from being called again after the timeout fires
            clearTimeout(this.private(el, THROTTLED));
            this.triggerCycle(el, DEBOUNCE_TRIGGER);
          });
        }
    }
  },

  triggerCycle(el, key, currentCycle) {
    const [cycle, trigger] = this.private(el, key);
    if (!currentCycle) {
      currentCycle = cycle;
    }
    if (currentCycle === cycle) {
      this.incCycle(el, key);
      trigger();
    }
  },

  once(el, key) {
    if (this.private(el, key) === true) {
      return false;
    }
    this.putPrivate(el, key, true);
    return true;
  },

  incCycle(el, key, trigger = function () {}) {
    let [currentCycle] = this.private(el, key) || [0, trigger];
    currentCycle++;
    this.putPrivate(el, key, [currentCycle, trigger]);
    return currentCycle;
  },

  // maintains or adds privately used hook information
  // fromEl and toEl can be the same element in the case of a newly added node
  // fromEl and toEl can be any HTML node type, so we need to check if it's an element node
  maintainPrivateHooks(fromEl, toEl, phxViewportTop, phxViewportBottom) {
    // maintain the hooks created with createHook
    if (
      fromEl.hasAttribute &&
      fromEl.hasAttribute("data-phx-hook") &&
      !toEl.hasAttribute("data-phx-hook")
    ) {
      toEl.setAttribute("data-phx-hook", fromEl.getAttribute("data-phx-hook"));
    }
    // add hooks to elements with viewport attributes
    if (
      toEl.hasAttribute &&
      (toEl.hasAttribute(phxViewportTop) ||
        toEl.hasAttribute(phxViewportBottom))
    ) {
      toEl.setAttribute("data-phx-hook", "Phoenix.InfiniteScroll");
    }
  },

  putCustomElHook(el, hook) {
    if (el.isConnected) {
      el.setAttribute("data-phx-hook", "");
    } else {
      console.error(`
        hook attached to non-connected DOM element
        ensure you are calling createHook within your connectedCallback. ${el.outerHTML}
      `);
    }
    this.putPrivate(el, "custom-el-hook", hook);
  },

  getCustomElHook(el) {
    return this.private(el, "custom-el-hook");
  },

  isUsedInput(el) {
    return (
      el.nodeType === Node.ELEMENT_NODE &&
      (this.private(el, PHX_HAS_FOCUSED) || this.private(el, PHX_HAS_SUBMITTED))
    );
  },

  resetForm(form) {
    Array.from(form.elements).forEach((input) => {
      this.deletePrivate(input, PHX_HAS_FOCUSED);
      this.deletePrivate(input, PHX_HAS_SUBMITTED);
    });
  },

  isPhxChild(node) {
    return node.getAttribute && node.getAttribute(PHX_PARENT_ID);
  },

  isPhxSticky(node) {
    return node.getAttribute && node.getAttribute(PHX_STICKY) !== null;
  },

  isChildOfAny(el, parents) {
    return !!parents.find((parent) => parent.contains(el));
  },

  firstPhxChild(el) {
    return this.isPhxChild(el) ? el : this.all(el, `[${PHX_PARENT_ID}]`)[0];
  },

  isPortalTemplate(el) {
    return el.tagName === "TEMPLATE" && el.hasAttribute(PHX_PORTAL);
  },

  closestViewEl(el) {
    // find the closest portal or view element, whichever comes first
    const portalOrViewEl = el.closest(
      `[${PHX_TELEPORTED_REF}],${PHX_VIEW_SELECTOR}`,
    );
    if (!portalOrViewEl) {
      return null;
    }
    if (portalOrViewEl.hasAttribute(PHX_TELEPORTED_REF)) {
      // PHX_TELEPORTED_REF is set to the id of the view that owns the portal element
      return this.byId(portalOrViewEl.getAttribute(PHX_TELEPORTED_REF));
    } else if (portalOrViewEl.hasAttribute(PHX_SESSION)) {
      return portalOrViewEl;
    }
    return null;
  },

  dispatchEvent(target, name, opts = {}) {
    let defaultBubble = true;
    const isUploadTarget =
      target.nodeName === "INPUT" && target.type === "file";
    if (isUploadTarget && name === "click") {
      defaultBubble = false;
    }
    const bubbles = opts.bubbles === undefined ? defaultBubble : !!opts.bubbles;
    const eventOpts = {
      bubbles: bubbles,
      cancelable: true,
      detail: opts.detail || {},
    };
    const event =
      name === "click"
        ? new MouseEvent("click", eventOpts)
        : new CustomEvent(name, eventOpts);
    target.dispatchEvent(event);
  },

  cloneNode(node, html) {
    if (typeof html === "undefined") {
      return node.cloneNode(true);
    } else {
      const cloned = node.cloneNode(false);
      cloned.innerHTML = html;
      return cloned;
    }
  },

  // merge attributes from source to target
  // if an element is ignored, we only merge data attributes
  // including removing data attributes that are no longer in the source
  mergeAttrs(target, source, opts = {}) {
    const exclude = new Set(opts.exclude || []);
    const isIgnored = opts.isIgnored;
    const sourceAttrs = source.attributes;
    for (let i = sourceAttrs.length - 1; i >= 0; i--) {
      const name = sourceAttrs[i].name;
      if (!exclude.has(name)) {
        const sourceValue = source.getAttribute(name);
        if (
          target.getAttribute(name) !== sourceValue &&
          (!isIgnored || (isIgnored && name.startsWith("data-")))
        ) {
          target.setAttribute(name, sourceValue);
        }
      } else {
        // We exclude the value from being merged on focused inputs, because the
        // user's input should always win.
        // We can still assign it as long as the value property is the same, though.
        // This prevents a situation where the updated hook is not being triggered
        // when an input is back in its "original state", because the attribute
        // was never changed, see:
        // https://github.com/phoenixframework/phoenix_live_view/issues/2163
        if (name === "value") {
          const sourceValue = source.value ?? source.getAttribute(name);
          if (target.value === sourceValue) {
            // actually set the value attribute to sync it with the value property
            target.setAttribute("value", source.getAttribute(name));
          }
        }
      }
    }

    const targetAttrs = target.attributes;
    for (let i = targetAttrs.length - 1; i >= 0; i--) {
      const name = targetAttrs[i].name;
      if (isIgnored) {
        if (
          name.startsWith("data-") &&
          !source.hasAttribute(name) &&
          !PHX_PENDING_ATTRS.includes(name)
        ) {
          target.removeAttribute(name);
        }
      } else {
        if (!source.hasAttribute(name)) {
          target.removeAttribute(name);
        }
      }
    }
  },

  mergeFocusedInput(target, source) {
    // skip selects because FF will reset highlighted index for any setAttribute
    if (!(target instanceof HTMLSelectElement)) {
      DOM.mergeAttrs(target, source, { exclude: ["value"] });
    }

    if (source.readOnly) {
      target.setAttribute("readonly", true);
    } else {
      target.removeAttribute("readonly");
    }
  },

  hasSelectionRange(el) {
    return (
      el.setSelectionRange && (el.type === "text" || el.type === "textarea")
    );
  },

  restoreFocus(focused, selectionStart, selectionEnd) {
    if (focused instanceof HTMLSelectElement) {
      focused.focus();
    }
    if (!DOM.isTextualInput(focused)) {
      return;
    }

    const wasFocused = focused.matches(":focus");
    if (!wasFocused) {
      focused.focus();
    }
    if (this.hasSelectionRange(focused)) {
      focused.setSelectionRange(selectionStart, selectionEnd);
    }
  },

  isFormInput(el) {
    if (el.localName && customElements.get(el.localName)) {
      // Custom Elements may be form associated. This allows them
      // to participate within a form's lifecycle, including form
      // validity and form submissions.
      // The spec for Form Associated custom elements requires the
      // custom element's class to contain a static boolean value of `formAssociated`
      // which identifies this class as allowed to associate to a form.
      // See https://html.spec.whatwg.org/dev/custom-elements.html#custom-elements-face-example
      // for details.
      return customElements.get(el.localName)[`formAssociated`];
    }

    return (
      /^(?:input|select|textarea)$/i.test(el.tagName) && el.type !== "button"
    );
  },

  syncAttrsToProps(el) {
    if (
      el instanceof HTMLInputElement &&
      CHECKABLE_INPUTS.indexOf(el.type.toLocaleLowerCase()) >= 0
    ) {
      el.checked = el.getAttribute("checked") !== null;
    }
  },

  isTextualInput(el) {
    return FOCUSABLE_INPUTS.indexOf(el.type) >= 0;
  },

  isNowTriggerFormExternal(el, phxTriggerExternal) {
    return (
      el.getAttribute &&
      el.getAttribute(phxTriggerExternal) !== null &&
      document.body.contains(el)
    );
  },

  cleanChildNodes(container, phxUpdate) {
    if (
      DOM.isPhxUpdate(container, phxUpdate, ["append", "prepend", PHX_STREAM])
    ) {
      const toRemove = [];
      container.childNodes.forEach((childNode) => {
        if (!childNode.id) {
          // Skip warning if it's an empty text node (e.g. a new-line)
          const isEmptyTextNode =
            childNode.nodeType === Node.TEXT_NODE &&
            childNode.nodeValue.trim() === "";
          if (!isEmptyTextNode && childNode.nodeType !== Node.COMMENT_NODE) {
            logError(
              "only HTML element tags with an id are allowed inside containers with phx-update.\n\n" +
                `removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"\n\n`,
            );
          }
          toRemove.push(childNode);
        }
      });
      toRemove.forEach((childNode) => childNode.remove());
    }
  },

  replaceRootContainer(container, tagName, attrs) {
    const retainedAttrs = new Set([
      "id",
      PHX_SESSION,
      PHX_STATIC,
      PHX_MAIN,
      PHX_ROOT_ID,
    ]);
    if (container.tagName.toLowerCase() === tagName.toLowerCase()) {
      Array.from(container.attributes)
        .filter((attr) => !retainedAttrs.has(attr.name.toLowerCase()))
        .forEach((attr) => container.removeAttribute(attr.name));

      Object.keys(attrs)
        .filter((name) => !retainedAttrs.has(name.toLowerCase()))
        .forEach((attr) => container.setAttribute(attr, attrs[attr]));

      return container;
    } else {
      const newContainer = document.createElement(tagName);
      Object.keys(attrs).forEach((attr) =>
        newContainer.setAttribute(attr, attrs[attr]),
      );
      retainedAttrs.forEach((attr) =>
        newContainer.setAttribute(attr, container.getAttribute(attr)),
      );
      newContainer.innerHTML = container.innerHTML;
      container.replaceWith(newContainer);
      return newContainer;
    }
  },

  getSticky(el, name, defaultVal) {
    const op = (DOM.private(el, "sticky") || []).find(
      ([existingName]) => name === existingName,
    );
    if (op) {
      const [_name, _op, stashedResult] = op;
      return stashedResult;
    } else {
      return typeof defaultVal === "function" ? defaultVal() : defaultVal;
    }
  },

  deleteSticky(el, name) {
    this.updatePrivate(el, "sticky", [], (ops) => {
      return ops.filter(([existingName, _]) => existingName !== name);
    });
  },

  putSticky(el, name, op) {
    const stashedResult = op(el);
    this.updatePrivate(el, "sticky", [], (ops) => {
      const existingIndex = ops.findIndex(
        ([existingName]) => name === existingName,
      );
      if (existingIndex >= 0) {
        ops[existingIndex] = [name, op, stashedResult];
      } else {
        ops.push([name, op, stashedResult]);
      }
      return ops;
    });
  },

  applyStickyOperations(el) {
    const ops = DOM.private(el, "sticky");
    if (!ops) {
      return;
    }

    ops.forEach(([name, op, _stashed]) => this.putSticky(el, name, op));
  },

  isLocked(el) {
    return el.hasAttribute && el.hasAttribute(PHX_REF_LOCK);
  },

  attributeIgnored(attribute, ignoredAttributes) {
    return ignoredAttributes.some(
      (toIgnore) =>
        attribute.name == toIgnore ||
        toIgnore === "*" ||
        (toIgnore.includes("*") && attribute.name.match(toIgnore) != null),
    );
  },
};

export default DOM;


================================================
FILE: assets/js/phoenix_live_view/dom_patch.js
================================================
import {
  PHX_COMPONENT,
  PHX_PRUNE,
  PHX_ROOT_ID,
  PHX_SESSION,
  PHX_SKIP,
  PHX_MAGIC_ID,
  PHX_STATIC,
  PHX_TRIGGER_ACTION,
  PHX_UPDATE,
  PHX_REF_SRC,
  PHX_REF_LOCK,
  PHX_STREAM,
  PHX_STREAM_REF,
  PHX_VIEWPORT_TOP,
  PHX_VIEWPORT_BOTTOM,
  PHX_PORTAL,
  PHX_TELEPORTED_REF,
  PHX_TELEPORTED_SRC,
  PHX_RUNTIME_HOOK,
} from "./constants";

import { detectDuplicateIds, detectInvalidStreamInserts, isCid } from "./utils";
import ElementRef from "./element_ref";
import DOM from "./dom";
import DOMPostMorphRestorer from "./dom_post_morph_restorer";
import morphdom from "morphdom";

export default class DOMPatch {
  constructor(view, container, id, html, streams, targetCID, opts = {}) {
    this.view = view;
    this.liveSocket = view.liveSocket;
    this.container = container;
    this.id = id;
    this.rootID = view.root.id;
    this.html = html;
    this.streams = streams;
    this.streamInserts = {};
    this.streamComponentRestore = {};
    this.targetCID = targetCID;
    this.cidPatch = isCid(this.targetCID);
    this.pendingRemoves = [];
    this.phxRemove = this.liveSocket.binding("remove");
    this.targetContainer = this.isCIDPatch()
      ? this.targetCIDContainer(html)
      : container;
    this.callbacks = {
      beforeadded: [],
      beforeupdated: [],
      beforephxChildAdded: [],
      afteradded: [],
      afterupdated: [],
      afterdiscarded: [],
      afterphxChildAdded: [],
      aftertransitionsDiscarded: [],
    };
    this.withChildren = opts.withChildren || opts.undoRef || false;
    this.undoRef = opts.undoRef;
  }

  before(kind, callback) {
    this.callbacks[`before${kind}`].push(callback);
  }
  after(kind, callback) {
    this.callbacks[`after${kind}`].push(callback);
  }

  trackBefore(kind, ...args) {
    this.callbacks[`before${kind}`].forEach((callback) => callback(...args));
  }

  trackAfter(kind, ...args) {
    this.callbacks[`after${kind}`].forEach((callback) => callback(...args));
  }

  markPrunableContentForRemoval() {
    const phxUpdate = this.liveSocket.binding(PHX_UPDATE);
    DOM.all(
      this.container,
      `[${phxUpdate}=append] > *, [${phxUpdate}=prepend] > *`,
      (el) => {
        el.setAttribute(PHX_PRUNE, "");
      },
    );
  }

  perform(isJoinPatch) {
    const { view, liveSocket, html, container } = this;
    let targetContainer = this.targetContainer;

    if (this.isCIDPatch() && !this.targetContainer) {
      return;
    }

    if (this.isCIDPatch()) {
      // https://github.com/phoenixframework/phoenix_live_view/pull/3942
      // we need to ensure that no parent is locked
      const closestLock = targetContainer.closest(`[${PHX_REF_LOCK}]`);
      // If the targetContainer itself is locked, that's okay.
      // https://github.com/phoenixframework/phoenix_live_view/issues/4088
      if (closestLock && !closestLock.isSameNode(targetContainer)) {
        const clonedTree = DOM.private(closestLock, PHX_REF_LOCK);
        if (clonedTree) {
          // if a parent is locked with a cloned tree, we need to patch the cloned tree instead
          targetContainer = clonedTree.querySelector(
            `[data-phx-component="${this.targetCID}"]`,
          );
        }
      }
    }

    const focused = liveSocket.getActiveElement();
    const { selectionStart, selectionEnd } =
      focused && DOM.hasSelectionRange(focused) ? focused : {};
    const phxUpdate = liveSocket.binding(PHX_UPDATE);
    const phxViewportTop = liveSocket.binding(PHX_VIEWPORT_TOP);
    const phxViewportBottom = liveSocket.binding(PHX_VIEWPORT_BOTTOM);
    const phxTriggerExternal = liveSocket.binding(PHX_TRIGGER_ACTION);
    const added = [];
    const updates = [];
    const appendPrependUpdates = [];

    // as the portal target itself could be at the end of the DOM,
    // it may not be present while morphing previous parts;
    // therefore we apply all teleports after the morphing is done+
    let portalCallbacks = [];

    let externalFormTriggered = null;

    const morph = (
      targetContainer,
      source,
      withChildren = this.withChildren,
    ) => {
      const morphCallbacks = {
        // normally, we are running with childrenOnly, as the patch HTML for a LV
        // does not include the LV attrs (data-phx-session, etc.)
        // when we are patching a live component, we do want to patch the root element as well;
        // another case is the recursive patch of a stream item that was kept on reset (-> onBeforeNodeAdded)
        childrenOnly:
          targetContainer.getAttribute(PHX_COMPONENT) === null && !withChildren,
        getNodeKey: (node) => {
          if (DOM.isPhxDestroyed(node)) {
            return null;
          }
          // If we have a join patch, then by definition there was no PHX_MAGIC_ID.
          // This is important to reduce the amount of elements morphdom discards.
          if (isJoinPatch) {
            return node.id;
          }
          return (
            node.id || (node.getAttribute && node.getAttribute(PHX_MAGIC_ID))
          );
        },
        // skip indexing from children when container is stream
        skipFromChildren: (from) => {
          return from.getAttribute(phxUpdate) === PHX_STREAM;
        },
        // tell morphdom how to add a child
        addChild: (parent, child) => {
          const { ref, streamAt } = this.getStreamInsert(child);
          if (ref === undefined) {
            return parent.appendChild(child);
          }

          this.setStreamRef(child, ref);

          // streaming
          if (streamAt === 0) {
            parent.insertAdjacentElement("afterbegin", child);
          } else if (streamAt === -1) {
            const lastChild = parent.lastElementChild;
            if (lastChild && !lastChild.hasAttribute(PHX_STREAM_REF)) {
              const nonStreamChild = Array.from(parent.children).find(
                (c) => !c.hasAttribute(PHX_STREAM_REF),
              );
              parent.insertBefore(child, nonStreamChild);
            } else {
              parent.appendChild(child);
            }
          } else if (streamAt > 0) {
            const sibling = Array.from(parent.children)[streamAt];
            parent.insertBefore(child, sibling);
          }
        },
        onBeforeNodeAdded: (el) => {
          // don't add update_only nodes if they did not already exist
          if (
            this.getStreamInsert(el)?.updateOnly &&
            !this.streamComponentRestore[el.id]
          ) {
            return false;
          }

          DOM.maintainPrivateHooks(el, el, phxViewportTop, phxViewportBottom);
          this.trackBefore("added", el);

          let morphedEl = el;
          // this is a stream item that was kept on reset, recursively morph it
          if (this.streamComponentRestore[el.id]) {
            morphedEl = this.streamComponentRestore[el.id];
            delete this.streamComponentRestore[el.id];
            morph(morphedEl, el, true);
          }

          return morphedEl;
        },
        onNodeAdded: (el) => {
          if (el.getAttribute) {
            this.maybeReOrderStream(el, true);
          }
          // phx-portal handling
          if (DOM.isPortalTemplate(el)) {
            portalCallbacks.push(() => this.teleport(el, morph));
          }

          // hack to fix Safari handling of img srcset and video tags
          if (el instanceof HTMLImageElement && el.srcset) {
            // eslint-disable-next-line no-self-assign
            el.srcset = el.srcset;
          } else if (el instanceof HTMLVideoElement && el.autoplay) {
            el.play();
          }
          if (DOM.isNowTriggerFormExternal(el, phxTriggerExternal)) {
            externalFormTriggered = el;
          }

          // nested view handling
          if (
            (DOM.isPhxChild(el) && view.ownsElement(el)) ||
            (DOM.isPhxSticky(el) && view.ownsElement(el.parentNode))
          ) {
            this.trackAfter("phxChildAdded", el);
          }

          // data-phx-runtime-hook
          if (el.nodeName === "SCRIPT" && el.hasAttribute(PHX_RUNTIME_HOOK)) {
            this.handleRuntimeHook(el, source);
          }

          added.push(el);
        },
        onNodeDiscarded: (el) => this.onNodeDiscarded(el),
        onBeforeNodeDiscarded: (el) => {
          if (el.getAttribute && el.getAttribute(PHX_PRUNE) !== null) {
            return true;
          }
          if (
            el.parentElement !== null &&
            el.id &&
            DOM.isPhxUpdate(el.parentElement, phxUpdate, [
              PHX_STREAM,
              "append",
              "prepend",
            ])
          ) {
            return false;
          }
          // don't remove teleported elements
          if (el.getAttribute && el.getAttribute(PHX_TELEPORTED_REF)) {
            return false;
          }
          if (this.maybePendingRemove(el)) {
            return false;
          }
          if (this.skipCIDSibling(el)) {
            return false;
          }

          if (DOM.isPortalTemplate(el)) {
            // if the portal template itself is removed, remove the teleported element as well;
            // we also perform a check after morphdom is finished to catch parent removals
            const teleportedEl = document.getElementById(
              el.content.firstElementChild.id,
            );
            if (teleportedEl) {
              teleportedEl.remove();
              morphCallbacks.onNodeDiscarded(teleportedEl);
              this.view.dropPortalElementId(teleportedEl.id);
            }
          }

          return true;
        },
        onElUpdated: (el) => {
          if (DOM.isNowTriggerFormExternal(el, phxTriggerExternal)) {
            externalFormTriggered = el;
          }
          updates.push(el);
          this.maybeReOrderStream(el, false);
        },
        onBeforeElUpdated: (fromEl, toEl) => {
          // if we are patching the root target container and the id has changed, treat it as a new node
          // by replacing the fromEl with the toEl, which ensures hooks are torn down and re-created
          if (
            fromEl.id &&
            fromEl.isSameNode(targetContainer) &&
            fromEl.id !== toEl.id
          ) {
            morphCallbacks.onNodeDiscarded(fromEl);
            fromEl.replaceWith(toEl);
            return morphCallbacks.onNodeAdded(toEl);
          }
          DOM.syncPendingAttrs(fromEl, toEl);
          DOM.maintainPrivateHooks(
            fromEl,
            toEl,
            phxViewportTop,
            phxViewportBottom,
          );
          DOM.cleanChildNodes(toEl, phxUpdate);
          if (this.skipCIDSibling(toEl)) {
            // if this is a live component used in a stream, we may need to reorder it
            this.maybeReOrderStream(fromEl);
            return false;
          }
          if (DOM.isPhxSticky(fromEl)) {
            [PHX_SESSION, PHX_STATIC, PHX_ROOT_ID]
              .map((attr) => [
                attr,
                fromEl.getAttribute(attr),
                toEl.getAttribute(attr),
              ])
              .forEach(([attr, fromVal, toVal]) => {
                if (toVal && fromVal !== toVal) {
                  fromEl.setAttribute(attr, toVal);
                }
              });

            return false;
          }
          if (
            DOM.isIgnored(fromEl, phxUpdate) ||
            (fromEl.form && fromEl.form.isSameNode(externalFormTriggered))
          ) {
            this.trackBefore("updated", fromEl, toEl);
            DOM.mergeAttrs(fromEl, toEl, {
              isIgnored: DOM.isIgnored(fromEl, phxUpdate),
            });
            updates.push(fromEl);
            DOM.applyStickyOperations(fromEl);
            return false;
          }
          if (
            fromEl.type === "number" &&
            fromEl.validity &&
            fromEl.validity.badInput
          ) {
            return false;
          }
          // If the element has PHX_REF_SRC, it is loading or locked and awaiting an ack.
          // If it's locked, we clone the fromEl tree and instruct morphdom to use
          // the cloned tree as the source of the morph for this branch from here on out.
          // We keep a reference to the cloned tree in the element's private data, and
          // on ack (view.undoRefs), we morph the cloned tree with the true fromEl in the DOM to
          // apply any changes that happened while the element was locked.
          const isFocusedFormEl =
            focused && fromEl.isSameNode(focused) && DOM.isFormInput(fromEl);
          const focusedSelectChanged =
            isFocusedFormEl && this.isChangedSelect(fromEl, toEl);
          if (fromEl.hasAttribute(PHX_REF_SRC)) {
            const ref = new ElementRef(fromEl);
            // only perform the clone step if this is not a patch that unlocks
            if (
              ref.lockRef &&
              (!this.undoRef || !ref.isLockUndoneBy(this.undoRef))
            ) {
              DOM.applyStickyOperations(fromEl);
              const isLocked = fromEl.hasAttribute(PHX_REF_LOCK);
              const clone = isLocked
                ? DOM.private(fromEl, PHX_REF_LOCK) || fromEl.cloneNode(true)
                : null;
              if (clone) {
                DOM.putPrivate(fromEl, PHX_REF_LOCK, clone);
                if (!isFocusedFormEl) {
                  fromEl = clone;
                }
              }
            }
          }

          // nested view handling
          if (DOM.isPhxChild(toEl)) {
            const prevSession = fromEl.getAttribute(PHX_SESSION);
            DOM.mergeAttrs(fromEl, toEl, { exclude: [PHX_STATIC] });
            if (prevSession !== "") {
              fromEl.setAttribute(PHX_SESSION, prevSession);
            }
            fromEl.setAttribute(PHX_ROOT_ID, this.rootID);
            DOM.applyStickyOperations(fromEl);
            return false;
          }

          // if we are undoing a lock, copy potentially nested clones over
          if (this.undoRef && DOM.private(toEl, PHX_REF_LOCK)) {
            DOM.putPrivate(
              fromEl,
              PHX_REF_LOCK,
              DOM.private(toEl, PHX_REF_LOCK),
            );
          }
          // now copy regular DOM.private data
          DOM.copyPrivates(toEl, fromEl);

          // phx-portal handling
          if (DOM.isPortalTemplate(toEl)) {
            portalCallbacks.push(() => this.teleport(toEl, morph));
            // for the magicId optimization we need to ensure that the template contents
            // are properly updated as they are used when restoring a cloned tree
            // Note: we can't write fromEl.innerHTML = toEl.innerHTML because in Chrome
            // the HTML parser would drop nested forms, even when it should not.
            // https://issues.chromium.org/issues/490290430
            fromEl.content.replaceChildren(toEl.content.cloneNode(true));
            return false;
          }

          // skip patching focused inputs unless focus is a select that has changed options
          if (
            isFocusedFormEl &&
            fromEl.type !== "hidden" &&
            !focusedSelectChanged
          ) {
            this.trackBefore("updated", fromEl, toEl);
            DOM.mergeFocusedInput(fromEl, toEl);
            DOM.syncAttrsToProps(fromEl);
            updates.push(fromEl);
            DOM.applyStickyOperations(fromEl);
            return false;
          } else {
            // blur focused select if it changed so native UI is updated (ie safari won't update visible options)
            if (focusedSelectChanged) {
              fromEl.blur();
            }
            if (DOM.isPhxUpdate(toEl, phxUpdate, ["append", "prepend"])) {
              appendPrependUpdates.push(
                new DOMPostMorphRestorer(
                  fromEl,
                  toEl,
                  toEl.getAttribute(phxUpdate),
                ),
              );
            }

            DOM.syncAttrsToProps(toEl);
            DOM.applyStickyOperations(toEl);
            this.trackBefore("updated", fromEl, toEl);
            return fromEl;
          }
        },
      };

      morphdom(targetContainer, source, morphCallbacks);
    };

    this.trackBefore("added", container);
    this.trackBefore("updated", container, container);

    liveSocket.time("morphdom", () => {
      this.streams.forEach(([ref, inserts, deleteIds, reset]) => {
        inserts.forEach(([key, streamAt, limit, updateOnly]) => {
          this.streamInserts[key] = { ref, streamAt, limit, reset, updateOnly };
        });
        if (reset !== undefined) {
          DOM.all(document, `[${PHX_STREAM_REF}="${ref}"]`, (child) => {
            this.removeStreamChildElement(child);
          });
        }
        deleteIds.forEach((id) => {
          const child = document.getElementById(id);
          if (child) {
            this.removeStreamChildElement(child);
          }
        });
      });

      // clear stream items from the dead render if they are not inserted again
      if (isJoinPatch) {
        DOM.all(this.container, `[${phxUpdate}=${PHX_STREAM}]`)
          // it is important to filter the element before removing them, as
          // it may happen that streams are nested and the owner check fails if
          // a parent is removed before a child
          .filter((el) => this.view.ownsElement(el))
          .forEach((el) => {
            Array.from(el.children).forEach((child) => {
              // we already performed the owner check, each child is guaranteed to be owned
              // by the view. To prevent the nested owner check from failing in case of nested
              // streams where the parent is removed before the child, we force the removal
              this.removeStreamChildElement(child, true);
            });
          });
      }

      morph(targetContainer, html);

      // normal patch complete, teleport elements now
      // and handle nested teleportation up to depth 5
      let teleportCount = 0;
      while (portalCallbacks.length > 0 && teleportCount < 5) {
        const copy = portalCallbacks.slice();
        portalCallbacks = [];
        copy.forEach((callback) => callback());
        teleportCount++;
      }

      // check for any teleported elements that are not in the view any more
      // and remove them
      this.view.portalElementIds.forEach((id) => {
        const el = document.getElementById(id);
        if (el) {
          const source = document.getElementById(
            el.getAttribute(PHX_TELEPORTED_SRC),
          );
          if (!source) {
            el.remove();
            this.onNodeDiscarded(el);
            this.view.dropPortalElementId(id);
          }
        }
      });
    });

    if (liveSocket.isDebugEnabled()) {
      detectDuplicateIds();
      detectInvalidStreamInserts(this.streamInserts);
      // warn if there are any inputs named "id"
      Array.from(document.querySelectorAll("input[name=id]")).forEach(
        (node) => {
          if (node instanceof HTMLInputElement && node.form) {
            console.error(
              'Detected an input with name="id" inside a form! This will cause problems when patching the DOM.\n',
              node,
            );
          }
        },
      );
    }

    if (appendPrependUpdates.length > 0) {
      liveSocket.time("post-morph append/prepend restoration", () => {
        appendPrependUpdates.forEach((update) => update.perform());
      });
    }

    liveSocket.silenceEvents(() =>
      DOM.restoreFocus(focused, selectionStart, selectionEnd),
    );
    DOM.dispatchEvent(document, "phx:update");
    added.forEach((el) => this.trackAfter("added", el));
    updates.forEach((el) => this.trackAfter("updated", el));

    this.transitionPendingRemoves();

    if (externalFormTriggered) {
      liveSocket.unload();
      // check for submitter and inject it as hidden input for external submit;
      // In theory, it could happen that the stored submitter is outdated and doesn't
      // exist in the DOM any more, but this is unlikely, so we just accept it for now.
      const submitter = DOM.private(externalFormTriggered, "submitter");
      if (submitter && submitter.name && targetContainer.contains(submitter)) {
        const input = document.createElement("input");
        input.type = "hidden";
        const formId = submitter.getAttribute("form");
        if (formId) {
          input.setAttribute("form", formId);
        }
        input.name = submitter.name;
        input.value = submitter.value;
        submitter.parentElement.insertBefore(input, submitter);
      }
      // use prototype's submit in case there's a form control with name or id of "submit"
      // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
      Object.getPrototypeOf(externalFormTriggered).submit.call(
        externalFormTriggered,
      );
    }
    return true;
  }

  onNodeDiscarded(el) {
    // nested view handling
    if (DOM.isPhxChild(el) || DOM.isPhxSticky(el)) {
      this.liveSocket.destroyViewByEl(el);
    }
    this.trackAfter("discarded", el);
  }

  maybePendingRemove(node) {
    if (node.getAttribute && node.getAttribute(this.phxRemove) !== null) {
      this.pendingRemoves.push(node);
      return true;
    } else {
      return false;
    }
  }

  removeStreamChildElement(child, force = false) {
    // make sure to only remove elements owned by the current view
    // see https://github.com/phoenixframework/phoenix_live_view/issues/3047
    // and https://github.com/phoenixframework/phoenix_live_view/issues/3681
    if (!force && !this.view.ownsElement(child)) {
      return;
    }

    // we need to store the node if it is actually re-added in the same patch
    // we do NOT want to execute phx-remove, we do NOT want to call onNodeDiscarded
    if (this.streamInserts[child.id]) {
      this.streamComponentRestore[child.id] = child;
      child.remove();
    } else {
      // only remove the element now if it has no phx-remove binding
      if (!this.maybePendingRemove(child)) {
        child.remove();
        this.onNodeDiscarded(child);
      }
    }
  }

  getStreamInsert(el) {
    const insert = el.id ? this.streamInserts[el.id] : {};
    return insert || {};
  }

  setStreamRef(el, ref) {
    DOM.putSticky(el, PHX_STREAM_REF, (el) =>
      el.setAttribute(PHX_STREAM_REF, ref),
    );
  }

  maybeReOrderStream(el, isNew) {
    const { ref, streamAt, reset } = this.getStreamInsert(el);
    if (streamAt === undefined) {
      return;
    }

    // we need to set the PHX_STREAM_REF here as well as addChild is invoked only for parents
    this.setStreamRef(el, ref);

    if (!reset && !isNew) {
      // we only reorder if the element is new or it's a stream reset
      return;
    }

    // check if the element has a parent element;
    // it doesn't if we are currently recursively morphing (restoring a saved stream child)
    // because the element is not yet added to the real dom;
    // reordering does not make sense in that case anyway
    if (!el.parentElement) {
      return;
    }

    if (streamAt === 0) {
      el.parentElement.insertBefore(el, el.parentElement.firstElementChild);
    } else if (streamAt > 0) {
      const children = Array.from(el.parentElement.children);
      const oldIndex = children.indexOf(el);
      if (streamAt >= children.length - 1) {
        el.parentElement.appendChild(el);
      } else {
        const sibling = children[streamAt];
        if (oldIndex > streamAt) {
          el.parentElement.insertBefore(el, sibling);
        } else {
          el.parentElement.insertBefore(el, sibling.nextElementSibling);
        }
      }
    }

    this.maybeLimitStream(el);
  }

  maybeLimitStream(el) {
    const { limit } = this.getStreamInsert(el);
    const children = limit !== null && Array.from(el.parentElement.children);
    if (limit && limit < 0 && children.length > limit * -1) {
      children
        .slice(0, children.length + limit)
        .forEach((child) => this.removeStreamChildElement(child));
    } else if (limit && limit >= 0 && children.length > limit) {
      children
        .slice(limit)
        .forEach((child) => this.removeStreamChildElement(child));
    }
  }

  transitionPendingRemoves() {
    const { pendingRemoves, liveSocket } = this;
    if (pendingRemoves.length > 0) {
      liveSocket.transitionRemoves(pendingRemoves, () => {
        pendingRemoves.forEach((el) => {
          const child = DOM.firstPhxChild(el);
          if (child) {
            liveSocket.destroyViewByEl(child);
          }
          el.remove();
        });
        this.trackAfter("transitionsDiscarded", pendingRemoves);
      });
    }
  }

  isChangedSelect(fromEl, toEl) {
    if (!(fromEl instanceof HTMLSelectElement) || fromEl.multiple) {
      return false;
    }
    if (fromEl.options.length !== toEl.options.length) {
      return true;
    }

    // keep the current value
    toEl.value = fromEl.value;

    // in general we have to be very careful with using isEqualNode as it does not a reliable
    // DOM tree equality check, but for selection attributes and options it works fine
    return !fromEl.isEqualNode(toEl);
  }

  isCIDPatch() {
    return this.cidPatch;
  }

  skipCIDSibling(el) {
    return el.nodeType === Node.ELEMENT_NODE && el.hasAttribute(PHX_SKIP);
  }

  targetCIDContainer(html) {
    if (!this.isCIDPatch()) {
      return;
    }
    const [first, ...rest] = DOM.findComponentNodeList(
      this.view.id,
      this.targetCID,
    );
    if (rest.length === 0 && DOM.childNodeLength(html) === 1) {
      return first;
    } else {
      return first && first.parentNode;
    }
  }

  indexOf(parent, child) {
    return Array.from(parent.children).indexOf(child);
  }

  teleport(el, morph) {
    const targetSelector = el.getAttribute(PHX_PORTAL);
    const portalContainer = document.querySelector(targetSelector);
    if (!portalContainer) {
      throw new Error(
        "portal target with selector " + targetSelector + " not found",
      );
    }
    // phx-portal templates must have a single root element, so we assume this to be
    // the case here
    const toTeleport = el.content.firstElementChild;
    // the PHX_SKIP optimization can also apply inside of the <template> elements
    if (this.skipCIDSibling(toTeleport)) {
      return;
    }
    if (!toTeleport?.id) {
      throw new Error(
        "phx-portal template must have a single root element with ID!",
      );
    }
    const existing = document.getElementById(toTeleport.id);
    let portalTarget;
    if (existing) {
      // check if the element needs to be moved to another target
      if (!portalContainer.contains(existing)) {
        portalContainer.appendChild(existing);
      }
      // we already teleported in a previous patch
      portalTarget = existing;
    } else {
      // create empty target and morph it recursively
      portalTarget = document.createElement(toTeleport.tagName);
      portalContainer.appendChild(portalTarget);
    }
    // mark the target as teleported;
    // to prevent unnecessary attribute modifications, we set the attribute
    // on the source and remove it after morphing (we could also just keep it)
    // otherwise morphdom would remove it, as the ref is not present in the source
    // and we'd need to set it back after each morph
    toTeleport.setAttribute(PHX_TELEPORTED_REF, this.view.id);
    toTeleport.setAttribute(PHX_TELEPORTED_SRC, el.id);
    morph(portalTarget, toTeleport, true);
    toTeleport.removeAttribute(PHX_TELEPORTED_REF);
    toTeleport.removeAttribute(PHX_TELEPORTED_SRC);
    // store a reference to the teleported element in the view
    // to cleanup when the view is destroyed, in case the portal target
    // is outside the view itself
    this.view.pushPortalElementId(toTeleport.id);
  }

  handleRuntimeHook(el, source) {
    // usually, scripts are not executed when morphdom adds them to the DOM
    // we special case runtime colocated hooks
    const name = el.getAttribute(PHX_RUNTIME_HOOK);
    let nonce = el.hasAttribute("nonce") ? el.getAttribute("nonce") : null;
    if (el.hasAttribute("nonce")) {
      const template = document.createElement("template");
      template.innerHTML = source;
      nonce = template.content
        .querySelector(`script[${PHX_RUNTIME_HOOK}="${CSS.escape(name)}"]`)
        .getAttribute("nonce");
    }
    const script = document.createElement("script");
    script.textContent = el.textContent;
    DOM.mergeAttrs(script, el, { isIgnored: false });
    if (nonce) {
      script.nonce = nonce;
    }
    el.replaceWith(script);
    el = script;
  }
}


================================================
FILE: assets/js/phoenix_live_view/dom_post_morph_restorer.js
================================================
import { maybe } from "./utils";

import DOM from "./dom";

export default class DOMPostMorphRestorer {
  constructor(containerBefore, containerAfter, updateType) {
    const idsBefore = new Set();
    const idsAfter = new Set(
      [...containerAfter.children].map((child) => child.id),
    );

    const elementsToModify = [];

    Array.from(containerBefore.children).forEach((child) => {
      if (child.id) {
        // all of our children should be elements with ids
        idsBefore.add(child.id);
        if (idsAfter.has(child.id)) {
          const previousElementId =
            child.previousElementSibling && child.previousElementSibling.id;
          elementsToModify.push({
            elementId: child.id,
            previousElementId: previousElementId,
          });
        }
      }
    });

    this.containerId = containerAfter.id;
    this.updateType = updateType;
    this.elementsToModify = elementsToModify;
    this.elementIdsToAdd = [...idsAfter].filter((id) => !idsBefore.has(id));
  }

  // We do the following to optimize append/prepend operations:
  //   1) Track ids of modified elements & of new elements
  //   2) All the modified elements are put back in the correct position in the DOM tree
  //      by storing the id of their previous sibling
  //   3) New elements are going to be put in the right place by morphdom during append.
  //      For prepend, we move them to the first position in the container
  perform() {
    const container = DOM.byId(this.containerId);
    if (!container) {
      return;
    }
    this.elementsToModify.forEach((elementToModify) => {
      if (elementToModify.previousElementId) {
        maybe(
          document.getElementById(elementToModify.previousElementId),
          (previousElem) => {
            maybe(
              document.getElementById(elementToModify.elementId),
              (elem) => {
                const isInRightPlace =
                  elem.previousElementSibling &&
                  elem.previousElementSibling.id == previousElem.id;
                if (!isInRightPlace) {
                  previousElem.insertAdjacentElement("afterend", elem);
                }
              },
            );
          },
        );
      } else {
        // This is the first element in the container
        maybe(document.getElementById(elementToModify.elementId), (elem) => {
          const isInRightPlace = elem.previousElementSibling == null;
          if (!isInRightPlace) {
            container.insertAdjacentElement("afterbegin", elem);
          }
        });
      }
    });

    if (this.updateType == "prepend") {
      this.elementIdsToAdd.reverse().forEach((elemId) => {
        maybe(document.getElementById(elemId), (elem) =>
          container.insertAdjacentElement("afterbegin", elem),
        );
      });
    }
  }
}


================================================
FILE: assets/js/phoenix_live_view/element_ref.js
================================================
import {
  PHX_REF_LOADING,
  PHX_REF_LOCK,
  PHX_REF_SRC,
  PHX_PENDING_REFS,
  PHX_EVENT_CLASSES,
  PHX_DISABLED,
  PHX_READONLY,
  PHX_DISABLE_WITH_RESTORE,
} from "./constants";

import DOM from "./dom";

export default class ElementRef {
  static onUnlock(el, callback) {
    if (!DOM.isLocked(el) && !el.closest(`[${PHX_REF_LOCK}]`)) {
      return callback();
    }
    const closestLock = el.closest(`[${PHX_REF_LOCK}]`);
    const ref = closestLock
      .closest(`[${PHX_REF_LOCK}]`)
      .getAttribute(PHX_REF_LOCK);
    closestLock.addEventListener(
      `phx:undo-lock:${ref}`,
      () => {
        callback();
      },
      { once: true },
    );
  }

  constructor(el) {
    this.el = el;
    this.loadingRef = el.hasAttribute(PHX_REF_LOADING)
      ? parseInt(el.getAttribute(PHX_REF_LOADING), 10)
      : null;
    this.lockRef = el.hasAttribute(PHX_REF_LOCK)
      ? parseInt(el.getAttribute(PHX_REF_LOCK), 10)
      : null;
  }

  // public

  maybeUndo(ref, phxEvent, eachCloneCallback) {
    if (!this.isWithin(ref)) {
      // we cannot undo the lock / loading now, as there is a newer one already set;
      // we need to store the original ref we tried to send the undo event later
      DOM.updatePrivate(this.el, PHX_PENDING_REFS, [], (pendingRefs) => {
        pendingRefs.push(ref);
        return pendingRefs;
      });
      return;
    }

    // undo locks and apply clones
    this.undoLocks(ref, phxEvent, eachCloneCallback);

    // undo loading states
    this.undoLoading(ref, phxEvent);

    // ensure undo events are fired for pending refs that
    // are resolved by the current ref, otherwise we'd leak event listeners
    DOM.updatePrivate(this.el, PHX_PENDING_REFS, [], (pendingRefs) => {
      return pendingRefs.filter((pendingRef) => {
        let opts = {
          detail: { ref: pendingRef, event: phxEvent },
          bubbles: true,
          cancelable: false,
        };
        if (this.loadingRef && this.loadingRef > pendingRef) {
          this.el.dispatchEvent(
            new CustomEvent(`phx:undo-loading:${pendingRef}`, opts),
          );
        }
        if (this.lockRef && this.lockRef > pendingRef) {
          this.el.dispatchEvent(
            new CustomEvent(`phx:undo-lock:${pendingRef}`, opts),
          );
        }
        return pendingRef > ref;
      });
    });

    // clean up if fully resolved
    if (this.isFullyResolvedBy(ref)) {
      this.el.removeAttribute(PHX_REF_SRC);
    }
  }

  // private

  isWithin(ref) {
    return !(
      this.loadingRef !== null &&
      this.loadingRef > ref &&
      this.lockRef !== null &&
      this.lockRef > ref
    );
  }

  // Check for cloned PHX_REF_LOCK element that has been morphed behind
  // the scenes while this element was locked in the DOM.
  // When we apply the cloned tree to the active DOM element, we must
  //
  //   1. execute pending mounted hooks for nodes now in the DOM
  //   2. undo any ref inside the cloned tree that has since been ack'd
  undoLocks(ref, phxEvent, eachCloneCallback) {
    if (!this.isLockUndoneBy(ref)) {
      return;
    }

    const clonedTree = DOM.private(this.el, PHX_REF_LOCK);
    if (clonedTree) {
      eachCloneCallback(clonedTree);
      DOM.deletePrivate(this.el, PHX_REF_LOCK);
    }
    this.el.removeAttribute(PHX_REF_LOCK);

    const opts = {
      detail: { ref: ref, event: phxEvent },
      bubbles: true,
      cancelable: false,
    };
    this.el.dispatchEvent(
      new CustomEvent(`phx:undo-lock:${this.lockRef}`, opts),
    );
  }

  undoLoading(ref, phxEvent) {
    if (!this.isLoadingUndoneBy(ref)) {
      if (
        this.canUndoLoading(ref) &&
        this.el.classList.contains("phx-submit-loading")
      ) {
        this.el.classList.remove("phx-change-loading");
      }
      return;
    }

    if (this.canUndoLoading(ref)) {
      this.el.removeAttribute(PHX_REF_LOADING);
      const disabledVal = this.el.getAttribute(PHX_DISABLED);
      const readOnlyVal = this.el.getAttribute(PHX_READONLY);
      // restore inputs
      if (readOnlyVal !== null) {
        this.el.readOnly = readOnlyVal === "true" ? true : false;
        this.el.removeAttribute(PHX_READONLY);
      }
      if (disabledVal !== null) {
        this.el.disabled = disabledVal === "true" ? true : false;
        this.el.removeAttribute(PHX_DISABLED);
      }
      // restore disables
      const disableRestore = this.el.getAttribute(PHX_DISABLE_WITH_RESTORE);
      if (disableRestore !== null) {
        this.el.textContent = disableRestore;
        this.el.removeAttribute(PHX_DISABLE_WITH_RESTORE);
      }

      const opts = {
        detail: { ref: ref, event: phxEvent },
        bubbles: true,
        cancelable: false,
      };
      this.el.dispatchEvent(
        new CustomEvent(`phx:undo-loading:${this.loadingRef}`, opts),
      );
    }

    // remove classes
    PHX_EVENT_CLASSES.forEach((name) => {
      if (name !== "phx-submit-loading" || this.canUndoLoading(ref)) {
        DOM.removeClass(this.el, name);
      }
    });
  }

  isLoadingUndoneBy(ref) {
    return this.loadingRef === null ? false : this.loadingRef <= ref;
  }
  isLockUndoneBy(ref) {
    return this.lockRef === null ? false : this.lockRef <= ref;
  }

  isFullyResolvedBy(ref) {
    return (
      (this.loadingRef === null || this.loadingRef <= ref) &&
      (this.lockRef === null || this.lockRef <= ref)
    );
  }

  // only remove the phx-submit-loading class if we are not locked
  canUndoLoading(ref) {
    return this.lockRef === null || this.lockRef <= ref;
  }
}


================================================
FILE: assets/js/phoenix_live_view/entry_uploader.js
================================================
import { logError } from "./utils";

export default class EntryUploader {
  constructor(entry, config, liveSocket) {
    const { chunk_size, chunk_timeout } = config;
    this.liveSocket = liveSocket;
    this.entry = entry;
    this.offset = 0;
    this.chunkSize = chunk_size;
    this.chunkTimeout = chunk_timeout;
    this.chunkTimer = null;
    this.errored = false;
    this.uploadChannel = liveSocket.channel(`lvu:${entry.ref}`, {
      token: entry.metadata(),
    });
  }

  error(reason) {
    if (this.errored) {
      return;
    }
    this.uploadChannel.leave();
    this.errored = true;
    clearTimeout(this.chunkTimer);
    this.entry.error(reason);
  }

  upload() {
    this.uploadChannel.onError((reason) => this.error(reason));
    this.uploadChannel
      .join()
      .receive("ok", (_data) => this.readNextChunk())
      .receive("error", (reason) => this.error(reason));
  }

  isDone() {
    return this.offset >= this.entry.file.size;
  }

  readNextChunk() {
    const reader = new window.FileReader();
    const blob = this.entry.file.slice(
      this.offset,
      this.chunkSize + this.offset,
    );
    reader.onload = (e) => {
      if (e.target.error === null) {
        this.offset += /** @type {ArrayBuffer} */ (e.target.result).byteLength;
        this.pushChunk(/** @type {ArrayBuffer} */ (e.target.result));
      } else {
        return logError("Read error: " + e.target.error);
      }
    };
    reader.readAsArrayBuffer(blob);
  }

  pushChunk(chunk) {
    if (!this.uploadChannel.isJoined()) {
      return;
    }
    this.uploadChannel
      .push("chunk", chunk, this.chunkTimeout)
      .receive("ok", () => {
        this.entry.progress((this.offset / this.entry.file.size) * 100);
        if (!this.isDone()) {
          this.chunkTimer = setTimeout(
            () => this.readNextChunk(),
            this.liveSocket.getLatencySim() || 0,
          );
        }
      })
      .receive("error", ({ reason }) => this.error(reason));
  }
}


================================================
FILE: assets/js/phoenix_live_view/global.d.ts
================================================
declare let LV_VSN: string;


================================================
FILE: assets/js/phoenix_live_view/hooks.js
================================================
import {
  PHX_ACTIVE_ENTRY_REFS,
  PHX_LIVE_FILE_UPDATED,
  PHX_PREFLIGHTED_REFS,
  PHX_UPLOAD_REF,
  PHX_VIEWPORT_OVERRUN_TARGET,
} from "./constants";

import LiveUploader from "./live_uploader";
import ARIA from "./aria";

const Hooks = {
  LiveFileUpload: {
    activeRefs() {
      return this.el.getAttribute(PHX_ACTIVE_ENTRY_REFS);
    },

    preflightedRefs() {
      return this.el.getAttribute(PHX_PREFLIGHTED_REFS);
    },

    mounted() {
      this.js().ignoreAttributes(this.el, ["value"]);
      this.preflightedWas = this.preflightedRefs();
    },

    updated() {
      const newPreflights = this.preflightedRefs();
      if (this.preflightedWas !== newPreflights) {
        this.preflightedWas = newPreflights;
        if (newPreflights === "") {
          this.__view().cancelSubmit(this.el.form);
        }
      }

      if (this.activeRefs() === "") {
        this.el.value = null;
      }
      this.el.dispatchEvent(new CustomEvent(PHX_LIVE_FILE_UPDATED));
    },
  },

  LiveImgPreview: {
    mounted() {
      this.ref = this.el.getAttribute("data-phx-entry-ref");
      this.inputEl = document.getElementById(
        this.el.getAttribute(PHX_UPLOAD_REF),
      );
      LiveUploader.getEntryDataURL(this.inputEl, this.ref, (url) => {
        this.url = url;
        this.el.src = url;
      });
    },
    destroyed() {
      URL.revokeObjectURL(this.url);
    },
  },
  FocusWrap: {
    mounted() {
      this.focusStart = this.el.firstElementChild;
      this.focusEnd = this.el.lastElementChild;
      this.focusStart.addEventListener("focus", (e) => {
        if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
          // Handle focus entering from outside (e.g. Tab when body is focused)
          // https://github.com/phoenixframework/phoenix_live_view/issues/3636
          const nextFocus = e.target.nextElementSibling;
          ARIA.attemptFocus(nextFocus) || ARIA.focusFirst(nextFocus);
        } else {
          ARIA.focusLast(this.el);
        }
      });
      this.focusEnd.addEventListener("focus", (e) => {
        if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
          // Handle focus entering from outside (e.g. Shift+Tab when body is focused)
          // https://github.com/phoenixframework/phoenix_live_view/issues/3636
          const nextFocus = e.target.previousElementSibling;
          ARIA.attemptFocus(nextFocus) || ARIA.focusLast(nextFocus);
        } else {
          ARIA.focusFirst(this.el);
        }
      });
      // only try to change the focus if it is not already inside
      if (!this.el.contains(document.activeElement)) {
        this.el.addEventListener("phx:show-end", () => this.el.focus());
        if (window.getComputedStyle(this.el).display !== "none") {
          ARIA.focusFirst(this.el);
        }
      }
    },
  },
};

const findScrollContainer = (el) => {
  // the scroll event won't be fired on the html/body element even if overflow is set
  // therefore we return null to instead listen for scroll events on document
  if (["HTML", "BODY"].indexOf(el.nodeName.toUpperCase()) >= 0) return null;
  if (["scroll", "auto"].indexOf(getComputedStyle(el).overflowY) >= 0)
    return el;
  return findScrollContainer(el.parentElement);
};

const scrollTop = (scrollContainer) => {
  if (scrollContainer) {
    return scrollContainer.scrollTop;
  } else {
    return document.documentElement.scrollTop || document.body.scrollTop;
  }
};

const bottom = (scrollContainer) => {
  if (scrollContainer) {
    return scrollContainer.getBoundingClientRect().bottom;
  } else {
    // when we have no container, the whole page scrolls,
    // therefore the bottom coordinate is the viewport height
    return window.innerHeight || document.documentElement.clientHeight;
  }
};

const top = (scrollContainer) => {
  if (scrollContainer) {
    return scrollContainer.getBoundingClientRect().top;
  } else {
    // when we have no container the whole page scrolls,
    // therefore the top coordinate is 0
    return 0;
  }
};

const isAtViewportTop = (el, scrollContainer) => {
  const rect = el.getBoundingClientRect();
  return (
    Math.ceil(rect.top) >= top(scrollContainer) &&
    Math.ceil(rect.left) >= 0 &&
    Math.floor(rect.top) <= bottom(scrollContainer)
  );
};

const isAtViewportBottom = (el, scrollContainer) => {
  const rect = el.getBoundingClientRect();
  return (
    Math.ceil(rect.bottom) >= top(scrollContainer) &&
    Math.ceil(rect.left) >= 0 &&
    Math.floor(rect.bottom) <= bottom(scrollContainer)
  );
};

const isWithinViewport = (el, scrollContainer) => {
  const rect = el.getBoundingClientRect();
  return (
    Math.ceil(rect.top) >= top(scrollContainer) &&
    Math.ceil(rect.left) >= 0 &&
    Math.floor(rect.top) <= bottom(scrollContainer)
  );
};

Hooks.InfiniteScroll = {
  mounted() {
    this.scrollContainer = findScrollContainer(this.el);
    let scrollBefore = scrollTop(this.scrollContainer);
    let topOverran = false;
    const throttleInterval = 500;
    let pendingOp = null;

    const onTopOverrun = this.throttle(
      throttleInterval,
      (topEvent, firstChild) => {
        pendingOp = () => true;
        this.liveSocket.js().push(this.el, topEvent, {
          value: { id: firstChild.id, _overran: true },
          callback: () => {
            pendingOp = null;
          },
        });
      },
    );

    const onFirstChildAtTop = this.throttle(
      throttleInterval,
      (topEvent, firstChild) => {
        pendingOp = () => firstChild.scrollIntoView({ block: "start" });
        this.liveSocket.js().push(this.el, topEvent, {
          value: { id: firstChild.id },
          callback: () => {
            pendingOp = null;
            // make sure that the DOM is patched by waiting for the next tick
            window.requestAnimationFrame(() => {
              if (!isWithinViewport(firstChild, this.scrollContainer)) {
                firstChild.scrollIntoView({ block: "start" });
              }
            });
          },
        });
      },
    );

    const onLastChildAtBottom = this.throttle(
      throttleInterval,
      (bottomEvent, lastChild) => {
        pendingOp = () => lastChild.scrollIntoView({ block: "end" });
        this.liveSocket.js().push(this.el, bottomEvent, {
          value: { id: lastChild.id },
          callback: () => {
            pendingOp = null;
            // make sure that the DOM is patched by waiting for the next tick
            window.requestAnimationFrame(() => {
              if (!isWithinViewport(lastChild, this.scrollContainer)) {
                lastChild.scrollIntoView({ block: "end" });
              }
            });
          },
        });
      },
    );

    this.onScroll = (_e) => {
      const scrollNow = scrollTop(this.scrollContainer);

      if (pendingOp) {
        scrollBefore = scrollNow;
        return pendingOp();
      }

      const rect = this.findOverrunTarget();
      const topEvent = this.el.getAttribute(
        this.liveSocket.binding("viewport-top"),
      );
      const bottomEvent = this.el.getAttribute(
        this.liveSocket.binding("viewport-bottom"),
      );
      const lastChild = this.el.lastElementChild;
      const firstChild = this.el.firstElementChild;
      const isScrollingUp = scrollNow < scrollBefore;
      const isScrollingDown = scrollNow > scrollBefore;

      // el overran while scrolling up
      if (isScrollingUp && topEvent && !topOverran && rect.top >= 0) {
        topOverran = true;
        onTopOverrun(topEvent, firstChild);
      } else if (isScrollingDown && topOverran && rect.top <= 0) {
        topOverran = false;
      }

      if (
        topEvent &&
        isScrollingUp &&
        isAtViewportTop(firstChild, this.scrollContainer)
      ) {
        onFirstChildAtTop(topEvent, firstChild);
      } else if (
        bottomEvent &&
        isScrollingDown &&
        isAtViewportBottom(lastChild, this.scrollContainer)
      ) {
        onLastChildAtBottom(bottomEvent, lastChild);
      }
      scrollBefore = scrollNow;
    };

    if (this.scrollContainer) {
      this.scrollContainer.addEventListener("scroll", this.onScroll);
    } else {
      window.addEventListener("scroll", this.onScroll);
    }
  },

  destroyed() {
    if (this.scrollContainer) {
      this.scrollContainer.removeEventListener("scroll", this.onScroll);
    } else {
      window.removeEventListener("scroll", this.onScroll);
    }
  },

  throttle(interval, callback) {
    let lastCallAt = 0;
    let timer;

    return (...args) => {
      const now = Date.now();
      const remainingTime = interval - (now - lastCallAt);

      if (remainingTime <= 0 || remainingTime > interval) {
        if (timer) {
          clearTimeout(timer);
          timer = null;
        }
        lastCallAt = now;
        callback(...args);
      } else if (!timer) {
        timer = setTimeout(() => {
          lastCallAt = Date.now();
          timer = null;
          callback(...args);
        }, remainingTime);
      }
    };
  },

  findOverrunTarget() {
    let rect;
    const overrunTarget = this.el.getAttribute(
      this.liveSocket.binding(PHX_VIEWPORT_OVERRUN_TARGET),
    );
    if (overrunTarget) {
      const overrunEl = document.getElementById(overrunTarget);
      if (overrunEl) {
        rect = overrunEl.getBoundingClientRect();
      } else {
        throw new Error("did not find element with id " + overrunTarget);
      }
    } else {
      rect = this.el.getBoundingClientRect();
    }
    return rect;
  },
};
export default Hooks;


================================================
FILE: assets/js/phoenix_live_view/index.ts
================================================
/*
================================================================================
Phoenix LiveView JavaScript Client
================================================================================

See the hexdocs at `https://hexdocs.pm/phoenix_live_view` for documentation.
*/

import OriginalLiveSocket, { isUsedInput } from "./live_socket";
import DOM from "./dom";
import { ViewHook } from "./view_hook";
import View from "./view";
import { logError } from "./utils";

import type { EncodedJS, LiveSocketJSCommands } from "./js_commands";
import type { Hook, HooksOptions } from "./view_hook";
import type { Socket as PhoenixSocket } from "phoenix";

/**
 * Options for configuring the LiveSocket instance.
 */
export interface LiveSocketOptions {
  /**
   * Defaults for phx-debounce and phx-throttle.
   */
  defaults?: {
    /** The millisecond phx-debounce time. Defaults 300 */
    debounce?: number;
    /** The millisecond phx-throttle time. Defaults 300 */
    throttle?: number;
  };
  /**
   * An object or function for passing connect params.
   * The function receives the element associated with a given LiveView. For example:
   *
   *     (el) => {view: el.getAttribute("data-my-view-name", token: window.myToken}
   *
   */
  params?:
    | ((el: HTMLElement) => { [key: string]: any })
    | { [key: string]: any };
  /**
   * The optional prefix to use for all phx DOM annotations.
   *
   * Defaults to "phx-".
   */
  bindingPrefix?: string;
  /**
   * Callbacks for LiveView hooks.
   *
   * See [Client hooks via `phx-hook`](https://hexdocs.pm/phoenix_live_view/js-interop.html#client-hooks-via-phx-hook) for more information.
   */
  hooks?: HooksOptions;
  /** Callbacks for LiveView uploaders. */
  uploaders?: { [key: string]: any }; // TODO: define more specifically
  /** Delay in milliseconds before applying loading states. */
  loaderTimeout?: number;
  /** Delay in milliseconds before executing phx-disconnected commands. */
  disconnectedTimeout?: number;
  /** Maximum reloads before entering failsafe mode. */
  maxReloads?: number;
  /** Minimum time between normal reload attempts. */
  reloadJitterMin?: number;
  /** Maximum time between normal reload attempts. */
  reloadJitterMax?: number;
  /** Time between reload attempts in failsafe mode. */
  failsafeJitter?: number;
  /**
   * Function to log debug information. For example:
   *
   *     (view, kind, msg, obj) => console.log(`${view.id} ${kind}: ${msg} - `, obj)
   */
  viewLogger?: (view: View, kind: string, msg: string, obj: any) => void;
  /**
   * Object mapping event names to functions for populating event metadata.
   *
   *     metadata: {
   *       click: (e, el) => {
   *         return {
   *           ctrlKey: e.ctrlKey,
   *           metaKey: e.metaKey,
   *           detail: e.detail || 1,
   *         }
   *       },
   *       keydown: (e, el) => {
   *         return {
   *           key: e.key,
   *           ctrlKey: e.ctrlKey,
   *           metaKey: e.metaKey,
   *           shiftKey: e.shiftKey
   *         }
   *       }
   *     }
   *
   */
  metadata?: { [eventName: string]: (e: Event, el: HTMLElement) => object };
  /**
   * An optional Storage compatible object
   * Useful when LiveView won't have access to `sessionStorage`. For example, This could
   * happen if a site loads a cross-domain LiveView in an iframe.
   *
   * Example usage:
   *
   *     class InMemoryStorage {
   *       constructor() { this.storage = {} }
   *       getItem(keyName) { return this.storage[keyName] || null }
   *       removeItem(keyName) { delete this.storage[keyName] }
   *       setItem(keyName, keyValue) { this.storage[keyName] = keyValue }
   *     }
   */
  sessionStorage?: Storage;
  /**
   * An optional Storage compatible object
   * Useful when LiveView won't have access to `localStorage`.
   *
   * See `sessionStorage` for an example.
   */
  localStorage?: Storage;
  /**
   * If set to `true`, `phx-change` events will be blocked (will not fire)
   * while the user is composing input using an IME (Input Method Editor).
   * This is determined by the `e.isComposing` property on keyboard events,
   * which is `true` when the user is in the process of entering composed characters (for example,
   * when typing Japanese or Chinese using romaji or pinyin input methods).
   * By default, `phx-change` will not be blocked during a composition session,
   * but note that there were issues reported in older versions of Safari,
   * where a LiveView patch to the input caused unexpected behavior.
   *
   * For more information, see
   * - https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/isComposing
   * - https://github.com/phoenixframework/phoenix_live_view/issues/3322
   *
   * Defaults to `false`.
   */
  blockPhxChangeWhileComposing?: boolean;
  /** DOM callbacks. */
  dom?: {
    /**
     * An optional function to modify the behavior of querying elements in JS commands.
     * @param sourceEl - The source element, e.g. the button that was clicked.
     * @param query - The query value.
     * @param defaultQuery - A default query function that can be used if no custom query should be applied.
     * @returns A list of DOM elements.
     */
    jsQuerySelectorAll?: (
      sourceEl: HTMLElement,
      query: string,
      defaultQuery: () => Element[],
    ) => Element[];
    /**
     * When defined, called with a start callback that needs to be called
     * to perform the actual patch. Failing to call the start callback causes
     * the page to become stuck.
     *
     * This can be used to delay patches in order to perform view transitions,
     * for example:
     *
     * ```javascript
     * let liveSocket = new LiveSocket("/live", Socket, {
     *   dom: {
     *     onDocumentPatch(start) {
     *       document.startViewTransition(start);
     *     }
     *   }
     * })
     * ```
     *
     * It is strongly advised to call start as quickly as possible.
     */
    onDocumentPatch?: (start: () => void) => void;
    /**
     * Called immediately before a DOM patch is applied.
     */
    onPatchStart?: (container: HTMLElement) => void;
    /**
     * Called immediately after a DOM patch is applied.
     */
    onPatchEnd?: (container: HTMLElement) => void;
    /**
     * Called when a new DOM node is added.
     */
    onNodeAdded?: (node: Node) => void;
    /**
     * Called before an element is updated.
     */
    onBeforeElUpdated?: (fromEl: Element, toEl: Element) => void;
  };
  /** Allow passthrough of other options to the Phoenix Socket constructor. */
  [key: string]: any;
}

/**
 * Interface describing the public API of a LiveSocket instance.
 */
export interface LiveSocketInstanceInterface {
  /**
   * Returns the version of the LiveView client.
   */
  version(): string;
  /**
   * Returns true if profiling is enabled. See `enableProfiling` and `disableProfiling`.
   */
  isProfileEnabled(): boolean;
  /**
   * Returns true if debugging is enabled. See `enableDebug` and `disableDebug`.
   */
  isDebugEnabled(): boolean;
  /**
   * Returns true if debugging is disabled. See `enableDebug` and `disableDebug`.
   */
  isDebugDisabled(): boolean;
  /**
   * Enables debugging.
   *
   * When debugging is enabled, the LiveView client will log debug information to the console.
   * See [Debugging client events](https://hexdocs.pm/phoenix_live_view/js-interop.html#debugging-client-events) for more information.
   */
  enableDebug(): void;
  /**
   * Enables profiling.
   *
   * When profiling is enabled, the LiveView client will log profiling information to the console.
   */
  enableProfiling(): void;
  /**
   * Disables debugging.
   */
  disableDebug(): void;
  /**
   * Disables profiling.
   */
  disableProfiling(): void;
  /**
   * Enables latency simulation.
   *
   * When latency simulation is enabled, the LiveView client will add a delay to requests and responses from the server.
   * See [Simulating Latency](https://hexdocs.pm/phoenix_live_view/js-interop.html#simulating-latency) for more information.
   */
  enableLatencySim(upperBoundMs: number): void;
  /**
   * Disables latency simulation.
   */
  disableLatencySim(): void;
  /**
   * Returns the current latency simulation upper bound.
   */
  getLatencySim(): number | null;
  /**
   * Returns the Phoenix Socket instance.
   */
  getSocket(): PhoenixSocket;
  /**
   * Connects to the LiveView server.
   */
  connect(): void;
  /**
   * Disconnects from the LiveView server.
   */
  disconnect(callback?: () => void): void;
  /**
   * Can be used to replace the transport used by the underlying Phoenix Socket.
   */
  replaceTransport(transport: any): void;
  /**
   * Executes an encoded JS command, targeting the given element.
   *
   * See [`Phoenix.LiveView.JS`](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html) for more information.
   */
  execJS(
    el: HTMLElement,
    encodedJS: EncodedJS,
    eventType?: string | null,
  ): void;
  /**
   * Returns an object with methods to manipulate the DOM and execute JavaScript.
   * The applied changes integrate with server DOM patching.
   *
   * See [JavaScript interoperability](https://hexdocs.pm/phoenix_live_view/js-interop.html) for more information.
   */
  js(): LiveSocketJSCommands;
}

/**
 * Interface describing the LiveSocket constructor.
 */
export interface LiveSocketConstructor {
  /**
   * Creates a new LiveSocket instance.
   *
   * @param endpoint - The string WebSocket endpoint, ie, `"wss://example.com/live"`,
   *                                               `"/live"` (inherited host & protocol)
   * @param socket - the required Phoenix Socket class imported from "phoenix". For example:
   *
   *     import {Socket} from "phoenix"
   *     import {LiveSocket} from "phoenix_live_view"
   *     let liveSocket = new LiveSocket("/live", Socket, {...})
   *
   * @param opts - Optional configuration.
   */
  new (
    endpoint: string,
    socket: typeof PhoenixSocket,
    opts?: LiveSocketOptions,
  ): LiveSocketInstanceInterface;
}

// because LiveSocket is in JS (for now), we cast it to our defined TypeScript constructor.
const LiveSocket = OriginalLiveSocket as unknown as LiveSocketConstructor;

/** Creates a hook instance for the given element and callbacks.
 *
 * @param el - The element to associate with the hook.
 * @param callbacks - The list of hook callbacks, such as mounted,
 *   updated, destroyed, etc.
 *
 * *Note*: `createHook` must be called from the `connectedCallback` lifecycle
 * which is triggered after the element has been added to the DOM. If you try
 * to call `createHook` from the constructor, an error will be logged.
 *
 * Furthermore, you can only start using the hook's APIs after the `mounted`
 * callback of the hook has been called. If you try to call them earlier,
 * an error will be logged.
 *
 * @example
 *
 * class MyComponent extends HTMLElement {
 *   connectedCallback(){
 *     let onLiveViewMounted = () => this.hook.pushEvent(...))
 *     this.hook = createHook(this, {mounted: onLiveViewMounted})
 *   }
 * }
 *
 * @returns Returns the Hook instance for the custom element.
 */
function createHook(el: HTMLElement, callbacks: Hook): ViewHook {
  let existingHook = DOM.getCustomElHook(el);
  if (existingHook) {
    return existingHook;
  }

  if (!el.hasAttribute("id")) {
    logError(
      "Elements passed to createHook need to have a unique id attribute",
      el,
    );
  }

  let hook = new ViewHook(View.closestView(el), el, callbacks);
  DOM.putCustomElHook(el, hook);
  return hook;
}

export { LiveSocket, isUsedInput, createHook, ViewHook, Hook, HooksOptions };


================================================
FILE: assets/js/phoenix_live_view/js.js
================================================
import DOM from "./dom";
import ARIA from "./aria";

const focusStack = [];
const default_transition_time = 200;

const JS = {
  // private
  exec(e, eventType, phxEvent, view, sourceEl, defaults) {
    const [defaultKind, defaultArgs] = defaults || [
      null,
      { callback: defaults && defaults.callback },
    ];
    const commands = Array.isArray(phxEvent)
      ? phxEvent
      : typeof phxEvent === "string" && phxEvent.startsWith("[")
        ? JSON.parse(phxEvent)
        : [[defaultKind, defaultArgs]];

    commands.forEach(([kind, args]) => {
      if (kind === defaultKind) {
        // always prefer the args, but keep existing keys from the defaultArgs
        args = { ...defaultArgs, ...args };
        args.callback = args.callback || defaultArgs.callback;
      }
      this.filterToEls(view.liveSocket, sourceEl, args).forEach((el) => {
        this[`exec_${kind}`](e, eventType, phxEvent, view, sourceEl, el, args);
      });
    });
  },

  isVisible(el) {
    return !!(
      el.offsetWidth ||
      el.offsetHeight ||
      el.getClientRects().length > 0
    );
  },

  // returns true if any part of the element is inside the viewport
  isInViewport(el) {
    const rect = el.getBoundingClientRect();
    const windowHeight =
      window.innerHeight || document.documentElement.clientHeight;
    const windowWidth =
      window.innerWidth || document.documentElement.clientWidth;

    return (
      rect.right > 0 &&
      rect.bottom > 0 &&
      rect.left < windowWidth &&
      rect.top < windowHeight
    );
  },

  // private

  // commands

  exec_exec(e, eventType, phxEvent, view, sourceEl, el, { attr, to }) {
    const encodedJS = el.getAttribute(attr);
    if (!encodedJS) {
      throw new Error(`expected ${attr} to contain JS command on "${to}"`);
    }
    view.liveSocket.execJS(el, encodedJS, eventType);
  },

  exec_dispatch(
    e,
    eventType,
    phxEvent,
    view,
    sourceEl,
    el,
    { event, detail, bubbles, blocking },
  ) {
    detail = detail || {};
    detail.dispatcher = sourceEl;
    if (blocking) {
      const promise = new Promise((resolve, _reject) => {
        detail.done = resolve;
      });
      view.liveSocket.asyncTransition(promise);
    }
    DOM.dispatchEvent(el, event, { detail, bubbles });
  },

  exec_push(e, eventType, phxEvent, view, sourceEl, el, args) {
    const {
      event,
      data,
      target,
      page_loading,
      loading,
      value,
      dispatcher,
      callback,
    } = args;
    const pushOpts = {
      loading,
      value,
      target,
      page_loading: !!page_loading,
      originalEvent: e,
    };
    const targetSrc =
      eventType === "change" && dispatcher ? dispatcher : sourceEl;
    const phxTarget =
      target || targetSrc.getAttribute(view.binding("target")) || targetSrc;
    const handler = (targetView, targetCtx) => {
      if (!targetView.isConnected()) {
        return;
      }
      if (eventType === "change") {
        let { newCid, _target } = args;
        _target =
          _target || (DOM.isFormInput(sourceEl) ? sourceEl.name : undefined);
        if (_target) {
          pushOpts._target = _target;
        }
        targetView.pushInput(
          sourceEl,
          targetCtx,
          newCid,
          event || phxEvent,
          pushOpts,
          callback,
        );
      } else if (eventType === "submit") {
        const { submitter } = args;
        targetView.submitForm(
          sourceEl,
          targetCtx,
          event || phxEvent,
          submitter,
          pushOpts,
          callback,
        );
      } else {
        targetView.pushEvent(
          eventType,
          sourceEl,
          targetCtx,
          event || phxEvent,
          data,
          pushOpts,
          callback,
        );
      }
    };
    // in case of formRecovery, targetView and targetCtx are passed as argument
    // as they are looked up in a template element, not the real DOM
    if (args.targetView && args.targetCtx) {
      handler(args.targetView, args.targetCtx);
    } else {
      view.withinTargets(phxTarget, handler);
    }
  },

  exec_navigate(e, eventType, phxEvent, view, sourceEl, el, { href, replace }) {
    view.liveSocket.historyRedirect(
      e,
      href,
      replace ? "replace" : "push",
      null,
      sourceEl,
    );
  },

  exec_patch(e, eventType, phxEvent, view, sourceEl, el, { href, replace }) {
    view.liveSocket.pushHistoryPatch(
      e,
      href,
      replace ? "replace" : "push",
      sourceEl,
    );
  },

  exec_focus(e, eventType, phxEvent, view, sourceEl, el) {
    ARIA.attemptFocus(el);
    // in case the JS.focus command is in a JS.show/hide/toggle chain, for show we need
    // to wait for JS.show to have updated the element's display property (see exec_toggle)
    // but that run in nested animation frames, therefore we need to use them here as well
    window.requestAnimationFrame(() => {
      window.requestAnimationFrame(() => ARIA.attemptFocus(el));
    });
  },

  exec_focus_first(e, eventType, phxEvent, view, sourceEl, el) {
    ARIA.focusFirstInteractive(el) || ARIA.focusFirst(el);
    // if you wonder about the nested animation frames, see exec_focus
    window.requestAnimationFrame(() => {
      window.requestAnimationFrame(
        () => ARIA.focusFirstInteractive(el) || ARIA.focusFirst(el),
      );
    });
  },

  exec_push_focus(e, eventType, phxEvent, view, sourceEl, el) {
    focusStack.push(el || sourceEl);
  },

  exec_pop_focus(_e, _eventType, _phxEvent, _view, _sourceEl, _el) {
    const el = focusStack.pop();
    if (el) {
      el.focus();
      // if you wonder about the nested animation frames, see exec_focus
      window.requestAnimationFrame(() => {
        window.requestAnimationFrame(() => el.focus());
      });
    }
  },

  exec_add_class(
    e,
    eventType,
    phxEvent,
    view,
    sourceEl,
    el,
    { names, transition, time, blocking },
  ) {
    this.addOrRemoveClasses(el, names, [], transition, time, view, blocking);
  },

  exec_remove_class(
    e,
    eventType,
    phxEvent,
    view,
    sourceEl,
    el,
    { names, transition, time, blocking },
  ) {
    this.addOrRemoveClasses(el, [], names, transition, time, view, blocking);
  },

  exec_toggle_class(
    e,
    eventType,
    phxEvent,
    view,
    sourceEl,
    el,
    { names, transition, time, blocking },
  ) {
    this.toggleClasses(el, names, transition, time, view, blocking);
  },

  exec_toggle_attr(
    e,
    eventType,
    phxEvent,
    view,
    sourceEl,
    el,
    { attr: [attr, val1, val2] },
  ) {
    this.toggleAttr(el, attr, val1, val2);
  },

  exec_ignore_attrs(e, eventType, phxEvent, view, sourceEl, el, { attrs }) {
    this.ignoreAttrs(el, attrs);
  },

  exec_transition(
    e,
    eventType,
    phxEvent,
    view,
    sourceEl,
    el,
    { time, transition, blocking },
  ) {
    this.addOrRemoveClasses(el, [], [], transition, time, view, blocking);
  },

  exec_toggle(
    e,
    eventType,
    phxEvent,
    view,
    sourceEl,
    el,
    { display, ins, outs, time, blocking },
  ) {
    this.toggle(eventType, view, el, display, ins, outs, time, blocking);
  },

  exec_show(
    e,
    eventType,
    phxEvent,
    view,
    sourceEl,
    el,
    { display, transition, time, blocking },
  ) {
    this.show(eventType, view, el, display, transition, time, blocking);
  },

  exec_hide(
    e,
    eventType,
    phxEvent,
    view,
    sourceEl,
    el,
    { display, transition, time, blocking },
  ) {
    this.hide(eventType, view, el, display, transition, time, blocking);
  },

  exec_set_attr(
    e,
    eventType,
    phxEvent,
    view,
    sourceEl,
    el,
    { attr: [attr, val] },
  ) {
    this.setOrRemoveAttrs(el, [[attr, val]], []);
  },

  exec_remove_attr(e, eventType, phxEvent, view, sourceEl, el, { attr }) {
    this.setOrRemoveAttrs(el, [], [attr]);
  },

  ignoreAttrs(el, attrs) {
    DOM.putPrivate(el, "JS:ignore_attrs", {
      apply: (fromEl, toEl) => {
        let fromAttributes = Array.from(fromEl.attributes);
        let fromAttributeNames = fromAttributes.map((attr) => attr.name);
        Array.from(toEl.attributes)
          .filter((attr) => {
            return !fromAttributeNames.includes(attr.name);
          })
          .forEach((attr) => {
            if (DOM.attributeIgnored(attr, attrs)) {
              toEl.removeAttribute(attr.name);
            }
          });
        fromAttributes.forEach((attr) => {
          if (DOM.attributeIgnored(attr, attrs)) {
            toEl.setAttribute(attr.name, attr.value);
          }
        });
      },
    });
  },

  onBeforeElUpdated(fromEl, toEl) {
    const ignoreAttrs = DOM.private(fromEl, "JS:ignore_attrs");
    if (ignoreAttrs) {
      ignoreAttrs.apply(fromEl, toEl);
    }
  },

  // utils for commands

  show(eventType, view, el, display, transition, time, blocking) {
    if (!this.isVisible(el)) {
      this.toggle(
        eventType,
        view,
        el,
        display,
        transition,
        null,
        time,
        blocking,
      );
    }
  },

  hide(eventType, view, el, display, transition, time, blocking) {
    if (this.isVisible(el)) {
      this.toggle(
        eventType,
        view,
        el,
        display,
        null,
        transition,
        time,
        blocking,
      );
    }
  },

  toggle(eventType, view, el, display, ins, outs, time, blocking) {
    time = time || default_transition_time;
    const [inClasses, inStartClasses, inEndClasses] = ins || [[], [], []];
    const [outClasses, outStartClasses, outEndClasses] = outs || [[], [], []];
    if (inClasses.length > 0 || outClasses.length > 0) {
      if (this.isVisible(el)) {
        const onStart = () => {
          this.addOrRemoveClasses(
            el,
            outStartClasses,
            inClasses.concat(inStartClasses).concat(inEndClasses),
          );
          window.requestAnimationFrame(() => {
            this.addOrRemoveClasses(el, outClasses, []);
            window.requestAnimationFrame(() =>
              this.addOrRemoveClasses(el, outEndClasses, outStartClasses),
            );
          });
        };
        const onEnd = () => {
          this.addOrRemoveClasses(el, [], outClasses.concat(outEndClasses));
          DOM.putSticky(
            el,
            "toggle",
            (currentEl) => (currentEl.style.display = "none"),
          );
          el.dispatchEvent(new Event("phx:hide-end"));
        };
        el.dispatchEvent(new Event("phx:hide-start"));
        if (blocking === false) {
          onStart();
          setTimeout(onEnd, time);
        } else {
          view.transition(time, onStart, onEnd);
        }
      } else {
        if (eventType === "remove") {
          return;
        }
        const onStart = () => {
          this.addOrRemoveClasses(
            el,
            inStartClasses,
            outClasses.concat(outStartClasses).concat(outEndClasses),
          );
          const stickyDisplay = display || this.defaultDisplay(el);
          window.requestAnimationFrame(() => {
            // first add the starting + active class, THEN make the element visible
            // otherwise if we toggled the visibility earlier css animations
            // would flicker, as the element becomes visible before the active animation
            // class is set (see https://github.com/phoenixframework/phoenix_live_view/issues/3456)
            this.addOrRemoveClasses(el, inClasses, []);
            // addOrRemoveClasses uses a requestAnimationFrame itself, therefore we need to move the putSticky
            // into the next requestAnimationFrame...
            window.requestAnimationFrame(() => {
              DOM.putSticky(
                el,
                "toggle",
                (currentEl) => (currentEl.style.display = stickyDisplay),
              );
              this.addOrRemoveClasses(el, inEndClasses, inStartClasses);
            });
          });
        };
        const onEnd = () => {
          this.addOrRemoveClasses(el, [], inClasses.concat(inEndClasses));
          el.dispatchEvent(new Event("phx:show-end"));
        };
        el.dispatchEvent(new Event("phx:show-start"));
        if (blocking === false) {
          onStart();
          setTimeout(onEnd, time);
        } else {
          view.transition(time, onStart, onEnd);
        }
      }
    } else {
      if (this.isVisible(el)) {
        window.requestAnimationFrame(() => {
          el.dispatchEvent(new Event("phx:hide-start"));
          DOM.putSticky(
            el,
            "toggle",
            (currentEl) => (currentEl.style.display = "none"),
          );
          el.dispatchEvent(new Event("phx:hide-end"));
        });
      } else {
        window.requestAnimationFrame(() => {
          el.dispatchEvent(new Event("phx:show-start"));
          const stickyDisplay = display || this.defaultDisplay(el);
          DOM.putSticky(
            el,
            "toggle",
            (currentEl) => (currentEl.style.display = stickyDisplay),
          );
          el.dispatchEvent(new Event("phx:show-end"));
        });
      }
    }
  },

  toggleClasses(el, classes, transition, time, view, blocking) {
    window.requestAnimationFrame(() => {
      const [prevAdds, prevRemoves] = DOM.getSticky(el, "classes", [[], []]);
      const newAdds = classes.filter(
        (name) => prevAdds.indexOf(name) < 0 && !el.classList.contains(name),
      );
      const newRemoves = classes.filter(
        (name) => prevRemoves.indexOf(name) < 0 && el.classList.contains(name),
      );
      this.addOrRemoveClasses(
        el,
        newAdds,
        newRemoves,
        transition,
        time,
        view,
        blocking,
      );
    });
  },

  toggleAttr(el, attr, val1, val2) {
    if (el.hasAttribute(attr)) {
      if (val2 !== undefined) {
        // toggle between val1 and val2
        if (el.getAttribute(attr) === val1) {
          this.setOrRemoveAttrs(el, [[attr, val2]], []);
        } else {
          this.setOrRemoveAttrs(el, [[attr, val1]], []);
        }
      } else {
        // remove attr
        this.setOrRemoveAttrs(el, [], [attr]);
      }
    } else {
      this.setOrRemoveAttrs(el, [[attr, val1]], []);
    }
  },

  addOrRemoveClasses(el, adds, removes, transition, time, view, blocking) {
    time = time || default_transition_time;
    const [transitionRun, transitionStart, transitionEnd] = transition || [
      [],
      [],
      [],
    ];
    if (transitionRun.length > 0) {
      const onStart = () => {
        this.addOrRemoveClasses(
          el,
          transitionStart,
          [].concat(transitionRun).concat(transitionEnd),
        );
        window.requestAnimationFrame(() => {
          this.addOrRemoveClasses(el, transitionRun, []);
          window.requestAnimationFrame(() =>
            this.addOrRemoveClasses(el, transitionEnd, transitionStart),
          );
        });
      };
      const onDone = () =>
        this.addOrRemoveClasses(
          el,
          adds.concat(transitionEnd),
          removes.concat(transitionRun).concat(transitionStart),
        );
      if (blocking === false) {
        onStart();
        setTimeout(onDone, time);
      } else {
        view.transition(time, onStart, onDone);
      }
      return;
    }

    window.requestAnimationFrame(() => {
      const [prevAdds, prevRemoves] = DOM.getSticky(el, "classes", [[], []]);
      const keepAdds = adds.filter(
        (name) => prevAdds.indexOf(name) < 0 && !el.classList.contains(name),
      );
      const keepRemoves = removes.filter(
        (name) => prevRemoves.indexOf(name) < 0 && el.classList.contains(name),
      );
      const newAdds = prevAdds
        .filter((name) => removes.indexOf(name) < 0)
        .concat(keepAdds);
      const newRemoves = prevRemoves
        .filter((name) => adds.indexOf(name) < 0)
        .concat(keepRemoves);

      DOM.putSticky(el, "classes", (currentEl) => {
        currentEl.classList.remove(...newRemoves);
        currentEl.classList.add(...newAdds);
        return [newAdds, newRemoves];
      });
    });
  },

  setOrRemoveAttrs(el, sets, removes) {
    const [prevSets, prevRemoves] = DOM.getSticky(el, "attrs", [[], []]);

    const alteredAttrs = sets.map(([attr, _val]) => attr).concat(removes);
    const newSets = prevSets
      .filter(([attr, _val]) => !alteredAttrs.includes(attr))
      .concat(sets);
    const newRemoves = prevRemoves
      .filter((attr) => !alteredAttrs.includes(attr))
      .concat(removes);

    DOM.putSticky(el, "attrs", (currentEl) => {
      newRemoves.forEach((attr) => currentEl.removeAttribute(attr));
      newSets.forEach(([attr, val]) => currentEl.setAttribute(attr, val));
      return [newSets, newRemoves];
    });
  },

  hasAllClasses(el, classes) {
    return classes.every((name) => el.classList.contains(name));
  },

  isToggledOut(el, outClasses) {
    return !this.isVisible(el) || this.hasAllClasses(el, outClasses);
  },

  filterToEls(liveSocket, sourceEl, { to }) {
    const defaultQuery = () => {
      if (typeof to === "string") {
        return document.querySelectorAll(to);
      } else if (to.closest) {
        const toEl = sourceEl.closest(to.closest);
        return toEl ? [toEl] : [];
      } else if (to.inner) {
        return sourceEl.querySelectorAll(to.inner);
      }
    };
    return to
      ? liveSocket.jsQuerySelectorAll(sourceEl, to, defaultQuery)
      : [sourceEl];
  },

  defaultDisplay(el) {
    return (
      { tr: "table-row", td: "table-cell" }[el.tagName.toLowerCase()] || "block"
    );
  },

  transitionClasses(val) {
    if (!val) {
      return null;
    }

    let [trans, tStart, tEnd] = Array.isArray(val)
      ? val
      : [val.split(" "), [], []];
    trans = Array.isArray(trans) ? trans : trans.split(" ");
    tStart = Array.isArray(tStart) ? tStart : tStart.split(" ");
    tEnd = Array.isArray(tEnd) ? tEnd : tEnd.split(" ");
    return [trans, tStart, tEnd];
  },
};

export default JS;


================================================
FILE: assets/js/phoenix_live_view/js_commands.ts
================================================
import JS from "./js";
import LiveSocket from "./live_socket";

/**
 * An encoded JS command. Use functions in the `Phoenix.LiveView.JS` module on
 * the server to create and compose JS commands.
 *
 * The underlying primitive type is considered opaque, and may change in future
 * versions.
 */
export type EncodedJS = string | Array<any>;

type Transition = string | string[];

// Base options for commands involving transitions and timing
type BaseOpts = {
  /**
   * The CSS transition classes to set.
   * Accepts a string of classes or a 3-tuple like:
   * `["ease-out duration-300", "opacity-0", "opacity-100"]`
   */
  transition?: Transition;
  /** The transition duration in milliseconds. Defaults 200. */
  time?: number;
  /** Whether to block UI during transition. Defaults `true`. */
  blocking?: boolean;
};

type ShowOpts = BaseOpts & {
  /** The CSS display value to set. Defaults "block". */
  display?: string;
};

type ToggleOpts = {
  /** The CSS display value to set. Defaults "block". */
  display?: string;
  /**
   * The CSS transition classes for showing.
   * Accepts either the string of classes to apply when toggling in, or
   * a 3-tuple containing the transition class, the class to apply
   * to start the transition, and the ending transition class, such as:
   * `["ease-out duration-300", "opacity-0", "opacity-100"]`
   */
  in?: Transition;
  /**
   * The CSS transition classes for hiding.
   * Accepts either string of classes to apply when toggling out, or
   * a 3-tuple containing the transition class, the class to apply
   * to start the transition, and the ending transition class, such as:
   * `["ease-out duration-300", "opacity-100", "opacity-0"]`
   */
  out?: Transition;
  /** The transition duration in milliseconds. */
  time?: number;
  /** Whether to block UI during transition. Defaults `true`. */
  blocking?: boolean;
};

// Options specific to the 'transition' command
type TransitionCommandOpts = {
  /** The transition duration in milliseconds. */
  time?: number;
  /** Whether to block UI during transition. Defaults `true`. */
  blocking?: boolean;
};

type PushOpts = {
  /** Data to be merged into the event payload. */
  value?: any;
  /** For targeting a LiveComponent by its ID, a component ID (number), or a CSS selector string. */
  target?: HTMLElement | number | string;
  /** Indicates if a page loading state should be shown. */
  page_loading?: boolean;
  [key: string]: any; // Allow other properties like 'cid', 'redirect', etc.
};

type NavigationOpts = {
  /** Whether to replace the current history entry instead of pushing a new one. */
  replace?: boolean;
};

/**
 * Represents all possible JS commands that can be generated by the factory.
 * This is used as a base for LiveSocketJSCommands and HookJSCommands.
 */
interface AllJSCommands {
  /**
   * Executes an encoded JS command in the context of an element.
   * This version is for general use via liveSocket.js().
   *
   * @param el - The element in whose context to execute the JS command.
   * @param encodedJS - The encoded JS command with operations to execute.
   */
  exec(el: HTMLElement, encodedJS: EncodedJS): void;

  /**
   * Shows an element.
   *
   * @param el - The element to show.
   * @param {ShowOpts} [opts={}] - Optional settings.
   *   Accepts: `display`, `transition`, `time`, and `blocking`.
   */
  show(el: HTMLElement, opts?: ShowOpts): void;

  /**
   * Hides an element.
   *
   * @param el - The element to hide.
   * @param [opts={}] - Optional settings.
   *   Accepts: `transition`, `time`, and `blocking`.
   */
  hide(el: HTMLElement, opts?: BaseOpts): void;

  /**
   * Toggles the visibility of an element.
   *
   * @param el - The element to toggle.
   * @param [opts={}] - Optional settings.
   *   Accepts: `display`, `in`, `out`, `time`, and `blocking`.
   */
  toggle(el: HTMLElement, opts?: ToggleOpts): void;

  /**
   * Adds CSS classes to an element.
   *
   * @param el - The element to add classes to.
   * @param names - The class name(s) to add.
   * @param [opts={}] - Optional settings.
   *   Accepts: `transition`, `time`, and `blocking`.
   */
  addClass(el: HTMLElement, names: string | string[], opts?: BaseOpts): void;

  /**
   * Removes CSS classes from an element.
   *
   * @param el - The element to remove classes from.
   * @param names - The class name(s) to remove.
   * @param [opts={}] - Optional settings.
   *   Accepts: `transition`, `time`, and `blocking`.
   */
  removeClass(el: HTMLElement, names: string | string[], opts?: BaseOpts): void;

  /**
   * Toggles CSS classes on an element.
   *
   * @param el - The element to toggle classes on.
   * @param names - The class name(s) to toggle.
   * @param [opts={}] - Optional settings.
   *   Accepts: `transition`, `time`, and `blocking`.
   */
  toggleClass(el: HTMLElement, names: string | string[], opts?: BaseOpts): void;

  /**
   * Applies a CSS transition to an element.
   *
   * @param el - The element to apply the transition to.
   * @param transition - The transition class(es) to apply.
   *   Accepts a string of classes to apply when transitioning or
   *   a 3-tuple containing the transition class, the class to apply
   *   to start the transition, and the ending transition class, such as:
   *
   *       ["ease-out duration-300", "opacity-100", "opacity-0"]
   *
   * @param [opts={}] - Optional settings for timing and blocking behavior.
   *   Accepts: `time` and `blocking`.
   */
  transition(
    el: HTMLElement,
    transition: string | string[],
    opts?: TransitionCommandOpts,
  ): void;

  /**
   * Sets an attribute on an element.
   *
   * @param el - The element to set the attribute on.
   * @param attr - The attribute name to set.
   * @param val - The value to set for the attribute.
   */
  setAttribute(el: HTMLElement, attr: string, val: string): void;

  /**
   * Removes an attribute from an element.
   *
   * @param el - The element to remove the attribute from.
   * @param attr - The attribute name to remove.
   */
  removeAttribute(el: HTMLElement, attr: string): void;

  /**
   * Toggles an attribute on an element between two values.
   *
   * @param el - The element to toggle the attribute on.
   * @param attr - The attribute name to toggle.
   * @param val1 - The first value to toggle between.
   * @param val2 - The second value to toggle between.
   */
  toggleAttribute(
    el: HTMLElement,
    attr: string,
    val1: string,
    val2: string,
  ): void;

  /**
   * Pushes an event to the server.
   *
   * @param el - An element that belongs to the target LiveView / LiveComponent or a component ID.
   *   To target a LiveComponent by its ID, pass a separate `target` in the options.
   * @param type - The event name to push.
   * @param [opts={}] - Optional settings.
   *   Accepts: `value`, `target`, `page_loading`.
   */
  push(el: HTMLElement, type: string, opts?: PushOpts): void;

  /**
   * Sends a navigation event to the server and updates the browser's pushState history.
   *
   * @param href - The URL to navigate to.
   * @param [opts={}] - Optional settings.
   *   Accepts: `replace`.
   */
  navigate(href: string, opts?: NavigationOpts): void;

  /**
   * Sends a patch event to the server and updates the browser's pushState history.
   *
   * @param href - The URL to patch to.
   * @param [opts={}] - Optional settings.
   *   Accepts: `replace`.
   */
  patch(href: string, opts?: NavigationOpts): void;

  /**
   * Mark attributes as ignored, skipping them when patching the DOM.
   *
   * @param el - The element to ignore attributes on.
   * @param attrs - The attribute name or names to ignore.
   */
  ignoreAttributes(el: HTMLElement, attrs: string | string[]): void;
}

export default (
  liveSocket: LiveSocket,
  eventType: string | null,
): AllJSCommands => {
  return {
    exec(el, encodedJS) {
      liveSocket.execJS(el, encodedJS, eventType);
    },
    show(el, opts = {}) {
      const owner = liveSocket.owner(el);
      JS.show(
        eventType,
        owner,
        el,
        opts.display,
        JS.transitionClasses(opts.transition),
        opts.time,
        opts.blocking,
      );
    },
    hide(el, opts = {}) {
      const owner = liveSocket.owner(el);
      JS.hide(
        eventType,
        owner,
        el,
        null,
        JS.transitionClasses(opts.transition),
        opts.time,
        opts.blocking,
      );
    },
    toggle(el, opts = {}) {
      const owner = liveSocket.owner(el);
      const inTransition = JS.transitionClasses(opts.in);
      const outTransition = JS.transitionClasses(opts.out);
      JS.toggle(
        eventType,
        owner,
        el,
        opts.display,
        inTransition,
        outTransition,
        opts.time,
        opts.blocking,
      );
    },
    addClass(el, names, opts = {}) {
      const classNames = Array.isArray(names) ? names : names.split(" ");
      const owner = liveSocket.owner(el);
      JS.addOrRemoveClasses(
        el,
        classNames,
        [],
        JS.transitionClasses(opts.transition),
        opts.time,
        owner,
        opts.blocking,
      );
    },
    removeClass(el, names, opts = {}) {
      const classNames = Array.isArray(names) ? names : names.split(" ");
      const owner = liveSocket.owner(el);
      JS.addOrRemoveClasses(
        el,
        [],
        classNames,
        JS.transitionClasses(opts.transition),
        opts.time,
        owner,
        opts.blocking,
      );
    },
    toggleClass(el, names, opts = {}) {
      const classNames = Array.isArray(names) ? names : names.split(" ");
      const owner = liveSocket.owner(el);
      JS.toggleClasses(
        el,
        classNames,
        JS.transitionClasses(opts.transition),
        opts.time,
        owner,
        opts.blocking,
      );
    },
    transition(el, transition, opts = {}) {
      const owner = liveSocket.owner(el);
      JS.addOrRemoveClasses(
        el,
        [],
        [],
        JS.transitionClasses(transition),
        opts.time,
        owner,
        opts.blocking,
      );
    },
    setAttribute(el, attr, val) {
      JS.setOrRemoveAttrs(el, [[attr, val]], []);
    },
    removeAttribute(el, attr) {
      JS.setOrRemoveAttrs(el, [], [attr]);
    },
    toggleAttribute(el, attr, val1, val2) {
      JS.toggleAttr(el, attr, val1, val2);
    },
    push(el, type, opts = {}) {
      liveSocket.withinOwners(el, (view) => {
        const data = opts.value || {};
        delete opts.value;
        let e = new CustomEvent("phx:exec", { detail: { sourceElement: el } });
        JS.exec(e, eventType, type, view, el, ["push", { data, ...opts }]);
      });
    },
    navigate(href, opts = {}) {
      const customEvent = new CustomEvent("phx:exec");
      liveSocket.historyRedirect(
        customEvent,
        href,
        opts.replace ? "replace" : "push",
        null,
        null,
      );
    },
    patch(href, opts = {}) {
      const customEvent = new CustomEvent("phx:exec");
      liveSocket.pushHistoryPatch(
        customEvent,
        href,
        opts.replace ? "replace" : "push",
        null,
      );
    },
    ignoreAttributes(el, attrs) {
      JS.ignoreAttrs(el, Array.isArray(attrs) ? attrs : [attrs]);
    },
  };
};

/**
 * JSCommands for use with `liveSocket.js()`.
 * Includes the general `exec` command that requires an element.
 */
export type LiveSocketJSCommands = AllJSCommands;

/**
 * JSCommands for use within a Hook.
 * The `exec` command is tailored for hooks, not requiring an explicit element.
 */
export interface HookJSCommands extends Omit<AllJSCommands, "exec"> {
  /**
   * Executes a JS command in the context of the hook's element.
   *
   * @param encodedJS - The encoded JS command with operations to execute.
   */
  exec(encodedJS: EncodedJS): void;
}


================================================
FILE: assets/js/phoenix_live_view/live_socket.js
================================================
import {
  BINDING_PREFIX,
  CONSECUTIVE_RELOADS,
  DEFAULTS,
  FAILSAFE_JITTER,
  LOADER_TIMEOUT,
  DISCONNECTED_TIMEOUT,
  MAX_RELOADS,
  PHX_DEBOUNCE,
  PHX_DROP_TARGET,
  PHX_HAS_FOCUSED,
  PHX_KEY,
  PHX_LINK_STATE,
  PHX_LIVE_LINK,
  PHX_LV_DEBUG,
  PHX_LV_LATENCY_SIM,
  PHX_LV_PROFILE,
  PHX_LV_HISTORY_POSITION,
  PHX_MAIN,
  PHX_PARENT_ID,
  PHX_VIEW_SELECTOR,
  PHX_ROOT_ID,
  PHX_THROTTLE,
  PHX_TRACK_UPLOADS,
  PHX_SESSION,
  RELOAD_JITTER_MIN,
  RELOAD_JITTER_MAX,
  PHX_REF_SRC,
  PHX_RELOAD_STATUS,
  PHX_RUNTIME_HOOK,
  PHX_DROP_TARGET_ACTIVE_CLASS,
  PHX_TELEPORTED_SRC,
} from "./constants";

import {
  clone,
  closestPhxBinding,
  closure,
  debug,
  maybe,
  logError,
  eventContainsFiles,
} from "./utils";

import Browser from "./browser";
import DOM from "./dom";
import Hooks from "./hooks";
import LiveUploader from "./live_uploader";
import View from "./view";
import JS from "./js";
import jsCommands from "./js_commands";

export const isUsedInput = (el) => DOM.isUsedInput(el);

export default class LiveSocket {
  constructor(url, phxSocket, opts = {}) {
    this.unloaded = false;
    if (!phxSocket || phxSocket.constructor.name === "Object") {
      throw new Error(`
      a phoenix Socket must be provided as the second argument to the LiveSocket constructor. For example:

          import {Socket} from "phoenix"
          import {LiveSocket} from "phoenix_live_view"
          let liveSocket = new LiveSocket("/live", Socket, {...})
      `);
    }
    this.socket = new phxSocket(url, opts);
    this.bindingPrefix = opts.bindingPrefix || BINDING_PREFIX;
    this.opts = opts;
    this.params = closure(opts.params || {});
    this.viewLogger = opts.viewLogger;
    this.metadataCallbacks = opts.metadata || {};
    this.defaults = Object.assign(clone(DEFAULTS), opts.defaults || {});
    this.prevActive = null;
    this.silenced = false;
    this.main = null;
    this.outgoingMainEl = null;
    this.clickStartedAtTarget = null;
    this.linkRef = 1;
    this.roots = {};
    this.href = window.location.href;
    this.pendingLink = null;
    this.currentLocation = clone(window.location);
    this.hooks = opts.hooks || {};
    this.uploaders = opts.uploaders || {};
    this.loaderTimeout = opts.loaderTimeout || LOADER_TIMEOUT;
    this.disconnectedTimeout = opts.disconnectedTimeout || DISCONNECTED_TIMEOUT;
    /**
     * @type {ReturnType<typeof setTimeout> | null}
     */
    this.reloadWithJitterTimer = null;
    this.maxReloads = opts.maxReloads || MAX_RELOADS;
    this.reloadJitterMin = opts.reloadJitterMin || RELOAD_JITTER_MIN;
    this.reloadJitterMax = opts.reloadJitterMax || RELOAD_JITTER_MAX;
    this.failsafeJitter = opts.failsafeJitter || FAILSAFE_JITTER;
    this.localStorage = opts.localStorage || window.localStorage;
    this.sessionStorage = opts.sessionStorage || window.sessionStorage;
    this.boundTopLevelEvents = false;
    this.boundEventNames = new Set();
    this.blockPhxChangeWhileComposing =
      opts.blockPhxChangeWhileComposing || false;
    this.serverCloseRef = null;
    this.domCallbacks = Object.assign(
      {
        jsQuerySelectorAll: null,
        onPatchStart: closure(),
        onPatchEnd: closure(),
        onNodeAdded: closure(),
        onBeforeElUpdated: closure(),
      },
      opts.dom || {},
    );
    this.transitions = new TransitionSet();
    this.currentHistoryPosition =
      parseInt(this.sessionStorage.getItem(PHX_LV_HISTORY_POSITION)) || 0;
    window.addEventListener("pagehide", (_e) => {
      this.unloaded = true;
    });
    this.socket.onOpen(() => {
      if (this.isUnloaded()) {
        // reload page if being restored from back/forward cache and browser does not emit "pageshow"
        window.location.reload();
      }
    });
  }

  // public

  version() {
    return LV_VSN;
  }

  isProfileEnabled() {
    return this.sessionStorage.getItem(PHX_LV_PROFILE) === "true";
  }

  isDebugEnabled() {
    return this.sessionStorage.getItem(PHX_LV_DEBUG) === "true";
  }

  isDebugDisabled() {
    return this.sessionStorage.getItem(PHX_LV_DEBUG) === "false";
  }

  enableDebug() {
    this.sessionStorage.setItem(PHX_LV_DEBUG, "true");
  }

  enableProfiling() {
    this.sessionStorage.setItem(PHX_LV_PROFILE, "true");
  }

  disableDebug() {
    this.sessionStorage.setItem(PHX_LV_DEBUG, "false");
  }

  disableProfiling() {
    this.sessionStorage.removeItem(PHX_LV_PROFILE);
  }

  enableLatencySim(upperBoundMs) {
    this.enableDebug();
    console.log(
      "latency simulator enabled for the duration of this browser session. Call disableLatencySim() to disable",
    );
    this.sessionStorage.setItem(PHX_LV_LATENCY_SIM, upperBoundMs);
  }

  disableLatencySim() {
    this.sessionStorage.removeItem(PHX_LV_LATENCY_SIM);
  }

  getLatencySim() {
    const str = this.sessionStorage.getItem(PHX_LV_LATENCY_SIM);
    return str ? parseInt(str) : null;
  }

  getSocket() {
    return this.socket;
  }

  connect() {
    // enable debug by default if on localhost and not explicitly disabled
    if (window.location.hostname === "localhost" && !this.isDebugDisabled()) {
      this.enableDebug();
    }
    const doConnect = () => {
      this.resetReloadStatus();
      if (this.joinRootViews()) {
        this.bindTopLevelEvents();
        this.socket.connect();
      } else if (this.main) {
        this.socket.connect();
      } else {
        this.bindTopLevelEvents({ dead: true });
      }
      this.joinDeadView();
    };
    if (
      ["complete", "loaded", "interactive"].indexOf(document.readyState) >= 0
    ) {
      doConnect();
    } else {
      document.addEventListener("DOMContentLoaded", () => doConnect());
    }
  }

  disconnect(callback) {
    clearTimeout(this.reloadWithJitterTimer);
    // remove the socket close listener to avoid trying to handle
    // a server close event when it is actually caused by us disconnecting
    if (this.serverCloseRef) {
      this.socket.off(this.serverCloseRef);
      this.serverCloseRef = null;
    }
    this.socket.disconnect(callback);
  }

  replaceTransport(transport) {
    clearTimeout(this.reloadWithJitterTimer);
    this.socket.replaceTransport(transport);
    this.connect();
  }

  /**
   * @param {HTMLElement} el
   * @param {import("./js_commands").EncodedJS} encodedJS
   * @param {string | null} [eventType]
   */
  execJS(el, encodedJS, eventType = null) {
    const e = new CustomEvent("phx:exec", { detail: { sourceElement: el } });
    this.owner(el, (view) => JS.exec(e, eventType, encodedJS, view, el));
  }

  /**
   * Returns an object with methods to manipulate the DOM and execute JavaScript.
   * The applied changes integrate with server DOM patching.
   *
   * @returns {import("./js_commands").LiveSocketJSCommands}
   */
  js() {
    return jsCommands(this, "js");
  }

  // private

  unload() {
    if (this.unloaded) {
      return;
    }
    if (this.main && this.isConnected()) {
      this.log(this.main, "socket", () => ["disconnect for page nav"]);
    }
    this.unloaded = true;
    this.destroyAllViews();
    this.disconnect();
  }

  triggerDOM(kind, args) {
    this.domCallbacks[kind](...args);
  }

  time(name, func) {
    if (!this.isProfileEnabled() || !console.time) {
      return func();
    }
    console.time(name);
    const result = func();
    console.timeEnd(name);
    return result;
  }

  log(view, kind, msgCallback) {
    if (this.viewLogger) {
      const [msg, obj] = msgCallback();
      this.viewLogger(view, kind, msg, obj);
    } else if (this.isDebugEnabled()) {
      const [msg, obj] = msgCallback();
      debug(view, kind, msg, obj);
    }
  }

  requestDOMUpdate(callback) {
    this.transitions.after(callback);
  }

  asyncTransition(promise) {
    this.transitions.addAsyncTransition(promise);
  }

  transition(time, onStart, onDone = function () {}) {
    this.transitions.addTransition(time, onStart, onDone);
  }

  onChannel(channel, event, cb) {
    channel.on(event, (data) => {
      const latency = this.getLatencySim();
      if (!latency) {
        cb(data);
      } else {
        setTimeout(() => cb(data), latency);
      }
    });
  }

  reloadWithJitter(view, log) {
    clearTimeout(this.reloadWithJitterTimer);
    this.disconnect();
    const minMs = this.reloadJitterMin;
    const maxMs = this.reloadJitterMax;
    let afterMs = Math.floor(Math.random() * (maxMs - minMs + 1)) + minMs;
    const tries = Browser.updateLocal(
      this.localStorage,
      window.location.pathname,
      CONSECUTIVE_RELOADS,
      0,
      (count) => count + 1,
    );
    if (tries >= this.maxReloads) {
      afterMs = this.failsafeJitter;
    }
    this.reloadWithJitterTimer = setTimeout(() => {
      // if view has recovered, such as transport replaced, then cancel
      if (view.isDestroyed() || view.isConnected()) {
        return;
      }
      view.destroy();
      log
        ? log()
        : this.log(view, "join", () => [
            `encountered ${tries} consecutive reloads`,
          ]);
      if (tries >= this.maxReloads) {
        this.log(view, "join", () => [
          `exceeded ${this.maxReloads} consecutive reloads. Entering failsafe mode`,
        ]);
      }
      if (this.hasPendingLink()) {
        window.location = this.pendingLink;
      } else {
        window.location.reload();
      }
    }, afterMs);
  }

  getHookDefinition(name) {
    if (!name) {
      return;
    }
    return (
      this.maybeInternalHook(name) ||
      this.hooks[name] ||
      this.maybeRuntimeHook(name)
    );
  }

  maybeInternalHook(name) {
    return name && name.startsWith("Phoenix.") && Hooks[name.split(".")[1]];
  }

  maybeRuntimeHook(name) {
    const runtimeHook = document.querySelector(
      `script[${PHX_RUNTIME_HOOK}="${CSS.escape(name)}"]`,
    );
    if (!runtimeHook) {
      return;
    }
    let callbacks = window[`phx_hook_${name}`];
    if (!callbacks || typeof callbacks !== "function") {
      logError("a runtime hook must be a function", runtimeHook);
      return;
    }
    const hookDefiniton = callbacks();
    if (
      hookDefiniton &&
      (typeof hookDefiniton === "object" || typeof hookDefiniton === "function")
    ) {
      return hookDefiniton;
    }
    logError(
      "runtime hook must return an object with hook callbacks or an instance of ViewHook",
      runtimeHook,
    );
  }

  isUnloaded() {
    return this.unloaded;
  }

  isConnected() {
    return this.socket.isConnected();
  }

  getBindingPrefix() {
    return this.bindingPrefix;
  }

  binding(kind) {
    return `${this.getBindingPrefix()}${kind}`;
  }

  channel(topic, params) {
    return this.socket.channel(topic, params);
  }

  joinDeadView() {
    const body = document.body;
    if (
      body &&
      !this.isPhxView(body) &&
      !this.isPhxView(document.firstElementChild)
    ) {
      const view = this.newRootView(body);
      view.setHref(this.getHref());
      view.joinDead();
      if (!this.main) {
        this.main = view;
      }
      window.requestAnimationFrame(() => {
        view.execNewMounted();
        // restore scroll position when navigating from an external / non-live page
        this.maybeScroll(history.state?.scroll);
      });
    }
  }

  joinRootViews() {
    let rootsFound = false;
    DOM.all(
      document,
      `${PHX_VIEW_SELECTOR}:not([${PHX_PARENT_ID}])`,
      (rootEl) => {
        if (!this.getRootById(rootEl.id)) {
          const view = this.newRootView(rootEl);
          // stickies cannot be mounted at the router and therefore should not
          // get a href set on them
          if (!DOM.isPhxSticky(rootEl)) {
            view.setHref(this.getHref());
          }
          view.join();
          if (rootEl.hasAttribute(PHX_MAIN)) {
            this.main = view;
          }
        }
        rootsFound = true;
      },
    );
    return rootsFound;
  }

  redirect(to, flash, reloadToken) {
    if (reloadToken) {
      Browser.setCookie(PHX_RELOAD_STATUS, reloadToken, 60);
    }
    this.unload();
    Browser.redirect(to, flash);
  }

  replaceMain(
    href,
    flash,
    callback = null,
    linkRef = this.setPendingLink(href),
  ) {
    const liveReferer = this.currentLocation.href;
    this.outgoingMainEl = this.outgoingMainEl || this.main.el;

    const stickies = DOM.findPhxSticky(document) || [];
    const removeEls = DOM.all(
      this.outgoingMainEl,
      `[${this.binding("remove")}]`,
    ).filter((el) => !DOM.isChildOfAny(el, stickies));

    const newMainEl = DOM.cloneNode(this.outgoingMainEl, "");
    this.main.showLoader(this.loaderTimeout);
    this.main.destroy();

    this.main = this.newRootView(newMainEl, flash, liveReferer);
    this.main.setRedirect(href);
    this.transitionRemoves(removeEls);
    this.main.join((joinCount, onDone) => {
      if (joinCount === 1 && this.commitPendingLink(linkRef)) {
        this.requestDOMUpdate(() => {
          // remove phx-remove els right before we replace the main element
          removeEls.forEach((el) => el.remove());
          stickies.forEach((el) => newMainEl.appendChild(el));
          this.outgoingMainEl.replaceWith(newMainEl);
          this.outgoingMainEl = null;
          callback && callback(linkRef);
          onDone();
        });
      }
    });
  }

  transitionRemoves(elements, callback) {
    const removeAttr = this.binding("remove");
    const silenceEvents = (e) => {
      e.preventDefault();
      e.stopImmediatePropagation();
    };
    elements.forEach((el) => {
      // prevent all listeners we care about from bubbling to window
      // since we are removing the element
      for (const event of this.boundEventNames) {
        el.addEventListener(event, silenceEvents, true);
      }
      this.execJS(el, el.getAttribute(removeAttr), "remove");
    });
    // remove the silenced listeners when transitions are done incase the element is re-used
    // and call caller's callback as soon as we are done with transitions
    this.requestDOMUpdate(() => {
      elements.forEach((el) => {
        for (const event of this.boundEventNames) {
          el.removeEventListener(event, silenceEvents, true);
        }
      });
      callback && callback();
    });
  }

  isPhxView(el) {
    return el.getAttribute && el.getAttribute(PHX_SESSION) !== null;
  }

  newRootView(el, flash, liveReferer) {
    const view = new View(el, this, null, flash, liveReferer);
    this.roots[view.id] = view;
    return view;
  }

  owner(childEl, callback) {
    let view;
    const viewEl = DOM.closestViewEl(childEl);
    if (viewEl) {
      // it can happen that we find a view that is already destroyed;
      // in that case we DO NOT want to fallback to the main element
      view = this.getViewByEl(viewEl);
    } else {
      if (!childEl.isConnected) {
        // if the element is not part of the DOM any more
        // there's no owner and we should not do fall back
        return null;
      }
      view = this.main;
    }
    return view && callback ? callback(view) : view;
  }

  withinOwners(childEl, callback) {
    this.owner(childEl, (view) => callback(view, childEl));
  }

  getViewByEl(el) {
    const rootId = el.getAttribute(PHX_ROOT_ID);
    return maybe(this.getRootById(rootId), (root) =>
      root.getDescendentByEl(el),
    );
  }

  getRootById(id) {
    return this.roots[id];
  }

  destroyAllViews() {
    for (const id in this.roots) {
      this.roots[id].destroy();
      delete this.roots[id];
    }
    this.main = null;
  }

  destroyViewByEl(el) {
    const root = this.getRootById(el.getAttribute(PHX_ROOT_ID));
    if (root && root.id === el.id) {
      root.destroy();
      delete this.roots[root.id];
    } else if (root) {
      root.destroyDescendent(el.id);
    }
  }

  getActiveElement() {
    return document.activeElement;
  }

  dropActiveElement(view) {
    if (this.prevActive && view.ownsElement(this.prevActive)) {
      this.prevActive = null;
    }
  }

  restorePreviouslyActiveFocus() {
    if (
      this.prevActive &&
      this.prevActive !== document.body &&
      this.prevActive instanceof HTMLElement
    ) {
      this.prevActive.focus();
    }
  }

  blurActiveElement() {
    this.prevActive = this.getActiveElement();
    if (
      this.prevActive !== document.body &&
      this.prevActive instanceof HTMLElement
    ) {
      this.prevActive.blur();
    }
  }

  /**
   * @param {{dead?: boolean}} [options={}]
   */
  bindTopLevelEvents({ dead } = {}) {
    if (this.boundTopLevelEvents) {
      return;
    }

    this.boundTopLevelEvents = true;
    // enter failsafe reload if server has gone away intentionally, such as "disconnect" broadcast
    this.serverCloseRef = this.socket.onClose((event) => {
      // failsafe reload if normal closure and we still have a main LV
      if (event && event.code === 1000 && this.main) {
        return this.reloadWithJitter(this.main);
      }
    });
    document.body.addEventListener("click", function () {}); // ensure all click events bubble for mobile Safari
    window.addEventListener(
      "pageshow",
      (e) => {
        if (e.persisted) {
          // reload page if being restored from back/forward cache
          this.getSocket().disconnect();
          this.withPageLoading({ to: window.location.href, kind: "redirect" });
          window.location.reload();
        }
      },
      true,
    );
    if (!dead) {
      this.bindNav();
    }
    this.bindClicks();
    if (!dead) {
      this.bindForms();
    }
    this.bind(
      { keyup: "keyup", keydown: "keydown" },
      (e, type, view, targetEl, phxEvent, _phxTarget) => {
        const matchKey = targetEl.getAttribute(this.binding(PHX_KEY));
        const pressedKey = e.key && e.key.toLowerCase(); // chrome clicked autocompletes send a keydown without key
        if (matchKey && matchKey.toLowerCase() !== pressedKey) {
          return;
        }

        const data = { key: e.key, ...this.eventMeta(type, e, targetEl) };
        JS.exec(e, type, phxEvent, view, targetEl, ["push", { data }]);
      },
    );
    this.bind(
      { blur: "focusout", focus: "focusin" },
      (e, type, view, targetEl, phxEvent, phxTarget) => {
        if (!phxTarget) {
          const data = { key: e.key, ...this.eventMeta(type, e, targetEl) };
          JS.exec(e, type, phxEvent, view, targetEl, ["push", { data }]);
        }
      },
    );
    this.bind(
      { blur: "blur", focus: "focus" },
      (e, type, view, targetEl, phxEvent, phxTarget) => {
        // blur and focus are triggered on document and window. Discard one to avoid dups
        if (phxTarget === "window") {
          const data = this.eventMeta(type, e, targetEl);
          JS.exec(e, type, phxEvent, view, targetEl, ["push", { data }]);
        }
      },
    );
    this.on("dragover", (e) => e.preventDefault());
    this.on("dragenter", (e) => {
      const dropzone = closestPhxBinding(
        e.target,
        this.binding(PHX_DROP_TARGET),
      );

      if (!dropzone || !(dropzone instanceof HTMLElement)) {
        return;
      }

      if (eventContainsFiles(e)) {
        this.js().addClass(dropzone, PHX_DROP_TARGET_ACTIVE_CLASS);
      }
    });
    this.on("dragleave", (e) => {
      const dropzone = closestPhxBinding(
        e.target,
        this.binding(PHX_DROP_TARGET),
      );

      if (!dropzone || !(dropzone instanceof HTMLElement)) {
        return;
      }

      // Avoid add/remove jitter in the case that we drag into a new child and that child would
      // resolve their closest drop target to the current dropzone element
      const rect = dropzone.getBoundingClientRect();
      if (
        e.clientX <= rect.left ||
        e.clientX >= rect.right ||
        e.clientY <= rect.top ||
        e.clientY >= rect.bottom
      ) {
        this.js().removeClass(dropzone, PHX_DROP_TARGET_ACTIVE_CLASS);
      }
    });
    this.on("drop", (e) => {
      e.preventDefault();

      const dropzone = closestPhxBinding(
        e.target,
        this.binding(PHX_DROP_TARGET),
      );
      if (!dropzone || !(dropzone instanceof HTMLElement)) {
        return;
      }
      this.js().removeClass(dropzone, PHX_DROP_TARGET_ACTIVE_CLASS);

      const dropTargetId = dropzone.getAttribute(this.binding(PHX_DROP_TARGET));
      const dropTarget = dropTargetId && document.getElementById(dropTargetId);
      const files = Array.from(e.dataTransfer.files || []);
      if (
        !dropTarget ||
        !(dropTarget instanceof HTMLInputElement) ||
        dropTarget.disabled ||
        files.length === 0 ||
        !(dropTarget.files instanceof FileList)
      ) {
        return;
      }

      LiveUploader.trackFiles(dropTarget, files, e.dataTransfer);
      dropTarget.dispatchEvent(new Event("input", { bubbles: true }));
    });
    this.on(PHX_TRACK_UPLOADS, (e) => {
      const uploadTarget = e.target;
      if (!DOM.isUploadInput(uploadTarget)) {
        return;
      }
      const files = Array.from(e.detail.files || []).filter(
        (f) => f instanceof File || f instanceof Blob,
      );
      LiveUploader.trackFiles(uploadTarget, files);
      uploadTarget.dispatchEvent(new Event("input", { bubbles: true }));
    });
  }

  eventMeta(eventName, e, targetEl) {
    const callback = this.metadataCallbacks[eventName];
    return callback ? callback(e, targetEl) : {};
  }

  setPendingLink(href) {
    this.linkRef++;
    this.pendingLink = href;
    this.resetReloadStatus();
    return this.linkRef;
  }

  // anytime we are navigating or connecting, drop reload cookie in case
  // we issue the cookie but the next request was interrupted and the server never dropped it
  resetReloadStatus() {
    Browser.deleteCookie(PHX_RELOAD_STATUS);
  }

  commitPendingLink(linkRef) {
    if (this.linkRef !== linkRef) {
      return false;
    } else {
      this.href = this.pendingLink;
      this.pendingLink = null;
      return true;
    }
  }

  getHref() {
    return this.href;
  }

  hasPendingLink() {
    return !!this.pendingLink;
  }

  bind(events, callback) {
    for (const event in events) {
      const browserEventName = events[event];

      this.on(browserEventName, (e) => {
        const binding = this.binding(event);
        const windowBinding = this.binding(`window-${event}`);
        const targetPhxEvent =
          e.target.getAttribute && e.target.getAttribute(binding);
        if (targetPhxEvent) {
          this.debounce(e.target, e, browserEventName, () => {
            this.withinOwners(e.target, (view) => {
              callback(e, event, view, e.target, targetPhxEvent, null);
            });
          });
        } else {
          DOM.all(document, `[${windowBinding}]`, (el) => {
            const phxEvent = el.getAttribute(windowBinding);
            this.debounce(el, e, browserEventName, () => {
              this.withinOwners(el, (view) => {
                callback(e, event, view, el, phxEvent, "window");
              });
            });
          });
        }
      });
    }
  }

  bindClicks() {
    this.on("mousedown", (e) => (this.clickStartedAtTarget = e.target));
    this.bindClick("click", "click");
  }

  bindClick(eventName, bindingName) {
    const click = this.binding(bindingName);
    window.addEventListener(
      eventName,
      (e) => {
        let target = null;
        // a synthetic click event (detail 0) will not have caused a mousedown event,
        // therefore the clickStartedAtTarget is stale
        if (e.detail === 0) this.clickStartedAtTarget = e.target;
        const clickStartedAtTarget = this.clickStartedAtTarget || e.target;
        // when searching the target for the click event, we always want to
        // use the actual event target, see #3372
        target = closestPhxBinding(e.target, click);
        this.dispatchClickAway(e, clickStartedAtTarget);
        this.clickStartedAtTarget = null;
        const phxEvent = target && target.getAttribute(click);
        if (!phxEvent) {
          if (DOM.isNewPageClick(e, window.location)) {
            this.unload();
          }
          return;
        }

        if (target.getAttribute("href") === "#") {
          e.preventDefault();
        }

        // noop if we are in the middle of awaiting an ack for this el already
        if (target.hasAttribute(PHX_REF_SRC)) {
          return;
        }

        this.debounce(target, e, "click", () => {
          this.withinOwners(target, (view) => {
            JS.exec(e, "click", phxEvent, view, target, [
              "push",
              { data: this.eventMeta("click", e, target) },
            ]);
          });
        });
      },
      false,
    );
  }

  dispatchClickAway(e, clickStartedAt) {
    const phxClickAway = this.binding("click-away");
    const portal = clickStartedAt.closest(`[${PHX_TELEPORTED_SRC}]`);
    const portalStartedAt =
      portal && DOM.byId(portal.getAttribute(PHX_TELEPORTED_SRC));
    DOM.all(document, `[${phxClickAway}]`, (el) => {
      let startedAt = clickStartedAt;
      if (portal && !portal.contains(el)) {
        // If we have a portal and the click-away element is not inside it,
        // then treat the portal source as the starting point instead.
        startedAt = portalStartedAt;
      }
      if (
        !(
          el.isSameNode(startedAt) ||
          el.contains(startedAt) ||
          // When clicking a link with custom method,
          // phoenix_html triggers a click on a submit button
          // of a hidden form appended to the body. For such cases
          // where the clicked target is hidden, we skip click-away.
          //
          // Also, when we have a portal, we don't want to check the visibility
          // of the portal source, as it's a <template> that is always not visible.
          // Instead, check the visibility of the original click target.
          !JS.isVisible(clickStartedAt)
        )
      ) {
        this.withinOwners(el, (view) => {
          const phxEvent = el.getAttribute(phxClickAway);
          if (JS.isVisible(el) && JS.isInViewport(el)) {
            JS.exec(e, "click", phxEvent, view, el, [
              "push",
              { data: this.eventMeta("click", e, e.target) },
            ]);
          }
        });
      }
    });
  }

  bindNav() {
    if (!Browser.canPushState()) {
      return;
    }
    if (history.scrollRestoration) {
      history.scrollRestoration = "manual";
    }
    let scrollTimer = null;
    window.addEventListener("scroll", (_e) => {
      clearTimeout(scrollTimer);
      scrollTimer = setTimeout(() => {
        Browser.updateCurrentState((state) =>
          Object.assign(state, { scroll: window.scrollY }),
        );
      }, 100);
    });
    window.addEventListener(
      "popstate",
      (event) => {
        if (!this.registerNewLocation(window.location)) {
          return;
        }
        const { type, backType, id, scroll, position } = event.state || {};
        const href = window.location.href;

        // Compare positions to determine direction
        const isForward = position > this.currentHistoryPosition;
        const navType = isForward ? type : backType || type;

        // Update current position
        this.currentHistoryPosition = position || 0;
        this.sessionStorage.setItem(
          PHX_LV_HISTORY_POSITION,
          this.currentHistoryPosition.toString(),
        );

        DOM.dispatchEvent(window, "phx:navigate", {
          detail: {
            href,
            patch: navType === "patch",
            pop: true,
            direction: isForward ? "forward" : "backward",
          },
        });
        this.requestDOMUpdate(() => {
          const callback = () => {
            this.maybeScroll(scroll);
          };
          if (
            this.main.isConnected() &&
            navType === "patch" &&
            id === this.main.id
          ) {
            this.main.pushLinkPatch(event, href, null, callback);
          } else {
            this.replaceMain(href, null, callback);
          }
        });
      },
      false,
    );
    window.addEventListener(
      "click",
      (e) => {
        const target = closestPhxBinding(e.target, PHX_LIVE_LINK);
        const type = target && target.getAttribute(PHX_LIVE_LINK);
        if (!type || !this.isConnected() || !this.main || DOM.wantsNewTab(e)) {
          return;
        }

        // When wrapping an SVG element in an anchor tag, the href can be an SVGAnimatedString
        const href =
          target.href instanceof SVGAnimatedString
            ? target.href.baseVal
            : target.href;

        const linkState = target.getAttribute(PHX_LINK_STATE);
        e.preventDefault();
        e.stopImmediatePropagation(); // do not bubble click to regular phx-click bindings
        if (this.pendingLink === href) {
          return;
        }

        this.requestDOMUpdate(() => {
          if (type === "patch") {
            this.pushHistoryPatch(e, href, linkState, target);
          } else if (type === "redirect") {
            this.historyRedirect(e, href, linkState, null, target);
          } else {
            throw new Error(
              `expected ${PHX_LIVE_LINK} to be "patch" or "redirect", got: ${type}`,
            );
          }
          const phxClick = target.getAttribute(this.binding("click"));
          if (phxClick) {
            this.requestDOMUpdate(() => this.execJS(target, phxClick, "click"));
          }
        });
      },
      false,
    );
  }

  maybeScroll(scroll) {
    if (typeof scroll === "number") {
      requestAnimationFrame(() => {
        window.scrollTo(0, scroll);
      }); // the body needs to render before we scroll.
    }
  }

  dispatchEvent(event, payload = {}) {
    DOM.dispatchEvent(window, `phx:${event}`, { detail: payload });
  }

  dispatchEvents(events) {
    events.forEach(([event, payload]) => this.dispatchEvent(event, payload));
  }

  withPageLoading(info, callback) {
    DOM.dispatchEvent(window, "phx:page-loading-start", { detail: info });
    const done = () =>
      DOM.dispatchEvent(window, "phx:page-loading-stop", { detail: info });
    return callback ? callback(done) : done;
  }

  pushHistoryPatch(e, href, linkState, targetEl) {
    if (!this.isConnected() || !this.main.isMain()) {
      return Browser.redirect(href);
    }

    this.withPageLoading({ to: href, kind: "patch" }, (done) => {
      this.main.pushLinkPatch(e, href, targetEl, (linkRef) => {
        this.historyPatch(href, linkState, linkRef);
        done();
      });
    });
  }

  historyPatch(href, linkState, linkRef = this.setPendingLink(href)) {
    if (!this.commitPendingLink(linkRef)) {
      return;
    }

    // Increment position for new state
    this.currentHistoryPosition++;
    this.sessionStorage.setItem(
      PHX_LV_HISTORY_POSITION,
      this.currentHistoryPosition.toString(),
    );

    // store the type for back navigation
    Browser.updateCurrentState((state) => ({ ...state, backType: "patch" }));

    Browser.pushState(
      linkState,
      {
        type: "patch",
        id: this.main.id,
        position: this.currentHistoryPosition,
      },
      href,
    );

    DOM.dispatchEvent(window, "phx:navigate", {
      detail: { patch: true, href, pop: false, direction: "forward" },
    });
    this.registerNewLocation(window.location);
  }

  historyRedirect(e, href, linkState, flash, targetEl) {
    const clickLoading = targetEl && e.isTrusted && e.type !== "popstate";
    if (clickLoading) {
      targetEl.classList.add("phx-click-loading");
    }
    if (!this.isConnected() || !this.main.isMain()) {
      return Browser.redirect(href, flash);
    }

    // convert to full href if only path prefix
    if (/^\/$|^\/[^\/]+.*$/.test(href)) {
      const { protocol, host } = window.location;
      href = `${protocol}//${host}${href}`;
    }
    const scroll = window.scrollY;
    this.withPageLoading({ to: href, kind: "redirect" }, (done) => {
      this.replaceMain(href, flash, (linkRef) => {
        if (linkRef === this.linkRef) {
          // Increment position for new state
          this.currentHistoryPosition++;
          this.sessionStorage.setItem(
            PHX_LV_HISTORY_POSITION,
            this.currentHistoryPosition.toString(),
          );

          // store the type for back navigation
          Browser.updateCurrentState((state) => ({
            ...state,
            backType: "redirect",
          }));

          Browser.pushState(
            linkState,
            {
              type: "redirect",
              id: this.main.id,
              scroll: scroll,
              position: this.currentHistoryPosition,
            },
            href,
          );

          DOM.dispatchEvent(window, "phx:navigate", {
            detail: { href, patch: false, pop: false, direction: "forward" },
          });
          this.registerNewLocation(window.location);
        }
        // explicitly undo click-loading class
        // (in case it originated in a sticky live view, otherwise it would be removed anyway)
        if (clickLoading) {
          targetEl.classList.remove("phx-click-loading");
        }
        done();
      });
    });
  }

  registerNewLocation(newLocation) {
    const { pathname, search } = this.currentLocation;
    if (pathname + search === newLocation.pathname + newLocation.search) {
      return false;
    } else {
      this.currentLocation = clone(newLocation);
      return true;
    }
  }

  bindForms() {
    let iterations = 0;
    let externalFormSubmitted = false;

    // disable forms on submit that track phx-change but perform external submit
    this.on("submit", (e) => {
      const phxSubmit = e.target.getAttribute(this.binding("submit"));
      const phxChange = e.target.getAttribute(this.binding("change"));
      if (!externalFormSubmitted && phxChange && !phxSubmit) {
        externalFormSubmitted = true;
        e.preventDefault();
        this.withinOwners(e.target, (view) => {
          view.disableForm(e.target);
          // safari needs next tick
          window.requestAnimationFrame(() => {
            if (DOM.isUnloadableFormSubmit(e)) {
              this.unload();
            }
            e.target.submit();
          });
        });
      }
    });

    this.on("submit", (e) => {
      const phxEvent = e.target.getAttribute(this.binding("submit"));
      if (!phxEvent) {
        if (DOM.isUnloadableFormSubmit(e)) {
          this.unload();
        }
        return;
      }
      e.preventDefault();
      e.target.disabled = true;
      this.withinOwners(e.target, (view) => {
        JS.exec(e, "submit", phxEvent, view, e.target, [
          "push",
          { submitter: e.submitter },
        ]);
      });
    });

    for (const type of ["change", "input"]) {
      this.on(type, (e) => {
        if (
          e instanceof CustomEvent &&
          (e.target instanceof HTMLInputElement ||
            e.target instanceof HTMLSelectElement ||
            e.target instanceof HTMLTextAreaElement) &&
          e.target.form === undefined
        ) {
          // throw on invalid JS.dispatch target and noop if CustomEvent triggered outside JS.dispatch
          if (e.detail && e.detail.dispatcher) {
            throw new Error(
              `dispatching a custom ${type} event is only supported on input elements inside a form`,
            );
          }
          return;
        }
        const phxChange = this.binding("change");
        const input = e.target;
        if (this.blockPhxChangeWhileComposing && e.isComposing) {
          const key = `composition-listener-${type}`;
          if (!DOM.private(input, key)) {
            DOM.putPrivate(input, key, true);
            input.addEventListener(
              "compositionend",
              () => {
                // trigger a new input/change event
                input.dispatchEvent(new Event(type, { bubbles: true }));
                DOM.deletePrivate(input, key);
              },
              { once: true },
            );
          }
          return;
        }
        const inputEvent = input.getAttribute(phxChange);
        const formEvent = input.form && input.form.getAttribute(phxChange);
        const phxEvent = inputEvent || formEvent;
        if (!phxEvent) {
          return;
        }
        if (
          input.type === "number" &&
          input.validity &&
          input.validity.badInput
        ) {
          return;
        }

        const dispatcher = inputEvent ? input : input.form;
        const currentIterations = iterations;
        iterations++;
        const { at: at, type: lastType } =
          DOM.private(input, "prev-iteration") || {};
        // Browsers should always fire at least one "input" event before every "change"
        // Ignore "change" events, unless there was no prior "input" event.
        // This could happen if user code triggers a "change" event, or if the browser is non-conforming.
        if (
          at === currentIterations - 1 &&
          type === "change" &&
          lastType === "input"
        ) {
          return;
        }

        DOM.putPrivate(input, "prev-iteration", {
          at: currentIterations,
          type: type,
        });

        this.debounce(input, e, type, () => {
          this.withinOwners(dispatcher, (view) => {
            DOM.putPrivate(input, PHX_HAS_FOCUSED, true);
            JS.exec(e, "change", phxEvent, view, input, [
              "push",
              { _target: e.target.name, dispatcher: dispatcher },
            ]);
          });
        });
      });
    }
    this.on("reset", (e) => {
      const form = e.target;
      DOM.resetForm(form);
      const input = Array.from(form.elements).find((el) => el.type === "reset");
      if (input) {
        // wait until next tick to get updated input value
        window.requestAnimationFrame(() => {
          input.dispatchEvent(
            new Event("input", { bubbles: true, cancelable: false }),
          );
        });
  
Download .txt
gitextract_3104ppvg/

├── .formatter.exs
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── config.yml
│   ├── extract-changelog.sh
│   ├── single-file-samples/
│   │   ├── main.exs
│   │   └── test.exs
│   └── workflows/
│       ├── assets.yml
│       ├── ci.yml
│       ├── docs.yml
│       ├── github_release.yml
│       └── npm-publish.yml
├── .gitignore
├── .igniter.exs
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── assets/
│   ├── .prettierignore
│   ├── .prettierrc
│   ├── js/
│   │   └── phoenix_live_view/
│   │       ├── aria.js
│   │       ├── browser.js
│   │       ├── constants.js
│   │       ├── dom.js
│   │       ├── dom_patch.js
│   │       ├── dom_post_morph_restorer.js
│   │       ├── element_ref.js
│   │       ├── entry_uploader.js
│   │       ├── global.d.ts
│   │       ├── hooks.js
│   │       ├── index.ts
│   │       ├── js.js
│   │       ├── js_commands.ts
│   │       ├── live_socket.js
│   │       ├── live_uploader.js
│   │       ├── rendered.js
│   │       ├── upload_entry.js
│   │       ├── utils.js
│   │       ├── view.js
│   │       └── view_hook.ts
│   └── test/
│       ├── browser_test.ts
│       ├── debounce_test.ts
│       ├── dom_test.ts
│       ├── event_test.ts
│       ├── globals.d.ts
│       ├── hook_types_test.ts
│       ├── index_test.ts
│       ├── integration/
│       │   ├── event_test.ts
│       │   ├── metadata_test.ts
│       │   └── portal_test.ts
│       ├── js_test.ts
│       ├── live_socket_test.ts
│       ├── modify_root_test.ts
│       ├── rendered_test.ts
│       ├── test_helpers.ts
│       ├── tsconfig.json
│       ├── utils_test.ts
│       └── view_test.ts
├── babel.config.json
├── config/
│   ├── config.exs
│   ├── dev.exs
│   ├── docs.exs
│   ├── e2e.exs
│   └── test.exs
├── eslint.config.js
├── guides/
│   ├── cheatsheets/
│   │   └── html-attrs.cheatmd
│   ├── client/
│   │   ├── bindings.md
│   │   ├── external-uploads.md
│   │   ├── form-bindings.md
│   │   ├── js-interop.md
│   │   └── syncing-changes.md
│   ├── introduction/
│   │   └── welcome.md
│   └── server/
│       ├── assigns-eex.md
│       ├── deployments.md
│       ├── error-handling.md
│       ├── gettext.md
│       ├── live-layouts.md
│       ├── live-navigation.md
│       ├── security-model.md
│       ├── telemetry.md
│       └── uploads.md
├── jest.config.js
├── lib/
│   ├── mix/
│   │   └── tasks/
│   │       ├── compile/
│   │       │   └── phoenix_live_view.ex
│   │       └── phoenix_live_view.upgrade.ex
│   ├── phoenix_component/
│   │   ├── declarative.ex
│   │   └── macro_component.ex
│   ├── phoenix_component.ex
│   ├── phoenix_live_component.ex
│   ├── phoenix_live_view/
│   │   ├── application.ex
│   │   ├── async.ex
│   │   ├── async_result.ex
│   │   ├── channel.ex
│   │   ├── colocated_hook.ex
│   │   ├── colocated_js.ex
│   │   ├── controller.ex
│   │   ├── debug.ex
│   │   ├── diff.ex
│   │   ├── engine.ex
│   │   ├── helpers.ex
│   │   ├── html_algebra.ex
│   │   ├── html_engine.ex
│   │   ├── html_formatter/
│   │   │   └── tag_formatter.ex
│   │   ├── html_formatter.ex
│   │   ├── igniter/
│   │   │   └── upgrade_to_1_1.ex
│   │   ├── js.ex
│   │   ├── lifecycle.ex
│   │   ├── live_stream.ex
│   │   ├── logger.ex
│   │   ├── plug.ex
│   │   ├── renderer.ex
│   │   ├── route.ex
│   │   ├── router.ex
│   │   ├── session.ex
│   │   ├── socket.ex
│   │   ├── static.ex
│   │   ├── tag_engine/
│   │   │   ├── compiler.ex
│   │   │   ├── parser.ex
│   │   │   └── tokenizer.ex
│   │   ├── tag_engine.ex
│   │   ├── test/
│   │   │   ├── client_proxy.ex
│   │   │   ├── diff.ex
│   │   │   ├── dom.ex
│   │   │   ├── live_view_test.ex
│   │   │   ├── structs.ex
│   │   │   ├── tree_dom.ex
│   │   │   ├── upload_client.ex
│   │   │   └── utils.ex
│   │   ├── upload.ex
│   │   ├── upload_channel.ex
│   │   ├── upload_config.ex
│   │   ├── upload_tmp_file_writer.ex
│   │   ├── upload_writer.ex
│   │   └── utils.ex
│   ├── phoenix_live_view.ex
│   └── prettier.ex
├── mix.exs
├── package.json
├── priv/
│   └── static/
│       ├── phoenix_live_view.cjs.js
│       ├── phoenix_live_view.esm.js
│       └── phoenix_live_view.js
├── setupTests.js
├── test/
│   ├── e2e/
│   │   ├── .prettierignore
│   │   ├── README.md
│   │   ├── merge-coverage.js
│   │   ├── playwright.config.js
│   │   ├── support/
│   │   │   ├── colocated_live.ex
│   │   │   ├── components_live.ex
│   │   │   ├── error_live.ex
│   │   │   ├── form_dynamic_inputs_live.ex
│   │   │   ├── form_feedback.ex
│   │   │   ├── form_live.ex
│   │   │   ├── issues/
│   │   │   │   ├── issue_2787.ex
│   │   │   │   ├── issue_2965.ex
│   │   │   │   ├── issue_3026.ex
│   │   │   │   ├── issue_3040.ex
│   │   │   │   ├── issue_3047.ex
│   │   │   │   ├── issue_3083.ex
│   │   │   │   ├── issue_3107.ex
│   │   │   │   ├── issue_3117.ex
│   │   │   │   ├── issue_3169.ex
│   │   │   │   ├── issue_3194.ex
│   │   │   │   ├── issue_3200.ex
│   │   │   │   ├── issue_3378.ex
│   │   │   │   ├── issue_3448.ex
│   │   │   │   ├── issue_3496.ex
│   │   │   │   ├── issue_3529.ex
│   │   │   │   ├── issue_3530.ex
│   │   │   │   ├── issue_3612.ex
│   │   │   │   ├── issue_3636.ex
│   │   │   │   ├── issue_3647.ex
│   │   │   │   ├── issue_3651.ex
│   │   │   │   ├── issue_3656.ex
│   │   │   │   ├── issue_3658.ex
│   │   │   │   ├── issue_3681.ex
│   │   │   │   ├── issue_3684.ex
│   │   │   │   ├── issue_3686.ex
│   │   │   │   ├── issue_3709.ex
│   │   │   │   ├── issue_3719.ex
│   │   │   │   ├── issue_3814.ex
│   │   │   │   ├── issue_3819.ex
│   │   │   │   ├── issue_3919.ex
│   │   │   │   ├── issue_3931.ex
│   │   │   │   ├── issue_3941.ex
│   │   │   │   ├── issue_3953.ex
│   │   │   │   ├── issue_3979.ex
│   │   │   │   ├── issue_4027.ex
│   │   │   │   ├── issue_4066.ex
│   │   │   │   ├── issue_4078.ex
│   │   │   │   ├── issue_4088.ex
│   │   │   │   ├── issue_4094.ex
│   │   │   │   ├── issue_4095.ex
│   │   │   │   ├── issue_4102.ex
│   │   │   │   ├── issue_4107.ex
│   │   │   │   ├── issue_4121.ex
│   │   │   │   └── issue_4147.ex
│   │   │   ├── js_live.ex
│   │   │   ├── keyed_comprehension_live.ex
│   │   │   ├── navigation.ex
│   │   │   ├── portal.ex
│   │   │   ├── select_live.ex
│   │   │   └── upload_live.ex
│   │   ├── teardown.js
│   │   ├── test-fixtures.js
│   │   ├── test_helper.exs
│   │   ├── tests/
│   │   │   ├── colocated.spec.js
│   │   │   ├── components.spec.js
│   │   │   ├── errors.spec.js
│   │   │   ├── forms.spec.js
│   │   │   ├── issues/
│   │   │   │   ├── 2787.spec.js
│   │   │   │   ├── 2965.spec.js
│   │   │   │   ├── 3026.spec.js
│   │   │   │   ├── 3040.spec.js
│   │   │   │   ├── 3047.spec.js
│   │   │   │   ├── 3083.spec.js
│   │   │   │   ├── 3107.spec.js
│   │   │   │   ├── 3117.spec.js
│   │   │   │   ├── 3169.spec.js
│   │   │   │   ├── 3194.spec.js
│   │   │   │   ├── 3200.spec.js
│   │   │   │   ├── 3378.spec.js
│   │   │   │   ├── 3448.spec.js
│   │   │   │   ├── 3496.spec.js
│   │   │   │   ├── 3529.spec.js
│   │   │   │   ├── 3530.spec.js
│   │   │   │   ├── 3612.spec.js
│   │   │   │   ├── 3636.spec.js
│   │   │   │   ├── 3647.spec.js
│   │   │   │   ├── 3651.spec.js
│   │   │   │   ├── 3656.spec.js
│   │   │   │   ├── 3658.spec.js
│   │   │   │   ├── 3681.spec.js
│   │   │   │   ├── 3684.spec.js
│   │   │   │   ├── 3686.spec.js
│   │   │   │   ├── 3709.spec.js
│   │   │   │   ├── 3719.spec.js
│   │   │   │   ├── 3814.spec.js
│   │   │   │   ├── 3819.spec.js
│   │   │   │   ├── 3919.spec.js
│   │   │   │   ├── 3931.spec.js
│   │   │   │   ├── 3941.spec.js
│   │   │   │   ├── 3953.spec.js
│   │   │   │   ├── 3979.spec.js
│   │   │   │   ├── 4027.spec.js
│   │   │   │   ├── 4066.spec.js
│   │   │   │   ├── 4078.spec.js
│   │   │   │   ├── 4088.spec.js
│   │   │   │   ├── 4094.spec.js
│   │   │   │   ├── 4095.spec.js
│   │   │   │   ├── 4102.spec.js
│   │   │   │   ├── 4107.spec.js
│   │   │   │   ├── 4121.spec.js
│   │   │   │   └── 4147.spec.js
│   │   │   ├── js.spec.js
│   │   │   ├── keyed-comprehension.spec.js
│   │   │   ├── navigation.spec.js
│   │   │   ├── portal.spec.js
│   │   │   ├── select.spec.js
│   │   │   ├── streams.spec.js
│   │   │   └── uploads.spec.js
│   │   └── utils.js
│   ├── phoenix_component/
│   │   ├── components_test.exs
│   │   ├── declarative_assigns_test.exs
│   │   ├── macro_component_integration_test.exs
│   │   ├── macro_component_test.exs
│   │   ├── pages/
│   │   │   ├── about_page.html.heex
│   │   │   ├── another_root/
│   │   │   │   ├── root.html.heex
│   │   │   │   └── root.text.eex
│   │   │   └── welcome_page.html.heex
│   │   ├── rendering_test.exs
│   │   └── verify_test.exs
│   ├── phoenix_component_test.exs
│   ├── phoenix_live_view/
│   │   ├── async_result_test.exs
│   │   ├── async_test.exs
│   │   ├── colocated_hook_test.exs
│   │   ├── colocated_js_test.exs
│   │   ├── controller_test.exs
│   │   ├── debug_test.exs
│   │   ├── diff_test.exs
│   │   ├── engine_test.exs
│   │   ├── heex_extension_test.exs
│   │   ├── hooks_test.exs
│   │   ├── html_engine_test.exs
│   │   ├── html_formatter_test.exs
│   │   ├── igniter/
│   │   │   └── upgrade_to_1_1_test.exs
│   │   ├── integrations/
│   │   │   ├── assign_async_test.exs
│   │   │   ├── assigns_test.exs
│   │   │   ├── collocated_test.exs
│   │   │   ├── connect_test.exs
│   │   │   ├── elements_test.exs
│   │   │   ├── event_test.exs
│   │   │   ├── expensive_runtime_checks_test.exs
│   │   │   ├── flash_test.exs
│   │   │   ├── hooks_test.exs
│   │   │   ├── html_formatter_test.exs
│   │   │   ├── layout_test.exs
│   │   │   ├── live_components_test.exs
│   │   │   ├── live_reload_test.exs
│   │   │   ├── live_view_test.exs
│   │   │   ├── live_view_test_warnings_test.exs
│   │   │   ├── navigation_test.exs
│   │   │   ├── nested_test.exs
│   │   │   ├── params_test.exs
│   │   │   ├── start_async_test.exs
│   │   │   ├── stream_async_test.exs
│   │   │   ├── stream_test.exs
│   │   │   ├── telemetry_test.exs
│   │   │   └── update_test.exs
│   │   ├── js_test.exs
│   │   ├── live_stream_test.exs
│   │   ├── plug_test.exs
│   │   ├── router_test.exs
│   │   ├── socket_test.exs
│   │   ├── tag_engine/
│   │   │   └── tokenizer_test.exs
│   │   ├── test/
│   │   │   ├── diff_test.exs
│   │   │   ├── dom_test.exs
│   │   │   └── tree_dom_test.exs
│   │   ├── upload/
│   │   │   ├── channel_test.exs
│   │   │   ├── config_test.exs
│   │   │   └── external_test.exs
│   │   └── utils_test.exs
│   ├── phoenix_live_view_test.exs
│   ├── support/
│   │   ├── async_sync.ex
│   │   ├── controller.ex
│   │   ├── endpoint.ex
│   │   ├── layout_view.ex
│   │   ├── live_views/
│   │   │   ├── assign_async.ex
│   │   │   ├── cids_destroyed.ex
│   │   │   ├── collocated.ex
│   │   │   ├── collocated_component.html.heex
│   │   │   ├── collocated_live.html.heex
│   │   │   ├── component_and_nested_in_live.ex
│   │   │   ├── component_in_live.ex
│   │   │   ├── components.ex
│   │   │   ├── connect.ex
│   │   │   ├── debug_anno.exs
│   │   │   ├── debug_anno_opt_out.exs
│   │   │   ├── elements.ex
│   │   │   ├── events.ex
│   │   │   ├── expensive_runtime_checks.ex
│   │   │   ├── flash.ex
│   │   │   ├── general.ex
│   │   │   ├── host.ex
│   │   │   ├── layout.ex
│   │   │   ├── lifecycle.ex
│   │   │   ├── live_in_component.ex
│   │   │   ├── params.ex
│   │   │   ├── reload_live.ex
│   │   │   ├── render_with.ex
│   │   │   ├── root_tag_attr.exs
│   │   │   ├── start_async.ex
│   │   │   ├── stream_async.ex
│   │   │   ├── streams.ex
│   │   │   ├── test_warnings.ex
│   │   │   ├── update.ex
│   │   │   └── upload_live.ex
│   │   ├── router.ex
│   │   ├── telemetry_test_helpers.ex
│   │   └── templates/
│   │       ├── heex/
│   │       │   ├── dead_with_function_component.html.heex
│   │       │   ├── dead_with_function_component_with_inner_content.html.heex
│   │       │   ├── dead_with_live.html.eex
│   │       │   ├── inner_dead.html.eex
│   │       │   ├── inner_live.html.heex
│   │       │   ├── live_with_comprehension.html.heex
│   │       │   ├── live_with_dead.html.heex
│   │       │   └── live_with_live.html.heex
│   │       └── leex/
│   │           ├── dead_with_live.html.eex
│   │           ├── inner_dead.html.eex
│   │           ├── inner_live.html.leex
│   │           ├── live_with_comprehension.html.leex
│   │           ├── live_with_dead.html.leex
│   │           └── live_with_live.html.leex
│   └── test_helper.exs
└── tsconfig.json
Download .txt
Showing preview only (430K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (5307 symbols across 231 files)

FILE: .github/single-file-samples/main.exs
  class Example.ErrorView (line 30) | defmodule Example.ErrorView
    method render (line 31) | def render(template, _), do: Phoenix.Controller.status_message_from_te...
  class Example.HomeLive (line 34) | defmodule Example.HomeLive
    method mount (line 37) | def mount(_params, _session, socket) do
    method render (line 41) | def render("live.html", assigns) do
    method render (line 62) | def render(assigns) do
    method handle_event (line 70) | def handle_event("inc", _params, socket) do
    method handle_event (line 74) | def handle_event("dec", _params, socket) do
  class Example.Router (line 79) | defmodule Example.Router
  class Example.Endpoint (line 94) | defmodule Example.Endpoint

FILE: .github/single-file-samples/test.exs
  class Example.ErrorView (line 23) | defmodule Example.ErrorView
    method render (line 24) | def render(template, _), do: Phoenix.Controller.status_message_from_te...
  class Example.HomeLive (line 27) | defmodule Example.HomeLive
    method mount (line 30) | def mount(_params, _session, socket) do
    method render (line 35) | def render("live.html", assigns) do
    method render (line 54) | def render(assigns) do
  class Example.Router (line 61) | defmodule Example.Router
  class Example.Endpoint (line 76) | defmodule Example.Endpoint
  class Example.HomeLiveTest (line 85) | defmodule Example.HomeLiveTest

FILE: assets/js/phoenix_live_view/aria.js
  constant ARIA (line 1) | const ARIA = {
  method anyOf (line 2) | anyOf(instance, classes) {
  method isFocusable (line 6) | isFocusable(el, interactiveOnly) {
  method attemptFocus (line 25) | attemptFocus(el, interactiveOnly) {
  method focusFirstInteractive (line 36) | focusFirstInteractive(el) {
  method focusFirst (line 46) | focusFirst(el) {
  method focusLast (line 56) | focusLast(el) {

FILE: assets/js/phoenix_live_view/browser.js
  method canPushState (line 2) | canPushState() {
  method dropLocal (line 6) | dropLocal(localStorage, namespace, subkey) {
  method updateLocal (line 10) | updateLocal(localStorage, namespace, subkey, initial, func) {
  method getLocal (line 18) | getLocal(localStorage, namespace, subkey) {
  method updateCurrentState (line 22) | updateCurrentState(callback) {
  method pushState (line 33) | pushState(kind, meta, to) {
  method setCookie (line 65) | setCookie(name, value, maxAgeSeconds) {
  method getCookie (line 71) | getCookie(name) {
  method deleteCookie (line 78) | deleteCookie(name) {
  method redirect (line 82) | redirect(
  method localKey (line 95) | localKey(namespace, subkey) {
  method getHashTargetEl (line 99) | getHashTargetEl(maybeHash) {

FILE: assets/js/phoenix_live_view/constants.js
  constant CONSECUTIVE_RELOADS (line 1) | const CONSECUTIVE_RELOADS = "consecutive-reloads";
  constant MAX_RELOADS (line 2) | const MAX_RELOADS = 10;
  constant RELOAD_JITTER_MIN (line 3) | const RELOAD_JITTER_MIN = 5000;
  constant RELOAD_JITTER_MAX (line 4) | const RELOAD_JITTER_MAX = 10000;
  constant FAILSAFE_JITTER (line 5) | const FAILSAFE_JITTER = 30000;
  constant PHX_EVENT_CLASSES (line 6) | const PHX_EVENT_CLASSES = [
  constant PHX_DROP_TARGET_ACTIVE_CLASS (line 16) | const PHX_DROP_TARGET_ACTIVE_CLASS = "phx-drop-target-active";
  constant PHX_COMPONENT (line 17) | const PHX_COMPONENT = "data-phx-component";
  constant PHX_VIEW_REF (line 18) | const PHX_VIEW_REF = "data-phx-view";
  constant PHX_LIVE_LINK (line 19) | const PHX_LIVE_LINK = "data-phx-link";
  constant PHX_TRACK_STATIC (line 20) | const PHX_TRACK_STATIC = "track-static";
  constant PHX_LINK_STATE (line 21) | const PHX_LINK_STATE = "data-phx-link-state";
  constant PHX_REF_LOADING (line 22) | const PHX_REF_LOADING = "data-phx-ref-loading";
  constant PHX_REF_SRC (line 23) | const PHX_REF_SRC = "data-phx-ref-src";
  constant PHX_REF_LOCK (line 24) | const PHX_REF_LOCK = "data-phx-ref-lock";
  constant PHX_PENDING_REFS (line 25) | const PHX_PENDING_REFS = "phx-pending-refs";
  constant PHX_TRACK_UPLOADS (line 26) | const PHX_TRACK_UPLOADS = "track-uploads";
  constant PHX_UPLOAD_REF (line 27) | const PHX_UPLOAD_REF = "data-phx-upload-ref";
  constant PHX_PREFLIGHTED_REFS (line 28) | const PHX_PREFLIGHTED_REFS = "data-phx-preflighted-refs";
  constant PHX_DONE_REFS (line 29) | const PHX_DONE_REFS = "data-phx-done-refs";
  constant PHX_DROP_TARGET (line 30) | const PHX_DROP_TARGET = "drop-target";
  constant PHX_ACTIVE_ENTRY_REFS (line 31) | const PHX_ACTIVE_ENTRY_REFS = "data-phx-active-refs";
  constant PHX_LIVE_FILE_UPDATED (line 32) | const PHX_LIVE_FILE_UPDATED = "phx:live-file:updated";
  constant PHX_SKIP (line 33) | const PHX_SKIP = "data-phx-skip";
  constant PHX_MAGIC_ID (line 34) | const PHX_MAGIC_ID = "data-phx-id";
  constant PHX_PRUNE (line 35) | const PHX_PRUNE = "data-phx-prune";
  constant PHX_CONNECTED_CLASS (line 36) | const PHX_CONNECTED_CLASS = "phx-connected";
  constant PHX_LOADING_CLASS (line 37) | const PHX_LOADING_CLASS = "phx-loading";
  constant PHX_ERROR_CLASS (line 38) | const PHX_ERROR_CLASS = "phx-error";
  constant PHX_CLIENT_ERROR_CLASS (line 39) | const PHX_CLIENT_ERROR_CLASS = "phx-client-error";
  constant PHX_SERVER_ERROR_CLASS (line 40) | const PHX_SERVER_ERROR_CLASS = "phx-server-error";
  constant PHX_PARENT_ID (line 41) | const PHX_PARENT_ID = "data-phx-parent-id";
  constant PHX_MAIN (line 42) | const PHX_MAIN = "data-phx-main";
  constant PHX_ROOT_ID (line 43) | const PHX_ROOT_ID = "data-phx-root-id";
  constant PHX_VIEWPORT_TOP (line 44) | const PHX_VIEWPORT_TOP = "viewport-top";
  constant PHX_VIEWPORT_BOTTOM (line 45) | const PHX_VIEWPORT_BOTTOM = "viewport-bottom";
  constant PHX_VIEWPORT_OVERRUN_TARGET (line 46) | const PHX_VIEWPORT_OVERRUN_TARGET = "viewport-overrun-target";
  constant PHX_TRIGGER_ACTION (line 47) | const PHX_TRIGGER_ACTION = "trigger-action";
  constant PHX_HAS_FOCUSED (line 48) | const PHX_HAS_FOCUSED = "phx-has-focused";
  constant FOCUSABLE_INPUTS (line 49) | const FOCUSABLE_INPUTS = [
  constant CHECKABLE_INPUTS (line 64) | const CHECKABLE_INPUTS = ["checkbox", "radio"];
  constant PHX_HAS_SUBMITTED (line 65) | const PHX_HAS_SUBMITTED = "phx-has-submitted";
  constant PHX_SESSION (line 66) | const PHX_SESSION = "data-phx-session";
  constant PHX_VIEW_SELECTOR (line 67) | const PHX_VIEW_SELECTOR = `[${PHX_SESSION}]`;
  constant PHX_STICKY (line 68) | const PHX_STICKY = "data-phx-sticky";
  constant PHX_STATIC (line 69) | const PHX_STATIC = "data-phx-static";
  constant PHX_READONLY (line 70) | const PHX_READONLY = "data-phx-readonly";
  constant PHX_DISABLED (line 71) | const PHX_DISABLED = "data-phx-disabled";
  constant PHX_DISABLE_WITH (line 72) | const PHX_DISABLE_WITH = "disable-with";
  constant PHX_DISABLE_WITH_RESTORE (line 73) | const PHX_DISABLE_WITH_RESTORE = "data-phx-disable-with-restore";
  constant PHX_HOOK (line 74) | const PHX_HOOK = "hook";
  constant PHX_DEBOUNCE (line 75) | const PHX_DEBOUNCE = "debounce";
  constant PHX_THROTTLE (line 76) | const PHX_THROTTLE = "throttle";
  constant PHX_UPDATE (line 77) | const PHX_UPDATE = "update";
  constant PHX_STREAM (line 78) | const PHX_STREAM = "stream";
  constant PHX_STREAM_REF (line 79) | const PHX_STREAM_REF = "data-phx-stream";
  constant PHX_PORTAL (line 80) | const PHX_PORTAL = "data-phx-portal";
  constant PHX_TELEPORTED_REF (line 81) | const PHX_TELEPORTED_REF = "data-phx-teleported";
  constant PHX_TELEPORTED_SRC (line 82) | const PHX_TELEPORTED_SRC = "data-phx-teleported-src";
  constant PHX_RUNTIME_HOOK (line 83) | const PHX_RUNTIME_HOOK = "data-phx-runtime-hook";
  constant PHX_LV_PID (line 84) | const PHX_LV_PID = "data-phx-pid";
  constant PHX_KEY (line 85) | const PHX_KEY = "key";
  constant PHX_PRIVATE (line 86) | const PHX_PRIVATE = "phxPrivate";
  constant PHX_AUTO_RECOVER (line 87) | const PHX_AUTO_RECOVER = "auto-recover";
  constant PHX_NO_UNUSED_FIELD (line 88) | const PHX_NO_UNUSED_FIELD = "no-unused-field";
  constant PHX_LV_DEBUG (line 89) | const PHX_LV_DEBUG = "phx:live-socket:debug";
  constant PHX_LV_PROFILE (line 90) | const PHX_LV_PROFILE = "phx:live-socket:profiling";
  constant PHX_LV_LATENCY_SIM (line 91) | const PHX_LV_LATENCY_SIM = "phx:live-socket:latency-sim";
  constant PHX_LV_HISTORY_POSITION (line 92) | const PHX_LV_HISTORY_POSITION = "phx:nav-history-position";
  constant PHX_PROGRESS (line 93) | const PHX_PROGRESS = "progress";
  constant PHX_MOUNTED (line 94) | const PHX_MOUNTED = "mounted";
  constant PHX_RELOAD_STATUS (line 95) | const PHX_RELOAD_STATUS = "__phoenix_reload_status__";
  constant LOADER_TIMEOUT (line 96) | const LOADER_TIMEOUT = 1;
  constant MAX_CHILD_JOIN_ATTEMPTS (line 97) | const MAX_CHILD_JOIN_ATTEMPTS = 3;
  constant BEFORE_UNLOAD_LOADER_TIMEOUT (line 98) | const BEFORE_UNLOAD_LOADER_TIMEOUT = 200;
  constant DISCONNECTED_TIMEOUT (line 99) | const DISCONNECTED_TIMEOUT = 500;
  constant BINDING_PREFIX (line 100) | const BINDING_PREFIX = "phx-";
  constant PUSH_TIMEOUT (line 101) | const PUSH_TIMEOUT = 30000;
  constant LINK_HEADER (line 102) | const LINK_HEADER = "x-requested-with";
  constant RESPONSE_URL_HEADER (line 103) | const RESPONSE_URL_HEADER = "x-response-url";
  constant DEBOUNCE_TRIGGER (line 104) | const DEBOUNCE_TRIGGER = "debounce-trigger";
  constant THROTTLED (line 105) | const THROTTLED = "throttled";
  constant DEBOUNCE_PREV_KEY (line 106) | const DEBOUNCE_PREV_KEY = "debounce-prev-key";
  constant DEFAULTS (line 107) | const DEFAULTS = {
  constant PHX_PENDING_ATTRS (line 111) | const PHX_PENDING_ATTRS = [PHX_REF_LOADING, PHX_REF_SRC, PHX_REF_LOCK];
  constant STATIC (line 113) | const STATIC = "s";
  constant ROOT (line 114) | const ROOT = "r";
  constant COMPONENTS (line 115) | const COMPONENTS = "c";
  constant KEYED (line 116) | const KEYED = "k";
  constant KEYED_COUNT (line 117) | const KEYED_COUNT = "kc";
  constant EVENTS (line 118) | const EVENTS = "e";
  constant REPLY (line 119) | const REPLY = "r";
  constant TITLE (line 120) | const TITLE = "t";
  constant TEMPLATES (line 121) | const TEMPLATES = "p";
  constant STREAM (line 122) | const STREAM = "stream";

FILE: assets/js/phoenix_live_view/dom.js
  constant DOM (line 31) | const DOM = {
  method byId (line 32) | byId(id) {
  method removeClass (line 36) | removeClass(el, className) {
  method all (line 43) | all(node, query, callback) {
  method childNodeLength (line 54) | childNodeLength(html) {
  method isUploadInput (line 60) | isUploadInput(el) {
  method isAutoUpload (line 64) | isAutoUpload(inputEl) {
  method findUploadInputs (line 68) | findUploadInputs(node) {
  method findComponentNodeList (line 79) | findComponentNodeList(viewId, cid, doc = document) {
  method isPhxDestroyed (line 86) | isPhxDestroyed(node) {
  method wantsNewTab (line 90) | wantsNewTab(e) {
  method isUnloadableFormSubmit (line 105) | isUnloadableFormSubmit(e) {
  method isNewPageClick (line 119) | isNewPageClick(e, currentLocation) {
  method markPhxChildDestroyed (line 161) | markPhxChildDestroyed(el) {
  method findPhxChildrenInFragment (line 168) | findPhxChildrenInFragment(html, parentId) {
  method isIgnored (line 174) | isIgnored(el, phxUpdate) {
  method isPhxUpdate (line 181) | isPhxUpdate(el, phxUpdate, updateTypes) {
  method findPhxSticky (line 187) | findPhxSticky(el) {
  method findPhxChildren (line 191) | findPhxChildren(el, parentId) {
  method findExistingParentCIDs (line 195) | findExistingParentCIDs(viewId, cids) {
  method private (line 221) | private(el, key) {
  method deletePrivate (line 225) | deletePrivate(el, key) {
  method putPrivate (line 229) | putPrivate(el, key, value) {
  method updatePrivate (line 236) | updatePrivate(el, key, defaultVal, updateFunc) {
  method syncPendingAttrs (line 245) | syncPendingAttrs(fromEl, toEl) {
  method copyPrivates (line 259) | copyPrivates(target, source) {
  method putTitle (line 265) | putTitle(str) {
  method debounce (line 281) | debounce(
  method triggerCycle (line 379) | triggerCycle(el, key, currentCycle) {
  method once (line 390) | once(el, key) {
  method incCycle (line 398) | incCycle(el, key, trigger = function () {}) {
  method maintainPrivateHooks (line 408) | maintainPrivateHooks(fromEl, toEl, phxViewportTop, phxViewportBottom) {
  method putCustomElHook (line 427) | putCustomElHook(el, hook) {
  method getCustomElHook (line 439) | getCustomElHook(el) {
  method isUsedInput (line 443) | isUsedInput(el) {
  method resetForm (line 450) | resetForm(form) {
  method isPhxChild (line 457) | isPhxChild(node) {
  method isPhxSticky (line 461) | isPhxSticky(node) {
  method isChildOfAny (line 465) | isChildOfAny(el, parents) {
  method firstPhxChild (line 469) | firstPhxChild(el) {
  method isPortalTemplate (line 473) | isPortalTemplate(el) {
  method closestViewEl (line 477) | closestViewEl(el) {
  method dispatchEvent (line 494) | dispatchEvent(target, name, opts = {}) {
  method cloneNode (line 514) | cloneNode(node, html) {
  method mergeAttrs (line 527) | mergeAttrs(target, source, opts = {}) {
  method mergeFocusedInput (line 578) | mergeFocusedInput(target, source) {
  method hasSelectionRange (line 591) | hasSelectionRange(el) {
  method restoreFocus (line 597) | restoreFocus(focused, selectionStart, selectionEnd) {
  method isFormInput (line 614) | isFormInput(el) {
  method syncAttrsToProps (line 632) | syncAttrsToProps(el) {
  method isTextualInput (line 641) | isTextualInput(el) {
  method isNowTriggerFormExternal (line 645) | isNowTriggerFormExternal(el, phxTriggerExternal) {
  method cleanChildNodes (line 653) | cleanChildNodes(container, phxUpdate) {
  method replaceRootContainer (line 677) | replaceRootContainer(container, tagName, attrs) {
  method getSticky (line 709) | getSticky(el, name, defaultVal) {
  method deleteSticky (line 721) | deleteSticky(el, name) {
  method putSticky (line 727) | putSticky(el, name, op) {
  method applyStickyOperations (line 742) | applyStickyOperations(el) {
  method isLocked (line 751) | isLocked(el) {
  method attributeIgnored (line 755) | attributeIgnored(attribute, ignoredAttributes) {

FILE: assets/js/phoenix_live_view/dom_patch.js
  class DOMPatch (line 29) | class DOMPatch {
    method constructor (line 30) | constructor(view, container, id, html, streams, targetCID, opts = {}) {
    method before (line 61) | before(kind, callback) {
    method after (line 64) | after(kind, callback) {
    method trackBefore (line 68) | trackBefore(kind, ...args) {
    method trackAfter (line 72) | trackAfter(kind, ...args) {
    method markPrunableContentForRemoval (line 76) | markPrunableContentForRemoval() {
    method perform (line 87) | perform(isJoinPatch) {
    method onNodeDiscarded (line 579) | onNodeDiscarded(el) {
    method maybePendingRemove (line 587) | maybePendingRemove(node) {
    method removeStreamChildElement (line 596) | removeStreamChildElement(child, force = false) {
    method getStreamInsert (line 618) | getStreamInsert(el) {
    method setStreamRef (line 623) | setStreamRef(el, ref) {
    method maybeReOrderStream (line 629) | maybeReOrderStream(el, isNew) {
    method maybeLimitStream (line 671) | maybeLimitStream(el) {
    method transitionPendingRemoves (line 685) | transitionPendingRemoves() {
    method isChangedSelect (line 701) | isChangedSelect(fromEl, toEl) {
    method isCIDPatch (line 717) | isCIDPatch() {
    method skipCIDSibling (line 721) | skipCIDSibling(el) {
    method targetCIDContainer (line 725) | targetCIDContainer(html) {
    method indexOf (line 740) | indexOf(parent, child) {
    method teleport (line 744) | teleport(el, morph) {
    method handleRuntimeHook (line 794) | handleRuntimeHook(el, source) {

FILE: assets/js/phoenix_live_view/dom_post_morph_restorer.js
  class DOMPostMorphRestorer (line 5) | class DOMPostMorphRestorer {
    method constructor (line 6) | constructor(containerBefore, containerAfter, updateType) {
    method perform (line 41) | perform() {

FILE: assets/js/phoenix_live_view/element_ref.js
  class ElementRef (line 14) | class ElementRef {
    method onUnlock (line 15) | static onUnlock(el, callback) {
    method constructor (line 32) | constructor(el) {
    method maybeUndo (line 44) | maybeUndo(ref, phxEvent, eachCloneCallback) {
    method isWithin (line 92) | isWithin(ref) {
    method undoLocks (line 107) | undoLocks(ref, phxEvent, eachCloneCallback) {
    method undoLoading (line 129) | undoLoading(ref, phxEvent) {
    method isLoadingUndoneBy (line 178) | isLoadingUndoneBy(ref) {
    method isLockUndoneBy (line 181) | isLockUndoneBy(ref) {
    method isFullyResolvedBy (line 185) | isFullyResolvedBy(ref) {
    method canUndoLoading (line 193) | canUndoLoading(ref) {

FILE: assets/js/phoenix_live_view/entry_uploader.js
  class EntryUploader (line 3) | class EntryUploader {
    method constructor (line 4) | constructor(entry, config, liveSocket) {
    method error (line 18) | error(reason) {
    method upload (line 28) | upload() {
    method isDone (line 36) | isDone() {
    method readNextChunk (line 40) | readNextChunk() {
    method pushChunk (line 57) | pushChunk(chunk) {

FILE: assets/js/phoenix_live_view/hooks.js
  method activeRefs (line 14) | activeRefs() {
  method preflightedRefs (line 18) | preflightedRefs() {
  method mounted (line 22) | mounted() {
  method updated (line 27) | updated() {
  method mounted (line 44) | mounted() {
  method destroyed (line 54) | destroyed() {
  method mounted (line 59) | mounted() {
  method mounted (line 158) | mounted() {
  method destroyed (line 267) | destroyed() {
  method throttle (line 275) | throttle(interval, callback) {
  method findOverrunTarget (line 300) | findOverrunTarget() {

FILE: assets/js/phoenix_live_view/index.ts
  type LiveSocketOptions (line 22) | interface LiveSocketOptions {
  type LiveSocketInstanceInterface (line 195) | interface LiveSocketInstanceInterface {
  type LiveSocketConstructor (line 286) | interface LiveSocketConstructor {
  function createHook (line 335) | function createHook(el: HTMLElement, callbacks: Hook): ViewHook {

FILE: assets/js/phoenix_live_view/js.js
  method exec (line 9) | exec(e, eventType, phxEvent, view, sourceEl, defaults) {
  method isVisible (line 32) | isVisible(el) {
  method isInViewport (line 41) | isInViewport(el) {
  method exec_exec (line 60) | exec_exec(e, eventType, phxEvent, view, sourceEl, el, { attr, to }) {
  method exec_dispatch (line 68) | exec_dispatch(
  method exec_push (line 88) | exec_push(e, eventType, phxEvent, view, sourceEl, el, args) {
  method exec_navigate (line 160) | exec_navigate(e, eventType, phxEvent, view, sourceEl, el, { href, replac...
  method exec_patch (line 170) | exec_patch(e, eventType, phxEvent, view, sourceEl, el, { href, replace }) {
  method exec_focus (line 179) | exec_focus(e, eventType, phxEvent, view, sourceEl, el) {
  method exec_focus_first (line 189) | exec_focus_first(e, eventType, phxEvent, view, sourceEl, el) {
  method exec_push_focus (line 199) | exec_push_focus(e, eventType, phxEvent, view, sourceEl, el) {
  method exec_pop_focus (line 203) | exec_pop_focus(_e, _eventType, _phxEvent, _view, _sourceEl, _el) {
  method exec_add_class (line 214) | exec_add_class(
  method exec_remove_class (line 226) | exec_remove_class(
  method exec_toggle_class (line 238) | exec_toggle_class(
  method exec_toggle_attr (line 250) | exec_toggle_attr(
  method exec_ignore_attrs (line 262) | exec_ignore_attrs(e, eventType, phxEvent, view, sourceEl, el, { attrs }) {
  method exec_transition (line 266) | exec_transition(
  method exec_toggle (line 278) | exec_toggle(
  method exec_show (line 290) | exec_show(
  method exec_hide (line 302) | exec_hide(
  method exec_set_attr (line 314) | exec_set_attr(
  method exec_remove_attr (line 326) | exec_remove_attr(e, eventType, phxEvent, view, sourceEl, el, { attr }) {
  method ignoreAttrs (line 330) | ignoreAttrs(el, attrs) {
  method onBeforeElUpdated (line 353) | onBeforeElUpdated(fromEl, toEl) {
  method show (line 362) | show(eventType, view, el, display, transition, time, blocking) {
  method hide (line 377) | hide(eventType, view, el, display, transition, time, blocking) {
  method toggle (line 392) | toggle(eventType, view, el, display, ins, outs, time, blocking) {
  method toggleClasses (line 494) | toggleClasses(el, classes, transition, time, view, blocking) {
  method toggleAttr (line 515) | toggleAttr(el, attr, val1, val2) {
  method addOrRemoveClasses (line 533) | addOrRemoveClasses(el, adds, removes, transition, time, view, blocking) {
  method setOrRemoveAttrs (line 592) | setOrRemoveAttrs(el, sets, removes) {
  method hasAllClasses (line 610) | hasAllClasses(el, classes) {
  method isToggledOut (line 614) | isToggledOut(el, outClasses) {
  method filterToEls (line 618) | filterToEls(liveSocket, sourceEl, { to }) {
  method defaultDisplay (line 634) | defaultDisplay(el) {
  method transitionClasses (line 640) | transitionClasses(val) {

FILE: assets/js/phoenix_live_view/js_commands.ts
  type EncodedJS (line 11) | type EncodedJS = string | Array<any>;
  type Transition (line 13) | type Transition = string | string[];
  type BaseOpts (line 16) | type BaseOpts = {
  type ShowOpts (line 29) | type ShowOpts = BaseOpts & {
  type ToggleOpts (line 34) | type ToggleOpts = {
  type TransitionCommandOpts (line 60) | type TransitionCommandOpts = {
  type PushOpts (line 67) | type PushOpts = {
  type NavigationOpts (line 77) | type NavigationOpts = {
  type AllJSCommands (line 86) | interface AllJSCommands {
  method exec (line 248) | exec(el, encodedJS) {
  method show (line 251) | show(el, opts = {}) {
  method hide (line 263) | hide(el, opts = {}) {
  method toggle (line 275) | toggle(el, opts = {}) {
  method addClass (line 290) | addClass(el, names, opts = {}) {
  method removeClass (line 303) | removeClass(el, names, opts = {}) {
  method toggleClass (line 316) | toggleClass(el, names, opts = {}) {
  method transition (line 328) | transition(el, transition, opts = {}) {
  method setAttribute (line 340) | setAttribute(el, attr, val) {
  method removeAttribute (line 343) | removeAttribute(el, attr) {
  method toggleAttribute (line 346) | toggleAttribute(el, attr, val1, val2) {
  method push (line 349) | push(el, type, opts = {}) {
  method navigate (line 357) | navigate(href, opts = {}) {
  method patch (line 367) | patch(href, opts = {}) {
  method ignoreAttributes (line 376) | ignoreAttributes(el, attrs) {
  type LiveSocketJSCommands (line 386) | type LiveSocketJSCommands = AllJSCommands;
  type HookJSCommands (line 392) | interface HookJSCommands extends Omit<AllJSCommands, "exec"> {

FILE: assets/js/phoenix_live_view/live_socket.js
  class LiveSocket (line 55) | class LiveSocket {
    method constructor (line 56) | constructor(url, phxSocket, opts = {}) {
    method version (line 129) | version() {
    method isProfileEnabled (line 133) | isProfileEnabled() {
    method isDebugEnabled (line 137) | isDebugEnabled() {
    method isDebugDisabled (line 141) | isDebugDisabled() {
    method enableDebug (line 145) | enableDebug() {
    method enableProfiling (line 149) | enableProfiling() {
    method disableDebug (line 153) | disableDebug() {
    method disableProfiling (line 157) | disableProfiling() {
    method enableLatencySim (line 161) | enableLatencySim(upperBoundMs) {
    method disableLatencySim (line 169) | disableLatencySim() {
    method getLatencySim (line 173) | getLatencySim() {
    method getSocket (line 178) | getSocket() {
    method connect (line 182) | connect() {
    method disconnect (line 208) | disconnect(callback) {
    method replaceTransport (line 219) | replaceTransport(transport) {
    method execJS (line 230) | execJS(el, encodedJS, eventType = null) {
    method js (line 241) | js() {
    method unload (line 247) | unload() {
    method triggerDOM (line 259) | triggerDOM(kind, args) {
    method time (line 263) | time(name, func) {
    method log (line 273) | log(view, kind, msgCallback) {
    method requestDOMUpdate (line 283) | requestDOMUpdate(callback) {
    method asyncTransition (line 287) | asyncTransition(promise) {
    method transition (line 291) | transition(time, onStart, onDone = function () {}) {
    method onChannel (line 295) | onChannel(channel, event, cb) {
    method reloadWithJitter (line 306) | reloadWithJitter(view, log) {
    method getHookDefinition (line 346) | getHookDefinition(name) {
    method maybeInternalHook (line 357) | maybeInternalHook(name) {
    method maybeRuntimeHook (line 361) | maybeRuntimeHook(name) {
    method isUnloaded (line 386) | isUnloaded() {
    method isConnected (line 390) | isConnected() {
    method getBindingPrefix (line 394) | getBindingPrefix() {
    method binding (line 398) | binding(kind) {
    method channel (line 402) | channel(topic, params) {
    method joinDeadView (line 406) | joinDeadView() {
    method joinRootViews (line 427) | joinRootViews() {
    method redirect (line 451) | redirect(to, flash, reloadToken) {
    method replaceMain (line 459) | replaceMain(
    method transitionRemoves (line 496) | transitionRemoves(elements, callback) {
    method isPhxView (line 522) | isPhxView(el) {
    method newRootView (line 526) | newRootView(el, flash, liveReferer) {
    method owner (line 532) | owner(childEl, callback) {
    method withinOwners (line 550) | withinOwners(childEl, callback) {
    method getViewByEl (line 554) | getViewByEl(el) {
    method getRootById (line 561) | getRootById(id) {
    method destroyAllViews (line 565) | destroyAllViews() {
    method destroyViewByEl (line 573) | destroyViewByEl(el) {
    method getActiveElement (line 583) | getActiveElement() {
    method dropActiveElement (line 587) | dropActiveElement(view) {
    method restorePreviouslyActiveFocus (line 593) | restorePreviouslyActiveFocus() {
    method blurActiveElement (line 603) | blurActiveElement() {
    method bindTopLevelEvents (line 616) | bindTopLevelEvents({ dead } = {}) {
    method eventMeta (line 759) | eventMeta(eventName, e, targetEl) {
    method setPendingLink (line 764) | setPendingLink(href) {
    method resetReloadStatus (line 773) | resetReloadStatus() {
    method commitPendingLink (line 777) | commitPendingLink(linkRef) {
    method getHref (line 787) | getHref() {
    method hasPendingLink (line 791) | hasPendingLink() {
    method bind (line 795) | bind(events, callback) {
    method bindClicks (line 824) | bindClicks() {
    method bindClick (line 829) | bindClick(eventName, bindingName) {
    method dispatchClickAway (line 874) | dispatchClickAway(e, clickStartedAt) {
    method bindNav (line 914) | bindNav() {
    method maybeScroll (line 1017) | maybeScroll(scroll) {
    method dispatchEvent (line 1025) | dispatchEvent(event, payload = {}) {
    method dispatchEvents (line 1029) | dispatchEvents(events) {
    method withPageLoading (line 1033) | withPageLoading(info, callback) {
    method pushHistoryPatch (line 1040) | pushHistoryPatch(e, href, linkState, targetEl) {
    method historyPatch (line 1053) | historyPatch(href, linkState, linkRef = this.setPendingLink(href)) {
    method historyRedirect (line 1084) | historyRedirect(e, href, linkState, flash, targetEl) {
    method registerNewLocation (line 1141) | registerNewLocation(newLocation) {
    method bindForms (line 1151) | bindForms() {
    method debounce (line 1289) | debounce(el, event, eventType, callback) {
    method silenceEvents (line 1317) | silenceEvents(callback) {
    method on (line 1323) | on(event, callback) {
    method jsQuerySelectorAll (line 1332) | jsQuerySelectorAll(sourceEl, query, defaultQuery) {
  class TransitionSet (line 1338) | class TransitionSet {
    method constructor (line 1339) | constructor() {
    method reset (line 1345) | reset() {
    method after (line 1354) | after(callback) {
    method addTransition (line 1362) | addTransition(time, onStart, onDone) {
    method addAsyncTransition (line 1372) | addAsyncTransition(promise) {
    method pushPendingOp (line 1380) | pushPendingOp(op) {
    method size (line 1384) | size() {
    method flushPendingOps (line 1388) | flushPendingOps() {

FILE: assets/js/phoenix_live_view/live_uploader.js
  class LiveUploader (line 14) | class LiveUploader {
    method genFileRef (line 15) | static genFileRef(file) {
    method getEntryDataURL (line 25) | static getEntryDataURL(inputEl, ref, callback) {
    method hasUploadsInProgress (line 32) | static hasUploadsInProgress(formEl) {
    method serializeUploads (line 45) | static serializeUploads(inputEl) {
    method clearFiles (line 66) | static clearFiles(inputEl) {
    method untrackFile (line 72) | static untrackFile(inputEl, file) {
    method trackFiles (line 85) | static trackFiles(inputEl, files, dataTransfer) {
    method activeFileInputs (line 103) | static activeFileInputs(formEl) {
    method activeFiles (line 110) | static activeFiles(input) {
    method inputsAwaitingPreflight (line 116) | static inputsAwaitingPreflight(formEl) {
    method filesAwaitingPreflight (line 123) | static filesAwaitingPreflight(input) {
    method markPreflightInProgress (line 131) | static markPreflightInProgress(entries) {
    method constructor (line 135) | constructor(inputEl, view, onComplete) {
    method isAutoUpload (line 149) | isAutoUpload() {
    method entries (line 153) | entries() {
    method initAdapterUpload (line 157) | initAdapterUpload(resp, onError, liveSocket) {

FILE: assets/js/phoenix_live_view/rendered.js
  constant VOID_TAGS (line 20) | const VOID_TAGS = new Set([
  class Rendered (line 122) | class Rendered {
    method extract (line 123) | static extract(diff) {
    method constructor (line 131) | constructor(viewId, rendered) {
    method parentViewId (line 138) | parentViewId() {
    method toString (line 142) | toString(onlyCids) {
    method recursiveToString (line 153) | recursiveToString(
    method componentCIDs (line 171) | componentCIDs(diff) {
    method isComponentOnlyDiff (line 175) | isComponentOnlyDiff(diff) {
    method getComponent (line 182) | getComponent(diff, cid) {
    method resetRender (line 186) | resetRender(cid) {
    method mergeDiff (line 194) | mergeDiff(diff) {
    method cachedFindComponent (line 215) | cachedFindComponent(cid, cdiff, oldc, newc, cache) {
    method mutableMerge (line 247) | mutableMerge(target, source) {
    method doMutableMerge (line 256) | doMutableMerge(target, source) {
    method clone (line 276) | clone(diff) {
    method mergeKeyed (line 286) | mergeKeyed(target, source) {
    method cloneMerge (line 340) | cloneMerge(target, source, pruneMagicId) {
    method componentToString (line 366) | componentToString(cid) {
    method pruneCIDs (line 376) | pruneCIDs(cids) {
    method get (line 382) | get() {
    method isNewFingerprint (line 386) | isNewFingerprint(diff = {}) {
    method templateStatic (line 390) | templateStatic(part, templates) {
    method nextMagicID (line 398) | nextMagicID() {
    method toOutputBuffer (line 406) | toOutputBuffer(rendered, templates, output, changeTracking, rootAttrs ...
    method comprehensionToBuffer (line 479) | comprehensionToBuffer(rendered, templates, output, changeTracking) {
    method dynamicToBuffer (line 513) | dynamicToBuffer(rendered, templates, output, changeTracking) {
    method recursiveCIDToString (line 529) | recursiveCIDToString(components, cid, onlyCids) {

FILE: assets/js/phoenix_live_view/upload_entry.js
  class UploadEntry (line 11) | class UploadEntry {
    method isActive (line 12) | static isActive(fileEl, file) {
    method isPreflighted (line 19) | static isPreflighted(fileEl, file) {
    method isPreflightInProgress (line 28) | static isPreflightInProgress(file) {
    method markPreflightInProgress (line 32) | static markPreflightInProgress(file) {
    method constructor (line 36) | constructor(fileEl, file, view, autoUpload) {
    method metadata (line 52) | metadata() {
    method progress (line 56) | progress(progress) {
    method isCancelled (line 74) | isCancelled() {
    method cancel (line 78) | cancel() {
    method isDone (line 85) | isDone() {
    method error (line 89) | error(reason = "failed") {
    method isAutoUpload (line 97) | isAutoUpload() {
    method onDone (line 103) | onDone(callback) {
    method onElUpdated (line 110) | onElUpdated() {
    method toPreflightPayload (line 120) | toPreflightPayload() {
    method uploader (line 132) | uploader(uploaders) {
    method zipPostFlight (line 143) | zipPostFlight(resp) {

FILE: assets/js/phoenix_live_view/utils.js
  function detectDuplicateIds (line 12) | function detectDuplicateIds() {
  function detectInvalidStreamInserts (line 26) | function detectInvalidStreamInserts(inserts) {

FILE: assets/js/phoenix_live_view/view.js
  class View (line 81) | class View {
    method closestView (line 82) | static closestView(el) {
    method constructor (line 87) | constructor(el, liveSocket, parentView, flash, liveReferer) {
    method setHref (line 161) | setHref(href) {
    method setRedirect (line 165) | setRedirect(href) {
    method isMain (line 170) | isMain() {
    method connectParams (line 174) | connectParams(liveReferer) {
    method isConnected (line 191) | isConnected() {
    method getSession (line 195) | getSession() {
    method getStatic (line 199) | getStatic() {
    method destroy (line 204) | destroy(callback = function () {}) {
    method setContainerClasses (line 231) | setContainerClasses(...classes) {
    method showLoader (line 242) | showLoader(timeout) {
    method execAll (line 254) | execAll(binding) {
    method hideLoader (line 260) | hideLoader() {
    method triggerReconnected (line 267) | triggerReconnected() {
    method log (line 273) | log(kind, msgCallback) {
    method transition (line 277) | transition(time, onStart, onDone = function () {}) {
    method withinTargets (line 287) | withinTargets(phxTarget, callback, dom = document) {
    method applyDiff (line 317) | applyDiff(type, rawDiff, callback) {
    method onJoin (line 353) | onJoin(resp) {
    method dropPendingRefs (line 407) | dropPendingRefs() {
    method onJoinComplete (line 415) | onJoinComplete({ live_patch }, html, streams, events) {
    method attachTrueDocEl (line 461) | attachTrueDocEl() {
    method execNewMounted (line 470) | execNewMounted(parent = document) {
    method all (line 498) | all(parent, selector, callback) {
    method applyJoinPatch (line 506) | applyJoinPatch(live_patch, html, streams, events) {
    method triggerBeforeUpdateHook (line 537) | triggerBeforeUpdateHook(fromEl, toEl) {
    method maybeMounted (line 551) | maybeMounted(el) {
    method maybeAddNewHook (line 560) | maybeAddNewHook(el) {
    method performPatch (line 567) | performPatch(patch, pruneCids, isJoinPatch = false) {
    method afterElementsRemoved (line 624) | afterElementsRemoved(elements, pruneCids) {
    method joinNewChildren (line 658) | joinNewChildren() {
    method maybeRecoverForms (line 662) | maybeRecoverForms(html, callback) {
    method getChildById (line 738) | getChildById(id) {
    method getDescendentByEl (line 742) | getDescendentByEl(el) {
    method destroyDescendent (line 750) | destroyDescendent(id) {
    method joinChild (line 760) | joinChild(el) {
    method isJoinPending (line 771) | isJoinPending() {
    method ackJoin (line 775) | ackJoin(_child) {
    method onAllChildJoinsComplete (line 787) | onAllChildJoinsComplete() {
    method update (line 803) | update(diff, events, isPending = false) {
    method renderContainer (line 854) | renderContainer(diff, kind) {
    method componentPatch (line 865) | componentPatch(diff, cid) {
    method getHook (line 873) | getHook(el) {
    method addHook (line 877) | addHook(el) {
    method destroyHook (line 961) | destroyHook(hook) {
    method applyPendingUpdates (line 970) | applyPendingUpdates() {
    method eachChild (line 980) | eachChild(callback) {
    method onChannel (line 987) | onChannel(event, cb) {
    method bindChannel (line 1002) | bindChannel() {
    method destroyAllChildren (line 1021) | destroyAllChildren() {
    method onLiveRedirect (line 1025) | onLiveRedirect(redir) {
    method onLivePatch (line 1034) | onLivePatch(redir) {
    method expandURL (line 1040) | expandURL(to) {
    method onRedirect (line 1049) | onRedirect({ to, flash, reloadToken }) {
    method isDestroyed (line 1053) | isDestroyed() {
    method joinDead (line 1057) | joinDead() {
    method joinPush (line 1061) | joinPush() {
    method join (line 1066) | join(callback) {
    method onJoinError (line 1087) | onJoinError(resp) {
    method onClose (line 1152) | onClose(reason) {
    method onError (line 1170) | onError(reason) {
    method displayError (line 1190) | displayError(classes, details = {}) {
    method delayedDisconnected (line 1201) | delayedDisconnected() {
    method wrapPush (line 1207) | wrapPush(callerPush, receives) {
    method pushWithReply (line 1227) | pushWithReply(refGenerator, event, payload) {
    method undoRefs (line 1300) | undoRefs(ref, phxEvent, onlyEls) {
    method undoElRef (line 1323) | undoElRef(el, ref, phxEvent) {
    method refSrc (line 1342) | refSrc() {
    method putRef (line 1346) | putRef(elements, phxEvent, eventType, opts = {}) {
    method isAcked (line 1466) | isAcked(ref) {
    method componentID (line 1470) | componentID(el) {
    method targetComponentID (line 1475) | targetComponentID(target, targetCtx, opts = {}) {
    method closestComponentID (line 1491) | closestComponentID(targetCtx) {
    method pushHookEvent (line 1518) | pushHookEvent(el, targetCtx, event, payload) {
    method extractMeta (line 1544) | extractMeta(el, meta, value) {
    method serializeForm (line 1580) | serializeForm(form, opts, onlyNames = []) {
    method pushEvent (line 1681) | pushEvent(type, el, targetCtx, phxEvent, meta, opts = {}, onReply) {
    method pushFileProgress (line 1700) | pushFileProgress(fileEl, entryRef, progress, onReply = function () {}) {
    method pushInput (line 1715) | pushInput(inputEl, targetCtx, forceCid, phxEvent, opts, callback) {
    method triggerAwaitingSubmit (line 1803) | triggerAwaitingSubmit(formEl, phxEvent) {
    method getScheduledSubmit (line 1812) | getScheduledSubmit(formEl) {
    method scheduleSubmit (line 1818) | scheduleSubmit(formEl, ref, opts, callback) {
    method cancelSubmit (line 1825) | cancelSubmit(formEl, phxEvent) {
    method disableForm (line 1838) | disableForm(formEl, phxEvent, opts = {}) {
    method pushFormSubmit (line 1889) | pushFormSubmit(formEl, targetCtx, phxEvent, submitter, opts, onReply) {
    method uploadFiles (line 1954) | uploadFiles(formEl, phxEvent, targetCtx, ref, cid, onComplete) {
    method handleFailedEntryPreflight (line 2022) | handleFailedEntryPreflight(uploadRef, reason, uploader) {
    method dispatchUploads (line 2037) | dispatchUploads(targetCtx, name, filesOrBlobs) {
    method targetCtxElement (line 2053) | targetCtxElement(targetCtx) {
    method pushFormRecovery (line 2064) | pushFormRecovery(oldForm, newForm, templateDom, callback) {
    method pushLinkPatch (line 2121) | pushLinkPatch(e, href, targetEl, callback) {
    method getFormsForRecovery (line 2160) | getFormsForRecovery() {
    method maybePushComponentsDestroyed (line 2239) | maybePushComponentsDestroyed(destroyedCIDs) {
    method ownsElement (line 2281) | ownsElement(el) {
    method submitForm (line 2290) | submitForm(form, targetCtx, phxEvent, submitter, opts = {}) {
    method binding (line 2300) | binding(kind) {
    method pushPortalElementId (line 2305) | pushPortalElementId(id) {
    method dropPortalElementId (line 2309) | dropPortalElementId(id) {
    method destroyPortalElements (line 2313) | destroyPortalElements() {

FILE: assets/js/phoenix_live_view/view_hook.ts
  constant HOOK_ID (line 6) | const HOOK_ID = "hookId";
  constant DEAD_HOOK (line 7) | const DEAD_HOOK = "deadHook";
  type OnReply (line 10) | type OnReply = (reply: any, ref: number) => any;
  type CallbackRef (line 11) | type CallbackRef = { event: string; callback: (payload: any) => any };
  type PhxTarget (line 13) | type PhxTarget = string | number | HTMLElement;
  type HookInterface (line 15) | interface HookInterface<E extends HTMLElement = HTMLElement> {
  type Hook (line 163) | interface Hook<T = object, E extends HTMLElement = HTMLElement> {
  class ViewHook (line 240) | class ViewHook<E extends HTMLElement = HTMLElement>
    method liveSocket (line 250) | get liveSocket(): LiveSocket {
    method makeID (line 254) | static makeID() {
    method elementID (line 257) | static elementID(el: HTMLElement) {
    method deadHook (line 260) | static deadHook(el: HTMLElement) {
    method constructor (line 264) | constructor(view: View | null, el: E, callbacks?: Hook) {
    method __attachView (line 337) | __attachView(view: View | null) {
    method mounted (line 356) | mounted(): void {}
    method beforeUpdate (line 357) | beforeUpdate(): void {}
    method updated (line 358) | updated(): void {}
    method destroyed (line 359) | destroyed(): void {}
    method disconnected (line 360) | disconnected(): void {}
    method reconnected (line 361) | reconnected(): void {}
    method __mounted (line 366) | __mounted() {
    method __updated (line 370) | __updated() {
    method __beforeUpdate (line 374) | __beforeUpdate() {
    method __destroyed (line 378) | __destroyed() {
    method __reconnected (line 383) | __reconnected() {
    method __disconnected (line 390) | __disconnected() {
    method js (line 395) | js(): HookJSCommands {
    method pushEvent (line 406) | pushEvent(
    method pushEventTo (line 438) | pushEventTo(
    method handleEvent (line 470) | handleEvent(event: string, callback: (payload: any) => any): CallbackR...
    method removeHandleEvent (line 483) | removeHandleEvent(ref: CallbackRef): void {
    method upload (line 491) | upload(name: string, files: FileList): any {
    method uploadTo (line 495) | uploadTo(selectorOrTarget: PhxTarget, name: string, files: FileList): ...
    method __cleanup__ (line 505) | __cleanup__() {
  type HooksOptions (line 512) | type HooksOptions = Record<string, typeof ViewHook | Hook<any, any>>;

FILE: assets/test/browser_test.ts
  function clearCookies (line 54) | function clearCookies() {

FILE: assets/test/dom_test.ts
  class CustomFormInput (line 401) | class CustomFormInput extends HTMLElement {
    method constructor (line 404) | constructor() {
  class CustomNotFormInput (line 412) | class CustomNotFormInput extends HTMLElement {
    method constructor (line 413) | constructor() {

FILE: assets/test/event_test.ts
  method receive (line 28) | receive(kind, cb) {
  method receive (line 44) | receive(kind, cb) {
  method mounted (line 66) | mounted() {
  method mounted (line 92) | mounted() {
  method mounted (line 122) | mounted() {
  method destroyed (line 131) | destroyed() {
  method mounted (line 170) | mounted() {
  method mounted (line 208) | mounted() {
  method mounted (line 246) | mounted() {
  method mounted (line 282) | mounted() {
  method mounted (line 311) | mounted() {
  method mounted (line 356) | mounted() {

FILE: assets/test/hook_types_test.ts
  type CounterState (line 16) | interface CounterState {
  method increment (line 23) | increment() {
  method mounted (line 26) | mounted() {
  type CanvasState (line 32) | interface CanvasState {
  method mounted (line 38) | mounted() {
  type VideoState (line 45) | interface VideoState {
  method toggle (line 52) | toggle() {
  method mounted (line 60) | mounted() {
  type LinksInTab (line 80) | interface LinksInTab {
  method mounted (line 88) | mounted() {
  method page (line 105) | page() {
  method mounted (line 108) | mounted() {
  method updated (line 117) | updated() {

FILE: assets/test/integration/portal_test.ts
  function createViewWithPortal (line 8) | function createViewWithPortal(rootId = "root") {
  function createHtmlWithPortal (line 27) | function createHtmlWithPortal(id, targetId, content) {
  function performPatch (line 38) | function performPatch(view, container, htmlString) {

FILE: assets/test/test_helpers.ts
  method receive (line 65) | receive(kind: string, cb: Function) {
  method receive (line 72) | receive(kind, cb) {
  function liveViewDOM (line 81) | function liveViewDOM(content?: string) {

FILE: assets/test/view_test.ts
  method leave (line 133) | leave() {
  method push (line 140) | push(_evt, payload, _timeout) {
  method leave (line 164) | leave() {
  method push (line 171) | push(_evt, payload, _timeout) {
  method leave (line 205) | leave() {
  method push (line 212) | push(_evt, payload, _timeout) {
  method leave (line 237) | leave() {
  method push (line 244) | push(_evt, payload, _timeout) {
  method leave (line 269) | leave() {
  method push (line 276) | push(_evt, payload, _timeout) {
  method leave (line 302) | leave() {
  method push (line 309) | push(_evt, payload, _timeout) {
  method leave (line 332) | leave() {
  method push (line 339) | push(_evt, payload, _timeout) {
  method push (line 373) | push(_evt, payload, _timeout) {
  method leave (line 410) | leave() {
  method push (line 417) | push(_evt, payload, _timeout) {
  method push (line 486) | push(_evt, payload, _timeout) {
  method push (line 518) | push(_evt, payload, _timeout) {
  function submitWithButton (line 571) | function submitWithButton(
  method mounted (line 1340) | mounted() {
  method beforeUpdate (line 1344) | beforeUpdate() {
  method updated (line 1347) | updated() {
  method disconnected (line 1350) | disconnected() {
  method reconnected (line 1353) | reconnected() {
  method destroyed (line 1356) | destroyed() {
  method mounted (line 1404) | mounted() {
  method beforeUpdate (line 1408) | beforeUpdate() {
  method updated (line 1411) | updated() {
  method disconnected (line 1414) | disconnected() {
  method reconnected (line 1417) | reconnected() {
  method destroyed (line 1420) | destroyed() {
  method connectedCallback (line 1469) | connectedCallback() {
  method destroyed (line 1491) | destroyed() {
  method mounted (line 1519) | mounted() {
  method disconnected (line 1522) | disconnected() {
  method reconnected (line 1525) | reconnected() {
  method onBeforeElUpdated (line 1598) | onBeforeElUpdated(from, to) {
  method mounted (line 1623) | mounted() {
  method updated (line 1629) | updated() {
  function liveViewComponent (line 1661) | function liveViewComponent() {
  method leave (line 1716) | leave() {
  method push (line 1723) | push(_evt, payload, _timeout) {
  method nextValidate (line 1790) | nextValidate(payload, meta) {
  method push (line 1799) | push(_evt, payload, _timeout) {
  method beforeUpdate (line 2006) | beforeUpdate() {
  method updated (line 2009) | updated() {

FILE: lib/mix/tasks/compile/phoenix_live_view.ex
  class Mix.Tasks.Compile.PhoenixLiveView (line 1) | defmodule Mix.Tasks.Compile.PhoenixLiveView
    method run (line 18) | def run(_args) do
    method compile (line 31) | defp compile do

FILE: lib/phoenix_component.ex
  class Phoenix.Component (line 1) | defmodule Phoenix.Component
    method assigns_to_attributes (line 973) | def assigns_to_attributes(assigns, exclude \\ []) do
    method live_render (line 1062) | def live_render(conn_or_socket, view, opts \\ [])
    method live_render (line 1064) | def live_render(%Plug.Conn{} = conn, view, opts) do
    method live_render (line 1074) | def live_render(%Socket{} = parent, view, opts) do
    method __render_slot__ (line 1135) | def __render_slot__(_, [], _), do: nil
    method __render_slot__ (line 1137) | def __render_slot__(changed, [entry], argument) do
    method call_inner_block! (line 1156) | defp call_inner_block!(entry, changed, argument) do
    method live_flash (line 1179) | def live_flash(%_struct{} = other, _key) do
    method live_flash (line 1183) | def live_flash(%{} = flash, key), do: Map.get(flash, to_string(key))
    method upload_errors (line 1204) | def upload_errors(%Phoenix.LiveView.UploadConfig{} = conf) do
    method upload_errors (line 1236) | def upload_errors(
    method assign_new (line 1335) | def assign_new(socket_or_assigns, key, fun)
    method assign_new (line 1337) | def assign_new(%Socket{} = socket, key, fun) do
    method raise_bad_socket_or_assign! (line 1360) | defp raise_bad_socket_or_assign!(name, assigns) do
    method assign (line 1402) | def assign(socket_or_assigns, key, value)
    method assign (line 1404) | def assign(%Socket{} = socket, key, value) do
    method assign (line 1409) | def assign(%{__changed__: changed} = assigns, key, value) do
    method assign (line 1423) | def assign(assigns, _key, _val) do
    method validate_assign_key! (line 1460) | defp validate_assign_key!(:flash) do
    method validate_assign_key! (line 1473) | defp validate_assign_key!(key) do
    method update (line 1497) | def update(socket_or_assigns, key, fun)
    method changed? (line 1531) | def changed?(socket_or_assigns, key)
    method changed? (line 1533) | def changed?(%Socket{assigns: assigns}, key) do
    method changed? (line 1537) | def changed?(%{__changed__: _} = assigns, key) do
    method changed? (line 1541) | def changed?(assigns, _key) do
    method to_form (line 1622) | def to_form(data_or_params, options \\ [])
    method to_form (line 1624) | def to_form(%Phoenix.HTML.Form{} = data, []) do
    method to_form (line 1628) | def to_form(%Phoenix.HTML.Form{} = data, options) do
    method to_form (line 1653) | def to_form(data, options) do
    method used_input? (line 1742) | def used_input?(%Phoenix.HTML.FormField{field: field, form: form}) do
    method used_param? (line 1746) | defp used_param?(_params, "_unused_" <> _), do: false
    method used_param? (line 1748) | defp used_param?(params, field) do
    method __embed__ (line 1840) | def __embed__(path, suffix),
    method live_component (line 2183) | def live_component(assigns)
    method live_title (line 2247) | def live_title(assigns) do
    method render_present (line 2253) | defp render_present(rendered_block, default) do
    method form (line 2482) | def form(assigns) do
    method form_method (line 2549) | defp form_method(nil), do: {"post", nil}
    method inputs_for (line 2806) | def inputs_for(assigns) do
    method apply_persistent_id (line 2848) | defp apply_persistent_id(parent_form, forms, field_name, options) do
    method next_id (line 2884) | defp next_id(idx, %{} = seen_ids) do
    method name_for_value_or_values (line 2898) | defp name_for_value_or_values(form, field, _value) do
    method link (line 3132) | def link(%{} = assigns) do
    method csrf_token (line 3138) | defp csrf_token(true, href), do: Plug.CSRFProtection.get_csrf_token_fo...
    method csrf_token (line 3139) | defp csrf_token(false, _href), do: nil
    method focus_wrap (line 3171) | def focus_wrap(assigns) do
    method dynamic_tag (line 3223) | def dynamic_tag(%{rest: rest} = assigns) do
    method live_file_input (line 3333) | def live_file_input(%{upload: upload} = assigns) do
    method join_refs (line 3355) | defp join_refs(entries), do: Enum.join(entries, ",")
    method live_img_preview (line 3391) | def live_img_preview(assigns) do
    method intersperse (line 3434) | def intersperse(assigns) do
    method async_result (line 3506) | def async_result(%{assign: async_assign} = assigns) do
    method portal (line 3575) | def portal(assigns) do

FILE: lib/phoenix_component/declarative.ex
  class Phoenix.Component.Declarative (line 1) | defmodule Phoenix.Component.Declarative
    method __reserved__ (line 12) | def __reserved__, do: @reserved_assigns
    method __global__? (line 145) | def __global__?(_), do: false
    method annotate_def (line 163) | defp annotate_def(kind, expr) do
    method annotate_call (line 170) | defp annotate_call(kind, {name, meta, [{:\\, default_meta, [left, righ...
    method annotate_call (line 173) | defp annotate_call(kind, {name, meta, [arg]}),
    method annotate_call (line 176) | defp annotate_call(_kind, left),
    method annotate_arg (line 193) | defp annotate_arg(kind, arg) do
    method __setup__ (line 201) | def __setup__(module, opts) do
    method __slot__! (line 240) | def __slot__!(module, name, opts, line, file, block_fun) do
    method validate_slot! (line 281) | defp validate_slot!(module, slot, line, file) do
    method validate_attr_type! (line 420) | defp validate_attr_type!(_module, _key, slot, name, type, line, file) do
    method validate_tuple_attr_type! (line 429) | defp validate_tuple_attr_type!(slot, name, type, line, file) do
    method bad_type! (line 433) | defp bad_type!(slot, name, type, line, file) do
    method attr_slot (line 447) | defp attr_slot(name, nil), do: "#{inspect(name)}"
    method attr_slot (line 448) | defp attr_slot(name, slot), do: "#{inspect(name)} in slot #{inspect(sl...
    method validate_attr_default! (line 450) | defp validate_attr_default!(slot, name, type, opts, line, file) do
    method bad_default! (line 467) | defp bad_default!(slot, name, type, default, line, file) do
    method validate_attr_values! (line 474) | defp validate_attr_values!(slot, name, type, values, line, file) do
    method is_enumerable (line 486) | defp is_enumerable(values) do
    method bad_value! (line 490) | defp bad_value!(slot, name, type, value, line, file) do
    method validate_attr_examples! (line 497) | defp validate_attr_examples!(slot, name, type, examples, line, file) do
    method bad_example! (line 509) | defp bad_example!(slot, name, type, example, line, file) do
    method valid_value? (line 516) | defp valid_value?(_type, nil), do: true
    method valid_value? (line 517) | defp valid_value?(:any, _value), do: true
    method valid_value? (line 518) | defp valid_value?(:string, value), do: is_binary(value)
    method valid_value? (line 519) | defp valid_value?(:atom, value), do: is_atom(value)
    method valid_value? (line 520) | defp valid_value?(:boolean, value), do: is_boolean(value)
    method valid_value? (line 521) | defp valid_value?(:integer, value), do: is_integer(value)
    method valid_value? (line 522) | defp valid_value?(:float, value), do: is_float(value)
    method valid_value? (line 523) | defp valid_value?(:list, value), do: is_list(value)
    method valid_value? (line 524) | defp valid_value?({:struct, mod}, value), do: is_struct(value, mod)
    method valid_value? (line 525) | defp valid_value?(_type, _value), do: true
    method validate_attr_opts! (line 527) | defp validate_attr_opts!(slot, name, opts, line, file) do
    method invalid_attr_message (line 537) | defp invalid_attr_message(:include, other),
    method invalid_attr_message (line 540) | defp invalid_attr_message(:default, nil), do: nil
    method invalid_attr_message (line 542) | defp invalid_attr_message(:default, _),
    method invalid_attr_message (line 547) | defp invalid_attr_message(:required, _), do: nil
    method invalid_attr_message (line 548) | defp invalid_attr_message(:values, _), do: nil
    method invalid_attr_message (line 549) | defp invalid_attr_message(:examples, _), do: nil
    method invalid_attr_message (line 551) | defp invalid_attr_message(_key, nil),
    method invalid_attr_message (line 554) | defp invalid_attr_message(_key, _slot),
    method compile_error! (line 557) | defp compile_error!(line, file, msg) do
    method __on_definition__ (line 578) | def __on_definition__(env, kind, name, args, _guards, body) do
    method delete_context (line 748) | defp delete_context(node) do
    method register_component! (line 752) | defp register_component!(kind, env, name, check_if_defined?) do
    method register_component_doc (line 794) | defp register_component_doc(env, :def, slots, attrs) do
    method register_component_doc (line 807) | defp register_component_doc(_env, :defp, _slots, _attrs) do
    method build_component_doc (line 811) | defp build_component_doc(doc \\ "", slots, attrs) do
    method build_left_doc (line 821) | defp build_left_doc("") do
    method build_left_doc (line 825) | defp build_left_doc(left) do
    method build_component_docs (line 829) | defp build_component_docs(slots, attrs) do
    method build_slots_docs (line 845) | defp build_slots_docs(slots) do
    method build_attrs_docs (line 865) | defp build_attrs_docs(attrs) do
    method build_slot_name (line 887) | defp build_slot_name(%{name: name}) do
    method build_slot_doc (line 891) | defp build_slot_doc(%{doc: nil}, []) do
    method build_slot_doc (line 895) | defp build_slot_doc(%{doc: doc}, []) do
    method build_slot_doc (line 899) | defp build_slot_doc(%{doc: nil}, slot_attrs) do
    method build_slot_doc (line 903) | defp build_slot_doc(%{doc: doc}, slot_attrs) do
    method build_slot_attrs_docs (line 912) | defp build_slot_attrs_docs(slot_attrs) do
    method build_slot_required (line 926) | defp build_slot_required(%{required: true}) do
    method build_slot_required (line 930) | defp build_slot_required(_slot) do
    method build_attr_name (line 934) | defp build_attr_name(%{name: name}) do
    method build_attr_type (line 938) | defp build_attr_type(%{type: {:struct, type}}) do
    method build_attr_type (line 942) | defp build_attr_type(%{type: type}) do
    method build_attr_required (line 946) | defp build_attr_required(%{required: true}) do
    method build_attr_required (line 950) | defp build_attr_required(_attr) do
    method build_attr_doc_and_default (line 954) | defp build_attr_doc_and_default(%{doc: doc, type: :global, opts: opts}...
    method build_attr_doc_and_default (line 968) | defp build_attr_doc_and_default(%{doc: doc, opts: opts}, indent) do
    method build_doc (line 982) | defp build_doc(doc, indent, text_after?) do
    method build_attr_values_or_examples (line 1010) | defp build_attr_values_or_examples(%{opts: opts} = attr) do
    method build_literals_list (line 1025) | defp build_literals_list([literal], _condition) do
    method build_literals_list (line 1029) | defp build_literals_list(literals, condition) do
    method build_literal (line 1035) | defp build_literal(literal) do
    method build_hyphen (line 1043) | defp build_hyphen(%{opts: []}) do
    method build_hyphen (line 1047) | defp build_hyphen(%{opts: _opts}) do
    method build_right_doc (line 1051) | defp build_right_doc("") do
    method build_right_doc (line 1055) | defp build_right_doc(right) do
    method validate_misplaced_attrs! (line 1059) | defp validate_misplaced_attrs!(attrs, file, message_fun) do
    method validate_misplaced_slots! (line 1065) | defp validate_misplaced_slots!(slots, file, message_fun) do
    method pop_attrs (line 1071) | defp pop_attrs(env) do
    method pop_slots (line 1076) | defp pop_slots(env) do
    method raise_if_function_already_defined! (line 1081) | defp raise_if_function_already_defined!(env, name, slots, attrs) do
    method __verify__ (line 1102) | def __verify__(module, component_calls) do
    method verify (line 1111) | defp verify(
    method implicit_inner_block? (line 1272) | defp implicit_inner_block?(slot_name, slots_defs) do
    method type_mismatch (line 1276) | defp type_mismatch(:any, _type_value), do: nil
    method type_mismatch (line 1277) | defp type_mismatch(_type, :any), do: nil
    method type_mismatch (line 1278) | defp type_mismatch(type, {type, _value}), do: nil
    method type_mismatch (line 1279) | defp type_mismatch(:atom, {:boolean, _value}), do: nil
    method type_mismatch (line 1280) | defp type_mismatch({:struct, _}, {:map, {:%{}, _, [{:|, _, [_, _]}]}})...
    method type_mismatch (line 1281) | defp type_mismatch(:fun, {:fun, _}), do: nil
    method type_mismatch (line 1282) | defp type_mismatch({:fun, arity}, {:fun, arity}), do: nil
    method type_mismatch (line 1283) | defp type_mismatch({:fun, _arity}, {:fun, arity}), do: type_with_artic...
    method type_mismatch (line 1284) | defp type_mismatch(_type, {:fun, arity}), do: type_with_article({:fun,...
    method type_mismatch (line 1285) | defp type_mismatch(_type, {_, value}), do: Macro.to_string(value)
    method component_fa (line 1287) | defp component_fa(%{component: {mod, fun}}) do
    method type_with_article (line 1293) | defp type_with_article({:struct, struct}), do: "a #{inspect(struct)} s...
    method type_with_article (line 1294) | defp type_with_article(:fun), do: "a function"
    method type_with_article (line 1295) | defp type_with_article({:fun, arity}), do: "a function of arity #{arity}"
    method type_with_article (line 1297) | defp type_with_article(type), do: "a #{inspect(type)}"
    method warn (line 1300) | defp warn(message, file, line) do
    method ensure_used! (line 1304) | defp ensure_used!(module, line, file) do

FILE: lib/phoenix_component/macro_component.ex
  class Phoenix.Component.MacroComponent (line 1) | defmodule Phoenix.Component.MacroComponent
    method get_data (line 170) | def get_data(component_module, macro_component) do
    method attrs_to_ast (line 233) | defp attrs_to_ast(attrs, env) do
    method ast_to_tree (line 270) | def ast_to_tree({tag, attrs, [], %{closing: _closing} = meta}, origina...
    method ast_to_tree (line 275) | def ast_to_tree({tag, attrs, children, _meta}, original_meta) do
    method attrs_to_tree (line 285) | defp attrs_to_tree(attrs, meta) do
    method ast_to_string (line 311) | def ast_to_string(ast, opts \\ []) do
    method ast_to_iodata (line 324) | defp ast_to_iodata({name, attrs, [], %{closing: closing}}, opts) do
    method ast_to_iodata (line 339) | defp ast_to_iodata({name, attrs, children, _meta}, opts) do
    method ast_attributes_to_iodata (line 356) | defp ast_attributes_to_iodata(attrs) do
    method attr_quotes (line 380) | defp attr_quotes(key, value) do

FILE: lib/phoenix_live_component.ex
  class Phoenix.LiveComponent (line 1) | defmodule Phoenix.LiveComponent
  class CID (line 557) | defmodule CID

FILE: lib/phoenix_live_view.ex
  class Phoenix.LiveView (line 1) | defmodule Phoenix.LiveView
    method __live__ (line 431) | def __live__(opts \\ []) do
    method connected? (line 595) | def connected?(%Socket{transport_pid: transport_pid}), do: transport_p...
    method put_private (line 678) | def put_private(%Socket{}, bad_key, _value) do
    method redirect (line 1019) | def redirect(socket, opts \\ []) do
    method do_internal_redirect (line 1034) | defp do_internal_redirect(%Socket{} = socket, url, redirect_status) do
    method do_external_redirect (line 1040) | defp do_external_redirect(%Socket{} = socket, url, redirect_status) do
    method push_patch (line 1084) | def push_patch(%Socket{} = socket, opts) do
    method push_navigate (line 1110) | def push_navigate(%Socket{} = socket, opts) do
    method push_redirect (line 1117) | def push_redirect(%Socket{} = socket, opts) do
    method push_opts! (line 1122) | defp push_opts!(opts, context) do
    method put_redirect (line 1129) | defp put_redirect(%Socket{redirected: nil} = socket, command) do
    method put_redirect (line 1133) | defp put_redirect(%Socket{redirected: to} = _socket, _command) do
    method validate_local_url! (line 1139) | defp validate_local_url!("//" <> _ = to, where) do
    method validate_local_url! (line 1143) | defp validate_local_url!("/" <> _ = to, where) do
    method validate_local_url! (line 1151) | defp validate_local_url!(to, where) do
    method raise_invalid_local_url! (line 1155) | defp raise_invalid_local_url!(to, where) do
    method get_connect_params (line 1208) | def get_connect_params(%Socket{private: private} = socket) do
    method conn_connect_info (line 1258) | defp conn_connect_info(conn, :peer_data) do
    method conn_connect_info (line 1262) | defp conn_connect_info(conn, :x_headers) do
    method conn_connect_info (line 1268) | defp conn_connect_info(conn, :trace_context_headers) do
    method conn_connect_info (line 1274) | defp conn_connect_info(conn, :uri) do
    method conn_connect_info (line 1284) | defp conn_connect_info(conn, :user_agent) do
    method static_changed? (line 1351) | def static_changed?(%Socket{private: private, endpoint: endpoint} = so...
    method static_changed? (line 1363) | defp static_changed?([_ | _] = statics, %{} = latest) do
    method static_changed? (line 1375) | defp static_changed?(_, _), do: false
    method raise_root_and_mount_only! (line 1377) | defp raise_root_and_mount_only!(socket, fun) do
    method send_update (line 1450) | def send_update(pid \\ self(), module_or_cid, assigns)
    method send_update_after (line 1493) | def send_update_after(pid \\ self(), module_or_cid, assigns, time_in_m...
    method transport_pid (line 1523) | def transport_pid(%Socket{}) do
    method child? (line 1530) | defp child?(%Socket{parent_pid: pid}), do: is_pid(pid)
    method stream (line 1915) | def stream(%Socket{} = socket, name, items, opts \\ []) do
    method ensure_streams (line 1971) | defp ensure_streams(%Socket{} = socket) do
    method stream_insert (line 2056) | def stream_insert(%Socket{} = socket, name, item, opts \\ []) do
    method stream_delete (line 2085) | def stream_delete(%Socket{} = socket, name, item) do
    method stream_delete_by_dom_id (line 2122) | def stream_delete_by_dom_id(%Socket{} = socket, name, id) do
    method assign_stream (line 2126) | defp assign_stream(%Socket{} = socket, name, items, opts) do
    method update_stream (line 2173) | defp update_stream(%Socket{} = socket, name, func) do
    method cancel_async (line 2376) | def cancel_async(socket, async_or_keys, reason \\ {:shutdown, :cancel}...

FILE: lib/phoenix_live_view/application.ex
  class Phoenix.LiveView.Application (line 1) | defmodule Phoenix.LiveView.Application
    method start (line 7) | def start(_type, _args) do

FILE: lib/phoenix_live_view/async.ex
  class Phoenix.LiveView.Async (line 1) | defmodule Phoenix.LiveView.Async
    method warn_socket_access (line 6) | defp warn_socket_access(op, warn) do
    method warn_assigns_access (line 32) | def warn_assigns_access(op, warn) do
    method validate_function_env (line 56) | defp validate_function_env(func, op, env) do
    method start_async (line 94) | def start_async(socket, key, func, opts, env) do
    method assign_async (line 116) | def assign_async(socket, key_or_keys, func, opts, env) do
    method stream_async (line 183) | def stream_async(socket, key, func, opts, env) do
    method maybe_init_stream (line 252) | defp maybe_init_stream(socket, key) do
    method do_async (line 285) | defp do_async(lv_pid, cid, key, func, async_kind) do
    method cancel_async (line 302) | def cancel_async(%Socket{} = socket, %AsyncResult{} = result, reason) do
    method cancel_async (line 316) | def cancel_async(%Socket{} = socket, key, reason) do
    method handle_async (line 328) | def handle_async(socket, maybe_component, kind, key, ref, result) do
    method handle_trap_exit (line 338) | def handle_trap_exit(socket, maybe_component, kind, key, ref, reason) do
    method handle_kind (line 342) | defp handle_kind(socket, maybe_component, :start, key, result) do
    method handle_kind (line 364) | defp handle_kind(socket, _maybe_component, :assign, keys, result) do
    method handle_kind (line 392) | defp handle_kind(socket, _maybe_component, :stream, key, result) do
    method prune_current_async (line 416) | defp prune_current_async(socket, key, ref) do
    method update_private_async (line 424) | defp update_private_async(%{private: private} = socket, func) do
    method get_private_async (line 429) | defp get_private_async(%Socket{} = socket, key) do
    method get_current_async! (line 433) | defp get_current_async!(socket, key) do
    method to_exit (line 442) | defp to_exit(:throw, reason, stack), do: {:exit, {{:nocatch, reason}, ...
    method to_exit (line 443) | defp to_exit(:error, reason, stack), do: {:exit, {reason, stack}}
    method to_exit (line 444) | defp to_exit(:exit, reason, _stack), do: {:exit, reason}
    method cid (line 446) | defp cid(%Socket{} = socket) do

FILE: lib/phoenix_live_view/async_result.ex
  class Phoenix.LiveView.AsyncResult (line 1) | defmodule Phoenix.LiveView.AsyncResult
    method loading (line 34) | def loading do
    method loading (line 53) | def loading(%AsyncResult{} = result) do
    method loading (line 57) | def loading(loading_state) do
    method loading (line 76) | def loading(%AsyncResult{} = result, loading_state) do
    method failed (line 97) | def failed(%AsyncResult{} = result, reason) do
    method ok (line 116) | def ok(value) do
    method ok (line 140) | def ok(%AsyncResult{} = result, value) do

FILE: lib/phoenix_live_view/channel.ex
  class Phoenix.LiveView.Channel (line 1) | defmodule Phoenix.LiveView.Channel
    method start_link (line 25) | def start_link({endpoint, from}) do
    method send_update (line 31) | def send_update(pid, ref, assigns) do
    method async_pids (line 49) | def async_pids(lv_pid) do
    method ping (line 53) | def ping(pid) do
    method register_upload (line 57) | def register_upload(pid, {upload_config_ref, entry_ref} = _ref, cid) do
    method fetch_upload_config (line 62) | def fetch_upload_config(pid, name, cid) do
    method drop_upload_entries (line 66) | def drop_upload_entries(%UploadConfig{} = conf, entry_refs) do
    method report_writer_error (line 71) | def report_writer_error(pid, reason) do
    method init (line 77) | def init({pid, _ref}) do
    method handle_info (line 82) | def handle_info({Phoenix.Channel, auth_payload, from, phx_socket}, ref...
    method handle_info (line 90) | def handle_info({:DOWN, ref, _, _, _reason}, ref) do
    method handle_info (line 94) | def handle_info(
    method handle_info (line 101) | def handle_info({:DOWN, _, _, parent, reason}, %{socket: %{parent_pid:...
    method handle_info (line 106) | def handle_info({:DOWN, _, :process, pid, reason} = msg, %{socket: soc...
    method handle_info (line 127) | def handle_info(%Broadcast{event: "phx_drain"}, state) do
    method handle_info (line 132) | def handle_info(%Message{topic: topic, event: "phx_leave"} = msg, %{to...
    method handle_info (line 138) | def handle_info(%Message{topic: topic, event: "live_patch"} = msg, %{t...
    method handle_info (line 156) | def handle_info(
    method handle_info (line 170) | def handle_info(%Message{topic: topic, event: "progress"} = msg, %{top...
    method handle_info (line 209) | def handle_info(%Message{topic: topic, event: "allow_upload"} = msg, %...
    method handle_info (line 237) | def handle_info(
    method handle_info (line 246) | def handle_info(%Message{topic: topic, event: "event"} = msg, %{topic:...
    method handle_info (line 265) | def handle_info({@prefix, :async_result, {kind, info}}, state) do
    method handle_info (line 283) | def handle_info({@prefix, :drop_upload_entries, info}, state) do
    method handle_info (line 295) | def handle_info({@prefix, :report_writer_error, channel_pid, reason}, ...
    method handle_info (line 320) | def handle_info({@prefix, :send_update, update}, state) do
    method handle_info (line 332) | def handle_info({@prefix, :redirect, command, flash}, state) do
    method handle_info (line 336) | def handle_info({{Phoenix.LiveView.Async, keys, cid, kind}, ref, :proc...
    method handle_info (line 346) | def handle_info({:phoenix_live_reload, _topic, _changed_file}, %{socke...
    method handle_info (line 360) | def handle_info(msg, %{socket: socket} = state) do
    method handle_noop (line 366) | defp handle_noop({%Phoenix.LiveComponent.CID{cid: cid}, _}) do
    method handle_noop (line 373) | defp handle_noop({{module, id}, _}) do
    method handle_call (line 385) | def handle_call({@prefix, :ping}, _from, state) do
    method handle_call (line 389) | def handle_call({@prefix, :async_pids}, _from, state) do
    method handle_call (line 394) | def handle_call({@prefix, :fetch_upload_config, name, cid}, _from, sta...
    method handle_call (line 404) | def handle_call({@prefix, :child_mount, _child_pid, assign_new}, _from...
    method handle_call (line 409) | def handle_call({@prefix, :register_entry_upload, info}, from, state) do
    method handle_call (line 414) | def handle_call({@prefix, :debug_get_socket}, _from, state) do
    method handle_call (line 419) | def handle_call(
    method handle_call (line 432) | def handle_call(msg, from, %{socket: socket} = state) do
    method handle_cast (line 446) | def handle_cast(msg, %{socket: socket} = state) do
    method format_status (line 453) | def format_status(:terminate, [_pdict, state]) do
    method format_status (line 457) | def format_status(:normal, [_pdict, %{} = state]) do
    method format_status (line 472) | def format_status(_, [_pdict, state]) do
    method terminate (line 477) | def terminate(reason, %{socket: socket}) do
    method terminate (line 487) | def terminate(_reason, _state) do
    method code_change (line 492) | def code_change(old, %{socket: socket} = state, extra) do
    method view_handle_event (line 503) | defp view_handle_event(%Socket{} = socket, "lv:clear-flash", val) do
    method view_handle_event (line 510) | defp view_handle_event(%Socket{}, "lv:" <> _ = bad_event, _val) do
    method view_handle_event (line 517) | defp view_handle_event(%Socket{} = socket, event, val) do
    method view_handle_info (line 545) | defp view_handle_info(msg, %{view: view} = socket) do
    method exported? (line 564) | defp exported?(m, f, a) do
    method maybe_call_mount_handle_params (line 568) | defp maybe_call_mount_handle_params(%{socket: socket} = state, router,...
    method mount_handle_params_result (line 595) | defp mount_handle_params_result({:noreply, %Socket{} = new_socket}, st...
    method handle_result (line 624) | defp handle_result(
    method handle_result (line 632) | defp handle_result({:noreply, %Socket{} = new_socket}, {_from, _arity,...
    method handle_result (line 636) | defp handle_result(result, {name, arity, _ref}, state) do
    method raise_bad_callback_response! (line 640) | defp raise_bad_callback_response!(result, view, :handle_call, 3) do
    method raise_bad_callback_response! (line 653) | defp raise_bad_callback_response!(result, view, :handle_event, arity) do
    method raise_bad_callback_response! (line 666) | defp raise_bad_callback_response!(result, view, name, arity) do
    method component_handle (line 678) | defp component_handle(state, cid, ref, fun) do
    method unregister_upload (line 705) | defp unregister_upload(state, ref, entry_ref, cid) do
    method drop_upload_name (line 728) | defp drop_upload_name(state, name) do
    method inner_component_handle_event (line 733) | defp inner_component_handle_event(component_socket, _component, "lv:cl...
    method inner_component_handle_event (line 743) | defp inner_component_handle_event(_component_socket, _component, "lv:"...
    method inner_component_handle_event (line 750) | defp inner_component_handle_event(component_socket, component, event, ...
    method decode_event_type (line 804) | defp decode_event_type("form", url_encoded, raw_payload) do
    method decode_event_type (line 811) | defp decode_event_type(_, value, _raw_payload), do: value
    method decode_merge_target (line 820) | defp decode_merge_target(%{} = params), do: params
    method maybe_merge_meta (line 826) | defp maybe_merge_meta(value, _raw_payload), do: value
    method gather_keys (line 828) | defp gather_keys(%{} = map, acc) do
    method gather_keys (line 835) | defp gather_keys([], acc), do: acc
    method gather_keys (line 836) | defp gather_keys([%{} = map], acc), do: gather_keys(map, acc)
    method gather_keys (line 837) | defp gather_keys(_, acc), do: acc
    method handle_changed (line 839) | defp handle_changed(state, %Socket{} = new_socket, ref, pending_live_p...
    method check_patch_redirect_limit! (line 855) | defp check_patch_redirect_limit!(state) do
    method clear_live_patch_counter (line 869) | defp clear_live_patch_counter(state) do
    method handle_redirect (line 873) | defp handle_redirect(new_state, result, flash, ref) do
    method push_pending_events_on_redirect (line 931) | defp push_pending_events_on_redirect(state, socket) do
    method patch_params_and_action! (line 936) | defp patch_params_and_action!(socket, %{to: to}) do
    method stop_shutdown_redirect (line 951) | defp stop_shutdown_redirect(state, kind, opts) do
    method drop_redirect (line 956) | defp drop_redirect(state) do
    method sync_handle_params_with_live_redirect (line 960) | defp sync_handle_params_with_live_redirect(state, params, action, %{to...
    method push_live_patch (line 971) | defp push_live_patch(state, nil), do: state
    method push_live_patch (line 972) | defp push_live_patch(state, opts), do: push(state, "live_patch", opts)
    method push_redirect (line 974) | defp push_redirect(state, opts, nil = _ref) do
    method push_redirect (line 978) | defp push_redirect(state, opts, ref) do
    method push_live_redirect (line 982) | defp push_live_redirect(state, opts, nil = _ref) do
    method push_live_redirect (line 986) | defp push_live_redirect(state, opts, ref) do
    method push_noop (line 990) | defp push_noop(state, nil = _ref), do: state
    method push_noop (line 991) | defp push_noop(state, ref), do: reply(state, ref, :ok, %{})
    method push_diff (line 994) | defp push_diff(state, diff, nil = _ref), do: push(state, "diff", diff)
    method push_diff (line 995) | defp push_diff(state, diff, ref), do: reply(state, ref, :ok, %{diff: d...
    method copy_flash (line 1000) | defp copy_flash(state, flash, opts),
    method maybe_diff (line 1003) | defp maybe_diff(%{socket: socket} = state, force?) do
    method render_diff (line 1007) | defp render_diff(state, socket, force?) do
    method reply (line 1043) | defp reply(state, {ref, extra}, status, payload) do
    method push (line 1053) | defp push(state, event, payload) do
    method mount (line 1067) | defp mount(%{"session" => session_token} = params, from, phx_socket) do
    method mount (line 1146) | defp mount(%{}, from, phx_socket) do
    method load_live_view (line 1152) | defp load_live_view(view) do
    method verified_mount (line 1164) | defp verified_mount(
    method verify_flash (line 1277) | defp verify_flash(endpoint, %Session{} = verified, flash_token, connec...
    method load_csrf_token (line 1293) | defp load_csrf_token(endpoint, socket_session) do
    method load_lifecycle (line 1301) | defp load_lifecycle(
    method load_lifecycle (line 1308) | defp load_lifecycle(%{lifecycle: lifecycle}, _) do
    method load_layout (line 1312) | defp load_layout(socket, %Route{live_session: %{extra: %{layout: layou...
    method load_layout (line 1316) | defp load_layout(socket, _route) do
    method mount_private (line 1320) | defp mount_private(%Session{parent_pid: nil} = session, connect_params...
    method mount_private (line 1339) | defp mount_private(
    method sync_with_parent (line 1371) | defp sync_with_parent(parent, assign_new) do
    method put_container (line 1379) | defp put_container(%Session{} = session, %Route{} = route, %{} = diff) do
    method put_container (line 1391) | defp put_container(%Session{}, nil = _route, %{} = diff), do: diff
    method resolve_class_attribute_as_list (line 1393) | defp resolve_class_attribute_as_list(attrs) do
    method reply_mount (line 1400) | defp reply_mount(result, from, %Session{} = session, route) do
    method maybe_put_debug_pid (line 1431) | defp maybe_put_debug_pid(diff) do
    method build_state (line 1439) | defp build_state(%Socket{} = lv_socket, %Phoenix.Socket{} = phx_socket...
    method build_uri (line 1453) | defp build_uri(%{socket: socket}, "/" <> _ = to) do
    method post_verified_mount (line 1457) | defp post_verified_mount(%{socket: socket} = state) do
    method assign_action (line 1461) | defp assign_action(socket, action) do
    method maybe_update_uploads (line 1465) | defp maybe_update_uploads(%Socket{} = socket, %{"uploads" => uploads} ...
    method maybe_update_uploads (line 1478) | defp maybe_update_uploads(%Socket{} = socket, %{} = _payload), do: socket
    method register_entry_upload (line 1480) | defp register_entry_upload(state, from, info) do
    method writer! (line 1505) | defp writer!(socket, name, entry, writer) do
    method read_socket (line 1517) | defp read_socket(state, nil = _cid, func) do
    method read_socket (line 1521) | defp read_socket(state, cid, func) do
    method write_socket (line 1528) | defp write_socket(state, nil, ref, fun) do
    method write_socket (line 1541) | defp write_socket(state, cid, ref, fun) do
    method delete_components (line 1560) | defp delete_components(state, cids) do
    method ensure_unique_upload_name! (line 1592) | defp ensure_unique_upload_name!(state, conf) do
    method authorize_session (line 1613) | defp authorize_session(%Session{} = session, endpoint, %{"redirect" =>...
    method authorize_session (line 1637) | defp authorize_session(%Session{} = session, endpoint, %{"url" => url}...
    method authorize_session (line 1658) | defp authorize_session(%Session{} = session, _endpoint, %{} = _params) do
    method session_route (line 1662) | defp session_route(%Session{} = session, endpoint, url) do
    method maybe_subscribe_to_live_reload (line 1669) | defp maybe_subscribe_to_live_reload({:noreply, state}) do
    method maybe_subscribe_to_live_reload (line 1683) | defp maybe_subscribe_to_live_reload(response), do: response
    method component_asyncs (line 1685) | defp component_asyncs(state) do
    method all_asyncs (line 1693) | defp all_asyncs(state) do
    method socket_asyncs (line 1701) | defp socket_asyncs(private, cid) do

FILE: lib/phoenix_live_view/colocated_hook.ex
  class Phoenix.LiveView.ColocatedHook (line 1) | defmodule Phoenix.LiveView.ColocatedHook
    method transform (line 142) | def transform({"script", attributes, [text_content], _tag_meta} = _ast...
    method transform (line 192) | def transform(_ast, _meta) do
    method validate_phx_version! (line 196) | defp validate_phx_version! do

FILE: lib/phoenix_live_view/colocated_js.ex
  class Phoenix.LiveView.ColocatedJS (line 1) | defmodule Phoenix.LiveView.ColocatedJS
    method transform (line 187) | def transform({"script", attributes, [text_content], _tag_meta} = _ast...
    method transform (line 198) | def transform(_ast, _meta) do
    method validate_phx_version! (line 202) | defp validate_phx_version! do
    method validate_name! (line 210) | defp validate_name!(opts) do
    method extract (line 229) | def extract(opts, text_content, meta) do
    method maybe_put_opt (line 253) | defp maybe_put_opt(map, opts, opts_key, target_key) do
    method compile (line 264) | def compile do
    method clear_manifests! (line 273) | defp clear_manifests! do
    method clear_outdated_and_get_files! (line 283) | defp clear_outdated_and_get_files! do
    method process_module (line 293) | defp process_module(module_folder, module) do
    method get_data (line 318) | defp get_data(module) do
    method write_new_manifests! (line 325) | defp write_new_manifests!(files) do
    method write_manifest (line 344) | defp write_manifest(manifest, entries) do
    method maybe_link_node_modules! (line 394) | defp maybe_link_node_modules! do
    method relative_to_target (line 408) | defp relative_to_target(location) do
    method do_symlink (line 416) | defp do_symlink(node_modules_path, is_fallback) do
    method global_settings (line 439) | defp global_settings do
    method project_settings (line 443) | defp project_settings do
    method target_dir (line 449) | defp target_dir do
    method subdirectories (line 458) | defp subdirectories(path) do

FILE: lib/phoenix_live_view/controller.ex
  class Phoenix.LiveView.Controller (line 1) | defmodule Phoenix.LiveView.Controller
    method live_render (line 38) | def live_render(%Plug.Conn{} = conn, view, opts \\ []) do
    method ensure_format (line 66) | defp ensure_format(conn) do
    method put_flash (line 74) | defp put_flash(conn, nil), do: conn
    method put_flash (line 76) | defp put_flash(conn, flash),

FILE: lib/phoenix_live_view/debug.ex
  class Phoenix.LiveView.Debug (line 1) | defmodule Phoenix.LiveView.Debug
    method list_liveviews (line 51) | def list_liveviews do
    method keyfind (line 58) | defp keyfind(list, key) do
    method lv_process_dict (line 65) | defp lv_process_dict(pid) do
    method liveview_process? (line 89) | def liveview_process?(pid) do
    method socket (line 105) | def socket(liveview_pid) do
    method live_components (line 134) | def live_components(liveview_pid) do

FILE: lib/phoenix_live_view/diff.ex
  class Phoenix.LiveView.Diff (line 1) | defmodule Phoenix.LiveView.Diff
    method new_components (line 33) | def new_components(uuids \\ 1) do
    method new_fingerprints (line 40) | def new_fingerprints do
    method to_iodata (line 49) | def to_iodata(map, component_mapper \\ fn _cid, content -> content end...
    method to_iodata (line 54) | defp to_iodata(
    method to_iodata (line 69) | defp to_iodata(%{@static => static} = parts, components, template, map...
    method keyed_to_iodata (line 92) | defp keyed_to_iodata(_index, _limit, _keyed, _static, components, _tem...
    method one_to_iodata (line 96) | defp one_to_iodata([last], _parts, _counter, acc, components, _templat...
    method one_to_iodata (line 100) | defp one_to_iodata([head | tail], parts, counter, acc, components, tem...
    method resolve_components_xrefs (line 108) | defp resolve_components_xrefs(cid, components) do
    method deep_merge (line 120) | defp deep_merge(_original, %{@static => _} = extra), do: extra
    method deep_merge (line 122) | defp deep_merge(original, extra) do
    method render_private (line 132) | def render_private(socket, diff) do
    method render (line 151) | def render(socket, %Rendered{} = rendered, prints, components) do
    method maybe_put_cdiffs (line 172) | defp maybe_put_cdiffs(diff, cdiffs), do: Map.put(diff, @components, cd...
    method get_push_events_diff (line 177) | def get_push_events_diff(socket) do
    method maybe_put_title (line 181) | defp maybe_put_title(diff, socket) do
    method maybe_put_events (line 189) | defp maybe_put_events(diff, socket) do
    method extract_events (line 196) | defp extract_events({diff, component_diffs}) do
    method maybe_put_reply (line 207) | defp maybe_put_reply(diff, socket) do
    method update_component (line 281) | def update_component(socket, components, {ref, updated_assigns}) do
    method mark_for_deletion_component (line 315) | def mark_for_deletion_component(cid, {cid_to_component, id_to_cid, uui...
    method delete_component (line 332) | def delete_component(cid, {cid_to_component, id_to_cid, uuids}) do
    method component_to_rendered (line 377) | defp component_to_rendered(socket, component, id) do
    method traverse (line 396) | defp traverse(
    method traverse (line 417) | defp traverse(
    method traverse (line 440) | defp traverse(
    method traverse (line 452) | defp traverse(
    method traverse (line 507) | defp traverse(
    method traverse (line 520) | defp traverse(
    method traverse (line 575) | defp traverse(nil, fingerprint_tree, pending, components, template, _c...
    method traverse (line 579) | defp traverse(iodata, _, pending, components, template, _changed?) do
    method invoke_dynamic (line 583) | defp invoke_dynamic(%Rendered{caller: :not_available, dynamic: dynamic...
    method invoke_dynamic (line 587) | defp invoke_dynamic(%Rendered{caller: caller, dynamic: dynamic}, chang...
    method inject_stacktrace (line 598) | defp inject_stacktrace([{__MODULE__, :invoke_dynamic, 2, _} | stacktra...
    method inject_stacktrace (line 602) | defp inject_stacktrace([head | tail], entry) do
    method inject_stacktrace (line 606) | defp inject_stacktrace([], entry) do
    method traverse_dynamic (line 610) | defp traverse_dynamic(dynamic, children, pending, components, template...
    method traverse_keyed (line 639) | defp traverse_keyed(
    method process_keyed (line 731) | defp process_keyed({key, vars, render}, _previous_prints, _changed?, s...
    method maybe_share_template (line 759) | defp maybe_share_template(map, fingerprint, static, {print_to_pos, pos...
    method maybe_share_template (line 772) | defp maybe_share_template(map, _fingerprint, static, nil) do
    method maybe_add_template (line 779) | defp maybe_add_template(map, _new_template), do: map
    method maybe_add_stream (line 781) | defp maybe_add_stream(diff, nil = _stream), do: diff
    method maybe_add_stream (line 782) | defp maybe_add_stream(diff, stream), do: Map.put(diff, @stream, stream)
    method maybe_add_keyed (line 784) | defp maybe_add_keyed(diff, nil = _keyed), do: diff
    method maybe_add_keyed (line 785) | defp maybe_add_keyed(diff, keyed), do: Map.put(diff, @keyed, keyed)
    method traverse_component (line 789) | defp traverse_component(
    method render_pending_components (line 807) | defp render_pending_components(socket, pending, cids, diffs, component...
    method render_pending_components (line 816) | defp render_pending_components(socket, pending, seen_ids, cids, diffs,...
    method zip_components (line 882) | defp zip_components(
    method zip_components (line 898) | defp zip_components([], [], _component, _cids, acc) do
    method zip_components (line 902) | defp zip_components(_sockets, _metadata, component, _cids, _acc) do
    method maybe_preload_components (line 907) | defp maybe_preload_components(component, entries) do
    method maybe_call_preload! (line 918) | defp maybe_call_preload!(module, assigns) do
    method zip_preloads (line 933) | defp zip_preloads([], [], _component, _preloaded) do
    method zip_preloads (line 937) | defp zip_preloads(_, _, component, preloaded) do
    method render_component (line 943) | defp render_component(socket, component, id, prints, cid, new?, cids, ...
    method unmark_for_deletion (line 990) | defp unmark_for_deletion(private, {cid_to_component, id_to_cid, uuids}...
    method do_unmark_for_deletion (line 995) | defp do_unmark_for_deletion(private, cids) do
    method maybe_reuse_static (line 1029) | defp maybe_reuse_static(rendered, component, prints, old_cids, compone...
    method find_same_component_print (line 1044) | defp find_same_component_print(_print, _iterator, _old_cids, _new_cids...
    method find_same_component_print (line 1046) | defp find_same_component_print(print, iterator, old_cids, new_cids, at...
    method put_cid (line 1069) | defp put_cid({id_to_components, id_to_cid, uuids}, component, id, cid) do
    method fetch_cid (line 1074) | defp fetch_cid(
    method fetch_cid (line 1084) | defp fetch_cid({component, id}, {_cid_to_components, id_to_cid, _} = _...
    method mount_component (line 1091) | defp mount_component(socket, component, assigns) do
    method configure_socket_for_component (line 1106) | defp configure_socket_for_component(socket, assigns, private) do
    method dump_component (line 1115) | defp dump_component(socket, component, id, prints) do

FILE: lib/phoenix_live_view/engine.ex
  class Phoenix.LiveView.Component (line 1) | defmodule Phoenix.LiveView.Component
  class Phoenix.LiveView.Comprehension (line 54) | defmodule Phoenix.LiveView.Comprehension
  class Phoenix.LiveView.Rendered (line 100) | defmodule Phoenix.LiveView.Rendered
  class Phoenix.LiveView.Engine (line 155) | defmodule Phoenix.LiveView.Engine
    method compile (line 299) | def compile(path, _name) do
    method init (line 308) | def init(opts) do
    method handle_begin (line 321) | def handle_begin(state) do
    method handle_end (line 326) | def handle_end(state, opts \\ []) do
    method handle_body (line 334) | def handle_body(state, opts \\ []) do
    method handle_text (line 348) | def handle_text(state, _meta, text) do
    method handle_expr (line 354) | def handle_expr(state, "=", ast) do
    method handle_expr (line 364) | def handle_expr(state, "", ast) do
    method handle_expr (line 369) | def handle_expr(state, marker, ast) do
    method to_rendered_struct (line 375) | defp to_rendered_struct(expr, vars, assigns, caller, opts) do
    method analyze_static_and_dynamic (line 451) | defp analyze_static_and_dynamic(static, dynamic, initial_vars, assigns...
    method to_live_struct (line 472) | defp to_live_struct({:for, _, [_ | _]} = expr, vars, assigns, caller) do
    method to_live_struct (line 544) | defp to_live_struct({left, meta, [_ | _] = args}, vars, assigns, calle...
    method to_live_struct (line 599) | defp to_live_struct(expr, _vars, _assigns, _caller) do
    method mark_variables_as_change_tracked (line 604) | def mark_variables_as_change_tracked({:^, _, [_]} = ast, vars) do
    method mark_variables_as_change_tracked (line 608) | def mark_variables_as_change_tracked({:"::", meta, [left, right]}, var...
    method mark_variables_as_change_tracked (line 625) | def mark_variables_as_change_tracked({left, meta, right}, vars) do
    method mark_variables_as_change_tracked (line 631) | def mark_variables_as_change_tracked({left, right}, vars) do
    method mark_variables_as_change_tracked (line 637) | def mark_variables_as_change_tracked([_ | _] = list, vars) do
    method mark_variables_as_change_tracked (line 641) | def mark_variables_as_change_tracked(other, vars) do
    method extract_call (line 645) | defp extract_call({:., _, [{:__aliases__, _, [:Phoenix, :LiveView, :Ta...
    method extract_call (line 648) | defp extract_call(call),
    method maybe_block_to_rendered (line 651) | defp maybe_block_to_rendered([{:->, _, _} | _] = blocks, vars, caller) do
    method maybe_block_to_rendered (line 662) | defp maybe_block_to_rendered(block, vars, caller) do
    method to_conditional_var (line 669) | defp to_conditional_var(:all, var, live_struct) do
    method to_conditional_var (line 683) | defp to_conditional_var(keys, var, live_struct) do
    method changed_assigns (line 693) | defp changed_assigns(assigns) do
    method nested_and_parent_is_checked? (line 739) | defp nested_and_parent_is_checked?([_], _assigns),
    method nested_and_parent_is_checked? (line 744) | defp nested_and_parent_is_checked?(keys, assigns),
    method parent_is_checked? (line 747) | defp parent_is_checked?([], _assigns),
    method parent_is_checked? (line 750) | defp parent_is_checked?(rest, assigns),
    method to_component_tracking (line 755) | defp to_component_tracking(meta, fun, expr, extra, vars, caller) do
    method without_dependencies? (line 835) | defp without_dependencies?(ast, vars, caller) do
    method to_component_keys (line 840) | defp to_component_keys(:all), do: :all
    method to_component_keys (line 841) | defp to_component_keys(map), do: Map.keys(map)
    method vars_changed_vars (line 843) | defp vars_changed_vars(:all), do: []
    method vars_changed_vars (line 845) | defp vars_changed_vars(keys) do
    method to_component_static (line 862) | def to_component_static(_keys, _assigns, nil, _vars_changed_vars, nil) do
    method to_component_static (line 866) | def to_component_static(keys, assigns, changed, vars_changed_vars, var...
    method component_changed (line 873) | defp component_changed(:all, _assigns, _changed, _vars_changed_vars, _...
    method component_changed (line 875) | defp component_changed([path], assigns, changed, vars_changed_vars, va...
    method component_changed (line 891) | defp component_changed(entries, assigns, changed, vars_changed_vars, v...
    method slots_to_rendered (line 907) | defp slots_to_rendered(static, vars, caller, slots) do
    method slot_to_rendered (line 920) | defp slot_to_rendered(
    method slot_to_rendered (line 955) | defp slot_to_rendered({left, meta, args}, key, vars, caller) do
    method slot_to_rendered (line 959) | defp slot_to_rendered({left, right}, key, vars, caller) do
    method slot_to_rendered (line 967) | defp slot_to_rendered(other, _key, _vars, _caller) do
    method bins_and_vars (line 973) | defp bins_and_vars(acc),
    method bins_and_vars (line 988) | defp bins_and_vars([], bins, vars),
    method analyze_and_return_tainted_keys (line 1009) | defp analyze_and_return_tainted_keys(ast, vars, assigns, caller) do
    method analyze_assign (line 1096) | defp analyze_assign(expr, vars, assigns, caller, _nest) do
    method analyze (line 1116) | defp analyze({:assigns, _, nil} = expr, vars, assigns, _caller) do
    method analyze (line 1180) | defp analyze({:"::", meta, [left, right]}, vars, assigns, caller) do
    method analyze (line 1200) | defp analyze({left, meta, args}, vars, assigns, caller) do
    method analyze (line 1225) | defp analyze({left, right}, vars, assigns, caller) do
    method analyze (line 1231) | defp analyze([_ | _] = list, vars, assigns, caller) do
    method analyze (line 1235) | defp analyze(other, vars, assigns, _caller) do
    method analyze_list (line 1239) | defp analyze_list([head | tail], vars, assigns, caller, acc) do
    method analyze_list (line 1244) | defp analyze_list([], vars, assigns, _caller, acc) do
    method analyze_skip_assignment_list (line 1248) | defp analyze_skip_assignment_list(
    method analyze_skip_assignment_list (line 1259) | defp analyze_skip_assignment_list([head | tail], vars, assigns, caller...
    method analyze_skip_assignment_list (line 1264) | defp analyze_skip_assignment_list([], vars, assigns, _caller, acc) do
    method analyze_with_restricted_vars (line 1279) | defp analyze_with_restricted_vars(ast, {kind, map}, assigns, caller) do
    method set_vars (line 1286) | defp set_vars({kind, _}, {_, map}), do: {kind, map}
    method untaint_vars (line 1287) | defp untaint_vars({_, map}), do: {:untainted, map}
    method unless_tainted (line 1289) | defp unless_tainted(:tainted, _), do: :tainted
    method unless_tainted (line 1290) | defp unless_tainted(_, kind), do: kind
    method taint_assigns (line 1292) | defp taint_assigns(assigns), do: Map.put(assigns, __MODULE__, true)
    method maybe_warn_taint (line 1296) | defp maybe_warn_taint(name, meta, caller) do
    method fingerprint (line 1327) | defp fingerprint(block, static) do
    method to_safe (line 1351) | defp to_safe(ast, bool) do
    method line_from_expr (line 1356) | defp line_from_expr(_), do: 0
    method to_safe (line 1369) | defp to_safe(expr, line, false) do
    method to_safe (line 1373) | defp to_safe(expr, line, true) do
    method safe_to_iodata (line 1378) | def safe_to_iodata(expr) do
    method live_to_iodata (line 1387) | def live_to_iodata(expr) do
    method changed_assign? (line 1399) | def changed_assign?(changed, name) do
    method changed_assign (line 1407) | defp changed_assign(changed, name) do
    method nested_changed_assign? (line 1416) | def nested_changed_assign?(tail, head, assigns, changed),
    method nested_changed_assign (line 1419) | defp nested_changed_assign(tail, head, assigns, changed) do
    method recur_changed_assign (line 1435) | defp recur_changed_assign([{:struct, head} | tail], assigns, changed) do
    method recur_changed_assign (line 1439) | defp recur_changed_assign([{:access, head}], %Form{} = form1, %Form{} ...
    method recur_changed_assign (line 1446) | defp recur_changed_assign([{:access, head} | tail], assigns, changed) do
    method recur_changed_assign (line 1454) | defp recur_changed_assign([], head, assigns, changed) do
    method recur_changed_assign (line 1463) | defp recur_changed_assign(tail, head, assigns, changed) do
    method classify_taint (line 1477) | defp classify_taint(:case, [_, _]), do: :live
    method classify_taint (line 1478) | defp classify_taint(:if, [_, _]), do: :live
    method classify_taint (line 1479) | defp classify_taint(:unless, [_, _]), do: :live
    method classify_taint (line 1480) | defp classify_taint(:cond, [_]), do: :live
    method classify_taint (line 1481) | defp classify_taint(:try, [_]), do: :live
    method classify_taint (line 1482) | defp classify_taint(:receive, [_]), do: :live
    method classify_taint (line 1485) | defp classify_taint(:with, [_ | _]), do: :live
    method classify_taint (line 1486) | defp classify_taint(:for, [_ | _]), do: :live
    method classify_taint (line 1489) | defp classify_taint(:inner_block, [_, [do: _]]), do: :live
    method classify_taint (line 1492) | defp classify_taint(:render_layout, [_, _, _, [do: _]]), do: :live
    method classify_taint (line 1495) | defp classify_taint(:alias, [_]), do: :special_form
    method classify_taint (line 1496) | defp classify_taint(:import, [_]), do: :special_form
    method classify_taint (line 1497) | defp classify_taint(:require, [_]), do: :special_form
    method classify_taint (line 1498) | defp classify_taint(:alias, [_, _]), do: :special_form
    method classify_taint (line 1499) | defp classify_taint(:import, [_, _]), do: :special_form
    method classify_taint (line 1500) | defp classify_taint(:require, [_, _]), do: :special_form
    method classify_taint (line 1502) | defp classify_taint(:&, [_]), do: :never
    method classify_taint (line 1503) | defp classify_taint(:fn, _), do: :never
    method classify_taint (line 1504) | defp classify_taint(_, _), do: :none

FILE: lib/phoenix_live_view/helpers.ex
  class Phoenix.LiveView.Helpers (line 1) | defmodule Phoenix.LiveView.Helpers
    method live_patch (line 34) | def live_patch(text, opts)
    method live_patch (line 36) | def live_patch(%Socket{}, _) do
    method live_redirect (line 59) | def live_redirect(text, opts)
    method live_redirect (line 61) | def live_redirect(%Socket{}, _) do
    method live_link (line 78) | defp live_link(type, block_or_text, opts) do
    method live_title_tag (line 97) | def live_title_tag(title, opts \\ []) do

FILE: lib/phoenix_live_view/html_algebra.ex
  class Phoenix.LiveView.HTMLAlgebra (line 1) | defmodule Phoenix.LiveView.HTMLAlgebra
    method block_to_algebra (line 40) | defp block_to_algebra([], _context), do: empty()
    method block_to_algebra (line 42) | defp block_to_algebra(block, %{mode: :preserve} = context) do
    method block_to_algebra (line 82) | defp block_to_algebra([head | tail], context) do
    method inline_break (line 151) | defp inline_break(prev_node, next_node) do
    method tag_block? (line 177) | defp tag_block?({:block, _, _, _, _, _, _}), do: true
    method tag_block? (line 178) | defp tag_block?(_node), do: false
    method tag? (line 180) | defp tag?({:block, _, _, _, _, _, _}), do: true
    method tag? (line 181) | defp tag?({:self_close, _, _, _, _}), do: true
    method tag? (line 182) | defp tag?(_node), do: false
    method text? (line 184) | defp text?({:text, _, _}), do: true
    method text? (line 185) | defp text?(_), do: false
    method text_starts_with_space? (line 192) | defp text_starts_with_space?(_node), do: false
    method text_ends_with_space? (line 197) | defp text_ends_with_space?(_node), do: false
    method block_preserve? (line 199) | defp block_preserve?({:block, _, _, _, _, %{mode: :preserve}, _}), do:...
    method block_preserve? (line 200) | defp block_preserve?({:body_expr, _, _}), do: true
    method block_preserve? (line 201) | defp block_preserve?({:eex, _, _}), do: true
    method block_preserve? (line 202) | defp block_preserve?(_node), do: false
    method text_preserve? (line 204) | defp text_preserve?({:text, _, %{mode: :preserve}}), do: true
    method text_preserve? (line 205) | defp text_preserve?(_), do: false
    method to_algebra (line 207) | defp to_algebra({:html_comment, block}, context) do
    method to_algebra (line 261) | defp to_algebra(
    method to_algebra (line 281) | defp to_algebra(
    method to_algebra (line 288) | defp to_algebra({:block, _type, _name, attrs, block, %{tag_name: name}...
    method to_algebra (line 318) | defp to_algebra({:self_close, _type, _name, attrs, %{tag_name: name}},...
    method to_algebra (line 330) | defp to_algebra({:eex_block, expr, block, meta}, %{mode: :preserve} = ...
    method to_algebra (line 342) | defp to_algebra({:eex_block, expr, block, meta}, context) do
    method to_algebra (line 353) | defp to_algebra({:eex_comment, text}, _context) do
    method to_algebra (line 357) | defp to_algebra({:eex, text, %{opt: opt} = meta}, context) do
    method to_algebra (line 371) | defp to_algebra({:body_expr, text, meta}, context) do
    method inline? (line 408) | defp inline?(name, context) do
    method text_to_algebra (line 413) | defp text_to_algebra(["" | lines], newlines, acc),
    method text_to_algebra (line 418) | defp text_to_algebra([line | lines], 0, acc),
    method text_to_algebra (line 424) | defp text_to_algebra([line | lines], _newlines, acc),
    method text_to_algebra (line 428) | defp text_to_algebra([], _, [doc, _line]),
    method text_to_algebra (line 431) | defp text_to_algebra([], _, []),
    method text_to_algebra (line 435) | defp text_to_algebra([], _, acc),
    method build_attrs (line 438) | defp build_attrs([], on_break, _formatters, _opts), do: on_break
    method build_attrs (line 440) | defp build_attrs([attr], on_break, formatters, opts) do
    method build_attrs (line 444) | defp build_attrs(attrs, on_break, formatters, opts) do
    method distinct_lines? (line 462) | defp distinct_lines?([{_, _, %{line: line}} | _], line), do: false
    method distinct_lines? (line 463) | defp distinct_lines?([{_, _, %{line: line}} | tail], _line), do: disti...
    method distinct_lines? (line 464) | defp distinct_lines?([], _line), do: true
    method attrs_sorter (line 475) | defp attrs_sorter({attr_name, _, _}) do
    method format_tag_open (line 482) | defp format_tag_open(name, attrs, context),
    method render_attribute (line 485) | defp render_attribute({:root, {:expr, expr, _}, _}, _formatters, _opts...
    method render_attribute (line 494) | defp render_attribute({attr, {:string, value, %{delimiter: ?'}}, _}, _...
    method render_attribute (line 502) | defp render_attribute({attr, {:string, value, _meta}, _}, _formatters,...
    method render_attribute (line 505) | defp render_attribute({attr, {:expr, value, meta}, _}, _formatters, op...
    method render_attribute (line 536) | defp render_attribute({attr, {_, value, _meta}, _}, _formatters, _opts),
    method render_attribute (line 539) | defp render_attribute({attr, nil, _}, _formatters, _opts), do: ~s(#{at...
    method expr_to_quoted (line 569) | defp expr_to_quoted(expr, meta, opts) do
    method expr_to_code_algebra (line 582) | defp expr_to_code_algebra(expr, meta, opts) do
    method quoted_to_code_algebra (line 587) | defp quoted_to_code_algebra(quoted, comments, opts) do
    method classify_leading (line 591) | def classify_leading(text), do: classify_leading(text, :spaces)
    method classify_leading (line 596) | def classify_leading(<<?\n, rest::binary>>, _), do: classify_leading(r...
    method classify_leading (line 597) | def classify_leading(<<>>, mode), do: mode
    method classify_leading (line 598) | def classify_leading(_rest, _), do: :other
    method maybe_force_unfit (line 600) | defp maybe_force_unfit({:block, doc}), do: {:block, force_unfit(doc)}
    method maybe_force_unfit (line 601) | defp maybe_force_unfit(doc), do: doc
    method trim_block_newlines (line 603) | defp trim_block_newlines(block, inline?) do
    method pop_head_if_only_spaces_or_newlines (line 617) | defp pop_head_if_only_spaces_or_newlines(
    method pop_head_if_only_spaces_or_newlines (line 638) | defp pop_head_if_only_spaces_or_newlines(block, _inline?, _where), do:...
    method cleanup_extra_spaces (line 640) | defp cleanup_extra_spaces(text, :newlines_before_text), do: " " <> Str...
    method cleanup_extra_spaces (line 641) | defp cleanup_extra_spaces(text, :newlines_after_text), do: String.trim...
    method whitespace_around? (line 643) | defp whitespace_around?(text, :newlines_before_text), do: :binary.firs...
    method whitespace_around? (line 644) | defp whitespace_around?(text, :newlines_after_text), do: :binary.last(...
    method count_indentation (line 646) | defp count_indentation(<<?\t, rest::binary>>, indent), do: count_inden...
    method count_indentation (line 647) | defp count_indentation(<<?\s, rest::binary>>, indent), do: count_inden...
    method count_indentation (line 648) | defp count_indentation(<<>>, _indent), do: :infinity
    method count_indentation (line 649) | defp count_indentation(_, indent), do: indent
    method remove_indentation (line 651) | defp remove_indentation(rest, 0), do: rest
    method remove_indentation (line 652) | defp remove_indentation(<<?\t, rest::binary>>, indent), do: remove_ind...
    method remove_indentation (line 653) | defp remove_indentation(<<?\s, rest::binary>>, indent), do: remove_ind...
    method remove_indentation (line 654) | defp remove_indentation(rest, _indent), do: rest
    method safe_to_migrate? (line 656) | defp safe_to_migrate?(~S[\{] <> rest, acc), do: safe_to_migrate?(rest,...
    method safe_to_migrate? (line 657) | defp safe_to_migrate?(~S[\}] <> rest, acc), do: safe_to_migrate?(rest,...
    method safe_to_migrate? (line 658) | defp safe_to_migrate?("{" <> rest, acc), do: safe_to_migrate?(rest, ac...
    method safe_to_migrate? (line 659) | defp safe_to_migrate?("}" <> rest, acc), do: safe_to_migrate?(rest, ac...
    method safe_to_migrate? (line 660) | defp safe_to_migrate?(<<_::utf8, rest::binary>>, acc), do: safe_to_mig...
    method safe_to_migrate? (line 661) | defp safe_to_migrate?(<<>>, acc), do: acc == 0

FILE: lib/phoenix_live_view/html_engine.ex
  class Phoenix.LiveView.HTMLEngine (line 1) | defmodule Phoenix.LiveView.HTMLEngine
    method compile (line 12) | def compile(path, _name) do
    method classify_type (line 40) | def classify_type(":inner_block"), do: {:error, "the slot name :inner_...
    method classify_type (line 41) | def classify_type(":" <> name), do: {:slot, name}
    method classify_type (line 46) | def classify_type("."), do: {:error, "a component name is required aft...
    method classify_type (line 47) | def classify_type("." <> name), do: {:local_component, name}
    method classify_type (line 48) | def classify_type(name), do: {:tag, name}
    method void? (line 55) | def void?(_), do: false
    method handle_attributes (line 58) | def handle_attributes(ast, meta) do
    method literal_keys? (line 81) | defp literal_keys?([]), do: true
    method literal_keys? (line 82) | defp literal_keys?(_other), do: false
    method handle_attrs_escape (line 84) | defp handle_attrs_escape(attrs, meta) do
    method handle_attr_escape (line 106) | defp handle_attr_escape("class", value, meta) do
    method handle_attr_escape (line 115) | defp handle_attr_escape("style", value, meta) do
    method handle_attr_escape (line 124) | defp handle_attr_escape(_name, value, meta) do
    method extract_binaries (line 131) | defp extract_binaries({:<>, _, [left, right]}, _root?, acc, meta) do
    method extract_binaries (line 135) | defp extract_binaries({:<<>>, _, parts} = binary, _root?, acc, meta) do
    method extract_binaries (line 154) | defp extract_binaries(value, false, acc, meta),
    method extract_binaries (line 157) | defp extract_binaries(_value, true, _acc, _meta),
    method attributes_escape (line 161) | def attributes_escape(attrs) do
    method class_attribute_encode (line 176) | def class_attribute_encode(other),
    method class_attribute_list (line 179) | defp class_attribute_list(value) do
    method empty_attribute_encode (line 191) | def empty_attribute_encode(nil), do: ""
    method empty_attribute_encode (line 192) | def empty_attribute_encode(false), do: ""
    method empty_attribute_encode (line 193) | def empty_attribute_encode(true), do: ""
    method empty_attribute_encode (line 194) | def empty_attribute_encode(value), do: Phoenix.HTML.Engine.encode_to_i...
    method binary_encode (line 203) | def binary_encode(value) do
    method quoted_binary_encode (line 207) | defp quoted_binary_encode(binary, meta) do
    method safe_unless_special (line 216) | defp safe_unless_special("id"), do: :id
    method safe_unless_special (line 217) | defp safe_unless_special("aria"), do: :aria
    method safe_unless_special (line 218) | defp safe_unless_special("class"), do: :class
    method safe_unless_special (line 219) | defp safe_unless_special("data"), do: :data
    method safe_unless_special (line 220) | defp safe_unless_special(name), do: {:safe, name}
    method annotate_body (line 223) | def annotate_body(%Macro.Env{} = caller) do
    method annotate_slot (line 232) | def annotate_slot(name, %{line: line}, _close_meta, %{file: file} = ca...
    method annotate_source (line 238) | defp annotate_source(name, file, line) do
    method annotate_caller (line 245) | def annotate_caller(file, line, caller) do
    method current_otp_app (line 254) | defp current_otp_app do
    method debug_annotations? (line 258) | defp debug_annotations?(caller) do

FILE: lib/phoenix_live_view/html_formatter.ex
  class Phoenix.LiveView.HTMLFormatter (line 1) | defmodule Phoenix.LiveView.HTMLFormatter
    method features (line 292) | def features(_opts) do
    method format (line 297) | def format(source, opts) do
    method process_buffer (line 351) | defp process_buffer([{:text, text, meta} | rest]) do
    method process_buffer (line 362) | defp process_buffer([{:body_expr, _, _} = node | rest]) do
    method process_buffer (line 366) | defp process_buffer([{:eex, _, _} = node | rest]) do
    method process_buffer (line 370) | defp process_buffer(buffer), do: buffer
    method may_set_preserve_on_block (line 374) | defp may_set_preserve_on_block(
    method may_set_preserve_on_block (line 388) | defp may_set_preserve_on_block(buffer, _text), do: buffer
    method set_preserve_on_block (line 391) | defp set_preserve_on_block([{:block, type, name, attrs, block, meta, c...
    method set_preserve_on_block (line 395) | defp set_preserve_on_block(buffer), do: buffer
    method count_newlines_before_text (line 397) | defp count_newlines_before_text(binary),
    method count_newlines_after_text (line 400) | defp count_newlines_after_text(binary),
    method count_newlines_until_text (line 403) | defp count_newlines_until_text(binary, counter, pos, inc) do
    method leading_whitespace (line 415) | defp leading_whitespace(binary) do
    method count_leading_whitespace (line 422) | defp count_leading_whitespace(_rest, count),
    method trailing_whitespace (line 425) | defp trailing_whitespace(binary) do
    method trailing_whitespace (line 429) | defp trailing_whitespace(binary, pos, len) do
    method transform_tree (line 441) | defp transform_tree(nodes, source, newlines, opts) do
    method reduce_html_comments (line 454) | defp reduce_html_comments([], acc), do: Enum.reverse(acc)
    method reduce_html_comments (line 457) | defp reduce_html_comments(
    method reduce_html_comments (line 503) | defp reduce_html_comments(
    method reduce_html_comments (line 511) | defp reduce_html_comments([node | rest], acc) do
    method collect_comment (line 516) | defp collect_comment(
    method collect_comment (line 532) | defp collect_comment([node | rest], comment_buffer, acc) do
    method augment_node (line 537) | defp augment_node({:block, type, name, attrs, children, meta, close_me...
    method augment_node (line 556) | defp augment_node({:eex_block, expr, blocks, meta}, state) do
    method augment_node (line 566) | defp augment_node({:html_comment, block}, state) do
    method augment_node (line 571) | defp augment_node(node, _state), do: node
    method determine_mode (line 574) | defp determine_mode(tag_name, attrs, meta) do
    method contains_special_attrs? (line 583) | defp contains_special_attrs?(attrs) do
    method content_from_source (line 593) | defp content_from_source(
    method line_byte_offset (line 608) | defp line_byte_offset(source, {line_before, line_size}, column) do
    method maybe_format_tag (line 656) | defp maybe_format_tag(node, _state), do: node

FILE: lib/phoenix_live_view/html_formatter/tag_formatter.ex
  class Phoenix.LiveView.HTMLFormatter.TagFormatter (line 1) | defmodule Phoenix.LiveView.HTMLFormatter.TagFormatter

FILE: lib/phoenix_live_view/js.ex
  class Phoenix.LiveView.JS (line 1) | defmodule Phoenix.LiveView.JS
    method to_encodable (line 333) | def to_encodable(%JS{} = js), do: js.ops
    method dispatch (line 426) | def dispatch(js \\ %JS{}, event)
    method dispatch (line 427) | def dispatch(%JS{} = js, event), do: dispatch(js, event, [])
    method dispatch (line 428) | def dispatch(event, opts), do: dispatch(%JS{}, event, opts)
    method dispatch (line 431) | def dispatch(%JS{} = js, event, opts) do
    method toggle (line 542) | def toggle(opts \\ [])
    method toggle (line 543) | def toggle(%JS{} = js), do: toggle(js, [])
    method show (line 601) | def show(opts \\ [])
    method show (line 602) | def show(%JS{} = js), do: show(js, [])
    method hide (line 657) | def hide(opts \\ [])
    method hide (line 658) | def hide(%JS{} = js), do: hide(js, [])
    method set_attribute (line 935) | def set_attribute({attr, val}), do: set_attribute(%JS{}, {attr, val}, [])
    method set_attribute (line 941) | def set_attribute(%JS{} = js, {attr, val}), do: set_attribute(js, {att...
    method remove_attribute (line 968) | def remove_attribute(attr), do: remove_attribute(%JS{}, attr, [])
    method remove_attribute (line 974) | def remove_attribute(%JS{} = js, attr), do: remove_attribute(js, attr,...
    method toggle_attribute (line 1009) | def toggle_attribute({attr, val}), do: toggle_attribute(%JS{}, {attr, ...
    method toggle_attribute (line 1010) | def toggle_attribute({attr, val1, val2}), do: toggle_attribute(%JS{}, ...
    method toggle_attribute (line 1019) | def toggle_attribute(%JS{} = js, {attr, val}), do: toggle_attribute(js...
    method toggle_attribute (line 1021) | def toggle_attribute(%JS{} = js, {attr, val1, val2}),
    method focus (line 1105) | def focus(opts \\ [])
    method focus (line 1106) | def focus(%JS{} = js), do: focus(js, [])
    method focus_first (line 1128) | def focus_first(opts \\ [])
    method focus_first (line 1129) | def focus_first(%JS{} = js), do: focus_first(js, [])
    method push_focus (line 1152) | def push_focus(opts \\ [])
    method push_focus (line 1153) | def push_focus(%JS{} = js), do: push_focus(js, [])
    method pop_focus (line 1169) | def pop_focus(%JS{} = js \\ %JS{}) do
    method concat (line 1273) | def concat(%JS{ops: first}, %JS{ops: second}), do: %JS{ops: first ++ s...
    method put_op (line 1275) | defp put_op(%JS{ops: ops} = js, kind, args) do
    method class_names (line 1285) | defp class_names(names) do
    method transition_class_names (line 1289) | defp transition_class_names(nil), do: nil
    method validate_keys (line 1299) | defp validate_keys(opts, kind, allowed_keys) do
    method put_value (line 1328) | defp put_value(opts) do
    method put_target (line 1336) | defp put_target(opts) do

FILE: lib/phoenix_live_view/lifecycle.ex
  class Phoenix.LiveView.Lifecycle (line 1) | defmodule Phoenix.LiveView.Lifecycle
    method stage_info (line 28) | def stage_info(%Socket{} = socket, view, stage, arity) do
    method attach_hook (line 44) | def attach_hook(%Socket{router: nil}, id, :handle_params, _fun) do
    method attach_hook (line 66) | def attach_hook(%Socket{}, _id, stage, _fun) do
    method detach_hook (line 83) | def detach_hook(%Socket{}, _id, stage) do
    method lifecycle (line 97) | defp lifecycle(socket, stage) do
    method update_lifecycle (line 105) | defp update_lifecycle(socket, stage, fun) do
    method put_lifecycle (line 111) | defp put_lifecycle(socket, lifecycle) do
    method validate_on_mount! (line 128) | def validate_on_mount!(view, result) do
    method prepare_on_mount! (line 142) | def prepare_on_mount!(hooks) do
    method mount (line 156) | def mount(params, session, %Socket{private: %{@lifecycle => lifecycle}...
    method handle_event (line 181) | def handle_event(event, val, %Socket{private: %{@lifecycle => lifecycl...
    method reduce_handle_event (line 187) | defp reduce_handle_event([hook | hooks], acc, function) do
    method reduce_handle_event (line 196) | defp reduce_handle_event([], acc, _function), do: {:cont, acc}
    method handle_params (line 199) | def handle_params(params, uri, %Socket{private: %{@lifecycle => lifecy...
    method handle_info (line 206) | def handle_info(msg, %Socket{private: %{@lifecycle => lifecycle}} = so...
    method handle_async (line 213) | def handle_async(key, result, %Socket{private: %{@lifecycle => lifecyc...
    method after_render (line 220) | def after_render(%Socket{private: %{@lifecycle => lifecycle}} = socket...
    method reduce_socket (line 236) | defp reduce_socket([hook | hooks], acc, function) do
    method reduce_socket (line 244) | defp reduce_socket([], acc, _function), do: {:cont, acc}
    method bad_lifecycle_response! (line 246) | defp bad_lifecycle_response!(result, hook) do
    method expected_return (line 258) | defp expected_return(%{stage: :handle_event}) do
    method expected_return (line 266) | defp expected_return(_) do
    method raise_halt_without_redirect! (line 273) | defp raise_halt_without_redirect!(hook) do
    method raise_continue_with_redirect! (line 278) | defp raise_continue_with_redirect!(hook) do

FILE: lib/phoenix_live_view/live_stream.ex
  class Phoenix.LiveView.LiveStream (line 1) | defmodule Phoenix.LiveView.LiveStream
    method default_id (line 41) | defp default_id(dom_prefix, %{id: id} = _struct_or_map), do: dom_prefi...
    method default_id (line 43) | defp default_id(dom_prefix, other) do
    method reset (line 51) | def reset(%LiveStream{} = stream) do
    method prune (line 55) | def prune(%LiveStream{} = stream) do
    method delete_item (line 59) | def delete_item(%LiveStream{} = stream, item) do
    method delete_item_by_dom_id (line 63) | def delete_item_by_dom_id(%LiveStream{} = stream, dom_id) do
    method insert_item (line 67) | def insert_item(%LiveStream{} = stream, item, at, limit, update_only) do
    method mark_consumable (line 73) | def mark_consumable(%Phoenix.LiveView.LiveStream{} = stream) do
    method mark_consumable (line 77) | def mark_consumable(collection), do: collection
    method annotate_comprehension (line 79) | def annotate_comprehension(comprehension, %Phoenix.LiveView.LiveStream...
    method annotate_comprehension (line 92) | def annotate_comprehension(comprehension, _collection), do: comprehension

FILE: lib/phoenix_live_view/logger.ex
  class Phoenix.LiveView.Logger (line 1) | defmodule Phoenix.LiveView.Logger
    method install (line 47) | def install do
    method log_level (line 64) | defp log_level(socket) do
    method lv_mount_start (line 69) | def lv_mount_start(_event, measurement, metadata, _config) do
    method lv_mount_stop (line 93) | def lv_mount_stop(_event, measurement, metadata, _config) do
    method lv_handle_params_start (line 111) | def lv_handle_params_start(_event, measurement, metadata, _config) do
    method lv_handle_params_stop (line 132) | def lv_handle_params_stop(_event, measurement, metadata, _config) do
    method lv_handle_event_start (line 150) | def lv_handle_event_start(_event, measurement, metadata, _config) do
    method lv_handle_event_stop (line 173) | def lv_handle_event_stop(_event, measurement, metadata, _config) do
    method lc_handle_event_start (line 191) | def lc_handle_event_start(_event, measurement, metadata, _config) do
    method lc_handle_event_stop (line 215) | def lc_handle_event_stop(_event, measurement, metadata, _config) do

FILE: lib/phoenix_live_view/plug.ex
  class Phoenix.LiveView.Plug (line 1) | defmodule Phoenix.LiveView.Plug
    method call (line 10) | def call(%Plug.Conn{private: %{phoenix_live_view: {view, opts, live_se...
    method live_session (line 21) | defp live_session(opts, conn) do
    method put_root_layout_from_router (line 34) | defp put_root_layout_from_router(conn, extra) do

FILE: lib/phoenix_live_view/renderer.ex
  class Phoenix.LiveView.Renderer (line 1) | defmodule Phoenix.LiveView.Renderer
    method template_filename (line 55) | defp template_filename(module) do
    method to_rendered (line 66) | def to_rendered(socket, view) do
    method render_assigns (line 121) | defp render_assigns(%Socket{assigns: assigns} = socket) do
    method check_rendered! (line 126) | defp check_rendered!(%Rendered{} = rendered, _view), do: rendered
    method check_rendered! (line 128) | defp check_rendered!(other, view) do
    method layout (line 141) | defp layout(socket, view) do

FILE: lib/phoenix_live_view/route.ex
  class Phoenix.LiveView.Route (line 1) | defmodule Phoenix.LiveView.Route
    method container (line 17) | def container(%Route{} = route) do
    method live_link_info! (line 25) | def live_link_info!(%Socket{router: nil}, view, _uri) do
    method live_link_info! (line 31) | def live_link_info!(%Socket{} = socket, view, uri) do
    method strip_segments (line 92) | defp strip_segments([head | tail1], [head | tail2]), do: strip_segment...
    method strip_segments (line 93) | defp strip_segments([], tail2), do: tail2
    method strip_segments (line 94) | defp strip_segments(_, _), do: nil

FILE: lib/phoenix_live_view/router.ex
  class Phoenix.LiveView.Router (line 1) | defmodule Phoenix.LiveView.Router
    method __live_session__ (line 228) | def __live_session__(module, opts, name) do
    method validate_live_session_opts (line 316) | defp validate_live_session_opts(invalid, _module, name) do
    method fetch_live_flash (line 342) | def fetch_live_flash(%Plug.Conn{} = conn, _opts \\ []) do
    method validate_live_opts! (line 398) | defp validate_live_opts!(opts) do
    method inferred_as (line 441) | defp inferred_as(live_view, as, nil), do: {as || :live, live_view}
    method inferred_as (line 443) | defp inferred_as(live_view, nil, action) do
    method inferred_as (line 462) | defp inferred_as(_live_view, as, action), do: {as, action}
    method cookie_flash (line 464) | defp cookie_flash(%Plug.Conn{cookies: %{@cookie_key => token}} = conn) do
    method cookie_flash (line 476) | defp cookie_flash(%Plug.Conn{} = conn), do: {conn, nil}

FILE: lib/phoenix_live_view/session.ex
  class Phoenix.LiveView.Session (line 1) | defmodule Phoenix.LiveView.Session
    method main? (line 17) | def main?(%Session{} = session), do: session.router != nil and session...
    method authorize_root_redirect (line 19) | def authorize_root_redirect(%Session{} = session, %Route{} = route) do
    method verify_session (line 58) | def verify_session(endpoint, topic, session_token, static_token) do
    method verify_topic (line 83) | defp verify_topic("lv:" <> session_id, session_id), do: :ok
    method verify_topic (line 84) | defp verify_topic(_topic, _session_id), do: {:error, :invalid}
    method verify_static_token (line 86) | defp verify_static_token(_endpoint, _id, nil), do: {:ok, %{assign_new:...
    method verify_static_token (line 88) | defp verify_static_token(endpoint, id, token) do

FILE: lib/phoenix_live_view/socket.ex
  class Phoenix.LiveView.Socket (line 14) | defmodule Phoenix.LiveView.Socket
    class Phoenix.LiveView.Socket.AssignsNotInSocket (line 1) | defmodule Phoenix.LiveView.Socket.AssignsNotInSocket
    method connect (line 98) | def connect(_params, %Phoenix.Socket{} = socket, connect_info) do
    method id (line 103) | def id(socket), do: socket.private.connect_info[:session]["live_socket...

FILE: lib/phoenix_live_view/static.ex
  class Phoenix.LiveView.ReloadError (line 1) | defmodule Phoenix.LiveView.ReloadError
  class Phoenix.LiveView.Static (line 5) | defmodule Phoenix.LiveView.Static
    method token_vsn (line 15) | def token_vsn, do: @token_vsn
    method render (line 24) | def render(_, %{content: content}) do
    method verify_token (line 31) | def verify_token(endpoint, token) do
    method live_session (line 40) | defp live_session(%Plug.Conn{} = conn) do
    method load_session (line 47) | defp load_session(conn_or_socket_session, opts) do
    method validate_session (line 53) | defp validate_session(session) do
    method maybe_get_session (line 62) | defp maybe_get_session(conn) do
    method maybe_put_live_layout (line 68) | defp maybe_put_live_layout(private, %{extra: %{layout: layout}}) do
    method maybe_put_live_layout (line 72) | defp maybe_put_live_layout(private, _live_session) do
    method render (line 89) | def render(%Plug.Conn{} = conn, view, opts) do
    method do_render (line 124) | defp do_render(%Plug.Conn{} = conn, endpoint, view, opts) do
    method nested_render (line 190) | def nested_render(
    method disconnected_nested_render (line 235) | defp disconnected_nested_render(parent, socket, view, container, opts,...
    method connected_nested_render (line 269) | defp connected_nested_render(parent, socket, view, container, opts, st...
    method to_rendered_content_tag (line 287) | defp to_rendered_content_tag(socket, tag, view, attrs) do
    method content_tag (line 296) | defp content_tag(tag, attrs, content) do
    method load_live! (line 302) | defp load_live!(view_or_component, kind) do
    method lifecycle (line 312) | defp lifecycle(%{lifecycle: lifecycle}, %{extra: %{on_mount: on_mount}...
    method lifecycle (line 316) | defp lifecycle(%{lifecycle: lifecycle}, _) do
    method call_mount_and_handle_params! (line 320) | defp call_mount_and_handle_params!(socket, view, session, params, uri) do
    method mount_handle_params (line 338) | defp mount_handle_params(%Socket{redirected: mount_redir} = socket, vi...
    method sign_root_session (line 357) | defp sign_root_session(%Socket{} = socket, router, view, session, live...
    method sign_nested_session (line 377) | defp sign_nested_session(%Socket{} = parent, %Socket{} = child, view, ...
    method sign_static_token (line 393) | defp sign_static_token(%Socket{id: id, endpoint: endpoint} = socket) do
    method sign_token (line 405) | def sign_token(endpoint, data) do
    method container (line 409) | defp container(%{container: {tag, attrs}}, opts) do
    method assign_new_keys (line 416) | defp assign_new_keys(socket) do

FILE: lib/phoenix_live_view/tag_engine.ex
  class Phoenix.LiveView.TagEngine (line 1) | defmodule Phoenix.LiveView.TagEngine
    method compile (line 36) | def compile(source, options) do
    method __assigns__ (line 204) | def __assigns__(assigns, key, parent_changed) do
    method __unmatched_let__! (line 222) | def __unmatched_let__!(pattern, value) do
    method init (line 241) | def init(opts) do
    method handle_body (line 269) | def handle_body(state) do
    method handle_end (line 298) | def handle_end(_state) do
    method handle_begin (line 303) | def handle_begin(_state) do
    method handle_text (line 308) | def handle_text(state, _meta, _text) do
    method handle_expr (line 313) | def handle_expr(state, _marker, _expr) do

FILE: lib/phoenix_live_view/tag_engine/compiler.ex
  class Phoenix.LiveView.TagEngine.Compiler (line 1) | defmodule Phoenix.LiveView.TagEngine.Compiler
    method compile (line 19) | def compile(%Parser{nodes: nodes, directives: directives}, opts) do
    method handle_node (line 88) | defp handle_node({:text, "", _meta}, substate, state) do
    method handle_node (line 92) | defp handle_node({:text, text, _meta}, substate, state) do
    method handle_node (line 98) | defp handle_node({:body_expr, expr, %{line: line, column: column}}, su...
    method handle_node (line 105) | defp handle_node({:eex, expr, %{opt: opt, line: line, column: column}}...
    method handle_node (line 118) | defp handle_node(
    method handle_node (line 229) | defp handle_node({:self_close, :tag, name, attrs, meta}, substate, sta...
    method handle_node (line 243) | defp handle_node({:self_close, :slot, slot_name, attrs, meta}, substat...
    method handle_node (line 263) | defp handle_node({:self_close, :local_component, name, attrs, meta}, s...
    method handle_node (line 293) | defp handle_node({:self_close, :remote_component, name, attrs, meta}, ...
    method handle_node (line 323) | defp handle_node({:block, :tag, name, attrs, children, meta, close_met...
    method handle_node (line 337) | defp handle_node({:block, :slot, slot_name, attrs, children, meta, clo...
    method handle_node (line 372) | defp handle_node(
    method handle_node (line 411) | defp handle_node(
    method all_spaces? (line 452) | defp all_spaces?(children) do
    method insert_quoted (line 462) | defp insert_quoted({:__EEX__, _, [key]}, quoted) do
    method insert_quoted (line 467) | defp insert_quoted({left, meta, right}, quoted) do
    method insert_quoted (line 471) | defp insert_quoted({left, right}, quoted) do
    method insert_quoted (line 479) | defp insert_quoted(other, _quoted) do
    method handle_tag_and_attrs (line 485) | defp handle_tag_and_attrs(name, attrs, suffix, meta, substate, state) do
    method maybe_add_phx_loc (line 496) | defp maybe_add_phx_loc(text, %{caller: caller}, meta) do
    method maybe_add_root_tag_attributes (line 504) | defp maybe_add_root_tag_attributes(text, %{local_root?: true} = state,...
    method maybe_add_root_tag_attributes (line 520) | defp maybe_add_root_tag_attributes(text, _state, _meta), do: text
    method assign? (line 522) | defp assign?({:@, _, [_]}), do: true
    method assign? (line 523) | defp assign?({{:., _, [lhs, _rhs]}, _, []}), do: assign?(lhs)
    method assign? (line 524) | defp assign?(_), do: false
    method handle_tag_attrs (line 526) | defp handle_tag_attrs(meta, attrs, substate, state) do
    method handle_tag_expr_attrs (line 564) | defp handle_tag_expr_attrs(meta, ast, substate, state) do
    method parse_expr! (line 596) | defp parse_expr!({:expr, value, %{line: line, column: col}}, file) do
    method literal_keys? (line 603) | defp literal_keys?([]), do: true
    method literal_keys? (line 604) | defp literal_keys?(_other), do: false
    method build_self_close_component_assigns (line 608) | defp build_self_close_component_assigns(type_component, attrs, line, s...
    method build_component_assigns (line 614) | defp build_component_assigns(
    method split_component_attrs (line 661) | defp split_component_attrs(type_component, attrs, state) do
    method split_component_attr (line 673) | defp split_component_attr(
    method split_component_attr (line 685) | defp split_component_attr(
    method split_component_attr (line 724) | defp split_component_attr({":" <> _ = name, _, meta}, _state, state, {...
    method split_component_attr (line 729) | defp split_component_attr(
    method split_component_attr (line 739) | defp split_component_attr(
    method split_component_attr (line 748) | defp split_component_attr(
    method line_column (line 757) | defp line_column(%{line: line, column: column}), do: {line, column}
    method merge_component_attrs (line 759) | defp merge_component_attrs(roots, attrs, line) do
    method decompose_remote_component_tag! (line 772) | defp decompose_remote_component_tag!(tag_name, tag_meta, state) do
    method raise_if_let! (line 787) | defp raise_if_let!(let, file) do
    method build_component_clauses (line 809) | defp build_component_clauses(
    method store_component_call (line 863) | defp store_component_call(component, attr_info, slot_info, line, %{cal...
    method attrs_for_call (line 901) | defp attrs_for_call(attrs, locs) do
    method attr_type (line 907) | defp attr_type({:<<>>, _, _} = value), do: {:string, value}
    method attr_type (line 909) | defp attr_type(value = {:%{}, _, _}), do: {:map, value}
    method attr_type (line 915) | defp attr_type({:fn, _, [{:->, _, [args, _]}]}), do: {:fun, length(args)}
    method attr_type (line 916) | defp attr_type({:&, _, [{:/, _, [_, arity]}]}), do: {:fun, arity}
    method attr_type (line 919) | defp attr_type({:&, _, args}) do
    method attr_type (line 932) | defp attr_type(_value), do: :any
    method init_slots (line 936) | defp init_slots(state) do
    method add_inner_block (line 940) | defp add_inner_block({roots?, attrs, locs}, ast, tag_meta) do
    method add_slot (line 944) | defp add_slot(state, slot_name, slot_assigns, slot_info, tag_meta, spe...
    method pop_slots (line 950) | defp pop_slots(%{slots: [slots | other_slots]} = state) do
    method wrap_special_slot (line 990) | defp wrap_special_slot(special, ast) do
    method tag_slots (line 1012) | defp tag_slots({call, meta, args}, slot_info) do
    method with_special_attrs (line 1021) | defp with_special_attrs(attrs, meta, substate, state, fun) do
    method pop_special_attrs! (line 1049) | defp pop_special_attrs!(attrs, tag_meta, state) do
    method raise_if_duplicated_special_attr! (line 1071) | defp raise_if_duplicated_special_attr!({{attr, _expr, _meta}, attrs} =...
    method raise_if_duplicated_special_attr! (line 1084) | defp raise_if_duplicated_special_attr!(nil, _state), do: nil
    method handle_special_expr (line 1086) | defp handle_special_expr(tag_meta, inner_ast, state) do
    method maybe_keyed (line 1112) | defp maybe_keyed(%{key: key_expr, for: for_expr}) do
    method maybe_keyed (line 1119) | defp maybe_keyed(%{for: for_expr}), do: for_expr
    method to_location (line 1123) | defp to_location(%{line: line, column: column}), do: [line: line, colu...
    method to_location (line 1124) | defp to_location(_), do: []
    method actual_component_module (line 1126) | defp actual_component_module(env, fun) do
    method postprocess_attrs (line 1134) | defp postprocess_attrs(attrs, state) do
    method validate_tag_attrs! (line 1149) | defp validate_tag_attrs!(attrs, %{tag_name: "input"}, state) do
    method validate_tag_attrs! (line 1177) | defp validate_tag_attrs!(_attrs, _meta, _state), do: :ok
    method validate_phx_attrs! (line 1181) | defp validate_phx_attrs!(attrs, meta, state) do
    method validate_phx_attrs! (line 1192) | defp validate_phx_attrs!([], _meta, _state, _attr, _id?), do: :ok
    method validate_phx_attrs! (line 1196) | defp validate_phx_attrs!([{:root, _, _} | t], meta, state, attr, _id?),
    method validate_phx_attrs! (line 1199) | defp validate_phx_attrs!([{"id", _, _} | t], meta, state, attr, _id?),
    method validate_phx_attrs! (line 1202) | defp validate_phx_attrs!(
    method validate_phx_attrs! (line 1231) | defp validate_phx_attrs!([{"phx-update", _attrs, _} | t], meta, state,...
    method validate_phx_attrs! (line 1235) | defp validate_phx_attrs!([{"phx-hook", _, _} | t], meta, state, _attr,...
    method validate_phx_attrs! (line 1256) | defp validate_phx_attrs!([_h | t], meta, state, attr, id?),
    method validate_quoted_special_attr! (line 1259) | defp validate_quoted_special_attr!(attr, quoted_value, attr_meta, stat...
    method expand_with_line (line 1269) | defp expand_with_line(ast, line, env) do
    method raise_syntax_error! (line 1273) | defp raise_syntax_error!(message, meta, state) do
    method maybe_anno_caller (line 1281) | defp maybe_anno_caller(substate, meta, file, line, state) do
    method has_tags? (line 1296) | defp has_tags?([]), do: false
    method has_tags? (line 1298) | defp has_tags?([{:text, _, _} | rest]), do: has_tags?(rest)
    method has_tags? (line 1299) | defp has_tags?([{:body_expr, _, _} | rest]), do: has_tags?(rest)
    method has_tags? (line 1300) | defp has_tags?([{:eex, _, _} | rest]), do: has_tags?(rest)
    method has_tags? (line 1301) | defp has_tags?([{:eex_comment, _} | rest]), do: has_tags?(rest)
    method has_tags? (line 1302) | defp has_tags?([{:html_comment, _} | rest]), do: has_tags?(rest)
    method has_tags? (line 1305) | defp has_tags?([{:eex_block, _, blocks, _} | rest]) do
    method has_tags? (line 1311) | defp has_tags?([{:self_close, :slot, _, _, _} | rest]), do: has_tags?(...
    method has_tags? (line 1312) | defp has_tags?([{:block, :slot, _, _, _, _, _} | rest]), do: has_tags?...
    method has_tags? (line 1315) | defp has_tags?([{:self_close, _type, _, _, _} | _]), do: true
    method has_tags? (line 1316) | defp has_tags?([{:block, _type, _, _, _, _, _} | _]), do: true
    method debug_attributes? (line 1318) | defp debug_attributes?(caller) do

FILE: lib/phoenix_live_view/tag_engine/parser.ex
  class Phoenix.LiveView.TagEngine.Parser (line 1) | defmodule Phoenix.LiveView.TagEngine.Parser
    method parse (line 40) | def parse(source, opts \\ []) do
    method parse! (line 62) | def parse!(source, opts \\ []) do
    method tokenize (line 111) | def tokenize(source, opts) do
    method do_tokenize (line 133) | defp do_tokenize({:text, text, meta}, {tokens, cont}, source, %{
    method do_tokenize (line 143) | defp do_tokenize({:comment, text, meta}, {tokens, cont}, _contents, op...
    method do_tokenize (line 164) | defp do_tokenize(_node, acc, _contents, _opts) do
    method maybe_trim_eex (line 168) | defp maybe_trim_eex(string, true), do: String.trim(string)
    method maybe_trim_eex (line 169) | defp maybe_trim_eex(string, _), do: string
    method to_tree (line 268) | defp to_tree([], buffer, [], state) do
    method to_tree (line 272) | defp to_tree(
    method to_tree (line 282) | defp to_tree([{:text, text, meta} | tokens], buffer, stack, state) do
    method to_tree (line 289) | defp to_tree([{:body_expr, value, meta} | tokens], buffer, stack, stat...
    method to_tree (line 318) | defp to_tree(
    method to_tree (line 342) | defp to_tree([{:slot, name, _attrs, meta} | _tokens], _buffer, _stack,...
    method to_tree (line 349) | defp to_tree(
    method to_tree (line 380) | defp to_tree(
    method to_tree (line 400) | defp to_tree(
    method to_tree (line 416) | defp to_tree([{:close, _type, name, meta} | _tokens], _buffer, [], sta...
    method to_tree (line 425) | defp to_tree([{:eex_comment, text, _meta} | tokens], buffer, stack, st...
    method to_tree (line 429) | defp to_tree([{:eex, :start_expr, expr, meta} | tokens], buffer, stack...
    method to_tree (line 433) | defp to_tree(
    method to_tree (line 449) | defp to_tree(
    method to_tree (line 465) | defp to_tree(
    method to_tree (line 475) | defp to_tree(
    method to_tree (line 486) | defp to_tree(
    method to_tree (line 496) | defp to_tree([{:eex, _type, expr, meta} | tokens], buffer, stack, stat...
    method extract_special_attrs (line 502) | defp extract_special_attrs(attrs) do
    method prune_text (line 507) | defp prune_text([{:text, text, meta} | tokens]) do
    method prune_text (line 511) | defp prune_text(tokens), do: tokens
    method process_buffer (line 515) | defp process_buffer(buffer, _state), do: buffer
    method strip_text (line 518) | defp strip_text([{:text, "", _meta} | tokens]), do: strip_text(tokens)
    method strip_text (line 520) | defp strip_text([{:text, text, meta} | tokens]) do
    method strip_text (line 530) | defp strip_text(tokens), do: tokens
    method void_tag_note (line 532) | defp void_tag_note(name, state) do
    method maybe_macro_component (line 542) | defp maybe_macro_component(meta, attrs) do
    method maybe_process_macro_component (line 609) | defp maybe_process_macro_component(tree_node, tokens, buffer, state) do
    method process_macro_component (line 614) | defp process_macro_component(tree_node, module, state) do
    method get_meta (line 658) | defp get_meta({:self_close, _type, _name, _attrs, meta}), do: meta
    method get_meta (line 659) | defp get_meta({:block, _type, _name, _attrs, _children, meta, _close_m...
    method validate_module! (line 661) | defp validate_module!(module_string, tag_meta, state) do
    method validate_directives! (line 689) | defp validate_directives!(module, directives, meta) do
    method validate_directive! (line 697) | defp validate_directive!(_module, :root_tag_attribute, nil, _), do: :ok
    method validate_directive! (line 704) | defp validate_directive!(module, :root_tag_attribute, other, meta) do
    method validate_directive! (line 715) | defp validate_directive!(module, directive, value, meta) do
    method throw_syntax_error! (line 722) | defp throw_syntax_error!(message, meta) do
    method format_type (line 726) | defp format_type(:slot), do: "slots"
    method format_type (line 727) | defp format_type(_), do: "components"

FILE: lib/phoenix_live_view/tag_engine/tokenizer.ex
  class Phoenix.LiveView.TagEngine.Tokenizer (line 1) | defmodule Phoenix.LiveView.TagEngine.Tokenizer
    method finalize (line 51) | def finalize(_tokens, file, {:comment, line, column}, source) do
    method finalize (line 57) | def finalize(tokens, _file, _cont, _source) do
    method init (line 75) | def init(indentation, file, source, tag_handler) do
    method tokenize (line 113) | def tokenize(text, meta, tokens, cont, state) do
    method handle_text (line 127) | defp handle_text("\r\n" <> rest, line, _column, buffer, acc, state) do
    method handle_text (line 131) | defp handle_text("\n" <> rest, line, _column, buffer, acc, state) do
    method handle_text (line 135) | defp handle_text("<!doctype" <> rest, line, column, buffer, acc, state...
    method handle_text (line 139) | defp handle_text("<!DOCTYPE" <> rest, line, column, buffer, acc, state...
    method handle_text (line 143) | defp handle_text("<!--" <> rest, line, column, buffer, acc, state) do
    method handle_text (line 148) | defp handle_text("</" <> rest, line, column, buffer, acc, state) do
    method handle_text (line 153) | defp handle_text("<" <> rest, line, column, buffer, acc, state) do
    method handle_text (line 158) | defp handle_text("{" <> rest, line, column, buffer, acc, %{braces: :en...
    method handle_text (line 173) | defp handle_text(<<c::utf8, rest::binary>>, line, column, buffer, acc,...
    method handle_text (line 177) | defp handle_text(<<>>, line, column, buffer, acc, state) do
    method handle_doctype (line 183) | defp handle_doctype(<<?>, rest::binary>>, line, column, buffer, acc, s...
    method handle_doctype (line 187) | defp handle_doctype("\r\n" <> rest, line, _column, buffer, acc, state) do
    method handle_doctype (line 191) | defp handle_doctype("\n" <> rest, line, _column, buffer, acc, state) do
    method handle_doctype (line 195) | defp handle_doctype(<<c::utf8, rest::binary>>, line, column, buffer, a...
    method handle_doctype (line 199) | defp handle_doctype(<<>>, line, column, _buffer, _acc, state) do
    method handle_script (line 209) | defp handle_script("</script>" <> rest, line, column, buffer, acc, sta...
    method handle_script (line 218) | defp handle_script("\r\n" <> rest, line, _column, buffer, acc, state) do
    method handle_script (line 222) | defp handle_script("\n" <> rest, line, _column, buffer, acc, state) do
    method handle_script (line 226) | defp handle_script(<<c::utf8, rest::binary>>, line, column, buffer, ac...
    method handle_script (line 230) | defp handle_script(<<>>, line, column, buffer, acc, _state) do
    method handle_style (line 236) | defp handle_style("</style>" <> rest, line, column, buffer, acc, state...
    method handle_style (line 245) | defp handle_style("\r\n" <> rest, line, _column, buffer, acc, state) do
    method handle_style (line 249) | defp handle_style("\n" <> rest, line, _column, buffer, acc, state) do
    method handle_style (line 253) | defp handle_style(<<c::utf8, rest::binary>>, line, column, buffer, acc...
    method handle_style (line 257) | defp handle_style(<<>>, line, column, buffer, acc, _state) do
    method handle_comment (line 263) | defp handle_comment(rest, line, column, buffer, acc, state) do
    method handle_comment (line 276) | defp handle_comment("\r\n" <> rest, line, _column, buffer, state) do
    method handle_comment (line 280) | defp handle_comment("\n" <> rest, line, _column, buffer, state) do
    method handle_comment (line 284) | defp handle_comment("-->" <> rest, line, column, buffer, _state) do
    method handle_comment (line 288) | defp handle_comment(<<c::utf8, rest::binary>>, line, column, buffer, s...
    method handle_comment (line 292) | defp handle_comment(<<>>, line, column, buffer, _state) do
    method handle_tag_open (line 298) | defp handle_tag_open(text, line, column, acc, state) do
    method handle_tag_close (line 324) | defp handle_tag_close(text, line, column, acc, state) do
    method handle_tag_name (line 362) | defp handle_tag_name(<<c::utf8, rest::binary>>, column, buffer) do
    method handle_tag_name (line 366) | defp handle_tag_name(<<>>, column, buffer) do
    method done_tag_name (line 370) | defp done_tag_name(_text, _column, []) do
    method done_tag_name (line 374) | defp done_tag_name(text, column, buffer) do
    method handle_maybe_tag_open_end (line 380) | defp handle_maybe_tag_open_end("\r\n" <> rest, line, _column, acc, sta...
    method handle_maybe_tag_open_end (line 384) | defp handle_maybe_tag_open_end("\n" <> rest, line, _column, acc, state...
    method handle_maybe_tag_open_end (line 393) | defp handle_maybe_tag_open_end("/>" <> rest, line, column, acc, state) do
    method handle_maybe_tag_open_end (line 398) | defp handle_maybe_tag_open_end(">" <> rest, line, column, acc, state) do
    method handle_maybe_tag_open_end (line 411) | defp handle_maybe_tag_open_end("{" <> rest, line, column, acc, state) do
    method handle_maybe_tag_open_end (line 415) | defp handle_maybe_tag_open_end(<<>>, line, column, _acc, state) do
    method handle_maybe_tag_open_end (line 444) | defp handle_maybe_tag_open_end(text, line, column, acc, state) do
    method handle_attribute (line 450) | defp handle_attribute(text, line, column, acc, state) do
    method maybe_warn_missing_attr_space (line 474) | defp maybe_warn_missing_attr_space(nil, _text, _line, _column, _state)...
    method maybe_warn_missing_attr_space (line 486) | defp maybe_warn_missing_attr_space(_value, _text, _line, _column, _sta...
    method script_or_style? (line 489) | defp script_or_style?(_), do: false
    method handle_root_attribute (line 493) | defp handle_root_attribute(text, line, column, acc, state) do
    method handle_attr_name (line 509) | defp handle_attr_name(<<"}"::utf8, _rest::binary>>, column, _buffer) do
    method handle_attr_name (line 528) | defp handle_attr_name(<<c::utf8, rest::binary>>, column, buffer) do
    method handle_attr_name (line 532) | defp handle_attr_name(<<>>, column, _buffer) do
    method handle_maybe_attr_value (line 538) | defp handle_maybe_attr_value("\r\n" <> rest, line, _column, state) do
    method handle_maybe_attr_value (line 542) | defp handle_maybe_attr_value("\n" <> rest, line, _column, state) do
    method handle_maybe_attr_value (line 551) | defp handle_maybe_attr_value("=" <> rest, line, column, state) do
    method handle_maybe_attr_value (line 555) | defp handle_maybe_attr_value(text, line, column, _state) do
    method handle_attr_value_begin (line 561) | defp handle_attr_value_begin("\r\n" <> rest, line, _column, state) do
    method handle_attr_value_begin (line 565) | defp handle_attr_value_begin("\n" <> rest, line, _column, state) do
    method handle_attr_value_begin (line 574) | defp handle_attr_value_begin("\"" <> rest, line, column, state) do
    method handle_attr_value_begin (line 578) | defp handle_attr_value_begin("'" <> rest, line, column, state) do
    method handle_attr_value_begin (line 582) | defp handle_attr_value_begin("{" <> rest, line, column, state) do
    method handle_attr_value_begin (line 586) | defp handle_attr_value_begin(_text, line, column, state) do
    method handle_attr_value_quote (line 597) | defp handle_attr_value_quote("\r\n" <> rest, delim, line, _column, buf...
    method handle_attr_value_quote (line 602) | defp handle_attr_value_quote("\n" <> rest, delim, line, _column, buffe...
    method handle_attr_value_quote (line 607) | defp handle_attr_value_quote(<<delim, rest::binary>>, delim, line, col...
    method handle_attr_value_quote (line 612) | defp handle_attr_value_quote(<<c::utf8, rest::binary>>, delim, line, c...
    method handle_attr_value_quote (line 616) | defp handle_attr_value_quote(<<>>, delim, line, column, _buffer, state...
    method handle_attr_value_as_expr (line 641) | defp handle_attr_value_as_expr(text, line, column, state) do
    method handle_interpolation (line 655) | defp handle_interpolation("\r\n" <> rest, line, _column, buffer, brace...
    method handle_interpolation (line 659) | defp handle_interpolation("\n" <> rest, line, _column, buffer, braces,...
    method handle_interpolation (line 663) | defp handle_interpolation("}" <> rest, line, column, buffer, 0, _state...
    method handle_interpolation (line 668) | defp handle_interpolation(~S(\}) <> rest, line, column, buffer, braces...
    method handle_interpolation (line 672) | defp handle_interpolation(~S(\{) <> rest, line, column, buffer, braces...
    method handle_interpolation (line 676) | defp handle_interpolation("}" <> rest, line, column, buffer, braces, s...
    method handle_interpolation (line 680) | defp handle_interpolation("{" <> rest, line, column, buffer, braces, s...
    method handle_interpolation (line 684) | defp handle_interpolation(<<c::utf8, rest::binary>>, line, column, buf...
    method handle_interpolation (line 688) | defp handle_interpolation(<<>>, _line, _column, _buffer, _braces, _sta...
    method ok (line 701) | defp ok(acc, cont), do: {acc, cont}
    method char_or_bin (line 704) | defp char_or_bin(c), do: <<c::utf8>>
    method buffer_to_string (line 706) | defp buffer_to_string(buffer) do
    method text_to_acc (line 710) | defp text_to_acc(buffer, acc, line, column, context)
    method text_to_acc (line 712) | defp text_to_acc([], acc, _line, _column, _context),
    method text_to_acc (line 715) | defp text_to_acc(buffer, acc, line, column, context) do
    method trim_context (line 728) | defp trim_context([:comment_end, :comment_start | [_ | _] = rest]), do...
    method trim_context (line 729) | defp trim_context(rest), do: Enum.reverse(rest)
    method push_braces (line 731) | defp push_braces(%{braces: :enabled} = state), do: state
    method push_braces (line 732) | defp push_braces(%{braces: braces} = state), do: %{state | braces: bra...
    method pop_braces (line 734) | defp pop_braces(%{braces: :enabled} = state), do: state
    method pop_braces (line 735) | defp pop_braces(%{braces: 1} = state), do: %{state | braces: :enabled}
    method pop_braces (line 736) | defp pop_braces(%{braces: braces} = state), do: %{state | braces: brac...
    method put_attr (line 738) | defp put_attr([{type, name, attrs, meta} | acc], attr, attr_meta, valu...
    method normalize_tag (line 743) | defp normalize_tag([{type, name, attrs, meta} | acc], line, column, se...
    method strip_text_token_fully (line 757) | defp strip_text_token_fully(tokens) do
    method raise_syntax_error! (line 766) | defp raise_syntax_error!(message, meta, state) do
  class ParseError (line 7) | defmodule ParseError
    method message (line 12) | def message(exception) do
    method code_snippet (line 21) | def code_snippet(source, meta, indentation \\ 0) do

FILE: lib/phoenix_live_view/test/client_proxy.ex
  class Phoenix.LiveViewTest.ClientProxy (line 1) | defmodule Phoenix.LiveViewTest.ClientProxy
    method encode! (line 35) | def encode!(msg), do: msg
    method stop (line 40) | def stop(proxy_pid, reason) do
    method root_view (line 47) | def root_view(proxy_pid) do
    method report_upload_progress (line 54) | def report_upload_progress(proxy_pid, from, element, entry_ref, percen...
    method start_link (line 68) | def start_link(opts) do
    method init (line 72) | def init(opts) do
    method maybe_put_container (line 201) | defp maybe_put_container(state, %{container: container}) do
    method maybe_put_container (line 211) | defp maybe_put_container(state, %{} = _resp), do: state
    method build_client_view (line 213) | defp build_client_view(%ClientProxy{} = proxy) do
    method mount_view (line 218) | defp mount_view(state, view, url, redirect_url) do
    method start_supervised_channel (line 257) | defp start_supervised_channel(state, view, ref, url, redirect_url) do
    method put_non_nil (line 292) | defp put_non_nil(%{} = map, _key, nil), do: map
    method put_non_nil (line 293) | defp put_non_nil(%{} = map, key, val), do: Map.put(map, key, val)
    method handle_info (line 295) | def handle_info({:sync_children, topic, from}, state) do
    method handle_info (line 310) | def handle_info({:sync_render_element, operation, topic_or_element, fr...
    method handle_info (line 342) | def handle_info({:sync_render_event, topic_or_element, type, value, fr...
    method handle_info (line 448) | def handle_info(
    method handle_info (line 459) | def handle_info(
    method handle_info (line 471) | def handle_info(
    method handle_info (line 482) | def handle_info(
    method handle_info (line 493) | def handle_info(%Phoenix.Socket.Reply{ref: ref} = reply, state) do
    method handle_info (line 513) | def handle_info({:DOWN, _ref, :process, pid, reason}, state) do
    method handle_info (line 523) | def handle_info({:socket_close, pid, reason}, state) do
    method handle_info (line 533) | def handle_info({:test_error, type, message}, state) do
    method configured_test_warning (line 575) | defp configured_test_warning(type, on_error) do
    method default_test_error (line 593) | defp default_test_error(:missing_form_id), do: :warn
    method default_test_error (line 594) | defp default_test_error(_), do: :raise
    method handle_call (line 596) | def handle_call({:upload_progress, from, %Element{} = el, entry_ref, p...
    method handle_call (line 607) | def handle_call(:page_title, _from, %{page_title: :unset} = state) do
    method handle_call (line 612) | def handle_call(:page_title, _from, state) do
    method handle_call (line 616) | def handle_call(:url, _from, state) do
    method handle_call (line 620) | def handle_call(:html, _from, state) do
    method handle_call (line 624) | def handle_call(:root_view, _from, state) do
    method handle_call (line 628) | def handle_call({:live_children, topic}, from, state) do
    method handle_call (line 637) | def handle_call({:render_element, operation, topic_or_element}, from, ...
    method handle_call (line 647) | def handle_call({:async_pids, topic_or_element}, _from, state) do
    method handle_call (line 653) | def handle_call({:render_event, topic_or_element, type, value}, from, ...
    method handle_call (line 663) | def handle_call({:render_patch, topic, path}, from, state) do
    method handle_call (line 671) | def handle_call({:render_allow_upload, topic, ref, {entries, cid}}, fr...
    method handle_call (line 684) | def handle_call({:stop, reason}, _from, state) do
    method handle_call (line 690) | def handle_call({:sync_with_root, topic}, _from, state) do
    method handle_call (line 705) | def handle_call({:get_lazy, %Element{} = element}, _from, state) do
    method handle_call (line 711) | def handle_call({:get_lazy, id}, _from, state) do
    method ping! (line 716) | defp ping!(pid, state, fun) do
    method drop_view_by_id (line 736) | defp drop_view_by_id(state, id, reason) do
    method flush_replies (line 756) | defp flush_replies(state, pid) do
    method fetch_reply (line 763) | defp fetch_reply(state, ref) do
    method put_reply (line 767) | defp put_reply(state, ref, pid, from, callback) do
    method drop_reply (line 771) | defp drop_reply(state, ref) do
    method put_child (line 785) | defp put_child(state, %ClientProxy{} = parent, id, session) do
    method drop_child (line 791) | defp drop_child(state, %ClientProxy{} = parent, id, reason) do
    method verify_session (line 799) | defp verify_session(%ClientProxy{} = view) do
    method put_view (line 808) | defp put_view(state, %ClientProxy{pid: pid} = view, rendered) do
    method patch_view (line 828) | defp patch_view(state, view, child_html, streams) do
    method fetch_view_by_topic! (line 868) | defp fetch_view_by_topic!(state, topic), do: Map.fetch!(state.views, t...
    method fetch_view_by_topic (line 869) | defp fetch_view_by_topic(state, topic), do: Map.fetch(state.views, topic)
    method fetch_view_by_id (line 877) | defp fetch_view_by_id(state, id) do
    method render_reply (line 883) | defp render_reply(reply, from, state) do
    method merge_rendered (line 898) | defp merge_rendered(state, topic, %{diff: diff}), do: merge_rendered(s...
    method merge_rendered (line 900) | defp merge_rendered(%{html_tree: html_before} = state, topic, %{} = di...
    method detect_added_or_removed_children (line 926) | defp detect_added_or_removed_children(state, view, html_before, stream...
    method recursive_detect_added_or_removed_children (line 945) | defp recursive_detect_added_or_removed_children(state, view, html_befo...
    method send_patch (line 974) | defp send_patch(state, topic, %{to: to} = opts) do
    method push (line 985) | defp push(state, view, event, payload) do
    method push_with_reply (line 1001) | defp push_with_reply(state, from, view, event, payload) do
    method handle_reply (line 1007) | defp handle_reply(state, reply) do
    method push_with_callback (line 1032) | defp push_with_callback(state, from, view, event, payload, callback) do
    method build_child (line 1040) | defp build_child(%ClientProxy{ref: ref, proxy: proxy, endpoint: endpoi...
    method encode_payload (line 1061) | defp encode_payload(type, event, value),
    method proxy_topic (line 1069) | defp proxy_topic(%{proxy: {_ref, topic, _pid}}), do: topic
    method root (line 1071) | defp root(state, %ClientProxy{id: id}), do: root(state, id)
    method select_node (line 1086) | defp select_node(root, %Element{selector: selector, text_filter: nil}) do
    method select_node (line 1090) | defp select_node(root, %Element{selector: selector, text_filter: text_...
    method select_node_by_text (line 1109) | defp select_node_by_text(root, nodes, text_filter, selector) do
    method maybe_event (line 1140) | defp maybe_event(:upload_progress, node, %Element{} = element) do
    method maybe_event (line 1149) | defp maybe_event(:allow_upload, node, %Element{} = element) do
    method maybe_event (line 1158) | defp maybe_event(:hook, node, %Element{event: event} = element) do
    method maybe_event (line 1180) | defp maybe_event(:click, {"a", _, _} = node, element) do
    method maybe_event (line 1230) | defp maybe_event(type, node, element) do
    method maybe_js_decode (line 1239) | defp maybe_js_decode("[" <> _ = encoded_js), do: Phoenix.json_library(...
    method maybe_js_decode (line 1240) | defp maybe_js_decode(event), do: [["push", %{"event" => event}]]
    method maybe_js_commands (line 1242) | defp maybe_js_commands(event_or_js, root, view, node, value, dom_value...
    method maybe_enabled (line 1289) | defp maybe_enabled(type, node, element) do
    method maybe_values (line 1298) | defp maybe_values(:hook, _root, _node, _element), do: {:ok, %{}}
    method maybe_values (line 1344) | defp maybe_values(_type, _root, node, _element) do
    method deep_merge (line 1348) | defp deep_merge(%{} = target, %{} = source),
    method deep_merge (line 1351) | defp deep_merge(_target, source),
    method maybe_submitter (line 1354) | defp maybe_submitter(defaults, :submit, lazy, %Element{meta: %{submitt...
    method maybe_submitter (line 1363) | defp maybe_submitter(defaults, _, _, _), do: {:ok, defaults}
    method collect_submitter (line 1365) | defp collect_submitter(node, base, element, defaults) do
    method submitter? (line 1383) | defp submitter?(node) do
    method maybe_push_events (line 1396) | defp maybe_push_events(diff, state) do
    method maybe_push_reply (line 1407) | defp maybe_push_reply(diff, state) do
    method maybe_push_title (line 1418) | defp maybe_push_title(diff, state) do
    method fill_in_map (line 1433) | defp fill_in_map([{key, value} | rest], prefix, node, acc) do
    method fill_in_map (line 1442) | defp fill_in_map([], _prefix, _node, acc) do
    method fill_in_type (line 1446) | defp fill_in_type([{_, _} | _] = value, key, node), do: fill_in_map(va...
    method fill_in_type (line 1447) | defp fill_in_type(%_{} = value, key, node), do: fill_in_value(value, k...
    method fill_in_type (line 1448) | defp fill_in_type(%{} = value, key, node), do: fill_in_map(Map.to_list...
    method fill_in_type (line 1449) | defp fill_in_type(value, key, node), do: fill_in_value(value, key, node)
    method fill_in_value (line 1454) | defp fill_in_value(non_string_value, name, node) do
    method calendar_value (line 1492) | defp calendar_value([], %{calendar: _} = calendar_type, name, node) do
    method calendar_value (line 1510) | defp calendar_value(_, _, _, _) do
    method collect_values (line 1514) | defp collect_values(nodes, types, values) do
    method collect_values (line 1524) | defp collect_values("textarea", _node, types, values) do
    method collect_values (line 1528) | defp collect_values("input", node, types, values) do
    method collect_values (line 1539) | defp collect_values("select", node, types, values) do
    method collect_values (line 1552) | defp collect_values(_tag, _node, types, values) do
    method fill_in_name (line 1556) | defp fill_in_name("", name), do: name
    method fill_in_name (line 1557) | defp fill_in_name(prefix, name), do: prefix <> "[" <> name <> "]"
    method maybe_put_uploads (line 1559) | defp maybe_put_uploads(payload, root, %Upload{} = upload) do
    method maybe_put_uploads (line 1565) | defp maybe_put_uploads(payload, _root, nil), do: payload
    method maybe_put_cid (line 1567) | defp maybe_put_cid(payload, nil), do: payload
    method maybe_put_cid (line 1568) | defp maybe_put_cid(payload, cid), do: Map.put(payload, "cid", cid)
    method root_page_title (line 1570) | defp root_page_title(root_html) do

FILE: lib/phoenix_live_view/test/diff.ex
  class Phoenix.LiveViewTest.Diff (line 1) | defmodule Phoenix.LiveViewTest.Diff
    method merge_diff (line 14) | def merge_diff(rendered, diff) do
    method find_component (line 46) | defp find_component(cid, cdiff, old, new, cache) do
    method drop_cids (line 69) | def drop_cids(rendered, cids) do
    method deep_merge_diff (line 73) | defp deep_merge_diff(target, %{@template => template} = source),
    method deep_merge_diff (line 102) | defp deep_merge_diff(_target, %{@static => _} = source),
    method deep_merge_diff (line 105) | defp deep_merge_diff(%{} = target, %{} = source),
    method deep_merge_diff (line 108) | defp deep_merge_diff(_target, source),
    method resolve_templates (line 113) | defp resolve_templates(%{@template => template} = rendered, nil) do
    method resolve_templates (line 125) | defp resolve_templates(other, _template), do: other
    method extract_streams (line 139) | def extract_streams(_value, acc), do: acc
    method render_diff (line 143) | def render_diff(rendered) do
    method add_cid_attr (line 151) | defp add_cid_attr(cid, [head | tail]) do

FILE: lib/phoenix_live_view/test/dom.ex
  class Phoenix.LiveViewTest.DOM (line 1) | defmodule Phoenix.LiveViewTest.DOM
    method ensure_loaded! (line 13) | def ensure_loaded! do
    method parse_document (line 25) | def parse_document(html, error_reporter \\ nil) do
    method parse_fragment (line 34) | def parse_fragment(html, error_reporter \\ nil) do
    method run_checks (line 42) | defp run_checks(lazydoc, error_reporter) do
    method detect_duplicate_ids (line 52) | defp detect_duplicate_ids(lazydoc, error_reporter) do
    method detect_forms_without_id (line 72) | defp detect_forms_without_id(lazydoc, error_reporter) do
    method all (line 89) | def all(lazy, selector) do
    method maybe_one (line 93) | def maybe_one(lazy, selector, type \\ :selector) do
    method targets_from_node (line 113) | def targets_from_node(lazy, node) do
    method targets_from_selector (line 121) | def targets_from_selector(lazy, selector)
    method targets_from_selector (line 123) | def targets_from_selector(_lazy, nil), do: [nil]
    method component_id (line 148) | defp component_id(tree) do
    method tag (line 153) | def tag(node) do
    method attribute (line 160) | def attribute(node, key) do
    method to_text (line 171) | def to_text(node) do
    method by_id! (line 181) | def by_id!(lazy, id) do
    method to_lazy (line 193) | def to_lazy(tree), do: LazyHTML.from_tree(tree)
    method escape_css_chars (line 218) | defp escape_css_chars(chars) do
    method escape_css_chars (line 227) | defp escape_css_chars([], _, acc), do: Enum.reverse(acc)
    method escape_css_chars (line 229) | defp escape_css_chars([char | rest], index, acc) do
    method find_static_views (line 271) | def find_static_views(lazy) do
    method all_value_inputs (line 280) | def all_value_inputs({"form", attrs, _} = form, root) do
    method collect_form_values (line 298) | def collect_form_values(form, root, done \\ &Query.decode_done/1) do
    method collect_input_values (line 305) | def collect_input_values(node) do
    method form_defaults (line 309) | defp form_defaults(node, acc) do
    method form_defaults (line 321) | defp form_defaults("select", node, name, acc) do
    method form_defaults (line 345) | defp form_defaults("textarea", node, name, acc) do
    method form_defaults (line 355) | defp form_defaults("input", node, name, acc) do
    method default_value (line 375) | def default_value("checkbox"), do: "on"
    method default_value (line 376) | def default_value(_type), do: ""
    method valid_display_size (line 378) | defp valid_display_size(node) do
    method filtered_inputs (line 387) | defp filtered_inputs(nodes) do

FILE: lib/phoenix_live_view/test/live_view_test.ex
  class Phoenix.LiveViewTest (line 1) | defmodule Phoenix.LiveViewTest
    method __isolated__ (line 321) | def __isolated__(conn, endpoint, live_view, opts) do
    method __live__ (line 330) | def __live__(%Plug.Conn{state: state, status: status} = conn, _path = ...
    method __live__ (line 365) | def __live__(conn, path, opts) do
    method connect_from_static_token (line 369) | defp connect_from_static_token(
    method connect_from_static_token (line 411) | defp connect_from_static_token(%Plug.Conn{status: 200}, _path, _opts) do
    method prune_conn (line 420) | defp prune_conn(conn) do
    method error_redirect_conn (line 424) | defp error_redirect_conn(conn) do
    method error_redirect_key (line 437) | defp error_redirect_key(%{private: %{phoenix_live_redirect: true}}), d...
    method error_redirect_key (line 438) | defp error_redirect_key(_), do: :redirect
    method start_proxy (line 440) | defp start_proxy(path, %{} = opts) do
    method fetch_test_supervisor! (line 473) | defp fetch_test_supervisor!() do
    method maybe_get_session (line 480) | defp maybe_get_session(%Plug.Conn{} = conn) do
    method rebuild_path (line 488) | defp rebuild_path(%Plug.Conn{request_path: request_path, query_string:...
    method rebuild_path (line 491) | defp rebuild_path(%Plug.Conn{request_path: request_path, query_string:...
    method rendered_to_diff_string (line 563) | defp rendered_to_diff_string(rendered, socket) do
    method rendered_to_string (line 587) | def rendered_to_string(rendered) do
    method render_click (line 625) | def render_click(element, value \\ %{})
    method render_click (line 626) | def render_click(%Element{} = element, value), do: render_event(elemen...
    method render_click (line 627) | def render_click(view, event), do: render_click(view, event, %{})
    method render_click (line 642) | def render_click(view, event, value) do
    method put_submitter (line 667) | def put_submitter(form, element_or_selector)
    method put_submitter (line 673) | def put_submitter(%Element{} = form, %Element{} = submitter) do
    method render_submit (line 762) | def render_submit(element, value \\ %{})
    method render_submit (line 763) | def render_submit(%Element{} = element, value), do: render_event(eleme...
    method render_submit (line 764) | def render_submit(view, event), do: render_submit(view, event, %{})
    method render_submit (line 778) | def render_submit(view, event, value) do
    method render_change (line 819) | def render_change(element, value \\ %{})
    method render_change (line 820) | def render_change(%Element{} = element, value), do: render_event(eleme...
    method render_change (line 821) | def render_change(view, event), do: render_change(view, event, %{})
    method render_change (line 835) | def render_change(view, event, value) do
    method render_keydown (line 859) | def render_keydown(element, value \\ %{})
    method render_keydown (line 861) | def render_keydown(%Element{} = element, value),
    method render_keydown (line 864) | def render_keydown(view, event), do: render_keydown(view, event, %{})
    method render_keydown (line 879) | def render_keydown(view, event, value) do
    method render_keyup (line 903) | def render_keyup(element, value \\ %{})
    method render_keyup (line 904) | def render_keyup(%Element{} = element, value), do: render_event(elemen...
    method render_keyup (line 905) | def render_keyup(view, event), do: render_keyup(view, event, %{})
    method render_keyup (line 920) | def render_keyup(view, event, value) do
    method render_blur (line 945) | def render_blur(element, value \\ %{})
    method render_blur (line 946) | def render_blur(%Element{} = element, value), do: render_event(element...
    method render_blur (line 947) | def render_blur(view, event), do: render_blur(view, event, %{})
    method render_blur (line 962) | def render_blur(view, event, value) do
    method render_focus (line 987) | def render_focus(element, value \\ %{})
    method render_focus (line 988) | def render_focus(%Element{} = element, value), do: render_event(elemen...
    method render_focus (line 989) | def render_focus(view, event), do: render_focus(view, event, %{})
    method render_focus (line 1004) | def render_focus(view, event, value) do
    method render_hook (line 1030) | def render_hook(view_or_element, event, value \\ %{})
    method render_hook (line 1032) | def render_hook(%Element{} = element, event, value) do
    method render_hook (line 1036) | def render_hook(view, event, value) do
    method render_async (line 1062) | def render_async(
    method live_children (line 1116) | def live_children(%View{} = parent) do
    method find_live_child (line 1129) | def find_live_child(%View{} = parent, child_id) do
    method has_element? (line 1143) | def has_element?(%Element{} = element) do
    method render (line 1181) | def render(view_or_element) do
    method with_target (line 1201) | def with_target(%View{} = view, target) do
    method render_tree (line 1205) | defp render_tree(%View{} = view) do
    method render_tree (line 1209) | defp render_tree(%Element{} = element) do
    method render_tree (line 1213) | defp render_tree(view_or_element, topic_or_element) do
    method call (line 1217) | defp call(view_or_element, tuple) do
    method __file_input__ (line 1332) | def __file_input__(view, selector, name, entries, builder) do
    method find_cid! (line 1362) | defp find_cid!(view, selector) do
    method start_upload_client (line 1375) | defp start_upload_client(builder, view, form_selector, name, entries, ...
    method start_external_upload_client (line 1382) | defp start_external_upload_client(view, form_selector, name, entries, ...
    method page_title (line 1397) | def page_title(view) do
    method assert_patch (line 1423) | def assert_patch(view, timeout \\ Application.fetch_env!(:ex_unit, :as...
    method assert_patched (line 1472) | def assert_patched(view, to) do
    method refute_patched (line 1487) | def refute_patched(view) do
    method assert_redirect (line 1513) | def assert_redirect(view, timeout \\ Application.fetch_env!(:ex_unit, ...
    method assert_redirected (line 1560) | def assert_redirected(view, to) do
    method assert_navigation (line 1564) | defp assert_navigation(view, kind, to, timeout) do
    method refute_redirected (line 1601) | def refute_redirected(view) do
    method refute_navigation (line 1609) | defp refute_navigation(view = %{proxy: {ref, topic, _}}, kind, to) do
    method flush_navigation (line 1625) | defp flush_navigation(ref, topic, last) do
    method open_browser (line 1649) | def open_browser(view_or_element, open_fun \\ &open_with_system_cmd/1)
    method maybe_wrap_html (line 1662) | defp maybe_wrap_html(view_or_element, content) do
    method maybe_prefix_static_path (line 1696) | defp maybe_prefix_static_path(attrs, nil), do: attrs
    method maybe_prefix_static_path (line 1698) | defp maybe_prefix_static_path(attrs, static_path) do
    method prefix_static_path (line 1706) | defp prefix_static_path(<<"//" <> _::binary>> = url, _prefix), do: url
    method prefix_static_path (line 1708) | defp prefix_static_path(<<"/" <> _::binary>> = path, prefix),
    method prefix_static_path (line 1711) | defp prefix_static_path(url, _), do: url
    method write_tmp_html_file (line 1713) | defp write_tmp_html_file(html) do
    method open_with_system_cmd (line 1720) | defp open_with_system_cmd(path) do
    method __follow_redirect__ (line 1873) | def __follow_redirect__(conn, endpoint, expected_to, %{to: to} = opts) do
    method ensure_signed_flash (line 1893) | defp ensure_signed_flash(_, flash), do: flash
    method live_redirect (line 1913) | def live_redirect(view, opts) do
    method __live_redirect__ (line 1918) | def __live_redirect__(%View{} = view, opts, token_func \\ & &1) do
    method __render_trigger_submit__ (line 2038) | def __render_trigger_submit__(%Element{} = form, name, required_attr, ...
    method proxy_pid (line 2069) | defp proxy_pid(%{proxy: {_ref, _topic, pid}}), do: pid
    method proxy_topic (line 2071) | defp proxy_topic(%{proxy: {_ref, topic, _pid}}), do: topic
    method render_upload (line 2122) | def render_upload(%Upload{} = upload, entry_name, percent \\ 100) do
    method preflight_upload (line 2169) | def preflight_upload(%Upload{} = upload) do
    method render_chunk (line 2180) | defp render_chunk(upload, entry_name, percent) do
    method sync_with_root! (line 2225) | defp sync_with_root!(%View{} = view) do
    method configured_test_warning (line 2232) | def configured_test_warning(type) do

FILE: lib/phoenix_live_view/test/structs.ex
  class Phoenix.LiveViewTest.View (line 1) | defmodule Phoenix.LiveViewTest.View
  class Phoenix.LiveViewTest.Element (line 25) | defmodule Phoenix.LiveViewTest.Element
  class Phoenix.LiveViewTest.Upload (line 46) | defmodule Phoenix.LiveViewTest.Upload
    method new (line 71) | def new(pid, %Phoenix.LiveViewTest.View{} = view, form_selector, name,...
    method populate_entry (line 84) | defp populate_entry(%{} = entry) do

FILE: lib/phoenix_live_view/test/tree_dom.ex
  class Phoenix.LiveViewTest.TreeDOM (line 1) | defmodule Phoenix.LiveViewTest.TreeDOM
    method filter (line 14) | def filter(node, fun) do
    method reverse_filter (line 21) | def reverse_filter(tree, fun) do
    method tag (line 30) | def tag({name, _attrs, _children}), do: name
    method tag (line 31) | def tag(_), do: nil
    method attribute (line 36) | def attribute(node, key) do
    method to_html (line 50) | def to_html(html) do
    method to_text (line 57) | def to_text(tree, trim \\ true) do
    method node_to_text (line 72) | defp node_to_text({_tag, _attrs, content}), do: node_to_text(content)
    method node_to_text (line 75) | defp node_to_text(_), do: []
    method by_id! (line 80) | def by_id!(tree, id) do
    method child_nodes (line 100) | def child_nodes(tree) do
    method attrs (line 111) | def attrs(tree) do
    method inner_html! (line 121) | def inner_html!(tree, id), do: tree |> by_id!(id) |> child_nodes()
    method all_attributes (line 126) | def all_attributes(tree, name) do
    method all_values (line 137) | def all_values(tree) do
    method value_key (line 148) | defp value_key("phx-value-" <> key), do: key
    method value_key (line 149) | defp value_key("value"), do: "value"
    method value_key (line 150) | defp value_key(_), do: nil
    method do_reduce (line 163) | defp do_reduce([], acc, _fun), do: acc
    method do_reduce (line 165) | defp do_reduce([node | rest], acc, fun) do
    method do_reduce (line 170) | defp do_reduce({tag, attrs, children}, acc, fun) do
    method do_reduce (line 175) | defp do_reduce(node, acc, fun) do
    method by_id (line 190) | defp by_id(tree, id) do
    method set_attr (line 200) | def set_attr({tag, attrs, children} = _el, name, val) do
    method inspect_html (line 216) | def inspect_html(dom_node),
    method find_live_views (line 224) | def find_live_views(tree) do
    method parse_live_views_attributes (line 231) | defp parse_live_views_attributes(attributes) do
    method keyfind (line 251) | defp keyfind(list, key) do
    method remove_stream_children (line 261) | def remove_stream_children(html_tree) do
    method patch_id (line 271) | def patch_id(id, html, inner_html, streams, error_reporter \\ nil) do
    method detect_duplicate_ids (line 304) | def detect_duplicate_ids(tree, error_reporter),
    method detect_duplicate_ids (line 307) | defp detect_duplicate_ids(tree, [node | rest], ids, error_reporter) do
    method detect_duplicate_ids (line 312) | defp detect_duplicate_ids(tree, {_tag_name, _attrs, children} = node, ...
    method detect_duplicate_ids (line 334) | defp detect_duplicate_ids(_tree, _non_tag, seen_ids, _error_reporter),...
    method detect_duplicate_components (line 336) | def detect_duplicate_components(tree, cids, error_reporter) do
    method detect_forms_without_id (line 354) | defp detect_forms_without_id([_ | _] = nodes, error_reporter) do
    method detect_forms_without_id (line 358) | defp detect_forms_without_id({"form", attrs, _children} = node, error_...
    method detect_forms_without_id (line 378) | defp detect_forms_without_id({_tag_name, _attrs, children}, error_repo...
    method detect_forms_without_id (line 382) | defp detect_forms_without_id(_node, _error_reporter), do: :ok
    method component_ids (line 384) | def component_ids(id, html_tree) do
    method traverse_component_ids (line 390) | defp traverse_component_ids(current, acc) do
    method replace_root_container (line 410) | def replace_root_container(container_html, new_tag, attrs) do
    method apply_phx_update (line 439) | defp apply_phx_update("stream", html_tree, {tag, attrs, appended_child...
    method apply_phx_update (line 512) | defp apply_phx_update("ignore", html_tree, node, _streams) do
    method apply_phx_update (line 535) | defp apply_phx_update(other, _state, _node, _streams) do
    method apply_stream (line 541) | defp apply_stream(existing_children, stream) do
    method maybe_apply_stream_limit (line 560) | defp maybe_apply_stream_limit(children, _maybe_insert), do: children
    method verify_phx_update_id! (line 568) | defp verify_phx_update_id!(_type, _id, _node) do
    method apply_phx_update_children (line 572) | defp apply_phx_update_children(html_tree, id) do
    method apply_phx_update_children_id (line 579) | defp apply_phx_update_children_id(type, children) do
    method normalize_to_tree (line 593) | def normalize_to_tree(html, opts \\ []) do
    method normalize_tree (line 617) | defp normalize_tree({node_type, attributes, content}, sort_attributes?...
    method normalize_tree (line 640) | defp normalize_tree(value, _sort_attributes?, _trim_whitespace?), do: ...
    method t2h (line 652) | def t2h(template) do

FILE: lib/phoenix_live_view/test/upload_client.ex
  class Phoenix.LiveViewTest.UploadClient (line 1) | defmodule Phoenix.LiveViewTest.UploadClient
    method child_spec (line 8) | def child_spec(opts) do
    method channel_pids (line 16) | def channel_pids(%Upload{pid: pid}) do
    method fetch_allow_acknowledged (line 20) | def fetch_allow_acknowledged(%Upload{pid: pid}, entry_name) do
    method chunk (line 24) | def chunk(%Upload{pid: pid, element: element}, name, percent, proxy_pi...
    method simulate_attacker_chunk (line 32) | def simulate_attacker_chunk(%Upload{pid: pid}, name, chunk) do
    method allowed_ack (line 36) | def allowed_ack(%Upload{pid: pid, entries: entries}, ref, config, name...
    method start_link (line 40) | def start_link(opts) do
    method init (line 44) | def init(opts) do
    method handle_call (line 51) | def handle_call({:fetch_allow_acknowledged, entry_name}, _from, state) do
    method handle_call (line 59) | def handle_call(
    method handle_call (line 85) | def handle_call(:channel_pids, _from, state) do
    method handle_call (line 90) | def handle_call({:chunk, entry_name, percent, proxy_pid, element}, fro...
    method handle_call (line 94) | def handle_call({:simulate_attacker_chunk, entry_name, chunk}, _from, ...
    method build_and_join_entry (line 107) | defp build_and_join_entry(%{socket: nil} = _state, client_entry, token...
    method build_and_join_entry (line 128) | defp build_and_join_entry(state, client_entry, token) do
    method with_chunk_boundaries (line 153) | def with_chunk_boundaries(entry) do
    method chunk_upload (line 203) | defp chunk_upload(state, from, entry_name, percent, proxy_pid, element...
    method do_chunk (line 213) | defp do_chunk(%{socket: nil, cid: cid} = state, from, entry, proxy_pid...
    method do_chunk (line 229) | defp do_chunk(state, from, entry, proxy_pid, element, percent) do
    method update_entry_percent (line 272) | defp update_entry_percent(state, entry, new_percent) do
    method get_entry! (line 279) | defp get_entry!(state, name) do
    method raise_unknown_entry! (line 286) | defp raise_unknown_entry!(state, name) do
    method get_chunk_timeout (line 290) | defp get_chunk_timeout(state) do
    method handle_info (line 294) | def handle_info(:garbage_collect, state) do
    method handle_info (line 298) | def handle_info({:socket_close, _pid, reason}, state) do

FILE: lib/phoenix_live_view/test/utils.ex
  class Phoenix.LiveViewTest.Utils (line 1) | defmodule Phoenix.LiveViewTest.Utils
    method stringify (line 6) | def stringify(%Upload{}, _fun), do: %{}
    method stringify (line 8) | def stringify(%{__struct__: _} = struct, fun),
    method stringify (line 11) | def stringify(%{} = params, fun),
    method stringify (line 14) | def stringify([{_, _} | _] = params, fun),
    method stringify (line 20) | def stringify(other, fun),
    method stringify_value (line 23) | def stringify_value(other, fun), do: fun.(other)
    method stringify_kv (line 24) | def stringify_kv({k, v}, fun), do: {to_string(k), stringify(v, fun)}

FILE: lib/phoenix_live_view/upload.ex
  class Phoenix.LiveView.Upload (line 1) | defmodule Phoenix.LiveView.Upload
    method cancel_upload (line 72) | def cancel_upload(socket, name, entry_ref) do
    method maybe_cancel_uploads (line 91) | def maybe_cancel_uploads(socket) do
    method update_upload_entry_meta (line 109) | def update_upload_entry_meta(%Socket{} = socket, upload_conf_name, %Up...
    method put_entries (line 145) | def put_entries(%Socket{} = socket, %UploadConfig{} = conf, entries, c...
    method unregister_completed_entry_upload (line 159) | def unregister_completed_entry_upload(%Socket{} = socket, %UploadConfi...
    method put_upload_error (line 183) | def put_upload_error(%Socket{} = socket, conf_name, entry_ref, reason) do
    method get_upload_by_ref! (line 194) | def get_upload_by_ref!(%Socket{} = socket, config_ref) do
    method no_upload_allowed_message (line 200) | defp no_upload_allowed_message(socket) do
    method uploaded_entries (line 219) | def uploaded_entries(%Socket{} = socket, name) do
    method drop_upload_entries (line 272) | def drop_upload_entries(%Socket{} = socket, %UploadConfig{} = conf, en...
    method update_uploads (line 279) | defp update_uploads(%UploadConfig{} = new_conf, %Socket{} = socket) do
    method generate_preflight_response (line 340) | def generate_preflight_response(%Socket{} = socket, name, cid, refs) do
    method mark_preflighted (line 368) | defp mark_preflighted(socket, conf, refs) do
    method channel_preflight (line 374) | defp channel_preflight(
    method entry_errors (line 403) | defp entry_errors(%UploadConfig{} = conf, %UploadEntry{} = entry) do
    method external_preflight (line 407) | defp external_preflight(%Socket{} = socket, %UploadConfig{} = conf, en...
    method register_cid (line 442) | def register_cid(%Socket{} = socket, ref, cid) do

FILE: lib/phoenix_live_view/upload_channel.ex
  class Phoenix.LiveView.UploadChannel (line 1) | defmodule Phoenix.LiveView.UploadChannel
    method cancel (line 8) | def cancel(pid) do
    method join (line 56) | def join(_topic, auth_payload, socket) do
    method handle_in (line 95) | def handle_in("chunk", {:binary, payload}, socket) do
    method handle_info (line 122) | def handle_info({:EXIT, _pid, reason}, socket) do
    method handle_info (line 126) | def handle_info(
    method handle_info (line 134) | def handle_info(:chunk_timeout, socket) do
    method handle_call (line 139) | def handle_call(:consume_start, _from, socket) do
    method handle_call (line 148) | def handle_call(:consume_done, from, socket) do
    method handle_call (line 153) | def handle_call(:cancel, from, socket) do
    method terminate (line 171) | def terminate(_reason, socket) do
    method reschedule_chunk_timer (line 176) | defp reschedule_chunk_timer(socket) do
    method cancel_timer (line 182) | defp cancel_timer(nil = _timer, _msg), do: :ok
    method cancel_timer (line 184) | defp cancel_timer(timer, msg) do
    method write_bytes (line 196) | defp write_bytes(socket, payload) do
    method maybe_close_completed_file (line 210) | defp maybe_close_completed_file(socket) do
    method maybe_cancel_writer (line 223) | defp maybe_cancel_writer(socket) do
    method close_writer (line 236) | defp close_writer(socket, reason) do
    method garbage_collect (line 252) | defp garbage_collect(socket) do
    method file_meta (line 259) | defp file_meta(socket), do: socket.assigns.writer.meta(socket.assigns....

FILE: lib/phoenix_live_view/upload_config.ex
  class Phoenix.LiveView.UploadEntry (line 1) | defmodule Phoenix.LiveView.UploadEntry
    method put_progress (line 42) | def put_progress(%UploadEntry{} = entry, 100) do
    method put_progress (line 46) | def put_progress(%UploadEntry{} = entry, progress) do
  class Phoenix.LiveView.UploadConfig (line 51) | defmodule Phoenix.LiveView.UploadConfig
    method entry_pid (line 318) | def entry_pid(%UploadConfig{} = conf, %UploadEntry{} = entry) do
    method get_entry_by_ref (line 335) | def get_entry_by_ref(%UploadConfig{} = conf, ref) do
    method unregister_completed_external_entry (line 340) | def unregister_completed_external_entry(%UploadConfig{} = conf, entry_...
    method unregister_completed_entry (line 347) | def unregister_completed_entry(%UploadConfig{} = conf, entry_ref) do
    method registered? (line 354) | def registered?(%UploadConfig{} = conf) do
    method mark_preflighted (line 359) | def mark_preflighted(%UploadConfig{} = conf, refs) do
    method validate_accept_option (line 392) | defp validate_accept_option(accept) do
    method accept_option! (line 405) | defp accept_option!(<<"." <> extname::binary>> = ext) do
    method disallow (line 450) | def disallow(%UploadConfig{} = conf), do: %{conf | allowed?: false}
    method uploaded_entries (line 453) | def uploaded_entries(%UploadConfig{} = conf) do
    method update_entry (line 458) | def update_entry(%UploadConfig{} = conf, entry_ref, func) do
    method update_entry_meta (line 475) | def update_entry_meta(%UploadConfig{} = conf, entry_ref, %{} = meta) do
    method put_entries (line 490) | def put_entries(%UploadConfig{} = conf, entries) do
    method maybe_replace_sole_entry (line 525) | defp maybe_replace_sole_entry(%UploadConfig{max_entries: 1} = conf, ne...
    method maybe_replace_sole_entry (line 535) | defp maybe_replace_sole_entry(%UploadConfig{} = conf, _new_entries) do
    method too_many_files? (line 539) | defp too_many_files?(%UploadConfig{entries: entries, max_entries: max}...
    method cast_and_validate_entry (line 543) | defp cast_and_validate_entry(%UploadConfig{} = conf, %{"ref" => ref} =...
    method put_valid_entry (line 570) | defp put_valid_entry(%UploadConfig{} = conf, %UploadEntry{} = entry) do
    method put_invalid_entry (line 583) | defp put_invalid_entry(%UploadConfig{} = conf, %UploadEntry{} = entry,...
    method validate_max_file_size (line 605) | defp validate_max_file_size({:ok, entry}, _conf), do: {:ok, entry}
    method validate_accepted (line 607) | defp validate_accepted({:ok, %UploadEntry{} = entry}, conf) do
    method validate_accepted (line 615) | defp validate_accepted({:error, _} = error, _conf), do: error
    method accepted? (line 617) | defp accepted?(%UploadConfig{accept: :any}, %UploadEntry{}), do: true
    method accepted? (line 619) | defp accepted?(
    method recalculate_computed_fields (line 635) | defp recalculate_computed_fields(%UploadConfig{} = conf) do
    method recalculate_errors (line 639) | defp recalculate_errors(%UploadConfig{ref: ref} = conf) do
    method put_error (line 654) | def put_error(%UploadConfig{} = conf, _entry_ref, @too_many_files = re...
    method put_error (line 659) | def put_error(%UploadConfig{} = conf, entry_ref, reason) do
    method cancel_entry (line 664) | def cancel_entry(%UploadConfig{} = conf, %UploadEntry{} = entry) do
    method drop_entry (line 676) | def drop_entry(%UploadConfig{} = conf, %UploadEntry{ref: ref}) do
    method register_cid (line 694) | def register_cid(%UploadConfig{} = conf, cid) do
    method generate_uuid (line 702) | defp generate_uuid do
    method e (line 716) | defp e(0), do: ?0
    method e (line 717) | defp e(1), do: ?1
    method e (line 718) | defp e(2), do: ?2
    method e (line 719) | defp e(3), do: ?3
    method e (line 720) | defp e(4), do: ?4
    method e (line 721) | defp e(5), do: ?5
    method e (line 722) | defp e(6), do: ?6
    method e (line 723) | defp e(7), do: ?7
    method e (line 724) | defp e(8), do: ?8
    method e (line 725) | defp e(9), do: ?9
    method e (line 726) | defp e(10), do: ?a
    method e (line 727) | defp e(11), do: ?b
    method e (line 728) | defp e(12), do: ?c
    method e (line 729) | defp e(13), do: ?d
    method e (line 730) | defp e(14), do: ?e
    method e (line 731) | defp e(15), do: ?f

FILE: lib/phoenix_live_view/upload_tmp_file_writer.ex
  class Phoenix.LiveView.UploadTmpFileWriter (line 1) | defmodule Phoenix.LiveView.UploadTmpFileWriter
    method init (line 7) | def init(_opts) do
    method meta (line 15) | def meta(state) do
    method write_chunk (line 20) | def write_chunk(data, state) do
    method close (line 28) | def close(state, _reason) do

FILE: lib/phoenix_live_view/upload_writer.ex
  class Phoenix.LiveView.UploadWriter (line 1) | defmodule Phoenix.LiveView.UploadWriter

FILE: lib/phoenix_live_view/utils.ex
  class Phoenix.LiveView.Utils (line 1) | defmodule Phoenix.LiveView.Utils
    method assign (line 36) | def assign(%Socket{} = socket, key, value) do
    method force_assign (line 89) | def force_assign(%Socket{assigns: assigns} = socket, key, val) do
    method force_assign (line 96) | def force_assign(assigns, nil, key, val), do: Map.put(assigns, key, val)
    method force_assign (line 98) | def force_assign(assigns, changed, key, val) do
    method clear_changed (line 117) | def clear_changed(%Socket{private: private, assigns: assigns} = socket...
    method clear_temp (line 125) | def clear_temp(socket) do
    method changed? (line 132) | def changed?(%Socket{assigns: %{__changed__: changed}}), do: changed !...
    method changed? (line 137) | def changed?(%Socket{} = socket, assign), do: changed?(socket.assigns,...
    method changed? (line 138) | def changed?(%{__changed__: nil}, _assign), do: true
    method changed? (line 139) | def changed?(%{__changed__: changed}, assign), do: Map.has_key?(change...
    method cid (line 144) | def cid(%Socket{assigns: %{myself: %Phoenix.LiveComponent.CID{} = cid}...
    method cid (line 145) | def cid(%Socket{}), do: nil
    method configure_socket (line 150) | def configure_socket(%Socket{id: nil} = socket, private, action, flash...
    method configure_socket (line 160) | def configure_socket(%Socket{} = socket, private, action, flash, host_...
    method configure_assigns (line 165) | defp configure_assigns(assigns, action, flash) do
    method prune_uri (line 169) | defp prune_uri(:not_mounted_at_router), do: :not_mounted_at_router
    method prune_uri (line 171) | defp prune_uri(url) do
    method random_id (line 184) | def random_id do
    method post_mount_prune (line 191) | def post_mount_prune(%Socket{} = socket) do
    method normalize_layout (line 201) | def normalize_layout(false), do: false
    method normalize_layout (line 207) | def normalize_layout(other) do
    method get_flash (line 216) | def get_flash(%Socket{assigns: assigns}), do: assigns.flash
    method get_flash (line 217) | def get_flash(%{} = flash, key), do: flash[key]
    method replace_flash (line 222) | def replace_flash(%Socket{} = socket, %{} = new_flash) do
    method clear_flash (line 229) | def clear_flash(%Socket{} = socket) do
    method clear_flash (line 236) | def clear_flash(%Socket{} = socket, key) do
    method put_flash (line 247) | def put_flash(%Socket{assigns: assigns} = socket, key, msg) do
    method changed_flash (line 258) | def changed_flash(%Socket{} = socket) do
    method push_event (line 273) | def push_event(%Socket{} = socket, event, %{} = payload, opts) do
    method put_reply (line 289) | def put_reply(%Socket{} = socket, %{} = payload) do
    method get_push_events (line 296) | def get_push_events(%Socket{} = socket) do
    method get_reply (line 303) | def get_reply(%Socket{} = socket) do
    method raise_bad_mount_and_live_patch! (line 333) | def raise_bad_mount_and_live_patch!() do
    method maybe_call_live_view_mount! (line 345) | def maybe_call_live_view_mount!(%Socket{} = socket, view, params, sess...
    method maybe_call_live_component_mount! (line 374) | def maybe_call_live_component_mount!(%Socket{} = socket, component) do
    method handle_mount_result! (line 390) | defp handle_mount_result!({:ok, %Socket{} = socket}, _context) do
    method handle_mount_result! (line 395) | defp handle_mount_result!(response, {mod, fun, arity}) do
    method validate_mount_redirect! (line 403) | defp validate_mount_redirect!({:live, :patch, _}), do: raise_bad_mount...
    method validate_mount_redirect! (line 404) | defp validate_mount_redirect!(_), do: :ok
    method handle_mount_options! (line 409) | def handle_mount_options!(%Socket{} = socket, opts, {mod, fun, arity}) do
    method handle_mount_option (line 424) | defp handle_mount_option(socket, :layout, layout) do
    method handle_mount_option (line 428) | defp handle_mount_option(%Socket{} = socket, :temporary_assigns, temp_...
    method maybe_call_update! (line 486) | def maybe_call_update!(socket, component, assigns) do
    method sign_flash (line 527) | def sign_flash(endpoint_mod, %{} = flash) do
    method verify_flash (line 534) | def verify_flash(endpoint_mod, flash_token) do
    method random_encoded_bytes (line 543) | defp random_encoded_bytes do
    method drop_private (line 553) | defp drop_private(%Socket{private: private} = socket, keys) do
    method valid_destination! (line 561) | def valid_destination!(%URI{} = uri, context) do
    method valid_destination! (line 565) | def valid_destination!({:safe, to}, context) do
    method valid_destination! (line 573) | def valid_destination!(to, context) do
    method valid_string_destination! (line 581) | def valid_string_destination!(to, context) do

FILE: mix.exs
  class Phoenix.LiveView.MixProject (line 1) | defmodule Phoenix.LiveView.MixProject
    method project (line 6) | def project do
    method cli (line 31) | def cli do
    method elixirc_paths (line 35) | defp elixirc_paths(:e2e), do: ["lib", "test/support", "test/e2e/support"]
    method elixirc_paths (line 36) | defp elixirc_paths(:test), do: ["lib", "test/support"]
    method elixirc_paths (line 37) | defp elixirc_paths(_), do: ["lib"]
    method application (line 39) | d
Condensed preview — 371 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,918K chars).
[
  {
    "path": ".formatter.exs",
    "chars": 175,
    "preview": "[\n  import_deps: [:phoenix],\n  plugins: [Phoenix.LiveView.HTMLFormatter],\n  inputs: [\"*.{ex,exs}\", \"{config,lib,test}/**"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 984,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n### Environment\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 351,
    "preview": "---\nblank_issues_enabled: true\n\ncontact_links:\n  - name: Ask questions, support, and general discussions\n    url: https:"
  },
  {
    "path": ".github/extract-changelog.sh",
    "chars": 1429,
    "preview": "#!/usr/bin/env bash\n#\n# Extract changelog for a specific version from CHANGELOG.md\n#\n# Usage: ./extract-changelog.sh <ve"
  },
  {
    "path": ".github/single-file-samples/main.exs",
    "chars": 3071,
    "preview": "Application.put_env(:sample, Example.Endpoint,\n  http: [ip: {127, 0, 0, 1}, port: 5001],\n  adapter: Bandit.PhoenixAdapte"
  },
  {
    "path": ".github/single-file-samples/test.exs",
    "chars": 2515,
    "preview": "Application.put_env(:phoenix, Example.Endpoint,\n  http: [ip: {127, 0, 0, 1}, port: 5001],\n  adapter: Bandit.PhoenixAdapt"
  },
  {
    "path": ".github/workflows/assets.yml",
    "chars": 1472,
    "preview": "name: Assets\n\non:\n  push:\n    branches:\n      - main\n      - \"v*.*\"\n\njobs:\n  build:\n    runs-on: ubuntu-22.04\n    env:\n "
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 7870,
    "preview": "name: CI\n\non:\n  push:\n  pull_request:\n    branches:\n      - main\n\njobs:\n  mix_test:\n    name: mix test (OTP ${{matrix.ot"
  },
  {
    "path": ".github/workflows/docs.yml",
    "chars": 1282,
    "preview": "name: Docs\n\non:\n  push:\n    branches:\n      - main\n\npermissions:\n  contents: read\n  pages: write\n  id-token: write\n\njobs"
  },
  {
    "path": ".github/workflows/github_release.yml",
    "chars": 963,
    "preview": "name: Creates a GitHub Release\n\non:\n  push:\n    tags:\n      - \"v*\"\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    perm"
  },
  {
    "path": ".github/workflows/npm-publish.yml",
    "chars": 1024,
    "preview": "# https://docs.npmjs.com/trusted-publishers\nname: NPM Publish\n\non:\n  push:\n    tags:\n      - \"v*\"\n\npermissions:\n  id-tok"
  },
  {
    "path": ".gitignore",
    "chars": 674,
    "preview": "# The directory Mix will write compiled artifacts to.\n/_build/\n\n# If you run \"mix test --cover\", coverage assets end up "
  },
  {
    "path": ".igniter.exs",
    "chars": 367,
    "preview": "# This is a configuration file for igniter.\n# For option documentation, see https://hexdocs.pm/igniter/Igniter.Project.I"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1203,
    "preview": "# Changelog for v1.2\n\n## v1.2.0-rc.0 (Unreleased)\n\n### Enhancements\n\n* Add `phx-no-unused-field` to prevent sending `_un"
  },
  {
    "path": "LICENSE.md",
    "chars": 1071,
    "preview": "# MIT License\n\nCopyright (c) 2018 Chris McCord\n\nPermission is hereby granted, free of charge, to any person obtaining\na "
  },
  {
    "path": "README.md",
    "chars": 11148,
    "preview": "# Phoenix LiveView\n\n[![Actions Status](https://github.com/phoenixframework/phoenix_live_view/workflows/CI/badge.svg)](ht"
  },
  {
    "path": "assets/.prettierignore",
    "chars": 10,
    "preview": "js/types/\n"
  },
  {
    "path": "assets/.prettierrc",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "assets/js/phoenix_live_view/aria.js",
    "chars": 1700,
    "preview": "const ARIA = {\n  anyOf(instance, classes) {\n    return classes.find((name) => instance instanceof name);\n  },\n\n  isFocus"
  },
  {
    "path": "assets/js/phoenix_live_view/browser.js",
    "chars": 3102,
    "preview": "const Browser = {\n  canPushState() {\n    return typeof history.pushState !== \"undefined\";\n  },\n\n  dropLocal(localStorage"
  },
  {
    "path": "assets/js/phoenix_live_view/constants.js",
    "chars": 4813,
    "preview": "export const CONSECUTIVE_RELOADS = \"consecutive-reloads\";\nexport const MAX_RELOADS = 10;\nexport const RELOAD_JITTER_MIN "
  },
  {
    "path": "assets/js/phoenix_live_view/dom.js",
    "chars": 22150,
    "preview": "import {\n  CHECKABLE_INPUTS,\n  DEBOUNCE_PREV_KEY,\n  DEBOUNCE_TRIGGER,\n  FOCUSABLE_INPUTS,\n  PHX_COMPONENT,\n  PHX_VIEW_RE"
  },
  {
    "path": "assets/js/phoenix_live_view/dom_patch.js",
    "chars": 28719,
    "preview": "import {\n  PHX_COMPONENT,\n  PHX_PRUNE,\n  PHX_ROOT_ID,\n  PHX_SESSION,\n  PHX_SKIP,\n  PHX_MAGIC_ID,\n  PHX_STATIC,\n  PHX_TRI"
  },
  {
    "path": "assets/js/phoenix_live_view/dom_post_morph_restorer.js",
    "chars": 2836,
    "preview": "import { maybe } from \"./utils\";\n\nimport DOM from \"./dom\";\n\nexport default class DOMPostMorphRestorer {\n  constructor(co"
  },
  {
    "path": "assets/js/phoenix_live_view/element_ref.js",
    "chars": 5554,
    "preview": "import {\n  PHX_REF_LOADING,\n  PHX_REF_LOCK,\n  PHX_REF_SRC,\n  PHX_PENDING_REFS,\n  PHX_EVENT_CLASSES,\n  PHX_DISABLED,\n  PH"
  },
  {
    "path": "assets/js/phoenix_live_view/entry_uploader.js",
    "chars": 1992,
    "preview": "import { logError } from \"./utils\";\n\nexport default class EntryUploader {\n  constructor(entry, config, liveSocket) {\n   "
  },
  {
    "path": "assets/js/phoenix_live_view/global.d.ts",
    "chars": 28,
    "preview": "declare let LV_VSN: string;\n"
  },
  {
    "path": "assets/js/phoenix_live_view/hooks.js",
    "chars": 9552,
    "preview": "import {\n  PHX_ACTIVE_ENTRY_REFS,\n  PHX_LIVE_FILE_UPDATED,\n  PHX_PREFLIGHTED_REFS,\n  PHX_UPLOAD_REF,\n  PHX_VIEWPORT_OVER"
  },
  {
    "path": "assets/js/phoenix_live_view/index.ts",
    "chars": 11661,
    "preview": "/*\n================================================================================\nPhoenix LiveView JavaScript Client\n="
  },
  {
    "path": "assets/js/phoenix_live_view/js.js",
    "chars": 18055,
    "preview": "import DOM from \"./dom\";\nimport ARIA from \"./aria\";\n\nconst focusStack = [];\nconst default_transition_time = 200;\n\nconst "
  },
  {
    "path": "assets/js/phoenix_live_view/js_commands.ts",
    "chars": 11833,
    "preview": "import JS from \"./js\";\nimport LiveSocket from \"./live_socket\";\n\n/**\n * An encoded JS command. Use functions in the `Phoe"
  },
  {
    "path": "assets/js/phoenix_live_view/live_socket.js",
    "chars": 40357,
    "preview": "import {\n  BINDING_PREFIX,\n  CONSECUTIVE_RELOADS,\n  DEFAULTS,\n  FAILSAFE_JITTER,\n  LOADER_TIMEOUT,\n  DISCONNECTED_TIMEOU"
  },
  {
    "path": "assets/js/phoenix_live_view/live_uploader.js",
    "chars": 5221,
    "preview": "import {\n  PHX_DONE_REFS,\n  PHX_PREFLIGHTED_REFS,\n  PHX_UPLOAD_REF,\n} from \"./constants\";\n\nimport {} from \"./utils\";\n\nim"
  },
  {
    "path": "assets/js/phoenix_live_view/rendered.js",
    "chars": 16648,
    "preview": "import {\n  COMPONENTS,\n  TEMPLATES,\n  EVENTS,\n  PHX_COMPONENT,\n  PHX_VIEW_REF,\n  PHX_SKIP,\n  PHX_MAGIC_ID,\n  REPLY,\n  ST"
  },
  {
    "path": "assets/js/phoenix_live_view/upload_entry.js",
    "chars": 3998,
    "preview": "import {\n  PHX_ACTIVE_ENTRY_REFS,\n  PHX_LIVE_FILE_UPDATED,\n  PHX_PREFLIGHTED_REFS,\n} from \"./constants\";\n\nimport { chann"
  },
  {
    "path": "assets/js/phoenix_live_view/utils.js",
    "chars": 2829,
    "preview": "import { PHX_VIEW_SELECTOR } from \"./constants\";\n\nimport EntryUploader from \"./entry_uploader\";\n\nexport const logError ="
  },
  {
    "path": "assets/js/phoenix_live_view/view.js",
    "chars": 73729,
    "preview": "import {\n  BEFORE_UNLOAD_LOADER_TIMEOUT,\n  CHECKABLE_INPUTS,\n  CONSECUTIVE_RELOADS,\n  PHX_AUTO_RECOVER,\n  PHX_COMPONENT,"
  },
  {
    "path": "assets/js/phoenix_live_view/view_hook.ts",
    "chars": 15124,
    "preview": "import jsCommands, { EncodedJS, HookJSCommands } from \"./js_commands\";\nimport DOM from \"./dom\";\nimport LiveSocket from \""
  },
  {
    "path": "assets/test/browser_test.ts",
    "chars": 2087,
    "preview": "import Browser from \"phoenix_live_view/browser\";\n\ndescribe(\"Browser\", () => {\n  beforeEach(() => {\n    clearCookies();\n "
  },
  {
    "path": "assets/test/debounce_test.ts",
    "chars": 12027,
    "preview": "import DOM from \"phoenix_live_view/dom\";\n\nconst after = (time, func) => setTimeout(func, time);\n\nconst simulateInput = ("
  },
  {
    "path": "assets/test/dom_test.ts",
    "chars": 12319,
    "preview": "import DOM from \"phoenix_live_view/dom\";\nimport { appendTitle, tag } from \"./test_helpers\";\n\nconst e = (href: string) =>"
  },
  {
    "path": "assets/test/event_test.ts",
    "chars": 9724,
    "preview": "import { Socket } from \"phoenix\";\nimport LiveSocket from \"phoenix_live_view/live_socket\";\nimport View from \"phoenix_live"
  },
  {
    "path": "assets/test/globals.d.ts",
    "chars": 53,
    "preview": "declare global {\n  let LV_VSN: string;\n}\n\nexport {};\n"
  },
  {
    "path": "assets/test/hook_types_test.ts",
    "chars": 3526,
    "preview": "/**\n * Type tests for Hook and HooksOptions.\n *\n * These tests verify:\n * 1. Typed hooks with custom state and element t"
  },
  {
    "path": "assets/test/index_test.ts",
    "chars": 799,
    "preview": "import { LiveSocket, isUsedInput, ViewHook } from \"phoenix_live_view\";\nimport * as LiveSocket2 from \"phoenix_live_view/l"
  },
  {
    "path": "assets/test/integration/event_test.ts",
    "chars": 1945,
    "preview": "import { Socket } from \"phoenix\";\nimport LiveSocket from \"phoenix_live_view/live_socket\";\n\nconst stubViewPushInput = (vi"
  },
  {
    "path": "assets/test/integration/metadata_test.ts",
    "chars": 1996,
    "preview": "import { Socket } from \"phoenix\";\nimport LiveSocket from \"phoenix_live_view/live_socket\";\n\nconst stubViewPushEvent = (vi"
  },
  {
    "path": "assets/test/integration/portal_test.ts",
    "chars": 11062,
    "preview": "import { Socket } from \"phoenix\";\nimport LiveSocket from \"phoenix_live_view/live_socket\";\nimport DOMPatch from \"phoenix_"
  },
  {
    "path": "assets/test/js_test.ts",
    "chars": 51223,
    "preview": "import { Socket } from \"phoenix\";\nimport LiveSocket from \"phoenix_live_view/live_socket\";\nimport JS from \"phoenix_live_v"
  },
  {
    "path": "assets/test/live_socket_test.ts",
    "chars": 15161,
    "preview": "import { Socket } from \"phoenix\";\nimport LiveSocket from \"phoenix_live_view/live_socket\";\nimport JS from \"phoenix_live_v"
  },
  {
    "path": "assets/test/modify_root_test.ts",
    "chars": 4836,
    "preview": "import { modifyRoot } from \"phoenix_live_view/rendered\";\n\ndescribe(\"modifyRoot stripping comments\", () => {\n  test(\"star"
  },
  {
    "path": "assets/test/rendered_test.ts",
    "chars": 12667,
    "preview": "import Rendered from \"phoenix_live_view/rendered\";\nimport {\n  STATIC,\n  COMPONENTS,\n  KEYED,\n  KEYED_COUNT,\n  TEMPLATES,"
  },
  {
    "path": "assets/test/test_helpers.ts",
    "chars": 3226,
    "preview": "import View from \"phoenix_live_view/view\";\nimport { version as liveview_version } from \"../../package.json\";\n\nexport con"
  },
  {
    "path": "assets/test/tsconfig.json",
    "chars": 415,
    "preview": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"noEmit\": true,\n    \"allowJs\": true,\n    \"checkJs\": fal"
  },
  {
    "path": "assets/test/utils_test.ts",
    "chars": 1362,
    "preview": "import { Socket } from \"phoenix\";\nimport { closestPhxBinding } from \"phoenix_live_view/utils\";\nimport LiveSocket from \"p"
  },
  {
    "path": "assets/test/view_test.ts",
    "chars": 64736,
    "preview": "import { Socket } from \"phoenix\";\nimport { createHook } from \"phoenix_live_view/index\";\nimport LiveSocket from \"phoenix_"
  },
  {
    "path": "babel.config.json",
    "chars": 79,
    "preview": "{\n  \"presets\": [\n    \"@babel/preset-env\",\n    \"@babel/preset-typescript\"\n  ]\n}\n"
  },
  {
    "path": "config/config.exs",
    "chars": 1122,
    "preview": "import Config\n\nconfig :phoenix, :json_library, Jason\nconfig :phoenix, :trim_on_html_eex_engine, false\n\nif Mix.env() == :"
  },
  {
    "path": "config/dev.exs",
    "chars": 14,
    "preview": "import Config\n"
  },
  {
    "path": "config/docs.exs",
    "chars": 14,
    "preview": "import Config\n"
  },
  {
    "path": "config/e2e.exs",
    "chars": 46,
    "preview": "import Config\n\nconfig :logger, :level, :error\n"
  },
  {
    "path": "config/test.exs",
    "chars": 221,
    "preview": "import Config\n\nconfig :logger, :level, :debug\nconfig :logger, :default_handler, false\n\nconfig :phoenix_live_view, enable"
  },
  {
    "path": "eslint.config.js",
    "chars": 1531,
    "preview": "import playwright from \"eslint-plugin-playwright\"\nimport jest from \"eslint-plugin-jest\"\nimport globals from \"globals\"\nim"
  },
  {
    "path": "guides/cheatsheets/html-attrs.cheatmd",
    "chars": 9860,
    "preview": "# phx-* HTML attributes\n\nA summary of special HTML attributes used in Phoenix LiveView templates.\nEach attribute is link"
  },
  {
    "path": "guides/client/bindings.md",
    "chars": 19708,
    "preview": "# Bindings\n\nPhoenix supports DOM element bindings for client-server interaction. For\nexample, to react to a click on a b"
  },
  {
    "path": "guides/client/external-uploads.md",
    "chars": 9936,
    "preview": "# External uploads\n\n> This guide continues from the configuration started in the\n> server [Uploads guide](uploads.html)."
  },
  {
    "path": "guides/client/form-bindings.md",
    "chars": 16750,
    "preview": "# Form bindings\n\n## Form events\n\nTo handle form changes and submissions, use the `phx-change` and `phx-submit`\nevents. I"
  },
  {
    "path": "guides/client/js-interop.md",
    "chars": 21016,
    "preview": "# JavaScript interoperability\n\nTo enable LiveView client/server interaction, we instantiate a LiveSocket. For example:\n\n"
  },
  {
    "path": "guides/client/syncing-changes.md",
    "chars": 11295,
    "preview": "# Syncing changes and optimistic UIs\n\nWhen using LiveView, whenever you change the state in your LiveView process, chang"
  },
  {
    "path": "guides/introduction/welcome.md",
    "chars": 13469,
    "preview": "# Welcome\n\nWelcome to Phoenix LiveView documentation. Phoenix LiveView enables\nrich, real-time user experiences with ser"
  },
  {
    "path": "guides/server/assigns-eex.md",
    "chars": 9784,
    "preview": "# Assigns and HEEx templates\n\nAll of the data in a LiveView is stored in the socket, which is a server\nside struct calle"
  },
  {
    "path": "guides/server/deployments.md",
    "chars": 3177,
    "preview": "# Deployments and recovery\n\nOne of the questions that arise from LiveView stateful model is what considerations are nece"
  },
  {
    "path": "guides/server/error-handling.md",
    "chars": 6072,
    "preview": "# Error and exception handling\n\nAs with any other Elixir code, exceptions may happen during the LiveView\nlife-cycle. Thi"
  },
  {
    "path": "guides/server/gettext.md",
    "chars": 3799,
    "preview": "# Gettext for internationalization\n\nFor internationalization with [gettext](https://hexdocs.pm/gettext/Gettext.html),\nyo"
  },
  {
    "path": "guides/server/live-layouts.md",
    "chars": 3133,
    "preview": "# Live layouts\n\nYour LiveView applications can be made of two layouts:\n\n  * the root layout - this layout typically cont"
  },
  {
    "path": "guides/server/live-navigation.md",
    "chars": 5577,
    "preview": "# Live navigation\n\nLiveView provides functionality to allow page navigation using the\n[browser's pushState API](https://"
  },
  {
    "path": "guides/server/security-model.md",
    "chars": 12073,
    "preview": "# Security considerations\n\nLiveView begins its life-cycle as a regular HTTP request. Then a stateful\nconnection is estab"
  },
  {
    "path": "guides/server/telemetry.md",
    "chars": 8963,
    "preview": "# Telemetry\n\nLiveView currently exposes the following [`telemetry`](https://hexdocs.pm/telemetry) events:\n\n  * `[:phoeni"
  },
  {
    "path": "guides/server/uploads.md",
    "chars": 10535,
    "preview": "# Uploads\n\nLiveView supports interactive file uploads with progress for\nboth direct to server uploads as well as direct-"
  },
  {
    "path": "jest.config.js",
    "chars": 7138,
    "preview": "/*\n * For a detailed explanation regarding each configuration property, visit:\n * https://jestjs.io/docs/configuration\n "
  },
  {
    "path": "lib/mix/tasks/compile/phoenix_live_view.ex",
    "chars": 682,
    "preview": "defmodule Mix.Tasks.Compile.PhoenixLiveView do\n  @moduledoc \"\"\"\n  A LiveView compiler for HEEx macro components.\n\n  Righ"
  },
  {
    "path": "lib/mix/tasks/phoenix_live_view.upgrade.ex",
    "chars": 2245,
    "preview": "if Code.ensure_loaded?(Igniter) do\n  defmodule Mix.Tasks.PhoenixLiveView.Upgrade do\n    @moduledoc false\n\n    use Ignite"
  },
  {
    "path": "lib/phoenix_component/declarative.ex",
    "chars": 38835,
    "preview": "defmodule Phoenix.Component.Declarative do\n  @moduledoc false\n\n  ## Reserved assigns\n\n  # This list should only contain "
  },
  {
    "path": "lib/phoenix_component/macro_component.ex",
    "chars": 13647,
    "preview": "defmodule Phoenix.Component.MacroComponent do\n  @moduledoc false\n\n  #   A macro component is a special type of component"
  },
  {
    "path": "lib/phoenix_component.ex",
    "chars": 113993,
    "preview": "defmodule Phoenix.Component do\n  @moduledoc ~S'''\n  Define reusable function components with HEEx templates.\n\n  A functi"
  },
  {
    "path": "lib/phoenix_live_component.ex",
    "chars": 22061,
    "preview": "defmodule Phoenix.LiveComponent do\n  @moduledoc ~S'''\n  LiveComponents are a mechanism to compartmentalize state, markup"
  },
  {
    "path": "lib/phoenix_live_view/application.ex",
    "chars": 260,
    "preview": "defmodule Phoenix.LiveView.Application do\n  @moduledoc false\n\n  use Application\n\n  @impl true\n  def start(_type, _args) "
  },
  {
    "path": "lib/phoenix_live_view/async.ex",
    "chars": 12849,
    "preview": "defmodule Phoenix.LiveView.Async do\n  @moduledoc false\n\n  alias Phoenix.LiveView.{AsyncResult, Socket, Channel}\n\n  defp "
  },
  {
    "path": "lib/phoenix_live_view/async_result.ex",
    "chars": 3437,
    "preview": "defmodule Phoenix.LiveView.AsyncResult do\n  @moduledoc ~S'''\n  Provides a data structure for tracking the state of an as"
  },
  {
    "path": "lib/phoenix_live_view/channel.ex",
    "chars": 53644,
    "preview": "defmodule Phoenix.LiveView.Channel do\n  @moduledoc false\n  use GenServer, restart: :temporary\n\n  require Logger\n\n  alias"
  },
  {
    "path": "lib/phoenix_live_view/colocated_hook.ex",
    "chars": 7211,
    "preview": "defmodule Phoenix.LiveView.ColocatedHook do\n  @moduledoc ~S'''\n  A special HEEx `:type` that extracts [hooks](js-interop"
  },
  {
    "path": "lib/phoenix_live_view/colocated_js.ex",
    "chars": 16335,
    "preview": "defmodule Phoenix.LiveView.ColocatedJS do\n  @moduledoc ~S'''\n  A special HEEx `:type` that extracts any JavaScript code "
  },
  {
    "path": "lib/phoenix_live_view/controller.ex",
    "chars": 2380,
    "preview": "defmodule Phoenix.LiveView.Controller do\n  @moduledoc \"\"\"\n  Helpers for rendering LiveViews from a controller.\n  \"\"\"\n\n  "
  },
  {
    "path": "lib/phoenix_live_view/debug.ex",
    "chars": 4005,
    "preview": "defmodule Phoenix.LiveView.Debug do\n  @moduledoc \"\"\"\n  Functions for runtime introspection and debugging of LiveViews.\n\n"
  },
  {
    "path": "lib/phoenix_live_view/diff.ex",
    "chars": 34893,
    "preview": "defmodule Phoenix.LiveView.Diff do\n  # The diff engine is responsible for tracking the rendering state.\n  # Given that c"
  },
  {
    "path": "lib/phoenix_live_view/engine.ex",
    "chars": 48960,
    "preview": "defmodule Phoenix.LiveView.Component do\n  @moduledoc \"\"\"\n  The struct returned by components in .heex templates.\n\n  This"
  },
  {
    "path": "lib/phoenix_live_view/helpers.ex",
    "chars": 2866,
    "preview": "defmodule Phoenix.LiveView.Helpers do\n  @moduledoc false\n\n  import Phoenix.Component\n  alias Phoenix.LiveView.Socket\n\n  "
  },
  {
    "path": "lib/phoenix_live_view/html_algebra.ex",
    "chars": 20405,
    "preview": "defmodule Phoenix.LiveView.HTMLAlgebra do\n  @moduledoc false\n\n  import Inspect.Algebra, except: [format: 2]\n\n  @language"
  },
  {
    "path": "lib/phoenix_live_view/html_engine.ex",
    "chars": 7731,
    "preview": "defmodule Phoenix.LiveView.HTMLEngine do\n  @moduledoc \"\"\"\n  The HTMLEngine that powers `.heex` templates and the `~H` si"
  },
  {
    "path": "lib/phoenix_live_view/html_formatter/tag_formatter.ex",
    "chars": 2785,
    "preview": "defmodule Phoenix.LiveView.HTMLFormatter.TagFormatter do\n  @moduledoc \"\"\"\n  A behaviour for formatting specific tags.\n  "
  },
  {
    "path": "lib/phoenix_live_view/html_formatter.ex",
    "chars": 18797,
    "preview": "defmodule Phoenix.LiveView.HTMLFormatter do\n  @moduledoc \"\"\"\n  Format HEEx templates from `.heex` files or `~H` sigils.\n"
  },
  {
    "path": "lib/phoenix_live_view/igniter/upgrade_to_1_1.ex",
    "chars": 9320,
    "preview": "if Code.ensure_loaded?(Igniter) do\n  defmodule Phoenix.LiveView.Igniter.UpgradeTo1_1 do\n    @moduledoc false\n\n    def ru"
  },
  {
    "path": "lib/phoenix_live_view/js.ex",
    "chars": 42820,
    "preview": "defmodule Phoenix.LiveView.JS do\n  @moduledoc ~S'''\n  Provides commands for executing JavaScript utility operations on t"
  },
  {
    "path": "lib/phoenix_live_view/lifecycle.ex",
    "chars": 8132,
    "preview": "defmodule Phoenix.LiveView.Lifecycle do\n  @moduledoc false\n  alias Phoenix.LiveView.{Socket, Utils}\n\n  @lifecycle :lifec"
  },
  {
    "path": "lib/phoenix_live_view/live_stream.ex",
    "chars": 4685,
    "preview": "defmodule Phoenix.LiveView.LiveStream do\n  @moduledoc false\n\n  defstruct name: nil,\n            dom_id: nil,\n           "
  },
  {
    "path": "lib/phoenix_live_view/logger.ex",
    "chars": 6081,
    "preview": "defmodule Phoenix.LiveView.Logger do\n  @moduledoc \"\"\"\n  Instrumenter to handle logging of `Phoenix.LiveView` and `Phoeni"
  },
  {
    "path": "lib/phoenix_live_view/plug.ex",
    "chars": 1032,
    "preview": "defmodule Phoenix.LiveView.Plug do\n  @moduledoc false\n\n  @behaviour Plug\n\n  @impl Plug\n  def init(view) when is_atom(vie"
  },
  {
    "path": "lib/phoenix_live_view/renderer.ex",
    "chars": 3985,
    "preview": "defmodule Phoenix.LiveView.Renderer do\n  @moduledoc false\n\n  alias Phoenix.LiveView.{Rendered, Socket}\n\n  defmacro __bef"
  },
  {
    "path": "lib/phoenix_live_view/route.ex",
    "chars": 2986,
    "preview": "defmodule Phoenix.LiveView.Route do\n  @moduledoc false\n\n  alias Phoenix.LiveView.{Route, Socket}\n\n  defstruct path: nil,"
  },
  {
    "path": "lib/phoenix_live_view/router.ex",
    "chars": 16750,
    "preview": "defmodule Phoenix.LiveView.Router do\n  @moduledoc \"\"\"\n  Provides LiveView routing for Phoenix routers.\n  \"\"\"\n\n  @cookie_"
  },
  {
    "path": "lib/phoenix_live_view/session.ex",
    "chars": 2825,
    "preview": "defmodule Phoenix.LiveView.Session do\n  @moduledoc false\n  alias Phoenix.LiveView.{Session, Route, Static}\n\n  defstruct "
  },
  {
    "path": "lib/phoenix_live_view/socket.ex",
    "chars": 3753,
    "preview": "defmodule Phoenix.LiveView.Socket.AssignsNotInSocket do\n  @moduledoc false\n\n  defimpl Inspect do\n    def inspect(_, _) d"
  },
  {
    "path": "lib/phoenix_live_view/static.ex",
    "chars": 12672,
    "preview": "defmodule Phoenix.LiveView.ReloadError do\n  defexception [:message, :plug_status]\nend\n\ndefmodule Phoenix.LiveView.Static"
  },
  {
    "path": "lib/phoenix_live_view/tag_engine/compiler.ex",
    "chars": 45036,
    "preview": "defmodule Phoenix.LiveView.TagEngine.Compiler do\n  @moduledoc false\n\n  alias Phoenix.LiveView.TagEngine.Parser\n  alias P"
  },
  {
    "path": "lib/phoenix_live_view/tag_engine/parser.ex",
    "chars": 25100,
    "preview": "defmodule Phoenix.LiveView.TagEngine.Parser do\n  @moduledoc false\n\n  defstruct [:nodes, :directives]\n\n  @type t :: %__MO"
  },
  {
    "path": "lib/phoenix_live_view/tag_engine/tokenizer.ex",
    "chars": 25838,
    "preview": "defmodule Phoenix.LiveView.TagEngine.Tokenizer do\n  @moduledoc false\n  @space_chars ~c\"\\s\\t\\f\"\n  @quote_chars ~c\"\\\"'\"\n  "
  },
  {
    "path": "lib/phoenix_live_view/tag_engine.ex",
    "chars": 9054,
    "preview": "defmodule Phoenix.LiveView.TagEngine do\n  @moduledoc \"\"\"\n  Building blocks for tag based `Phoenix.Template.Engine`s.\n\n  "
  },
  {
    "path": "lib/phoenix_live_view/test/client_proxy.ex",
    "chars": 47354,
    "preview": "defmodule Phoenix.LiveViewTest.ClientProxy do\n  @moduledoc false\n  use GenServer\n\n  @data_phx_upload_ref \"data-phx-uploa"
  },
  {
    "path": "lib/phoenix_live_view/test/diff.ex",
    "chars": 4995,
    "preview": "defmodule Phoenix.LiveViewTest.Diff do\n  @moduledoc false\n\n  alias Phoenix.LiveViewTest.DOM\n\n  @components :c\n  @static "
  },
  {
    "path": "lib/phoenix_live_view/test/dom.ex",
    "chars": 10771,
    "preview": "defmodule Phoenix.LiveViewTest.DOM do\n  @moduledoc false\n\n  @phx_component \"data-phx-component\"\n\n  alias Phoenix.LiveVie"
  },
  {
    "path": "lib/phoenix_live_view/test/live_view_test.ex",
    "chars": 71576,
    "preview": "defmodule Phoenix.LiveViewTest do\n  @moduledoc ~S'''\n  Conveniences for testing function components as well as\n  LiveVie"
  },
  {
    "path": "lib/phoenix_live_view/test/structs.ex",
    "chars": 2971,
    "preview": "defmodule Phoenix.LiveViewTest.View do\n  @moduledoc \"\"\"\n  The struct for testing LiveViews.\n\n  The following public fiel"
  },
  {
    "path": "lib/phoenix_live_view/test/tree_dom.ex",
    "chars": 19513,
    "preview": "defmodule Phoenix.LiveViewTest.TreeDOM do\n  @moduledoc false\n\n  @phx_static \"data-phx-static\"\n  @phx_component \"data-phx"
  },
  {
    "path": "lib/phoenix_live_view/test/upload_client.ex",
    "chars": 8400,
    "preview": "defmodule Phoenix.LiveViewTest.UploadClient do\n  @moduledoc false\n  use GenServer\n  require Logger\n\n  alias Phoenix.Live"
  },
  {
    "path": "lib/phoenix_live_view/test/utils.ex",
    "chars": 710,
    "preview": "defmodule Phoenix.LiveViewTest.Utils do\n  @moduledoc false\n\n  alias Phoenix.LiveViewTest.Upload\n\n  def stringify(%Upload"
  },
  {
    "path": "lib/phoenix_live_view/upload.ex",
    "chars": 13992,
    "preview": "defmodule Phoenix.LiveView.Upload do\n  # Operations integrating Phoenix.LiveView.Socket with UploadConfig.\n  @moduledoc "
  },
  {
    "path": "lib/phoenix_live_view/upload_channel.ex",
    "chars": 7516,
    "preview": "defmodule Phoenix.LiveView.UploadChannel do\n  @moduledoc false\n  use Phoenix.Channel, log_handle_in: false\n  @timeout :i"
  },
  {
    "path": "lib/phoenix_live_view/upload_config.ex",
    "chars": 21376,
    "preview": "defmodule Phoenix.LiveView.UploadEntry do\n  @moduledoc \"\"\"\n  The struct representing an upload entry.\n  \"\"\"\n\n  alias Pho"
  },
  {
    "path": "lib/phoenix_live_view/upload_tmp_file_writer.ex",
    "chars": 744,
    "preview": "defmodule Phoenix.LiveView.UploadTmpFileWriter do\n  @moduledoc false\n\n  @behaviour Phoenix.LiveView.UploadWriter\n\n  @imp"
  },
  {
    "path": "lib/phoenix_live_view/upload_writer.ex",
    "chars": 3154,
    "preview": "defmodule Phoenix.LiveView.UploadWriter do\n  @moduledoc ~S\"\"\"\n  Provide a behavior for writing uploaded chunks to a fina"
  },
  {
    "path": "lib/phoenix_live_view/utils.ex",
    "chars": 17334,
    "preview": "defmodule Phoenix.LiveView.Utils do\n  # Shared helpers used mostly by Channel and Diff,\n  # but also Static, and LiveVie"
  },
  {
    "path": "lib/phoenix_live_view.ex",
    "chars": 82941,
    "preview": "defmodule Phoenix.LiveView do\n  @moduledoc ~S'''\n  A LiveView is a process that receives events, updates\n  its state, an"
  },
  {
    "path": "lib/prettier.ex",
    "chars": 909,
    "preview": "if Mix.env() == :dev do\n  defmodule Prettier do\n    @moduledoc false\n\n    @behaviour Phoenix.LiveView.HTMLFormatter.TagF"
  },
  {
    "path": "mix.exs",
    "chars": 6029,
    "preview": "defmodule Phoenix.LiveView.MixProject do\n  use Mix.Project\n\n  @version \"1.2.0-dev\"\n\n  def project do\n    [\n      app: :p"
  },
  {
    "path": "package.json",
    "chars": 2676,
    "preview": "{\n  \"name\": \"phoenix_live_view\",\n  \"version\": \"1.2.0-dev\",\n  \"description\": \"The Phoenix LiveView JavaScript client.\",\n "
  },
  {
    "path": "priv/static/phoenix_live_view.cjs.js",
    "chars": 230943,
    "preview": "var __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = O"
  },
  {
    "path": "priv/static/phoenix_live_view.esm.js",
    "chars": 229942,
    "preview": "// js/phoenix_live_view/constants.js\nvar CONSECUTIVE_RELOADS = \"consecutive-reloads\";\nvar MAX_RELOADS = 10;\nvar RELOAD_J"
  },
  {
    "path": "priv/static/phoenix_live_view.js",
    "chars": 246199,
    "preview": "var LiveView = (() => {\n  var __defProp = Object.defineProperty;\n  var __defProps = Object.defineProperties;\n  var __get"
  },
  {
    "path": "setupTests.js",
    "chars": 21,
    "preview": "import \"css.escape\";\n"
  },
  {
    "path": "test/e2e/.prettierignore",
    "chars": 14,
    "preview": "test-results/\n"
  },
  {
    "path": "test/e2e/README.md",
    "chars": 1285,
    "preview": "# End-to-end tests\n\nThis directory contains end-to-end tests that use the [Playwright](https://playwright.dev/)\ntest fra"
  },
  {
    "path": "test/e2e/merge-coverage.js",
    "chars": 507,
    "preview": "import { CoverageReport } from \"monocart-coverage-reports\";\n\nconst coverageOptions = {\n  name: \"Phoenix LiveView JS Cove"
  },
  {
    "path": "test/e2e/playwright.config.js",
    "chars": 1571,
    "preview": "// playwright.config.js\n// @ts-check\nimport { devices } from \"@playwright/test\";\nimport { dirname, resolve } from \"node:"
  },
  {
    "path": "test/e2e/support/colocated_live.ex",
    "chars": 5832,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.ColocatedLive do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  defmodule Syn"
  },
  {
    "path": "test/e2e/support/components_live.ex",
    "chars": 5096,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.ComponentsLive do\n  use Phoenix.LiveView\n\n  alias Phoenix.LiveView.JS\n\n  @impl Phoeni"
  },
  {
    "path": "test/e2e/support/error_live.ex",
    "chars": 5614,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.ErrorLive do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  # TODO: find a wa"
  },
  {
    "path": "test/e2e/support/form_dynamic_inputs_live.ex",
    "chars": 3419,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.FormDynamicInputsLive do\n  use Phoenix.LiveView\n\n  alias Phoenix.LiveView.JS\n\n  @impl"
  },
  {
    "path": "test/e2e/support/form_feedback.ex",
    "chars": 5645,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.FormFeedbackLive do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  def render"
  },
  {
    "path": "test/e2e/support/form_live.ex",
    "chars": 7772,
    "preview": "for type <- [FormLive, FormLiveNested] do\n  defmodule Module.concat(Phoenix.LiveViewTest.E2E, type) do\n    use Phoenix.L"
  },
  {
    "path": "test/e2e/support/issues/issue_2787.ex",
    "chars": 4709,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue2787Live do\n  use Phoenix.LiveView\n\n  # https://github.com/phoenixframework/phoe"
  },
  {
    "path": "test/e2e/support/issues/issue_2965.ex",
    "chars": 6732,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue2965Live do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  defmodule NoO"
  },
  {
    "path": "test/e2e/support/issues/issue_3026.ex",
    "chars": 2335,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3026Live do\n  use Phoenix.LiveView\n\n  # https://github.com/phoenixframework/phoe"
  },
  {
    "path": "test/e2e/support/issues/issue_3040.ex",
    "chars": 6102,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3040Live do\n  use Phoenix.LiveView\n\n  # https://github.com/phoenixframework/phoe"
  },
  {
    "path": "test/e2e/support/issues/issue_3047.ex",
    "chars": 2006,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3047ALive do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  def render(\""
  },
  {
    "path": "test/e2e/support/issues/issue_3083.ex",
    "chars": 1262,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3083Live do\n  use Phoenix.LiveView\n\n  # https://github.com/phoenixframework/phoe"
  },
  {
    "path": "test/e2e/support/issues/issue_3107.ex",
    "chars": 777,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3107Live do\n  use Phoenix.LiveView\n\n  # https://github.com/phoenixframework/phoe"
  },
  {
    "path": "test/e2e/support/issues/issue_3117.ex",
    "chars": 939,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3117Live do\n  use Phoenix.LiveView\n\n  # https://github.com/phoenixframework/phoe"
  },
  {
    "path": "test/e2e/support/issues/issue_3169.ex",
    "chars": 3662,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3169Live.Components do\n  use Phoenix.Component\n\n  def input(assigns) do\n    ~H\"\""
  },
  {
    "path": "test/e2e/support/issues/issue_3194.ex",
    "chars": 1051,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3194Live do\n  use Phoenix.LiveView\n\n  # https://github.com/phoenixframework/phoe"
  },
  {
    "path": "test/e2e/support/issues/issue_3200.ex",
    "chars": 2651,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3200 do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/3200\n\n "
  },
  {
    "path": "test/e2e/support/issues/issue_3378.ex",
    "chars": 1286,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3378.NotificationsLive do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n "
  },
  {
    "path": "test/e2e/support/issues/issue_3448.ex",
    "chars": 1628,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3448Live do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/344"
  },
  {
    "path": "test/e2e/support/issues/issue_3496.ex",
    "chars": 2437,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3496.ALive do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/3"
  },
  {
    "path": "test/e2e/support/issues/issue_3529.ex",
    "chars": 580,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3529Live do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/352"
  },
  {
    "path": "test/e2e/support/issues/issue_3530.ex",
    "chars": 2611,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3530Live do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  defmodule Nes"
  },
  {
    "path": "test/e2e/support/issues/issue_3612.ex",
    "chars": 1369,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3612.ALive do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/3"
  },
  {
    "path": "test/e2e/support/issues/issue_3636.ex",
    "chars": 844,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3636Live do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/363"
  },
  {
    "path": "test/e2e/support/issues/issue_3647.ex",
    "chars": 6504,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3647Live do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/364"
  },
  {
    "path": "test/e2e/support/issues/issue_3651.ex",
    "chars": 2875,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3651Live do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/365"
  },
  {
    "path": "test/e2e/support/issues/issue_3656.ex",
    "chars": 1236,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3656Live do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/365"
  },
  {
    "path": "test/e2e/support/issues/issue_3658.ex",
    "chars": 790,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3658Live do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/365"
  },
  {
    "path": "test/e2e/support/issues/issue_3681.ex",
    "chars": 2186,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3681Live do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/368"
  },
  {
    "path": "test/e2e/support/issues/issue_3684.ex",
    "chars": 2080,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3684Live do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/368"
  },
  {
    "path": "test/e2e/support/issues/issue_3686.ex",
    "chars": 1206,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3686.ALive do\n  use Phoenix.LiveView\n\n  def render(assigns) do\n    ~H\"\"\"\n    <h1"
  },
  {
    "path": "test/e2e/support/issues/issue_3709.ex",
    "chars": 963,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3709Live do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/370"
  },
  {
    "path": "test/e2e/support/issues/issue_3719.ex",
    "chars": 589,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3719Live do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/371"
  },
  {
    "path": "test/e2e/support/issues/issue_3814.ex",
    "chars": 605,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3814Live do\n  use Phoenix.LiveView\n\n  def mount(_params, _session, socket) do\n  "
  },
  {
    "path": "test/e2e/support/issues/issue_3819.ex",
    "chars": 664,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3819Live do\n  use Phoenix.LiveView\n\n  def mount(_params, _session, socket) do\n  "
  },
  {
    "path": "test/e2e/support/issues/issue_3919.ex",
    "chars": 856,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3919Live do\n  use Phoenix.LiveView\n\n  def mount(_params, _session, socket) do\n  "
  },
  {
    "path": "test/e2e/support/issues/issue_3931.ex",
    "chars": 960,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3931Live do\n  use Phoenix.LiveView\n\n  def mount(_params, _session, socket) do\n  "
  },
  {
    "path": "test/e2e/support/issues/issue_3941.ex",
    "chars": 4149,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3941Live do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  alias Phoenix"
  },
  {
    "path": "test/e2e/support/issues/issue_3953.ex",
    "chars": 1212,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3953Live do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  alias Phoenix"
  },
  {
    "path": "test/e2e/support/issues/issue_3979.ex",
    "chars": 1545,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue3979Live do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  alias Phoenix"
  },
  {
    "path": "test/e2e/support/issues/issue_4027.ex",
    "chars": 2607,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue4027Live do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  alias Phoenix"
  },
  {
    "path": "test/e2e/support/issues/issue_4066.ex",
    "chars": 1427,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue4066Live do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  alias Phoenix"
  },
  {
    "path": "test/e2e/support/issues/issue_4078.ex",
    "chars": 2277,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue4078Live do\n  # https://github.com/phoenixframework/phoenix_live_view/issues/407"
  },
  {
    "path": "test/e2e/support/issues/issue_4088.ex",
    "chars": 976,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue4088Live do\n  use Phoenix.LiveView\n\n  def mount(_params, _session, socket) do\n  "
  },
  {
    "path": "test/e2e/support/issues/issue_4094.ex",
    "chars": 462,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue4094Live do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  def mount(_pa"
  },
  {
    "path": "test/e2e/support/issues/issue_4095.ex",
    "chars": 889,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue4095Live do\n  use Phoenix.LiveView, layout: {__MODULE__, :live}\n\n  def render(as"
  },
  {
    "path": "test/e2e/support/issues/issue_4102.ex",
    "chars": 967,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue4102Live do\n  use Phoenix.LiveView\n\n  def mount(_params, _session, socket) do\n  "
  },
  {
    "path": "test/e2e/support/issues/issue_4107.ex",
    "chars": 525,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue4107Live do\n  use Phoenix.LiveView\n\n  def mount(_params, _session, socket) do\n  "
  },
  {
    "path": "test/e2e/support/issues/issue_4121.ex",
    "chars": 761,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue4121Live do\n  use Phoenix.LiveView\n\n  def mount(_params, _session, socket) do\n  "
  },
  {
    "path": "test/e2e/support/issues/issue_4147.ex",
    "chars": 565,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Issue4147Live do\n  use Phoenix.LiveView\n\n  def mount(_params, _session, socket) do\n  "
  },
  {
    "path": "test/e2e/support/js_live.ex",
    "chars": 1458,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.JsLive do\n  use Phoenix.LiveView\n\n  alias Phoenix.LiveView.JS\n\n  @impl Phoenix.LiveVi"
  },
  {
    "path": "test/e2e/support/keyed_comprehension_live.ex",
    "chars": 8238,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.KeyedComprehensionLive do\n  use Phoenix.LiveView\n\n  @count 10\n\n  def render(assigns) "
  },
  {
    "path": "test/e2e/support/navigation.ex",
    "chars": 6570,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.Navigation.Layout do\n  use Phoenix.LiveView\n\n  def render(\"live.html\", assigns) do\n  "
  },
  {
    "path": "test/e2e/support/portal.ex",
    "chars": 17382,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.PortalLive do\n  use Phoenix.LiveView\n\n  alias Phoenix.LiveView.JS\n\n  def render(\"root"
  },
  {
    "path": "test/e2e/support/select_live.ex",
    "chars": 7394,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.SelectLive do\n  use Phoenix.LiveView\n\n  @impl Phoenix.LiveView\n  def mount(_params, _"
  },
  {
    "path": "test/e2e/support/upload_live.ex",
    "chars": 2903,
    "preview": "defmodule Phoenix.LiveViewTest.E2E.UploadLive do\n  use Phoenix.LiveView\n\n  # for end-to-end testing https://hexdocs.pm/p"
  },
  {
    "path": "test/e2e/teardown.js",
    "chars": 389,
    "preview": "import { request } from \"@playwright/test\";\n\nexport default async () => {\n  try {\n    const context = await request.newC"
  }
]

// ... and 171 more files (download for full content)

About this extraction

This page contains the full source code of the phoenixframework/phoenix_live_view GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 371 files (3.5 MB), approximately 946.5k tokens, and a symbol index with 5307 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!