Repository: phoenixframework/phoenix
Pull request #4517 (2021-09-30 02:45:00)

guides: update 'Views and templates'
This PR includes the following changes:

* In the "Rendering templates" section, update the path to the main layout template `lib/hello_web/templates/layout/root.html.heex` instead of `lib/hello_web/templates/layout/app.html.heex`
* Update the example in the "Rendering templates" section to follow the output from Phoenix v1.6.0


Patch:
diff --git a/config/config.exs b/config/config.exs
index 99b9e0d7f..08749fe4c 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -20,6 +20,7 @@ if Mix.env() == :dev do
   config :esbuild,
     version: "0.12.18",
     module: esbuild.(~w(--format=esm --sourcemap --outfile=../priv/static/phoenix.esm.js)),
+    main: esbuild.(~w(--format=cjs --sourcemap --outfile=../priv/static/phoenix.cjs.js)),
     cdn:
       esbuild.(
         ~w(--target=es2016 --format=iife --global-name=Phoenix --outfile=../priv/static/phoenix.js)
diff --git a/guides/authentication/mix_phx_gen_auth.md b/guides/authentication/mix_phx_gen_auth.md
index 47b173cfe..e5eaf742a 100644
--- a/guides/authentication/mix_phx_gen_auth.md
+++ b/guides/authentication/mix_phx_gen_auth.md
@@ -46,7 +46,17 @@ The following are notes about the generated authentication system.
 
 ### Password hashing
 
-The password hashing mechanism defaults to `argon2` for Unix systems and `pbkdf2` for Windows systems. Both systems use the [Comeonin interface](https://hexdocs.pm/comeonin/).
+The password hashing mechanism defaults to `bcrypt` for Unix systems and `pbkdf2` for Windows systems. Both systems use the [Comeonin interface](https://hexdocs.pm/comeonin/).
+
+The password hashing mechanism can be overridden with the `--hashing-lib` option. The following values are supported:
+
+    * `bcrypt` - [bcrypt_elixir](https://hex.pm/packages/bcrypt_elixir)
+    * `pbkdf2` - [pbkdf2_elixir](https://hex.pm/packages/pbkdf2_elixir)
+    * `argon2` - [argon2_elixir](https://hex.pm/packages/argon2_elixir)
+
+We recommend developers to consider using `argon2`, which is the most robust of all 3. The downside is that `argon2` is quite CPU and memory intensive, and you will need more powerful instances to run your applications on.
+
+For more information about choosing these libraries, see the [Comeonin project](https://github.com/riverrun/comeonin).
 
 ### Forbidding access
 
diff --git a/guides/ecto.md b/guides/ecto.md
index 731565eda..0b9f87772 100644
--- a/guides/ecto.md
+++ b/guides/ecto.md
@@ -127,7 +127,7 @@ end
 
 It begins by defining the repository module. Then it configures our `otp_app` name, and the `adapter` – `Postgres`, in our case.
 
-Our repo has three main tasks - to bring in all the common query functions from [`Ecto.Repo`], to set the `otp_app` name equal to our application name, and to configure our database adapter. We'll talk more about how to use `Hello.Repo` in a bit.
+Our repo has three main tasks - to bring in all the common query functions from `Ecto.Repo`, to set the `otp_app` name equal to our application name, and to configure our database adapter. We'll talk more about how to use `Hello.Repo` in a bit.
 
 When `phx.new` generated our application, it included some basic repository configuration as well. Let's look at `config/dev.exs`.
 
@@ -191,11 +191,11 @@ def changeset(%User{} = user, attrs) do
 end
 ```
 
-Right now, we have two transformations in our pipeline. In the first call, we invoke [`Ecto.Changeset.cast/3`], passing in our external parameters and marking which fields are required for validation.
+Right now, we have two transformations in our pipeline. In the first call, we invoke `Ecto.Changeset.cast/3`, passing in our external parameters and marking which fields are required for validation.
 
 [`cast/3`] first takes a struct, then the parameters (the proposed updates), and then the final field is the list of columns to be updated. [`cast/3`] also will only take fields that exist in the schema.
 
-Next, [`Ecto.Changeset.validate_required/3`] checks that this list of fields is present in the changeset that [`cast/3`] returns. By default with the generator, all fields are required.
+Next, `Ecto.Changeset.validate_required/3` checks that this list of fields is present in the changeset that [`cast/3`] returns. By default with the generator, all fields are required.
 
 We can verify this functionality in `IEx`. Let's fire up our application inside IEx by running `iex -S mix`. In order to minimize typing and make this easier to read, let's alias our `Hello.User` struct.
 
@@ -307,7 +307,7 @@ iex> changeset = User.changeset(%User{}, params)
   errors: [],
   data: #Hello.User<>,
   valid?: true
-> 
+>
 ```
 
 Our new changeset is valid.
@@ -365,7 +365,7 @@ def changeset(%User{} = user, attrs) do
 end
 ```
 
-Let's say we want to perform at least some rudimentary format validation on the `email` field. All we want to check for is the presence of the `@`. The [`Ecto.Changeset.validate_format/3`] function is just what we need.
+Let's say we want to perform at least some rudimentary format validation on the `email` field. All we want to check for is the presence of the `@`. The `Ecto.Changeset.validate_format/3` function is just what we need.
 
 ```elixir
 def changeset(%User{} = user, attrs) do
@@ -434,7 +434,7 @@ INSERT INTO "users" ("email","inserted_at","updated_at") VALUES ($1,$2,$3) RETUR
  }}
 ```
 
-We started by aliasing our `User` and `Repo` modules for easy access. Next, we called [`Repo.insert/1`] and passed a user struct. Since we are in the `dev` environment, we can see the debug logs for the query our repository performed when inserting the underlying `%User{}` data. We received a two-element tuple back with `{:ok, %User{}}`, which lets us know the insertion was successful. With a couple of users inserted, let's fetch them back out of the repo.
+We started by aliasing our `User` and `Repo` modules for easy access. Next, we called `Repo.insert/1` and passed a user struct. Since we are in the `dev` environment, we can see the debug logs for the query our repository performed when inserting the underlying `%User{}` data. We received a two-element tuple back with `{:ok, %User{}}`, which lets us know the insertion was successful. With a couple of users inserted, let's fetch them back out of the repo.
 
 ```elixir
 iex> Repo.all(User)
@@ -476,7 +476,7 @@ SELECT u0."email" FROM "users" AS u0 []
 ["user1@example.com", "user2@example.com"]
 ```
 
-First, we imported [`Ecto.Query`], which imports the [`from/2`] macro of Ecto's Query DSL. Next, we built a query which selects all the email addresses in our users table. Let's try another example.
+First, we imported `Ecto.Query`, which imports the [`from/2`] macro of Ecto's Query DSL. Next, we built a query which selects all the email addresses in our users table. Let's try another example.
 
 ```elixir
 iex)> Repo.one(from u in User, where: ilike(u.email, "%1%"),
@@ -485,7 +485,7 @@ iex)> Repo.one(from u in User, where: ilike(u.email, "%1%"),
 1
 ```
 
-Now we're starting to get a taste of Ecto's rich querying capabilities. We used [`Repo.one/1`] to fetch the count of all users with an email address containing `1`, and received the expected count in return. This just scratches the surface of Ecto's query interface, and much more is supported such as sub-querying, interval queries, and advanced select statements. For example, let's build a query to fetch a map of all user id's to their email addresses.
+Now we're starting to get a taste of Ecto's rich querying capabilities. We used `Repo.one/1` to fetch the count of all users with an email address containing `1`, and received the expected count in return. This just scratches the surface of Ecto's query interface, and much more is supported such as sub-querying, interval queries, and advanced select statements. For example, let's build a query to fetch a map of all user id's to their email addresses.
 
 ```elixir
 iex> Repo.all(from u in User, select: %{u.id => u.email})
diff --git a/guides/views.md b/guides/views.md
index 2d62faa81..cd0bfaee5 100644
--- a/guides/views.md
+++ b/guides/views.md
@@ -24,10 +24,10 @@ That's simple enough. There's only one line, `use HelloWeb, :view`. This line ca
 
 All of the imports and aliases we make in our view will also be available in our templates. That's because templates are effectively compiled into functions inside their respective views. For example, if you define a function in your view, you will be able to invoke it directly from the template. Let's see this in practice.
 
-Open up our application layout template, `lib/hello_web/templates/layout/app.html.heex`, and change this line,
+Open up our application layout template, `lib/hello_web/templates/layout/root.html.heex`, and change this line,
 
 ```html
-<title>Hello · Phoenix Framework</title>
+<%= live_title_tag assigns[:page_title] || "Hello", suffix: " · Phoenix Framework" %>
 ```
 
 to call a `title/0` function, like this.
@@ -255,7 +255,7 @@ Now if you visit the Welcome page, you see the template results also shown.
 
 ## Layouts
 
-Layouts are just templates. They have a view, just like other templates. In a newly generated app, this is `lib/hello_web/views/layout_view.ex`. You may be wondering how the string resulting from a rendered view ends up inside a layout. That's a great question! If we look at `lib/hello_web/templates/layout/app.html.heex`, just about in the middle of the `<body>`, we will see this.
+Layouts are just templates. They have a view, just like other templates. In a newly generated app, this is `lib/hello_web/views/layout_view.ex`. You may be wondering how the string resulting from a rendered view ends up inside a layout. That's a great question! If we look at `lib/hello_web/templates/layout/root.html.heex`, just about at the end of the `<body>`, we will see this.
 
 ```html
 <%= @inner_content %>
@@ -267,7 +267,7 @@ In other words, the inner template is placed in the `@inner_content` assign.
 
 The view's job is not only to render HTML templates. Views are about data presentation. Given a bag of data, the view's purpose is to present that in a meaningful way given some format, be it HTML, JSON, CSV, or others. Many web apps today return JSON to remote clients, and Phoenix views are *great* for JSON rendering.
 
-Phoenix uses the [Jason](https://github.com/michalmuskala/jason) library to encode JSON, so all we need to do in our views is to format the data we would like to respond with as a list or a map, and Phoenix will do the rest.
+Phoenix uses the `Jason` library to encode JSON, so all we need to do in our views is to format the data we would like to respond with as a list or a map, and Phoenix will do the rest.
 
 While it is possible to respond with JSON back directly from the controller and skip the view, Phoenix views provide a much more structured approach for doing  so. Let's take our `PageController`, and see what it may look like when we respond with some static page maps as JSON, instead of HTML.
 
@@ -311,7 +311,7 @@ end
 
 In the view we see our [`render/2`] function pattern matching on `"index.json"`, `"show.json"`, and `"page.json"`. The `"index.json"` and `"show.json"` are the ones requested directly from the controller. They also match on the assigns sent by the controller. `"index.json"` will respond with JSON like this:
 
-```javascript
+```json
 {
   "data": [
     {
@@ -326,7 +326,7 @@ In the view we see our [`render/2`] function pattern matching on `"index.json"`,
 
 And the [`render/2`] matching `"show.json"`:
 
-```javascript
+```json
 {
   "data": {
     "title": "foo"
diff --git a/lib/mix/tasks/phx.gen.auth.ex b/lib/mix/tasks/phx.gen.auth.ex
index 23d1c8df5..7f05a958d 100644
--- a/lib/mix/tasks/phx.gen.auth.ex
+++ b/lib/mix/tasks/phx.gen.auth.ex
@@ -14,7 +14,7 @@ defmodule Mix.Tasks.Phx.Gen.Auth do
 
   ## Password hashing
 
-  The password hashing mechanism defaults to `argon2` for
+  The password hashing mechanism defaults to `bcrypt` for
   Unix systems and `pbkdf2` for Windows systems. Both
   systems use the [Comeonin interface](https://hexdocs.pm/comeonin/).
 
@@ -25,6 +25,11 @@ defmodule Mix.Tasks.Phx.Gen.Auth do
     * `pbkdf2` - [pbkdf2_elixir](https://hex.pm/packages/pbkdf2_elixir)
     * `argon2` - [argon2_elixir](https://hex.pm/packages/argon2_elixir)
 
+  We recommend developers to consider using `argon2`, which
+  is the most robust of all 3. The downside is that `argon2`
+  is quite CPU and memory intensive, and you will need more
+  powerful instances to run your applications on.
+
   For more information about choosing these libraries, see the
   [Comeonin project](https://github.com/riverrun/comeonin).
 
@@ -200,7 +205,7 @@ defmodule Mix.Tasks.Phx.Gen.Auth do
 
   defp default_hashing_library_option do
     case :os.type() do
-      {:unix, _} -> "argon2"
+      {:unix, _} -> "bcrypt"
       {:win32, _} -> "pbkdf2"
     end
   end
diff --git a/mix.exs b/mix.exs
index 38679e389..0a073b21e 100644
--- a/mix.exs
+++ b/mix.exs
@@ -198,7 +198,7 @@ defmodule Phoenix.MixProject do
   defp aliases do
     [
       docs: ["docs", &generate_js_docs/1],
-      "assets.build": ["esbuild module", "esbuild cdn", "esbuild cdn_min"],
+      "assets.build": ["esbuild module", "esbuild cdn", "esbuild cdn_min", "esbuild main"],
       "assets.watch": "esbuild module --watch"
     ]
   end
diff --git a/package.json b/package.json
index 8ccc5ef0d..db6502d57 100644
--- a/package.json
+++ b/package.json
@@ -4,10 +4,12 @@
   "description": "The official JavaScript client for the Phoenix web framework.",
   "license": "MIT",
   "module": "./priv/static/phoenix.esm.js",
+  "main": "./priv/static/phoenix.cjs.js",
   "unpkg": "./priv/static/phoenix.min.js",
   "jsdelivr": "./priv/static/phoenix.min.js",
   "exports": {
-    "import": "./priv/static/phoenix.esm.js"
+    "import": "./priv/static/phoenix.esm.js",
+    "require": "./priv/static/phoenix.cjs.js"
   },
   "repository": {
     "type": "git",
diff --git a/priv/static/phoenix.cjs.js b/priv/static/phoenix.cjs.js
new file mode 100644
index 000000000..49b31c23a
--- /dev/null
+++ b/priv/static/phoenix.cjs.js
@@ -0,0 +1,1085 @@
+var __defProp = Object.defineProperty;
+var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
+var __export = (target, all) => {
+  __markAsModule(target);
+  for (var name in all)
+    __defProp(target, name, { get: all[name], enumerable: true });
+};
+
+// js/phoenix/index.js
+__export(exports, {
+  Channel: () => Channel,
+  LongPoll: () => LongPoll,
+  Presence: () => Presence,
+  Serializer: () => serializer_default,
+  Socket: () => Socket
+});
+
+// js/phoenix/utils.js
+var closure = (value) => {
+  if (typeof value === "function") {
+    return value;
+  } else {
+    let closure2 = function() {
+      return value;
+    };
+    return closure2;
+  }
+};
+
+// js/phoenix/constants.js
+var globalSelf = typeof self !== "undefined" ? self : null;
+var phxWindow = typeof window !== "undefined" ? window : null;
+var global = globalSelf || phxWindow || void 0;
+var DEFAULT_VSN = "2.0.0";
+var SOCKET_STATES = { connecting: 0, open: 1, closing: 2, closed: 3 };
+var DEFAULT_TIMEOUT = 1e4;
+var WS_CLOSE_NORMAL = 1e3;
+var CHANNEL_STATES = {
+  closed: "closed",
+  errored: "errored",
+  joined: "joined",
+  joining: "joining",
+  leaving: "leaving"
+};
+var CHANNEL_EVENTS = {
+  close: "phx_close",
+  error: "phx_error",
+  join: "phx_join",
+  reply: "phx_reply",
+  leave: "phx_leave"
+};
+var TRANSPORTS = {
+  longpoll: "longpoll",
+  websocket: "websocket"
+};
+var XHR_STATES = {
+  complete: 4
+};
+
+// js/phoenix/push.js
+var Push = class {
+  constructor(channel, event, payload, timeout) {
+    this.channel = channel;
+    this.event = event;
+    this.payload = payload || function() {
+      return {};
+    };
+    this.receivedResp = null;
+    this.timeout = timeout;
+    this.timeoutTimer = null;
+    this.recHooks = [];
+    this.sent = false;
+  }
+  resend(timeout) {
+    this.timeout = timeout;
+    this.reset();
+    this.send();
+  }
+  send() {
+    if (this.hasReceived("timeout")) {
+      return;
+    }
+    this.startTimeout();
+    this.sent = true;
+    this.channel.socket.push({
+      topic: this.channel.topic,
+      event: this.event,
+      payload: this.payload(),
+      ref: this.ref,
+      join_ref: this.channel.joinRef()
+    });
+  }
+  receive(status, callback) {
+    if (this.hasReceived(status)) {
+      callback(this.receivedResp.response);
+    }
+    this.recHooks.push({ status, callback });
+    return this;
+  }
+  reset() {
+    this.cancelRefEvent();
+    this.ref = null;
+    this.refEvent = null;
+    this.receivedResp = null;
+    this.sent = false;
+  }
+  matchReceive({ status, response, _ref }) {
+    this.recHooks.filter((h) => h.status === status).forEach((h) => h.callback(response));
+  }
+  cancelRefEvent() {
+    if (!this.refEvent) {
+      return;
+    }
+    this.channel.off(this.refEvent);
+  }
+  cancelTimeout() {
+    clearTimeout(this.timeoutTimer);
+    this.timeoutTimer = null;
+  }
+  startTimeout() {
+    if (this.timeoutTimer) {
+      this.cancelTimeout();
+    }
+    this.ref = this.channel.socket.makeRef();
+    this.refEvent = this.channel.replyEventName(this.ref);
+    this.channel.on(this.refEvent, (payload) => {
+      this.cancelRefEvent();
+      this.cancelTimeout();
+      this.receivedResp = payload;
+      this.matchReceive(payload);
+    });
+    this.timeoutTimer = setTimeout(() => {
+      this.trigger("timeout", {});
+    }, this.timeout);
+  }
+  hasReceived(status) {
+    return this.receivedResp && this.receivedResp.status === status;
+  }
+  trigger(status, response) {
+    this.channel.trigger(this.refEvent, { status, response });
+  }
+};
+
+// js/phoenix/timer.js
+var Timer = class {
+  constructor(callback, timerCalc) {
+    this.callback = callback;
+    this.timerCalc = timerCalc;
+    this.timer = null;
+    this.tries = 0;
+  }
+  reset() {
+    this.tries = 0;
+    clearTimeout(this.timer);
+  }
+  scheduleTimeout() {
+    clearTimeout(this.timer);
+    this.timer = setTimeout(() => {
+      this.tries = this.tries + 1;
+      this.callback();
+    }, this.timerCalc(this.tries + 1));
+  }
+};
+
+// js/phoenix/channel.js
+var Channel = class {
+  constructor(topic, params, socket) {
+    this.state = CHANNEL_STATES.closed;
+    this.topic = topic;
+    this.params = closure(params || {});
+    this.socket = socket;
+    this.bindings = [];
+    this.bindingRef = 0;
+    this.timeout = this.socket.timeout;
+    this.joinedOnce = false;
+    this.joinPush = new Push(this, CHANNEL_EVENTS.join, this.params, this.timeout);
+    this.pushBuffer = [];
+    this.stateChangeRefs = [];
+    this.rejoinTimer = new Timer(() => {
+      if (this.socket.isConnected()) {
+        this.rejoin();
+      }
+    }, this.socket.rejoinAfterMs);
+    this.stateChangeRefs.push(this.socket.onError(() => this.rejoinTimer.reset()));
+    this.stateChangeRefs.push(this.socket.onOpen(() => {
+      this.rejoinTimer.reset();
+      if (this.isErrored()) {
+        this.rejoin();
+      }
+    }));
+    this.joinPush.receive("ok", () => {
+      this.state = CHANNEL_STATES.joined;
+      this.rejoinTimer.reset();
+      this.pushBuffer.forEach((pushEvent) => pushEvent.send());
+      this.pushBuffer = [];
+    });
+    this.joinPush.receive("error", () => {
+      this.state = CHANNEL_STATES.errored;
+      if (this.socket.isConnected()) {
+        this.rejoinTimer.scheduleTimeout();
+      }
+    });
+    this.onClose(() => {
+      this.rejoinTimer.reset();
+      if (this.socket.hasLogger())
+        this.socket.log("channel", `close ${this.topic} ${this.joinRef()}`);
+      this.state = CHANNEL_STATES.closed;
+      this.socket.remove(this);
+    });
+    this.onError((reason) => {
+      if (this.socket.hasLogger())
+        this.socket.log("channel", `error ${this.topic}`, reason);
+      if (this.isJoining()) {
+        this.joinPush.reset();
+      }
+      this.state = CHANNEL_STATES.errored;
+      if (this.socket.isConnected()) {
+        this.rejoinTimer.scheduleTimeout();
+      }
+    });
+    this.joinPush.receive("timeout", () => {
+      if (this.socket.hasLogger())
+        this.socket.log("channel", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout);
+      let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout);
+      leavePush.send();
+      this.state = CHANNEL_STATES.errored;
+      this.joinPush.reset();
+      if (this.socket.isConnected()) {
+        this.rejoinTimer.scheduleTimeout();
+      }
+    });
+    this.on(CHANNEL_EVENTS.reply, (payload, ref) => {
+      this.trigger(this.replyEventName(ref), payload);
+    });
+  }
+  join(timeout = this.timeout) {
+    if (this.joinedOnce) {
+      throw new Error("tried to join multiple times. 'join' can only be called a single time per channel instance");
+    } else {
+      this.timeout = timeout;
+      this.joinedOnce = true;
+      this.rejoin();
+      return this.joinPush;
+    }
+  }
+  onClose(callback) {
+    this.on(CHANNEL_EVENTS.close, callback);
+  }
+  onError(callback) {
+    return this.on(CHANNEL_EVENTS.error, (reason) => callback(reason));
+  }
+  on(event, callback) {
+    let ref = this.bindingRef++;
+    this.bindings.push({ event, ref, callback });
+    return ref;
+  }
+  off(event, ref) {
+    this.bindings = this.bindings.filter((bind) => {
+      return !(bind.event === event && (typeof ref === "undefined" || ref === bind.ref));
+    });
+  }
+  canPush() {
+    return this.socket.isConnected() && this.isJoined();
+  }
+  push(event, payload, timeout = this.timeout) {
+    payload = payload || {};
+    if (!this.joinedOnce) {
+      throw new Error(`tried to push '${event}' to '${this.topic}' before joining. Use channel.join() before pushing events`);
+    }
+    let pushEvent = new Push(this, event, function() {
+      return payload;
+    }, timeout);
+    if (this.canPush()) {
+      pushEvent.send();
+    } else {
+      pushEvent.startTimeout();
+      this.pushBuffer.push(pushEvent);
+    }
+    return pushEvent;
+  }
+  leave(timeout = this.timeout) {
+    this.rejoinTimer.reset();
+    this.joinPush.cancelTimeout();
+    this.state = CHANNEL_STATES.leaving;
+    let onClose = () => {
+      if (this.socket.hasLogger())
+        this.socket.log("channel", `leave ${this.topic}`);
+      this.trigger(CHANNEL_EVENTS.close, "leave");
+    };
+    let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout);
+    leavePush.receive("ok", () => onClose()).receive("timeout", () => onClose());
+    leavePush.send();
+    if (!this.canPush()) {
+      leavePush.trigger("ok", {});
+    }
+    return leavePush;
+  }
+  onMessage(_event, payload, _ref) {
+    return payload;
+  }
+  isMember(topic, event, payload, joinRef) {
+    if (this.topic !== topic) {
+      return false;
+    }
+    if (joinRef && joinRef !== this.joinRef()) {
+      if (this.socket.hasLogger())
+        this.socket.log("channel", "dropping outdated message", { topic, event, payload, joinRef });
+      return false;
+    } else {
+      return true;
+    }
+  }
+  joinRef() {
+    return this.joinPush.ref;
+  }
+  rejoin(timeout = this.timeout) {
+    if (this.isLeaving()) {
+      return;
+    }
+    this.socket.leaveOpenTopic(this.topic);
+    this.state = CHANNEL_STATES.joining;
+    this.joinPush.resend(timeout);
+  }
+  trigger(event, payload, ref, joinRef) {
+    let handledPayload = this.onMessage(event, payload, ref, joinRef);
+    if (payload && !handledPayload) {
+      throw new Error("channel onMessage callbacks must return the payload, modified or unmodified");
+    }
+    let eventBindings = this.bindings.filter((bind) => bind.event === event);
+    for (let i = 0; i < eventBindings.length; i++) {
+      let bind = eventBindings[i];
+      bind.callback(handledPayload, ref, joinRef || this.joinRef());
+    }
+  }
+  replyEventName(ref) {
+    return `chan_reply_${ref}`;
+  }
+  isClosed() {
+    return this.state === CHANNEL_STATES.closed;
+  }
+  isErrored() {
+    return this.state === CHANNEL_STATES.errored;
+  }
+  isJoined() {
+    return this.state === CHANNEL_STATES.joined;
+  }
+  isJoining() {
+    return this.state === CHANNEL_STATES.joining;
+  }
+  isLeaving() {
+    return this.state === CHANNEL_STATES.leaving;
+  }
+};
+
+// js/phoenix/ajax.js
+var Ajax = class {
+  static request(method, endPoint, accept, body, timeout, ontimeout, callback) {
+    if (global.XDomainRequest) {
+      let req = new global.XDomainRequest();
+      this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback);
+    } else {
+      let req = new global.XMLHttpRequest();
+      this.xhrRequest(req, method, endPoint, accept, body, timeout, ontimeout, callback);
+    }
+  }
+  static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback) {
+    req.timeout = timeout;
+    req.open(method, endPoint);
+    req.onload = () => {
+      let response = this.parseJSON(req.responseText);
+      callback && callback(response);
+    };
+    if (ontimeout) {
+      req.ontimeout = ontimeout;
+    }
+    req.onprogress = () => {
+    };
+    req.send(body);
+  }
+  static xhrRequest(req, method, endPoint, accept, body, timeout, ontimeout, callback) {
+    req.open(method, endPoint, true);
+    req.timeout = timeout;
+    req.setRequestHeader("Content-Type", accept);
+    req.onerror = () => {
+      callback && callback(null);
+    };
+    req.onreadystatechange = () => {
+      if (req.readyState === XHR_STATES.complete && callback) {
+        let response = this.parseJSON(req.responseText);
+        callback(response);
+      }
+    };
+    if (ontimeout) {
+      req.ontimeout = ontimeout;
+    }
+    req.send(body);
+  }
+  static parseJSON(resp) {
+    if (!resp || resp === "") {
+      return null;
+    }
+    try {
+      return JSON.parse(resp);
+    } catch (e) {
+      console && console.log("failed to parse JSON response", resp);
+      return null;
+    }
+  }
+  static serialize(obj, parentKey) {
+    let queryStr = [];
+    for (var key in obj) {
+      if (!Object.prototype.hasOwnProperty.call(obj, key)) {
+        continue;
+      }
+      let paramKey = parentKey ? `${parentKey}[${key}]` : key;
+      let paramVal = obj[key];
+      if (typeof paramVal === "object") {
+        queryStr.push(this.serialize(paramVal, paramKey));
+      } else {
+        queryStr.push(encodeURIComponent(paramKey) + "=" + encodeURIComponent(paramVal));
+      }
+    }
+    return queryStr.join("&");
+  }
+  static appendParams(url, params) {
+    if (Object.keys(params).length === 0) {
+      return url;
+    }
+    let prefix = url.match(/\?/) ? "&" : "?";
+    return `${url}${prefix}${this.serialize(params)}`;
+  }
+};
+
+// js/phoenix/longpoll.js
+var LongPoll = class {
+  constructor(endPoint) {
+    this.endPoint = null;
+    this.token = null;
+    this.skipHeartbeat = true;
+    this.onopen = function() {
+    };
+    this.onerror = function() {
+    };
+    this.onmessage = function() {
+    };
+    this.onclose = function() {
+    };
+    this.pollEndpoint = this.normalizeEndpoint(endPoint);
+    this.readyState = SOCKET_STATES.connecting;
+    this.poll();
+  }
+  normalizeEndpoint(endPoint) {
+    return endPoint.replace("ws://", "http://").replace("wss://", "https://").replace(new RegExp("(.*)/" + TRANSPORTS.websocket), "$1/" + TRANSPORTS.longpoll);
+  }
+  endpointURL() {
+    return Ajax.appendParams(this.pollEndpoint, { token: this.token });
+  }
+  closeAndRetry() {
+    this.close();
+    this.readyState = SOCKET_STATES.connecting;
+  }
+  ontimeout() {
+    this.onerror("timeout");
+    this.closeAndRetry();
+  }
+  poll() {
+    if (!(this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting)) {
+      return;
+    }
+    Ajax.request("GET", this.endpointURL(), "application/json", null, this.timeout, this.ontimeout.bind(this), (resp) => {
+      if (resp) {
+        var { status, token, messages } = resp;
+        this.token = token;
+      } else {
+        status = 0;
+      }
+      switch (status) {
+        case 200:
+          messages.forEach((msg) => {
+            setTimeout(() => {
+              this.onmessage({ data: msg });
+            }, 0);
+          });
+          this.poll();
+          break;
+        case 204:
+          this.poll();
+          break;
+        case 410:
+          this.readyState = SOCKET_STATES.open;
+          this.onopen();
+          this.poll();
+          break;
+        case 403:
+          this.onerror();
+          this.close();
+          break;
+        case 0:
+        case 500:
+          this.onerror();
+          this.closeAndRetry();
+          break;
+        default:
+          throw new Error(`unhandled poll status ${status}`);
+      }
+    });
+  }
+  send(body) {
+    Ajax.request("POST", this.endpointURL(), "application/json", body, this.timeout, this.onerror.bind(this, "timeout"), (resp) => {
+      if (!resp || resp.status !== 200) {
+        this.onerror(resp && resp.status);
+        this.closeAndRetry();
+      }
+    });
+  }
+  close(_code, _reason) {
+    this.readyState = SOCKET_STATES.closed;
+    this.onclose();
+  }
+};
+
+// js/phoenix/presence.js
+var Presence = class {
+  constructor(channel, opts = {}) {
+    let events = opts.events || { state: "presence_state", diff: "presence_diff" };
+    this.state = {};
+    this.pendingDiffs = [];
+    this.channel = channel;
+    this.joinRef = null;
+    this.caller = {
+      onJoin: function() {
+      },
+      onLeave: function() {
+      },
+      onSync: function() {
+      }
+    };
+    this.channel.on(events.state, (newState) => {
+      let { onJoin, onLeave, onSync } = this.caller;
+      this.joinRef = this.channel.joinRef();
+      this.state = Presence.syncState(this.state, newState, onJoin, onLeave);
+      this.pendingDiffs.forEach((diff) => {
+        this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave);
+      });
+      this.pendingDiffs = [];
+      onSync();
+    });
+    this.channel.on(events.diff, (diff) => {
+      let { onJoin, onLeave, onSync } = this.caller;
+      if (this.inPendingSyncState()) {
+        this.pendingDiffs.push(diff);
+      } else {
+        this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave);
+        onSync();
+      }
+    });
+  }
+  onJoin(callback) {
+    this.caller.onJoin = callback;
+  }
+  onLeave(callback) {
+    this.caller.onLeave = callback;
+  }
+  onSync(callback) {
+    this.caller.onSync = callback;
+  }
+  list(by) {
+    return Presence.list(this.state, by);
+  }
+  inPendingSyncState() {
+    return !this.joinRef || this.joinRef !== this.channel.joinRef();
+  }
+  static syncState(currentState, newState, onJoin, onLeave) {
+    let state = this.clone(currentState);
+    let joins = {};
+    let leaves = {};
+    this.map(state, (key, presence) => {
+      if (!newState[key]) {
+        leaves[key] = presence;
+      }
+    });
+    this.map(newState, (key, newPresence) => {
+      let currentPresence = state[key];
+      if (currentPresence) {
+        let newRefs = newPresence.metas.map((m) => m.phx_ref);
+        let curRefs = currentPresence.metas.map((m) => m.phx_ref);
+        let joinedMetas = newPresence.metas.filter((m) => curRefs.indexOf(m.phx_ref) < 0);
+        let leftMetas = currentPresence.metas.filter((m) => newRefs.indexOf(m.phx_ref) < 0);
+        if (joinedMetas.length > 0) {
+          joins[key] = newPresence;
+          joins[key].metas = joinedMetas;
+        }
+        if (leftMetas.length > 0) {
+          leaves[key] = this.clone(currentPresence);
+          leaves[key].metas = leftMetas;
+        }
+      } else {
+        joins[key] = newPresence;
+      }
+    });
+    return this.syncDiff(state, { joins, leaves }, onJoin, onLeave);
+  }
+  static syncDiff(state, diff, onJoin, onLeave) {
+    let { joins, leaves } = this.clone(diff);
+    if (!onJoin) {
+      onJoin = function() {
+      };
+    }
+    if (!onLeave) {
+      onLeave = function() {
+      };
+    }
+    this.map(joins, (key, newPresence) => {
+      let currentPresence = state[key];
+      state[key] = this.clone(newPresence);
+      if (currentPresence) {
+        let joinedRefs = state[key].metas.map((m) => m.phx_ref);
+        let curMetas = currentPresence.metas.filter((m) => joinedRefs.indexOf(m.phx_ref) < 0);
+        state[key].metas.unshift(...curMetas);
+      }
+      onJoin(key, currentPresence, newPresence);
+    });
+    this.map(leaves, (key, leftPresence) => {
+      let currentPresence = state[key];
+      if (!currentPresence) {
+        return;
+      }
+      let refsToRemove = leftPresence.metas.map((m) => m.phx_ref);
+      currentPresence.metas = currentPresence.metas.filter((p) => {
+        return refsToRemove.indexOf(p.phx_ref) < 0;
+      });
+      onLeave(key, currentPresence, leftPresence);
+      if (currentPresence.metas.length === 0) {
+        delete state[key];
+      }
+    });
+    return state;
+  }
+  static list(presences, chooser) {
+    if (!chooser) {
+      chooser = function(key, pres) {
+        return pres;
+      };
+    }
+    return this.map(presences, (key, presence) => {
+      return chooser(key, presence);
+    });
+  }
+  static map(obj, func) {
+    return Object.getOwnPropertyNames(obj).map((key) => func(key, obj[key]));
+  }
+  static clone(obj) {
+    return JSON.parse(JSON.stringify(obj));
+  }
+};
+
+// js/phoenix/serializer.js
+var serializer_default = {
+  HEADER_LENGTH: 1,
+  META_LENGTH: 4,
+  KINDS: { push: 0, reply: 1, broadcast: 2 },
+  encode(msg, callback) {
+    if (msg.payload.constructor === ArrayBuffer) {
+      return callback(this.binaryEncode(msg));
+    } else {
+      let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload];
+      return callback(JSON.stringify(payload));
+    }
+  },
+  decode(rawPayload, callback) {
+    if (rawPayload.constructor === ArrayBuffer) {
+      return callback(this.binaryDecode(rawPayload));
+    } else {
+      let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload);
+      return callback({ join_ref, ref, topic, event, payload });
+    }
+  },
+  binaryEncode(message) {
+    let { join_ref, ref, event, topic, payload } = message;
+    let metaLength = this.META_LENGTH + join_ref.length + ref.length + topic.length + event.length;
+    let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength);
+    let view = new DataView(header);
+    let offset = 0;
+    view.setUint8(offset++, this.KINDS.push);
+    view.setUint8(offset++, join_ref.length);
+    view.setUint8(offset++, ref.length);
+    view.setUint8(offset++, topic.length);
+    view.setUint8(offset++, event.length);
+    Array.from(join_ref, (char) => view.setUint8(offset++, char.charCodeAt(0)));
+    Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0)));
+    Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0)));
+    Array.from(event, (char) => view.setUint8(offset++, char.charCodeAt(0)));
+    var combined = new Uint8Array(header.byteLength + payload.byteLength);
+    combined.set(new Uint8Array(header), 0);
+    combined.set(new Uint8Array(payload), header.byteLength);
+    return combined.buffer;
+  },
+  binaryDecode(buffer) {
+    let view = new DataView(buffer);
+    let kind = view.getUint8(0);
+    let decoder = new TextDecoder();
+    switch (kind) {
+      case this.KINDS.push:
+        return this.decodePush(buffer, view, decoder);
+      case this.KINDS.reply:
+        return this.decodeReply(buffer, view, decoder);
+      case this.KINDS.broadcast:
+        return this.decodeBroadcast(buffer, view, decoder);
+    }
+  },
+  decodePush(buffer, view, decoder) {
+    let joinRefSize = view.getUint8(1);
+    let topicSize = view.getUint8(2);
+    let eventSize = view.getUint8(3);
+    let offset = this.HEADER_LENGTH + this.META_LENGTH - 1;
+    let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize));
+    offset = offset + joinRefSize;
+    let topic = decoder.decode(buffer.slice(offset, offset + topicSize));
+    offset = offset + topicSize;
+    let event = decoder.decode(buffer.slice(offset, offset + eventSize));
+    offset = offset + eventSize;
+    let data = buffer.slice(offset, buffer.byteLength);
+    return { join_ref: joinRef, ref: null, topic, event, payload: data };
+  },
+  decodeReply(buffer, view, decoder) {
+    let joinRefSize = view.getUint8(1);
+    let refSize = view.getUint8(2);
+    let topicSize = view.getUint8(3);
+    let eventSize = view.getUint8(4);
+    let offset = this.HEADER_LENGTH + this.META_LENGTH;
+    let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize));
+    offset = offset + joinRefSize;
+    let ref = decoder.decode(buffer.slice(offset, offset + refSize));
+    offset = offset + refSize;
+    let topic = decoder.decode(buffer.slice(offset, offset + topicSize));
+    offset = offset + topicSize;
+    let event = decoder.decode(buffer.slice(offset, offset + eventSize));
+    offset = offset + eventSize;
+    let data = buffer.slice(offset, buffer.byteLength);
+    let payload = { status: event, response: data };
+    return { join_ref: joinRef, ref, topic, event: CHANNEL_EVENTS.reply, payload };
+  },
+  decodeBroadcast(buffer, view, decoder) {
+    let topicSize = view.getUint8(1);
+    let eventSize = view.getUint8(2);
+    let offset = this.HEADER_LENGTH + 2;
+    let topic = decoder.decode(buffer.slice(offset, offset + topicSize));
+    offset = offset + topicSize;
+    let event = decoder.decode(buffer.slice(offset, offset + eventSize));
+    offset = offset + eventSize;
+    let data = buffer.slice(offset, buffer.byteLength);
+    return { join_ref: null, ref: null, topic, event, payload: data };
+  }
+};
+
+// js/phoenix/socket.js
+var Socket = class {
+  constructor(endPoint, opts = {}) {
+    this.stateChangeCallbacks = { open: [], close: [], error: [], message: [] };
+    this.channels = [];
+    this.sendBuffer = [];
+    this.ref = 0;
+    this.timeout = opts.timeout || DEFAULT_TIMEOUT;
+    this.transport = opts.transport || global.WebSocket || LongPoll;
+    this.establishedConnections = 0;
+    this.defaultEncoder = serializer_default.encode.bind(serializer_default);
+    this.defaultDecoder = serializer_default.decode.bind(serializer_default);
+    this.closeWasClean = false;
+    this.binaryType = opts.binaryType || "arraybuffer";
+    this.connectClock = 1;
+    if (this.transport !== LongPoll) {
+      this.encode = opts.encode || this.defaultEncoder;
+      this.decode = opts.decode || this.defaultDecoder;
+    } else {
+      this.encode = this.defaultEncoder;
+      this.decode = this.defaultDecoder;
+    }
+    let awaitingConnectionOnPageShow = null;
+    if (phxWindow && phxWindow.addEventListener) {
+      phxWindow.addEventListener("pagehide", (_e) => {
+        if (this.conn) {
+          this.disconnect();
+          awaitingConnectionOnPageShow = this.connectClock;
+        }
+      });
+      phxWindow.addEventListener("pageshow", (_e) => {
+        if (awaitingConnectionOnPageShow === this.connectClock) {
+          awaitingConnectionOnPageShow = null;
+          this.connect();
+        }
+      });
+    }
+    this.heartbeatIntervalMs = opts.heartbeatIntervalMs || 3e4;
+    this.rejoinAfterMs = (tries) => {
+      if (opts.rejoinAfterMs) {
+        return opts.rejoinAfterMs(tries);
+      } else {
+        return [1e3, 2e3, 5e3][tries - 1] || 1e4;
+      }
+    };
+    this.reconnectAfterMs = (tries) => {
+      if (opts.reconnectAfterMs) {
+        return opts.reconnectAfterMs(tries);
+      } else {
+        return [10, 50, 100, 150, 200, 250, 500, 1e3, 2e3][tries - 1] || 5e3;
+      }
+    };
+    this.logger = opts.logger || null;
+    this.longpollerTimeout = opts.longpollerTimeout || 2e4;
+    this.params = closure(opts.params || {});
+    this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`;
+    this.vsn = opts.vsn || DEFAULT_VSN;
+    this.heartbeatTimer = null;
+    this.pendingHeartbeatRef = null;
+    this.reconnectTimer = new Timer(() => {
+      this.teardown(() => this.connect());
+    }, this.reconnectAfterMs);
+  }
+  replaceTransport(newTransport) {
+    this.disconnect();
+    this.transport = newTransport;
+  }
+  protocol() {
+    return location.protocol.match(/^https/) ? "wss" : "ws";
+  }
+  endPointURL() {
+    let uri = Ajax.appendParams(Ajax.appendParams(this.endPoint, this.params()), { vsn: this.vsn });
+    if (uri.charAt(0) !== "/") {
+      return uri;
+    }
+    if (uri.charAt(1) === "/") {
+      return `${this.protocol()}:${uri}`;
+    }
+    return `${this.protocol()}://${location.host}${uri}`;
+  }
+  disconnect(callback, code, reason) {
+    this.connectClock++;
+    this.closeWasClean = true;
+    this.reconnectTimer.reset();
+    this.teardown(callback, code, reason);
+  }
+  connect(params) {
+    this.connectClock++;
+    if (params) {
+      console && console.log("passing params to connect is deprecated. Instead pass :params to the Socket constructor");
+      this.params = closure(params);
+    }
+    if (this.conn) {
+      return;
+    }
+    this.closeWasClean = false;
+    this.conn = new this.transport(this.endPointURL());
+    this.conn.binaryType = this.binaryType;
+    this.conn.timeout = this.longpollerTimeout;
+    this.conn.onopen = () => this.onConnOpen();
+    this.conn.onerror = (error) => this.onConnError(error);
+    this.conn.onmessage = (event) => this.onConnMessage(event);
+    this.conn.onclose = (event) => this.onConnClose(event);
+  }
+  log(kind, msg, data) {
+    this.logger(kind, msg, data);
+  }
+  hasLogger() {
+    return this.logger !== null;
+  }
+  onOpen(callback) {
+    let ref = this.makeRef();
+    this.stateChangeCallbacks.open.push([ref, callback]);
+    return ref;
+  }
+  onClose(callback) {
+    let ref = this.makeRef();
+    this.stateChangeCallbacks.close.push([ref, callback]);
+    return ref;
+  }
+  onError(callback) {
+    let ref = this.makeRef();
+    this.stateChangeCallbacks.error.push([ref, callback]);
+    return ref;
+  }
+  onMessage(callback) {
+    let ref = this.makeRef();
+    this.stateChangeCallbacks.message.push([ref, callback]);
+    return ref;
+  }
+  onConnOpen() {
+    if (this.hasLogger())
+      this.log("transport", `connected to ${this.endPointURL()}`);
+    this.closeWasClean = false;
+    this.establishedConnections++;
+    this.flushSendBuffer();
+    this.reconnectTimer.reset();
+    this.resetHeartbeat();
+    this.stateChangeCallbacks.open.forEach(([, callback]) => callback());
+  }
+  heartbeatTimeout() {
+    if (this.pendingHeartbeatRef) {
+      this.pendingHeartbeatRef = null;
+      if (this.hasLogger()) {
+        this.log("transport", "heartbeat timeout. Attempting to re-establish connection");
+      }
+      this.abnormalClose("heartbeat timeout");
+    }
+  }
+  resetHeartbeat() {
+    if (this.conn && this.conn.skipHeartbeat) {
+      return;
+    }
+    this.pendingHeartbeatRef = null;
+    clearTimeout(this.heartbeatTimer);
+    setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs);
+  }
+  teardown(callback, code, reason) {
+    if (!this.conn) {
+      return callback && callback();
+    }
+    this.waitForBufferDone(() => {
+      if (this.conn) {
+        if (code) {
+          this.conn.close(code, reason || "");
+        } else {
+          this.conn.close();
+        }
+      }
+      this.waitForSocketClosed(() => {
+        if (this.conn) {
+          this.conn.onclose = function() {
+          };
+          this.conn = null;
+        }
+        callback && callback();
+      });
+    });
+  }
+  waitForBufferDone(callback, tries = 1) {
+    if (tries === 5 || !this.conn || !this.conn.bufferedAmount) {
+      callback();
+      return;
+    }
+    setTimeout(() => {
+      this.waitForBufferDone(callback, tries + 1);
+    }, 150 * tries);
+  }
+  waitForSocketClosed(callback, tries = 1) {
+    if (tries === 5 || !this.conn || this.conn.readyState === SOCKET_STATES.closed) {
+      callback();
+      return;
+    }
+    setTimeout(() => {
+      this.waitForSocketClosed(callback, tries + 1);
+    }, 150 * tries);
+  }
+  onConnClose(event) {
+    if (this.hasLogger())
+      this.log("transport", "close", event);
+    this.triggerChanError();
+    clearTimeout(this.heartbeatTimer);
+    if (!this.closeWasClean) {
+      this.reconnectTimer.scheduleTimeout();
+    }
+    this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event));
+  }
+  onConnError(error) {
+    if (this.hasLogger())
+      this.log("transport", error);
+    let transportBefore = this.transport;
+    let establishedBefore = this.establishedConnections;
+    this.stateChangeCallbacks.error.forEach(([, callback]) => {
+      callback(error, transportBefore, establishedBefore);
+    });
+    if (transportBefore === this.transport || establishedBefore > 0) {
+      this.triggerChanError();
+    }
+  }
+  triggerChanError() {
+    this.channels.forEach((channel) => {
+      if (!(channel.isErrored() || channel.isLeaving() || channel.isClosed())) {
+        channel.trigger(CHANNEL_EVENTS.error);
+      }
+    });
+  }
+  connectionState() {
+    switch (this.conn && this.conn.readyState) {
+      case SOCKET_STATES.connecting:
+        return "connecting";
+      case SOCKET_STATES.open:
+        return "open";
+      case SOCKET_STATES.closing:
+        return "closing";
+      default:
+        return "closed";
+    }
+  }
+  isConnected() {
+    return this.connectionState() === "open";
+  }
+  remove(channel) {
+    this.off(channel.stateChangeRefs);
+    this.channels = this.channels.filter((c) => c.joinRef() !== channel.joinRef());
+  }
+  off(refs) {
+    for (let key in this.stateChangeCallbacks) {
+      this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => {
+        return refs.indexOf(ref) === -1;
+      });
+    }
+  }
+  channel(topic, chanParams = {}) {
+    let chan = new Channel(topic, chanParams, this);
+    this.channels.push(chan);
+    return chan;
+  }
+  push(data) {
+    if (this.hasLogger()) {
+      let { topic, event, payload, ref, join_ref } = data;
+      this.log("push", `${topic} ${event} (${join_ref}, ${ref})`, payload);
+    }
+    if (this.isConnected()) {
+      this.encode(data, (result) => this.conn.send(result));
+    } else {
+      this.sendBuffer.push(() => this.encode(data, (result) => this.conn.send(result)));
+    }
+  }
+  makeRef() {
+    let newRef = this.ref + 1;
+    if (newRef === this.ref) {
+      this.ref = 0;
+    } else {
+      this.ref = newRef;
+    }
+    return this.ref.toString();
+  }
+  sendHeartbeat() {
+    if (this.pendingHeartbeatRef && !this.isConnected()) {
+      return;
+    }
+    this.pendingHeartbeatRef = this.makeRef();
+    this.push({ topic: "phoenix", event: "heartbeat", payload: {}, ref: this.pendingHeartbeatRef });
+    this.heartbeatTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs);
+  }
+  abnormalClose(reason) {
+    this.closeWasClean = false;
+    if (this.isConnected()) {
+      this.conn.close(WS_CLOSE_NORMAL, reason);
+    }
+  }
+  flushSendBuffer() {
+    if (this.isConnected() && this.sendBuffer.length > 0) {
+      this.sendBuffer.forEach((callback) => callback());
+      this.sendBuffer = [];
+    }
+  }
+  onConnMessage(rawMessage) {
+    this.decode(rawMessage.data, (msg) => {
+      let { topic, event, payload, ref, join_ref } = msg;
+      if (ref && ref === this.pendingHeartbeatRef) {
+        clearTimeout(this.heartbeatTimer);
+        this.pendingHeartbeatRef = null;
+        setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs);
+      }
+      if (this.hasLogger())
+        this.log("receive", `${payload.status || ""} ${topic} ${event} ${ref && "(" + ref + ")" || ""}`, payload);
+      for (let i = 0; i < this.channels.length; i++) {
+        const channel = this.channels[i];
+        if (!channel.isMember(topic, event, payload, join_ref)) {
+          continue;
+        }
+        channel.trigger(event, payload, ref, join_ref);
+      }
+      for (let i = 0; i < this.stateChangeCallbacks.message.length; i++) {
+        let [, callback] = this.stateChangeCallbacks.message[i];
+        callback(msg);
+      }
+    });
+  }
+  leaveOpenTopic(topic) {
+    let dupChannel = this.channels.find((c) => c.topic === topic && (c.isJoined() || c.isJoining()));
+    if (dupChannel) {
+      if (this.hasLogger())
+        this.log("transport", `leaving duplicate topic "${topic}"`);
+      dupChannel.leave();
+    }
+  }
+};
+//# sourceMappingURL=phoenix.cjs.js.map
diff --git a/priv/static/phoenix.cjs.js.map b/priv/static/phoenix.cjs.js.map
new file mode 100644
index 000000000..9c93c4bb9
--- /dev/null
+++ b/priv/static/phoenix.cjs.js.map
@@ -0,0 +1,7 @@
+{
+  "version": 3,
+  "sources": ["../../assets/js/phoenix/index.js", "../../assets/js/phoenix/utils.js", "../../assets/js/phoenix/constants.js", "../../assets/js/phoenix/push.js", "../../assets/js/phoenix/timer.js", "../../assets/js/phoenix/channel.js", "../../assets/js/phoenix/ajax.js", "../../assets/js/phoenix/longpoll.js", "../../assets/js/phoenix/presence.js", "../../assets/js/phoenix/serializer.js", "../../assets/js/phoenix/socket.js"],
+  "sourcesContent": ["/**\n * Phoenix Channels JavaScript client\n *\n * ## Socket Connection\n *\n * A single connection is established to the server and\n * channels are multiplexed over the connection.\n * Connect to the server using the `Socket` class:\n *\n * ```javascript\n * let socket = new Socket(\"/socket\", {params: {userToken: \"123\"}})\n * socket.connect()\n * ```\n *\n * The `Socket` constructor takes the mount point of the socket,\n * the authentication params, as well as options that can be found in\n * the Socket docs, such as configuring the `LongPoll` transport, and\n * heartbeat.\n *\n * ## Channels\n *\n * Channels are isolated, concurrent processes on the server that\n * subscribe to topics and broker events between the client and server.\n * To join a channel, you must provide the topic, and channel params for\n * authorization. Here's an example chat room example where `\"new_msg\"`\n * events are listened for, messages are pushed to the server, and\n * the channel is joined with ok/error/timeout matches:\n *\n * ```javascript\n * let channel = socket.channel(\"room:123\", {token: roomToken})\n * channel.on(\"new_msg\", msg => console.log(\"Got message\", msg) )\n * $input.onEnter( e => {\n *   channel.push(\"new_msg\", {body: e.target.val}, 10000)\n *     .receive(\"ok\", (msg) => console.log(\"created message\", msg) )\n *     .receive(\"error\", (reasons) => console.log(\"create failed\", reasons) )\n *     .receive(\"timeout\", () => console.log(\"Networking issue...\") )\n * })\n *\n * channel.join()\n *   .receive(\"ok\", ({messages}) => console.log(\"catching up\", messages) )\n *   .receive(\"error\", ({reason}) => console.log(\"failed join\", reason) )\n *   .receive(\"timeout\", () => console.log(\"Networking issue. Still waiting...\"))\n *```\n *\n * ## Joining\n *\n * Creating a channel with `socket.channel(topic, params)`, binds the params to\n * `channel.params`, which are sent up on `channel.join()`.\n * Subsequent rejoins will send up the modified params for\n * updating authorization params, or passing up last_message_id information.\n * Successful joins receive an \"ok\" status, while unsuccessful joins\n * receive \"error\".\n *\n * With the default serializers and WebSocket transport, JSON text frames are\n * used for pushing a JSON object literal. If an `ArrayBuffer` instance is provided,\n * binary encoding will be used and the message will be sent with the binary\n * opcode.\n *\n * *Note*: binary messages are only supported on the WebSocket transport.\n *\n * ## Duplicate Join Subscriptions\n *\n * While the client may join any number of topics on any number of channels,\n * the client may only hold a single subscription for each unique topic at any\n * given time. When attempting to create a duplicate subscription,\n * the server will close the existing channel, log a warning, and\n * spawn a new channel for the topic. The client will have their\n * `channel.onClose` callbacks fired for the existing channel, and the new\n * channel join will have its receive hooks processed as normal.\n *\n * ## Pushing Messages\n *\n * From the previous example, we can see that pushing messages to the server\n * can be done with `channel.push(eventName, payload)` and we can optionally\n * receive responses from the push. Additionally, we can use\n * `receive(\"timeout\", callback)` to abort waiting for our other `receive` hooks\n *  and take action after some period of waiting. The default timeout is 10000ms.\n *\n *\n * ## Socket Hooks\n *\n * Lifecycle events of the multiplexed connection can be hooked into via\n * `socket.onError()` and `socket.onClose()` events, ie:\n *\n * ```javascript\n * socket.onError( () => console.log(\"there was an error with the connection!\") )\n * socket.onClose( () => console.log(\"the connection dropped\") )\n * ```\n *\n *\n * ## Channel Hooks\n *\n * For each joined channel, you can bind to `onError` and `onClose` events\n * to monitor the channel lifecycle, ie:\n *\n * ```javascript\n * channel.onError( () => console.log(\"there was an error!\") )\n * channel.onClose( () => console.log(\"the channel has gone away gracefully\") )\n * ```\n *\n * ### onError hooks\n *\n * `onError` hooks are invoked if the socket connection drops, or the channel\n * crashes on the server. In either case, a channel rejoin is attempted\n * automatically in an exponential backoff manner.\n *\n * ### onClose hooks\n *\n * `onClose` hooks are invoked only in two cases. 1) the channel explicitly\n * closed on the server, or 2). The client explicitly closed, by calling\n * `channel.leave()`\n *\n *\n * ## Presence\n *\n * The `Presence` object provides features for syncing presence information\n * from the server with the client and handling presences joining and leaving.\n *\n * ### Syncing state from the server\n *\n * To sync presence state from the server, first instantiate an object and\n * pass your channel in to track lifecycle events:\n *\n * ```javascript\n * let channel = socket.channel(\"some:topic\")\n * let presence = new Presence(channel)\n * ```\n *\n * Next, use the `presence.onSync` callback to react to state changes\n * from the server. For example, to render the list of users every time\n * the list changes, you could write:\n *\n * ```javascript\n * presence.onSync(() => {\n *   myRenderUsersFunction(presence.list())\n * })\n * ```\n *\n * ### Listing Presences\n *\n * `presence.list` is used to return a list of presence information\n * based on the local state of metadata. By default, all presence\n * metadata is returned, but a `listBy` function can be supplied to\n * allow the client to select which metadata to use for a given presence.\n * For example, you may have a user online from different devices with\n * a metadata status of \"online\", but they have set themselves to \"away\"\n * on another device. In this case, the app may choose to use the \"away\"\n * status for what appears on the UI. The example below defines a `listBy`\n * function which prioritizes the first metadata which was registered for\n * each user. This could be the first tab they opened, or the first device\n * they came online from:\n *\n * ```javascript\n * let listBy = (id, {metas: [first, ...rest]}) => {\n *   first.count = rest.length + 1 // count of this user's presences\n *   first.id = id\n *   return first\n * }\n * let onlineUsers = presence.list(listBy)\n * ```\n *\n * ### Handling individual presence join and leave events\n *\n * The `presence.onJoin` and `presence.onLeave` callbacks can be used to\n * react to individual presences joining and leaving the app. For example:\n *\n * ```javascript\n * let presence = new Presence(channel)\n *\n * // detect if user has joined for the 1st time or from another tab/device\n * presence.onJoin((id, current, newPres) => {\n *   if(!current){\n *     console.log(\"user has entered for the first time\", newPres)\n *   } else {\n *     console.log(\"user additional presence\", newPres)\n *   }\n * })\n *\n * // detect if user has left from all tabs/devices, or is still present\n * presence.onLeave((id, current, leftPres) => {\n *   if(current.metas.length === 0){\n *     console.log(\"user has left from all devices\", leftPres)\n *   } else {\n *     console.log(\"user left from a device\", leftPres)\n *   }\n * })\n * // receive presence data from server\n * presence.onSync(() => {\n *   displayUsers(presence.list())\n * })\n * ```\n * @module phoenix\n */\n\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Presence from \"./presence\"\nimport Serializer from \"./serializer\"\nimport Socket from \"./socket\"\n\nexport {\n  Channel,\n  LongPoll,\n  Presence,\n  Serializer,\n  Socket\n}\n", "// wraps value in closure or returns closure\nexport let closure = (value) => {\n  if(typeof value === \"function\"){\n    return value\n  } else {\n    let closure = function (){ return value }\n    return closure\n  }\n}\n", "export const globalSelf = typeof self !== \"undefined\" ? self : null\nexport const phxWindow = typeof window !== \"undefined\" ? window : null\nexport const global = globalSelf || phxWindow || this\nexport const DEFAULT_VSN = \"2.0.0\"\nexport const SOCKET_STATES = {connecting: 0, open: 1, closing: 2, closed: 3}\nexport const DEFAULT_TIMEOUT = 10000\nexport const WS_CLOSE_NORMAL = 1000\nexport const CHANNEL_STATES = {\n  closed: \"closed\",\n  errored: \"errored\",\n  joined: \"joined\",\n  joining: \"joining\",\n  leaving: \"leaving\",\n}\nexport const CHANNEL_EVENTS = {\n  close: \"phx_close\",\n  error: \"phx_error\",\n  join: \"phx_join\",\n  reply: \"phx_reply\",\n  leave: \"phx_leave\"\n}\n\nexport const TRANSPORTS = {\n  longpoll: \"longpoll\",\n  websocket: \"websocket\"\n}\nexport const XHR_STATES = {\n  complete: 4\n}\n", "/**\n * Initializes the Push\n * @param {Channel} channel - The Channel\n * @param {string} event - The event, for example `\"phx_join\"`\n * @param {Object} payload - The payload, for example `{user_id: 123}`\n * @param {number} timeout - The push timeout in milliseconds\n */\nexport default class Push {\n  constructor(channel, event, payload, timeout){\n    this.channel = channel\n    this.event = event\n    this.payload = payload || function (){ return {} }\n    this.receivedResp = null\n    this.timeout = timeout\n    this.timeoutTimer = null\n    this.recHooks = []\n    this.sent = false\n  }\n\n  /**\n   *\n   * @param {number} timeout\n   */\n  resend(timeout){\n    this.timeout = timeout\n    this.reset()\n    this.send()\n  }\n\n  /**\n   *\n   */\n  send(){\n    if(this.hasReceived(\"timeout\")){ return }\n    this.startTimeout()\n    this.sent = true\n    this.channel.socket.push({\n      topic: this.channel.topic,\n      event: this.event,\n      payload: this.payload(),\n      ref: this.ref,\n      join_ref: this.channel.joinRef()\n    })\n  }\n\n  /**\n   *\n   * @param {*} status\n   * @param {*} callback\n   */\n  receive(status, callback){\n    if(this.hasReceived(status)){\n      callback(this.receivedResp.response)\n    }\n\n    this.recHooks.push({status, callback})\n    return this\n  }\n\n  /**\n   * @private\n   */\n  reset(){\n    this.cancelRefEvent()\n    this.ref = null\n    this.refEvent = null\n    this.receivedResp = null\n    this.sent = false\n  }\n\n  /**\n   * @private\n   */\n  matchReceive({status, response, _ref}){\n    this.recHooks.filter(h => h.status === status)\n      .forEach(h => h.callback(response))\n  }\n\n  /**\n   * @private\n   */\n  cancelRefEvent(){\n    if(!this.refEvent){ return }\n    this.channel.off(this.refEvent)\n  }\n\n  /**\n   * @private\n   */\n  cancelTimeout(){\n    clearTimeout(this.timeoutTimer)\n    this.timeoutTimer = null\n  }\n\n  /**\n   * @private\n   */\n  startTimeout(){\n    if(this.timeoutTimer){ this.cancelTimeout() }\n    this.ref = this.channel.socket.makeRef()\n    this.refEvent = this.channel.replyEventName(this.ref)\n\n    this.channel.on(this.refEvent, payload => {\n      this.cancelRefEvent()\n      this.cancelTimeout()\n      this.receivedResp = payload\n      this.matchReceive(payload)\n    })\n\n    this.timeoutTimer = setTimeout(() => {\n      this.trigger(\"timeout\", {})\n    }, this.timeout)\n  }\n\n  /**\n   * @private\n   */\n  hasReceived(status){\n    return this.receivedResp && this.receivedResp.status === status\n  }\n\n  /**\n   * @private\n   */\n  trigger(status, response){\n    this.channel.trigger(this.refEvent, {status, response})\n  }\n}\n", "/**\n *\n * Creates a timer that accepts a `timerCalc` function to perform\n * calculated timeout retries, such as exponential backoff.\n *\n * @example\n * let reconnectTimer = new Timer(() => this.connect(), function(tries){\n *   return [1000, 5000, 10000][tries - 1] || 10000\n * })\n * reconnectTimer.scheduleTimeout() // fires after 1000\n * reconnectTimer.scheduleTimeout() // fires after 5000\n * reconnectTimer.reset()\n * reconnectTimer.scheduleTimeout() // fires after 1000\n *\n * @param {Function} callback\n * @param {Function} timerCalc\n */\nexport default class Timer {\n  constructor(callback, timerCalc){\n    this.callback = callback\n    this.timerCalc = timerCalc\n    this.timer = null\n    this.tries = 0\n  }\n\n  reset(){\n    this.tries = 0\n    clearTimeout(this.timer)\n  }\n\n  /**\n   * Cancels any previous scheduleTimeout and schedules callback\n   */\n  scheduleTimeout(){\n    clearTimeout(this.timer)\n\n    this.timer = setTimeout(() => {\n      this.tries = this.tries + 1\n      this.callback()\n    }, this.timerCalc(this.tries + 1))\n  }\n}\n", "import {closure} from \"./utils\"\nimport {\n  CHANNEL_EVENTS,\n  CHANNEL_STATES,\n} from \"./constants\"\n\nimport Push from \"./push\"\nimport Timer from \"./timer\"\n\n/**\n *\n * @param {string} topic\n * @param {(Object|function)} params\n * @param {Socket} socket\n */\nexport default class Channel {\n  constructor(topic, params, socket){\n    this.state = CHANNEL_STATES.closed\n    this.topic = topic\n    this.params = closure(params || {})\n    this.socket = socket\n    this.bindings = []\n    this.bindingRef = 0\n    this.timeout = this.socket.timeout\n    this.joinedOnce = false\n    this.joinPush = new Push(this, CHANNEL_EVENTS.join, this.params, this.timeout)\n    this.pushBuffer = []\n    this.stateChangeRefs = []\n\n    this.rejoinTimer = new Timer(() => {\n      if(this.socket.isConnected()){ this.rejoin() }\n    }, this.socket.rejoinAfterMs)\n    this.stateChangeRefs.push(this.socket.onError(() => this.rejoinTimer.reset()))\n    this.stateChangeRefs.push(this.socket.onOpen(() => {\n      this.rejoinTimer.reset()\n      if(this.isErrored()){ this.rejoin() }\n    })\n    )\n    this.joinPush.receive(\"ok\", () => {\n      this.state = CHANNEL_STATES.joined\n      this.rejoinTimer.reset()\n      this.pushBuffer.forEach(pushEvent => pushEvent.send())\n      this.pushBuffer = []\n    })\n    this.joinPush.receive(\"error\", () => {\n      this.state = CHANNEL_STATES.errored\n      if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n    })\n    this.onClose(() => {\n      this.rejoinTimer.reset()\n      if(this.socket.hasLogger()) this.socket.log(\"channel\", `close ${this.topic} ${this.joinRef()}`)\n      this.state = CHANNEL_STATES.closed\n      this.socket.remove(this)\n    })\n    this.onError(reason => {\n      if(this.socket.hasLogger()) this.socket.log(\"channel\", `error ${this.topic}`, reason)\n      if(this.isJoining()){ this.joinPush.reset() }\n      this.state = CHANNEL_STATES.errored\n      if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n    })\n    this.joinPush.receive(\"timeout\", () => {\n      if(this.socket.hasLogger()) this.socket.log(\"channel\", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout)\n      let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout)\n      leavePush.send()\n      this.state = CHANNEL_STATES.errored\n      this.joinPush.reset()\n      if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n    })\n    this.on(CHANNEL_EVENTS.reply, (payload, ref) => {\n      this.trigger(this.replyEventName(ref), payload)\n    })\n  }\n\n  /**\n   * Join the channel\n   * @param {integer} timeout\n   * @returns {Push}\n   */\n  join(timeout = this.timeout){\n    if(this.joinedOnce){\n      throw new Error(\"tried to join multiple times. 'join' can only be called a single time per channel instance\")\n    } else {\n      this.timeout = timeout\n      this.joinedOnce = true\n      this.rejoin()\n      return this.joinPush\n    }\n  }\n\n  /**\n   * Hook into channel close\n   * @param {Function} callback\n   */\n  onClose(callback){\n    this.on(CHANNEL_EVENTS.close, callback)\n  }\n\n  /**\n   * Hook into channel errors\n   * @param {Function} callback\n   */\n  onError(callback){\n    return this.on(CHANNEL_EVENTS.error, reason => callback(reason))\n  }\n\n  /**\n   * Subscribes on channel events\n   *\n   * Subscription returns a ref counter, which can be used later to\n   * unsubscribe the exact event listener\n   *\n   * @example\n   * const ref1 = channel.on(\"event\", do_stuff)\n   * const ref2 = channel.on(\"event\", do_other_stuff)\n   * channel.off(\"event\", ref1)\n   * // Since unsubscription, do_stuff won't fire,\n   * // while do_other_stuff will keep firing on the \"event\"\n   *\n   * @param {string} event\n   * @param {Function} callback\n   * @returns {integer} ref\n   */\n  on(event, callback){\n    let ref = this.bindingRef++\n    this.bindings.push({event, ref, callback})\n    return ref\n  }\n\n  /**\n   * Unsubscribes off of channel events\n   *\n   * Use the ref returned from a channel.on() to unsubscribe one\n   * handler, or pass nothing for the ref to unsubscribe all\n   * handlers for the given event.\n   *\n   * @example\n   * // Unsubscribe the do_stuff handler\n   * const ref1 = channel.on(\"event\", do_stuff)\n   * channel.off(\"event\", ref1)\n   *\n   * // Unsubscribe all handlers from event\n   * channel.off(\"event\")\n   *\n   * @param {string} event\n   * @param {integer} ref\n   */\n  off(event, ref){\n    this.bindings = this.bindings.filter((bind) => {\n      return !(bind.event === event && (typeof ref === \"undefined\" || ref === bind.ref))\n    })\n  }\n\n  /**\n   * @private\n   */\n  canPush(){ return this.socket.isConnected() && this.isJoined() }\n\n  /**\n   * Sends a message `event` to phoenix with the payload `payload`.\n   * Phoenix receives this in the `handle_in(event, payload, socket)`\n   * function. if phoenix replies or it times out (default 10000ms),\n   * then optionally the reply can be received.\n   *\n   * @example\n   * channel.push(\"event\")\n   *   .receive(\"ok\", payload => console.log(\"phoenix replied:\", payload))\n   *   .receive(\"error\", err => console.log(\"phoenix errored\", err))\n   *   .receive(\"timeout\", () => console.log(\"timed out pushing\"))\n   * @param {string} event\n   * @param {Object} payload\n   * @param {number} [timeout]\n   * @returns {Push}\n   */\n  push(event, payload, timeout = this.timeout){\n    payload = payload || {}\n    if(!this.joinedOnce){\n      throw new Error(`tried to push '${event}' to '${this.topic}' before joining. Use channel.join() before pushing events`)\n    }\n    let pushEvent = new Push(this, event, function (){ return payload }, timeout)\n    if(this.canPush()){\n      pushEvent.send()\n    } else {\n      pushEvent.startTimeout()\n      this.pushBuffer.push(pushEvent)\n    }\n\n    return pushEvent\n  }\n\n  /** Leaves the channel\n   *\n   * Unsubscribes from server events, and\n   * instructs channel to terminate on server\n   *\n   * Triggers onClose() hooks\n   *\n   * To receive leave acknowledgements, use the `receive`\n   * hook to bind to the server ack, ie:\n   *\n   * @example\n   * channel.leave().receive(\"ok\", () => alert(\"left!\") )\n   *\n   * @param {integer} timeout\n   * @returns {Push}\n   */\n  leave(timeout = this.timeout){\n    this.rejoinTimer.reset()\n    this.joinPush.cancelTimeout()\n\n    this.state = CHANNEL_STATES.leaving\n    let onClose = () => {\n      if(this.socket.hasLogger()) this.socket.log(\"channel\", `leave ${this.topic}`)\n      this.trigger(CHANNEL_EVENTS.close, \"leave\")\n    }\n    let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout)\n    leavePush.receive(\"ok\", () => onClose())\n      .receive(\"timeout\", () => onClose())\n    leavePush.send()\n    if(!this.canPush()){ leavePush.trigger(\"ok\", {}) }\n\n    return leavePush\n  }\n\n  /**\n   * Overridable message hook\n   *\n   * Receives all events for specialized message handling\n   * before dispatching to the channel callbacks.\n   *\n   * Must return the payload, modified or unmodified\n   * @param {string} event\n   * @param {Object} payload\n   * @param {integer} ref\n   * @returns {Object}\n   */\n  onMessage(_event, payload, _ref){ return payload }\n\n  /**\n   * @private\n   */\n  isMember(topic, event, payload, joinRef){\n    if(this.topic !== topic){ return false }\n\n    if(joinRef && joinRef !== this.joinRef()){\n      if(this.socket.hasLogger()) this.socket.log(\"channel\", \"dropping outdated message\", {topic, event, payload, joinRef})\n      return false\n    } else {\n      return true\n    }\n  }\n\n  /**\n   * @private\n   */\n  joinRef(){ return this.joinPush.ref }\n\n  /**\n   * @private\n   */\n  rejoin(timeout = this.timeout){\n    if(this.isLeaving()){ return }\n    this.socket.leaveOpenTopic(this.topic)\n    this.state = CHANNEL_STATES.joining\n    this.joinPush.resend(timeout)\n  }\n\n  /**\n   * @private\n   */\n  trigger(event, payload, ref, joinRef){\n    let handledPayload = this.onMessage(event, payload, ref, joinRef)\n    if(payload && !handledPayload){ throw new Error(\"channel onMessage callbacks must return the payload, modified or unmodified\") }\n\n    let eventBindings = this.bindings.filter(bind => bind.event === event)\n\n    for(let i = 0; i < eventBindings.length; i++){\n      let bind = eventBindings[i]\n      bind.callback(handledPayload, ref, joinRef || this.joinRef())\n    }\n  }\n\n  /**\n   * @private\n   */\n  replyEventName(ref){ return `chan_reply_${ref}` }\n\n  /**\n   * @private\n   */\n  isClosed(){ return this.state === CHANNEL_STATES.closed }\n\n  /**\n   * @private\n   */\n  isErrored(){ return this.state === CHANNEL_STATES.errored }\n\n  /**\n   * @private\n   */\n  isJoined(){ return this.state === CHANNEL_STATES.joined }\n\n  /**\n   * @private\n   */\n  isJoining(){ return this.state === CHANNEL_STATES.joining }\n\n  /**\n   * @private\n   */\n  isLeaving(){ return this.state === CHANNEL_STATES.leaving }\n}\n", "import {\n  global,\n  XHR_STATES\n} from \"./constants\"\n\nexport default class Ajax {\n\n  static request(method, endPoint, accept, body, timeout, ontimeout, callback){\n    if(global.XDomainRequest){\n      let req = new global.XDomainRequest() // IE8, IE9\n      this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback)\n    } else {\n      let req = new global.XMLHttpRequest() // IE7+, Firefox, Chrome, Opera, Safari\n      this.xhrRequest(req, method, endPoint, accept, body, timeout, ontimeout, callback)\n    }\n  }\n\n  static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback){\n    req.timeout = timeout\n    req.open(method, endPoint)\n    req.onload = () => {\n      let response = this.parseJSON(req.responseText)\n      callback && callback(response)\n    }\n    if(ontimeout){ req.ontimeout = ontimeout }\n\n    // Work around bug in IE9 that requires an attached onprogress handler\n    req.onprogress = () => { }\n\n    req.send(body)\n  }\n\n  static xhrRequest(req, method, endPoint, accept, body, timeout, ontimeout, callback){\n    req.open(method, endPoint, true)\n    req.timeout = timeout\n    req.setRequestHeader(\"Content-Type\", accept)\n    req.onerror = () => { callback && callback(null) }\n    req.onreadystatechange = () => {\n      if(req.readyState === XHR_STATES.complete && callback){\n        let response = this.parseJSON(req.responseText)\n        callback(response)\n      }\n    }\n    if(ontimeout){ req.ontimeout = ontimeout }\n\n    req.send(body)\n  }\n\n  static parseJSON(resp){\n    if(!resp || resp === \"\"){ return null }\n\n    try {\n      return JSON.parse(resp)\n    } catch (e){\n      console && console.log(\"failed to parse JSON response\", resp)\n      return null\n    }\n  }\n\n  static serialize(obj, parentKey){\n    let queryStr = []\n    for(var key in obj){\n      if(!Object.prototype.hasOwnProperty.call(obj, key)){ continue }\n      let paramKey = parentKey ? `${parentKey}[${key}]` : key\n      let paramVal = obj[key]\n      if(typeof paramVal === \"object\"){\n        queryStr.push(this.serialize(paramVal, paramKey))\n      } else {\n        queryStr.push(encodeURIComponent(paramKey) + \"=\" + encodeURIComponent(paramVal))\n      }\n    }\n    return queryStr.join(\"&\")\n  }\n\n  static appendParams(url, params){\n    if(Object.keys(params).length === 0){ return url }\n\n    let prefix = url.match(/\\?/) ? \"&\" : \"?\"\n    return `${url}${prefix}${this.serialize(params)}`\n  }\n}\n", "import {\n  SOCKET_STATES,\n  TRANSPORTS\n} from \"./constants\"\n\nimport Ajax from \"./ajax\"\n\nexport default class LongPoll {\n\n  constructor(endPoint){\n    this.endPoint = null\n    this.token = null\n    this.skipHeartbeat = true\n    this.onopen = function (){ } // noop\n    this.onerror = function (){ } // noop\n    this.onmessage = function (){ } // noop\n    this.onclose = function (){ } // noop\n    this.pollEndpoint = this.normalizeEndpoint(endPoint)\n    this.readyState = SOCKET_STATES.connecting\n\n    this.poll()\n  }\n\n  normalizeEndpoint(endPoint){\n    return (endPoint\n      .replace(\"ws://\", \"http://\")\n      .replace(\"wss://\", \"https://\")\n      .replace(new RegExp(\"(.*)\\/\" + TRANSPORTS.websocket), \"$1/\" + TRANSPORTS.longpoll))\n  }\n\n  endpointURL(){\n    return Ajax.appendParams(this.pollEndpoint, {token: this.token})\n  }\n\n  closeAndRetry(){\n    this.close()\n    this.readyState = SOCKET_STATES.connecting\n  }\n\n  ontimeout(){\n    this.onerror(\"timeout\")\n    this.closeAndRetry()\n  }\n\n  poll(){\n    if(!(this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting)){ return }\n\n    Ajax.request(\"GET\", this.endpointURL(), \"application/json\", null, this.timeout, this.ontimeout.bind(this), (resp) => {\n      if(resp){\n        var {status, token, messages} = resp\n        this.token = token\n      } else {\n        status = 0\n      }\n\n      switch(status){\n        case 200:\n          messages.forEach(msg => {\n            // Tasks are what things like event handlers, setTimeout callbacks,\n            // promise resolves and more are run within.\n            // In modern browsers, there are two different kinds of tasks,\n            // microtasks and macrotasks.\n            // Microtasks are mainly used for Promises, while macrotasks are\n            // used for everything else.\n            // Microtasks always have priority over macrotasks. If the JS engine\n            // is looking for a task to run, it will always try to empty the\n            // microtask queue before attempting to run anything from the\n            // macrotask queue.\n            //\n            // For the WebSocket transport, messages always arrive in their own\n            // event. This means that if any promises are resolved from within,\n            // their callbacks will always finish execution by the time the\n            // next message event handler is run.\n            //\n            // In order to emulate this behaviour, we need to make sure each\n            // onmessage handler is run within it's own macrotask.\n            setTimeout(() => {\n              this.onmessage({data: msg})\n            }, 0)\n          })\n          this.poll()\n          break\n        case 204:\n          this.poll()\n          break\n        case 410:\n          this.readyState = SOCKET_STATES.open\n          this.onopen()\n          this.poll()\n          break\n        case 403:\n          this.onerror()\n          this.close()\n          break\n        case 0:\n        case 500:\n          this.onerror()\n          this.closeAndRetry()\n          break\n        default: throw new Error(`unhandled poll status ${status}`)\n      }\n    })\n  }\n\n  send(body){\n    Ajax.request(\"POST\", this.endpointURL(), \"application/json\", body, this.timeout, this.onerror.bind(this, \"timeout\"), (resp) => {\n      if(!resp || resp.status !== 200){\n        this.onerror(resp && resp.status)\n        this.closeAndRetry()\n      }\n    })\n  }\n\n  close(_code, _reason){\n    this.readyState = SOCKET_STATES.closed\n    this.onclose()\n  }\n}\n", "/**\n * Initializes the Presence\n * @param {Channel} channel - The Channel\n * @param {Object} opts - The options,\n *        for example `{events: {state: \"state\", diff: \"diff\"}}`\n */\nexport default class Presence {\n\n  constructor(channel, opts = {}){\n    let events = opts.events || {state: \"presence_state\", diff: \"presence_diff\"}\n    this.state = {}\n    this.pendingDiffs = []\n    this.channel = channel\n    this.joinRef = null\n    this.caller = {\n      onJoin: function (){ },\n      onLeave: function (){ },\n      onSync: function (){ }\n    }\n\n    this.channel.on(events.state, newState => {\n      let {onJoin, onLeave, onSync} = this.caller\n\n      this.joinRef = this.channel.joinRef()\n      this.state = Presence.syncState(this.state, newState, onJoin, onLeave)\n\n      this.pendingDiffs.forEach(diff => {\n        this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n      })\n      this.pendingDiffs = []\n      onSync()\n    })\n\n    this.channel.on(events.diff, diff => {\n      let {onJoin, onLeave, onSync} = this.caller\n\n      if(this.inPendingSyncState()){\n        this.pendingDiffs.push(diff)\n      } else {\n        this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n        onSync()\n      }\n    })\n  }\n\n  onJoin(callback){ this.caller.onJoin = callback }\n\n  onLeave(callback){ this.caller.onLeave = callback }\n\n  onSync(callback){ this.caller.onSync = callback }\n\n  list(by){ return Presence.list(this.state, by) }\n\n  inPendingSyncState(){\n    return !this.joinRef || (this.joinRef !== this.channel.joinRef())\n  }\n\n  // lower-level public static API\n\n  /**\n   * Used to sync the list of presences on the server\n   * with the client's state. An optional `onJoin` and `onLeave` callback can\n   * be provided to react to changes in the client's local presences across\n   * disconnects and reconnects with the server.\n   *\n   * @returns {Presence}\n   */\n  static syncState(currentState, newState, onJoin, onLeave){\n    let state = this.clone(currentState)\n    let joins = {}\n    let leaves = {}\n\n    this.map(state, (key, presence) => {\n      if(!newState[key]){\n        leaves[key] = presence\n      }\n    })\n    this.map(newState, (key, newPresence) => {\n      let currentPresence = state[key]\n      if(currentPresence){\n        let newRefs = newPresence.metas.map(m => m.phx_ref)\n        let curRefs = currentPresence.metas.map(m => m.phx_ref)\n        let joinedMetas = newPresence.metas.filter(m => curRefs.indexOf(m.phx_ref) < 0)\n        let leftMetas = currentPresence.metas.filter(m => newRefs.indexOf(m.phx_ref) < 0)\n        if(joinedMetas.length > 0){\n          joins[key] = newPresence\n          joins[key].metas = joinedMetas\n        }\n        if(leftMetas.length > 0){\n          leaves[key] = this.clone(currentPresence)\n          leaves[key].metas = leftMetas\n        }\n      } else {\n        joins[key] = newPresence\n      }\n    })\n    return this.syncDiff(state, {joins: joins, leaves: leaves}, onJoin, onLeave)\n  }\n\n  /**\n   *\n   * Used to sync a diff of presence join and leave\n   * events from the server, as they happen. Like `syncState`, `syncDiff`\n   * accepts optional `onJoin` and `onLeave` callbacks to react to a user\n   * joining or leaving from a device.\n   *\n   * @returns {Presence}\n   */\n  static syncDiff(state, diff, onJoin, onLeave){\n    let {joins, leaves} = this.clone(diff)\n    if(!onJoin){ onJoin = function (){ } }\n    if(!onLeave){ onLeave = function (){ } }\n\n    this.map(joins, (key, newPresence) => {\n      let currentPresence = state[key]\n      state[key] = this.clone(newPresence)\n      if(currentPresence){\n        let joinedRefs = state[key].metas.map(m => m.phx_ref)\n        let curMetas = currentPresence.metas.filter(m => joinedRefs.indexOf(m.phx_ref) < 0)\n        state[key].metas.unshift(...curMetas)\n      }\n      onJoin(key, currentPresence, newPresence)\n    })\n    this.map(leaves, (key, leftPresence) => {\n      let currentPresence = state[key]\n      if(!currentPresence){ return }\n      let refsToRemove = leftPresence.metas.map(m => m.phx_ref)\n      currentPresence.metas = currentPresence.metas.filter(p => {\n        return refsToRemove.indexOf(p.phx_ref) < 0\n      })\n      onLeave(key, currentPresence, leftPresence)\n      if(currentPresence.metas.length === 0){\n        delete state[key]\n      }\n    })\n    return state\n  }\n\n  /**\n   * Returns the array of presences, with selected metadata.\n   *\n   * @param {Object} presences\n   * @param {Function} chooser\n   *\n   * @returns {Presence}\n   */\n  static list(presences, chooser){\n    if(!chooser){ chooser = function (key, pres){ return pres } }\n\n    return this.map(presences, (key, presence) => {\n      return chooser(key, presence)\n    })\n  }\n\n  // private\n\n  static map(obj, func){\n    return Object.getOwnPropertyNames(obj).map(key => func(key, obj[key]))\n  }\n\n  static clone(obj){ return JSON.parse(JSON.stringify(obj)) }\n}\n", "/* The default serializer for encoding and decoding messages */\nimport {\n  CHANNEL_EVENTS\n} from \"./constants\"\n\nexport default {\n  HEADER_LENGTH: 1,\n  META_LENGTH: 4,\n  KINDS: {push: 0, reply: 1, broadcast: 2},\n\n  encode(msg, callback){\n    if(msg.payload.constructor === ArrayBuffer){\n      return callback(this.binaryEncode(msg))\n    } else {\n      let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]\n      return callback(JSON.stringify(payload))\n    }\n  },\n\n  decode(rawPayload, callback){\n    if(rawPayload.constructor === ArrayBuffer){\n      return callback(this.binaryDecode(rawPayload))\n    } else {\n      let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload)\n      return callback({join_ref, ref, topic, event, payload})\n    }\n  },\n\n  // private\n\n  binaryEncode(message){\n    let {join_ref, ref, event, topic, payload} = message\n    let metaLength = this.META_LENGTH + join_ref.length + ref.length + topic.length + event.length\n    let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)\n    let view = new DataView(header)\n    let offset = 0\n\n    view.setUint8(offset++, this.KINDS.push) // kind\n    view.setUint8(offset++, join_ref.length)\n    view.setUint8(offset++, ref.length)\n    view.setUint8(offset++, topic.length)\n    view.setUint8(offset++, event.length)\n    Array.from(join_ref, char => view.setUint8(offset++, char.charCodeAt(0)))\n    Array.from(ref, char => view.setUint8(offset++, char.charCodeAt(0)))\n    Array.from(topic, char => view.setUint8(offset++, char.charCodeAt(0)))\n    Array.from(event, char => view.setUint8(offset++, char.charCodeAt(0)))\n\n    var combined = new Uint8Array(header.byteLength + payload.byteLength)\n    combined.set(new Uint8Array(header), 0)\n    combined.set(new Uint8Array(payload), header.byteLength)\n\n    return combined.buffer\n  },\n\n  binaryDecode(buffer){\n    let view = new DataView(buffer)\n    let kind = view.getUint8(0)\n    let decoder = new TextDecoder()\n    switch(kind){\n      case this.KINDS.push: return this.decodePush(buffer, view, decoder)\n      case this.KINDS.reply: return this.decodeReply(buffer, view, decoder)\n      case this.KINDS.broadcast: return this.decodeBroadcast(buffer, view, decoder)\n    }\n  },\n\n  decodePush(buffer, view, decoder){\n    let joinRefSize = view.getUint8(1)\n    let topicSize = view.getUint8(2)\n    let eventSize = view.getUint8(3)\n    let offset = this.HEADER_LENGTH + this.META_LENGTH - 1 // pushes have no ref\n    let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n    offset = offset + joinRefSize\n    let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n    offset = offset + topicSize\n    let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n    offset = offset + eventSize\n    let data = buffer.slice(offset, buffer.byteLength)\n    return {join_ref: joinRef, ref: null, topic: topic, event: event, payload: data}\n  },\n\n  decodeReply(buffer, view, decoder){\n    let joinRefSize = view.getUint8(1)\n    let refSize = view.getUint8(2)\n    let topicSize = view.getUint8(3)\n    let eventSize = view.getUint8(4)\n    let offset = this.HEADER_LENGTH + this.META_LENGTH\n    let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n    offset = offset + joinRefSize\n    let ref = decoder.decode(buffer.slice(offset, offset + refSize))\n    offset = offset + refSize\n    let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n    offset = offset + topicSize\n    let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n    offset = offset + eventSize\n    let data = buffer.slice(offset, buffer.byteLength)\n    let payload = {status: event, response: data}\n    return {join_ref: joinRef, ref: ref, topic: topic, event: CHANNEL_EVENTS.reply, payload: payload}\n  },\n\n  decodeBroadcast(buffer, view, decoder){\n    let topicSize = view.getUint8(1)\n    let eventSize = view.getUint8(2)\n    let offset = this.HEADER_LENGTH + 2\n    let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n    offset = offset + topicSize\n    let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n    offset = offset + eventSize\n    let data = buffer.slice(offset, buffer.byteLength)\n\n    return {join_ref: null, ref: null, topic: topic, event: event, payload: data}\n  }\n}\n", "/** Initializes the Socket *\n *\n * For IE8 support use an ES5-shim (https://github.com/es-shims/es5-shim)\n *\n * @param {string} endPoint - The string WebSocket endpoint, ie, `\"ws://example.com/socket\"`,\n *                                               `\"wss://example.com\"`\n *                                               `\"/socket\"` (inherited host & protocol)\n * @param {Object} [opts] - Optional configuration\n * @param {Function} [opts.transport] - The Websocket Transport, for example WebSocket or Phoenix.LongPoll.\n *\n * Defaults to WebSocket with automatic LongPoll fallback.\n * @param {Function} [opts.encode] - The function to encode outgoing messages.\n *\n * Defaults to JSON encoder.\n *\n * @param {Function} [opts.decode] - The function to decode incoming messages.\n *\n * Defaults to JSON:\n *\n * ```javascript\n * (payload, callback) => callback(JSON.parse(payload))\n * ```\n *\n * @param {number} [opts.timeout] - The default timeout in milliseconds to trigger push timeouts.\n *\n * Defaults `DEFAULT_TIMEOUT`\n * @param {number} [opts.heartbeatIntervalMs] - The millisec interval to send a heartbeat message\n * @param {number} [opts.reconnectAfterMs] - The optional function that returns the millsec\n * socket reconnect interval.\n *\n * Defaults to stepped backoff of:\n *\n * ```javascript\n * function(tries){\n *   return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n * }\n * ````\n *\n * @param {number} [opts.rejoinAfterMs] - The optional function that returns the millsec\n * rejoin interval for individual channels.\n *\n * ```javascript\n * function(tries){\n *   return [1000, 2000, 5000][tries - 1] || 10000\n * }\n * ````\n *\n * @param {Function} [opts.logger] - The optional function for specialized logging, ie:\n *\n * ```javascript\n * function(kind, msg, data) {\n *   console.log(`${kind}: ${msg}`, data)\n * }\n * ```\n *\n * @param {number} [opts.longpollerTimeout] - The maximum timeout of a long poll AJAX request.\n *\n * Defaults to 20s (double the server long poll timer).\n *\n * @param {{Object|function)} [opts.params] - The optional params to pass when connecting\n * @param {string} [opts.binaryType] - The binary type to use for binary WebSocket frames.\n *\n * Defaults to \"arraybuffer\"\n *\n * @param {vsn} [opts.vsn] - The serializer's protocol version to send on connect.\n *\n * Defaults to DEFAULT_VSN.\n*/\n\nimport {\n  global,\n  phxWindow,\n  CHANNEL_EVENTS,\n  DEFAULT_TIMEOUT,\n  DEFAULT_VSN,\n  SOCKET_STATES,\n  TRANSPORTS,\n  WS_CLOSE_NORMAL\n} from \"./constants\"\n\nimport {\n  closure\n} from \"./utils\"\n\nimport Ajax from \"./ajax\"\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Serializer from \"./serializer\"\nimport Timer from \"./timer\"\n\nexport default class Socket {\n  constructor(endPoint, opts = {}){\n    this.stateChangeCallbacks = {open: [], close: [], error: [], message: []}\n    this.channels = []\n    this.sendBuffer = []\n    this.ref = 0\n    this.timeout = opts.timeout || DEFAULT_TIMEOUT\n    this.transport = opts.transport || global.WebSocket || LongPoll\n    this.establishedConnections = 0\n    this.defaultEncoder = Serializer.encode.bind(Serializer)\n    this.defaultDecoder = Serializer.decode.bind(Serializer)\n    this.closeWasClean = false\n    this.binaryType = opts.binaryType || \"arraybuffer\"\n    this.connectClock = 1\n    if(this.transport !== LongPoll){\n      this.encode = opts.encode || this.defaultEncoder\n      this.decode = opts.decode || this.defaultDecoder\n    } else {\n      this.encode = this.defaultEncoder\n      this.decode = this.defaultDecoder\n    }\n    let awaitingConnectionOnPageShow = null\n    if(phxWindow && phxWindow.addEventListener){\n      phxWindow.addEventListener(\"pagehide\", _e => {\n        if(this.conn){\n          this.disconnect()\n          awaitingConnectionOnPageShow = this.connectClock\n        }\n      })\n      phxWindow.addEventListener(\"pageshow\", _e => {\n        if(awaitingConnectionOnPageShow === this.connectClock){\n          awaitingConnectionOnPageShow = null\n          this.connect()\n        }\n      })\n    }\n    this.heartbeatIntervalMs = opts.heartbeatIntervalMs || 30000\n    this.rejoinAfterMs = (tries) => {\n      if(opts.rejoinAfterMs){\n        return opts.rejoinAfterMs(tries)\n      } else {\n        return [1000, 2000, 5000][tries - 1] || 10000\n      }\n    }\n    this.reconnectAfterMs = (tries) => {\n      if(opts.reconnectAfterMs){\n        return opts.reconnectAfterMs(tries)\n      } else {\n        return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n      }\n    }\n    this.logger = opts.logger || null\n    this.longpollerTimeout = opts.longpollerTimeout || 20000\n    this.params = closure(opts.params || {})\n    this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`\n    this.vsn = opts.vsn || DEFAULT_VSN\n    this.heartbeatTimer = null\n    this.pendingHeartbeatRef = null\n    this.reconnectTimer = new Timer(() => {\n      this.teardown(() => this.connect())\n    }, this.reconnectAfterMs)\n  }\n\n  /**\n   * Disconnects and replaces the active transport\n   *\n   * @param {Function} newTransport - The new transport class to instantiate\n   *\n   */\n  replaceTransport(newTransport){\n    this.disconnect()\n    this.transport = newTransport\n  }\n\n  /**\n   * Returns the socket protocol\n   *\n   * @returns {string}\n   */\n  protocol(){ return location.protocol.match(/^https/) ? \"wss\" : \"ws\" }\n\n  /**\n   * The fully qualifed socket url\n   *\n   * @returns {string}\n   */\n  endPointURL(){\n    let uri = Ajax.appendParams(\n      Ajax.appendParams(this.endPoint, this.params()), {vsn: this.vsn})\n    if(uri.charAt(0) !== \"/\"){ return uri }\n    if(uri.charAt(1) === \"/\"){ return `${this.protocol()}:${uri}` }\n\n    return `${this.protocol()}://${location.host}${uri}`\n  }\n\n  /**\n   * Disconnects the socket\n   *\n   * See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes for valid status codes.\n   *\n   * @param {Function} callback - Optional callback which is called after socket is disconnected.\n   * @param {integer} code - A status code for disconnection (Optional).\n   * @param {string} reason - A textual description of the reason to disconnect. (Optional)\n   */\n  disconnect(callback, code, reason){\n    this.connectClock++\n    this.closeWasClean = true\n    this.reconnectTimer.reset()\n    this.teardown(callback, code, reason)\n  }\n\n  /**\n   *\n   * @param {Object} params - The params to send when connecting, for example `{user_id: userToken}`\n   *\n   * Passing params to connect is deprecated; pass them in the Socket constructor instead:\n   * `new Socket(\"/socket\", {params: {user_id: userToken}})`.\n   */\n  connect(params){\n    this.connectClock++\n    if(params){\n      console && console.log(\"passing params to connect is deprecated. Instead pass :params to the Socket constructor\")\n      this.params = closure(params)\n    }\n    if(this.conn){ return }\n    this.closeWasClean = false\n    this.conn = new this.transport(this.endPointURL())\n    this.conn.binaryType = this.binaryType\n    this.conn.timeout = this.longpollerTimeout\n    this.conn.onopen = () => this.onConnOpen()\n    this.conn.onerror = error => this.onConnError(error)\n    this.conn.onmessage = event => this.onConnMessage(event)\n    this.conn.onclose = event => this.onConnClose(event)\n  }\n\n  /**\n   * Logs the message. Override `this.logger` for specialized logging. noops by default\n   * @param {string} kind\n   * @param {string} msg\n   * @param {Object} data\n   */\n  log(kind, msg, data){ this.logger(kind, msg, data) }\n\n  /**\n   * Returns true if a logger has been set on this socket.\n   */\n  hasLogger(){ return this.logger !== null }\n\n  /**\n   * Registers callbacks for connection open events\n   *\n   * @example socket.onOpen(function(){ console.info(\"the socket was opened\") })\n   *\n   * @param {Function} callback\n   */\n  onOpen(callback){\n    let ref = this.makeRef()\n    this.stateChangeCallbacks.open.push([ref, callback])\n    return ref\n  }\n\n  /**\n   * Registers callbacks for connection close events\n   * @param {Function} callback\n   */\n  onClose(callback){\n    let ref = this.makeRef()\n    this.stateChangeCallbacks.close.push([ref, callback])\n    return ref\n  }\n\n  /**\n   * Registers callbacks for connection error events\n   *\n   * @example socket.onError(function(error){ alert(\"An error occurred\") })\n   *\n   * @param {Function} callback\n   */\n  onError(callback){\n    let ref = this.makeRef()\n    this.stateChangeCallbacks.error.push([ref, callback])\n    return ref\n  }\n\n  /**\n   * Registers callbacks for connection message events\n   * @param {Function} callback\n   */\n  onMessage(callback){\n    let ref = this.makeRef()\n    this.stateChangeCallbacks.message.push([ref, callback])\n    return ref\n  }\n\n  /**\n   * @private\n   */\n  onConnOpen(){\n    if(this.hasLogger()) this.log(\"transport\", `connected to ${this.endPointURL()}`)\n    this.closeWasClean = false\n    this.establishedConnections++\n    this.flushSendBuffer()\n    this.reconnectTimer.reset()\n    this.resetHeartbeat()\n    this.stateChangeCallbacks.open.forEach(([, callback]) => callback())\n  }\n\n  /**\n   * @private\n   */\n\n  heartbeatTimeout(){\n    if(this.pendingHeartbeatRef){\n      this.pendingHeartbeatRef = null\n      if(this.hasLogger()){ this.log(\"transport\", \"heartbeat timeout. Attempting to re-establish connection\") }\n      this.abnormalClose(\"heartbeat timeout\")\n    }\n  }\n\n  resetHeartbeat(){\n    if(this.conn && this.conn.skipHeartbeat){ return }\n    this.pendingHeartbeatRef = null\n    clearTimeout(this.heartbeatTimer)\n    setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n  }\n\n  teardown(callback, code, reason){\n    if(!this.conn){\n      return callback && callback()\n    }\n\n    this.waitForBufferDone(() => {\n      if(this.conn){\n        if(code){ this.conn.close(code, reason || \"\") } else { this.conn.close() }\n      }\n\n      this.waitForSocketClosed(() => {\n        if(this.conn){\n          this.conn.onclose = function (){ } // noop\n          this.conn = null\n        }\n\n        callback && callback()\n      })\n    })\n  }\n\n  waitForBufferDone(callback, tries = 1){\n    if(tries === 5 || !this.conn || !this.conn.bufferedAmount){\n      callback()\n      return\n    }\n\n    setTimeout(() => {\n      this.waitForBufferDone(callback, tries + 1)\n    }, 150 * tries)\n  }\n\n  waitForSocketClosed(callback, tries = 1){\n    if(tries === 5 || !this.conn || this.conn.readyState === SOCKET_STATES.closed){\n      callback()\n      return\n    }\n\n    setTimeout(() => {\n      this.waitForSocketClosed(callback, tries + 1)\n    }, 150 * tries)\n  }\n\n  onConnClose(event){\n    if(this.hasLogger()) this.log(\"transport\", \"close\", event)\n    this.triggerChanError()\n    clearTimeout(this.heartbeatTimer)\n    if(!this.closeWasClean){\n      this.reconnectTimer.scheduleTimeout()\n    }\n    this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event))\n  }\n\n  /**\n   * @private\n   */\n  onConnError(error){\n    if(this.hasLogger()) this.log(\"transport\", error)\n    let transportBefore = this.transport\n    let establishedBefore = this.establishedConnections\n    this.stateChangeCallbacks.error.forEach(([, callback]) => {\n      callback(error, transportBefore, establishedBefore)\n    })\n    if(transportBefore === this.transport || establishedBefore > 0){\n      this.triggerChanError()\n    }\n  }\n\n  /**\n   * @private\n   */\n  triggerChanError(){\n    this.channels.forEach(channel => {\n      if(!(channel.isErrored() || channel.isLeaving() || channel.isClosed())){\n        channel.trigger(CHANNEL_EVENTS.error)\n      }\n    })\n  }\n\n  /**\n   * @returns {string}\n   */\n  connectionState(){\n    switch(this.conn && this.conn.readyState){\n      case SOCKET_STATES.connecting: return \"connecting\"\n      case SOCKET_STATES.open: return \"open\"\n      case SOCKET_STATES.closing: return \"closing\"\n      default: return \"closed\"\n    }\n  }\n\n  /**\n   * @returns {boolean}\n   */\n  isConnected(){ return this.connectionState() === \"open\" }\n\n  /**\n   * @private\n   *\n   * @param {Channel}\n   */\n  remove(channel){\n    this.off(channel.stateChangeRefs)\n    this.channels = this.channels.filter(c => c.joinRef() !== channel.joinRef())\n  }\n\n  /**\n   * Removes `onOpen`, `onClose`, `onError,` and `onMessage` registrations.\n   *\n   * @param {refs} - list of refs returned by calls to\n   *                 `onOpen`, `onClose`, `onError,` and `onMessage`\n   */\n  off(refs){\n    for(let key in this.stateChangeCallbacks){\n      this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => {\n        return refs.indexOf(ref) === -1\n      })\n    }\n  }\n\n  /**\n   * Initiates a new channel for the given topic\n   *\n   * @param {string} topic\n   * @param {Object} chanParams - Parameters for the channel\n   * @returns {Channel}\n   */\n  channel(topic, chanParams = {}){\n    let chan = new Channel(topic, chanParams, this)\n    this.channels.push(chan)\n    return chan\n  }\n\n  /**\n   * @param {Object} data\n   */\n  push(data){\n    if(this.hasLogger()){\n      let {topic, event, payload, ref, join_ref} = data\n      this.log(\"push\", `${topic} ${event} (${join_ref}, ${ref})`, payload)\n    }\n\n    if(this.isConnected()){\n      this.encode(data, result => this.conn.send(result))\n    } else {\n      this.sendBuffer.push(() => this.encode(data, result => this.conn.send(result)))\n    }\n  }\n\n  /**\n   * Return the next message ref, accounting for overflows\n   * @returns {string}\n   */\n  makeRef(){\n    let newRef = this.ref + 1\n    if(newRef === this.ref){ this.ref = 0 } else { this.ref = newRef }\n\n    return this.ref.toString()\n  }\n\n  sendHeartbeat(){\n    if(this.pendingHeartbeatRef && !this.isConnected()){ return }\n    this.pendingHeartbeatRef = this.makeRef()\n    this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: this.pendingHeartbeatRef})\n    this.heartbeatTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs)\n  }\n\n  abnormalClose(reason){\n    this.closeWasClean = false\n    if(this.isConnected()){ this.conn.close(WS_CLOSE_NORMAL, reason) }\n  }\n\n  flushSendBuffer(){\n    if(this.isConnected() && this.sendBuffer.length > 0){\n      this.sendBuffer.forEach(callback => callback())\n      this.sendBuffer = []\n    }\n  }\n\n  onConnMessage(rawMessage){\n    this.decode(rawMessage.data, msg => {\n      let {topic, event, payload, ref, join_ref} = msg\n      if(ref && ref === this.pendingHeartbeatRef){\n        clearTimeout(this.heartbeatTimer)\n        this.pendingHeartbeatRef = null\n        setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n      }\n\n      if(this.hasLogger()) this.log(\"receive\", `${payload.status || \"\"} ${topic} ${event} ${ref && \"(\" + ref + \")\" || \"\"}`, payload)\n\n      for(let i = 0; i < this.channels.length; i++){\n        const channel = this.channels[i]\n        if(!channel.isMember(topic, event, payload, join_ref)){ continue }\n        channel.trigger(event, payload, ref, join_ref)\n      }\n\n      for(let i = 0; i < this.stateChangeCallbacks.message.length; i++){\n        let [, callback] = this.stateChangeCallbacks.message[i]\n        callback(msg)\n      }\n    })\n  }\n\n  leaveOpenTopic(topic){\n    let dupChannel = this.channels.find(c => c.topic === topic && (c.isJoined() || c.isJoining()))\n    if(dupChannel){\n      if(this.hasLogger()) this.log(\"transport\", `leaving duplicate topic \"${topic}\"`)\n      dupChannel.leave()\n    }\n  }\n}\n"],
+  "mappings": "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",
+  "names": []
+}


