PageSourceSearch

https://www.asastats.com/static/js/authmodal.1e200936e854.js

js asastats.com collected 2026-10-02 00:05:42 UTC 9,289 bytes, 241 lines download raw bytes

1/**
2 * @file Login modal on the DaisyUI base template
3 * @author Ivica Paleka
4 * @description Opens `#modalLogin` and switches its tabs. Materialize supplied
5 * both behaviours (`M.Modal` and `M.Tabs`); this replaces them with a native
6 * `<dialog>` and a few lines of delegation, so the modal needs no framework.
7 *
8 * Tabs stay anchor-driven (`<a href="#modal-tab-wallet">`) rather than becoming
9 * radio inputs: the anchors are addressable, they work as ordinary links with
10 * no JS, and the existing functional tests click them.
11 */
12(function () {
13  "use strict";
14
15  /** Marks the panel and its tab as the visible one. */
16  var ACTIVE = "is-active";
17
18  /**
19   * Show the tab panel with the given id, hiding its siblings.
20   *
21   * @param {Document|Element} root - subtree holding the tabs
22   * @param {string} panelId - id of the panel to reveal
23   * @returns {boolean} whether a matching panel was found
24   */
25  function showTab(root, panelId) {
26    var panel = root.querySelector("#" + panelId);
27    if (!panel) return false;
28    var group = panel.parentNode;
29    Array.prototype.forEach.call(group.children, function (child) {
30      if (child.classList.contains("modal-tab-panel")) {
31        child.classList.toggle(ACTIVE, child === panel);
32        child.hidden = child !== panel;
33      }
34    });
35    Array.prototype.forEach.call(
36      root.querySelectorAll('[href^="#modal-tab-"]'),
37      function (link) {
38        var on = link.getAttribute("href") === "#" + panelId;
39        link.classList.toggle(ACTIVE, on);
40        link.setAttribute("aria-selected", String(on));
41      }
42    );
43    return true;
44  }
45
46  /** Open the login dialog. Returns false when the page has no such dialog. */
47  function openAuthModal(doc) {
48    var dialog = (doc || document).getElementById("modalLogin");
49    if (!dialog) return false;
50    if (dialog.showModal && !dialog.open) dialog.showModal();
51    return true;
52  }
53
54  /** Close the login dialog if it is open. */
55  function closeAuthModal(doc) {
56    var dialog = (doc || document).getElementById("modalLogin");
57    if (dialog && dialog.close && dialog.open) dialog.close();
58    return !!dialog;
59  }
60
61  /**
62   * The controls the wallet card reveals once a connection has landed.
63   *
64   * `frontend/wallet` toggles these with `style.display` from its own
65   * subscription to the wallet state: `auth-button-<id>` when the wallet is
66   * connected and active (sign the 0 ALGO message), `set-active-button-<id>`
67   * when it is connected but some other wallet is active. Either one appearing
68   * means the reader has a next step, and the next step is inside the dialog.
69   */
70  var NEXT_STEP = '[id^="auth-button-"],[id^="set-active-button-"]';
71
72  /**
73   * Step out of the way of a wallet's own picker, then come back.
74   *
75   * `showModal()` puts the login dialog in the browser's *top layer*, which
76   * sits above every element in the normal layer no matter what z-index they
77   * carry. Wallet SDKs render their picker as ordinary DOM appended to
78   * `<body>`, so from inside this dialog the picker was painted underneath it:
79   * unreachable without closing the dialog first, and the connect attempt died
80   * along with the flow when the reader did. That is why the same wallet works
81   * on the dedicated login page, which has no dialog to be trapped behind.
82   *
83   * Neither element can be persuaded to yield -- the top layer is not
84   * negotiable from CSS -- so the dialog leaves it. It closes as the picker
85   * opens, and reopens on the wallet tab once the picker is gone, which covers
86   * a completed connection and an abandoned one alike: either way the SDK
87   * removes what it added.
88   *
89   * **Two cues, because the first one is only as good as the SDK's habits.**
90   * Watching the picker leave assumes the SDK appends its container *after*
91   * this snapshot is taken. That holds for a first connect and fails for a
92   * reconnect: a reader who arrives with a wallet session restored from a
93   * previous visit, disconnects, and connects again meets an SDK whose
94   * container is already on the body, so nothing is ever recorded as injected
95   * and the dialog never came back. They had to open it a second time to reach
96   * the Sign in button.
97   *
98   * So the connection itself is watched as well -- `NEXT_STEP` appearing inside
99   * the dialog -- which needs no assumption about anyone's DOM. It is the more
100   * reliable of the two and covers every wallet, but only when the reader
101   * completes the connection; the picker cue stays for the reader who abandons
102   * it, where there is no new control to wait for.
103   *
104   * If an SDK leaves its container behind *and* the reader abandons the
105   * connection, the dialog stays closed and the reader opens it again --
106   * exactly the position they are in today.
107   *
108   * @param {Document} host - document holding the dialog
109   * @param {Element} dialog - the login dialog, already open
110   * @returns {boolean} whether the handoff was armed
111   */
112  function walletHandoff(host, dialog) {
113    if (typeof MutationObserver === "undefined") return false;
114
115    /** Everything on the body before the SDK ran: the page itself. */
116    var before = Array.prototype.slice.call(host.body.children);
117
118    /** What the SDK has appended since. Its removal is our cue to come back. */
119    var injected = [];
120
121    var reopen = function () {
122      picker.disconnect();
123      connection.disconnect();
124      // Whether the reader has already reopened the dialog themselves, which
125      // they do when a cue is slow. Taking them to the wallet tab is the whole
126      // point when we reopen it; doing it to a dialog they opened and are
127      // typing into would be the second surprise in a row.
128      var closed = !dialog.open;
129      openAuthModal(host);
130      if (closed) showTab(host, "modal-tab-wallet");
131    };
132
133    var picker = new MutationObserver(function (records) {
134      Array.prototype.forEach.call(records, function (record) {
135        Array.prototype.forEach.call(record.addedNodes, function (node) {
136          if (node.nodeType !== 1) return;
137          if (before.indexOf(node) !== -1) return;
138          if (injected.indexOf(node) === -1) injected.push(node);
139        });
140      });
141
142      // Nothing has appeared yet -- the SDK may still be loading.
143      if (!injected.length) return;
144
145      var stillThere = injected.some(function (node) {
146        return host.body.contains(node);
147      });
148      if (stillThere) return;
149
150      reopen();
151    });
152
153    var connection = new MutationObserver(function (records) {
154      // Only a control that *changed* counts. Reading the buttons directly
155      // would answer for a card that has never rendered, whose `style` carries
156      // no display at all, and reopen the dialog the instant it was closed.
157      var landed = Array.prototype.some.call(records, function (record) {
158        var node = record.target;
159        return (
160          node.nodeType === 1 &&
161          node.matches &&
162          node.matches(NEXT_STEP) &&
163          node.style.display !== "none"
164        );
165      });
166      if (landed) reopen();
167    });
168
169    picker.observe(host.body, { childList: true });
170    connection.observe(dialog, {
171      attributes: true,
172      subtree: true,
173      attributeFilter: ["style"],
174    });
175    closeAuthModal(host);
176    return true;
177  }
178
179  /**
180   * Delegate every interaction from one listener on the document.
181   *
182   * @param {Document} doc - document to bind to
183   * @returns {boolean} whether binding happened
184   */
185  function wireAuthModal(doc) {
186    var host = doc || document;
187    if (!host.body || host.body.dataset.authModalBound === "1") return false;
188    host.body.dataset.authModalBound = "1";
189
190    host.addEventListener("click", function (ev) {
191      var target = ev.target;
192      if (!target || !target.closest) return;
193
194      var opener = target.closest('[href="#modalLogin"]');
195      if (opener) {
196        ev.preventDefault();
197        openAuthModal(host);
198        return;
199      }
200      var closer = target.closest(".id-modal-close");
201      if (closer) {
202        ev.preventDefault();
203        closeAuthModal(host);
204        return;
205      }
206      var tab = target.closest('[href^="#modal-tab-"]');
207      if (tab) {
208        ev.preventDefault();
209        showTab(host, tab.getAttribute("href").slice(1));
210        return;
211      }
212      // Not preventDefault'd and not returned early: the wallet package binds
213      // its own listener to this button and must still receive the click.
214      var connect = target.closest('[id^="connect-button-"]');
215      if (connect && connect.closest("#modalLogin")) {
216        var dialog = host.getElementById("modalLogin");
217        if (dialog && dialog.open) walletHandoff(host, dialog);
218      }
219    });
220    return true;
221  }
222
223  /* istanbul ignore else -- in the browser we self-start; under jest we export */
224  if (typeof module !== "undefined" && module.exports) {
225    module.exports = {
226      showTab: showTab,
227      openAuthModal: openAuthModal,
228      closeAuthModal: closeAuthModal,
229      walletHandoff: walletHandoff,
230      wireAuthModal: wireAuthModal,
231    };
232  } else {
233    if (document.readyState === "loading") {
234      document.addEventListener("DOMContentLoaded", function () {
235        wireAuthModal(document);
236      });
237    } else {
238      wireAuthModal(document);
239    }
240  }
241})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.