1579 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1579 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
var Phoenix = (() => {
 | 
						|
  var __defProp = Object.defineProperty;
 | 
						|
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
 | 
						|
  var __getOwnPropNames = Object.getOwnPropertyNames;
 | 
						|
  var __hasOwnProp = Object.prototype.hasOwnProperty;
 | 
						|
  var __export = (target, all) => {
 | 
						|
    for (var name in all)
 | 
						|
      __defProp(target, name, { get: all[name], enumerable: true });
 | 
						|
  };
 | 
						|
  var __copyProps = (to, from, except, desc) => {
 | 
						|
    if (from && typeof from === "object" || typeof from === "function") {
 | 
						|
      for (let key of __getOwnPropNames(from))
 | 
						|
        if (!__hasOwnProp.call(to, key) && key !== except)
 | 
						|
          __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
 | 
						|
    }
 | 
						|
    return to;
 | 
						|
  };
 | 
						|
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
 | 
						|
 | 
						|
  // js/phoenix/index.js
 | 
						|
  var phoenix_exports = {};
 | 
						|
  __export(phoenix_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 || global;
 | 
						|
  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;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     *
 | 
						|
     * @param {number} timeout
 | 
						|
     */
 | 
						|
    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()
 | 
						|
      });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     *
 | 
						|
     * @param {*} status
 | 
						|
     * @param {*} callback
 | 
						|
     */
 | 
						|
    receive(status, callback) {
 | 
						|
      if (this.hasReceived(status)) {
 | 
						|
        callback(this.receivedResp.response);
 | 
						|
      }
 | 
						|
      this.recHooks.push({ status, callback });
 | 
						|
      return this;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    reset() {
 | 
						|
      this.cancelRefEvent();
 | 
						|
      this.ref = null;
 | 
						|
      this.refEvent = null;
 | 
						|
      this.receivedResp = null;
 | 
						|
      this.sent = false;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    matchReceive({ status, response, _ref }) {
 | 
						|
      this.recHooks.filter((h) => h.status === status).forEach((h) => h.callback(response));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    cancelRefEvent() {
 | 
						|
      if (!this.refEvent) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      this.channel.off(this.refEvent);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    cancelTimeout() {
 | 
						|
      clearTimeout(this.timeoutTimer);
 | 
						|
      this.timeoutTimer = null;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    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);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    hasReceived(status) {
 | 
						|
      return this.receivedResp && this.receivedResp.status === status;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    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);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Cancels any previous scheduleTimeout and schedules callback
 | 
						|
     */
 | 
						|
    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 the channel
 | 
						|
     * @param {integer} timeout
 | 
						|
     * @returns {Push}
 | 
						|
     */
 | 
						|
    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;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Hook into channel close
 | 
						|
     * @param {Function} callback
 | 
						|
     */
 | 
						|
    onClose(callback) {
 | 
						|
      this.on(CHANNEL_EVENTS.close, callback);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Hook into channel errors
 | 
						|
     * @param {Function} callback
 | 
						|
     */
 | 
						|
    onError(callback) {
 | 
						|
      return this.on(CHANNEL_EVENTS.error, (reason) => callback(reason));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Subscribes on channel events
 | 
						|
     *
 | 
						|
     * Subscription returns a ref counter, which can be used later to
 | 
						|
     * unsubscribe the exact event listener
 | 
						|
     *
 | 
						|
     * @example
 | 
						|
     * const ref1 = channel.on("event", do_stuff)
 | 
						|
     * const ref2 = channel.on("event", do_other_stuff)
 | 
						|
     * channel.off("event", ref1)
 | 
						|
     * // Since unsubscription, do_stuff won't fire,
 | 
						|
     * // while do_other_stuff will keep firing on the "event"
 | 
						|
     *
 | 
						|
     * @param {string} event
 | 
						|
     * @param {Function} callback
 | 
						|
     * @returns {integer} ref
 | 
						|
     */
 | 
						|
    on(event, callback) {
 | 
						|
      let ref = this.bindingRef++;
 | 
						|
      this.bindings.push({ event, ref, callback });
 | 
						|
      return ref;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Unsubscribes off of channel events
 | 
						|
     *
 | 
						|
     * Use the ref returned from a channel.on() to unsubscribe one
 | 
						|
     * handler, or pass nothing for the ref to unsubscribe all
 | 
						|
     * handlers for the given event.
 | 
						|
     *
 | 
						|
     * @example
 | 
						|
     * // Unsubscribe the do_stuff handler
 | 
						|
     * const ref1 = channel.on("event", do_stuff)
 | 
						|
     * channel.off("event", ref1)
 | 
						|
     *
 | 
						|
     * // Unsubscribe all handlers from event
 | 
						|
     * channel.off("event")
 | 
						|
     *
 | 
						|
     * @param {string} event
 | 
						|
     * @param {integer} ref
 | 
						|
     */
 | 
						|
    off(event, ref) {
 | 
						|
      this.bindings = this.bindings.filter((bind) => {
 | 
						|
        return !(bind.event === event && (typeof ref === "undefined" || ref === bind.ref));
 | 
						|
      });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    canPush() {
 | 
						|
      return this.socket.isConnected() && this.isJoined();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Sends a message `event` to phoenix with the payload `payload`.
 | 
						|
     * Phoenix receives this in the `handle_in(event, payload, socket)`
 | 
						|
     * function. if phoenix replies or it times out (default 10000ms),
 | 
						|
     * then optionally the reply can be received.
 | 
						|
     *
 | 
						|
     * @example
 | 
						|
     * channel.push("event")
 | 
						|
     *   .receive("ok", payload => console.log("phoenix replied:", payload))
 | 
						|
     *   .receive("error", err => console.log("phoenix errored", err))
 | 
						|
     *   .receive("timeout", () => console.log("timed out pushing"))
 | 
						|
     * @param {string} event
 | 
						|
     * @param {Object} payload
 | 
						|
     * @param {number} [timeout]
 | 
						|
     * @returns {Push}
 | 
						|
     */
 | 
						|
    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;
 | 
						|
    }
 | 
						|
    /** Leaves the channel
 | 
						|
     *
 | 
						|
     * Unsubscribes from server events, and
 | 
						|
     * instructs channel to terminate on server
 | 
						|
     *
 | 
						|
     * Triggers onClose() hooks
 | 
						|
     *
 | 
						|
     * To receive leave acknowledgements, use the `receive`
 | 
						|
     * hook to bind to the server ack, ie:
 | 
						|
     *
 | 
						|
     * @example
 | 
						|
     * channel.leave().receive("ok", () => alert("left!") )
 | 
						|
     *
 | 
						|
     * @param {integer} timeout
 | 
						|
     * @returns {Push}
 | 
						|
     */
 | 
						|
    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;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Overridable message hook
 | 
						|
     *
 | 
						|
     * Receives all events for specialized message handling
 | 
						|
     * before dispatching to the channel callbacks.
 | 
						|
     *
 | 
						|
     * Must return the payload, modified or unmodified
 | 
						|
     * @param {string} event
 | 
						|
     * @param {Object} payload
 | 
						|
     * @param {integer} ref
 | 
						|
     * @returns {Object}
 | 
						|
     */
 | 
						|
    onMessage(_event, payload, _ref) {
 | 
						|
      return payload;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    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;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    joinRef() {
 | 
						|
      return this.joinPush.ref;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    rejoin(timeout = this.timeout) {
 | 
						|
      if (this.isLeaving()) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      this.socket.leaveOpenTopic(this.topic);
 | 
						|
      this.state = CHANNEL_STATES.joining;
 | 
						|
      this.joinPush.resend(timeout);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    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());
 | 
						|
      }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    replyEventName(ref) {
 | 
						|
      return `chan_reply_${ref}`;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    isClosed() {
 | 
						|
      return this.state === CHANNEL_STATES.closed;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    isErrored() {
 | 
						|
      return this.state === CHANNEL_STATES.errored;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    isJoined() {
 | 
						|
      return this.state === CHANNEL_STATES.joined;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    isJoining() {
 | 
						|
      return this.state === CHANNEL_STATES.joining;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    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();
 | 
						|
        return this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback);
 | 
						|
      } else {
 | 
						|
        let req = new global.XMLHttpRequest();
 | 
						|
        return 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);
 | 
						|
      return req;
 | 
						|
    }
 | 
						|
    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);
 | 
						|
      return req;
 | 
						|
    }
 | 
						|
    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 arrayBufferToBase64 = (buffer) => {
 | 
						|
    let binary = "";
 | 
						|
    let bytes = new Uint8Array(buffer);
 | 
						|
    let len = bytes.byteLength;
 | 
						|
    for (let i = 0; i < len; i++) {
 | 
						|
      binary += String.fromCharCode(bytes[i]);
 | 
						|
    }
 | 
						|
    return btoa(binary);
 | 
						|
  };
 | 
						|
  var LongPoll = class {
 | 
						|
    constructor(endPoint) {
 | 
						|
      this.endPoint = null;
 | 
						|
      this.token = null;
 | 
						|
      this.skipHeartbeat = true;
 | 
						|
      this.reqs = /* @__PURE__ */ new Set();
 | 
						|
      this.awaitingBatchAck = false;
 | 
						|
      this.currentBatch = null;
 | 
						|
      this.currentBatchTimer = null;
 | 
						|
      this.batchBuffer = [];
 | 
						|
      this.onopen = function() {
 | 
						|
      };
 | 
						|
      this.onerror = function() {
 | 
						|
      };
 | 
						|
      this.onmessage = function() {
 | 
						|
      };
 | 
						|
      this.onclose = function() {
 | 
						|
      };
 | 
						|
      this.pollEndpoint = this.normalizeEndpoint(endPoint);
 | 
						|
      this.readyState = SOCKET_STATES.connecting;
 | 
						|
      setTimeout(() => this.poll(), 0);
 | 
						|
    }
 | 
						|
    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(code, reason, wasClean) {
 | 
						|
      this.close(code, reason, wasClean);
 | 
						|
      this.readyState = SOCKET_STATES.connecting;
 | 
						|
    }
 | 
						|
    ontimeout() {
 | 
						|
      this.onerror("timeout");
 | 
						|
      this.closeAndRetry(1005, "timeout", false);
 | 
						|
    }
 | 
						|
    isActive() {
 | 
						|
      return this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting;
 | 
						|
    }
 | 
						|
    poll() {
 | 
						|
      this.ajax("GET", "application/json", null, () => this.ontimeout(), (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(403);
 | 
						|
            this.close(1008, "forbidden", false);
 | 
						|
            break;
 | 
						|
          case 0:
 | 
						|
          case 500:
 | 
						|
            this.onerror(500);
 | 
						|
            this.closeAndRetry(1011, "internal server error", 500);
 | 
						|
            break;
 | 
						|
          default:
 | 
						|
            throw new Error(`unhandled poll status ${status}`);
 | 
						|
        }
 | 
						|
      });
 | 
						|
    }
 | 
						|
    // we collect all pushes within the current event loop by
 | 
						|
    // setTimeout 0, which optimizes back-to-back procedural
 | 
						|
    // pushes against an empty buffer
 | 
						|
    send(body) {
 | 
						|
      if (typeof body !== "string") {
 | 
						|
        body = arrayBufferToBase64(body);
 | 
						|
      }
 | 
						|
      if (this.currentBatch) {
 | 
						|
        this.currentBatch.push(body);
 | 
						|
      } else if (this.awaitingBatchAck) {
 | 
						|
        this.batchBuffer.push(body);
 | 
						|
      } else {
 | 
						|
        this.currentBatch = [body];
 | 
						|
        this.currentBatchTimer = setTimeout(() => {
 | 
						|
          this.batchSend(this.currentBatch);
 | 
						|
          this.currentBatch = null;
 | 
						|
        }, 0);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    batchSend(messages) {
 | 
						|
      this.awaitingBatchAck = true;
 | 
						|
      this.ajax("POST", "application/x-ndjson", messages.join("\n"), () => this.onerror("timeout"), (resp) => {
 | 
						|
        this.awaitingBatchAck = false;
 | 
						|
        if (!resp || resp.status !== 200) {
 | 
						|
          this.onerror(resp && resp.status);
 | 
						|
          this.closeAndRetry(1011, "internal server error", false);
 | 
						|
        } else if (this.batchBuffer.length > 0) {
 | 
						|
          this.batchSend(this.batchBuffer);
 | 
						|
          this.batchBuffer = [];
 | 
						|
        }
 | 
						|
      });
 | 
						|
    }
 | 
						|
    close(code, reason, wasClean) {
 | 
						|
      for (let req of this.reqs) {
 | 
						|
        req.abort();
 | 
						|
      }
 | 
						|
      this.readyState = SOCKET_STATES.closed;
 | 
						|
      let opts = Object.assign({ code: 1e3, reason: void 0, wasClean: true }, { code, reason, wasClean });
 | 
						|
      this.batchBuffer = [];
 | 
						|
      clearTimeout(this.currentBatchTimer);
 | 
						|
      this.currentBatchTimer = null;
 | 
						|
      if (typeof CloseEvent !== "undefined") {
 | 
						|
        this.onclose(new CloseEvent("close", opts));
 | 
						|
      } else {
 | 
						|
        this.onclose(opts);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    ajax(method, contentType, body, onCallerTimeout, callback) {
 | 
						|
      let req;
 | 
						|
      let ontimeout = () => {
 | 
						|
        this.reqs.delete(req);
 | 
						|
        onCallerTimeout();
 | 
						|
      };
 | 
						|
      req = Ajax.request(method, this.endpointURL(), contentType, body, this.timeout, ontimeout, (resp) => {
 | 
						|
        this.reqs.delete(req);
 | 
						|
        if (this.isActive()) {
 | 
						|
          callback(resp);
 | 
						|
        }
 | 
						|
      });
 | 
						|
      this.reqs.add(req);
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  // 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();
 | 
						|
    }
 | 
						|
    // lower-level public static API
 | 
						|
    /**
 | 
						|
     * Used to sync the list of presences on the server
 | 
						|
     * with the client's state. An optional `onJoin` and `onLeave` callback can
 | 
						|
     * be provided to react to changes in the client's local presences across
 | 
						|
     * disconnects and reconnects with the server.
 | 
						|
     *
 | 
						|
     * @returns {Presence}
 | 
						|
     */
 | 
						|
    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);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     *
 | 
						|
     * Used to sync a diff of presence join and leave
 | 
						|
     * events from the server, as they happen. Like `syncState`, `syncDiff`
 | 
						|
     * accepts optional `onJoin` and `onLeave` callbacks to react to a user
 | 
						|
     * joining or leaving from a device.
 | 
						|
     *
 | 
						|
     * @returns {Presence}
 | 
						|
     */
 | 
						|
    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;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Returns the array of presences, with selected metadata.
 | 
						|
     *
 | 
						|
     * @param {Object} presences
 | 
						|
     * @param {Function} chooser
 | 
						|
     *
 | 
						|
     * @returns {Presence}
 | 
						|
     */
 | 
						|
    static list(presences, chooser) {
 | 
						|
      if (!chooser) {
 | 
						|
        chooser = function(key, pres) {
 | 
						|
          return pres;
 | 
						|
        };
 | 
						|
      }
 | 
						|
      return this.map(presences, (key, presence) => {
 | 
						|
        return chooser(key, presence);
 | 
						|
      });
 | 
						|
    }
 | 
						|
    // private
 | 
						|
    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 });
 | 
						|
      }
 | 
						|
    },
 | 
						|
    // private
 | 
						|
    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.primaryPassedHealthCheck = false;
 | 
						|
      this.longPollFallbackMs = opts.longPollFallbackMs;
 | 
						|
      this.fallbackTimer = null;
 | 
						|
      this.sessionStore = opts.sessionStorage || global && global.sessionStorage;
 | 
						|
      this.establishedConnections = 0;
 | 
						|
      this.defaultEncoder = serializer_default.encode.bind(serializer_default);
 | 
						|
      this.defaultDecoder = serializer_default.decode.bind(serializer_default);
 | 
						|
      this.closeWasClean = false;
 | 
						|
      this.disconnecting = 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;
 | 
						|
      if (!this.logger && opts.debug) {
 | 
						|
        this.logger = (kind, msg, data) => {
 | 
						|
          console.log(`${kind}: ${msg}`, data);
 | 
						|
        };
 | 
						|
      }
 | 
						|
      this.longpollerTimeout = opts.longpollerTimeout || 2e4;
 | 
						|
      this.params = closure(opts.params || {});
 | 
						|
      this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`;
 | 
						|
      this.vsn = opts.vsn || DEFAULT_VSN;
 | 
						|
      this.heartbeatTimeoutTimer = null;
 | 
						|
      this.heartbeatTimer = null;
 | 
						|
      this.pendingHeartbeatRef = null;
 | 
						|
      this.reconnectTimer = new Timer(() => {
 | 
						|
        this.teardown(() => this.connect());
 | 
						|
      }, this.reconnectAfterMs);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Returns the LongPoll transport reference
 | 
						|
     */
 | 
						|
    getLongPollTransport() {
 | 
						|
      return LongPoll;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Disconnects and replaces the active transport
 | 
						|
     *
 | 
						|
     * @param {Function} newTransport - The new transport class to instantiate
 | 
						|
     *
 | 
						|
     */
 | 
						|
    replaceTransport(newTransport) {
 | 
						|
      this.connectClock++;
 | 
						|
      this.closeWasClean = true;
 | 
						|
      clearTimeout(this.fallbackTimer);
 | 
						|
      this.reconnectTimer.reset();
 | 
						|
      if (this.conn) {
 | 
						|
        this.conn.close();
 | 
						|
        this.conn = null;
 | 
						|
      }
 | 
						|
      this.transport = newTransport;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Returns the socket protocol
 | 
						|
     *
 | 
						|
     * @returns {string}
 | 
						|
     */
 | 
						|
    protocol() {
 | 
						|
      return location.protocol.match(/^https/) ? "wss" : "ws";
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The fully qualified socket url
 | 
						|
     *
 | 
						|
     * @returns {string}
 | 
						|
     */
 | 
						|
    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}`;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Disconnects the socket
 | 
						|
     *
 | 
						|
     * See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes for valid status codes.
 | 
						|
     *
 | 
						|
     * @param {Function} callback - Optional callback which is called after socket is disconnected.
 | 
						|
     * @param {integer} code - A status code for disconnection (Optional).
 | 
						|
     * @param {string} reason - A textual description of the reason to disconnect. (Optional)
 | 
						|
     */
 | 
						|
    disconnect(callback, code, reason) {
 | 
						|
      this.connectClock++;
 | 
						|
      this.disconnecting = true;
 | 
						|
      this.closeWasClean = true;
 | 
						|
      clearTimeout(this.fallbackTimer);
 | 
						|
      this.reconnectTimer.reset();
 | 
						|
      this.teardown(() => {
 | 
						|
        this.disconnecting = false;
 | 
						|
        callback && callback();
 | 
						|
      }, code, reason);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     *
 | 
						|
     * @param {Object} params - The params to send when connecting, for example `{user_id: userToken}`
 | 
						|
     *
 | 
						|
     * Passing params to connect is deprecated; pass them in the Socket constructor instead:
 | 
						|
     * `new Socket("/socket", {params: {user_id: userToken}})`.
 | 
						|
     */
 | 
						|
    connect(params) {
 | 
						|
      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 && !this.disconnecting) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      if (this.longPollFallbackMs && this.transport !== LongPoll) {
 | 
						|
        this.connectWithFallback(LongPoll, this.longPollFallbackMs);
 | 
						|
      } else {
 | 
						|
        this.transportConnect();
 | 
						|
      }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Logs the message. Override `this.logger` for specialized logging. noops by default
 | 
						|
     * @param {string} kind
 | 
						|
     * @param {string} msg
 | 
						|
     * @param {Object} data
 | 
						|
     */
 | 
						|
    log(kind, msg, data) {
 | 
						|
      this.logger && this.logger(kind, msg, data);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Returns true if a logger has been set on this socket.
 | 
						|
     */
 | 
						|
    hasLogger() {
 | 
						|
      return this.logger !== null;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Registers callbacks for connection open events
 | 
						|
     *
 | 
						|
     * @example socket.onOpen(function(){ console.info("the socket was opened") })
 | 
						|
     *
 | 
						|
     * @param {Function} callback
 | 
						|
     */
 | 
						|
    onOpen(callback) {
 | 
						|
      let ref = this.makeRef();
 | 
						|
      this.stateChangeCallbacks.open.push([ref, callback]);
 | 
						|
      return ref;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Registers callbacks for connection close events
 | 
						|
     * @param {Function} callback
 | 
						|
     */
 | 
						|
    onClose(callback) {
 | 
						|
      let ref = this.makeRef();
 | 
						|
      this.stateChangeCallbacks.close.push([ref, callback]);
 | 
						|
      return ref;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Registers callbacks for connection error events
 | 
						|
     *
 | 
						|
     * @example socket.onError(function(error){ alert("An error occurred") })
 | 
						|
     *
 | 
						|
     * @param {Function} callback
 | 
						|
     */
 | 
						|
    onError(callback) {
 | 
						|
      let ref = this.makeRef();
 | 
						|
      this.stateChangeCallbacks.error.push([ref, callback]);
 | 
						|
      return ref;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Registers callbacks for connection message events
 | 
						|
     * @param {Function} callback
 | 
						|
     */
 | 
						|
    onMessage(callback) {
 | 
						|
      let ref = this.makeRef();
 | 
						|
      this.stateChangeCallbacks.message.push([ref, callback]);
 | 
						|
      return ref;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Pings the server and invokes the callback with the RTT in milliseconds
 | 
						|
     * @param {Function} callback
 | 
						|
     *
 | 
						|
     * Returns true if the ping was pushed or false if unable to be pushed.
 | 
						|
     */
 | 
						|
    ping(callback) {
 | 
						|
      if (!this.isConnected()) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      let ref = this.makeRef();
 | 
						|
      let startTime = Date.now();
 | 
						|
      this.push({ topic: "phoenix", event: "heartbeat", payload: {}, ref });
 | 
						|
      let onMsgRef = this.onMessage((msg) => {
 | 
						|
        if (msg.ref === ref) {
 | 
						|
          this.off([onMsgRef]);
 | 
						|
          callback(Date.now() - startTime);
 | 
						|
        }
 | 
						|
      });
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    transportConnect() {
 | 
						|
      this.connectClock++;
 | 
						|
      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);
 | 
						|
    }
 | 
						|
    getSession(key) {
 | 
						|
      return this.sessionStore && this.sessionStore.getItem(key);
 | 
						|
    }
 | 
						|
    storeSession(key, val) {
 | 
						|
      this.sessionStore && this.sessionStore.setItem(key, val);
 | 
						|
    }
 | 
						|
    connectWithFallback(fallbackTransport, fallbackThreshold = 2500) {
 | 
						|
      clearTimeout(this.fallbackTimer);
 | 
						|
      let established = false;
 | 
						|
      let primaryTransport = true;
 | 
						|
      let openRef, errorRef;
 | 
						|
      let fallback = (reason) => {
 | 
						|
        this.log("transport", `falling back to ${fallbackTransport.name}...`, reason);
 | 
						|
        this.off([openRef, errorRef]);
 | 
						|
        primaryTransport = false;
 | 
						|
        this.replaceTransport(fallbackTransport);
 | 
						|
        this.transportConnect();
 | 
						|
      };
 | 
						|
      if (this.getSession(`phx:fallback:${fallbackTransport.name}`)) {
 | 
						|
        return fallback("memorized");
 | 
						|
      }
 | 
						|
      this.fallbackTimer = setTimeout(fallback, fallbackThreshold);
 | 
						|
      errorRef = this.onError((reason) => {
 | 
						|
        this.log("transport", "error", reason);
 | 
						|
        if (primaryTransport && !established) {
 | 
						|
          clearTimeout(this.fallbackTimer);
 | 
						|
          fallback(reason);
 | 
						|
        }
 | 
						|
      });
 | 
						|
      this.onOpen(() => {
 | 
						|
        established = true;
 | 
						|
        if (!primaryTransport) {
 | 
						|
          if (!this.primaryPassedHealthCheck) {
 | 
						|
            this.storeSession(`phx:fallback:${fallbackTransport.name}`, "true");
 | 
						|
          }
 | 
						|
          return this.log("transport", `established ${fallbackTransport.name} fallback`);
 | 
						|
        }
 | 
						|
        clearTimeout(this.fallbackTimer);
 | 
						|
        this.fallbackTimer = setTimeout(fallback, fallbackThreshold);
 | 
						|
        this.ping((rtt) => {
 | 
						|
          this.log("transport", "connected to primary after", rtt);
 | 
						|
          this.primaryPassedHealthCheck = true;
 | 
						|
          clearTimeout(this.fallbackTimer);
 | 
						|
        });
 | 
						|
      });
 | 
						|
      this.transportConnect();
 | 
						|
    }
 | 
						|
    clearHeartbeats() {
 | 
						|
      clearTimeout(this.heartbeatTimer);
 | 
						|
      clearTimeout(this.heartbeatTimeoutTimer);
 | 
						|
    }
 | 
						|
    onConnOpen() {
 | 
						|
      if (this.hasLogger())
 | 
						|
        this.log("transport", `${this.transport.name} connected to ${this.endPointURL()}`);
 | 
						|
      this.closeWasClean = false;
 | 
						|
      this.disconnecting = false;
 | 
						|
      this.establishedConnections++;
 | 
						|
      this.flushSendBuffer();
 | 
						|
      this.reconnectTimer.reset();
 | 
						|
      this.resetHeartbeat();
 | 
						|
      this.stateChangeCallbacks.open.forEach(([, callback]) => callback());
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    heartbeatTimeout() {
 | 
						|
      if (this.pendingHeartbeatRef) {
 | 
						|
        this.pendingHeartbeatRef = null;
 | 
						|
        if (this.hasLogger()) {
 | 
						|
          this.log("transport", "heartbeat timeout. Attempting to re-establish connection");
 | 
						|
        }
 | 
						|
        this.triggerChanError();
 | 
						|
        this.closeWasClean = false;
 | 
						|
        this.teardown(() => this.reconnectTimer.scheduleTimeout(), WS_CLOSE_NORMAL, "heartbeat timeout");
 | 
						|
      }
 | 
						|
    }
 | 
						|
    resetHeartbeat() {
 | 
						|
      if (this.conn && this.conn.skipHeartbeat) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      this.pendingHeartbeatRef = null;
 | 
						|
      this.clearHeartbeats();
 | 
						|
      this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs);
 | 
						|
    }
 | 
						|
    teardown(callback, code, reason) {
 | 
						|
      if (!this.conn) {
 | 
						|
        return callback && callback();
 | 
						|
      }
 | 
						|
      let connectClock = this.connectClock;
 | 
						|
      this.waitForBufferDone(() => {
 | 
						|
        if (connectClock !== this.connectClock) {
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        if (this.conn) {
 | 
						|
          if (code) {
 | 
						|
            this.conn.close(code, reason || "");
 | 
						|
          } else {
 | 
						|
            this.conn.close();
 | 
						|
          }
 | 
						|
        }
 | 
						|
        this.waitForSocketClosed(() => {
 | 
						|
          if (connectClock !== this.connectClock) {
 | 
						|
            return;
 | 
						|
          }
 | 
						|
          if (this.conn) {
 | 
						|
            this.conn.onopen = function() {
 | 
						|
            };
 | 
						|
            this.conn.onerror = function() {
 | 
						|
            };
 | 
						|
            this.conn.onmessage = function() {
 | 
						|
            };
 | 
						|
            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) {
 | 
						|
      let closeCode = event && event.code;
 | 
						|
      if (this.hasLogger())
 | 
						|
        this.log("transport", "close", event);
 | 
						|
      this.triggerChanError();
 | 
						|
      this.clearHeartbeats();
 | 
						|
      if (!this.closeWasClean && closeCode !== 1e3) {
 | 
						|
        this.reconnectTimer.scheduleTimeout();
 | 
						|
      }
 | 
						|
      this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    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();
 | 
						|
      }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    triggerChanError() {
 | 
						|
      this.channels.forEach((channel) => {
 | 
						|
        if (!(channel.isErrored() || channel.isLeaving() || channel.isClosed())) {
 | 
						|
          channel.trigger(CHANNEL_EVENTS.error);
 | 
						|
        }
 | 
						|
      });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @returns {string}
 | 
						|
     */
 | 
						|
    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";
 | 
						|
      }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @returns {boolean}
 | 
						|
     */
 | 
						|
    isConnected() {
 | 
						|
      return this.connectionState() === "open";
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @private
 | 
						|
     *
 | 
						|
     * @param {Channel}
 | 
						|
     */
 | 
						|
    remove(channel) {
 | 
						|
      this.off(channel.stateChangeRefs);
 | 
						|
      this.channels = this.channels.filter((c) => c !== channel);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Removes `onOpen`, `onClose`, `onError,` and `onMessage` registrations.
 | 
						|
     *
 | 
						|
     * @param {refs} - list of refs returned by calls to
 | 
						|
     *                 `onOpen`, `onClose`, `onError,` and `onMessage`
 | 
						|
     */
 | 
						|
    off(refs) {
 | 
						|
      for (let key in this.stateChangeCallbacks) {
 | 
						|
        this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => {
 | 
						|
          return refs.indexOf(ref) === -1;
 | 
						|
        });
 | 
						|
      }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Initiates a new channel for the given topic
 | 
						|
     *
 | 
						|
     * @param {string} topic
 | 
						|
     * @param {Object} chanParams - Parameters for the channel
 | 
						|
     * @returns {Channel}
 | 
						|
     */
 | 
						|
    channel(topic, chanParams = {}) {
 | 
						|
      let chan = new Channel(topic, chanParams, this);
 | 
						|
      this.channels.push(chan);
 | 
						|
      return chan;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @param {Object} data
 | 
						|
     */
 | 
						|
    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)));
 | 
						|
      }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Return the next message ref, accounting for overflows
 | 
						|
     * @returns {string}
 | 
						|
     */
 | 
						|
    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.heartbeatTimeoutTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs);
 | 
						|
    }
 | 
						|
    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) {
 | 
						|
          this.clearHeartbeats();
 | 
						|
          this.pendingHeartbeatRef = null;
 | 
						|
          this.heartbeatTimer = 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();
 | 
						|
      }
 | 
						|
    }
 | 
						|
  };
 | 
						|
  return __toCommonJS(phoenix_exports);
 | 
						|
})();
 |