Test patch:
diff --git a/test/mix/tasks/phx.gen.auth_test.exs b/test/mix/tasks/phx.gen.auth_test.exs
index 5160a314c..962bc4a18 100644
--- a/test/mix/tasks/phx.gen.auth_test.exs
+++ b/test/mix/tasks/phx.gen.auth_test.exs
@@ -83,7 +83,7 @@ defmodule Mix.Tasks.Phx.Gen.AuthTest do
       )
 
       assert_file "config/test.exs", fn file ->
-        assert file =~ "config :argon2_elixir, t_cost: 1, m_cost: 8"
+        assert file =~ "config :bcrypt_elixir, :log_rounds, 1"
       end
 
       assert_file "lib/my_app/accounts.ex"
@@ -130,7 +130,7 @@ defmodule Mix.Tasks.Phx.Gen.AuthTest do
       end
 
       assert_file "mix.exs", fn file ->
-        assert file =~ ~s|{:argon2_elixir, "~> 2.0"},|
+        assert file =~ ~s|{:bcrypt_elixir, "~> 2.0"},|
       end
 
       assert_file "lib/my_app_web/router.ex", fn file ->
@@ -776,11 +776,11 @@ defmodule Mix.Tasks.Phx.Gen.AuthTest do
 
         assert_received {:mix_shell, :info, ["""
 
-        Add your {:argon2_elixir, "~> 2.0"} dependency to mix.exs:
+        Add your {:bcrypt_elixir, "~> 2.0"} dependency to mix.exs:
 
             defp deps do
               [
-                {:argon2_elixir, "~> 2.0"},
+                {:bcrypt_elixir, "~> 2.0"},
                 ...
               ]
             end