PageSourceSearch

https://www.nightflightplus.com/assets/application-eb-23f2fce38fd6…3ecbf8b22d58afbdcdbe4c22d0fda38c2cfb5d10.js

js nightflightplus.com collected 2026-10-02 03:20:31 UTC 317,035 bytes, 9,658 lines download raw bytes

vendor: 339 bytes, lines 1-10
1var __defProp = Object.defineProperty;
2var __getOwnPropNames = Object.getOwnPropertyNames;
3var __esm = (fn, res) => function __init() {
4  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5};
6var __export = (target, all) => {
7  for (var name in all)
8    __defProp(target, name, { get: all[name], enumerable: true });
9};
10
vendor: 334 bytes, lines 11-21
11// node_modules/@rails/actioncable/src/adapters.js
12var adapters_default;
13var init_adapters = __esm({
14  "node_modules/@rails/actioncable/src/adapters.js"() {
15    adapters_default = {
16      logger: typeof console !== "undefined" ? console : void 0,
17      WebSocket: typeof WebSocket !== "undefined" ? WebSocket : void 0
18    };
19  }
20});
21
vendor: 386 bytes, lines 22-37
22// node_modules/@rails/actioncable/src/logger.js
23var logger_default;
24var init_logger = __esm({
25  "node_modules/@rails/actioncable/src/logger.js"() {
26    init_adapters();
27    logger_default = {
28      log(...messages) {
29        if (this.enabled) {
30          messages.push(Date.now());
31          adapters_default.logger.log("[ActionCable]", ...messages);
32        }
33      }
34    };
35  }
36});
37
vendor: 4,101 bytes, lines 38-147
38// node_modules/@rails/actioncable/src/connection_monitor.js
39var now, secondsSince, ConnectionMonitor, connection_monitor_default;
40var init_connection_monitor = __esm({
41  "node_modules/@rails/actioncable/src/connection_monitor.js"() {
42    init_logger();
43    now = () => (/* @__PURE__ */ new Date()).getTime();
44    secondsSince = (time) => (now() - time) / 1e3;
45    ConnectionMonitor = class {
46      constructor(connection) {
47        this.visibilityDidChange = this.visibilityDidChange.bind(this);
48        this.connection = connection;
49        this.reconnectAttempts = 0;
50      }
51      start() {
52        if (!this.isRunning()) {
53          this.startedAt = now();
54          delete this.stoppedAt;
55          this.startPolling();
56          addEventListener("visibilitychange", this.visibilityDidChange);
57          logger_default.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`);
58        }
59      }
60      stop() {
61        if (this.isRunning()) {
62          this.stoppedAt = now();
63          this.stopPolling();
64          removeEventListener("visibilitychange", this.visibilityDidChange);
65          logger_default.log("ConnectionMonitor stopped");
66        }
67      }
68      isRunning() {
69        return this.startedAt && !this.stoppedAt;
70      }
71      recordMessage() {
72        this.pingedAt = now();
73      }
74      recordConnect() {
75        this.reconnectAttempts = 0;
76        delete this.disconnectedAt;
77        logger_default.log("ConnectionMonitor recorded connect");
78      }
79      recordDisconnect() {
80        this.disconnectedAt = now();
81        logger_default.log("ConnectionMonitor recorded disconnect");
82      }
83      // Private
84      startPolling() {
85        this.stopPolling();
86        this.poll();
87      }
88      stopPolling() {
89        clearTimeout(this.pollTimeout);
90      }
91      poll() {
92        this.pollTimeout = setTimeout(
93          () => {
94            this.reconnectIfStale();
95            this.poll();
96          },
97          this.getPollInterval()
98        );
99      }
100      getPollInterval() {
101        const { staleThreshold, reconnectionBackoffRate } = this.constructor;
102        const backoff = Math.pow(1 + reconnectionBackoffRate, Math.min(this.reconnectAttempts, 10));
103        const jitterMax = this.reconnectAttempts === 0 ? 1 : reconnectionBackoffRate;
104        const jitter = jitterMax * Math.random();
105        return staleThreshold * 1e3 * backoff * (1 + jitter);
106      }
107      reconnectIfStale() {
108        if (this.connectionIsStale()) {
109          logger_default.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${secondsSince(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`);
110          this.reconnectAttempts++;
111          if (this.disconnectedRecently()) {
112            logger_default.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${secondsSince(this.disconnectedAt)} s`);
113          } else {
114            logger_default.log("ConnectionMonitor reopening");
115            this.connection.reopen();
116          }
117        }
118      }
119      get refreshedAt() {
120        return this.pingedAt ? this.pingedAt : this.startedAt;
121      }
122      connectionIsStale() {
123        return secondsSince(this.refreshedAt) > this.constructor.staleThreshold;
124      }
125      disconnectedRecently() {
126        return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
127      }
128      visibilityDidChange() {
129        if (document.visibilityState === "visible") {
130          setTimeout(
131            () => {
132              if (this.connectionIsStale() || !this.connection.isOpen()) {
133                logger_default.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`);
134                this.connection.reopen();
135              }
136            },
137            200
138          );
139        }
140      }
141    };
142    ConnectionMonitor.staleThreshold = 6;
143    ConnectionMonitor.reconnectionBackoffRate = 0.15;
144    connection_monitor_default = ConnectionMonitor;
145  }
146});
147
vendor: 741 bytes, lines 148-174
148// node_modules/@rails/actioncable/src/internal.js
149var internal_default;
150var init_internal = __esm({
151  "node_modules/@rails/actioncable/src/internal.js"() {
152    internal_default = {
153      "message_types": {
154        "welcome": "welcome",
155        "disconnect": "disconnect",
156        "ping": "ping",
157        "confirmation": "confirm_subscription",
158        "rejection": "reject_subscription"
159      },
160      "disconnect_reasons": {
161        "unauthorized": "unauthorized",
162        "invalid_request": "invalid_request",
163        "server_restart": "server_restart",
164        "remote": "remote"
165      },
166      "default_mount_path": "/cable",
167      "protocols": [
168        "actioncable-v1-json",
169        "actioncable-unsupported"
170      ]
171    };
172  }
173});
174
vendor: 5,887 bytes, lines 175-343
175// node_modules/@rails/actioncable/src/connection.js
176var message_types, protocols, supportedProtocols, indexOf, Connection, connection_default;
177var init_connection = __esm({
178  "node_modules/@rails/actioncable/src/connection.js"() {
179    init_adapters();
180    init_connection_monitor();
181    init_internal();
182    init_logger();
183    ({ message_types, protocols } = internal_default);
184    supportedProtocols = protocols.slice(0, protocols.length - 1);
185    indexOf = [].indexOf;
186    Connection = class {
187      constructor(consumer2) {
188        this.open = this.open.bind(this);
189        this.consumer = consumer2;
190        this.subscriptions = this.consumer.subscriptions;
191        this.monitor = new connection_monitor_default(this);
192        this.disconnected = true;
193      }
194      send(data) {
195        if (this.isOpen()) {
196          this.webSocket.send(JSON.stringify(data));
197          return true;
198        } else {
199          return false;
200        }
201      }
202      open() {
203        if (this.isActive()) {
204          logger_default.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
205          return false;
206        } else {
207          const socketProtocols = [...protocols, ...this.consumer.subprotocols || []];
208          logger_default.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
209          if (this.webSocket) {
210            this.uninstallEventHandlers();
211          }
212          this.webSocket = new adapters_default.WebSocket(this.consumer.url, socketProtocols);
213          this.installEventHandlers();
214          this.monitor.start();
215          return true;
216        }
217      }
218      close({ allowReconnect } = { allowReconnect: true }) {
219        if (!allowReconnect) {
220          this.monitor.stop();
221        }
222        if (this.isOpen()) {
223          return this.webSocket.close();
224        }
225      }
226      reopen() {
227        logger_default.log(`Reopening WebSocket, current state is ${this.getState()}`);
228        if (this.isActive()) {
229          try {
230            return this.close();
231          } catch (error2) {
232            logger_default.log("Failed to reopen WebSocket", error2);
233          } finally {
234            logger_default.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`);
235            setTimeout(this.open, this.constructor.reopenDelay);
236          }
237        } else {
238          return this.open();
239        }
240      }
241      getProtocol() {
242        if (this.webSocket) {
243          return this.webSocket.protocol;
244        }
245      }
246      isOpen() {
247        return this.isState("open");
248      }
249      isActive() {
250        return this.isState("open", "connecting");
251      }
252      triedToReconnect() {
253        return this.monitor.reconnectAttempts > 0;
254      }
255      // Private
256      isProtocolSupported() {
257        return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
258      }
259      isState(...states) {
260        return indexOf.call(states, this.getState()) >= 0;
261      }
262      getState() {
263        if (this.webSocket) {
264          for (let state in adapters_default.WebSocket) {
265            if (adapters_default.WebSocket[state] === this.webSocket.readyState) {
266              return state.toLowerCase();
267            }
268          }
269        }
270        return null;
271      }
272      installEventHandlers() {
273        for (let eventName in this.events) {
274          const handler = this.events[eventName].bind(this);
275          this.webSocket[`on${eventName}`] = handler;
276        }
277      }
278      uninstallEventHandlers() {
279        for (let eventName in this.events) {
280          this.webSocket[`on${eventName}`] = function() {
281          };
282        }
283      }
284    };
285    Connection.reopenDelay = 500;
286    Connection.prototype.events = {
287      message(event) {
288        if (!this.isProtocolSupported()) {
289          return;
290        }
291        const { identifier, message, reason, reconnect, type } = JSON.parse(event.data);
292        this.monitor.recordMessage();
293        switch (type) {
294          case message_types.welcome:
295            if (this.triedToReconnect()) {
296              this.reconnectAttempted = true;
297            }
298            this.monitor.recordConnect();
299            return this.subscriptions.reload();
300          case message_types.disconnect:
301            logger_default.log(`Disconnecting. Reason: ${reason}`);
302            return this.close({ allowReconnect: reconnect });
303          case message_types.ping:
304            return null;
305          case message_types.confirmation:
306            this.subscriptions.confirmSubscription(identifier);
307            if (this.reconnectAttempted) {
308              this.reconnectAttempted = false;
309              return this.subscriptions.notify(identifier, "connected", { reconnected: true });
310            } else {
311              return this.subscriptions.notify(identifier, "connected", { reconnected: false });
312            }
313          case message_types.rejection:
314            return this.subscriptions.reject(identifier);
315          default:
316            return this.subscriptions.notify(identifier, "received", message);
317        }
318      },
319      open() {
320        logger_default.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`);
321        this.disconnected = false;
322        if (!this.isProtocolSupported()) {
323          logger_default.log("Protocol is unsupported. Stopping monitor and disconnecting.");
324          return this.close({ allowReconnect: false });
325        }
326      },
327      close(event) {
328        logger_default.log("WebSocket onclose event");
329        if (this.disconnected) {
330          return;
331        }
332        this.disconnected = true;
333        this.monitor.recordDisconnect();
334        return this.subscriptions.notifyAll("disconnected", { willAttemptReconnect: this.monitor.isRunning() });
335      },
336      error() {
337        logger_default.log("WebSocket onerror event");
338      }
339    };
340    connection_default = Connection;
341  }
342});
343
vendor: 1,036 bytes, lines 344-377
344// node_modules/@rails/actioncable/src/subscription.js
345var extend, Subscription;
346var init_subscription = __esm({
347  "node_modules/@rails/actioncable/src/subscription.js"() {
348    extend = function(object, properties) {
349      if (properties != null) {
350        for (let key in properties) {
351          const value = properties[key];
352          object[key] = value;
353        }
354      }
355      return object;
356    };
357    Subscription = class {
358      constructor(consumer2, params = {}, mixin) {
359        this.consumer = consumer2;
360        this.identifier = JSON.stringify(params);
361        extend(this, mixin);
362      }
363      // Perform a channel action with the optional data passed as an attribute
364      perform(action, data = {}) {
365        data.action = action;
366        return this.send(data);
367      }
368      send(data) {
369        return this.consumer.send({ command: "message", identifier: this.identifier, data: JSON.stringify(data) });
370      }
371      unsubscribe() {
372        return this.consumer.subscriptions.remove(this);
373      }
374    };
375  }
376});
377
vendor: 1,802 bytes, lines 378-425
378// node_modules/@rails/actioncable/src/subscription_guarantor.js
379var SubscriptionGuarantor, subscription_guarantor_default;
380var init_subscription_guarantor = __esm({
381  "node_modules/@rails/actioncable/src/subscription_guarantor.js"() {
382    init_logger();
383    SubscriptionGuarantor = class {
384      constructor(subscriptions) {
385        this.subscriptions = subscriptions;
386        this.pendingSubscriptions = [];
387      }
388      guarantee(subscription) {
389        if (this.pendingSubscriptions.indexOf(subscription) == -1) {
390          logger_default.log(`SubscriptionGuarantor guaranteeing ${subscription.identifier}`);
391          this.pendingSubscriptions.push(subscription);
392        } else {
393          logger_default.log(`SubscriptionGuarantor already guaranteeing ${subscription.identifier}`);
394        }
395        this.startGuaranteeing();
396      }
397      forget(subscription) {
398        logger_default.log(`SubscriptionGuarantor forgetting ${subscription.identifier}`);
399        this.pendingSubscriptions = this.pendingSubscriptions.filter((s) => s !== subscription);
400      }
401      startGuaranteeing() {
402        this.stopGuaranteeing();
403        this.retrySubscribing();
404      }
405      stopGuaranteeing() {
406        clearTimeout(this.retryTimeout);
407      }
408      retrySubscribing() {
409        this.retryTimeout = setTimeout(
410          () => {
411            if (this.subscriptions && typeof this.subscriptions.subscribe === "function") {
412              this.pendingSubscriptions.map((subscription) => {
413                logger_default.log(`SubscriptionGuarantor resubscribing ${subscription.identifier}`);
414                this.subscriptions.subscribe(subscription);
415              });
416            }
417          },
418          500
419        );
420      }
421    };
422    subscription_guarantor_default = SubscriptionGuarantor;
423  }
424});
425
vendor: 2,968 bytes, lines 426-506
426// node_modules/@rails/actioncable/src/subscriptions.js
427var Subscriptions;
428var init_subscriptions = __esm({
429  "node_modules/@rails/actioncable/src/subscriptions.js"() {
430    init_subscription();
431    init_subscription_guarantor();
432    init_logger();
433    Subscriptions = class {
434      constructor(consumer2) {
435        this.consumer = consumer2;
436        this.guarantor = new subscription_guarantor_default(this);
437        this.subscriptions = [];
438      }
439      create(channelName, mixin) {
440        const channel = channelName;
441        const params = typeof channel === "object" ? channel : { channel };
442        const subscription = new Subscription(this.consumer, params, mixin);
443        return this.add(subscription);
444      }
445      // Private
446      add(subscription) {
447        this.subscriptions.push(subscription);
448        this.consumer.ensureActiveConnection();
449        this.notify(subscription, "initialized");
450        this.subscribe(subscription);
451        return subscription;
452      }
453      remove(subscription) {
454        this.forget(subscription);
455        if (!this.findAll(subscription.identifier).length) {
456          this.sendCommand(subscription, "unsubscribe");
457        }
458        return subscription;
459      }
460      reject(identifier) {
461        return this.findAll(identifier).map((subscription) => {
462          this.forget(subscription);
463          this.notify(subscription, "rejected");
464          return subscription;
465        });
466      }
467      forget(subscription) {
468        this.guarantor.forget(subscription);
469        this.subscriptions = this.subscriptions.filter((s) => s !== subscription);
470        return subscription;
471      }
472      findAll(identifier) {
473        return this.subscriptions.filter((s) => s.identifier === identifier);
474      }
475      reload() {
476        return this.subscriptions.map((subscription) => this.subscribe(subscription));
477      }
478      notifyAll(callbackName, ...args) {
479        return this.subscriptions.map((subscription) => this.notify(subscription, callbackName, ...args));
480      }
481      notify(subscription, callbackName, ...args) {
482        let subscriptions;
483        if (typeof subscription === "string") {
484          subscriptions = this.findAll(subscription);
485        } else {
486          subscriptions = [subscription];
487        }
488        return subscriptions.map((subscription2) => typeof subscription2[callbackName] === "function" ? subscription2[callbackName](...args) : void 0);
489      }
490      subscribe(subscription) {
491        if (this.sendCommand(subscription, "subscribe")) {
492          this.guarantor.guarantee(subscription);
493        }
494      }
495      confirmSubscription(identifier) {
496        logger_default.log(`Subscription confirmed ${identifier}`);
497        this.findAll(identifier).map((subscription) => this.guarantor.forget(subscription));
498      }
499      sendCommand(subscription, command) {
500        const { identifier } = subscription;
501        return this.consumer.send({ command, identifier });
502      }
503    };
504  }
505});
506
vendor: 1,295 bytes, lines 507-557
507// node_modules/@rails/actioncable/src/consumer.js
508function createWebSocketURL(url) {
509  if (typeof url === "function") {
510    url = url();
511  }
512  if (url && !/^wss?:/i.test(url)) {
513    const a = document.createElement("a");
514    a.href = url;
515    a.href = a.href;
516    a.protocol = a.protocol.replace("http", "ws");
517    return a.href;
518  } else {
519    return url;
520  }
521}
522var Consumer;
523var init_consumer = __esm({
524  "node_modules/@rails/actioncable/src/consumer.js"() {
525    init_connection();
526    init_subscriptions();
527    Consumer = class {
528      constructor(url) {
529        this._url = url;
530        this.subscriptions = new Subscriptions(this);
531        this.connection = new connection_default(this);
532        this.subprotocols = [];
533      }
534      get url() {
535        return createWebSocketURL(this._url);
536      }
537      send(data) {
538        return this.connection.send(data);
539      }
540      connect() {
541        return this.connection.open();
542      }
543      disconnect() {
544        return this.connection.close({ allowReconnect: false });
545      }
546      ensureActiveConnection() {
547        if (!this.connection.isActive()) {
548          return this.connection.open();
549        }
550      }
551      addSubProtocol(subprotocol) {
552        this.subprotocols = [...this.subprotocols, subprotocol];
553      }
554    };
555  }
556});
557
vendor: 1,183 bytes, lines 558-596
558// node_modules/@rails/actioncable/src/index.js
559var src_exports = {};
560__export(src_exports, {
561  Connection: () => connection_default,
562  ConnectionMonitor: () => connection_monitor_default,
563  Consumer: () => Consumer,
564  INTERNAL: () => internal_default,
565  Subscription: () => Subscription,
566  SubscriptionGuarantor: () => subscription_guarantor_default,
567  Subscriptions: () => Subscriptions,
568  adapters: () => adapters_default,
569  createConsumer: () => createConsumer,
570  createWebSocketURL: () => createWebSocketURL,
571  getConfig: () => getConfig,
572  logger: () => logger_default
573});
574function createConsumer(url = getConfig("url") || internal_default.default_mount_path) {
575  return new Consumer(url);
576}
577function getConfig(name) {
578  const element = document.head.querySelector(`meta[name='action-cable-${name}']`);
579  if (element) {
580    return element.getAttribute("content");
581  }
582}
583var init_src = __esm({
584  "node_modules/@rails/actioncable/src/index.js"() {
585    init_connection();
586    init_connection_monitor();
587    init_consumer();
588    init_internal();
589    init_subscription();
590    init_subscriptions();
591    init_subscription_guarantor();
592    init_adapters();
593    init_logger();
594  }
595});
596
vendor: 172,739 bytes, lines 597-5962
597// node_modules/@hotwired/turbo/dist/turbo.es2017-esm.js
598var turbo_es2017_esm_exports = {};
599__export(turbo_es2017_esm_exports, {
600  FetchEnctype: () => FetchEnctype,
601  FetchMethod: () => FetchMethod,
602  FetchRequest: () => FetchRequest,
603  FetchResponse: () => FetchResponse,
604  FrameElement: () => FrameElement,
605  FrameLoadingStyle: () => FrameLoadingStyle,
606  FrameRenderer: () => FrameRenderer,
607  PageRenderer: () => PageRenderer,
608  PageSnapshot: () => PageSnapshot,
609  StreamActions: () => StreamActions,
610  StreamElement: () => StreamElement,
611  StreamSourceElement: () => StreamSourceElement,
612  cache: () => cache,
613  clearCache: () => clearCache,
614  config: () => config,
615  connectStreamSource: () => connectStreamSource,
616  disconnectStreamSource: () => disconnectStreamSource,
617  fetch: () => fetchWithTurboHeaders,
618  fetchEnctypeFromString: () => fetchEnctypeFromString,
619  fetchMethodFromString: () => fetchMethodFromString,
620  isSafe: () => isSafe,
621  navigator: () => navigator$1,
622  registerAdapter: () => registerAdapter,
623  renderStreamMessage: () => renderStreamMessage,
624  session: () => session,
625  setConfirmMethod: () => setConfirmMethod,
626  setFormMode: () => setFormMode,
627  setProgressBarDelay: () => setProgressBarDelay,
628  start: () => start,
629  visit: () => visit
630});
631(function(prototype) {
632  if (typeof prototype.requestSubmit == "function") return;
633  prototype.requestSubmit = function(submitter2) {
634    if (submitter2) {
635      validateSubmitter(submitter2, this);
636      submitter2.click();
637    } else {
638      submitter2 = document.createElement("input");
639      submitter2.type = "submit";
640      submitter2.hidden = true;
641      this.appendChild(submitter2);
642      submitter2.click();
643      this.removeChild(submitter2);
644    }
645  };
646  function validateSubmitter(submitter2, form) {
647    submitter2 instanceof HTMLElement || raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
648    submitter2.type == "submit" || raise(TypeError, "The specified element is not a submit button");
649    submitter2.form == form || raise(DOMException, "The specified element is not owned by this form element", "NotFoundError");
650  }
651  function raise(errorConstructor, message, name) {
652    throw new errorConstructor("Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".", name);
653  }
654})(HTMLFormElement.prototype);
655var submittersByForm = /* @__PURE__ */ new WeakMap();
656function findSubmitterFromClickTarget(target) {
657  const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
658  const candidate = element ? element.closest("input, button") : null;
659  return candidate?.type == "submit" ? candidate : null;
660}
661function clickCaptured(event) {
662  const submitter2 = findSubmitterFromClickTarget(event.target);
663  if (submitter2 && submitter2.form) {
664    submittersByForm.set(submitter2.form, submitter2);
665  }
666}
667(function() {
668  if ("submitter" in Event.prototype) return;
669  let prototype = window.Event.prototype;
670  if ("SubmitEvent" in window) {
671    const prototypeOfSubmitEvent = window.SubmitEvent.prototype;
672    if (/Apple Computer/.test(navigator.vendor) && !("submitter" in prototypeOfSubmitEvent)) {
673      prototype = prototypeOfSubmitEvent;
674    } else {
675      return;
676    }
677  }
678  addEventListener("click", clickCaptured, true);
679  Object.defineProperty(prototype, "submitter", {
680    get() {
681      if (this.type == "submit" && this.target instanceof HTMLFormElement) {
682        return submittersByForm.get(this.target);
683      }
684    }
685  });
686})();
687var FrameLoadingStyle = {
688  eager: "eager",
689  lazy: "lazy"
690};
691var FrameElement = class _FrameElement extends HTMLElement {
692  static delegateConstructor = void 0;
693  loaded = Promise.resolve();
694  static get observedAttributes() {
695    return ["disabled", "loading", "src"];
696  }
697  constructor() {
698    super();
699    this.delegate = new _FrameElement.delegateConstructor(this);
700  }
701  connectedCallback() {
702    this.delegate.connect();
703  }
704  disconnectedCallback() {
705    this.delegate.disconnect();
706  }
707  reload() {
708    return this.delegate.sourceURLReloaded();
709  }
710  attributeChangedCallback(name) {
711    if (name == "loading") {
712      this.delegate.loadingStyleChanged();
713    } else if (name == "src") {
714      this.delegate.sourceURLChanged();
715    } else if (name == "disabled") {
716      this.delegate.disabledChanged();
717    }
718  }
719  /**
720   * Gets the URL to lazily load source HTML from
721   */
722  get src() {
723    return this.getAttribute("src");
724  }
725  /**
726   * Sets the URL to lazily load source HTML from
727   */
728  set src(value) {
729    if (value) {
730      this.setAttribute("src", value);
731    } else {
732      this.removeAttribute("src");
733    }
734  }
735  /**
736   * Gets the refresh mode for the frame.
737   */
738  get refresh() {
739    return this.getAttribute("refresh");
740  }
741  /**
742   * Sets the refresh mode for the frame.
743   */
744  set refresh(value) {
745    if (value) {
746      this.setAttribute("refresh", value);
747    } else {
748      this.removeAttribute("refresh");
749    }
750  }
751  get shouldReloadWithMorph() {
752    return this.src && this.refresh === "morph";
753  }
754  /**
755   * Determines if the element is loading
756   */
757  get loading() {
758    return frameLoadingStyleFromString(this.getAttribute("loading") || "");
759  }
760  /**
761   * Sets the value of if the element is loading
762   */
763  set loading(value) {
764    if (value) {
765      this.setAttribute("loading", value);
766    } else {
767      this.removeAttribute("loading");
768    }
769  }
770  /**
771   * Gets the disabled state of the frame.
772   *
773   * If disabled, no requests will be intercepted by the frame.
774   */
775  get disabled() {
776    return this.hasAttribute("disabled");
777  }
778  /**
779   * Sets the disabled state of the frame.
780   *
781   * If disabled, no requests will be intercepted by the frame.
782   */
783  set disabled(value) {
784    if (value) {
785      this.setAttribute("disabled", "");
786    } else {
787      this.removeAttribute("disabled");
788    }
789  }
790  /**
791   * Gets the autoscroll state of the frame.
792   *
793   * If true, the frame will be scrolled into view automatically on update.
794   */
795  get autoscroll() {
796    return this.hasAttribute("autoscroll");
797  }
798  /**
799   * Sets the autoscroll state of the frame.
800   *
801   * If true, the frame will be scrolled into view automatically on update.
802   */
803  set autoscroll(value) {
804    if (value) {
805      this.setAttribute("autoscroll", "");
806    } else {
807      this.removeAttribute("autoscroll");
808    }
809  }
810  /**
811   * Determines if the element has finished loading
812   */
813  get complete() {
814    return !this.delegate.isLoading;
815  }
816  /**
817   * Gets the active state of the frame.
818   *
819   * If inactive, source changes will not be observed.
820   */
821  get isActive() {
822    return this.ownerDocument === document && !this.isPreview;
823  }
824  /**
825   * Sets the active state of the frame.
826   *
827   * If inactive, source changes will not be observed.
828   */
829  get isPreview() {
830    return this.ownerDocument?.documentElement?.hasAttribute("data-turbo-preview");
831  }
832};
833function frameLoadingStyleFromString(style) {
834  switch (style.toLowerCase()) {
835    case "lazy":
836      return FrameLoadingStyle.lazy;
837    default:
838      return FrameLoadingStyle.eager;
839  }
840}
841var drive = {
842  enabled: true,
843  progressBarDelay: 500,
844  unvisitableExtensions: /* @__PURE__ */ new Set(
845    [
846      ".7z",
847      ".aac",
848      ".apk",
849      ".avi",
850      ".bmp",
851      ".bz2",
852      ".css",
853      ".csv",
854      ".deb",
855      ".dmg",
856      ".doc",
857      ".docx",
858      ".exe",
859      ".gif",
860      ".gz",
861      ".heic",
862      ".heif",
863      ".ico",
864      ".iso",
865      ".jpeg",
866      ".jpg",
867      ".js",
868      ".json",
869      ".m4a",
870      ".mkv",
871      ".mov",
872      ".mp3",
873      ".mp4",
874      ".mpeg",
875      ".mpg",
876      ".msi",
877      ".ogg",
878      ".ogv",
879      ".pdf",
880      ".pkg",
881      ".png",
882      ".ppt",
883      ".pptx",
884      ".rar",
885      ".rtf",
886      ".svg",
887      ".tar",
888      ".tif",
889      ".tiff",
890      ".txt",
891      ".wav",
892      ".webm",
893      ".webp",
894      ".wma",
895      ".wmv",
896      ".xls",
897      ".xlsx",
898      ".xml",
899      ".zip"
900    ]
901  )
902};
903function activateScriptElement(element) {
904  if (element.getAttribute("data-turbo-eval") == "false") {
905    return element;
906  } else {
907    const createdScriptElement = document.createElement("script");
908    const cspNonce = getCspNonce();
909    if (cspNonce) {
910      createdScriptElement.nonce = cspNonce;
911    }
912    createdScriptElement.textContent = element.textContent;
913    createdScriptElement.async = false;
914    copyElementAttributes(createdScriptElement, element);
915    return createdScriptElement;
916  }
917}
918function copyElementAttributes(destinationElement, sourceElement) {
919  for (const { name, value } of sourceElement.attributes) {
920    destinationElement.setAttribute(name, value);
921  }
922}
923function createDocumentFragment(html) {
924  const template = document.createElement("template");
925  template.innerHTML = html;
926  return template.content;
927}
928function dispatch(eventName, { target, cancelable, detail } = {}) {
929  const event = new CustomEvent(eventName, {
930    cancelable,
931    bubbles: true,
932    composed: true,
933    detail
934  });
935  if (target && target.isConnected) {
936    target.dispatchEvent(event);
937  } else {
938    document.documentElement.dispatchEvent(event);
939  }
940  return event;
941}
942function cancelEvent(event) {
943  event.preventDefault();
944  event.stopImmediatePropagation();
945}
946function nextRepaint() {
947  if (document.visibilityState === "hidden") {
948    return nextEventLoopTick();
949  } else {
950    return nextAnimationFrame();
951  }
952}
953function nextAnimationFrame() {
954  return new Promise((resolve) => requestAnimationFrame(() => resolve()));
955}
956function nextEventLoopTick() {
957  return new Promise((resolve) => setTimeout(() => resolve(), 0));
958}
959function nextMicrotask() {
960  return Promise.resolve();
961}
962function parseHTMLDocument(html = "") {
963  return new DOMParser().parseFromString(html, "text/html");
964}
965function unindent(strings, ...values) {
966  const lines = interpolate(strings, values).replace(/^\n/, "").split("\n");
967  const match = lines[0].match(/^\s+/);
968  const indent = match ? match[0].length : 0;
969  return lines.map((line) => line.slice(indent)).join("\n");
970}
971function interpolate(strings, values) {
972  return strings.reduce((result, string, i) => {
973    const value = values[i] == void 0 ? "" : values[i];
974    return result + string + value;
975  }, "");
976}
977function uuid() {
978  return Array.from({ length: 36 }).map((_, i) => {
979    if (i == 8 || i == 13 || i == 18 || i == 23) {
980      return "-";
981    } else if (i == 14) {
982      return "4";
983    } else if (i == 19) {
984      return (Math.floor(Math.random() * 4) + 8).toString(16);
985    } else {
986      return Math.floor(Math.random() * 15).toString(16);
987    }
988  }).join("");
989}
990function getAttribute(attributeName, ...elements) {
991  for (const value of elements.map((element) => element?.getAttribute(attributeName))) {
992    if (typeof value == "string") return value;
993  }
994  return null;
995}
996function hasAttribute(attributeName, ...elements) {
997  return elements.some((element) => element && element.hasAttribute(attributeName));
998}
999function markAsBusy(...elements) {
1000  for (const element of elements) {
1001    if (element.localName == "turbo-frame") {
1002      element.setAttribute("busy", "");
1003    }
1004    element.setAttribute("aria-busy", "true");
1005  }
1006}
1007function clearBusyState(...elements) {
1008  for (const element of elements) {
1009    if (element.localName == "turbo-frame") {
1010      element.removeAttribute("busy");
1011    }
1012    element.removeAttribute("aria-busy");
1013  }
1014}
1015function waitForLoad(element, timeoutInMilliseconds = 2e3) {
1016  return new Promise((resolve) => {
1017    const onComplete = () => {
1018      element.removeEventListener("error", onComplete);
1019      element.removeEventListener("load", onComplete);
1020      resolve();
1021    };
1022    element.addEventListener("load", onComplete, { once: true });
1023    element.addEventListener("error", onComplete, { once: true });
1024    setTimeout(resolve, timeoutInMilliseconds);
1025  });
1026}
1027function getHistoryMethodForAction(action) {
1028  switch (action) {
1029    case "replace":
1030      return history.replaceState;
1031    case "advance":
1032    case "restore":
1033      return history.pushState;
1034  }
1035}
1036function isAction(action) {
1037  return action == "advance" || action == "replace" || action == "restore";
1038}
1039function getVisitAction(...elements) {
1040  const action = getAttribute("data-turbo-action", ...elements);
1041  return isAction(action) ? action : null;
1042}
1043function getMetaElement(name) {
1044  return document.querySelector(`meta[name="${name}"]`);
1045}
1046function getMetaContent(name) {
1047  const element = getMetaElement(name);
1048  return element && element.content;
1049}
1050function getCspNonce() {
1051  const element = getMetaElement("csp-nonce");
1052  if (element) {
1053    const { nonce, content } = element;
1054    return nonce == "" ? content : nonce;
1055  }
1056}
1057function setMetaContent(name, content) {
1058  let element = getMetaElement(name);
1059  if (!element) {
1060    element = document.createElement("meta");
1061    element.setAttribute("name", name);
1062    document.head.appendChild(element);
1063  }
1064  element.setAttribute("content", content);
1065  return element;
1066}
1067function findClosestRecursively(element, selector) {
1068  if (element instanceof Element) {
1069    return element.closest(selector) || findClosestRecursively(element.assignedSlot || element.getRootNode()?.host, selector);
1070  }
1071}
1072function elementIsFocusable(element) {
1073  const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
1074  return !!element && element.closest(inertDisabledOrHidden) == null && typeof element.focus == "function";
1075}
1076function queryAutofocusableElement(elementOrDocumentFragment) {
1077  return Array.from(elementOrDocumentFragment.querySelectorAll("[autofocus]")).find(elementIsFocusable);
1078}
1079async function around(callback, reader) {
1080  const before = reader();
1081  callback();
1082  await nextAnimationFrame();
1083  const after = reader();
1084  return [before, after];
1085}
1086function doesNotTargetIFrame(name) {
1087  if (name === "_blank") {
1088    return false;
1089  } else if (name) {
1090    for (const element of document.getElementsByName(name)) {
1091      if (element instanceof HTMLIFrameElement) return false;
1092    }
1093    return true;
1094  } else {
1095    return true;
1096  }
1097}
1098function findLinkFromClickTarget(target) {
1099  return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
1100}
1101function getLocationForLink(link) {
1102  return expandURL(link.getAttribute("href") || "");
1103}
1104function debounce(fn, delay) {
1105  let timeoutId = null;
1106  return (...args) => {
1107    const callback = () => fn.apply(this, args);
1108    clearTimeout(timeoutId);
1109    timeoutId = setTimeout(callback, delay);
1110  };
1111}
1112var submitter = {
1113  "aria-disabled": {
1114    beforeSubmit: (submitter2) => {
1115      submitter2.setAttribute("aria-disabled", "true");
1116      submitter2.addEventListener("click", cancelEvent);
1117    },
1118    afterSubmit: (submitter2) => {
1119      submitter2.removeAttribute("aria-disabled");
1120      submitter2.removeEventListener("click", cancelEvent);
1121    }
1122  },
1123  "disabled": {
1124    beforeSubmit: (submitter2) => submitter2.disabled = true,
1125    afterSubmit: (submitter2) => submitter2.disabled = false
1126  }
1127};
1128var Config = class {
1129  #submitter = null;
1130  constructor(config2) {
1131    Object.assign(this, config2);
1132  }
1133  get submitter() {
1134    return this.#submitter;
1135  }
1136  set submitter(value) {
1137    this.#submitter = submitter[value] || value;
1138  }
1139};
1140var forms = new Config({
1141  mode: "on",
1142  submitter: "disabled"
1143});
1144var config = {
1145  drive,
1146  forms
1147};
1148function expandURL(locatable) {
1149  return new URL(locatable.toString(), document.baseURI);
1150}
1151function getAnchor(url) {
1152  let anchorMatch;
1153  if (url.hash) {
1154    return url.hash.slice(1);
1155  } else if (anchorMatch = url.href.match(/#(.*)$/)) {
1156    return anchorMatch[1];
1157  }
1158}
1159function getAction$1(form, submitter2) {
1160  const action = submitter2?.getAttribute("formaction") || form.getAttribute("action") || form.action;
1161  return expandURL(action);
1162}
1163function getExtension(url) {
1164  return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || "";
1165}
1166function isPrefixedBy(baseURL, url) {
1167  const prefix = getPrefix(url);
1168  return baseURL.href === expandURL(prefix).href || baseURL.href.startsWith(prefix);
1169}
1170function locationIsVisitable(location2, rootLocation) {
1171  return isPrefixedBy(location2, rootLocation) && !config.drive.unvisitableExtensions.has(getExtension(location2));
1172}
1173function getRequestURL(url) {
1174  const anchor = getAnchor(url);
1175  return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href;
1176}
1177function toCacheKey(url) {
1178  return getRequestURL(url);
1179}
1180function urlsAreEqual(left, right) {
1181  return expandURL(left).href == expandURL(right).href;
1182}
1183function getPathComponents(url) {
1184  return url.pathname.split("/").slice(1);
1185}
1186function getLastPathComponent(url) {
1187  return getPathComponents(url).slice(-1)[0];
1188}
1189function getPrefix(url) {
1190  return addTrailingSlash(url.origin + url.pathname);
1191}
1192function addTrailingSlash(value) {
1193  return value.endsWith("/") ? value : value + "/";
1194}
1195var FetchResponse = class {
1196  constructor(response) {
1197    this.response = response;
1198  }
1199  get succeeded() {
1200    return this.response.ok;
1201  }
1202  get failed() {
1203    return !this.succeeded;
1204  }
1205  get clientError() {
1206    return this.statusCode >= 400 && this.statusCode <= 499;
1207  }
1208  get serverError() {
1209    return this.statusCode >= 500 && this.statusCode <= 599;
1210  }
1211  get redirected() {
1212    return this.response.redirected;
1213  }
1214  get location() {
1215    return expandURL(this.response.url);
1216  }
1217  get isHTML() {
1218    return this.contentType && this.contentType.match(/^(?:text\/([^\s;,]+\b)?html|application\/xhtml\+xml)\b/);
1219  }
1220  get statusCode() {
1221    return this.response.status;
1222  }
1223  get contentType() {
1224    return this.header("Content-Type");
1225  }
1226  get responseText() {
1227    return this.response.clone().text();
1228  }
1229  get responseHTML() {
1230    if (this.isHTML) {
1231      return this.response.clone().text();
1232    } else {
1233      return Promise.resolve(void 0);
1234    }
1235  }
1236  header(name) {
1237    return this.response.headers.get(name);
1238  }
1239};
1240var LimitedSet = class extends Set {
1241  constructor(maxSize) {
1242    super();
1243    this.maxSize = maxSize;
1244  }
1245  add(value) {
1246    if (this.size >= this.maxSize) {
1247      const iterator = this.values();
1248      const oldestValue = iterator.next().value;
1249      this.delete(oldestValue);
1250    }
1251    super.add(value);
1252  }
1253};
1254var recentRequests = new LimitedSet(20);
1255var nativeFetch = window.fetch;
1256function fetchWithTurboHeaders(url, options = {}) {
1257  const modifiedHeaders = new Headers(options.headers || {});
1258  const requestUID = uuid();
1259  recentRequests.add(requestUID);
1260  modifiedHeaders.append("X-Turbo-Request-Id", requestUID);
1261  return nativeFetch(url, {
1262    ...options,
1263    headers: modifiedHeaders
1264  });
1265}
1266function fetchMethodFromString(method) {
1267  switch (method.toLowerCase()) {
1268    case "get":
1269      return FetchMethod.get;
1270    case "post":
1271      return FetchMethod.post;
1272    case "put":
1273      return FetchMethod.put;
1274    case "patch":
1275      return FetchMethod.patch;
1276    case "delete":
1277      return FetchMethod.delete;
1278  }
1279}
1280var FetchMethod = {
1281  get: "get",
1282  post: "post",
1283  put: "put",
1284  patch: "patch",
1285  delete: "delete"
1286};
1287function fetchEnctypeFromString(encoding) {
1288  switch (encoding.toLowerCase()) {
1289    case FetchEnctype.multipart:
1290      return FetchEnctype.multipart;
1291    case FetchEnctype.plain:
1292      return FetchEnctype.plain;
1293    default:
1294      return FetchEnctype.urlEncoded;
1295  }
1296}
1297var FetchEnctype = {
1298  urlEncoded: "application/x-www-form-urlencoded",
1299  multipart: "multipart/form-data",
1300  plain: "text/plain"
1301};
1302var FetchRequest = class {
1303  abortController = new AbortController();
1304  #resolveRequestPromise = (_value) => {
1305  };
1306  constructor(delegate, method, location2, requestBody = new URLSearchParams(), target = null, enctype = FetchEnctype.urlEncoded) {
1307    const [url, body] = buildResourceAndBody(expandURL(location2), method, requestBody, enctype);
1308    this.delegate = delegate;
1309    this.url = url;
1310    this.target = target;
1311    this.fetchOptions = {
1312      credentials: "same-origin",
1313      redirect: "follow",
1314      method: method.toUpperCase(),
1315      headers: { ...this.defaultHeaders },
1316      body,
1317      signal: this.abortSignal,
1318      referrer: this.delegate.referrer?.href
1319    };
1320    this.enctype = enctype;
1321  }
1322  get method() {
1323    return this.fetchOptions.method;
1324  }
1325  set method(value) {
1326    const fetchBody = this.isSafe ? this.url.searchParams : this.fetchOptions.body || new FormData();
1327    const fetchMethod = fetchMethodFromString(value) || FetchMethod.get;
1328    this.url.search = "";
1329    const [url, body] = buildResourceAndBody(this.url, fetchMethod, fetchBody, this.enctype);
1330    this.url = url;
1331    this.fetchOptions.body = body;
1332    this.fetchOptions.method = fetchMethod.toUpperCase();
1333  }
1334  get headers() {
1335    return this.fetchOptions.headers;
1336  }
1337  set headers(value) {
1338    this.fetchOptions.headers = value;
1339  }
1340  get body() {
1341    if (this.isSafe) {
1342      return this.url.searchParams;
1343    } else {
1344      return this.fetchOptions.body;
1345    }
1346  }
1347  set body(value) {
1348    this.fetchOptions.body = value;
1349  }
1350  get location() {
1351    return this.url;
1352  }
1353  get params() {
1354    return this.url.searchParams;
1355  }
1356  get entries() {
1357    return this.body ? Array.from(this.body.entries()) : [];
1358  }
1359  cancel() {
1360    this.abortController.abort();
1361  }
1362  async perform() {
1363    const { fetchOptions } = this;
1364    this.delegate.prepareRequest(this);
1365    const event = await this.#allowRequestToBeIntercepted(fetchOptions);
1366    try {
1367      this.delegate.requestStarted(this);
1368      if (event.detail.fetchRequest) {
1369        this.response = event.detail.fetchRequest.response;
1370      } else {
1371        this.response = fetchWithTurboHeaders(this.url.href, fetchOptions);
1372      }
1373      const response = await this.response;
1374      return await this.receive(response);
1375    } catch (error2) {
1376      if (error2.name !== "AbortError") {
1377        if (this.#willDelegateErrorHandling(error2)) {
1378          this.delegate.requestErrored(this, error2);
1379        }
1380        throw error2;
1381      }
1382    } finally {
1383      this.delegate.requestFinished(this);
1384    }
1385  }
1386  async receive(response) {
1387    const fetchResponse = new FetchResponse(response);
1388    const event = dispatch("turbo:before-fetch-response", {
1389      cancelable: true,
1390      detail: { fetchResponse },
1391      target: this.target
1392    });
1393    if (event.defaultPrevented) {
1394      this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
1395    } else if (fetchResponse.succeeded) {
1396      this.delegate.requestSucceededWithResponse(this, fetchResponse);
1397    } else {
1398      this.delegate.requestFailedWithResponse(this, fetchResponse);
1399    }
1400    return fetchResponse;
1401  }
1402  get defaultHeaders() {
1403    return {
1404      Accept: "text/html, application/xhtml+xml"
1405    };
1406  }
1407  get isSafe() {
1408    return isSafe(this.method);
1409  }
1410  get abortSignal() {
1411    return this.abortController.signal;
1412  }
1413  acceptResponseType(mimeType) {
1414    this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", ");
1415  }
1416  async #allowRequestToBeIntercepted(fetchOptions) {
1417    const requestInterception = new Promise((resolve) => this.#resolveRequestPromise = resolve);
1418    const event = dispatch("turbo:before-fetch-request", {
1419      cancelable: true,
1420      detail: {
1421        fetchOptions,
1422        url: this.url,
1423        resume: this.#resolveRequestPromise
1424      },
1425      target: this.target
1426    });
1427    this.url = event.detail.url;
1428    if (event.defaultPrevented) await requestInterception;
1429    return event;
1430  }
1431  #willDelegateErrorHandling(error2) {
1432    const event = dispatch("turbo:fetch-request-error", {
1433      target: this.target,
1434      cancelable: true,
1435      detail: { request: this, error: error2 }
1436    });
1437    return !event.defaultPrevented;
1438  }
1439};
1440function isSafe(fetchMethod) {
1441  return fetchMethodFromString(fetchMethod) == FetchMethod.get;
1442}
1443function buildResourceAndBody(resource, method, requestBody, enctype) {
1444  const searchParams = Array.from(requestBody).length > 0 ? new URLSearchParams(entriesExcludingFiles(requestBody)) : resource.searchParams;
1445  if (isSafe(method)) {
1446    return [mergeIntoURLSearchParams(resource, searchParams), null];
1447  } else if (enctype == FetchEnctype.urlEncoded) {
1448    return [resource, searchParams];
1449  } else {
1450    return [resource, requestBody];
1451  }
1452}
1453function entriesExcludingFiles(requestBody) {
1454  const entries = [];
1455  for (const [name, value] of requestBody) {
1456    if (value instanceof File) continue;
1457    else entries.push([name, value]);
1458  }
1459  return entries;
1460}
1461function mergeIntoURLSearchParams(url, requestBody) {
1462  const searchParams = new URLSearchParams(entriesExcludingFiles(requestBody));
1463  url.search = searchParams.toString();
1464  return url;
1465}
1466var AppearanceObserver = class {
1467  started = false;
1468  constructor(delegate, element) {
1469    this.delegate = delegate;
1470    this.element = element;
1471    this.intersectionObserver = new IntersectionObserver(this.intersect);
1472  }
1473  start() {
1474    if (!this.started) {
1475      this.started = true;
1476      this.intersectionObserver.observe(this.element);
1477    }
1478  }
1479  stop() {
1480    if (this.started) {
1481      this.started = false;
1482      this.intersectionObserver.unobserve(this.element);
1483    }
1484  }
1485  intersect = (entries) => {
1486    const lastEntry = entries.slice(-1)[0];
1487    if (lastEntry?.isIntersecting) {
1488      this.delegate.elementAppearedInViewport(this.element);
1489    }
1490  };
1491};
1492var StreamMessage = class {
1493  static contentType = "text/vnd.turbo-stream.html";
1494  static wrap(message) {
1495    if (typeof message == "string") {
1496      return new this(createDocumentFragment(message));
1497    } else {
1498      return message;
1499    }
1500  }
1501  constructor(fragment) {
1502    this.fragment = importStreamElements(fragment);
1503  }
1504};
1505function importStreamElements(fragment) {
1506  for (const element of fragment.querySelectorAll("turbo-stream")) {
1507    const streamElement = document.importNode(element, true);
1508    for (const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")) {
1509      inertScriptElement.replaceWith(activateScriptElement(inertScriptElement));
1510    }
1511    element.replaceWith(streamElement);
1512  }
1513  return fragment;
1514}
1515var PREFETCH_DELAY = 100;
1516var PrefetchCache = class {
1517  #prefetchTimeout = null;
1518  #prefetched = null;
1519  get(url) {
1520    if (this.#prefetched && this.#prefetched.url === url && this.#prefetched.expire > Date.now()) {
1521      return this.#prefetched.request;
1522    }
1523  }
1524  setLater(url, request, ttl) {
1525    this.clear();
1526    this.#prefetchTimeout = setTimeout(() => {
1527      request.perform();
1528      this.set(url, request, ttl);
1529      this.#prefetchTimeout = null;
1530    }, PREFETCH_DELAY);
1531  }
1532  set(url, request, ttl) {
1533    this.#prefetched = { url, request, expire: new Date((/* @__PURE__ */ new Date()).getTime() + ttl) };
1534  }
1535  clear() {
1536    if (this.#prefetchTimeout) clearTimeout(this.#prefetchTimeout);
1537    this.#prefetched = null;
1538  }
1539};
1540var cacheTtl = 10 * 1e3;
1541var prefetchCache = new PrefetchCache();
1542var FormSubmissionState = {
1543  initialized: "initialized",
1544  requesting: "requesting",
1545  waiting: "waiting",
1546  receiving: "receiving",
1547  stopping: "stopping",
1548  stopped: "stopped"
1549};
1550var FormSubmission = class _FormSubmission {
1551  state = FormSubmissionState.initialized;
1552  static confirmMethod(message) {
1553    return Promise.resolve(confirm(message));
1554  }
1555  constructor(delegate, formElement, submitter2, mustRedirect = false) {
1556    const method = getMethod(formElement, submitter2);
1557    const action = getAction(getFormAction(formElement, submitter2), method);
1558    const body = buildFormData(formElement, submitter2);
1559    const enctype = getEnctype(formElement, submitter2);
1560    this.delegate = delegate;
1561    this.formElement = formElement;
1562    this.submitter = submitter2;
1563    this.fetchRequest = new FetchRequest(this, method, action, body, formElement, enctype);
1564    this.mustRedirect = mustRedirect;
1565  }
1566  get method() {
1567    return this.fetchRequest.method;
1568  }
1569  set method(value) {
1570    this.fetchRequest.method = value;
1571  }
1572  get action() {
1573    return this.fetchRequest.url.toString();
1574  }
1575  set action(value) {
1576    this.fetchRequest.url = expandURL(value);
1577  }
1578  get body() {
1579    return this.fetchRequest.body;
1580  }
1581  get enctype() {
1582    return this.fetchRequest.enctype;
1583  }
1584  get isSafe() {
1585    return this.fetchRequest.isSafe;
1586  }
1587  get location() {
1588    return this.fetchRequest.url;
1589  }
1590  // The submission process
1591  async start() {
1592    const { initialized, requesting } = FormSubmissionState;
1593    const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
1594    if (typeof confirmationMessage === "string") {
1595      const confirmMethod = typeof config.forms.confirm === "function" ? config.forms.confirm : _FormSubmission.confirmMethod;
1596      const answer = await confirmMethod(confirmationMessage, this.formElement, this.submitter);
1597      if (!answer) {
1598        return;
1599      }
1600    }
1601    if (this.state == initialized) {
1602      this.state = requesting;
1603      return this.fetchRequest.perform();
1604    }
1605  }
1606  stop() {
1607    const { stopping, stopped } = FormSubmissionState;
1608    if (this.state != stopping && this.state != stopped) {
1609      this.state = stopping;
1610      this.fetchRequest.cancel();
1611      return true;
1612    }
1613  }
1614  // Fetch request delegate
1615  prepareRequest(request) {
1616    if (!request.isSafe) {
1617      const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token");
1618      if (token) {
1619        request.headers["X-CSRF-Token"] = token;
1620      }
1621    }
1622    if (this.requestAcceptsTurboStreamResponse(request)) {
1623      request.acceptResponseType(StreamMessage.contentType);
1624    }
1625  }
1626  requestStarted(_request) {
1627    this.state = FormSubmissionState.waiting;
1628    if (this.submitter) config.forms.submitter.beforeSubmit(this.submitter);
1629    this.setSubmitsWith();
1630    markAsBusy(this.formElement);
1631    dispatch("turbo:submit-start", {
1632      target: this.formElement,
1633      detail: { formSubmission: this }
1634    });
1635    this.delegate.formSubmissionStarted(this);
1636  }
1637  requestPreventedHandlingResponse(request, response) {
1638    prefetchCache.clear();
1639    this.result = { success: response.succeeded, fetchResponse: response };
1640  }
1641  requestSucceededWithResponse(request, response) {
1642    if (response.clientError || response.serverError) {
1643      this.delegate.formSubmissionFailedWithResponse(this, response);
1644      return;
1645    }
1646    prefetchCache.clear();
1647    if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) {
1648      const error2 = new Error("Form responses must redirect to another location");
1649      this.delegate.formSubmissionErrored(this, error2);
1650    } else {
1651      this.state = FormSubmissionState.receiving;
1652      this.result = { success: true, fetchResponse: response };
1653      this.delegate.formSubmissionSucceededWithResponse(this, response);
1654    }
1655  }
1656  requestFailedWithResponse(request, response) {
1657    this.result = { success: false, fetchResponse: response };
1658    this.delegate.formSubmissionFailedWithResponse(this, response);
1659  }
1660  requestErrored(request, error2) {
1661    this.result = { success: false, error: error2 };
1662    this.delegate.formSubmissionErrored(this, error2);
1663  }
1664  requestFinished(_request) {
1665    this.state = FormSubmissionState.stopped;
1666    if (this.submitter) config.forms.submitter.afterSubmit(this.submitter);
1667    this.resetSubmitterText();
1668    clearBusyState(this.formElement);
1669    dispatch("turbo:submit-end", {
1670      target: this.formElement,
1671      detail: { formSubmission: this, ...this.result }
1672    });
1673    this.delegate.formSubmissionFinished(this);
1674  }
1675  // Private
1676  setSubmitsWith() {
1677    if (!this.submitter || !this.submitsWith) return;
1678    if (this.submitter.matches("button")) {
1679      this.originalSubmitText = this.submitter.innerHTML;
1680      this.submitter.innerHTML = this.submitsWith;
1681    } else if (this.submitter.matches("input")) {
1682      const input = this.submitter;
1683      this.originalSubmitText = input.value;
1684      input.value = this.submitsWith;
1685    }
1686  }
1687  resetSubmitterText() {
1688    if (!this.submitter || !this.originalSubmitText) return;
1689    if (this.submitter.matches("button")) {
1690      this.submitter.innerHTML = this.originalSubmitText;
1691    } else if (this.submitter.matches("input")) {
1692      const input = this.submitter;
1693      input.value = this.originalSubmitText;
1694    }
1695  }
1696  requestMustRedirect(request) {
1697    return !request.isSafe && this.mustRedirect;
1698  }
1699  requestAcceptsTurboStreamResponse(request) {
1700    return !request.isSafe || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
1701  }
1702  get submitsWith() {
1703    return this.submitter?.getAttribute("data-turbo-submits-with");
1704  }
1705};
1706function buildFormData(formElement, submitter2) {
1707  const formData = new FormData(formElement);
1708  const name = submitter2?.getAttribute("name");
1709  const value = submitter2?.getAttribute("value");
1710  if (name) {
1711    formData.append(name, value || "");
1712  }
1713  return formData;
1714}
1715function getCookieValue(cookieName) {
1716  if (cookieName != null) {
1717    const cookies = document.cookie ? document.cookie.split("; ") : [];
1718    const cookie = cookies.find((cookie2) => cookie2.startsWith(cookieName));
1719    if (cookie) {
1720      const value = cookie.split("=").slice(1).join("=");
1721      return value ? decodeURIComponent(value) : void 0;
1722    }
1723  }
1724}
1725function responseSucceededWithoutRedirect(response) {
1726  return response.statusCode == 200 && !response.redirected;
1727}
1728function getFormAction(formElement, submitter2) {
1729  const formElementAction = typeof formElement.action === "string" ? formElement.action : null;
1730  if (submitter2?.hasAttribute("formaction")) {
1731    return submitter2.getAttribute("formaction") || "";
1732  } else {
1733    return formElement.getAttribute("action") || formElementAction || "";
1734  }
1735}
1736function getAction(formAction, fetchMethod) {
1737  const action = expandURL(formAction);
1738  if (isSafe(fetchMethod)) {
1739    action.search = "";
1740  }
1741  return action;
1742}
1743function getMethod(formElement, submitter2) {
1744  const method = submitter2?.getAttribute("formmethod") || formElement.getAttribute("method") || "";
1745  return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get;
1746}
1747function getEnctype(formElement, submitter2) {
1748  return fetchEnctypeFromString(submitter2?.getAttribute("formenctype") || formElement.enctype);
1749}
1750var Snapshot = class {
1751  constructor(element) {
1752    this.element = element;
1753  }
1754  get activeElement() {
1755    return this.element.ownerDocument.activeElement;
1756  }
1757  get children() {
1758    return [...this.element.children];
1759  }
1760  hasAnchor(anchor) {
1761    return this.getElementForAnchor(anchor) != null;
1762  }
1763  getElementForAnchor(anchor) {
1764    return anchor ? this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`) : null;
1765  }
1766  get isConnected() {
1767    return this.element.isConnected;
1768  }
1769  get firstAutofocusableElement() {
1770    return queryAutofocusableElement(this.element);
1771  }
1772  get permanentElements() {
1773    return queryPermanentElementsAll(this.element);
1774  }
1775  getPermanentElementById(id) {
1776    return getPermanentElementById(this.element, id);
1777  }
1778  getPermanentElementMapForSnapshot(snapshot) {
1779    const permanentElementMap = {};
1780    for (const currentPermanentElement of this.permanentElements) {
1781      const { id } = currentPermanentElement;
1782      const newPermanentElement = snapshot.getPermanentElementById(id);
1783      if (newPermanentElement) {
1784        permanentElementMap[id] = [currentPermanentElement, newPermanentElement];
1785      }
1786    }
1787    return permanentElementMap;
1788  }
1789};
1790function getPermanentElementById(node, id) {
1791  return node.querySelector(`#${id}[data-turbo-permanent]`);
1792}
1793function queryPermanentElementsAll(node) {
1794  return node.querySelectorAll("[id][data-turbo-permanent]");
1795}
1796var FormSubmitObserver = class {
1797  started = false;
1798  constructor(delegate, eventTarget) {
1799    this.delegate = delegate;
1800    this.eventTarget = eventTarget;
1801  }
1802  start() {
1803    if (!this.started) {
1804      this.eventTarget.addEventListener("submit", this.submitCaptured, true);
1805      this.started = true;
1806    }
1807  }
1808  stop() {
1809    if (this.started) {
1810      this.eventTarget.removeEventListener("submit", this.submitCaptured, true);
1811      this.started = false;
1812    }
1813  }
1814  submitCaptured = () => {
1815    this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
1816    this.eventTarget.addEventListener("submit", this.submitBubbled, false);
1817  };
1818  submitBubbled = (event) => {
1819    if (!event.defaultPrevented) {
1820      const form = event.target instanceof HTMLFormElement ? event.target : void 0;
1821      const submitter2 = event.submitter || void 0;
1822      if (form && submissionDoesNotDismissDialog(form, submitter2) && submissionDoesNotTargetIFrame(form, submitter2) && this.delegate.willSubmitForm(form, submitter2)) {
1823        event.preventDefault();
1824        event.stopImmediatePropagation();
1825        this.delegate.formSubmitted(form, submitter2);
1826      }
1827    }
1828  };
1829};
1830function submissionDoesNotDismissDialog(form, submitter2) {
1831  const method = submitter2?.getAttribute("formmethod") || form.getAttribute("method");
1832  return method != "dialog";
1833}
1834function submissionDoesNotTargetIFrame(form, submitter2) {
1835  const target = submitter2?.getAttribute("formtarget") || form.getAttribute("target");
1836  return doesNotTargetIFrame(target);
1837}
1838var View = class {
1839  #resolveRenderPromise = (_value) => {
1840  };
1841  #resolveInterceptionPromise = (_value) => {
1842  };
1843  constructor(delegate, element) {
1844    this.delegate = delegate;
1845    this.element = element;
1846  }
1847  // Scrolling
1848  scrollToAnchor(anchor) {
1849    const element = this.snapshot.getElementForAnchor(anchor);
1850    if (element) {
1851      this.scrollToElement(element);
1852      this.focusElement(element);
1853    } else {
1854      this.scrollToPosition({ x: 0, y: 0 });
1855    }
1856  }
1857  scrollToAnchorFromLocation(location2) {
1858    this.scrollToAnchor(getAnchor(location2));
1859  }
1860  scrollToElement(element) {
1861    element.scrollIntoView();
1862  }
1863  focusElement(element) {
1864    if (element instanceof HTMLElement) {
1865      if (element.hasAttribute("tabindex")) {
1866        element.focus();
1867      } else {
1868        element.setAttribute("tabindex", "-1");
1869        element.focus();
1870        element.removeAttribute("tabindex");
1871      }
1872    }
1873  }
1874  scrollToPosition({ x, y }) {
1875    this.scrollRoot.scrollTo(x, y);
1876  }
1877  scrollToTop() {
1878    this.scrollToPosition({ x: 0, y: 0 });
1879  }
1880  get scrollRoot() {
1881    return window;
1882  }
1883  // Rendering
1884  async render(renderer) {
1885    const { isPreview, shouldRender, willRender, newSnapshot: snapshot } = renderer;
1886    const shouldInvalidate = willRender;
1887    if (shouldRender) {
1888      try {
1889        this.renderPromise = new Promise((resolve) => this.#resolveRenderPromise = resolve);
1890        this.renderer = renderer;
1891        await this.prepareToRenderSnapshot(renderer);
1892        const renderInterception = new Promise((resolve) => this.#resolveInterceptionPromise = resolve);
1893        const options = { resume: this.#resolveInterceptionPromise, render: this.renderer.renderElement, renderMethod: this.renderer.renderMethod };
1894        const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
1895        if (!immediateRender) await renderInterception;
1896        await this.renderSnapshot(renderer);
1897        this.delegate.viewRenderedSnapshot(snapshot, isPreview, this.renderer.renderMethod);
1898        this.delegate.preloadOnLoadLinksForView(this.element);
1899        this.finishRenderingSnapshot(renderer);
1900      } finally {
1901        delete this.renderer;
1902        this.#resolveRenderPromise(void 0);
1903        delete this.renderPromise;
1904      }
1905    } else if (shouldInvalidate) {
1906      this.invalidate(renderer.reloadReason);
1907    }
1908  }
1909  invalidate(reason) {
1910    this.delegate.viewInvalidated(reason);
1911  }
1912  async prepareToRenderSnapshot(renderer) {
1913    this.markAsPreview(renderer.isPreview);
1914    await renderer.prepareToRender();
1915  }
1916  markAsPreview(isPreview) {
1917    if (isPreview) {
1918      this.element.setAttribute("data-turbo-preview", "");
1919    } else {
1920      this.element.removeAttribute("data-turbo-preview");
1921    }
1922  }
1923  markVisitDirection(direction) {
1924    this.element.setAttribute("data-turbo-visit-direction", direction);
1925  }
1926  unmarkVisitDirection() {
1927    this.element.removeAttribute("data-turbo-visit-direction");
1928  }
1929  async renderSnapshot(renderer) {
1930    await renderer.render();
1931  }
1932  finishRenderingSnapshot(renderer) {
1933    renderer.finishRendering();
1934  }
1935};
1936var FrameView = class extends View {
1937  missing() {
1938    this.element.innerHTML = `<strong class="turbo-frame-error">Content missing</strong>`;
1939  }
1940  get snapshot() {
1941    return new Snapshot(this.element);
1942  }
1943};
1944var LinkInterceptor = class {
1945  constructor(delegate, element) {
1946    this.delegate = delegate;
1947    this.element = element;
1948  }
1949  start() {
1950    this.element.addEventListener("click", this.clickBubbled);
1951    document.addEventListener("turbo:click", this.linkClicked);
1952    document.addEventListener("turbo:before-visit", this.willVisit);
1953  }
1954  stop() {
1955    this.element.removeEventListener("click", this.clickBubbled);
1956    document.removeEventListener("turbo:click", this.linkClicked);
1957    document.removeEventListener("turbo:before-visit", this.willVisit);
1958  }
1959  clickBubbled = (event) => {
1960    if (this.clickEventIsSignificant(event)) {
1961      this.clickEvent = event;
1962    } else {
1963      delete this.clickEvent;
1964    }
1965  };
1966  linkClicked = (event) => {
1967    if (this.clickEvent && this.clickEventIsSignificant(event)) {
1968      if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url, event.detail.originalEvent)) {
1969        this.clickEvent.preventDefault();
1970        event.preventDefault();
1971        this.delegate.linkClickIntercepted(event.target, event.detail.url, event.detail.originalEvent);
1972      }
1973    }
1974    delete this.clickEvent;
1975  };
1976  willVisit = (_event) => {
1977    delete this.clickEvent;
1978  };
1979  clickEventIsSignificant(event) {
1980    const target = event.composed ? event.target?.parentElement : event.target;
1981    const element = findLinkFromClickTarget(target) || target;
1982    return element instanceof Element && element.closest("turbo-frame, html") == this.element;
1983  }
1984};
1985var LinkClickObserver = class {
1986  started = false;
1987  constructor(delegate, eventTarget) {
1988    this.delegate = delegate;
1989    this.eventTarget = eventTarget;
1990  }
1991  start() {
1992    if (!this.started) {
1993      this.eventTarget.addEventListener("click", this.clickCaptured, true);
1994      this.started = true;
1995    }
1996  }
1997  stop() {
1998    if (this.started) {
1999      this.eventTarget.removeEventListener("click", this.clickCaptured, true);
2000      this.started = false;
2001    }
2002  }
2003  clickCaptured = () => {
2004    this.eventTarget.removeEventListener("click", this.clickBubbled, false);
2005    this.eventTarget.addEventListener("click", this.clickBubbled, false);
2006  };
2007  clickBubbled = (event) => {
2008    if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
2009      const target = event.composedPath && event.composedPath()[0] || event.target;
2010      const link = findLinkFromClickTarget(target);
2011      if (link && doesNotTargetIFrame(link.target)) {
2012        const location2 = getLocationForLink(link);
2013        if (this.delegate.willFollowLinkToLocation(link, location2, event)) {
2014          event.preventDefault();
2015          this.delegate.followedLinkToLocation(link, location2);
2016        }
2017      }
2018    }
2019  };
2020  clickEventIsSignificant(event) {
2021    return !(event.target && event.target.isContentEditable || event.defaultPrevented || event.which > 1 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey);
2022  }
2023};
2024var FormLinkClickObserver = class {
2025  constructor(delegate, element) {
2026    this.delegate = delegate;
2027    this.linkInterceptor = new LinkClickObserver(this, element);
2028  }
2029  start() {
2030    this.linkInterceptor.start();
2031  }
2032  stop() {
2033    this.linkInterceptor.stop();
2034  }
2035  // Link hover observer delegate
2036  canPrefetchRequestToLocation(link, location2) {
2037    return false;
2038  }
2039  prefetchAndCacheRequestToLocation(link, location2) {
2040    return;
2041  }
2042  // Link click observer delegate
2043  willFollowLinkToLocation(link, location2, originalEvent) {
2044    return this.delegate.willSubmitFormLinkToLocation(link, location2, originalEvent) && (link.hasAttribute("data-turbo-method") || link.hasAttribute("data-turbo-stream"));
2045  }
2046  followedLinkToLocation(link, location2) {
2047    const form = document.createElement("form");
2048    const type = "hidden";
2049    for (const [name, value] of location2.searchParams) {
2050      form.append(Object.assign(document.createElement("input"), { type, name, value }));
2051    }
2052    const action = Object.assign(location2, { search: "" });
2053    form.setAttribute("data-turbo", "true");
2054    form.setAttribute("action", action.href);
2055    form.setAttribute("hidden", "");
2056    const method = link.getAttribute("data-turbo-method");
2057    if (method) form.setAttribute("method", method);
2058    const turboFrame = link.getAttribute("data-turbo-frame");
2059    if (turboFrame) form.setAttribute("data-turbo-frame", turboFrame);
2060    const turboAction = getVisitAction(link);
2061    if (turboAction) form.setAttribute("data-turbo-action", turboAction);
2062    const turboConfirm = link.getAttribute("data-turbo-confirm");
2063    if (turboConfirm) form.setAttribute("data-turbo-confirm", turboConfirm);
2064    const turboStream = link.hasAttribute("data-turbo-stream");
2065    if (turboStream) form.setAttribute("data-turbo-stream", "");
2066    this.delegate.submittedFormLinkToLocation(link, location2, form);
2067    document.body.appendChild(form);
2068    form.addEventListener("turbo:submit-end", () => form.remove(), { once: true });
2069    requestAnimationFrame(() => form.requestSubmit());
2070  }
2071};
2072var Bardo = class {
2073  static async preservingPermanentElements(delegate, permanentElementMap, callback) {
2074    const bardo = new this(delegate, permanentElementMap);
2075    bardo.enter();
2076    await callback();
2077    bardo.leave();
2078  }
2079  constructor(delegate, permanentElementMap) {
2080    this.delegate = delegate;
2081    this.permanentElementMap = permanentElementMap;
2082  }
2083  enter() {
2084    for (const id in this.permanentElementMap) {
2085      const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
2086      this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
2087      this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
2088    }
2089  }
2090  leave() {
2091    for (const id in this.permanentElementMap) {
2092      const [currentPermanentElement] = this.permanentElementMap[id];
2093      this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
2094      this.replacePlaceholderWithPermanentElement(currentPermanentElement);
2095      this.delegate.leavingBardo(currentPermanentElement);
2096    }
2097  }
2098  replaceNewPermanentElementWithPlaceholder(permanentElement) {
2099    const placeholder = createPlaceholderForPermanentElement(permanentElement);
2100    permanentElement.replaceWith(placeholder);
2101  }
2102  replaceCurrentPermanentElementWithClone(permanentElement) {
2103    const clone = permanentElement.cloneNode(true);
2104    permanentElement.replaceWith(clone);
2105  }
2106  replacePlaceholderWithPermanentElement(permanentElement) {
2107    const placeholder = this.getPlaceholderById(permanentElement.id);
2108    placeholder?.replaceWith(permanentElement);
2109  }
2110  getPlaceholderById(id) {
2111    return this.placeholders.find((element) => element.content == id);
2112  }
2113  get placeholders() {
2114    return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
2115  }
2116};
2117function createPlaceholderForPermanentElement(permanentElement) {
2118  const element = document.createElement("meta");
2119  element.setAttribute("name", "turbo-permanent-placeholder");
2120  element.setAttribute("content", permanentElement.id);
2121  return element;
2122}
2123var Renderer = class {
2124  #activeElement = null;
2125  static renderElement(currentElement, newElement) {
2126  }
2127  constructor(currentSnapshot, newSnapshot, isPreview, willRender = true) {
2128    this.currentSnapshot = currentSnapshot;
2129    this.newSnapshot = newSnapshot;
2130    this.isPreview = isPreview;
2131    this.willRender = willRender;
2132    this.renderElement = this.constructor.renderElement;
2133    this.promise = new Promise((resolve, reject) => this.resolvingFunctions = { resolve, reject });
2134  }
2135  get shouldRender() {
2136    return true;
2137  }
2138  get shouldAutofocus() {
2139    return true;
2140  }
2141  get reloadReason() {
2142    return;
2143  }
2144  prepareToRender() {
2145    return;
2146  }
2147  render() {
2148  }
2149  finishRendering() {
2150    if (this.resolvingFunctions) {
2151      this.resolvingFunctions.resolve();
2152      delete this.resolvingFunctions;
2153    }
2154  }
2155  async preservingPermanentElements(callback) {
2156    await Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
2157  }
2158  focusFirstAutofocusableElement() {
2159    if (this.shouldAutofocus) {
2160      const element = this.connectedSnapshot.firstAutofocusableElement;
2161      if (element) {
2162        element.focus();
2163      }
2164    }
2165  }
2166  // Bardo delegate
2167  enteringBardo(currentPermanentElement) {
2168    if (this.#activeElement) return;
2169    if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
2170      this.#activeElement = this.currentSnapshot.activeElement;
2171    }
2172  }
2173  leavingBardo(currentPermanentElement) {
2174    if (currentPermanentElement.contains(this.#activeElement) && this.#activeElement instanceof HTMLElement) {
2175      this.#activeElement.focus();
2176      this.#activeElement = null;
2177    }
2178  }
2179  get connectedSnapshot() {
2180    return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
2181  }
2182  get currentElement() {
2183    return this.currentSnapshot.element;
2184  }
2185  get newElement() {
2186    return this.newSnapshot.element;
2187  }
2188  get permanentElementMap() {
2189    return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
2190  }
2191  get renderMethod() {
2192    return "replace";
2193  }
2194};
2195var FrameRenderer = class extends Renderer {
2196  static renderElement(currentElement, newElement) {
2197    const destinationRange = document.createRange();
2198    destinationRange.selectNodeContents(currentElement);
2199    destinationRange.deleteContents();
2200    const frameElement = newElement;
2201    const sourceRange = frameElement.ownerDocument?.createRange();
2202    if (sourceRange) {
2203      sourceRange.selectNodeContents(frameElement);
2204      currentElement.appendChild(sourceRange.extractContents());
2205    }
2206  }
2207  constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
2208    super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
2209    this.delegate = delegate;
2210  }
2211  get shouldRender() {
2212    return true;
2213  }
2214  async render() {
2215    await nextRepaint();
2216    this.preservingPermanentElements(() => {
2217      this.loadFrameElement();
2218    });
2219    this.scrollFrameIntoView();
2220    await nextRepaint();
2221    this.focusFirstAutofocusableElement();
2222    await nextRepaint();
2223    this.activateScriptElements();
2224  }
2225  loadFrameElement() {
2226    this.delegate.willRenderFrame(this.currentElement, this.newElement);
2227    this.renderElement(this.currentElement, this.newElement);
2228  }
2229  scrollFrameIntoView() {
2230    if (this.currentElement.autoscroll || this.newElement.autoscroll) {
2231      const element = this.currentElement.firstElementChild;
2232      const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
2233      const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
2234      if (element) {
2235        element.scrollIntoView({ block, behavior });
2236        return true;
2237      }
2238    }
2239    return false;
2240  }
2241  activateScriptElements() {
2242    for (const inertScriptElement of this.newScriptElements) {
2243      const activatedScriptElement = activateScriptElement(inertScriptElement);
2244      inertScriptElement.replaceWith(activatedScriptElement);
2245    }
2246  }
2247  get newScriptElements() {
2248    return this.currentElement.querySelectorAll("script");
2249  }
2250};
2251function readScrollLogicalPosition(value, defaultValue) {
2252  if (value == "end" || value == "start" || value == "center" || value == "nearest") {
2253    return value;
2254  } else {
2255    return defaultValue;
2256  }
2257}
2258function readScrollBehavior(value, defaultValue) {
2259  if (value == "auto" || value == "smooth") {
2260    return value;
2261  } else {
2262    return defaultValue;
2263  }
2264}
2265var Idiomorph = function() {
2266  const noOp = () => {
2267  };
2268  const defaults = {
2269    morphStyle: "outerHTML",
2270    callbacks: {
2271      beforeNodeAdded: noOp,
2272      afterNodeAdded: noOp,
2273      beforeNodeMorphed: noOp,
2274      afterNodeMorphed: noOp,
2275      beforeNodeRemoved: noOp,
2276      afterNodeRemoved: noOp,
2277      beforeAttributeUpdated: noOp
2278    },
2279    head: {
2280      style: "merge",
2281      shouldPreserve: (elt) => elt.getAttribute("im-preserve") === "true",
2282      shouldReAppend: (elt) => elt.getAttribute("im-re-append") === "true",
2283      shouldRemove: noOp,
2284      afterHeadMorphed: noOp
2285    },
2286    restoreFocus: true
2287  };
2288  function morph(oldNode, newContent, config2 = {}) {
2289    oldNode = normalizeElement(oldNode);
2290    const newNode = normalizeParent(newContent);
2291    const ctx = createMorphContext(oldNode, newNode, config2);
2292    const morphedNodes = saveAndRestoreFocus(ctx, () => {
2293      return withHeadBlocking(
2294        ctx,
2295        oldNode,
2296        newNode,
2297        /** @param {MorphContext} ctx */
2298        (ctx2) => {
2299          if (ctx2.morphStyle === "innerHTML") {
2300            morphChildren2(ctx2, oldNode, newNode);
2301            return Array.from(oldNode.childNodes);
2302          } else {
2303            return morphOuterHTML(ctx2, oldNode, newNode);
2304          }
2305        }
2306      );
2307    });
2308    ctx.pantry.remove();
2309    return morphedNodes;
2310  }
2311  function morphOuterHTML(ctx, oldNode, newNode) {
2312    const oldParent = normalizeParent(oldNode);
2313    let childNodes = Array.from(oldParent.childNodes);
2314    const index = childNodes.indexOf(oldNode);
2315    const rightMargin = childNodes.length - (index + 1);
2316    morphChildren2(
2317      ctx,
2318      oldParent,
2319      newNode,
2320      // these two optional params are the secret sauce
2321      oldNode,
2322      // start point for iteration
2323      oldNode.nextSibling
2324      // end point for iteration
2325    );
2326    childNodes = Array.from(oldParent.childNodes);
2327    return childNodes.slice(index, childNodes.length - rightMargin);
2328  }
2329  function saveAndRestoreFocus(ctx, fn) {
2330    if (!ctx.config.restoreFocus) return fn();
2331    let activeElement = (
2332      /** @type {HTMLInputElement|HTMLTextAreaElement|null} */
2333      document.activeElement
2334    );
2335    if (!(activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement)) {
2336      return fn();
2337    }
2338    const { id: activeElementId, selectionStart, selectionEnd } = activeElement;
2339    const results = fn();
2340    if (activeElementId && activeElementId !== document.activeElement?.id) {
2341      activeElement = ctx.target.querySelector(`#${activeElementId}`);
2342      activeElement?.focus();
2343    }
2344    if (activeElement && !activeElement.selectionEnd && selectionEnd) {
2345      activeElement.setSelectionRange(selectionStart, selectionEnd);
2346    }
2347    return results;
2348  }
2349  const morphChildren2 = /* @__PURE__ */ function() {
2350    function morphChildren3(ctx, oldParent, newParent, insertionPoint = null, endPoint = null) {
2351      if (oldParent instanceof HTMLTemplateElement && newParent instanceof HTMLTemplateElement) {
2352        oldParent = oldParent.content;
2353        newParent = newParent.content;
2354      }
2355      insertionPoint ||= oldParent.firstChild;
2356      for (const newChild of newParent.childNodes) {
2357        if (insertionPoint && insertionPoint != endPoint) {
2358          const bestMatch = findBestMatch(
2359            ctx,
2360            newChild,
2361            insertionPoint,
2362            endPoint
2363          );
2364          if (bestMatch) {
2365            if (bestMatch !== insertionPoint) {
2366              removeNodesBetween(ctx, insertionPoint, bestMatch);
2367            }
2368            morphNode(bestMatch, newChild, ctx);
2369            insertionPoint = bestMatch.nextSibling;
2370            continue;
2371          }
2372        }
2373        if (newChild instanceof Element && ctx.persistentIds.has(newChild.id)) {
2374          const movedChild = moveBeforeById(
2375            oldParent,
2376            newChild.id,
2377            insertionPoint,
2378            ctx
2379          );
2380          morphNode(movedChild, newChild, ctx);
2381          insertionPoint = movedChild.nextSibling;
2382          continue;
2383        }
2384        const insertedNode = createNode(
2385          oldParent,
2386          newChild,
2387          insertionPoint,
2388          ctx
2389        );
2390        if (insertedNode) {
2391          insertionPoint = insertedNode.nextSibling;
2392        }
2393      }
2394      while (insertionPoint && insertionPoint != endPoint) {
2395        const tempNode = insertionPoint;
2396        insertionPoint = insertionPoint.nextSibling;
2397        removeNode(ctx, tempNode);
2398      }
2399    }
2400    function createNode(oldParent, newChild, insertionPoint, ctx) {
2401      if (ctx.callbacks.beforeNodeAdded(newChild) === false) return null;
2402      if (ctx.idMap.has(newChild)) {
2403        const newEmptyChild = document.createElement(
2404          /** @type {Element} */
2405          newChild.tagName
2406        );
2407        oldParent.insertBefore(newEmptyChild, insertionPoint);
2408        morphNode(newEmptyChild, newChild, ctx);
2409        ctx.callbacks.afterNodeAdded(newEmptyChild);
2410        return newEmptyChild;
2411      } else {
2412        const newClonedChild = document.importNode(newChild, true);
2413        oldParent.insertBefore(newClonedChild, insertionPoint);
2414        ctx.callbacks.afterNodeAdded(newClonedChild);
2415        return newClonedChild;
2416      }
2417    }
2418    const findBestMatch = /* @__PURE__ */ function() {
2419      function findBestMatch2(ctx, node, startPoint, endPoint) {
2420        let softMatch = null;
2421        let nextSibling = node.nextSibling;
2422        let siblingSoftMatchCount = 0;
2423        let cursor = startPoint;
2424        while (cursor && cursor != endPoint) {
2425          if (isSoftMatch(cursor, node)) {
2426            if (isIdSetMatch(ctx, cursor, node)) {
2427              return cursor;
2428            }
2429            if (softMatch === null) {
2430              if (!ctx.idMap.has(cursor)) {
2431                softMatch = cursor;
2432              }
2433            }
2434          }
2435          if (softMatch === null && nextSibling && isSoftMatch(cursor, nextSibling)) {
2436            siblingSoftMatchCount++;
2437            nextSibling = nextSibling.nextSibling;
2438            if (siblingSoftMatchCount >= 2) {
2439              softMatch = void 0;
2440            }
2441          }
2442          if (cursor.contains(document.activeElement)) break;
2443          cursor = cursor.nextSibling;
2444        }
2445        return softMatch || null;
2446      }
2447      function isIdSetMatch(ctx, oldNode, newNode) {
2448        let oldSet = ctx.idMap.get(oldNode);
2449        let newSet = ctx.idMap.get(newNode);
2450        if (!newSet || !oldSet) return false;
2451        for (const id of oldSet) {
2452          if (newSet.has(id)) {
2453            return true;
2454          }
2455        }
2456        return false;
2457      }
2458      function isSoftMatch(oldNode, newNode) {
2459        const oldElt = (
2460          /** @type {Element} */
2461          oldNode
2462        );
2463        const newElt = (
2464          /** @type {Element} */
2465          newNode
2466        );
2467        return oldElt.nodeType === newElt.nodeType && oldElt.tagName === newElt.tagName && // If oldElt has an `id` with possible state and it doesn't match newElt.id then avoid morphing.
2468        // We'll still match an anonymous node with an IDed newElt, though, because if it got this far,
2469        // its not persistent, and new nodes can't have any hidden state.
2470        (!oldElt.id || oldElt.id === newElt.id);
2471      }
2472      return findBestMatch2;
2473    }();
2474    function removeNode(ctx, node) {
2475      if (ctx.idMap.has(node)) {
2476        moveBefore(ctx.pantry, node, null);
2477      } else {
2478        if (ctx.callbacks.beforeNodeRemoved(node) === false) return;
2479        node.parentNode?.removeChild(node);
2480        ctx.callbacks.afterNodeRemoved(node);
2481      }
2482    }
2483    function removeNodesBetween(ctx, startInclusive, endExclusive) {
2484      let cursor = startInclusive;
2485      while (cursor && cursor !== endExclusive) {
2486        let tempNode = (
2487          /** @type {Node} */
2488          cursor
2489        );
2490        cursor = cursor.nextSibling;
2491        removeNode(ctx, tempNode);
2492      }
2493      return cursor;
2494    }
2495    function moveBeforeById(parentNode, id, after, ctx) {
2496      const target = (
2497        /** @type {Element} - will always be found */
2498        ctx.target.querySelector(`#${id}`) || ctx.pantry.querySelector(`#${id}`)
2499      );
2500      removeElementFromAncestorsIdMaps(target, ctx);
2501      moveBefore(parentNode, target, after);
2502      return target;
2503    }
2504    function removeElementFromAncestorsIdMaps(element, ctx) {
2505      const id = element.id;
2506      while (element = element.parentNode) {
2507        let idSet = ctx.idMap.get(element);
2508        if (idSet) {
2509          idSet.delete(id);
2510          if (!idSet.size) {
2511            ctx.idMap.delete(element);
2512          }
2513        }
2514      }
2515    }
2516    function moveBefore(parentNode, element, after) {
2517      if (parentNode.moveBefore) {
2518        try {
2519          parentNode.moveBefore(element, after);
2520        } catch (e) {
2521          parentNode.insertBefore(element, after);
2522        }
2523      } else {
2524        parentNode.insertBefore(element, after);
2525      }
2526    }
2527    return morphChildren3;
2528  }();
2529  const morphNode = /* @__PURE__ */ function() {
2530    function morphNode2(oldNode, newContent, ctx) {
2531      if (ctx.ignoreActive && oldNode === document.activeElement) {
2532        return null;
2533      }
2534      if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false) {
2535        return oldNode;
2536      }
2537      if (oldNode instanceof HTMLHeadElement && ctx.head.ignore) ;
2538      else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") {
2539        handleHeadElement(
2540          oldNode,
2541          /** @type {HTMLHeadElement} */
2542          newContent,
2543          ctx
2544        );
2545      } else {
2546        morphAttributes(oldNode, newContent, ctx);
2547        if (!ignoreValueOfActiveElement(oldNode, ctx)) {
2548          morphChildren2(ctx, oldNode, newContent);
2549        }
2550      }
2551      ctx.callbacks.afterNodeMorphed(oldNode, newContent);
2552      return oldNode;
2553    }
2554    function morphAttributes(oldNode, newNode, ctx) {
2555      let type = newNode.nodeType;
2556      if (type === 1) {
2557        const oldElt = (
2558          /** @type {Element} */
2559          oldNode
2560        );
2561        const newElt = (
2562          /** @type {Element} */
2563          newNode
2564        );
2565        const oldAttributes = oldElt.attributes;
2566        const newAttributes = newElt.attributes;
2567        for (const newAttribute of newAttributes) {
2568          if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) {
2569            continue;
2570          }
2571          if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) {
2572            oldElt.setAttribute(newAttribute.name, newAttribute.value);
2573          }
2574        }
2575        for (let i = oldAttributes.length - 1; 0 <= i; i--) {
2576          const oldAttribute = oldAttributes[i];
2577          if (!oldAttribute) continue;
2578          if (!newElt.hasAttribute(oldAttribute.name)) {
2579            if (ignoreAttribute(oldAttribute.name, oldElt, "remove", ctx)) {
2580              continue;
2581            }
2582            oldElt.removeAttribute(oldAttribute.name);
2583          }
2584        }
2585        if (!ignoreValueOfActiveElement(oldElt, ctx)) {
2586          syncInputValue(oldElt, newElt, ctx);
2587        }
2588      }
2589      if (type === 8 || type === 3) {
2590        if (oldNode.nodeValue !== newNode.nodeValue) {
2591          oldNode.nodeValue = newNode.nodeValue;
2592        }
2593      }
2594    }
2595    function syncInputValue(oldElement, newElement, ctx) {
2596      if (oldElement instanceof HTMLInputElement && newElement instanceof HTMLInputElement && newElement.type !== "file") {
2597        let newValue = newElement.value;
2598        let oldValue = oldElement.value;
2599        syncBooleanAttribute(oldElement, newElement, "checked", ctx);
2600        syncBooleanAttribute(oldElement, newElement, "disabled", ctx);
2601        if (!newElement.hasAttribute("value")) {
2602          if (!ignoreAttribute("value", oldElement, "remove", ctx)) {
2603            oldElement.value = "";
2604            oldElement.removeAttribute("value");
2605          }
2606        } else if (oldValue !== newValue) {
2607          if (!ignoreAttribute("value", oldElement, "update", ctx)) {
2608            oldElement.setAttribute("value", newValue);
2609            oldElement.value = newValue;
2610          }
2611        }
2612      } else if (oldElement instanceof HTMLOptionElement && newElement instanceof HTMLOptionElement) {
2613        syncBooleanAttribute(oldElement, newElement, "selected", ctx);
2614      } else if (oldElement instanceof HTMLTextAreaElement && newElement instanceof HTMLTextAreaElement) {
2615        let newValue = newElement.value;
2616        let oldValue = oldElement.value;
2617        if (ignoreAttribute("value", oldElement, "update", ctx)) {
2618          return;
2619        }
2620        if (newValue !== oldValue) {
2621          oldElement.value = newValue;
2622        }
2623        if (oldElement.firstChild && oldElement.firstChild.nodeValue !== newValue) {
2624          oldElement.firstChild.nodeValue = newValue;
2625        }
2626      }
2627    }
2628    function syncBooleanAttribute(oldElement, newElement, attributeName, ctx) {
2629      const newLiveValue = newElement[attributeName], oldLiveValue = oldElement[attributeName];
2630      if (newLiveValue !== oldLiveValue) {
2631        const ignoreUpdate = ignoreAttribute(
2632          attributeName,
2633          oldElement,
2634          "update",
2635          ctx
2636        );
2637        if (!ignoreUpdate) {
2638          oldElement[attributeName] = newElement[attributeName];
2639        }
2640        if (newLiveValue) {
2641          if (!ignoreUpdate) {
2642            oldElement.setAttribute(attributeName, "");
2643          }
2644        } else {
2645          if (!ignoreAttribute(attributeName, oldElement, "remove", ctx)) {
2646            oldElement.removeAttribute(attributeName);
2647          }
2648        }
2649      }
2650    }
2651    function ignoreAttribute(attr, element, updateType, ctx) {
2652      if (attr === "value" && ctx.ignoreActiveValue && element === document.activeElement) {
2653        return true;
2654      }
2655      return ctx.callbacks.beforeAttributeUpdated(attr, element, updateType) === false;
2656    }
2657    function ignoreValueOfActiveElement(possibleActiveElement, ctx) {
2658      return !!ctx.ignoreActiveValue && possibleActiveElement === document.activeElement && possibleActiveElement !== document.body;
2659    }
2660    return morphNode2;
2661  }();
2662  function withHeadBlocking(ctx, oldNode, newNode, callback) {
2663    if (ctx.head.block) {
2664      const oldHead = oldNode.querySelector("head");
2665      const newHead = newNode.querySelector("head");
2666      if (oldHead && newHead) {
2667        const promises = handleHeadElement(oldHead, newHead, ctx);
2668        return Promise.all(promises).then(() => {
2669          const newCtx = Object.assign(ctx, {
2670            head: {
2671              block: false,
2672              ignore: true
2673            }
2674          });
2675          return callback(newCtx);
2676        });
2677      }
2678    }
2679    return callback(ctx);
2680  }
2681  function handleHeadElement(oldHead, newHead, ctx) {
2682    let added = [];
2683    let removed = [];
2684    let preserved = [];
2685    let nodesToAppend = [];
2686    let srcToNewHeadNodes = /* @__PURE__ */ new Map();
2687    for (const newHeadChild of newHead.children) {
2688      srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild);
2689    }
2690    for (const currentHeadElt of oldHead.children) {
2691      let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML);
2692      let isReAppended = ctx.head.shouldReAppend(currentHeadElt);
2693      let isPreserved = ctx.head.shouldPreserve(currentHeadElt);
2694      if (inNewContent || isPreserved) {
2695        if (isReAppended) {
2696          removed.push(currentHeadElt);
2697        } else {
2698          srcToNewHeadNodes.delete(currentHeadElt.outerHTML);
2699          preserved.push(currentHeadElt);
2700        }
2701      } else {
2702        if (ctx.head.style === "append") {
2703          if (isReAppended) {
2704            removed.push(currentHeadElt);
2705            nodesToAppend.push(currentHeadElt);
2706          }
2707        } else {
2708          if (ctx.head.shouldRemove(currentHeadElt) !== false) {
2709            removed.push(currentHeadElt);
2710          }
2711        }
2712      }
2713    }
2714    nodesToAppend.push(...srcToNewHeadNodes.values());
2715    let promises = [];
2716    for (const newNode of nodesToAppend) {
2717      let newElt = (
2718        /** @type {ChildNode} */
2719        document.createRange().createContextualFragment(newNode.outerHTML).firstChild
2720      );
2721      if (ctx.callbacks.beforeNodeAdded(newElt) !== false) {
2722        if ("href" in newElt && newElt.href || "src" in newElt && newElt.src) {
2723          let resolve;
2724          let promise = new Promise(function(_resolve) {
2725            resolve = _resolve;
2726          });
2727          newElt.addEventListener("load", function() {
2728            resolve();
2729          });
2730          promises.push(promise);
2731        }
2732        oldHead.appendChild(newElt);
2733        ctx.callbacks.afterNodeAdded(newElt);
2734        added.push(newElt);
2735      }
2736    }
2737    for (const removedElement of removed) {
2738      if (ctx.callbacks.beforeNodeRemoved(removedElement) !== false) {
2739        oldHead.removeChild(removedElement);
2740        ctx.callbacks.afterNodeRemoved(removedElement);
2741      }
2742    }
2743    ctx.head.afterHeadMorphed(oldHead, {
2744      added,
2745      kept: preserved,
2746      removed
2747    });
2748    return promises;
2749  }
2750  const createMorphContext = /* @__PURE__ */ function() {
2751    function createMorphContext2(oldNode, newContent, config2) {
2752      const { persistentIds, idMap } = createIdMaps(oldNode, newContent);
2753      const mergedConfig = mergeDefaults(config2);
2754      const morphStyle = mergedConfig.morphStyle || "outerHTML";
2755      if (!["innerHTML", "outerHTML"].includes(morphStyle)) {
2756        throw `Do not understand how to morph style ${morphStyle}`;
2757      }
2758      return {
2759        target: oldNode,
2760        newContent,
2761        config: mergedConfig,
2762        morphStyle,
2763        ignoreActive: mergedConfig.ignoreActive,
2764        ignoreActiveValue: mergedConfig.ignoreActiveValue,
2765        restoreFocus: mergedConfig.restoreFocus,
2766        idMap,
2767        persistentIds,
2768        pantry: createPantry(),
2769        callbacks: mergedConfig.callbacks,
2770        head: mergedConfig.head
2771      };
2772    }
2773    function mergeDefaults(config2) {
2774      let finalConfig = Object.assign({}, defaults);
2775      Object.assign(finalConfig, config2);
2776      finalConfig.callbacks = Object.assign(
2777        {},
2778        defaults.callbacks,
2779        config2.callbacks
2780      );
2781      finalConfig.head = Object.assign({}, defaults.head, config2.head);
2782      return finalConfig;
2783    }
2784    function createPantry() {
2785      const pantry = document.createElement("div");
2786      pantry.hidden = true;
2787      document.body.insertAdjacentElement("afterend", pantry);
2788      return pantry;
2789    }
2790    function findIdElements(root) {
2791      let elements = Array.from(root.querySelectorAll("[id]"));
2792      if (root.id) {
2793        elements.push(root);
2794      }
2795      return elements;
2796    }
2797    function populateIdMapWithTree(idMap, persistentIds, root, elements) {
2798      for (const elt of elements) {
2799        if (persistentIds.has(elt.id)) {
2800          let current = elt;
2801          while (current) {
2802            let idSet = idMap.get(current);
2803            if (idSet == null) {
2804              idSet = /* @__PURE__ */ new Set();
2805              idMap.set(current, idSet);
2806            }
2807            idSet.add(elt.id);
2808            if (current === root) break;
2809            current = current.parentElement;
2810          }
2811        }
2812      }
2813    }
2814    function createIdMaps(oldContent, newContent) {
2815      const oldIdElements = findIdElements(oldContent);
2816      const newIdElements = findIdElements(newContent);
2817      const persistentIds = createPersistentIds(oldIdElements, newIdElements);
2818      let idMap = /* @__PURE__ */ new Map();
2819      populateIdMapWithTree(idMap, persistentIds, oldContent, oldIdElements);
2820      const newRoot = newContent.__idiomorphRoot || newContent;
2821      populateIdMapWithTree(idMap, persistentIds, newRoot, newIdElements);
2822      return { persistentIds, idMap };
2823    }
2824    function createPersistentIds(oldIdElements, newIdElements) {
2825      let duplicateIds = /* @__PURE__ */ new Set();
2826      let oldIdTagNameMap = /* @__PURE__ */ new Map();
2827      for (const { id, tagName } of oldIdElements) {
2828        if (oldIdTagNameMap.has(id)) {
2829          duplicateIds.add(id);
2830        } else {
2831          oldIdTagNameMap.set(id, tagName);
2832        }
2833      }
2834      let persistentIds = /* @__PURE__ */ new Set();
2835      for (const { id, tagName } of newIdElements) {
2836        if (persistentIds.has(id)) {
2837          duplicateIds.add(id);
2838        } else if (oldIdTagNameMap.get(id) === tagName) {
2839          persistentIds.add(id);
2840        }
2841      }
2842      for (const id of duplicateIds) {
2843        persistentIds.delete(id);
2844      }
2845      return persistentIds;
2846    }
2847    return createMorphContext2;
2848  }();
2849  const { normalizeElement, normalizeParent } = /* @__PURE__ */ function() {
2850    const generatedByIdiomorph = /* @__PURE__ */ new WeakSet();
2851    function normalizeElement2(content) {
2852      if (content instanceof Document) {
2853        return content.documentElement;
2854      } else {
2855        return content;
2856      }
2857    }
2858    function normalizeParent2(newContent) {
2859      if (newContent == null) {
2860        return document.createElement("div");
2861      } else if (typeof newContent === "string") {
2862        return normalizeParent2(parseContent(newContent));
2863      } else if (generatedByIdiomorph.has(
2864        /** @type {Element} */
2865        newContent
2866      )) {
2867        return (
2868          /** @type {Element} */
2869          newContent
2870        );
2871      } else if (newContent instanceof Node) {
2872        if (newContent.parentNode) {
2873          return createDuckTypedParent(newContent);
2874        } else {
2875          const dummyParent = document.createElement("div");
2876          dummyParent.append(newContent);
2877          return dummyParent;
2878        }
2879      } else {
2880        const dummyParent = document.createElement("div");
2881        for (const elt of [...newContent]) {
2882          dummyParent.append(elt);
2883        }
2884        return dummyParent;
2885      }
2886    }
2887    function createDuckTypedParent(newContent) {
2888      return (
2889        /** @type {Element} */
2890        /** @type {unknown} */
2891        {
2892          childNodes: [newContent],
2893          /** @ts-ignore - cover your eyes for a minute, tsc */
2894          querySelectorAll: (s) => {
2895            const elements = newContent.querySelectorAll(s);
2896            return newContent.matches(s) ? [newContent, ...elements] : elements;
2897          },
2898          /** @ts-ignore */
2899          insertBefore: (n, r) => newContent.parentNode.insertBefore(n, r),
2900          /** @ts-ignore */
2901          moveBefore: (n, r) => newContent.parentNode.moveBefore(n, r),
2902          // for later use with populateIdMapWithTree to halt upwards iteration
2903          get __idiomorphRoot() {
2904            return newContent;
2905          }
2906        }
2907      );
2908    }
2909    function parseContent(newContent) {
2910      let parser = new DOMParser();
2911      let contentWithSvgsRemoved = newContent.replace(
2912        /<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim,
2913        ""
2914      );
2915      if (contentWithSvgsRemoved.match(/<\/html>/) || contentWithSvgsRemoved.match(/<\/head>/) || contentWithSvgsRemoved.match(/<\/body>/)) {
2916        let content = parser.parseFromString(newContent, "text/html");
2917        if (contentWithSvgsRemoved.match(/<\/html>/)) {
2918          generatedByIdiomorph.add(content);
2919          return content;
2920        } else {
2921          let htmlElement = content.firstChild;
2922          if (htmlElement) {
2923            generatedByIdiomorph.add(htmlElement);
2924          }
2925          return htmlElement;
2926        }
2927      } else {
2928        let responseDoc = parser.parseFromString(
2929          "<body><template>" + newContent + "</template></body>",
2930          "text/html"
2931        );
2932        let content = (
2933          /** @type {HTMLTemplateElement} */
2934          responseDoc.body.querySelector("template").content
2935        );
2936        generatedByIdiomorph.add(content);
2937        return content;
2938      }
2939    }
2940    return { normalizeElement: normalizeElement2, normalizeParent: normalizeParent2 };
2941  }();
2942  return {
2943    morph,
2944    defaults
2945  };
2946}();
2947function morphElements(currentElement, newElement, { callbacks, ...options } = {}) {
2948  Idiomorph.morph(currentElement, newElement, {
2949    ...options,
2950    callbacks: new DefaultIdiomorphCallbacks(callbacks)
2951  });
2952}
2953function morphChildren(currentElement, newElement) {
2954  morphElements(currentElement, newElement.childNodes, {
2955    morphStyle: "innerHTML"
2956  });
2957}
2958var DefaultIdiomorphCallbacks = class {
2959  #beforeNodeMorphed;
2960  constructor({ beforeNodeMorphed } = {}) {
2961    this.#beforeNodeMorphed = beforeNodeMorphed || (() => true);
2962  }
2963  beforeNodeAdded = (node) => {
2964    return !(node.id && node.hasAttribute("data-turbo-permanent") && document.getElementById(node.id));
2965  };
2966  beforeNodeMorphed = (currentElement, newElement) => {
2967    if (currentElement instanceof Element) {
2968      if (!currentElement.hasAttribute("data-turbo-permanent") && this.#beforeNodeMorphed(currentElement, newElement)) {
2969        const event = dispatch("turbo:before-morph-element", {
2970          cancelable: true,
2971          target: currentElement,
2972          detail: { currentElement, newElement }
2973        });
2974        return !event.defaultPrevented;
2975      } else {
2976        return false;
2977      }
2978    }
2979  };
2980  beforeAttributeUpdated = (attributeName, target, mutationType) => {
2981    const event = dispatch("turbo:before-morph-attribute", {
2982      cancelable: true,
2983      target,
2984      detail: { attributeName, mutationType }
2985    });
2986    return !event.defaultPrevented;
2987  };
2988  beforeNodeRemoved = (node) => {
2989    return this.beforeNodeMorphed(node);
2990  };
2991  afterNodeMorphed = (currentElement, newElement) => {
2992    if (currentElement instanceof Element) {
2993      dispatch("turbo:morph-element", {
2994        target: currentElement,
2995        detail: { currentElement, newElement }
2996      });
2997    }
2998  };
2999};
3000var MorphingFrameRenderer = class extends FrameRenderer {
3001  static renderElement(currentElement, newElement) {
3002    dispatch("turbo:before-frame-morph", {
3003      target: currentElement,
3004      detail: { currentElement, newElement }
3005    });
3006    morphChildren(currentElement, newElement);
3007  }
3008  async preservingPermanentElements(callback) {
3009    return await callback();
3010  }
3011};
3012var ProgressBar = class _ProgressBar {
3013  static animationDuration = 300;
3014  /*ms*/
3015  static get defaultCSS() {
3016    return unindent`
3017      .turbo-progress-bar {
3018        position: fixed;
3019        display: block;
3020        top: 0;
3021        left: 0;
3022        height: 3px;
3023        background: #0076ff;
3024        z-index: 2147483647;
3025        transition:
3026          width ${_ProgressBar.animationDuration}ms ease-out,
3027          opacity ${_ProgressBar.animationDuration / 2}ms ${_ProgressBar.animationDuration / 2}ms ease-in;
3028        transform: translate3d(0, 0, 0);
3029      }
3030    `;
3031  }
3032  hiding = false;
3033  value = 0;
3034  visible = false;
3035  constructor() {
3036    this.stylesheetElement = this.createStylesheetElement();
3037    this.progressElement = this.createProgressElement();
3038    this.installStylesheetElement();
3039    this.setValue(0);
3040  }
3041  show() {
3042    if (!this.visible) {
3043      this.visible = true;
3044      this.installProgressElement();
3045      this.startTrickling();
3046    }
3047  }
3048  hide() {
3049    if (this.visible && !this.hiding) {
3050      this.hiding = true;
3051      this.fadeProgressElement(() => {
3052        this.uninstallProgressElement();
3053        this.stopTrickling();
3054        this.visible = false;
3055        this.hiding = false;
3056      });
3057    }
3058  }
3059  setValue(value) {
3060    this.value = value;
3061    this.refresh();
3062  }
3063  // Private
3064  installStylesheetElement() {
3065    document.head.insertBefore(this.stylesheetElement, document.head.firstChild);
3066  }
3067  installProgressElement() {
3068    this.progressElement.style.width = "0";
3069    this.progressElement.style.opacity = "1";
3070    document.documentElement.insertBefore(this.progressElement, document.body);
3071    this.refresh();
3072  }
3073  fadeProgressElement(callback) {
3074    this.progressElement.style.opacity = "0";
3075    setTimeout(callback, _ProgressBar.animationDuration * 1.5);
3076  }
3077  uninstallProgressElement() {
3078    if (this.progressElement.parentNode) {
3079      document.documentElement.removeChild(this.progressElement);
3080    }
3081  }
3082  startTrickling() {
3083    if (!this.trickleInterval) {
3084      this.trickleInterval = window.setInterval(this.trickle, _ProgressBar.animationDuration);
3085    }
3086  }
3087  stopTrickling() {
3088    window.clearInterval(this.trickleInterval);
3089    delete this.trickleInterval;
3090  }
3091  trickle = () => {
3092    this.setValue(this.value + Math.random() / 100);
3093  };
3094  refresh() {
3095    requestAnimationFrame(() => {
3096      this.progressElement.style.width = `${10 + this.value * 90}%`;
3097    });
3098  }
3099  createStylesheetElement() {
3100    const element = document.createElement("style");
3101    element.type = "text/css";
3102    element.textContent = _ProgressBar.defaultCSS;
3103    const cspNonce = getCspNonce();
3104    if (cspNonce) {
3105      element.nonce = cspNonce;
3106    }
3107    return element;
3108  }
3109  createProgressElement() {
3110    const element = document.createElement("div");
3111    element.className = "turbo-progress-bar";
3112    return element;
3113  }
3114};
3115var HeadSnapshot = class extends Snapshot {
3116  detailsByOuterHTML = this.children.filter((element) => !elementIsNoscript(element)).map((element) => elementWithoutNonce(element)).reduce((result, element) => {
3117    const { outerHTML } = element;
3118    const details = outerHTML in result ? result[outerHTML] : {
3119      type: elementType(element),
3120      tracked: elementIsTracked(element),
3121      elements: []
3122    };
3123    return {
3124      ...result,
3125      [outerHTML]: {
3126        ...details,
3127        elements: [...details.elements, element]
3128      }
3129    };
3130  }, {});
3131  get trackedElementSignature() {
3132    return Object.keys(this.detailsByOuterHTML).filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked).join("");
3133  }
3134  getScriptElementsNotInSnapshot(snapshot) {
3135    return this.getElementsMatchingTypeNotInSnapshot("script", snapshot);
3136  }
3137  getStylesheetElementsNotInSnapshot(snapshot) {
3138    return this.getElementsMatchingTypeNotInSnapshot("stylesheet", snapshot);
3139  }
3140  getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
3141    return Object.keys(this.detailsByOuterHTML).filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML)).map((outerHTML) => this.detailsByOuterHTML[outerHTML]).filter(({ type }) => type == matchedType).map(({ elements: [element] }) => element);
3142  }
3143  get provisionalElements() {
3144    return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
3145      const { type, tracked, elements } = this.detailsByOuterHTML[outerHTML];
3146      if (type == null && !tracked) {
3147        return [...result, ...elements];
3148      } else if (elements.length > 1) {
3149        return [...result, ...elements.slice(1)];
3150      } else {
3151        return result;
3152      }
3153    }, []);
3154  }
3155  getMetaValue(name) {
3156    const element = this.findMetaElementByName(name);
3157    return element ? element.getAttribute("content") : null;
3158  }
3159  findMetaElementByName(name) {
3160    return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
3161      const {
3162        elements: [element]
3163      } = this.detailsByOuterHTML[outerHTML];
3164      return elementIsMetaElementWithName(element, name) ? element : result;
3165    }, void 0 | void 0);
3166  }
3167};
3168function elementType(element) {
3169  if (elementIsScript(element)) {
3170    return "script";
3171  } else if (elementIsStylesheet(element)) {
3172    return "stylesheet";
3173  }
3174}
3175function elementIsTracked(element) {
3176  return element.getAttribute("data-turbo-track") == "reload";
3177}
3178function elementIsScript(element) {
3179  const tagName = element.localName;
3180  return tagName == "script";
3181}
3182function elementIsNoscript(element) {
3183  const tagName = element.localName;
3184  return tagName == "noscript";
3185}
3186function elementIsStylesheet(element) {
3187  const tagName = element.localName;
3188  return tagName == "style" || tagName == "link" && element.getAttribute("rel") == "stylesheet";
3189}
3190function elementIsMetaElementWithName(element, name) {
3191  const tagName = element.localName;
3192  return tagName == "meta" && element.getAttribute("name") == name;
3193}
3194function elementWithoutNonce(element) {
3195  if (element.hasAttribute("nonce")) {
3196    element.setAttribute("nonce", "");
3197  }
3198  return element;
3199}
3200var PageSnapshot = class _PageSnapshot extends Snapshot {
3201  static fromHTMLString(html = "") {
3202    return this.fromDocument(parseHTMLDocument(html));
3203  }
3204  static fromElement(element) {
3205    return this.fromDocument(element.ownerDocument);
3206  }
3207  static fromDocument({ documentElement, body, head }) {
3208    return new this(documentElement, body, new HeadSnapshot(head));
3209  }
3210  constructor(documentElement, body, headSnapshot) {
3211    super(body);
3212    this.documentElement = documentElement;
3213    this.headSnapshot = headSnapshot;
3214  }
3215  clone() {
3216    const clonedElement = this.element.cloneNode(true);
3217    const selectElements = this.element.querySelectorAll("select");
3218    const clonedSelectElements = clonedElement.querySelectorAll("select");
3219    for (const [index, source] of selectElements.entries()) {
3220      const clone = clonedSelectElements[index];
3221      for (const option of clone.selectedOptions) option.selected = false;
3222      for (const option of source.selectedOptions) clone.options[option.index].selected = true;
3223    }
3224    for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
3225      clonedPasswordInput.value = "";
3226    }
3227    return new _PageSnapshot(this.documentElement, clonedElement, this.headSnapshot);
3228  }
3229  get lang() {
3230    return this.documentElement.getAttribute("lang");
3231  }
3232  get headElement() {
3233    return this.headSnapshot.element;
3234  }
3235  get rootLocation() {
3236    const root = this.getSetting("root") ?? "/";
3237    return expandURL(root);
3238  }
3239  get cacheControlValue() {
3240    return this.getSetting("cache-control");
3241  }
3242  get isPreviewable() {
3243    return this.cacheControlValue != "no-preview";
3244  }
3245  get isCacheable() {
3246    return this.cacheControlValue != "no-cache";
3247  }
3248  get isVisitable() {
3249    return this.getSetting("visit-control") != "reload";
3250  }
3251  get prefersViewTransitions() {
3252    return this.headSnapshot.getMetaValue("view-transition") === "same-origin";
3253  }
3254  get shouldMorphPage() {
3255    return this.getSetting("refresh-method") === "morph";
3256  }
3257  get shouldPreserveScrollPosition() {
3258    return this.getSetting("refresh-scroll") === "preserve";
3259  }
3260  // Private
3261  getSetting(name) {
3262    return this.headSnapshot.getMetaValue(`turbo-${name}`);
3263  }
3264};
3265var ViewTransitioner = class {
3266  #viewTransitionStarted = false;
3267  #lastOperation = Promise.resolve();
3268  renderChange(useViewTransition, render) {
3269    if (useViewTransition && this.viewTransitionsAvailable && !this.#viewTransitionStarted) {
3270      this.#viewTransitionStarted = true;
3271      this.#lastOperation = this.#lastOperation.then(async () => {
3272        await document.startViewTransition(render).finished;
3273      });
3274    } else {
3275      this.#lastOperation = this.#lastOperation.then(render);
3276    }
3277    return this.#lastOperation;
3278  }
3279  get viewTransitionsAvailable() {
3280    return document.startViewTransition;
3281  }
3282};
3283var defaultOptions = {
3284  action: "advance",
3285  historyChanged: false,
3286  visitCachedSnapshot: () => {
3287  },
3288  willRender: true,
3289  updateHistory: true,
3290  shouldCacheSnapshot: true,
3291  acceptsStreamResponse: false
3292};
3293var TimingMetric = {
3294  visitStart: "visitStart",
3295  requestStart: "requestStart",
3296  requestEnd: "requestEnd",
3297  visitEnd: "visitEnd"
3298};
3299var VisitState = {
3300  initialized: "initialized",
3301  started: "started",
3302  canceled: "canceled",
3303  failed: "failed",
3304  completed: "completed"
3305};
3306var SystemStatusCode = {
3307  networkFailure: 0,
3308  timeoutFailure: -1,
3309  contentTypeMismatch: -2
3310};
3311var Direction = {
3312  advance: "forward",
3313  restore: "back",
3314  replace: "none"
3315};
3316var Visit = class {
3317  identifier = uuid();
3318  // Required by turbo-ios
3319  timingMetrics = {};
3320  followedRedirect = false;
3321  historyChanged = false;
3322  scrolled = false;
3323  shouldCacheSnapshot = true;
3324  acceptsStreamResponse = false;
3325  snapshotCached = false;
3326  state = VisitState.initialized;
3327  viewTransitioner = new ViewTransitioner();
3328  constructor(delegate, location2, restorationIdentifier, options = {}) {
3329    this.delegate = delegate;
3330    this.location = location2;
3331    this.restorationIdentifier = restorationIdentifier || uuid();
3332    const {
3333      action,
3334      historyChanged,
3335      referrer,
3336      snapshot,
3337      snapshotHTML,
3338      response,
3339      visitCachedSnapshot,
3340      willRender,
3341      updateHistory,
3342      shouldCacheSnapshot,
3343      acceptsStreamResponse,
3344      direction
3345    } = {
3346      ...defaultOptions,
3347      ...options
3348    };
3349    this.action = action;
3350    this.historyChanged = historyChanged;
3351    this.referrer = referrer;
3352    this.snapshot = snapshot;
3353    this.snapshotHTML = snapshotHTML;
3354    this.response = response;
3355    this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
3356    this.isPageRefresh = this.view.isPageRefresh(this);
3357    this.visitCachedSnapshot = visitCachedSnapshot;
3358    this.willRender = willRender;
3359    this.updateHistory = updateHistory;
3360    this.scrolled = !willRender;
3361    this.shouldCacheSnapshot = shouldCacheSnapshot;
3362    this.acceptsStreamResponse = acceptsStreamResponse;
3363    this.direction = direction || Direction[action];
3364  }
3365  get adapter() {
3366    return this.delegate.adapter;
3367  }
3368  get view() {
3369    return this.delegate.view;
3370  }
3371  get history() {
3372    return this.delegate.history;
3373  }
3374  get restorationData() {
3375    return this.history.getRestorationDataForIdentifier(this.restorationIdentifier);
3376  }
3377  get silent() {
3378    return this.isSamePage;
3379  }
3380  start() {
3381    if (this.state == VisitState.initialized) {
3382      this.recordTimingMetric(TimingMetric.visitStart);
3383      this.state = VisitState.started;
3384      this.adapter.visitStarted(this);
3385      this.delegate.visitStarted(this);
3386    }
3387  }
3388  cancel() {
3389    if (this.state == VisitState.started) {
3390      if (this.request) {
3391        this.request.cancel();
3392      }
3393      this.cancelRender();
3394      this.state = VisitState.canceled;
3395    }
3396  }
3397  complete() {
3398    if (this.state == VisitState.started) {
3399      this.recordTimingMetric(TimingMetric.visitEnd);
3400      this.adapter.visitCompleted(this);
3401      this.state = VisitState.completed;
3402      this.followRedirect();
3403      if (!this.followedRedirect) {
3404        this.delegate.visitCompleted(this);
3405      }
3406    }
3407  }
3408  fail() {
3409    if (this.state == VisitState.started) {
3410      this.state = VisitState.failed;
3411      this.adapter.visitFailed(this);
3412      this.delegate.visitCompleted(this);
3413    }
3414  }
3415  changeHistory() {
3416    if (!this.historyChanged && this.updateHistory) {
3417      const actionForHistory = this.location.href === this.referrer?.href ? "replace" : this.action;
3418      const method = getHistoryMethodForAction(actionForHistory);
3419      this.history.update(method, this.location, this.restorationIdentifier);
3420      this.historyChanged = true;
3421    }
3422  }
3423  issueRequest() {
3424    if (this.hasPreloadedResponse()) {
3425      this.simulateRequest();
3426    } else if (this.shouldIssueRequest() && !this.request) {
3427      this.request = new FetchRequest(this, FetchMethod.get, this.location);
3428      this.request.perform();
3429    }
3430  }
3431  simulateRequest() {
3432    if (this.response) {
3433      this.startRequest();
3434      this.recordResponse();
3435      this.finishRequest();
3436    }
3437  }
3438  startRequest() {
3439    this.recordTimingMetric(TimingMetric.requestStart);
3440    this.adapter.visitRequestStarted(this);
3441  }
3442  recordResponse(response = this.response) {
3443    this.response = response;
3444    if (response) {
3445      const { statusCode } = response;
3446      if (isSuccessful(statusCode)) {
3447        this.adapter.visitRequestCompleted(this);
3448      } else {
3449        this.adapter.visitRequestFailedWithStatusCode(this, statusCode);
3450      }
3451    }
3452  }
3453  finishRequest() {
3454    this.recordTimingMetric(TimingMetric.requestEnd);
3455    this.adapter.visitRequestFinished(this);
3456  }
3457  loadResponse() {
3458    if (this.response) {
3459      const { statusCode, responseHTML } = this.response;
3460      this.render(async () => {
3461        if (this.shouldCacheSnapshot) this.cacheSnapshot();
3462        if (this.view.renderPromise) await this.view.renderPromise;
3463        if (isSuccessful(statusCode) && responseHTML != null) {
3464          const snapshot = PageSnapshot.fromHTMLString(responseHTML);
3465          await this.renderPageSnapshot(snapshot, false);
3466          this.adapter.visitRendered(this);
3467          this.complete();
3468        } else {
3469          await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
3470          this.adapter.visitRendered(this);
3471          this.fail();
3472        }
3473      });
3474    }
3475  }
3476  getCachedSnapshot() {
3477    const snapshot = this.view.getCachedSnapshotForLocation(this.location) || this.getPreloadedSnapshot();
3478    if (snapshot && (!getAnchor(this.location) || snapshot.hasAnchor(getAnchor(this.location)))) {
3479      if (this.action == "restore" || snapshot.isPreviewable) {
3480        return snapshot;
3481      }
3482    }
3483  }
3484  getPreloadedSnapshot() {
3485    if (this.snapshotHTML) {
3486      return PageSnapshot.fromHTMLString(this.snapshotHTML);
3487    }
3488  }
3489  hasCachedSnapshot() {
3490    return this.getCachedSnapshot() != null;
3491  }
3492  loadCachedSnapshot() {
3493    const snapshot = this.getCachedSnapshot();
3494    if (snapshot) {
3495      const isPreview = this.shouldIssueRequest();
3496      this.render(async () => {
3497        this.cacheSnapshot();
3498        if (this.isSamePage || this.isPageRefresh) {
3499          this.adapter.visitRendered(this);
3500        } else {
3501          if (this.view.renderPromise) await this.view.renderPromise;
3502          await this.renderPageSnapshot(snapshot, isPreview);
3503          this.adapter.visitRendered(this);
3504          if (!isPreview) {
3505            this.complete();
3506          }
3507        }
3508      });
3509    }
3510  }
3511  followRedirect() {
3512    if (this.redirectedToLocation && !this.followedRedirect && this.response?.redirected) {
3513      this.adapter.visitProposedToLocation(this.redirectedToLocation, {
3514        action: "replace",
3515        response: this.response,
3516        shouldCacheSnapshot: false,
3517        willRender: false
3518      });
3519      this.followedRedirect = true;
3520    }
3521  }
3522  goToSamePageAnchor() {
3523    if (this.isSamePage) {
3524      this.render(async () => {
3525        this.cacheSnapshot();
3526        this.performScroll();
3527        this.changeHistory();
3528        this.adapter.visitRendered(this);
3529      });
3530    }
3531  }
3532  // Fetch request delegate
3533  prepareRequest(request) {
3534    if (this.acceptsStreamResponse) {
3535      request.acceptResponseType(StreamMessage.contentType);
3536    }
3537  }
3538  requestStarted() {
3539    this.startRequest();
3540  }
3541  requestPreventedHandlingResponse(_request, _response) {
3542  }
3543  async requestSucceededWithResponse(request, response) {
3544    const responseHTML = await response.responseHTML;
3545    const { redirected, statusCode } = response;
3546    if (responseHTML == void 0) {
3547      this.recordResponse({
3548        statusCode: SystemStatusCode.contentTypeMismatch,
3549        redirected
3550      });
3551    } else {
3552      this.redirectedToLocation = response.redirected ? response.location : void 0;
3553      this.recordResponse({ statusCode, responseHTML, redirected });
3554    }
3555  }
3556  async requestFailedWithResponse(request, response) {
3557    const responseHTML = await response.responseHTML;
3558    const { redirected, statusCode } = response;
3559    if (responseHTML == void 0) {
3560      this.recordResponse({
3561        statusCode: SystemStatusCode.contentTypeMismatch,
3562        redirected
3563      });
3564    } else {
3565      this.recordResponse({ statusCode, responseHTML, redirected });
3566    }
3567  }
3568  requestErrored(_request, _error) {
3569    this.recordResponse({
3570      statusCode: SystemStatusCode.networkFailure,
3571      redirected: false
3572    });
3573  }
3574  requestFinished() {
3575    this.finishRequest();
3576  }
3577  // Scrolling
3578  performScroll() {
3579    if (!this.scrolled && !this.view.forceReloaded && !this.view.shouldPreserveScrollPosition(this)) {
3580      if (this.action == "restore") {
3581        this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
3582      } else {
3583        this.scrollToAnchor() || this.view.scrollToTop();
3584      }
3585      if (this.isSamePage) {
3586        this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation, this.location);
3587      }
3588      this.scrolled = true;
3589    }
3590  }
3591  scrollToRestoredPosition() {
3592    const { scrollPosition } = this.restorationData;
3593    if (scrollPosition) {
3594      this.view.scrollToPosition(scrollPosition);
3595      return true;
3596    }
3597  }
3598  scrollToAnchor() {
3599    const anchor = getAnchor(this.location);
3600    if (anchor != null) {
3601      this.view.scrollToAnchor(anchor);
3602      return true;
3603    }
3604  }
3605  // Instrumentation
3606  recordTimingMetric(metric) {
3607    this.timingMetrics[metric] = (/* @__PURE__ */ new Date()).getTime();
3608  }
3609  getTimingMetrics() {
3610    return { ...this.timingMetrics };
3611  }
3612  // Private
3613  hasPreloadedResponse() {
3614    return typeof this.response == "object";
3615  }
3616  shouldIssueRequest() {
3617    if (this.isSamePage) {
3618      return false;
3619    } else if (this.action == "restore") {
3620      return !this.hasCachedSnapshot();
3621    } else {
3622      return this.willRender;
3623    }
3624  }
3625  cacheSnapshot() {
3626    if (!this.snapshotCached) {
3627      this.view.cacheSnapshot(this.snapshot).then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
3628      this.snapshotCached = true;
3629    }
3630  }
3631  async render(callback) {
3632    this.cancelRender();
3633    await new Promise((resolve) => {
3634      this.frame = document.visibilityState === "hidden" ? setTimeout(() => resolve(), 0) : requestAnimationFrame(() => resolve());
3635    });
3636    await callback();
3637    delete this.frame;
3638  }
3639  async renderPageSnapshot(snapshot, isPreview) {
3640    await this.viewTransitioner.renderChange(this.view.shouldTransitionTo(snapshot), async () => {
3641      await this.view.renderPage(snapshot, isPreview, this.willRender, this);
3642      this.performScroll();
3643    });
3644  }
3645  cancelRender() {
3646    if (this.frame) {
3647      cancelAnimationFrame(this.frame);
3648      delete this.frame;
3649    }
3650  }
3651};
3652function isSuccessful(statusCode) {
3653  return statusCode >= 200 && statusCode < 300;
3654}
3655var BrowserAdapter = class {
3656  progressBar = new ProgressBar();
3657  constructor(session2) {
3658    this.session = session2;
3659  }
3660  visitProposedToLocation(location2, options) {
3661    if (locationIsVisitable(location2, this.navigator.rootLocation)) {
3662      this.navigator.startVisit(location2, options?.restorationIdentifier || uuid(), options);
3663    } else {
3664      window.location.href = location2.toString();
3665    }
3666  }
3667  visitStarted(visit2) {
3668    this.location = visit2.location;
3669    visit2.loadCachedSnapshot();
3670    visit2.issueRequest();
3671    visit2.goToSamePageAnchor();
3672  }
3673  visitRequestStarted(visit2) {
3674    this.progressBar.setValue(0);
3675    if (visit2.hasCachedSnapshot() || visit2.action != "restore") {
3676      this.showVisitProgressBarAfterDelay();
3677    } else {
3678      this.showProgressBar();
3679    }
3680  }
3681  visitRequestCompleted(visit2) {
3682    visit2.loadResponse();
3683  }
3684  visitRequestFailedWithStatusCode(visit2, statusCode) {
3685    switch (statusCode) {
3686      case SystemStatusCode.networkFailure:
3687      case SystemStatusCode.timeoutFailure:
3688      case SystemStatusCode.contentTypeMismatch:
3689        return this.reload({
3690          reason: "request_failed",
3691          context: {
3692            statusCode
3693          }
3694        });
3695      default:
3696        return visit2.loadResponse();
3697    }
3698  }
3699  visitRequestFinished(_visit) {
3700  }
3701  visitCompleted(_visit) {
3702    this.progressBar.setValue(1);
3703    this.hideVisitProgressBar();
3704  }
3705  pageInvalidated(reason) {
3706    this.reload(reason);
3707  }
3708  visitFailed(_visit) {
3709    this.progressBar.setValue(1);
3710    this.hideVisitProgressBar();
3711  }
3712  visitRendered(_visit) {
3713  }
3714  // Link prefetching
3715  linkPrefetchingIsEnabledForLocation(location2) {
3716    return true;
3717  }
3718  // Form Submission Delegate
3719  formSubmissionStarted(_formSubmission) {
3720    this.progressBar.setValue(0);
3721    this.showFormProgressBarAfterDelay();
3722  }
3723  formSubmissionFinished(_formSubmission) {
3724    this.progressBar.setValue(1);
3725    this.hideFormProgressBar();
3726  }
3727  // Private
3728  showVisitProgressBarAfterDelay() {
3729    this.visitProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
3730  }
3731  hideVisitProgressBar() {
3732    this.progressBar.hide();
3733    if (this.visitProgressBarTimeout != null) {
3734      window.clearTimeout(this.visitProgressBarTimeout);
3735      delete this.visitProgressBarTimeout;
3736    }
3737  }
3738  showFormProgressBarAfterDelay() {
3739    if (this.formProgressBarTimeout == null) {
3740      this.formProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
3741    }
3742  }
3743  hideFormProgressBar() {
3744    this.progressBar.hide();
3745    if (this.formProgressBarTimeout != null) {
3746      window.clearTimeout(this.formProgressBarTimeout);
3747      delete this.formProgressBarTimeout;
3748    }
3749  }
3750  showProgressBar = () => {
3751    this.progressBar.show();
3752  };
3753  reload(reason) {
3754    dispatch("turbo:reload", { detail: reason });
3755    window.location.href = this.location?.toString() || window.location.href;
3756  }
3757  get navigator() {
3758    return this.session.navigator;
3759  }
3760};
3761var CacheObserver = class {
3762  selector = "[data-turbo-temporary]";
3763  deprecatedSelector = "[data-turbo-cache=false]";
3764  started = false;
3765  start() {
3766    if (!this.started) {
3767      this.started = true;
3768      addEventListener("turbo:before-cache", this.removeTemporaryElements, false);
3769    }
3770  }
3771  stop() {
3772    if (this.started) {
3773      this.started = false;
3774      removeEventListener("turbo:before-cache", this.removeTemporaryElements, false);
3775    }
3776  }
3777  removeTemporaryElements = (_event) => {
3778    for (const element of this.temporaryElements) {
3779      element.remove();
3780    }
3781  };
3782  get temporaryElements() {
3783    return [...document.querySelectorAll(this.selector), ...this.temporaryElementsWithDeprecation];
3784  }
3785  get temporaryElementsWithDeprecation() {
3786    const elements = document.querySelectorAll(this.deprecatedSelector);
3787    if (elements.length) {
3788      console.warn(
3789        `The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.`
3790      );
3791    }
3792    return [...elements];
3793  }
3794};
3795var FrameRedirector = class {
3796  constructor(session2, element) {
3797    this.session = session2;
3798    this.element = element;
3799    this.linkInterceptor = new LinkInterceptor(this, element);
3800    this.formSubmitObserver = new FormSubmitObserver(this, element);
3801  }
3802  start() {
3803    this.linkInterceptor.start();
3804    this.formSubmitObserver.start();
3805  }
3806  stop() {
3807    this.linkInterceptor.stop();
3808    this.formSubmitObserver.stop();
3809  }
3810  // Link interceptor delegate
3811  shouldInterceptLinkClick(element, _location, _event) {
3812    return this.#shouldRedirect(element);
3813  }
3814  linkClickIntercepted(element, url, event) {
3815    const frame = this.#findFrameElement(element);
3816    if (frame) {
3817      frame.delegate.linkClickIntercepted(element, url, event);
3818    }
3819  }
3820  // Form submit observer delegate
3821  willSubmitForm(element, submitter2) {
3822    return element.closest("turbo-frame") == null && this.#shouldSubmit(element, submitter2) && this.#shouldRedirect(element, submitter2);
3823  }
3824  formSubmitted(element, submitter2) {
3825    const frame = this.#findFrameElement(element, submitter2);
3826    if (frame) {
3827      frame.delegate.formSubmitted(element, submitter2);
3828    }
3829  }
3830  #shouldSubmit(form, submitter2) {
3831    const action = getAction$1(form, submitter2);
3832    const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
3833    const rootLocation = expandURL(meta?.content ?? "/");
3834    return this.#shouldRedirect(form, submitter2) && locationIsVisitable(action, rootLocation);
3835  }
3836  #shouldRedirect(element, submitter2) {
3837    const isNavigatable = element instanceof HTMLFormElement ? this.session.submissionIsNavigatable(element, submitter2) : this.session.elementIsNavigatable(element);
3838    if (isNavigatable) {
3839      const frame = this.#findFrameElement(element, submitter2);
3840      return frame ? frame != element.closest("turbo-frame") : false;
3841    } else {
3842      return false;
3843    }
3844  }
3845  #findFrameElement(element, submitter2) {
3846    const id = submitter2?.getAttribute("data-turbo-frame") || element.getAttribute("data-turbo-frame");
3847    if (id && id != "_top") {
3848      const frame = this.element.querySelector(`#${id}:not([disabled])`);
3849      if (frame instanceof FrameElement) {
3850        return frame;
3851      }
3852    }
3853  }
3854};
3855var History = class {
3856  location;
3857  restorationIdentifier = uuid();
3858  restorationData = {};
3859  started = false;
3860  pageLoaded = false;
3861  currentIndex = 0;
3862  constructor(delegate) {
3863    this.delegate = delegate;
3864  }
3865  start() {
3866    if (!this.started) {
3867      addEventListener("popstate", this.onPopState, false);
3868      addEventListener("load", this.onPageLoad, false);
3869      this.currentIndex = history.state?.turbo?.restorationIndex || 0;
3870      this.started = true;
3871      this.replace(new URL(window.location.href));
3872    }
3873  }
3874  stop() {
3875    if (this.started) {
3876      removeEventListener("popstate", this.onPopState, false);
3877      removeEventListener("load", this.onPageLoad, false);
3878      this.started = false;
3879    }
3880  }
3881  push(location2, restorationIdentifier) {
3882    this.update(history.pushState, location2, restorationIdentifier);
3883  }
3884  replace(location2, restorationIdentifier) {
3885    this.update(history.replaceState, location2, restorationIdentifier);
3886  }
3887  update(method, location2, restorationIdentifier = uuid()) {
3888    if (method === history.pushState) ++this.currentIndex;
3889    const state = { turbo: { restorationIdentifier, restorationIndex: this.currentIndex } };
3890    method.call(history, state, "", location2.href);
3891    this.location = location2;
3892    this.restorationIdentifier = restorationIdentifier;
3893  }
3894  // Restoration data
3895  getRestorationDataForIdentifier(restorationIdentifier) {
3896    return this.restorationData[restorationIdentifier] || {};
3897  }
3898  updateRestorationData(additionalData) {
3899    const { restorationIdentifier } = this;
3900    const restorationData = this.restorationData[restorationIdentifier];
3901    this.restorationData[restorationIdentifier] = {
3902      ...restorationData,
3903      ...additionalData
3904    };
3905  }
3906  // Scroll restoration
3907  assumeControlOfScrollRestoration() {
3908    if (!this.previousScrollRestoration) {
3909      this.previousScrollRestoration = history.scrollRestoration ?? "auto";
3910      history.scrollRestoration = "manual";
3911    }
3912  }
3913  relinquishControlOfScrollRestoration() {
3914    if (this.previousScrollRestoration) {
3915      history.scrollRestoration = this.previousScrollRestoration;
3916      delete this.previousScrollRestoration;
3917    }
3918  }
3919  // Event handlers
3920  onPopState = (event) => {
3921    if (this.shouldHandlePopState()) {
3922      const { turbo } = event.state || {};
3923      if (turbo) {
3924        this.location = new URL(window.location.href);
3925        const { restorationIdentifier, restorationIndex } = turbo;
3926        this.restorationIdentifier = restorationIdentifier;
3927        const direction = restorationIndex > this.currentIndex ? "forward" : "back";
3928        this.delegate.historyPoppedToLocationWithRestorationIdentifierAndDirection(this.location, restorationIdentifier, direction);
3929        this.currentIndex = restorationIndex;
3930      }
3931    }
3932  };
3933  onPageLoad = async (_event) => {
3934    await nextMicrotask();
3935    this.pageLoaded = true;
3936  };
3937  // Private
3938  shouldHandlePopState() {
3939    return this.pageIsLoaded();
3940  }
3941  pageIsLoaded() {
3942    return this.pageLoaded || document.readyState == "complete";
3943  }
3944};
3945var LinkPrefetchObserver = class {
3946  started = false;
3947  #prefetchedLink = null;
3948  constructor(delegate, eventTarget) {
3949    this.delegate = delegate;
3950    this.eventTarget = eventTarget;
3951  }
3952  start() {
3953    if (this.started) return;
3954    if (this.eventTarget.readyState === "loading") {
3955      this.eventTarget.addEventListener("DOMContentLoaded", this.#enable, { once: true });
3956    } else {
3957      this.#enable();
3958    }
3959  }
3960  stop() {
3961    if (!this.started) return;
3962    this.eventTarget.removeEventListener("mouseenter", this.#tryToPrefetchRequest, {
3963      capture: true,
3964      passive: true
3965    });
3966    this.eventTarget.removeEventListener("mouseleave", this.#cancelRequestIfObsolete, {
3967      capture: true,
3968      passive: true
3969    });
3970    this.eventTarget.removeEventListener("turbo:before-fetch-request", this.#tryToUsePrefetchedRequest, true);
3971    this.started = false;
3972  }
3973  #enable = () => {
3974    this.eventTarget.addEventListener("mouseenter", this.#tryToPrefetchRequest, {
3975      capture: true,
3976      passive: true
3977    });
3978    this.eventTarget.addEventListener("mouseleave", this.#cancelRequestIfObsolete, {
3979      capture: true,
3980      passive: true
3981    });
3982    this.eventTarget.addEventListener("turbo:before-fetch-request", this.#tryToUsePrefetchedRequest, true);
3983    this.started = true;
3984  };
3985  #tryToPrefetchRequest = (event) => {
3986    if (getMetaContent("turbo-prefetch") === "false") return;
3987    const target = event.target;
3988    const isLink = target.matches && target.matches("a[href]:not([target^=_]):not([download])");
3989    if (isLink && this.#isPrefetchable(target)) {
3990      const link = target;
3991      const location2 = getLocationForLink(link);
3992      if (this.delegate.canPrefetchRequestToLocation(link, location2)) {
3993        this.#prefetchedLink = link;
3994        const fetchRequest = new FetchRequest(
3995          this,
3996          FetchMethod.get,
3997          location2,
3998          new URLSearchParams(),
3999          target
4000        );
4001        prefetchCache.setLater(location2.toString(), fetchRequest, this.#cacheTtl);
4002      }
4003    }
4004  };
4005  #cancelRequestIfObsolete = (event) => {
4006    if (event.target === this.#prefetchedLink) this.#cancelPrefetchRequest();
4007  };
4008  #cancelPrefetchRequest = () => {
4009    prefetchCache.clear();
4010    this.#prefetchedLink = null;
4011  };
4012  #tryToUsePrefetchedRequest = (event) => {
4013    if (event.target.tagName !== "FORM" && event.detail.fetchOptions.method === "GET") {
4014      const cached = prefetchCache.get(event.detail.url.toString());
4015      if (cached) {
4016        event.detail.fetchRequest = cached;
4017      }
4018      prefetchCache.clear();
4019    }
4020  };
4021  prepareRequest(request) {
4022    const link = request.target;
4023    request.headers["X-Sec-Purpose"] = "prefetch";
4024    const turboFrame = link.closest("turbo-frame");
4025    const turboFrameTarget = link.getAttribute("data-turbo-frame") || turboFrame?.getAttribute("target") || turboFrame?.id;
4026    if (turboFrameTarget && turboFrameTarget !== "_top") {
4027      request.headers["Turbo-Frame"] = turboFrameTarget;
4028    }
4029  }
4030  // Fetch request interface
4031  requestSucceededWithResponse() {
4032  }
4033  requestStarted(fetchRequest) {
4034  }
4035  requestErrored(fetchRequest) {
4036  }
4037  requestFinished(fetchRequest) {
4038  }
4039  requestPreventedHandlingResponse(fetchRequest, fetchResponse) {
4040  }
4041  requestFailedWithResponse(fetchRequest, fetchResponse) {
4042  }
4043  get #cacheTtl() {
4044    return Number(getMetaContent("turbo-prefetch-cache-time")) || cacheTtl;
4045  }
4046  #isPrefetchable(link) {
4047    const href = link.getAttribute("href");
4048    if (!href) return false;
4049    if (unfetchableLink(link)) return false;
4050    if (linkToTheSamePage(link)) return false;
4051    if (linkOptsOut(link)) return false;
4052    if (nonSafeLink(link)) return false;
4053    if (eventPrevented(link)) return false;
4054    return true;
4055  }
4056};
4057var unfetchableLink = (link) => {
4058  return link.origin !== document.location.origin || !["http:", "https:"].includes(link.protocol) || link.hasAttribute("target");
4059};
4060var linkToTheSamePage = (link) => {
4061  return link.pathname + link.search === document.location.pathname + document.location.search || link.href.startsWith("#");
4062};
4063var linkOptsOut = (link) => {
4064  if (link.getAttribute("data-turbo-prefetch") === "false") return true;
4065  if (link.getAttribute("data-turbo") === "false") return true;
4066  const turboPrefetchParent = findClosestRecursively(link, "[data-turbo-prefetch]");
4067  if (turboPrefetchParent && turboPrefetchParent.getAttribute("data-turbo-prefetch") === "false") return true;
4068  return false;
4069};
4070var nonSafeLink = (link) => {
4071  const turboMethod = link.getAttribute("data-turbo-method");
4072  if (turboMethod && turboMethod.toLowerCase() !== "get") return true;
4073  if (isUJS(link)) return true;
4074  if (link.hasAttribute("data-turbo-confirm")) return true;
4075  if (link.hasAttribute("data-turbo-stream")) return true;
4076  return false;
4077};
4078var isUJS = (link) => {
4079  return link.hasAttribute("data-remote") || link.hasAttribute("data-behavior") || link.hasAttribute("data-confirm") || link.hasAttribute("data-method");
4080};
4081var eventPrevented = (link) => {
4082  const event = dispatch("turbo:before-prefetch", { target: link, cancelable: true });
4083  return event.defaultPrevented;
4084};
4085var Navigator = class {
4086  constructor(delegate) {
4087    this.delegate = delegate;
4088  }
4089  proposeVisit(location2, options = {}) {
4090    if (this.delegate.allowsVisitingLocationWithAction(location2, options.action)) {
4091      this.delegate.visitProposedToLocation(location2, options);
4092    }
4093  }
4094  startVisit(locatable, restorationIdentifier, options = {}) {
4095    this.stop();
4096    this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, {
4097      referrer: this.location,
4098      ...options
4099    });
4100    this.currentVisit.start();
4101  }
4102  submitForm(form, submitter2) {
4103    this.stop();
4104    this.formSubmission = new FormSubmission(this, form, submitter2, true);
4105    this.formSubmission.start();
4106  }
4107  stop() {
4108    if (this.formSubmission) {
4109      this.formSubmission.stop();
4110      delete this.formSubmission;
4111    }
4112    if (this.currentVisit) {
4113      this.currentVisit.cancel();
4114      delete this.currentVisit;
4115    }
4116  }
4117  get adapter() {
4118    return this.delegate.adapter;
4119  }
4120  get view() {
4121    return this.delegate.view;
4122  }
4123  get rootLocation() {
4124    return this.view.snapshot.rootLocation;
4125  }
4126  get history() {
4127    return this.delegate.history;
4128  }
4129  // Form submission delegate
4130  formSubmissionStarted(formSubmission) {
4131    if (typeof this.adapter.formSubmissionStarted === "function") {
4132      this.adapter.formSubmissionStarted(formSubmission);
4133    }
4134  }
4135  async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) {
4136    if (formSubmission == this.formSubmission) {
4137      const responseHTML = await fetchResponse.responseHTML;
4138      if (responseHTML) {
4139        const shouldCacheSnapshot = formSubmission.isSafe;
4140        if (!shouldCacheSnapshot) {
4141          this.view.clearSnapshotCache();
4142        }
4143        const { statusCode, redirected } = fetchResponse;
4144        const action = this.#getActionForFormSubmission(formSubmission, fetchResponse);
4145        const visitOptions = {
4146          action,
4147          shouldCacheSnapshot,
4148          response: { statusCode, responseHTML, redirected }
4149        };
4150        this.proposeVisit(fetchResponse.location, visitOptions);
4151      }
4152    }
4153  }
4154  async formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
4155    const responseHTML = await fetchResponse.responseHTML;
4156    if (responseHTML) {
4157      const snapshot = PageSnapshot.fromHTMLString(responseHTML);
4158      if (fetchResponse.serverError) {
4159        await this.view.renderError(snapshot, this.currentVisit);
4160      } else {
4161        await this.view.renderPage(snapshot, false, true, this.currentVisit);
4162      }
4163      if (!snapshot.shouldPreserveScrollPosition) {
4164        this.view.scrollToTop();
4165      }
4166      this.view.clearSnapshotCache();
4167    }
4168  }
4169  formSubmissionErrored(formSubmission, error2) {
4170    console.error(error2);
4171  }
4172  formSubmissionFinished(formSubmission) {
4173    if (typeof this.adapter.formSubmissionFinished === "function") {
4174      this.adapter.formSubmissionFinished(formSubmission);
4175    }
4176  }
4177  // Link prefetching
4178  linkPrefetchingIsEnabledForLocation(location2) {
4179    if (typeof this.adapter.linkPrefetchingIsEnabledForLocation === "function") {
4180      return this.adapter.linkPrefetchingIsEnabledForLocation(location2);
4181    }
4182    return true;
4183  }
4184  // Visit delegate
4185  visitStarted(visit2) {
4186    this.delegate.visitStarted(visit2);
4187  }
4188  visitCompleted(visit2) {
4189    this.delegate.visitCompleted(visit2);
4190    delete this.currentVisit;
4191  }
4192  locationWithActionIsSamePage(location2, action) {
4193    const anchor = getAnchor(location2);
4194    const currentAnchor = getAnchor(this.view.lastRenderedLocation);
4195    const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
4196    return action !== "replace" && getRequestURL(location2) === getRequestURL(this.view.lastRenderedLocation) && (isRestorationToTop || anchor != null && anchor !== currentAnchor);
4197  }
4198  visitScrolledToSamePageLocation(oldURL, newURL) {
4199    this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
4200  }
4201  // Visits
4202  get location() {
4203    return this.history.location;
4204  }
4205  get restorationIdentifier() {
4206    return this.history.restorationIdentifier;
4207  }
4208  #getActionForFormSubmission(formSubmission, fetchResponse) {
4209    const { submitter: submitter2, formElement } = formSubmission;
4210    return getVisitAction(submitter2, formElement) || this.#getDefaultAction(fetchResponse);
4211  }
4212  #getDefaultAction(fetchResponse) {
4213    const sameLocationRedirect = fetchResponse.redirected && fetchResponse.location.href === this.location?.href;
4214    return sameLocationRedirect ? "replace" : "advance";
4215  }
4216};
4217var PageStage = {
4218  initial: 0,
4219  loading: 1,
4220  interactive: 2,
4221  complete: 3
4222};
4223var PageObserver = class {
4224  stage = PageStage.initial;
4225  started = false;
4226  constructor(delegate) {
4227    this.delegate = delegate;
4228  }
4229  start() {
4230    if (!this.started) {
4231      if (this.stage == PageStage.initial) {
4232        this.stage = PageStage.loading;
4233      }
4234      document.addEventListener("readystatechange", this.interpretReadyState, false);
4235      addEventListener("pagehide", this.pageWillUnload, false);
4236      this.started = true;
4237    }
4238  }
4239  stop() {
4240    if (this.started) {
4241      document.removeEventListener("readystatechange", this.interpretReadyState, false);
4242      removeEventListener("pagehide", this.pageWillUnload, false);
4243      this.started = false;
4244    }
4245  }
4246  interpretReadyState = () => {
4247    const { readyState } = this;
4248    if (readyState == "interactive") {
4249      this.pageIsInteractive();
4250    } else if (readyState == "complete") {
4251      this.pageIsComplete();
4252    }
4253  };
4254  pageIsInteractive() {
4255    if (this.stage == PageStage.loading) {
4256      this.stage = PageStage.interactive;
4257      this.delegate.pageBecameInteractive();
4258    }
4259  }
4260  pageIsComplete() {
4261    this.pageIsInteractive();
4262    if (this.stage == PageStage.interactive) {
4263      this.stage = PageStage.complete;
4264      this.delegate.pageLoaded();
4265    }
4266  }
4267  pageWillUnload = () => {
4268    this.delegate.pageWillUnload();
4269  };
4270  get readyState() {
4271    return document.readyState;
4272  }
4273};
4274var ScrollObserver = class {
4275  started = false;
4276  constructor(delegate) {
4277    this.delegate = delegate;
4278  }
4279  start() {
4280    if (!this.started) {
4281      addEventListener("scroll", this.onScroll, false);
4282      this.onScroll();
4283      this.started = true;
4284    }
4285  }
4286  stop() {
4287    if (this.started) {
4288      removeEventListener("scroll", this.onScroll, false);
4289      this.started = false;
4290    }
4291  }
4292  onScroll = () => {
4293    this.updatePosition({ x: window.pageXOffset, y: window.pageYOffset });
4294  };
4295  // Private
4296  updatePosition(position) {
4297    this.delegate.scrollPositionChanged(position);
4298  }
4299};
4300var StreamMessageRenderer = class {
4301  render({ fragment }) {
4302    Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => {
4303      withAutofocusFromFragment(fragment, () => {
4304        withPreservedFocus(() => {
4305          document.documentElement.appendChild(fragment);
4306        });
4307      });
4308    });
4309  }
4310  // Bardo delegate
4311  enteringBardo(currentPermanentElement, newPermanentElement) {
4312    newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
4313  }
4314  leavingBardo() {
4315  }
4316};
4317function getPermanentElementMapForFragment(fragment) {
4318  const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement);
4319  const permanentElementMap = {};
4320  for (const permanentElementInDocument of permanentElementsInDocument) {
4321    const { id } = permanentElementInDocument;
4322    for (const streamElement of fragment.querySelectorAll("turbo-stream")) {
4323      const elementInStream = getPermanentElementById(streamElement.templateElement.content, id);
4324      if (elementInStream) {
4325        permanentElementMap[id] = [permanentElementInDocument, elementInStream];
4326      }
4327    }
4328  }
4329  return permanentElementMap;
4330}
4331async function withAutofocusFromFragment(fragment, callback) {
4332  const generatedID = `turbo-stream-autofocus-${uuid()}`;
4333  const turboStreams = fragment.querySelectorAll("turbo-stream");
4334  const elementWithAutofocus = firstAutofocusableElementInStreams(turboStreams);
4335  let willAutofocusId = null;
4336  if (elementWithAutofocus) {
4337    if (elementWithAutofocus.id) {
4338      willAutofocusId = elementWithAutofocus.id;
4339    } else {
4340      willAutofocusId = generatedID;
4341    }
4342    elementWithAutofocus.id = willAutofocusId;
4343  }
4344  callback();
4345  await nextRepaint();
4346  const hasNoActiveElement = document.activeElement == null || document.activeElement == document.body;
4347  if (hasNoActiveElement && willAutofocusId) {
4348    const elementToAutofocus = document.getElementById(willAutofocusId);
4349    if (elementIsFocusable(elementToAutofocus)) {
4350      elementToAutofocus.focus();
4351    }
4352    if (elementToAutofocus && elementToAutofocus.id == generatedID) {
4353      elementToAutofocus.removeAttribute("id");
4354    }
4355  }
4356}
4357async function withPreservedFocus(callback) {
4358  const [activeElementBeforeRender, activeElementAfterRender] = await around(callback, () => document.activeElement);
4359  const restoreFocusTo = activeElementBeforeRender && activeElementBeforeRender.id;
4360  if (restoreFocusTo) {
4361    const elementToFocus = document.getElementById(restoreFocusTo);
4362    if (elementIsFocusable(elementToFocus) && elementToFocus != activeElementAfterRender) {
4363      elementToFocus.focus();
4364    }
4365  }
4366}
4367function firstAutofocusableElementInStreams(nodeListOfStreamElements) {
4368  for (const streamElement of nodeListOfStreamElements) {
4369    const elementWithAutofocus = queryAutofocusableElement(streamElement.templateElement.content);
4370    if (elementWithAutofocus) return elementWithAutofocus;
4371  }
4372  return null;
4373}
4374var StreamObserver = class {
4375  sources = /* @__PURE__ */ new Set();
4376  #started = false;
4377  constructor(delegate) {
4378    this.delegate = delegate;
4379  }
4380  start() {
4381    if (!this.#started) {
4382      this.#started = true;
4383      addEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
4384    }
4385  }
4386  stop() {
4387    if (this.#started) {
4388      this.#started = false;
4389      removeEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
4390    }
4391  }
4392  connectStreamSource(source) {
4393    if (!this.streamSourceIsConnected(source)) {
4394      this.sources.add(source);
4395      source.addEventListener("message", this.receiveMessageEvent, false);
4396    }
4397  }
4398  disconnectStreamSource(source) {
4399    if (this.streamSourceIsConnected(source)) {
4400      this.sources.delete(source);
4401      source.removeEventListener("message", this.receiveMessageEvent, false);
4402    }
4403  }
4404  streamSourceIsConnected(source) {
4405    return this.sources.has(source);
4406  }
4407  inspectFetchResponse = (event) => {
4408    const response = fetchResponseFromEvent(event);
4409    if (response && fetchResponseIsStream(response)) {
4410      event.preventDefault();
4411      this.receiveMessageResponse(response);
4412    }
4413  };
4414  receiveMessageEvent = (event) => {
4415    if (this.#started && typeof event.data == "string") {
4416      this.receiveMessageHTML(event.data);
4417    }
4418  };
4419  async receiveMessageResponse(response) {
4420    const html = await response.responseHTML;
4421    if (html) {
4422      this.receiveMessageHTML(html);
4423    }
4424  }
4425  receiveMessageHTML(html) {
4426    this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
4427  }
4428};
4429function fetchResponseFromEvent(event) {
4430  const fetchResponse = event.detail?.fetchResponse;
4431  if (fetchResponse instanceof FetchResponse) {
4432    return fetchResponse;
4433  }
4434}
4435function fetchResponseIsStream(response) {
4436  const contentType = response.contentType ?? "";
4437  return contentType.startsWith(StreamMessage.contentType);
4438}
4439var ErrorRenderer = class extends Renderer {
4440  static renderElement(currentElement, newElement) {
4441    const { documentElement, body } = document;
4442    documentElement.replaceChild(newElement, body);
4443  }
4444  async render() {
4445    this.replaceHeadAndBody();
4446    this.activateScriptElements();
4447  }
4448  replaceHeadAndBody() {
4449    const { documentElement, head } = document;
4450    documentElement.replaceChild(this.newHead, head);
4451    this.renderElement(this.currentElement, this.newElement);
4452  }
4453  activateScriptElements() {
4454    for (const replaceableElement of this.scriptElements) {
4455      const parentNode = replaceableElement.parentNode;
4456      if (parentNode) {
4457        const element = activateScriptElement(replaceableElement);
4458        parentNode.replaceChild(element, replaceableElement);
4459      }
4460    }
4461  }
4462  get newHead() {
4463    return this.newSnapshot.headSnapshot.element;
4464  }
4465  get scriptElements() {
4466    return document.documentElement.querySelectorAll("script");
4467  }
4468};
4469var PageRenderer = class extends Renderer {
4470  static renderElement(currentElement, newElement) {
4471    if (document.body && newElement instanceof HTMLBodyElement) {
4472      document.body.replaceWith(newElement);
4473    } else {
4474      document.documentElement.appendChild(newElement);
4475    }
4476  }
4477  get shouldRender() {
4478    return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
4479  }
4480  get reloadReason() {
4481    if (!this.newSnapshot.isVisitable) {
4482      return {
4483        reason: "turbo_visit_control_is_reload"
4484      };
4485    }
4486    if (!this.trackedElementsAreIdentical) {
4487      return {
4488        reason: "tracked_element_mismatch"
4489      };
4490    }
4491  }
4492  async prepareToRender() {
4493    this.#setLanguage();
4494    await this.mergeHead();
4495  }
4496  async render() {
4497    if (this.willRender) {
4498      await this.replaceBody();
4499    }
4500  }
4501  finishRendering() {
4502    super.finishRendering();
4503    if (!this.isPreview) {
4504      this.focusFirstAutofocusableElement();
4505    }
4506  }
4507  get currentHeadSnapshot() {
4508    return this.currentSnapshot.headSnapshot;
4509  }
4510  get newHeadSnapshot() {
4511    return this.newSnapshot.headSnapshot;
4512  }
4513  get newElement() {
4514    return this.newSnapshot.element;
4515  }
4516  #setLanguage() {
4517    const { documentElement } = this.currentSnapshot;
4518    const { lang } = this.newSnapshot;
4519    if (lang) {
4520      documentElement.setAttribute("lang", lang);
4521    } else {
4522      documentElement.removeAttribute("lang");
4523    }
4524  }
4525  async mergeHead() {
4526    const mergedHeadElements = this.mergeProvisionalElements();
4527    const newStylesheetElements = this.copyNewHeadStylesheetElements();
4528    this.copyNewHeadScriptElements();
4529    await mergedHeadElements;
4530    await newStylesheetElements;
4531    if (this.willRender) {
4532      this.removeUnusedDynamicStylesheetElements();
4533    }
4534  }
4535  async replaceBody() {
4536    await this.preservingPermanentElements(async () => {
4537      this.activateNewBody();
4538      await this.assignNewBody();
4539    });
4540  }
4541  get trackedElementsAreIdentical() {
4542    return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature;
4543  }
4544  async copyNewHeadStylesheetElements() {
4545    const loadingElements = [];
4546    for (const element of this.newHeadStylesheetElements) {
4547      loadingElements.push(waitForLoad(element));
4548      document.head.appendChild(element);
4549    }
4550    await Promise.all(loadingElements);
4551  }
4552  copyNewHeadScriptElements() {
4553    for (const element of this.newHeadScriptElements) {
4554      document.head.appendChild(activateScriptElement(element));
4555    }
4556  }
4557  removeUnusedDynamicStylesheetElements() {
4558    for (const element of this.unusedDynamicStylesheetElements) {
4559      document.head.removeChild(element);
4560    }
4561  }
4562  async mergeProvisionalElements() {
4563    const newHeadElements = [...this.newHeadProvisionalElements];
4564    for (const element of this.currentHeadProvisionalElements) {
4565      if (!this.isCurrentElementInElementList(element, newHeadElements)) {
4566        document.head.removeChild(element);
4567      }
4568    }
4569    for (const element of newHeadElements) {
4570      document.head.appendChild(element);
4571    }
4572  }
4573  isCurrentElementInElementList(element, elementList) {
4574    for (const [index, newElement] of elementList.entries()) {
4575      if (element.tagName == "TITLE") {
4576        if (newElement.tagName != "TITLE") {
4577          continue;
4578        }
4579        if (element.innerHTML == newElement.innerHTML) {
4580          elementList.splice(index, 1);
4581          return true;
4582        }
4583      }
4584      if (newElement.isEqualNode(element)) {
4585        elementList.splice(index, 1);
4586        return true;
4587      }
4588    }
4589    return false;
4590  }
4591  removeCurrentHeadProvisionalElements() {
4592    for (const element of this.currentHeadProvisionalElements) {
4593      document.head.removeChild(element);
4594    }
4595  }
4596  copyNewHeadProvisionalElements() {
4597    for (const element of this.newHeadProvisionalElements) {
4598      document.head.appendChild(element);
4599    }
4600  }
4601  activateNewBody() {
4602    document.adoptNode(this.newElement);
4603    this.activateNewBodyScriptElements();
4604  }
4605  activateNewBodyScriptElements() {
4606    for (const inertScriptElement of this.newBodyScriptElements) {
4607      const activatedScriptElement = activateScriptElement(inertScriptElement);
4608      inertScriptElement.replaceWith(activatedScriptElement);
4609    }
4610  }
4611  async assignNewBody() {
4612    await this.renderElement(this.currentElement, this.newElement);
4613  }
4614  get unusedDynamicStylesheetElements() {
4615    return this.oldHeadStylesheetElements.filter((element) => {
4616      return element.getAttribute("data-turbo-track") === "dynamic";
4617    });
4618  }
4619  get oldHeadStylesheetElements() {
4620    return this.currentHeadSnapshot.getStylesheetElementsNotInSnapshot(this.newHeadSnapshot);
4621  }
4622  get newHeadStylesheetElements() {
4623    return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
4624  }
4625  get newHeadScriptElements() {
4626    return this.newHeadSnapshot.getScriptElementsNotInSnapshot(this.currentHeadSnapshot);
4627  }
4628  get currentHeadProvisionalElements() {
4629    return this.currentHeadSnapshot.provisionalElements;
4630  }
4631  get newHeadProvisionalElements() {
4632    return this.newHeadSnapshot.provisionalElements;
4633  }
4634  get newBodyScriptElements() {
4635    return this.newElement.querySelectorAll("script");
4636  }
4637};
4638var MorphingPageRenderer = class extends PageRenderer {
4639  static renderElement(currentElement, newElement) {
4640    morphElements(currentElement, newElement, {
4641      callbacks: {
4642        beforeNodeMorphed: (element) => !canRefreshFrame(element)
4643      }
4644    });
4645    for (const frame of currentElement.querySelectorAll("turbo-frame")) {
4646      if (canRefreshFrame(frame)) frame.reload();
4647    }
4648    dispatch("turbo:morph", { detail: { currentElement, newElement } });
4649  }
4650  async preservingPermanentElements(callback) {
4651    return await callback();
4652  }
4653  get renderMethod() {
4654    return "morph";
4655  }
4656  get shouldAutofocus() {
4657    return false;
4658  }
4659};
4660function canRefreshFrame(frame) {
4661  return frame instanceof FrameElement && frame.src && frame.refresh === "morph" && !frame.closest("[data-turbo-permanent]");
4662}
4663var SnapshotCache = class {
4664  keys = [];
4665  snapshots = {};
4666  constructor(size) {
4667    this.size = size;
4668  }
4669  has(location2) {
4670    return toCacheKey(location2) in this.snapshots;
4671  }
4672  get(location2) {
4673    if (this.has(location2)) {
4674      const snapshot = this.read(location2);
4675      this.touch(location2);
4676      return snapshot;
4677    }
4678  }
4679  put(location2, snapshot) {
4680    this.write(location2, snapshot);
4681    this.touch(location2);
4682    return snapshot;
4683  }
4684  clear() {
4685    this.snapshots = {};
4686  }
4687  // Private
4688  read(location2) {
4689    return this.snapshots[toCacheKey(location2)];
4690  }
4691  write(location2, snapshot) {
4692    this.snapshots[toCacheKey(location2)] = snapshot;
4693  }
4694  touch(location2) {
4695    const key = toCacheKey(location2);
4696    const index = this.keys.indexOf(key);
4697    if (index > -1) this.keys.splice(index, 1);
4698    this.keys.unshift(key);
4699    this.trim();
4700  }
4701  trim() {
4702    for (const key of this.keys.splice(this.size)) {
4703      delete this.snapshots[key];
4704    }
4705  }
4706};
4707var PageView = class extends View {
4708  snapshotCache = new SnapshotCache(10);
4709  lastRenderedLocation = new URL(location.href);
4710  forceReloaded = false;
4711  shouldTransitionTo(newSnapshot) {
4712    return this.snapshot.prefersViewTransitions && newSnapshot.prefersViewTransitions;
4713  }
4714  renderPage(snapshot, isPreview = false, willRender = true, visit2) {
4715    const shouldMorphPage = this.isPageRefresh(visit2) && this.snapshot.shouldMorphPage;
4716    const rendererClass = shouldMorphPage ? MorphingPageRenderer : PageRenderer;
4717    const renderer = new rendererClass(this.snapshot, snapshot, isPreview, willRender);
4718    if (!renderer.shouldRender) {
4719      this.forceReloaded = true;
4720    } else {
4721      visit2?.changeHistory();
4722    }
4723    return this.render(renderer);
4724  }
4725  renderError(snapshot, visit2) {
4726    visit2?.changeHistory();
4727    const renderer = new ErrorRenderer(this.snapshot, snapshot, false);
4728    return this.render(renderer);
4729  }
4730  clearSnapshotCache() {
4731    this.snapshotCache.clear();
4732  }
4733  async cacheSnapshot(snapshot = this.snapshot) {
4734    if (snapshot.isCacheable) {
4735      this.delegate.viewWillCacheSnapshot();
4736      const { lastRenderedLocation: location2 } = this;
4737      await nextEventLoopTick();
4738      const cachedSnapshot = snapshot.clone();
4739      this.snapshotCache.put(location2, cachedSnapshot);
4740      return cachedSnapshot;
4741    }
4742  }
4743  getCachedSnapshotForLocation(location2) {
4744    return this.snapshotCache.get(location2);
4745  }
4746  isPageRefresh(visit2) {
4747    return !visit2 || this.lastRenderedLocation.pathname === visit2.location.pathname && visit2.action === "replace";
4748  }
4749  shouldPreserveScrollPosition(visit2) {
4750    return this.isPageRefresh(visit2) && this.snapshot.shouldPreserveScrollPosition;
4751  }
4752  get snapshot() {
4753    return PageSnapshot.fromElement(this.element);
4754  }
4755};
4756var Preloader = class {
4757  selector = "a[data-turbo-preload]";
4758  constructor(delegate, snapshotCache) {
4759    this.delegate = delegate;
4760    this.snapshotCache = snapshotCache;
4761  }
4762  start() {
4763    if (document.readyState === "loading") {
4764      document.addEventListener("DOMContentLoaded", this.#preloadAll);
4765    } else {
4766      this.preloadOnLoadLinksForView(document.body);
4767    }
4768  }
4769  stop() {
4770    document.removeEventListener("DOMContentLoaded", this.#preloadAll);
4771  }
4772  preloadOnLoadLinksForView(element) {
4773    for (const link of element.querySelectorAll(this.selector)) {
4774      if (this.delegate.shouldPreloadLink(link)) {
4775        this.preloadURL(link);
4776      }
4777    }
4778  }
4779  async preloadURL(link) {
4780    const location2 = new URL(link.href);
4781    if (this.snapshotCache.has(location2)) {
4782      return;
4783    }
4784    const fetchRequest = new FetchRequest(this, FetchMethod.get, location2, new URLSearchParams(), link);
4785    await fetchRequest.perform();
4786  }
4787  // Fetch request delegate
4788  prepareRequest(fetchRequest) {
4789    fetchRequest.headers["X-Sec-Purpose"] = "prefetch";
4790  }
4791  async requestSucceededWithResponse(fetchRequest, fetchResponse) {
4792    try {
4793      const responseHTML = await fetchResponse.responseHTML;
4794      const snapshot = PageSnapshot.fromHTMLString(responseHTML);
4795      this.snapshotCache.put(fetchRequest.url, snapshot);
4796    } catch (_) {
4797    }
4798  }
4799  requestStarted(fetchRequest) {
4800  }
4801  requestErrored(fetchRequest) {
4802  }
4803  requestFinished(fetchRequest) {
4804  }
4805  requestPreventedHandlingResponse(fetchRequest, fetchResponse) {
4806  }
4807  requestFailedWithResponse(fetchRequest, fetchResponse) {
4808  }
4809  #preloadAll = () => {
4810    this.preloadOnLoadLinksForView(document.body);
4811  };
4812};
4813var Cache = class {
4814  constructor(session2) {
4815    this.session = session2;
4816  }
4817  clear() {
4818    this.session.clearCache();
4819  }
4820  resetCacheControl() {
4821    this.#setCacheControl("");
4822  }
4823  exemptPageFromCache() {
4824    this.#setCacheControl("no-cache");
4825  }
4826  exemptPageFromPreview() {
4827    this.#setCacheControl("no-preview");
4828  }
4829  #setCacheControl(value) {
4830    setMetaContent("turbo-cache-control", value);
4831  }
4832};
4833var Session = class {
4834  navigator = new Navigator(this);
4835  history = new History(this);
4836  view = new PageView(this, document.documentElement);
4837  adapter = new BrowserAdapter(this);
4838  pageObserver = new PageObserver(this);
4839  cacheObserver = new CacheObserver();
4840  linkPrefetchObserver = new LinkPrefetchObserver(this, document);
4841  linkClickObserver = new LinkClickObserver(this, window);
4842  formSubmitObserver = new FormSubmitObserver(this, document);
4843  scrollObserver = new ScrollObserver(this);
4844  streamObserver = new StreamObserver(this);
4845  formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
4846  frameRedirector = new FrameRedirector(this, document.documentElement);
4847  streamMessageRenderer = new StreamMessageRenderer();
4848  cache = new Cache(this);
4849  enabled = true;
4850  started = false;
4851  #pageRefreshDebouncePeriod = 150;
4852  constructor(recentRequests2) {
4853    this.recentRequests = recentRequests2;
4854    this.preloader = new Preloader(this, this.view.snapshotCache);
4855    this.debouncedRefresh = this.refresh;
4856    this.pageRefreshDebouncePeriod = this.pageRefreshDebouncePeriod;
4857  }
4858  start() {
4859    if (!this.started) {
4860      this.pageObserver.start();
4861      this.cacheObserver.start();
4862      this.linkPrefetchObserver.start();
4863      this.formLinkClickObserver.start();
4864      this.linkClickObserver.start();
4865      this.formSubmitObserver.start();
4866      this.scrollObserver.start();
4867      this.streamObserver.start();
4868      this.frameRedirector.start();
4869      this.history.start();
4870      this.preloader.start();
4871      this.started = true;
4872      this.enabled = true;
4873    }
4874  }
4875  disable() {
4876    this.enabled = false;
4877  }
4878  stop() {
4879    if (this.started) {
4880      this.pageObserver.stop();
4881      this.cacheObserver.stop();
4882      this.linkPrefetchObserver.stop();
4883      this.formLinkClickObserver.stop();
4884      this.linkClickObserver.stop();
4885      this.formSubmitObserver.stop();
4886      this.scrollObserver.stop();
4887      this.streamObserver.stop();
4888      this.frameRedirector.stop();
4889      this.history.stop();
4890      this.preloader.stop();
4891      this.started = false;
4892    }
4893  }
4894  registerAdapter(adapter) {
4895    this.adapter = adapter;
4896  }
4897  visit(location2, options = {}) {
4898    const frameElement = options.frame ? document.getElementById(options.frame) : null;
4899    if (frameElement instanceof FrameElement) {
4900      const action = options.action || getVisitAction(frameElement);
4901      frameElement.delegate.proposeVisitIfNavigatedWithAction(frameElement, action);
4902      frameElement.src = location2.toString();
4903    } else {
4904      this.navigator.proposeVisit(expandURL(location2), options);
4905    }
4906  }
4907  refresh(url, requestId) {
4908    const isRecentRequest = requestId && this.recentRequests.has(requestId);
4909    const isCurrentUrl = url === document.baseURI;
4910    if (!isRecentRequest && !this.navigator.currentVisit && isCurrentUrl) {
4911      this.visit(url, { action: "replace", shouldCacheSnapshot: false });
4912    }
4913  }
4914  connectStreamSource(source) {
4915    this.streamObserver.connectStreamSource(source);
4916  }
4917  disconnectStreamSource(source) {
4918    this.streamObserver.disconnectStreamSource(source);
4919  }
4920  renderStreamMessage(message) {
4921    this.streamMessageRenderer.render(StreamMessage.wrap(message));
4922  }
4923  clearCache() {
4924    this.view.clearSnapshotCache();
4925  }
4926  setProgressBarDelay(delay) {
4927    console.warn(
4928      "Please replace `session.setProgressBarDelay(delay)` with `session.progressBarDelay = delay`. The function is deprecated and will be removed in a future version of Turbo.`"
4929    );
4930    this.progressBarDelay = delay;
4931  }
4932  set progressBarDelay(delay) {
4933    config.drive.progressBarDelay = delay;
4934  }
4935  get progressBarDelay() {
4936    return config.drive.progressBarDelay;
4937  }
4938  set drive(value) {
4939    config.drive.enabled = value;
4940  }
4941  get drive() {
4942    return config.drive.enabled;
4943  }
4944  set formMode(value) {
4945    config.forms.mode = value;
4946  }
4947  get formMode() {
4948    return config.forms.mode;
4949  }
4950  get location() {
4951    return this.history.location;
4952  }
4953  get restorationIdentifier() {
4954    return this.history.restorationIdentifier;
4955  }
4956  get pageRefreshDebouncePeriod() {
4957    return this.#pageRefreshDebouncePeriod;
4958  }
4959  set pageRefreshDebouncePeriod(value) {
4960    this.refresh = debounce(this.debouncedRefresh.bind(this), value);
4961    this.#pageRefreshDebouncePeriod = value;
4962  }
4963  // Preloader delegate
4964  shouldPreloadLink(element) {
4965    const isUnsafe = element.hasAttribute("data-turbo-method");
4966    const isStream = element.hasAttribute("data-turbo-stream");
4967    const frameTarget = element.getAttribute("data-turbo-frame");
4968    const frame = frameTarget == "_top" ? null : document.getElementById(frameTarget) || findClosestRecursively(element, "turbo-frame:not([disabled])");
4969    if (isUnsafe || isStream || frame instanceof FrameElement) {
4970      return false;
4971    } else {
4972      const location2 = new URL(element.href);
4973      return this.elementIsNavigatable(element) && locationIsVisitable(location2, this.snapshot.rootLocation);
4974    }
4975  }
4976  // History delegate
4977  historyPoppedToLocationWithRestorationIdentifierAndDirection(location2, restorationIdentifier, direction) {
4978    if (this.enabled) {
4979      this.navigator.startVisit(location2, restorationIdentifier, {
4980        action: "restore",
4981        historyChanged: true,
4982        direction
4983      });
4984    } else {
4985      this.adapter.pageInvalidated({
4986        reason: "turbo_disabled"
4987      });
4988    }
4989  }
4990  // Scroll observer delegate
4991  scrollPositionChanged(position) {
4992    this.history.updateRestorationData({ scrollPosition: position });
4993  }
4994  // Form click observer delegate
4995  willSubmitFormLinkToLocation(link, location2) {
4996    return this.elementIsNavigatable(link) && locationIsVisitable(location2, this.snapshot.rootLocation);
4997  }
4998  submittedFormLinkToLocation() {
4999  }
5000  // Link hover observer delegate
5001  canPrefetchRequestToLocation(link, location2) {
5002    return this.elementIsNavigatable(link) && locationIsVisitable(location2, this.snapshot.rootLocation) && this.navigator.linkPrefetchingIsEnabledForLocation(location2);
5003  }
5004  // Link click observer delegate
5005  willFollowLinkToLocation(link, location2, event) {
5006    return this.elementIsNavigatable(link) && locationIsVisitable(location2, this.snapshot.rootLocation) && this.applicationAllowsFollowingLinkToLocation(link, location2, event);
5007  }
5008  followedLinkToLocation(link, location2) {
5009    const action = this.getActionForLink(link);
5010    const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
5011    this.visit(location2.href, { action, acceptsStreamResponse });
5012  }
5013  // Navigator delegate
5014  allowsVisitingLocationWithAction(location2, action) {
5015    return this.locationWithActionIsSamePage(location2, action) || this.applicationAllowsVisitingLocation(location2);
5016  }
5017  visitProposedToLocation(location2, options) {
5018    extendURLWithDeprecatedProperties(location2);
5019    this.adapter.visitProposedToLocation(location2, options);
5020  }
5021  // Visit delegate
5022  visitStarted(visit2) {
5023    if (!visit2.acceptsStreamResponse) {
5024      markAsBusy(document.documentElement);
5025      this.view.markVisitDirection(visit2.direction);
5026    }
5027    extendURLWithDeprecatedProperties(visit2.location);
5028    if (!visit2.silent) {
5029      this.notifyApplicationAfterVisitingLocation(visit2.location, visit2.action);
5030    }
5031  }
5032  visitCompleted(visit2) {
5033    this.view.unmarkVisitDirection();
5034    clearBusyState(document.documentElement);
5035    this.notifyApplicationAfterPageLoad(visit2.getTimingMetrics());
5036  }
5037  locationWithActionIsSamePage(location2, action) {
5038    return this.navigator.locationWithActionIsSamePage(location2, action);
5039  }
5040  visitScrolledToSamePageLocation(oldURL, newURL) {
5041    this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
5042  }
5043  // Form submit observer delegate
5044  willSubmitForm(form, submitter2) {
5045    const action = getAction$1(form, submitter2);
5046    return this.submissionIsNavigatable(form, submitter2) && locationIsVisitable(expandURL(action), this.snapshot.rootLocation);
5047  }
5048  formSubmitted(form, submitter2) {
5049    this.navigator.submitForm(form, submitter2);
5050  }
5051  // Page observer delegate
5052  pageBecameInteractive() {
5053    this.view.lastRenderedLocation = this.location;
5054    this.notifyApplicationAfterPageLoad();
5055  }
5056  pageLoaded() {
5057    this.history.assumeControlOfScrollRestoration();
5058  }
5059  pageWillUnload() {
5060    this.history.relinquishControlOfScrollRestoration();
5061  }
5062  // Stream observer delegate
5063  receivedMessageFromStream(message) {
5064    this.renderStreamMessage(message);
5065  }
5066  // Page view delegate
5067  viewWillCacheSnapshot() {
5068    if (!this.navigator.currentVisit?.silent) {
5069      this.notifyApplicationBeforeCachingSnapshot();
5070    }
5071  }
5072  allowsImmediateRender({ element }, options) {
5073    const event = this.notifyApplicationBeforeRender(element, options);
5074    const {
5075      defaultPrevented,
5076      detail: { render }
5077    } = event;
5078    if (this.view.renderer && render) {
5079      this.view.renderer.renderElement = render;
5080    }
5081    return !defaultPrevented;
5082  }
5083  viewRenderedSnapshot(_snapshot, _isPreview, renderMethod) {
5084    this.view.lastRenderedLocation = this.history.location;
5085    this.notifyApplicationAfterRender(renderMethod);
5086  }
5087  preloadOnLoadLinksForView(element) {
5088    this.preloader.preloadOnLoadLinksForView(element);
5089  }
5090  viewInvalidated(reason) {
5091    this.adapter.pageInvalidated(reason);
5092  }
5093  // Frame element
5094  frameLoaded(frame) {
5095    this.notifyApplicationAfterFrameLoad(frame);
5096  }
5097  frameRendered(fetchResponse, frame) {
5098    this.notifyApplicationAfterFrameRender(fetchResponse, frame);
5099  }
5100  // Application events
5101  applicationAllowsFollowingLinkToLocation(link, location2, ev) {
5102    const event = this.notifyApplicationAfterClickingLinkToLocation(link, location2, ev);
5103    return !event.defaultPrevented;
5104  }
5105  applicationAllowsVisitingLocation(location2) {
5106    const event = this.notifyApplicationBeforeVisitingLocation(location2);
5107    return !event.defaultPrevented;
5108  }
5109  notifyApplicationAfterClickingLinkToLocation(link, location2, event) {
5110    return dispatch("turbo:click", {
5111      target: link,
5112      detail: { url: location2.href, originalEvent: event },
5113      cancelable: true
5114    });
5115  }
5116  notifyApplicationBeforeVisitingLocation(location2) {
5117    return dispatch("turbo:before-visit", {
5118      detail: { url: location2.href },
5119      cancelable: true
5120    });
5121  }
5122  notifyApplicationAfterVisitingLocation(location2, action) {
5123    return dispatch("turbo:visit", { detail: { url: location2.href, action } });
5124  }
5125  notifyApplicationBeforeCachingSnapshot() {
5126    return dispatch("turbo:before-cache");
5127  }
5128  notifyApplicationBeforeRender(newBody, options) {
5129    return dispatch("turbo:before-render", {
5130      detail: { newBody, ...options },
5131      cancelable: true
5132    });
5133  }
5134  notifyApplicationAfterRender(renderMethod) {
5135    return dispatch("turbo:render", { detail: { renderMethod } });
5136  }
5137  notifyApplicationAfterPageLoad(timing = {}) {
5138    return dispatch("turbo:load", {
5139      detail: { url: this.location.href, timing }
5140    });
5141  }
5142  notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
5143    dispatchEvent(
5144      new HashChangeEvent("hashchange", {
5145        oldURL: oldURL.toString(),
5146        newURL: newURL.toString()
5147      })
5148    );
5149  }
5150  notifyApplicationAfterFrameLoad(frame) {
5151    return dispatch("turbo:frame-load", { target: frame });
5152  }
5153  notifyApplicationAfterFrameRender(fetchResponse, frame) {
5154    return dispatch("turbo:frame-render", {
5155      detail: { fetchResponse },
5156      target: frame,
5157      cancelable: true
5158    });
5159  }
5160  // Helpers
5161  submissionIsNavigatable(form, submitter2) {
5162    if (config.forms.mode == "off") {
5163      return false;
5164    } else {
5165      const submitterIsNavigatable = submitter2 ? this.elementIsNavigatable(submitter2) : true;
5166      if (config.forms.mode == "optin") {
5167        return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null;
5168      } else {
5169        return submitterIsNavigatable && this.elementIsNavigatable(form);
5170      }
5171    }
5172  }
5173  elementIsNavigatable(element) {
5174    const container = findClosestRecursively(element, "[data-turbo]");
5175    const withinFrame = findClosestRecursively(element, "turbo-frame");
5176    if (config.drive.enabled || withinFrame) {
5177      if (container) {
5178        return container.getAttribute("data-turbo") != "false";
5179      } else {
5180        return true;
5181      }
5182    } else {
5183      if (container) {
5184        return container.getAttribute("data-turbo") == "true";
5185      } else {
5186        return false;
5187      }
5188    }
5189  }
5190  // Private
5191  getActionForLink(link) {
5192    return getVisitAction(link) || "advance";
5193  }
5194  get snapshot() {
5195    return this.view.snapshot;
5196  }
5197};
5198function extendURLWithDeprecatedProperties(url) {
5199  Object.defineProperties(url, deprecatedLocationPropertyDescriptors);
5200}
5201var deprecatedLocationPropertyDescriptors = {
5202  absoluteURL: {
5203    get() {
5204      return this.toString();
5205    }
5206  }
5207};
5208var session = new Session(recentRequests);
5209var { cache, navigator: navigator$1 } = session;
5210function start() {
5211  session.start();
5212}
5213function registerAdapter(adapter) {
5214  session.registerAdapter(adapter);
5215}
5216function visit(location2, options) {
5217  session.visit(location2, options);
5218}
5219function connectStreamSource(source) {
5220  session.connectStreamSource(source);
5221}
5222function disconnectStreamSource(source) {
5223  session.disconnectStreamSource(source);
5224}
5225function renderStreamMessage(message) {
5226  session.renderStreamMessage(message);
5227}
5228function clearCache() {
5229  console.warn(
5230    "Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`"
5231  );
5232  session.clearCache();
5233}
5234function setProgressBarDelay(delay) {
5235  console.warn(
5236    "Please replace `Turbo.setProgressBarDelay(delay)` with `Turbo.config.drive.progressBarDelay = delay`. The top-level function is deprecated and will be removed in a future version of Turbo.`"
5237  );
5238  config.drive.progressBarDelay = delay;
5239}
5240function setConfirmMethod(confirmMethod) {
5241  console.warn(
5242    "Please replace `Turbo.setConfirmMethod(confirmMethod)` with `Turbo.config.forms.confirm = confirmMethod`. The top-level function is deprecated and will be removed in a future version of Turbo.`"
5243  );
5244  config.forms.confirm = confirmMethod;
5245}
5246function setFormMode(mode) {
5247  console.warn(
5248    "Please replace `Turbo.setFormMode(mode)` with `Turbo.config.forms.mode = mode`. The top-level function is deprecated and will be removed in a future version of Turbo.`"
5249  );
5250  config.forms.mode = mode;
5251}
5252var Turbo2 = /* @__PURE__ */ Object.freeze({
5253  __proto__: null,
5254  navigator: navigator$1,
5255  session,
5256  cache,
5257  PageRenderer,
5258  PageSnapshot,
5259  FrameRenderer,
5260  fetch: fetchWithTurboHeaders,
5261  config,
5262  start,
5263  registerAdapter,
5264  visit,
5265  connectStreamSource,
5266  disconnectStreamSource,
5267  renderStreamMessage,
5268  clearCache,
5269  setProgressBarDelay,
5270  setConfirmMethod,
5271  setFormMode
5272});
5273var TurboFrameMissingError = class extends Error {
5274};
5275var FrameController = class {
5276  fetchResponseLoaded = (_fetchResponse) => Promise.resolve();
5277  #currentFetchRequest = null;
5278  #resolveVisitPromise = () => {
5279  };
5280  #connected = false;
5281  #hasBeenLoaded = false;
5282  #ignoredAttributes = /* @__PURE__ */ new Set();
5283  #shouldMorphFrame = false;
5284  action = null;
5285  constructor(element) {
5286    this.element = element;
5287    this.view = new FrameView(this, this.element);
5288    this.appearanceObserver = new AppearanceObserver(this, this.element);
5289    this.formLinkClickObserver = new FormLinkClickObserver(this, this.element);
5290    this.linkInterceptor = new LinkInterceptor(this, this.element);
5291    this.restorationIdentifier = uuid();
5292    this.formSubmitObserver = new FormSubmitObserver(this, this.element);
5293  }
5294  // Frame delegate
5295  connect() {
5296    if (!this.#connected) {
5297      this.#connected = true;
5298      if (this.loadingStyle == FrameLoadingStyle.lazy) {
5299        this.appearanceObserver.start();
5300      } else {
5301        this.#loadSourceURL();
5302      }
5303      this.formLinkClickObserver.start();
5304      this.linkInterceptor.start();
5305      this.formSubmitObserver.start();
5306    }
5307  }
5308  disconnect() {
5309    if (this.#connected) {
5310      this.#connected = false;
5311      this.appearanceObserver.stop();
5312      this.formLinkClickObserver.stop();
5313      this.linkInterceptor.stop();
5314      this.formSubmitObserver.stop();
5315    }
5316  }
5317  disabledChanged() {
5318    if (this.loadingStyle == FrameLoadingStyle.eager) {
5319      this.#loadSourceURL();
5320    }
5321  }
5322  sourceURLChanged() {
5323    if (this.#isIgnoringChangesTo("src")) return;
5324    if (this.element.isConnected) {
5325      this.complete = false;
5326    }
5327    if (this.loadingStyle == FrameLoadingStyle.eager || this.#hasBeenLoaded) {
5328      this.#loadSourceURL();
5329    }
5330  }
5331  sourceURLReloaded() {
5332    const { refresh, src } = this.element;
5333    this.#shouldMorphFrame = src && refresh === "morph";
5334    this.element.removeAttribute("complete");
5335    this.element.src = null;
5336    this.element.src = src;
5337    return this.element.loaded;
5338  }
5339  loadingStyleChanged() {
5340    if (this.loadingStyle == FrameLoadingStyle.lazy) {
5341      this.appearanceObserver.start();
5342    } else {
5343      this.appearanceObserver.stop();
5344      this.#loadSourceURL();
5345    }
5346  }
5347  async #loadSourceURL() {
5348    if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
5349      this.element.loaded = this.#visit(expandURL(this.sourceURL));
5350      this.appearanceObserver.stop();
5351      await this.element.loaded;
5352      this.#hasBeenLoaded = true;
5353    }
5354  }
5355  async loadResponse(fetchResponse) {
5356    if (fetchResponse.redirected || fetchResponse.succeeded && fetchResponse.isHTML) {
5357      this.sourceURL = fetchResponse.response.url;
5358    }
5359    try {
5360      const html = await fetchResponse.responseHTML;
5361      if (html) {
5362        const document2 = parseHTMLDocument(html);
5363        const pageSnapshot = PageSnapshot.fromDocument(document2);
5364        if (pageSnapshot.isVisitable) {
5365          await this.#loadFrameResponse(fetchResponse, document2);
5366        } else {
5367          await this.#handleUnvisitableFrameResponse(fetchResponse);
5368        }
5369      }
5370    } finally {
5371      this.#shouldMorphFrame = false;
5372      this.fetchResponseLoaded = () => Promise.resolve();
5373    }
5374  }
5375  // Appearance observer delegate
5376  elementAppearedInViewport(element) {
5377    this.proposeVisitIfNavigatedWithAction(element, getVisitAction(element));
5378    this.#loadSourceURL();
5379  }
5380  // Form link click observer delegate
5381  willSubmitFormLinkToLocation(link) {
5382    return this.#shouldInterceptNavigation(link);
5383  }
5384  submittedFormLinkToLocation(link, _location, form) {
5385    const frame = this.#findFrameElement(link);
5386    if (frame) form.setAttribute("data-turbo-frame", frame.id);
5387  }
5388  // Link interceptor delegate
5389  shouldInterceptLinkClick(element, _location, _event) {
5390    return this.#shouldInterceptNavigation(element);
5391  }
5392  linkClickIntercepted(element, location2) {
5393    this.#navigateFrame(element, location2);
5394  }
5395  // Form submit observer delegate
5396  willSubmitForm(element, submitter2) {
5397    return element.closest("turbo-frame") == this.element && this.#shouldInterceptNavigation(element, submitter2);
5398  }
5399  formSubmitted(element, submitter2) {
5400    if (this.formSubmission) {
5401      this.formSubmission.stop();
5402    }
5403    this.formSubmission = new FormSubmission(this, element, submitter2);
5404    const { fetchRequest } = this.formSubmission;
5405    this.prepareRequest(fetchRequest);
5406    this.formSubmission.start();
5407  }
5408  // Fetch request delegate
5409  prepareRequest(request) {
5410    request.headers["Turbo-Frame"] = this.id;
5411    if (this.currentNavigationElement?.hasAttribute("data-turbo-stream")) {
5412      request.acceptResponseType(StreamMessage.contentType);
5413    }
5414  }
5415  requestStarted(_request) {
5416    markAsBusy(this.element);
5417  }
5418  requestPreventedHandlingResponse(_request, _response) {
5419    this.#resolveVisitPromise();
5420  }
5421  async requestSucceededWithResponse(request, response) {
5422    await this.loadResponse(response);
5423    this.#resolveVisitPromise();
5424  }
5425  async requestFailedWithResponse(request, response) {
5426    await this.loadResponse(response);
5427    this.#resolveVisitPromise();
5428  }
5429  requestErrored(request, error2) {
5430    console.error(error2);
5431    this.#resolveVisitPromise();
5432  }
5433  requestFinished(_request) {
5434    clearBusyState(this.element);
5435  }
5436  // Form submission delegate
5437  formSubmissionStarted({ formElement }) {
5438    markAsBusy(formElement, this.#findFrameElement(formElement));
5439  }
5440  formSubmissionSucceededWithResponse(formSubmission, response) {
5441    const frame = this.#findFrameElement(formSubmission.formElement, formSubmission.submitter);
5442    frame.delegate.proposeVisitIfNavigatedWithAction(frame, getVisitAction(formSubmission.submitter, formSubmission.formElement, frame));
5443    frame.delegate.loadResponse(response);
5444    if (!formSubmission.isSafe) {
5445      session.clearCache();
5446    }
5447  }
5448  formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
5449    this.element.delegate.loadResponse(fetchResponse);
5450    session.clearCache();
5451  }
5452  formSubmissionErrored(formSubmission, error2) {
5453    console.error(error2);
5454  }
5455  formSubmissionFinished({ formElement }) {
5456    clearBusyState(formElement, this.#findFrameElement(formElement));
5457  }
5458  // View delegate
5459  allowsImmediateRender({ element: newFrame }, options) {
5460    const event = dispatch("turbo:before-frame-render", {
5461      target: this.element,
5462      detail: { newFrame, ...options },
5463      cancelable: true
5464    });
5465    const {
5466      defaultPrevented,
5467      detail: { render }
5468    } = event;
5469    if (this.view.renderer && render) {
5470      this.view.renderer.renderElement = render;
5471    }
5472    return !defaultPrevented;
5473  }
5474  viewRenderedSnapshot(_snapshot, _isPreview, _renderMethod) {
5475  }
5476  preloadOnLoadLinksForView(element) {
5477    session.preloadOnLoadLinksForView(element);
5478  }
5479  viewInvalidated() {
5480  }
5481  // Frame renderer delegate
5482  willRenderFrame(currentElement, _newElement) {
5483    this.previousFrameElement = currentElement.cloneNode(true);
5484  }
5485  visitCachedSnapshot = ({ element }) => {
5486    const frame = element.querySelector("#" + this.element.id);
5487    if (frame && this.previousFrameElement) {
5488      frame.replaceChildren(...this.previousFrameElement.children);
5489    }
5490    delete this.previousFrameElement;
5491  };
5492  // Private
5493  async #loadFrameResponse(fetchResponse, document2) {
5494    const newFrameElement = await this.extractForeignFrameElement(document2.body);
5495    const rendererClass = this.#shouldMorphFrame ? MorphingFrameRenderer : FrameRenderer;
5496    if (newFrameElement) {
5497      const snapshot = new Snapshot(newFrameElement);
5498      const renderer = new rendererClass(this, this.view.snapshot, snapshot, false, false);
5499      if (this.view.renderPromise) await this.view.renderPromise;
5500      this.changeHistory();
5501      await this.view.render(renderer);
5502      this.complete = true;
5503      session.frameRendered(fetchResponse, this.element);
5504      session.frameLoaded(this.element);
5505      await this.fetchResponseLoaded(fetchResponse);
5506    } else if (this.#willHandleFrameMissingFromResponse(fetchResponse)) {
5507      this.#handleFrameMissingFromResponse(fetchResponse);
5508    }
5509  }
5510  async #visit(url) {
5511    const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
5512    this.#currentFetchRequest?.cancel();
5513    this.#currentFetchRequest = request;
5514    return new Promise((resolve) => {
5515      this.#resolveVisitPromise = () => {
5516        this.#resolveVisitPromise = () => {
5517        };
5518        this.#currentFetchRequest = null;
5519        resolve();
5520      };
5521      request.perform();
5522    });
5523  }
5524  #navigateFrame(element, url, submitter2) {
5525    const frame = this.#findFrameElement(element, submitter2);
5526    frame.delegate.proposeVisitIfNavigatedWithAction(frame, getVisitAction(submitter2, element, frame));
5527    this.#withCurrentNavigationElement(element, () => {
5528      frame.src = url;
5529    });
5530  }
5531  proposeVisitIfNavigatedWithAction(frame, action = null) {
5532    this.action = action;
5533    if (this.action) {
5534      const pageSnapshot = PageSnapshot.fromElement(frame).clone();
5535      const { visitCachedSnapshot } = frame.delegate;
5536      frame.delegate.fetchResponseLoaded = async (fetchResponse) => {
5537        if (frame.src) {
5538          const { statusCode, redirected } = fetchResponse;
5539          const responseHTML = await fetchResponse.responseHTML;
5540          const response = { statusCode, redirected, responseHTML };
5541          const options = {
5542            response,
5543            visitCachedSnapshot,
5544            willRender: false,
5545            updateHistory: false,
5546            restorationIdentifier: this.restorationIdentifier,
5547            snapshot: pageSnapshot
5548          };
5549          if (this.action) options.action = this.action;
5550          session.visit(frame.src, options);
5551        }
5552      };
5553    }
5554  }
5555  changeHistory() {
5556    if (this.action) {
5557      const method = getHistoryMethodForAction(this.action);
5558      session.history.update(method, expandURL(this.element.src || ""), this.restorationIdentifier);
5559    }
5560  }
5561  async #handleUnvisitableFrameResponse(fetchResponse) {
5562    console.warn(
5563      `The response (${fetchResponse.statusCode}) from <turbo-frame id="${this.element.id}"> is performing a full page visit due to turbo-visit-control.`
5564    );
5565    await this.#visitResponse(fetchResponse.response);
5566  }
5567  #willHandleFrameMissingFromResponse(fetchResponse) {
5568    this.element.setAttribute("complete", "");
5569    const response = fetchResponse.response;
5570    const visit2 = async (url, options) => {
5571      if (url instanceof Response) {
5572        this.#visitResponse(url);
5573      } else {
5574        session.visit(url, options);
5575      }
5576    };
5577    const event = dispatch("turbo:frame-missing", {
5578      target: this.element,
5579      detail: { response, visit: visit2 },
5580      cancelable: true
5581    });
5582    return !event.defaultPrevented;
5583  }
5584  #handleFrameMissingFromResponse(fetchResponse) {
5585    this.view.missing();
5586    this.#throwFrameMissingError(fetchResponse);
5587  }
5588  #throwFrameMissingError(fetchResponse) {
5589    const message = `The response (${fetchResponse.statusCode}) did not contain the expected <turbo-frame id="${this.element.id}"> and will be ignored. To perform a full page visit instead, set turbo-visit-control to reload.`;
5590    throw new TurboFrameMissingError(message);
5591  }
5592  async #visitResponse(response) {
5593    const wrapped = new FetchResponse(response);
5594    const responseHTML = await wrapped.responseHTML;
5595    const { location: location2, redirected, statusCode } = wrapped;
5596    return session.visit(location2, { response: { redirected, statusCode, responseHTML } });
5597  }
5598  #findFrameElement(element, submitter2) {
5599    const id = getAttribute("data-turbo-frame", submitter2, element) || this.element.getAttribute("target");
5600    return getFrameElementById(id) ?? this.element;
5601  }
5602  async extractForeignFrameElement(container) {
5603    let element;
5604    const id = CSS.escape(this.id);
5605    try {
5606      element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
5607      if (element) {
5608        return element;
5609      }
5610      element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL);
5611      if (element) {
5612        await element.loaded;
5613        return await this.extractForeignFrameElement(element);
5614      }
5615    } catch (error2) {
5616      console.error(error2);
5617      return new FrameElement();
5618    }
5619    return null;
5620  }
5621  #formActionIsVisitable(form, submitter2) {
5622    const action = getAction$1(form, submitter2);
5623    return locationIsVisitable(expandURL(action), this.rootLocation);
5624  }
5625  #shouldInterceptNavigation(element, submitter2) {
5626    const id = getAttribute("data-turbo-frame", submitter2, element) || this.element.getAttribute("target");
5627    if (element instanceof HTMLFormElement && !this.#formActionIsVisitable(element, submitter2)) {
5628      return false;
5629    }
5630    if (!this.enabled || id == "_top") {
5631      return false;
5632    }
5633    if (id) {
5634      const frameElement = getFrameElementById(id);
5635      if (frameElement) {
5636        return !frameElement.disabled;
5637      }
5638    }
5639    if (!session.elementIsNavigatable(element)) {
5640      return false;
5641    }
5642    if (submitter2 && !session.elementIsNavigatable(submitter2)) {
5643      return false;
5644    }
5645    return true;
5646  }
5647  // Computed properties
5648  get id() {
5649    return this.element.id;
5650  }
5651  get enabled() {
5652    return !this.element.disabled;
5653  }
5654  get sourceURL() {
5655    if (this.element.src) {
5656      return this.element.src;
5657    }
5658  }
5659  set sourceURL(sourceURL) {
5660    this.#ignoringChangesToAttribute("src", () => {
5661      this.element.src = sourceURL ?? null;
5662    });
5663  }
5664  get loadingStyle() {
5665    return this.element.loading;
5666  }
5667  get isLoading() {
5668    return this.formSubmission !== void 0 || this.#resolveVisitPromise() !== void 0;
5669  }
5670  get complete() {
5671    return this.element.hasAttribute("complete");
5672  }
5673  set complete(value) {
5674    if (value) {
5675      this.element.setAttribute("complete", "");
5676    } else {
5677      this.element.removeAttribute("complete");
5678    }
5679  }
5680  get isActive() {
5681    return this.element.isActive && this.#connected;
5682  }
5683  get rootLocation() {
5684    const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
5685    const root = meta?.content ?? "/";
5686    return expandURL(root);
5687  }
5688  #isIgnoringChangesTo(attributeName) {
5689    return this.#ignoredAttributes.has(attributeName);
5690  }
5691  #ignoringChangesToAttribute(attributeName, callback) {
5692    this.#ignoredAttributes.add(attributeName);
5693    callback();
5694    this.#ignoredAttributes.delete(attributeName);
5695  }
5696  #withCurrentNavigationElement(element, callback) {
5697    this.currentNavigationElement = element;
5698    callback();
5699    delete this.currentNavigationElement;
5700  }
5701};
5702function getFrameElementById(id) {
5703  if (id != null) {
5704    const element = document.getElementById(id);
5705    if (element instanceof FrameElement) {
5706      return element;
5707    }
5708  }
5709}
5710function activateElement(element, currentURL) {
5711  if (element) {
5712    const src = element.getAttribute("src");
5713    if (src != null && currentURL != null && urlsAreEqual(src, currentURL)) {
5714      throw new Error(`Matching <turbo-frame id="${element.id}"> element has a source URL which references itself`);
5715    }
5716    if (element.ownerDocument !== document) {
5717      element = document.importNode(element, true);
5718    }
5719    if (element instanceof FrameElement) {
5720      element.connectedCallback();
5721      element.disconnectedCallback();
5722      return element;
5723    }
5724  }
5725}
5726var StreamActions = {
5727  after() {
5728    this.targetElements.forEach((e) => e.parentElement?.insertBefore(this.templateContent, e.nextSibling));
5729  },
5730  append() {
5731    this.removeDuplicateTargetChildren();
5732    this.targetElements.forEach((e) => e.append(this.templateContent));
5733  },
5734  before() {
5735    this.targetElements.forEach((e) => e.parentElement?.insertBefore(this.templateContent, e));
5736  },
5737  prepend() {
5738    this.removeDuplicateTargetChildren();
5739    this.targetElements.forEach((e) => e.prepend(this.templateContent));
5740  },
5741  remove() {
5742    this.targetElements.forEach((e) => e.remove());
5743  },
5744  replace() {
5745    const method = this.getAttribute("method");
5746    this.targetElements.forEach((targetElement) => {
5747      if (method === "morph") {
5748        morphElements(targetElement, this.templateContent);
5749      } else {
5750        targetElement.replaceWith(this.templateContent);
5751      }
5752    });
5753  },
5754  update() {
5755    const method = this.getAttribute("method");
5756    this.targetElements.forEach((targetElement) => {
5757      if (method === "morph") {
5758        morphChildren(targetElement, this.templateContent);
5759      } else {
5760        targetElement.innerHTML = "";
5761        targetElement.append(this.templateContent);
5762      }
5763    });
5764  },
5765  refresh() {
5766    session.refresh(this.baseURI, this.requestId);
5767  }
5768};
5769var StreamElement = class _StreamElement extends HTMLElement {
5770  static async renderElement(newElement) {
5771    await newElement.performAction();
5772  }
5773  async connectedCallback() {
5774    try {
5775      await this.render();
5776    } catch (error2) {
5777      console.error(error2);
5778    } finally {
5779      this.disconnect();
5780    }
5781  }
5782  async render() {
5783    return this.renderPromise ??= (async () => {
5784      const event = this.beforeRenderEvent;
5785      if (this.dispatchEvent(event)) {
5786        await nextRepaint();
5787        await event.detail.render(this);
5788      }
5789    })();
5790  }
5791  disconnect() {
5792    try {
5793      this.remove();
5794    } catch {
5795    }
5796  }
5797  /**
5798   * Removes duplicate children (by ID)
5799   */
5800  removeDuplicateTargetChildren() {
5801    this.duplicateChildren.forEach((c) => c.remove());
5802  }
5803  /**
5804   * Gets the list of duplicate children (i.e. those with the same ID)
5805   */
5806  get duplicateChildren() {
5807    const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.getAttribute("id"));
5808    const newChildrenIds = [...this.templateContent?.children || []].filter((c) => !!c.getAttribute("id")).map((c) => c.getAttribute("id"));
5809    return existingChildren.filter((c) => newChildrenIds.includes(c.getAttribute("id")));
5810  }
5811  /**
5812   * Gets the action function to be performed.
5813   */
5814  get performAction() {
5815    if (this.action) {
5816      const actionFunction = StreamActions[this.action];
5817      if (actionFunction) {
5818        return actionFunction;
5819      }
5820      this.#raise("unknown action");
5821    }
5822    this.#raise("action attribute is missing");
5823  }
5824  /**
5825   * Gets the target elements which the template will be rendered to.
5826   */
5827  get targetElements() {
5828    if (this.target) {
5829      return this.targetElementsById;
5830    } else if (this.targets) {
5831      return this.targetElementsByQuery;
5832    } else {
5833      this.#raise("target or targets attribute is missing");
5834    }
5835  }
5836  /**
5837   * Gets the contents of the main `<template>`.
5838   */
5839  get templateContent() {
5840    return this.templateElement.content.cloneNode(true);
5841  }
5842  /**
5843   * Gets the main `<template>` used for rendering
5844   */
5845  get templateElement() {
5846    if (this.firstElementChild === null) {
5847      const template = this.ownerDocument.createElement("template");
5848      this.appendChild(template);
5849      return template;
5850    } else if (this.firstElementChild instanceof HTMLTemplateElement) {
5851      return this.firstElementChild;
5852    }
5853    this.#raise("first child element must be a <template> element");
5854  }
5855  /**
5856   * Gets the current action.
5857   */
5858  get action() {
5859    return this.getAttribute("action");
5860  }
5861  /**
5862   * Gets the current target (an element ID) to which the result will
5863   * be rendered.
5864   */
5865  get target() {
5866    return this.getAttribute("target");
5867  }
5868  /**
5869   * Gets the current "targets" selector (a CSS selector)
5870   */
5871  get targets() {
5872    return this.getAttribute("targets");
5873  }
5874  /**
5875   * Reads the request-id attribute
5876   */
5877  get requestId() {
5878    return this.getAttribute("request-id");
5879  }
5880  #raise(message) {
5881    throw new Error(`${this.description}: ${message}`);
5882  }
5883  get description() {
5884    return (this.outerHTML.match(/<[^>]+>/) ?? [])[0] ?? "<turbo-stream>";
5885  }
5886  get beforeRenderEvent() {
5887    return new CustomEvent("turbo:before-stream-render", {
5888      bubbles: true,
5889      cancelable: true,
5890      detail: { newStream: this, render: _StreamElement.renderElement }
5891    });
5892  }
5893  get targetElementsById() {
5894    const element = this.ownerDocument?.getElementById(this.target);
5895    if (element !== null) {
5896      return [element];
5897    } else {
5898      return [];
5899    }
5900  }
5901  get targetElementsByQuery() {
5902    const elements = this.ownerDocument?.querySelectorAll(this.targets);
5903    if (elements.length !== 0) {
5904      return Array.prototype.slice.call(elements);
5905    } else {
5906      return [];
5907    }
5908  }
5909};
5910var StreamSourceElement = class extends HTMLElement {
5911  streamSource = null;
5912  connectedCallback() {
5913    this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
5914    connectStreamSource(this.streamSource);
5915  }
5916  disconnectedCallback() {
5917    if (this.streamSource) {
5918      this.streamSource.close();
5919      disconnectStreamSource(this.streamSource);
5920    }
5921  }
5922  get src() {
5923    return this.getAttribute("src") || "";
5924  }
5925};
5926FrameElement.delegateConstructor = FrameController;
5927if (customElements.get("turbo-frame") === void 0) {
5928  customElements.define("turbo-frame", FrameElement);
5929}
5930if (customElements.get("turbo-stream") === void 0) {
5931  customElements.define("turbo-stream", StreamElement);
5932}
5933if (customElements.get("turbo-stream-source") === void 0) {
5934  customElements.define("turbo-stream-source", StreamSourceElement);
5935}
5936(() => {
5937  let element = document.currentScript;
5938  if (!element) return;
5939  if (element.hasAttribute("data-turbo-suppress-warning")) return;
5940  element = element.parentElement;
5941  while (element) {
5942    if (element == document.body) {
5943      return console.warn(
5944        unindent`
5945        You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
5946
5947        Load your application’s JavaScript bundle inside the <head> element instead. <script> elements in <body> are evaluated with each page change.
5948
5949        For more information, see: https://turbo.hotwired.dev/handbook/building#working-with-script-elements
5950
5951        ——
5952        Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
5953      `,
5954        element.outerHTML
5955      );
5956    }
5957    element = element.parentElement;
5958  }
5959})();
5960window.Turbo = { ...Turbo2, StreamActions };
5961start();
5962
vendor: 575 bytes, lines 5963-5979
5963// node_modules/@hotwired/turbo-rails/app/javascript/turbo/cable.js
5964var consumer;
5965async function getConsumer() {
5966  return consumer || setConsumer(createConsumer2().then(setConsumer));
5967}
5968function setConsumer(newConsumer) {
5969  return consumer = newConsumer;
5970}
5971async function createConsumer2() {
5972  const { createConsumer: createConsumer3 } = await Promise.resolve().then(() => (init_src(), src_exports));
5973  return createConsumer3();
5974}
5975async function subscribeTo(channel, mixin) {
5976  const { subscriptions } = await getConsumer();
5977  return subscriptions.create(channel, mixin);
5978}
5979
vendor: 505 bytes, lines 5980-5993
5980// node_modules/@hotwired/turbo-rails/app/javascript/turbo/snakeize.js
5981function walk(obj) {
5982  if (!obj || typeof obj !== "object") return obj;
5983  if (obj instanceof Date || obj instanceof RegExp) return obj;
5984  if (Array.isArray(obj)) return obj.map(walk);
5985  return Object.keys(obj).reduce(function(acc, key) {
5986    var camel = key[0].toLowerCase() + key.slice(1).replace(/([A-Z]+)/g, function(m, x) {
5987      return "_" + x.toLowerCase();
5988    });
5989    acc[camel] = walk(obj[key]);
5990    return acc;
5991  }, {});
5992}
5993
vendor: 1,471 bytes, lines 5994-6035
5994// node_modules/@hotwired/turbo-rails/app/javascript/turbo/cable_stream_source_element.js
5995var TurboCableStreamSourceElement = class extends HTMLElement {
5996  static observedAttributes = ["channel", "signed-stream-name"];
5997  async connectedCallback() {
5998    connectStreamSource(this);
5999    this.subscription = await subscribeTo(this.channel, {
6000      received: this.dispatchMessageEvent.bind(this),
6001      connected: this.subscriptionConnected.bind(this),
6002      disconnected: this.subscriptionDisconnected.bind(this)
6003    });
6004  }
6005  disconnectedCallback() {
6006    disconnectStreamSource(this);
6007    if (this.subscription) this.subscription.unsubscribe();
6008    this.subscriptionDisconnected();
6009  }
6010  attributeChangedCallback() {
6011    if (this.subscription) {
6012      this.disconnectedCallback();
6013      this.connectedCallback();
6014    }
6015  }
6016  dispatchMessageEvent(data) {
6017    const event = new MessageEvent("message", { data });
6018    return this.dispatchEvent(event);
6019  }
6020  subscriptionConnected() {
6021    this.setAttribute("connected", "");
6022  }
6023  subscriptionDisconnected() {
6024    this.removeAttribute("connected");
6025  }
6026  get channel() {
6027    const channel = this.getAttribute("channel");
6028    const signed_stream_name = this.getAttribute("signed-stream-name");
6029    return { channel, signed_stream_name, ...walk({ ...this.dataset }) };
6030  }
6031};
6032if (customElements.get("turbo-cable-stream-source") === void 0) {
6033  customElements.define("turbo-cable-stream-source", TurboCableStreamSourceElement);
6034}
6035
vendor: 1,611 bytes, lines 6036-6082
6036// node_modules/@hotwired/turbo-rails/app/javascript/turbo/fetch_requests.js
6037function encodeMethodIntoRequestBody(event) {
6038  if (event.target instanceof HTMLFormElement) {
6039    const { target: form, detail: { fetchOptions } } = event;
6040    form.addEventListener("turbo:submit-start", ({ detail: { formSubmission: { submitter: submitter2 } } }) => {
6041      const body = isBodyInit(fetchOptions.body) ? fetchOptions.body : new URLSearchParams();
6042      const method = determineFetchMethod(submitter2, body, form);
6043      if (!/get/i.test(method)) {
6044        if (/post/i.test(method)) {
6045          body.delete("_method");
6046        } else {
6047          body.set("_method", method);
6048        }
6049        fetchOptions.method = "post";
6050      }
6051    }, { once: true });
6052  }
6053}
6054function determineFetchMethod(submitter2, body, form) {
6055  const formMethod = determineFormMethod(submitter2);
6056  const overrideMethod = body.get("_method");
6057  const method = form.getAttribute("method") || "get";
6058  if (typeof formMethod == "string") {
6059    return formMethod;
6060  } else if (typeof overrideMethod == "string") {
6061    return overrideMethod;
6062  } else {
6063    return method;
6064  }
6065}
6066function determineFormMethod(submitter2) {
6067  if (submitter2 instanceof HTMLButtonElement || submitter2 instanceof HTMLInputElement) {
6068    if (submitter2.name === "_method") {
6069      return submitter2.value;
6070    } else if (submitter2.hasAttribute("formmethod")) {
6071      return submitter2.formMethod;
6072    } else {
6073      return null;
6074    }
6075  } else {
6076    return null;
6077  }
6078}
6079function isBodyInit(body) {
6080  return body instanceof FormData || body instanceof URLSearchParams;
6081}
6082
vendor: 187 bytes, lines 6083-6086
6083// node_modules/@hotwired/turbo-rails/app/javascript/turbo/index.js
6084window.Turbo = turbo_es2017_esm_exports;
6085addEventListener("turbo:before-fetch-request", encodeMethodIntoRequestBody);
6086
vendor: 79,408 bytes, lines 6087-8534
6087// node_modules/@hotwired/stimulus/dist/stimulus.js
6088var EventListener = class {
6089  constructor(eventTarget, eventName, eventOptions) {
6090    this.eventTarget = eventTarget;
6091    this.eventName = eventName;
6092    this.eventOptions = eventOptions;
6093    this.unorderedBindings = /* @__PURE__ */ new Set();
6094  }
6095  connect() {
6096    this.eventTarget.addEventListener(this.eventName, this, this.eventOptions);
6097  }
6098  disconnect() {
6099    this.eventTarget.removeEventListener(this.eventName, this, this.eventOptions);
6100  }
6101  bindingConnected(binding) {
6102    this.unorderedBindings.add(binding);
6103  }
6104  bindingDisconnected(binding) {
6105    this.unorderedBindings.delete(binding);
6106  }
6107  handleEvent(event) {
6108    const extendedEvent = extendEvent(event);
6109    for (const binding of this.bindings) {
6110      if (extendedEvent.immediatePropagationStopped) {
6111        break;
6112      } else {
6113        binding.handleEvent(extendedEvent);
6114      }
6115    }
6116  }
6117  hasBindings() {
6118    return this.unorderedBindings.size > 0;
6119  }
6120  get bindings() {
6121    return Array.from(this.unorderedBindings).sort((left, right) => {
6122      const leftIndex = left.index, rightIndex = right.index;
6123      return leftIndex < rightIndex ? -1 : leftIndex > rightIndex ? 1 : 0;
6124    });
6125  }
6126};
6127function extendEvent(event) {
6128  if ("immediatePropagationStopped" in event) {
6129    return event;
6130  } else {
6131    const { stopImmediatePropagation } = event;
6132    return Object.assign(event, {
6133      immediatePropagationStopped: false,
6134      stopImmediatePropagation() {
6135        this.immediatePropagationStopped = true;
6136        stopImmediatePropagation.call(this);
6137      }
6138    });
6139  }
6140}
6141var Dispatcher = class {
6142  constructor(application2) {
6143    this.application = application2;
6144    this.eventListenerMaps = /* @__PURE__ */ new Map();
6145    this.started = false;
6146  }
6147  start() {
6148    if (!this.started) {
6149      this.started = true;
6150      this.eventListeners.forEach((eventListener) => eventListener.connect());
6151    }
6152  }
6153  stop() {
6154    if (this.started) {
6155      this.started = false;
6156      this.eventListeners.forEach((eventListener) => eventListener.disconnect());
6157    }
6158  }
6159  get eventListeners() {
6160    return Array.from(this.eventListenerMaps.values()).reduce((listeners, map) => listeners.concat(Array.from(map.values())), []);
6161  }
6162  bindingConnected(binding) {
6163    this.fetchEventListenerForBinding(binding).bindingConnected(binding);
6164  }
6165  bindingDisconnected(binding, clearEventListeners = false) {
6166    this.fetchEventListenerForBinding(binding).bindingDisconnected(binding);
6167    if (clearEventListeners)
6168      this.clearEventListenersForBinding(binding);
6169  }
6170  handleError(error2, message, detail = {}) {
6171    this.application.handleError(error2, `Error ${message}`, detail);
6172  }
6173  clearEventListenersForBinding(binding) {
6174    const eventListener = this.fetchEventListenerForBinding(binding);
6175    if (!eventListener.hasBindings()) {
6176      eventListener.disconnect();
6177      this.removeMappedEventListenerFor(binding);
6178    }
6179  }
6180  removeMappedEventListenerFor(binding) {
6181    const { eventTarget, eventName, eventOptions } = binding;
6182    const eventListenerMap = this.fetchEventListenerMapForEventTarget(eventTarget);
6183    const cacheKey = this.cacheKey(eventName, eventOptions);
6184    eventListenerMap.delete(cacheKey);
6185    if (eventListenerMap.size == 0)
6186      this.eventListenerMaps.delete(eventTarget);
6187  }
6188  fetchEventListenerForBinding(binding) {
6189    const { eventTarget, eventName, eventOptions } = binding;
6190    return this.fetchEventListener(eventTarget, eventName, eventOptions);
6191  }
6192  fetchEventListener(eventTarget, eventName, eventOptions) {
6193    const eventListenerMap = this.fetchEventListenerMapForEventTarget(eventTarget);
6194    const cacheKey = this.cacheKey(eventName, eventOptions);
6195    let eventListener = eventListenerMap.get(cacheKey);
6196    if (!eventListener) {
6197      eventListener = this.createEventListener(eventTarget, eventName, eventOptions);
6198      eventListenerMap.set(cacheKey, eventListener);
6199    }
6200    return eventListener;
6201  }
6202  createEventListener(eventTarget, eventName, eventOptions) {
6203    const eventListener = new EventListener(eventTarget, eventName, eventOptions);
6204    if (this.started) {
6205      eventListener.connect();
6206    }
6207    return eventListener;
6208  }
6209  fetchEventListenerMapForEventTarget(eventTarget) {
6210    let eventListenerMap = this.eventListenerMaps.get(eventTarget);
6211    if (!eventListenerMap) {
6212      eventListenerMap = /* @__PURE__ */ new Map();
6213      this.eventListenerMaps.set(eventTarget, eventListenerMap);
6214    }
6215    return eventListenerMap;
6216  }
6217  cacheKey(eventName, eventOptions) {
6218    const parts = [eventName];
6219    Object.keys(eventOptions).sort().forEach((key) => {
6220      parts.push(`${eventOptions[key] ? "" : "!"}${key}`);
6221    });
6222    return parts.join(":");
6223  }
6224};
6225var defaultActionDescriptorFilters = {
6226  stop({ event, value }) {
6227    if (value)
6228      event.stopPropagation();
6229    return true;
6230  },
6231  prevent({ event, value }) {
6232    if (value)
6233      event.preventDefault();
6234    return true;
6235  },
6236  self({ event, value, element }) {
6237    if (value) {
6238      return element === event.target;
6239    } else {
6240      return true;
6241    }
6242  }
6243};
6244var descriptorPattern = /^(?:(?:([^.]+?)\+)?(.+?)(?:\.(.+?))?(?:@(window|document))?->)?(.+?)(?:#([^:]+?))(?::(.+))?$/;
6245function parseActionDescriptorString(descriptorString) {
6246  const source = descriptorString.trim();
6247  const matches = source.match(descriptorPattern) || [];
6248  let eventName = matches[2];
6249  let keyFilter = matches[3];
6250  if (keyFilter && !["keydown", "keyup", "keypress"].includes(eventName)) {
6251    eventName += `.${keyFilter}`;
6252    keyFilter = "";
6253  }
6254  return {
6255    eventTarget: parseEventTarget(matches[4]),
6256    eventName,
6257    eventOptions: matches[7] ? parseEventOptions(matches[7]) : {},
6258    identifier: matches[5],
6259    methodName: matches[6],
6260    keyFilter: matches[1] || keyFilter
6261  };
6262}
6263function parseEventTarget(eventTargetName) {
6264  if (eventTargetName == "window") {
6265    return window;
6266  } else if (eventTargetName == "document") {
6267    return document;
6268  }
6269}
6270function parseEventOptions(eventOptions) {
6271  return eventOptions.split(":").reduce((options, token) => Object.assign(options, { [token.replace(/^!/, "")]: !/^!/.test(token) }), {});
6272}
6273function stringifyEventTarget(eventTarget) {
6274  if (eventTarget == window) {
6275    return "window";
6276  } else if (eventTarget == document) {
6277    return "document";
6278  }
6279}
6280function camelize(value) {
6281  return value.replace(/(?:[_-])([a-z0-9])/g, (_, char) => char.toUpperCase());
6282}
6283function namespaceCamelize(value) {
6284  return camelize(value.replace(/--/g, "-").replace(/__/g, "_"));
6285}
6286function capitalize(value) {
6287  return value.charAt(0).toUpperCase() + value.slice(1);
6288}
6289function dasherize(value) {
6290  return value.replace(/([A-Z])/g, (_, char) => `-${char.toLowerCase()}`);
6291}
6292function tokenize(value) {
6293  return value.match(/[^\s]+/g) || [];
6294}
6295function isSomething(object) {
6296  return object !== null && object !== void 0;
6297}
6298function hasProperty(object, property) {
6299  return Object.prototype.hasOwnProperty.call(object, property);
6300}
6301var allModifiers = ["meta", "ctrl", "alt", "shift"];
6302var Action = class {
6303  constructor(element, index, descriptor, schema) {
6304    this.element = element;
6305    this.index = index;
6306    this.eventTarget = descriptor.eventTarget || element;
6307    this.eventName = descriptor.eventName || getDefaultEventNameForElement(element) || error("missing event name");
6308    this.eventOptions = descriptor.eventOptions || {};
6309    this.identifier = descriptor.identifier || error("missing identifier");
6310    this.methodName = descriptor.methodName || error("missing method name");
6311    this.keyFilter = descriptor.keyFilter || "";
6312    this.schema = schema;
6313  }
6314  static forToken(token, schema) {
6315    return new this(token.element, token.index, parseActionDescriptorString(token.content), schema);
6316  }
6317  toString() {
6318    const eventFilter = this.keyFilter ? `.${this.keyFilter}` : "";
6319    const eventTarget = this.eventTargetName ? `@${this.eventTargetName}` : "";
6320    return `${this.eventName}${eventFilter}${eventTarget}->${this.identifier}#${this.methodName}`;
6321  }
6322  shouldIgnoreKeyboardEvent(event) {
6323    if (!this.keyFilter) {
6324      return false;
6325    }
6326    const filters = this.keyFilter.split("+");
6327    if (this.keyFilterDissatisfied(event, filters)) {
6328      return true;
6329    }
6330    const standardFilter = filters.filter((key) => !allModifiers.includes(key))[0];
6331    if (!standardFilter) {
6332      return false;
6333    }
6334    if (!hasProperty(this.keyMappings, standardFilter)) {
6335      error(`contains unknown key filter: ${this.keyFilter}`);
6336    }
6337    return this.keyMappings[standardFilter].toLowerCase() !== event.key.toLowerCase();
6338  }
6339  shouldIgnoreMouseEvent(event) {
6340    if (!this.keyFilter) {
6341      return false;
6342    }
6343    const filters = [this.keyFilter];
6344    if (this.keyFilterDissatisfied(event, filters)) {
6345      return true;
6346    }
6347    return false;
6348  }
6349  get params() {
6350    const params = {};
6351    const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`, "i");
6352    for (const { name, value } of Array.from(this.element.attributes)) {
6353      const match = name.match(pattern);
6354      const key = match && match[1];
6355      if (key) {
6356        params[camelize(key)] = typecast(value);
6357      }
6358    }
6359    return params;
6360  }
6361  get eventTargetName() {
6362    return stringifyEventTarget(this.eventTarget);
6363  }
6364  get keyMappings() {
6365    return this.schema.keyMappings;
6366  }
6367  keyFilterDissatisfied(event, filters) {
6368    const [meta, ctrl, alt, shift] = allModifiers.map((modifier) => filters.includes(modifier));
6369    return event.metaKey !== meta || event.ctrlKey !== ctrl || event.altKey !== alt || event.shiftKey !== shift;
6370  }
6371};
6372var defaultEventNames = {
6373  a: () => "click",
6374  button: () => "click",
6375  form: () => "submit",
6376  details: () => "toggle",
6377  input: (e) => e.getAttribute("type") == "submit" ? "click" : "input",
6378  select: () => "change",
6379  textarea: () => "input"
6380};
6381function getDefaultEventNameForElement(element) {
6382  const tagName = element.tagName.toLowerCase();
6383  if (tagName in defaultEventNames) {
6384    return defaultEventNames[tagName](element);
6385  }
6386}
6387function error(message) {
6388  throw new Error(message);
6389}
6390function typecast(value) {
6391  try {
6392    return JSON.parse(value);
6393  } catch (o_O) {
6394    return value;
6395  }
6396}
6397var Binding = class {
6398  constructor(context, action) {
6399    this.context = context;
6400    this.action = action;
6401  }
6402  get index() {
6403    return this.action.index;
6404  }
6405  get eventTarget() {
6406    return this.action.eventTarget;
6407  }
6408  get eventOptions() {
6409    return this.action.eventOptions;
6410  }
6411  get identifier() {
6412    return this.context.identifier;
6413  }
6414  handleEvent(event) {
6415    const actionEvent = this.prepareActionEvent(event);
6416    if (this.willBeInvokedByEvent(event) && this.applyEventModifiers(actionEvent)) {
6417      this.invokeWithEvent(actionEvent);
6418    }
6419  }
6420  get eventName() {
6421    return this.action.eventName;
6422  }
6423  get method() {
6424    const method = this.controller[this.methodName];
6425    if (typeof method == "function") {
6426      return method;
6427    }
6428    throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`);
6429  }
6430  applyEventModifiers(event) {
6431    const { element } = this.action;
6432    const { actionDescriptorFilters } = this.context.application;
6433    const { controller } = this.context;
6434    let passes = true;
6435    for (const [name, value] of Object.entries(this.eventOptions)) {
6436      if (name in actionDescriptorFilters) {
6437        const filter = actionDescriptorFilters[name];
6438        passes = passes && filter({ name, value, event, element, controller });
6439      } else {
6440        continue;
6441      }
6442    }
6443    return passes;
6444  }
6445  prepareActionEvent(event) {
6446    return Object.assign(event, { params: this.action.params });
6447  }
6448  invokeWithEvent(event) {
6449    const { target, currentTarget } = event;
6450    try {
6451      this.method.call(this.controller, event);
6452      this.context.logDebugActivity(this.methodName, { event, target, currentTarget, action: this.methodName });
6453    } catch (error2) {
6454      const { identifier, controller, element, index } = this;
6455      const detail = { identifier, controller, element, index, event };
6456      this.context.handleError(error2, `invoking action "${this.action}"`, detail);
6457    }
6458  }
6459  willBeInvokedByEvent(event) {
6460    const eventTarget = event.target;
6461    if (event instanceof KeyboardEvent && this.action.shouldIgnoreKeyboardEvent(event)) {
6462      return false;
6463    }
6464    if (event instanceof MouseEvent && this.action.shouldIgnoreMouseEvent(event)) {
6465      return false;
6466    }
6467    if (this.element === eventTarget) {
6468      return true;
6469    } else if (eventTarget instanceof Element && this.element.contains(eventTarget)) {
6470      return this.scope.containsElement(eventTarget);
6471    } else {
6472      return this.scope.containsElement(this.action.element);
6473    }
6474  }
6475  get controller() {
6476    return this.context.controller;
6477  }
6478  get methodName() {
6479    return this.action.methodName;
6480  }
6481  get element() {
6482    return this.scope.element;
6483  }
6484  get scope() {
6485    return this.context.scope;
6486  }
6487};
6488var ElementObserver = class {
6489  constructor(element, delegate) {
6490    this.mutationObserverInit = { attributes: true, childList: true, subtree: true };
6491    this.element = element;
6492    this.started = false;
6493    this.delegate = delegate;
6494    this.elements = /* @__PURE__ */ new Set();
6495    this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations));
6496  }
6497  start() {
6498    if (!this.started) {
6499      this.started = true;
6500      this.mutationObserver.observe(this.element, this.mutationObserverInit);
6501      this.refresh();
6502    }
6503  }
6504  pause(callback) {
6505    if (this.started) {
6506      this.mutationObserver.disconnect();
6507      this.started = false;
6508    }
6509    callback();
6510    if (!this.started) {
6511      this.mutationObserver.observe(this.element, this.mutationObserverInit);
6512      this.started = true;
6513    }
6514  }
6515  stop() {
6516    if (this.started) {
6517      this.mutationObserver.takeRecords();
6518      this.mutationObserver.disconnect();
6519      this.started = false;
6520    }
6521  }
6522  refresh() {
6523    if (this.started) {
6524      const matches = new Set(this.matchElementsInTree());
6525      for (const element of Array.from(this.elements)) {
6526        if (!matches.has(element)) {
6527          this.removeElement(element);
6528        }
6529      }
6530      for (const element of Array.from(matches)) {
6531        this.addElement(element);
6532      }
6533    }
6534  }
6535  processMutations(mutations) {
6536    if (this.started) {
6537      for (const mutation of mutations) {
6538        this.processMutation(mutation);
6539      }
6540    }
6541  }
6542  processMutation(mutation) {
6543    if (mutation.type == "attributes") {
6544      this.processAttributeChange(mutation.target, mutation.attributeName);
6545    } else if (mutation.type == "childList") {
6546      this.processRemovedNodes(mutation.removedNodes);
6547      this.processAddedNodes(mutation.addedNodes);
6548    }
6549  }
6550  processAttributeChange(element, attributeName) {
6551    if (this.elements.has(element)) {
6552      if (this.delegate.elementAttributeChanged && this.matchElement(element)) {
6553        this.delegate.elementAttributeChanged(element, attributeName);
6554      } else {
6555        this.removeElement(element);
6556      }
6557    } else if (this.matchElement(element)) {
6558      this.addElement(element);
6559    }
6560  }
6561  processRemovedNodes(nodes) {
6562    for (const node of Array.from(nodes)) {
6563      const element = this.elementFromNode(node);
6564      if (element) {
6565        this.processTree(element, this.removeElement);
6566      }
6567    }
6568  }
6569  processAddedNodes(nodes) {
6570    for (const node of Array.from(nodes)) {
6571      const element = this.elementFromNode(node);
6572      if (element && this.elementIsActive(element)) {
6573        this.processTree(element, this.addElement);
6574      }
6575    }
6576  }
6577  matchElement(element) {
6578    return this.delegate.matchElement(element);
6579  }
6580  matchElementsInTree(tree = this.element) {
6581    return this.delegate.matchElementsInTree(tree);
6582  }
6583  processTree(tree, processor) {
6584    for (const element of this.matchElementsInTree(tree)) {
6585      processor.call(this, element);
6586    }
6587  }
6588  elementFromNode(node) {
6589    if (node.nodeType == Node.ELEMENT_NODE) {
6590      return node;
6591    }
6592  }
6593  elementIsActive(element) {
6594    if (element.isConnected != this.element.isConnected) {
6595      return false;
6596    } else {
6597      return this.element.contains(element);
6598    }
6599  }
6600  addElement(element) {
6601    if (!this.elements.has(element)) {
6602      if (this.elementIsActive(element)) {
6603        this.elements.add(element);
6604        if (this.delegate.elementMatched) {
6605          this.delegate.elementMatched(element);
6606        }
6607      }
6608    }
6609  }
6610  removeElement(element) {
6611    if (this.elements.has(element)) {
6612      this.elements.delete(element);
6613      if (this.delegate.elementUnmatched) {
6614        this.delegate.elementUnmatched(element);
6615      }
6616    }
6617  }
6618};
6619var AttributeObserver = class {
6620  constructor(element, attributeName, delegate) {
6621    this.attributeName = attributeName;
6622    this.delegate = delegate;
6623    this.elementObserver = new ElementObserver(element, this);
6624  }
6625  get element() {
6626    return this.elementObserver.element;
6627  }
6628  get selector() {
6629    return `[${this.attributeName}]`;
6630  }
6631  start() {
6632    this.elementObserver.start();
6633  }
6634  pause(callback) {
6635    this.elementObserver.pause(callback);
6636  }
6637  stop() {
6638    this.elementObserver.stop();
6639  }
6640  refresh() {
6641    this.elementObserver.refresh();
6642  }
6643  get started() {
6644    return this.elementObserver.started;
6645  }
6646  matchElement(element) {
6647    return element.hasAttribute(this.attributeName);
6648  }
6649  matchElementsInTree(tree) {
6650    const match = this.matchElement(tree) ? [tree] : [];
6651    const matches = Array.from(tree.querySelectorAll(this.selector));
6652    return match.concat(matches);
6653  }
6654  elementMatched(element) {
6655    if (this.delegate.elementMatchedAttribute) {
6656      this.delegate.elementMatchedAttribute(element, this.attributeName);
6657    }
6658  }
6659  elementUnmatched(element) {
6660    if (this.delegate.elementUnmatchedAttribute) {
6661      this.delegate.elementUnmatchedAttribute(element, this.attributeName);
6662    }
6663  }
6664  elementAttributeChanged(element, attributeName) {
6665    if (this.delegate.elementAttributeValueChanged && this.attributeName == attributeName) {
6666      this.delegate.elementAttributeValueChanged(element, attributeName);
6667    }
6668  }
6669};
6670function add(map, key, value) {
6671  fetch2(map, key).add(value);
6672}
6673function del(map, key, value) {
6674  fetch2(map, key).delete(value);
6675  prune(map, key);
6676}
6677function fetch2(map, key) {
6678  let values = map.get(key);
6679  if (!values) {
6680    values = /* @__PURE__ */ new Set();
6681    map.set(key, values);
6682  }
6683  return values;
6684}
6685function prune(map, key) {
6686  const values = map.get(key);
6687  if (values != null && values.size == 0) {
6688    map.delete(key);
6689  }
6690}
6691var Multimap = class {
6692  constructor() {
6693    this.valuesByKey = /* @__PURE__ */ new Map();
6694  }
6695  get keys() {
6696    return Array.from(this.valuesByKey.keys());
6697  }
6698  get values() {
6699    const sets = Array.from(this.valuesByKey.values());
6700    return sets.reduce((values, set) => values.concat(Array.from(set)), []);
6701  }
6702  get size() {
6703    const sets = Array.from(this.valuesByKey.values());
6704    return sets.reduce((size, set) => size + set.size, 0);
6705  }
6706  add(key, value) {
6707    add(this.valuesByKey, key, value);
6708  }
6709  delete(key, value) {
6710    del(this.valuesByKey, key, value);
6711  }
6712  has(key, value) {
6713    const values = this.valuesByKey.get(key);
6714    return values != null && values.has(value);
6715  }
6716  hasKey(key) {
6717    return this.valuesByKey.has(key);
6718  }
6719  hasValue(value) {
6720    const sets = Array.from(this.valuesByKey.values());
6721    return sets.some((set) => set.has(value));
6722  }
6723  getValuesForKey(key) {
6724    const values = this.valuesByKey.get(key);
6725    return values ? Array.from(values) : [];
6726  }
6727  getKeysForValue(value) {
6728    return Array.from(this.valuesByKey).filter(([_key, values]) => values.has(value)).map(([key, _values]) => key);
6729  }
6730};
6731var SelectorObserver = class {
6732  constructor(element, selector, delegate, details) {
6733    this._selector = selector;
6734    this.details = details;
6735    this.elementObserver = new ElementObserver(element, this);
6736    this.delegate = delegate;
6737    this.matchesByElement = new Multimap();
6738  }
6739  get started() {
6740    return this.elementObserver.started;
6741  }
6742  get selector() {
6743    return this._selector;
6744  }
6745  set selector(selector) {
6746    this._selector = selector;
6747    this.refresh();
6748  }
6749  start() {
6750    this.elementObserver.start();
6751  }
6752  pause(callback) {
6753    this.elementObserver.pause(callback);
6754  }
6755  stop() {
6756    this.elementObserver.stop();
6757  }
6758  refresh() {
6759    this.elementObserver.refresh();
6760  }
6761  get element() {
6762    return this.elementObserver.element;
6763  }
6764  matchElement(element) {
6765    const { selector } = this;
6766    if (selector) {
6767      const matches = element.matches(selector);
6768      if (this.delegate.selectorMatchElement) {
6769        return matches && this.delegate.selectorMatchElement(element, this.details);
6770      }
6771      return matches;
6772    } else {
6773      return false;
6774    }
6775  }
6776  matchElementsInTree(tree) {
6777    const { selector } = this;
6778    if (selector) {
6779      const match = this.matchElement(tree) ? [tree] : [];
6780      const matches = Array.from(tree.querySelectorAll(selector)).filter((match2) => this.matchElement(match2));
6781      return match.concat(matches);
6782    } else {
6783      return [];
6784    }
6785  }
6786  elementMatched(element) {
6787    const { selector } = this;
6788    if (selector) {
6789      this.selectorMatched(element, selector);
6790    }
6791  }
6792  elementUnmatched(element) {
6793    const selectors = this.matchesByElement.getKeysForValue(element);
6794    for (const selector of selectors) {
6795      this.selectorUnmatched(element, selector);
6796    }
6797  }
6798  elementAttributeChanged(element, _attributeName) {
6799    const { selector } = this;
6800    if (selector) {
6801      const matches = this.matchElement(element);
6802      const matchedBefore = this.matchesByElement.has(selector, element);
6803      if (matches && !matchedBefore) {
6804        this.selectorMatched(element, selector);
6805      } else if (!matches && matchedBefore) {
6806        this.selectorUnmatched(element, selector);
6807      }
6808    }
6809  }
6810  selectorMatched(element, selector) {
6811    this.delegate.selectorMatched(element, selector, this.details);
6812    this.matchesByElement.add(selector, element);
6813  }
6814  selectorUnmatched(element, selector) {
6815    this.delegate.selectorUnmatched(element, selector, this.details);
6816    this.matchesByElement.delete(selector, element);
6817  }
6818};
6819var StringMapObserver = class {
6820  constructor(element, delegate) {
6821    this.element = element;
6822    this.delegate = delegate;
6823    this.started = false;
6824    this.stringMap = /* @__PURE__ */ new Map();
6825    this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations));
6826  }
6827  start() {
6828    if (!this.started) {
6829      this.started = true;
6830      this.mutationObserver.observe(this.element, { attributes: true, attributeOldValue: true });
6831      this.refresh();
6832    }
6833  }
6834  stop() {
6835    if (this.started) {
6836      this.mutationObserver.takeRecords();
6837      this.mutationObserver.disconnect();
6838      this.started = false;
6839    }
6840  }
6841  refresh() {
6842    if (this.started) {
6843      for (const attributeName of this.knownAttributeNames) {
6844        this.refreshAttribute(attributeName, null);
6845      }
6846    }
6847  }
6848  processMutations(mutations) {
6849    if (this.started) {
6850      for (const mutation of mutations) {
6851        this.processMutation(mutation);
6852      }
6853    }
6854  }
6855  processMutation(mutation) {
6856    const attributeName = mutation.attributeName;
6857    if (attributeName) {
6858      this.refreshAttribute(attributeName, mutation.oldValue);
6859    }
6860  }
6861  refreshAttribute(attributeName, oldValue) {
6862    const key = this.delegate.getStringMapKeyForAttribute(attributeName);
6863    if (key != null) {
6864      if (!this.stringMap.has(attributeName)) {
6865        this.stringMapKeyAdded(key, attributeName);
6866      }
6867      const value = this.element.getAttribute(attributeName);
6868      if (this.stringMap.get(attributeName) != value) {
6869        this.stringMapValueChanged(value, key, oldValue);
6870      }
6871      if (value == null) {
6872        const oldValue2 = this.stringMap.get(attributeName);
6873        this.stringMap.delete(attributeName);
6874        if (oldValue2)
6875          this.stringMapKeyRemoved(key, attributeName, oldValue2);
6876      } else {
6877        this.stringMap.set(attributeName, value);
6878      }
6879    }
6880  }
6881  stringMapKeyAdded(key, attributeName) {
6882    if (this.delegate.stringMapKeyAdded) {
6883      this.delegate.stringMapKeyAdded(key, attributeName);
6884    }
6885  }
6886  stringMapValueChanged(value, key, oldValue) {
6887    if (this.delegate.stringMapValueChanged) {
6888      this.delegate.stringMapValueChanged(value, key, oldValue);
6889    }
6890  }
6891  stringMapKeyRemoved(key, attributeName, oldValue) {
6892    if (this.delegate.stringMapKeyRemoved) {
6893      this.delegate.stringMapKeyRemoved(key, attributeName, oldValue);
6894    }
6895  }
6896  get knownAttributeNames() {
6897    return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames)));
6898  }
6899  get currentAttributeNames() {
6900    return Array.from(this.element.attributes).map((attribute) => attribute.name);
6901  }
6902  get recordedAttributeNames() {
6903    return Array.from(this.stringMap.keys());
6904  }
6905};
6906var TokenListObserver = class {
6907  constructor(element, attributeName, delegate) {
6908    this.attributeObserver = new AttributeObserver(element, attributeName, this);
6909    this.delegate = delegate;
6910    this.tokensByElement = new Multimap();
6911  }
6912  get started() {
6913    return this.attributeObserver.started;
6914  }
6915  start() {
6916    this.attributeObserver.start();
6917  }
6918  pause(callback) {
6919    this.attributeObserver.pause(callback);
6920  }
6921  stop() {
6922    this.attributeObserver.stop();
6923  }
6924  refresh() {
6925    this.attributeObserver.refresh();
6926  }
6927  get element() {
6928    return this.attributeObserver.element;
6929  }
6930  get attributeName() {
6931    return this.attributeObserver.attributeName;
6932  }
6933  elementMatchedAttribute(element) {
6934    this.tokensMatched(this.readTokensForElement(element));
6935  }
6936  elementAttributeValueChanged(element) {
6937    const [unmatchedTokens, matchedTokens] = this.refreshTokensForElement(element);
6938    this.tokensUnmatched(unmatchedTokens);
6939    this.tokensMatched(matchedTokens);
6940  }
6941  elementUnmatchedAttribute(element) {
6942    this.tokensUnmatched(this.tokensByElement.getValuesForKey(element));
6943  }
6944  tokensMatched(tokens) {
6945    tokens.forEach((token) => this.tokenMatched(token));
6946  }
6947  tokensUnmatched(tokens) {
6948    tokens.forEach((token) => this.tokenUnmatched(token));
6949  }
6950  tokenMatched(token) {
6951    this.delegate.tokenMatched(token);
6952    this.tokensByElement.add(token.element, token);
6953  }
6954  tokenUnmatched(token) {
6955    this.delegate.tokenUnmatched(token);
6956    this.tokensByElement.delete(token.element, token);
6957  }
6958  refreshTokensForElement(element) {
6959    const previousTokens = this.tokensByElement.getValuesForKey(element);
6960    const currentTokens = this.readTokensForElement(element);
6961    const firstDifferingIndex = zip(previousTokens, currentTokens).findIndex(([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken));
6962    if (firstDifferingIndex == -1) {
6963      return [[], []];
6964    } else {
6965      return [previousTokens.slice(firstDifferingIndex), currentTokens.slice(firstDifferingIndex)];
6966    }
6967  }
6968  readTokensForElement(element) {
6969    const attributeName = this.attributeName;
6970    const tokenString = element.getAttribute(attributeName) || "";
6971    return parseTokenString(tokenString, element, attributeName);
6972  }
6973};
6974function parseTokenString(tokenString, element, attributeName) {
6975  return tokenString.trim().split(/\s+/).filter((content) => content.length).map((content, index) => ({ element, attributeName, content, index }));
6976}
6977function zip(left, right) {
6978  const length = Math.max(left.length, right.length);
6979  return Array.from({ length }, (_, index) => [left[index], right[index]]);
6980}
6981function tokensAreEqual(left, right) {
6982  return left && right && left.index == right.index && left.content == right.content;
6983}
6984var ValueListObserver = class {
6985  constructor(element, attributeName, delegate) {
6986    this.tokenListObserver = new TokenListObserver(element, attributeName, this);
6987    this.delegate = delegate;
6988    this.parseResultsByToken = /* @__PURE__ */ new WeakMap();
6989    this.valuesByTokenByElement = /* @__PURE__ */ new WeakMap();
6990  }
6991  get started() {
6992    return this.tokenListObserver.started;
6993  }
6994  start() {
6995    this.tokenListObserver.start();
6996  }
6997  stop() {
6998    this.tokenListObserver.stop();
6999  }
7000  refresh() {
7001    this.tokenListObserver.refresh();
7002  }
7003  get element() {
7004    return this.tokenListObserver.element;
7005  }
7006  get attributeName() {
7007    return this.tokenListObserver.attributeName;
7008  }
7009  tokenMatched(token) {
7010    const { element } = token;
7011    const { value } = this.fetchParseResultForToken(token);
7012    if (value) {
7013      this.fetchValuesByTokenForElement(element).set(token, value);
7014      this.delegate.elementMatchedValue(element, value);
7015    }
7016  }
7017  tokenUnmatched(token) {
7018    const { element } = token;
7019    const { value } = this.fetchParseResultForToken(token);
7020    if (value) {
7021      this.fetchValuesByTokenForElement(element).delete(token);
7022      this.delegate.elementUnmatchedValue(element, value);
7023    }
7024  }
7025  fetchParseResultForToken(token) {
7026    let parseResult = this.parseResultsByToken.get(token);
7027    if (!parseResult) {
7028      parseResult = this.parseToken(token);
7029      this.parseResultsByToken.set(token, parseResult);
7030    }
7031    return parseResult;
7032  }
7033  fetchValuesByTokenForElement(element) {
7034    let valuesByToken = this.valuesByTokenByElement.get(element);
7035    if (!valuesByToken) {
7036      valuesByToken = /* @__PURE__ */ new Map();
7037      this.valuesByTokenByElement.set(element, valuesByToken);
7038    }
7039    return valuesByToken;
7040  }
7041  parseToken(token) {
7042    try {
7043      const value = this.delegate.parseValueForToken(token);
7044      return { value };
7045    } catch (error2) {
7046      return { error: error2 };
7047    }
7048  }
7049};
7050var BindingObserver = class {
7051  constructor(context, delegate) {
7052    this.context = context;
7053    this.delegate = delegate;
7054    this.bindingsByAction = /* @__PURE__ */ new Map();
7055  }
7056  start() {
7057    if (!this.valueListObserver) {
7058      this.valueListObserver = new ValueListObserver(this.element, this.actionAttribute, this);
7059      this.valueListObserver.start();
7060    }
7061  }
7062  stop() {
7063    if (this.valueListObserver) {
7064      this.valueListObserver.stop();
7065      delete this.valueListObserver;
7066      this.disconnectAllActions();
7067    }
7068  }
7069  get element() {
7070    return this.context.element;
7071  }
7072  get identifier() {
7073    return this.context.identifier;
7074  }
7075  get actionAttribute() {
7076    return this.schema.actionAttribute;
7077  }
7078  get schema() {
7079    return this.context.schema;
7080  }
7081  get bindings() {
7082    return Array.from(this.bindingsByAction.values());
7083  }
7084  connectAction(action) {
7085    const binding = new Binding(this.context, action);
7086    this.bindingsByAction.set(action, binding);
7087    this.delegate.bindingConnected(binding);
7088  }
7089  disconnectAction(action) {
7090    const binding = this.bindingsByAction.get(action);
7091    if (binding) {
7092      this.bindingsByAction.delete(action);
7093      this.delegate.bindingDisconnected(binding);
7094    }
7095  }
7096  disconnectAllActions() {
7097    this.bindings.forEach((binding) => this.delegate.bindingDisconnected(binding, true));
7098    this.bindingsByAction.clear();
7099  }
7100  parseValueForToken(token) {
7101    const action = Action.forToken(token, this.schema);
7102    if (action.identifier == this.identifier) {
7103      return action;
7104    }
7105  }
7106  elementMatchedValue(element, action) {
7107    this.connectAction(action);
7108  }
7109  elementUnmatchedValue(element, action) {
7110    this.disconnectAction(action);
7111  }
7112};
7113var ValueObserver = class {
7114  constructor(context, receiver) {
7115    this.context = context;
7116    this.receiver = receiver;
7117    this.stringMapObserver = new StringMapObserver(this.element, this);
7118    this.valueDescriptorMap = this.controller.valueDescriptorMap;
7119  }
7120  start() {
7121    this.stringMapObserver.start();
7122    this.invokeChangedCallbacksForDefaultValues();
7123  }
7124  stop() {
7125    this.stringMapObserver.stop();
7126  }
7127  get element() {
7128    return this.context.element;
7129  }
7130  get controller() {
7131    return this.context.controller;
7132  }
7133  getStringMapKeyForAttribute(attributeName) {
7134    if (attributeName in this.valueDescriptorMap) {
7135      return this.valueDescriptorMap[attributeName].name;
7136    }
7137  }
7138  stringMapKeyAdded(key, attributeName) {
7139    const descriptor = this.valueDescriptorMap[attributeName];
7140    if (!this.hasValue(key)) {
7141      this.invokeChangedCallback(key, descriptor.writer(this.receiver[key]), descriptor.writer(descriptor.defaultValue));
7142    }
7143  }
7144  stringMapValueChanged(value, name, oldValue) {
7145    const descriptor = this.valueDescriptorNameMap[name];
7146    if (value === null)
7147      return;
7148    if (oldValue === null) {
7149      oldValue = descriptor.writer(descriptor.defaultValue);
7150    }
7151    this.invokeChangedCallback(name, value, oldValue);
7152  }
7153  stringMapKeyRemoved(key, attributeName, oldValue) {
7154    const descriptor = this.valueDescriptorNameMap[key];
7155    if (this.hasValue(key)) {
7156      this.invokeChangedCallback(key, descriptor.writer(this.receiver[key]), oldValue);
7157    } else {
7158      this.invokeChangedCallback(key, descriptor.writer(descriptor.defaultValue), oldValue);
7159    }
7160  }
7161  invokeChangedCallbacksForDefaultValues() {
7162    for (const { key, name, defaultValue, writer } of this.valueDescriptors) {
7163      if (defaultValue != void 0 && !this.controller.data.has(key)) {
7164        this.invokeChangedCallback(name, writer(defaultValue), void 0);
7165      }
7166    }
7167  }
7168  invokeChangedCallback(name, rawValue, rawOldValue) {
7169    const changedMethodName = `${name}Changed`;
7170    const changedMethod = this.receiver[changedMethodName];
7171    if (typeof changedMethod == "function") {
7172      const descriptor = this.valueDescriptorNameMap[name];
7173      try {
7174        const value = descriptor.reader(rawValue);
7175        let oldValue = rawOldValue;
7176        if (rawOldValue) {
7177          oldValue = descriptor.reader(rawOldValue);
7178        }
7179        changedMethod.call(this.receiver, value, oldValue);
7180      } catch (error2) {
7181        if (error2 instanceof TypeError) {
7182          error2.message = `Stimulus Value "${this.context.identifier}.${descriptor.name}" - ${error2.message}`;
7183        }
7184        throw error2;
7185      }
7186    }
7187  }
7188  get valueDescriptors() {
7189    const { valueDescriptorMap } = this;
7190    return Object.keys(valueDescriptorMap).map((key) => valueDescriptorMap[key]);
7191  }
7192  get valueDescriptorNameMap() {
7193    const descriptors = {};
7194    Object.keys(this.valueDescriptorMap).forEach((key) => {
7195      const descriptor = this.valueDescriptorMap[key];
7196      descriptors[descriptor.name] = descriptor;
7197    });
7198    return descriptors;
7199  }
7200  hasValue(attributeName) {
7201    const descriptor = this.valueDescriptorNameMap[attributeName];
7202    const hasMethodName = `has${capitalize(descriptor.name)}`;
7203    return this.receiver[hasMethodName];
7204  }
7205};
7206var TargetObserver = class {
7207  constructor(context, delegate) {
7208    this.context = context;
7209    this.delegate = delegate;
7210    this.targetsByName = new Multimap();
7211  }
7212  start() {
7213    if (!this.tokenListObserver) {
7214      this.tokenListObserver = new TokenListObserver(this.element, this.attributeName, this);
7215      this.tokenListObserver.start();
7216    }
7217  }
7218  stop() {
7219    if (this.tokenListObserver) {
7220      this.disconnectAllTargets();
7221      this.tokenListObserver.stop();
7222      delete this.tokenListObserver;
7223    }
7224  }
7225  tokenMatched({ element, content: name }) {
7226    if (this.scope.containsElement(element)) {
7227      this.connectTarget(element, name);
7228    }
7229  }
7230  tokenUnmatched({ element, content: name }) {
7231    this.disconnectTarget(element, name);
7232  }
7233  connectTarget(element, name) {
7234    var _a;
7235    if (!this.targetsByName.has(name, element)) {
7236      this.targetsByName.add(name, element);
7237      (_a = this.tokenListObserver) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.targetConnected(element, name));
7238    }
7239  }
7240  disconnectTarget(element, name) {
7241    var _a;
7242    if (this.targetsByName.has(name, element)) {
7243      this.targetsByName.delete(name, element);
7244      (_a = this.tokenListObserver) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.targetDisconnected(element, name));
7245    }
7246  }
7247  disconnectAllTargets() {
7248    for (const name of this.targetsByName.keys) {
7249      for (const element of this.targetsByName.getValuesForKey(name)) {
7250        this.disconnectTarget(element, name);
7251      }
7252    }
7253  }
7254  get attributeName() {
7255    return `data-${this.context.identifier}-target`;
7256  }
7257  get element() {
7258    return this.context.element;
7259  }
7260  get scope() {
7261    return this.context.scope;
7262  }
7263};
7264function readInheritableStaticArrayValues(constructor, propertyName) {
7265  const ancestors = getAncestorsForConstructor(constructor);
7266  return Array.from(ancestors.reduce((values, constructor2) => {
7267    getOwnStaticArrayValues(constructor2, propertyName).forEach((name) => values.add(name));
7268    return values;
7269  }, /* @__PURE__ */ new Set()));
7270}
7271function readInheritableStaticObjectPairs(constructor, propertyName) {
7272  const ancestors = getAncestorsForConstructor(constructor);
7273  return ancestors.reduce((pairs, constructor2) => {
7274    pairs.push(...getOwnStaticObjectPairs(constructor2, propertyName));
7275    return pairs;
7276  }, []);
7277}
7278function getAncestorsForConstructor(constructor) {
7279  const ancestors = [];
7280  while (constructor) {
7281    ancestors.push(constructor);
7282    constructor = Object.getPrototypeOf(constructor);
7283  }
7284  return ancestors.reverse();
7285}
7286function getOwnStaticArrayValues(constructor, propertyName) {
7287  const definition = constructor[propertyName];
7288  return Array.isArray(definition) ? definition : [];
7289}
7290function getOwnStaticObjectPairs(constructor, propertyName) {
7291  const definition = constructor[propertyName];
7292  return definition ? Object.keys(definition).map((key) => [key, definition[key]]) : [];
7293}
7294var OutletObserver = class {
7295  constructor(context, delegate) {
7296    this.started = false;
7297    this.context = context;
7298    this.delegate = delegate;
7299    this.outletsByName = new Multimap();
7300    this.outletElementsByName = new Multimap();
7301    this.selectorObserverMap = /* @__PURE__ */ new Map();
7302    this.attributeObserverMap = /* @__PURE__ */ new Map();
7303  }
7304  start() {
7305    if (!this.started) {
7306      this.outletDefinitions.forEach((outletName) => {
7307        this.setupSelectorObserverForOutlet(outletName);
7308        this.setupAttributeObserverForOutlet(outletName);
7309      });
7310      this.started = true;
7311      this.dependentContexts.forEach((context) => context.refresh());
7312    }
7313  }
7314  refresh() {
7315    this.selectorObserverMap.forEach((observer) => observer.refresh());
7316    this.attributeObserverMap.forEach((observer) => observer.refresh());
7317  }
7318  stop() {
7319    if (this.started) {
7320      this.started = false;
7321      this.disconnectAllOutlets();
7322      this.stopSelectorObservers();
7323      this.stopAttributeObservers();
7324    }
7325  }
7326  stopSelectorObservers() {
7327    if (this.selectorObserverMap.size > 0) {
7328      this.selectorObserverMap.forEach((observer) => observer.stop());
7329      this.selectorObserverMap.clear();
7330    }
7331  }
7332  stopAttributeObservers() {
7333    if (this.attributeObserverMap.size > 0) {
7334      this.attributeObserverMap.forEach((observer) => observer.stop());
7335      this.attributeObserverMap.clear();
7336    }
7337  }
7338  selectorMatched(element, _selector, { outletName }) {
7339    const outlet = this.getOutlet(element, outletName);
7340    if (outlet) {
7341      this.connectOutlet(outlet, element, outletName);
7342    }
7343  }
7344  selectorUnmatched(element, _selector, { outletName }) {
7345    const outlet = this.getOutletFromMap(element, outletName);
7346    if (outlet) {
7347      this.disconnectOutlet(outlet, element, outletName);
7348    }
7349  }
7350  selectorMatchElement(element, { outletName }) {
7351    const selector = this.selector(outletName);
7352    const hasOutlet = this.hasOutlet(element, outletName);
7353    const hasOutletController = element.matches(`[${this.schema.controllerAttribute}~=${outletName}]`);
7354    if (selector) {
7355      return hasOutlet && hasOutletController && element.matches(selector);
7356    } else {
7357      return false;
7358    }
7359  }
7360  elementMatchedAttribute(_element, attributeName) {
7361    const outletName = this.getOutletNameFromOutletAttributeName(attributeName);
7362    if (outletName) {
7363      this.updateSelectorObserverForOutlet(outletName);
7364    }
7365  }
7366  elementAttributeValueChanged(_element, attributeName) {
7367    const outletName = this.getOutletNameFromOutletAttributeName(attributeName);
7368    if (outletName) {
7369      this.updateSelectorObserverForOutlet(outletName);
7370    }
7371  }
7372  elementUnmatchedAttribute(_element, attributeName) {
7373    const outletName = this.getOutletNameFromOutletAttributeName(attributeName);
7374    if (outletName) {
7375      this.updateSelectorObserverForOutlet(outletName);
7376    }
7377  }
7378  connectOutlet(outlet, element, outletName) {
7379    var _a;
7380    if (!this.outletElementsByName.has(outletName, element)) {
7381      this.outletsByName.add(outletName, outlet);
7382      this.outletElementsByName.add(outletName, element);
7383      (_a = this.selectorObserverMap.get(outletName)) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.outletConnected(outlet, element, outletName));
7384    }
7385  }
7386  disconnectOutlet(outlet, element, outletName) {
7387    var _a;
7388    if (this.outletElementsByName.has(outletName, element)) {
7389      this.outletsByName.delete(outletName, outlet);
7390      this.outletElementsByName.delete(outletName, element);
7391      (_a = this.selectorObserverMap.get(outletName)) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.outletDisconnected(outlet, element, outletName));
7392    }
7393  }
7394  disconnectAllOutlets() {
7395    for (const outletName of this.outletElementsByName.keys) {
7396      for (const element of this.outletElementsByName.getValuesForKey(outletName)) {
7397        for (const outlet of this.outletsByName.getValuesForKey(outletName)) {
7398          this.disconnectOutlet(outlet, element, outletName);
7399        }
7400      }
7401    }
7402  }
7403  updateSelectorObserverForOutlet(outletName) {
7404    const observer = this.selectorObserverMap.get(outletName);
7405    if (observer) {
7406      observer.selector = this.selector(outletName);
7407    }
7408  }
7409  setupSelectorObserverForOutlet(outletName) {
7410    const selector = this.selector(outletName);
7411    const selectorObserver = new SelectorObserver(document.body, selector, this, { outletName });
7412    this.selectorObserverMap.set(outletName, selectorObserver);
7413    selectorObserver.start();
7414  }
7415  setupAttributeObserverForOutlet(outletName) {
7416    const attributeName = this.attributeNameForOutletName(outletName);
7417    const attributeObserver = new AttributeObserver(this.scope.element, attributeName, this);
7418    this.attributeObserverMap.set(outletName, attributeObserver);
7419    attributeObserver.start();
7420  }
7421  selector(outletName) {
7422    return this.scope.outlets.getSelectorForOutletName(outletName);
7423  }
7424  attributeNameForOutletName(outletName) {
7425    return this.scope.schema.outletAttributeForScope(this.identifier, outletName);
7426  }
7427  getOutletNameFromOutletAttributeName(attributeName) {
7428    return this.outletDefinitions.find((outletName) => this.attributeNameForOutletName(outletName) === attributeName);
7429  }
7430  get outletDependencies() {
7431    const dependencies = new Multimap();
7432    this.router.modules.forEach((module) => {
7433      const constructor = module.definition.controllerConstructor;
7434      const outlets = readInheritableStaticArrayValues(constructor, "outlets");
7435      outlets.forEach((outlet) => dependencies.add(outlet, module.identifier));
7436    });
7437    return dependencies;
7438  }
7439  get outletDefinitions() {
7440    return this.outletDependencies.getKeysForValue(this.identifier);
7441  }
7442  get dependentControllerIdentifiers() {
7443    return this.outletDependencies.getValuesForKey(this.identifier);
7444  }
7445  get dependentContexts() {
7446    const identifiers = this.dependentControllerIdentifiers;
7447    return this.router.contexts.filter((context) => identifiers.includes(context.identifier));
7448  }
7449  hasOutlet(element, outletName) {
7450    return !!this.getOutlet(element, outletName) || !!this.getOutletFromMap(element, outletName);
7451  }
7452  getOutlet(element, outletName) {
7453    return this.application.getControllerForElementAndIdentifier(element, outletName);
7454  }
7455  getOutletFromMap(element, outletName) {
7456    return this.outletsByName.getValuesForKey(outletName).find((outlet) => outlet.element === element);
7457  }
7458  get scope() {
7459    return this.context.scope;
7460  }
7461  get schema() {
7462    return this.context.schema;
7463  }
7464  get identifier() {
7465    return this.context.identifier;
7466  }
7467  get application() {
7468    return this.context.application;
7469  }
7470  get router() {
7471    return this.application.router;
7472  }
7473};
7474var Context = class {
7475  constructor(module, scope) {
7476    this.logDebugActivity = (functionName, detail = {}) => {
7477      const { identifier, controller, element } = this;
7478      detail = Object.assign({ identifier, controller, element }, detail);
7479      this.application.logDebugActivity(this.identifier, functionName, detail);
7480    };
7481    this.module = module;
7482    this.scope = scope;
7483    this.controller = new module.controllerConstructor(this);
7484    this.bindingObserver = new BindingObserver(this, this.dispatcher);
7485    this.valueObserver = new ValueObserver(this, this.controller);
7486    this.targetObserver = new TargetObserver(this, this);
7487    this.outletObserver = new OutletObserver(this, this);
7488    try {
7489      this.controller.initialize();
7490      this.logDebugActivity("initialize");
7491    } catch (error2) {
7492      this.handleError(error2, "initializing controller");
7493    }
7494  }
7495  connect() {
7496    this.bindingObserver.start();
7497    this.valueObserver.start();
7498    this.targetObserver.start();
7499    this.outletObserver.start();
7500    try {
7501      this.controller.connect();
7502      this.logDebugActivity("connect");
7503    } catch (error2) {
7504      this.handleError(error2, "connecting controller");
7505    }
7506  }
7507  refresh() {
7508    this.outletObserver.refresh();
7509  }
7510  disconnect() {
7511    try {
7512      this.controller.disconnect();
7513      this.logDebugActivity("disconnect");
7514    } catch (error2) {
7515      this.handleError(error2, "disconnecting controller");
7516    }
7517    this.outletObserver.stop();
7518    this.targetObserver.stop();
7519    this.valueObserver.stop();
7520    this.bindingObserver.stop();
7521  }
7522  get application() {
7523    return this.module.application;
7524  }
7525  get identifier() {
7526    return this.module.identifier;
7527  }
7528  get schema() {
7529    return this.application.schema;
7530  }
7531  get dispatcher() {
7532    return this.application.dispatcher;
7533  }
7534  get element() {
7535    return this.scope.element;
7536  }
7537  get parentElement() {
7538    return this.element.parentElement;
7539  }
7540  handleError(error2, message, detail = {}) {
7541    const { identifier, controller, element } = this;
7542    detail = Object.assign({ identifier, controller, element }, detail);
7543    this.application.handleError(error2, `Error ${message}`, detail);
7544  }
7545  targetConnected(element, name) {
7546    this.invokeControllerMethod(`${name}TargetConnected`, element);
7547  }
7548  targetDisconnected(element, name) {
7549    this.invokeControllerMethod(`${name}TargetDisconnected`, element);
7550  }
7551  outletConnected(outlet, element, name) {
7552    this.invokeControllerMethod(`${namespaceCamelize(name)}OutletConnected`, outlet, element);
7553  }
7554  outletDisconnected(outlet, element, name) {
7555    this.invokeControllerMethod(`${namespaceCamelize(name)}OutletDisconnected`, outlet, element);
7556  }
7557  invokeControllerMethod(methodName, ...args) {
7558    const controller = this.controller;
7559    if (typeof controller[methodName] == "function") {
7560      controller[methodName](...args);
7561    }
7562  }
7563};
7564function bless(constructor) {
7565  return shadow(constructor, getBlessedProperties(constructor));
7566}
7567function shadow(constructor, properties) {
7568  const shadowConstructor = extend2(constructor);
7569  const shadowProperties = getShadowProperties(constructor.prototype, properties);
7570  Object.defineProperties(shadowConstructor.prototype, shadowProperties);
7571  return shadowConstructor;
7572}
7573function getBlessedProperties(constructor) {
7574  const blessings = readInheritableStaticArrayValues(constructor, "blessings");
7575  return blessings.reduce((blessedProperties, blessing) => {
7576    const properties = blessing(constructor);
7577    for (const key in properties) {
7578      const descriptor = blessedProperties[key] || {};
7579      blessedProperties[key] = Object.assign(descriptor, properties[key]);
7580    }
7581    return blessedProperties;
7582  }, {});
7583}
7584function getShadowProperties(prototype, properties) {
7585  return getOwnKeys(properties).reduce((shadowProperties, key) => {
7586    const descriptor = getShadowedDescriptor(prototype, properties, key);
7587    if (descriptor) {
7588      Object.assign(shadowProperties, { [key]: descriptor });
7589    }
7590    return shadowProperties;
7591  }, {});
7592}
7593function getShadowedDescriptor(prototype, properties, key) {
7594  const shadowingDescriptor = Object.getOwnPropertyDescriptor(prototype, key);
7595  const shadowedByValue = shadowingDescriptor && "value" in shadowingDescriptor;
7596  if (!shadowedByValue) {
7597    const descriptor = Object.getOwnPropertyDescriptor(properties, key).value;
7598    if (shadowingDescriptor) {
7599      descriptor.get = shadowingDescriptor.get || descriptor.get;
7600      descriptor.set = shadowingDescriptor.set || descriptor.set;
7601    }
7602    return descriptor;
7603  }
7604}
7605var getOwnKeys = (() => {
7606  if (typeof Object.getOwnPropertySymbols == "function") {
7607    return (object) => [...Object.getOwnPropertyNames(object), ...Object.getOwnPropertySymbols(object)];
7608  } else {
7609    return Object.getOwnPropertyNames;
7610  }
7611})();
7612var extend2 = (() => {
7613  function extendWithReflect(constructor) {
7614    function extended() {
7615      return Reflect.construct(constructor, arguments, new.target);
7616    }
7617    extended.prototype = Object.create(constructor.prototype, {
7618      constructor: { value: extended }
7619    });
7620    Reflect.setPrototypeOf(extended, constructor);
7621    return extended;
7622  }
7623  function testReflectExtension() {
7624    const a = function() {
7625      this.a.call(this);
7626    };
7627    const b = extendWithReflect(a);
7628    b.prototype.a = function() {
7629    };
7630    return new b();
7631  }
7632  try {
7633    testReflectExtension();
7634    return extendWithReflect;
7635  } catch (error2) {
7636    return (constructor) => class extended extends constructor {
7637    };
7638  }
7639})();
7640function blessDefinition(definition) {
7641  return {
7642    identifier: definition.identifier,
7643    controllerConstructor: bless(definition.controllerConstructor)
7644  };
7645}
7646var Module = class {
7647  constructor(application2, definition) {
7648    this.application = application2;
7649    this.definition = blessDefinition(definition);
7650    this.contextsByScope = /* @__PURE__ */ new WeakMap();
7651    this.connectedContexts = /* @__PURE__ */ new Set();
7652  }
7653  get identifier() {
7654    return this.definition.identifier;
7655  }
7656  get controllerConstructor() {
7657    return this.definition.controllerConstructor;
7658  }
7659  get contexts() {
7660    return Array.from(this.connectedContexts);
7661  }
7662  connectContextForScope(scope) {
7663    const context = this.fetchContextForScope(scope);
7664    this.connectedContexts.add(context);
7665    context.connect();
7666  }
7667  disconnectContextForScope(scope) {
7668    const context = this.contextsByScope.get(scope);
7669    if (context) {
7670      this.connectedContexts.delete(context);
7671      context.disconnect();
7672    }
7673  }
7674  fetchContextForScope(scope) {
7675    let context = this.contextsByScope.get(scope);
7676    if (!context) {
7677      context = new Context(this, scope);
7678      this.contextsByScope.set(scope, context);
7679    }
7680    return context;
7681  }
7682};
7683var ClassMap = class {
7684  constructor(scope) {
7685    this.scope = scope;
7686  }
7687  has(name) {
7688    return this.data.has(this.getDataKey(name));
7689  }
7690  get(name) {
7691    return this.getAll(name)[0];
7692  }
7693  getAll(name) {
7694    const tokenString = this.data.get(this.getDataKey(name)) || "";
7695    return tokenize(tokenString);
7696  }
7697  getAttributeName(name) {
7698    return this.data.getAttributeNameForKey(this.getDataKey(name));
7699  }
7700  getDataKey(name) {
7701    return `${name}-class`;
7702  }
7703  get data() {
7704    return this.scope.data;
7705  }
7706};
7707var DataMap = class {
7708  constructor(scope) {
7709    this.scope = scope;
7710  }
7711  get element() {
7712    return this.scope.element;
7713  }
7714  get identifier() {
7715    return this.scope.identifier;
7716  }
7717  get(key) {
7718    const name = this.getAttributeNameForKey(key);
7719    return this.element.getAttribute(name);
7720  }
7721  set(key, value) {
7722    const name = this.getAttributeNameForKey(key);
7723    this.element.setAttribute(name, value);
7724    return this.get(key);
7725  }
7726  has(key) {
7727    const name = this.getAttributeNameForKey(key);
7728    return this.element.hasAttribute(name);
7729  }
7730  delete(key) {
7731    if (this.has(key)) {
7732      const name = this.getAttributeNameForKey(key);
7733      this.element.removeAttribute(name);
7734      return true;
7735    } else {
7736      return false;
7737    }
7738  }
7739  getAttributeNameForKey(key) {
7740    return `data-${this.identifier}-${dasherize(key)}`;
7741  }
7742};
7743var Guide = class {
7744  constructor(logger) {
7745    this.warnedKeysByObject = /* @__PURE__ */ new WeakMap();
7746    this.logger = logger;
7747  }
7748  warn(object, key, message) {
7749    let warnedKeys = this.warnedKeysByObject.get(object);
7750    if (!warnedKeys) {
7751      warnedKeys = /* @__PURE__ */ new Set();
7752      this.warnedKeysByObject.set(object, warnedKeys);
7753    }
7754    if (!warnedKeys.has(key)) {
7755      warnedKeys.add(key);
7756      this.logger.warn(message, object);
7757    }
7758  }
7759};
7760function attributeValueContainsToken(attributeName, token) {
7761  return `[${attributeName}~="${token}"]`;
7762}
7763var TargetSet = class {
7764  constructor(scope) {
7765    this.scope = scope;
7766  }
7767  get element() {
7768    return this.scope.element;
7769  }
7770  get identifier() {
7771    return this.scope.identifier;
7772  }
7773  get schema() {
7774    return this.scope.schema;
7775  }
7776  has(targetName) {
7777    return this.find(targetName) != null;
7778  }
7779  find(...targetNames) {
7780    return targetNames.reduce((target, targetName) => target || this.findTarget(targetName) || this.findLegacyTarget(targetName), void 0);
7781  }
7782  findAll(...targetNames) {
7783    return targetNames.reduce((targets, targetName) => [
7784      ...targets,
7785      ...this.findAllTargets(targetName),
7786      ...this.findAllLegacyTargets(targetName)
7787    ], []);
7788  }
7789  findTarget(targetName) {
7790    const selector = this.getSelectorForTargetName(targetName);
7791    return this.scope.findElement(selector);
7792  }
7793  findAllTargets(targetName) {
7794    const selector = this.getSelectorForTargetName(targetName);
7795    return this.scope.findAllElements(selector);
7796  }
7797  getSelectorForTargetName(targetName) {
7798    const attributeName = this.schema.targetAttributeForScope(this.identifier);
7799    return attributeValueContainsToken(attributeName, targetName);
7800  }
7801  findLegacyTarget(targetName) {
7802    const selector = this.getLegacySelectorForTargetName(targetName);
7803    return this.deprecate(this.scope.findElement(selector), targetName);
7804  }
7805  findAllLegacyTargets(targetName) {
7806    const selector = this.getLegacySelectorForTargetName(targetName);
7807    return this.scope.findAllElements(selector).map((element) => this.deprecate(element, targetName));
7808  }
7809  getLegacySelectorForTargetName(targetName) {
7810    const targetDescriptor = `${this.identifier}.${targetName}`;
7811    return attributeValueContainsToken(this.schema.targetAttribute, targetDescriptor);
7812  }
7813  deprecate(element, targetName) {
7814    if (element) {
7815      const { identifier } = this;
7816      const attributeName = this.schema.targetAttribute;
7817      const revisedAttributeName = this.schema.targetAttributeForScope(identifier);
7818      this.guide.warn(element, `target:${targetName}`, `Please replace ${attributeName}="${identifier}.${targetName}" with ${revisedAttributeName}="${targetName}". The ${attributeName} attribute is deprecated and will be removed in a future version of Stimulus.`);
7819    }
7820    return element;
7821  }
7822  get guide() {
7823    return this.scope.guide;
7824  }
7825};
7826var OutletSet = class {
7827  constructor(scope, controllerElement) {
7828    this.scope = scope;
7829    this.controllerElement = controllerElement;
7830  }
7831  get element() {
7832    return this.scope.element;
7833  }
7834  get identifier() {
7835    return this.scope.identifier;
7836  }
7837  get schema() {
7838    return this.scope.schema;
7839  }
7840  has(outletName) {
7841    return this.find(outletName) != null;
7842  }
7843  find(...outletNames) {
7844    return outletNames.reduce((outlet, outletName) => outlet || this.findOutlet(outletName), void 0);
7845  }
7846  findAll(...outletNames) {
7847    return outletNames.reduce((outlets, outletName) => [...outlets, ...this.findAllOutlets(outletName)], []);
7848  }
7849  getSelectorForOutletName(outletName) {
7850    const attributeName = this.schema.outletAttributeForScope(this.identifier, outletName);
7851    return this.controllerElement.getAttribute(attributeName);
7852  }
7853  findOutlet(outletName) {
7854    const selector = this.getSelectorForOutletName(outletName);
7855    if (selector)
7856      return this.findElement(selector, outletName);
7857  }
7858  findAllOutlets(outletName) {
7859    const selector = this.getSelectorForOutletName(outletName);
7860    return selector ? this.findAllElements(selector, outletName) : [];
7861  }
7862  findElement(selector, outletName) {
7863    const elements = this.scope.queryElements(selector);
7864    return elements.filter((element) => this.matchesElement(element, selector, outletName))[0];
7865  }
7866  findAllElements(selector, outletName) {
7867    const elements = this.scope.queryElements(selector);
7868    return elements.filter((element) => this.matchesElement(element, selector, outletName));
7869  }
7870  matchesElement(element, selector, outletName) {
7871    const controllerAttribute = element.getAttribute(this.scope.schema.controllerAttribute) || "";
7872    return element.matches(selector) && controllerAttribute.split(" ").includes(outletName);
7873  }
7874};
7875var Scope = class _Scope {
7876  constructor(schema, element, identifier, logger) {
7877    this.targets = new TargetSet(this);
7878    this.classes = new ClassMap(this);
7879    this.data = new DataMap(this);
7880    this.containsElement = (element2) => {
7881      return element2.closest(this.controllerSelector) === this.element;
7882    };
7883    this.schema = schema;
7884    this.element = element;
7885    this.identifier = identifier;
7886    this.guide = new Guide(logger);
7887    this.outlets = new OutletSet(this.documentScope, element);
7888  }
7889  findElement(selector) {
7890    return this.element.matches(selector) ? this.element : this.queryElements(selector).find(this.containsElement);
7891  }
7892  findAllElements(selector) {
7893    return [
7894      ...this.element.matches(selector) ? [this.element] : [],
7895      ...this.queryElements(selector).filter(this.containsElement)
7896    ];
7897  }
7898  queryElements(selector) {
7899    return Array.from(this.element.querySelectorAll(selector));
7900  }
7901  get controllerSelector() {
7902    return attributeValueContainsToken(this.schema.controllerAttribute, this.identifier);
7903  }
7904  get isDocumentScope() {
7905    return this.element === document.documentElement;
7906  }
7907  get documentScope() {
7908    return this.isDocumentScope ? this : new _Scope(this.schema, document.documentElement, this.identifier, this.guide.logger);
7909  }
7910};
7911var ScopeObserver = class {
7912  constructor(element, schema, delegate) {
7913    this.element = element;
7914    this.schema = schema;
7915    this.delegate = delegate;
7916    this.valueListObserver = new ValueListObserver(this.element, this.controllerAttribute, this);
7917    this.scopesByIdentifierByElement = /* @__PURE__ */ new WeakMap();
7918    this.scopeReferenceCounts = /* @__PURE__ */ new WeakMap();
7919  }
7920  start() {
7921    this.valueListObserver.start();
7922  }
7923  stop() {
7924    this.valueListObserver.stop();
7925  }
7926  get controllerAttribute() {
7927    return this.schema.controllerAttribute;
7928  }
7929  parseValueForToken(token) {
7930    const { element, content: identifier } = token;
7931    return this.parseValueForElementAndIdentifier(element, identifier);
7932  }
7933  parseValueForElementAndIdentifier(element, identifier) {
7934    const scopesByIdentifier = this.fetchScopesByIdentifierForElement(element);
7935    let scope = scopesByIdentifier.get(identifier);
7936    if (!scope) {
7937      scope = this.delegate.createScopeForElementAndIdentifier(element, identifier);
7938      scopesByIdentifier.set(identifier, scope);
7939    }
7940    return scope;
7941  }
7942  elementMatchedValue(element, value) {
7943    const referenceCount = (this.scopeReferenceCounts.get(value) || 0) + 1;
7944    this.scopeReferenceCounts.set(value, referenceCount);
7945    if (referenceCount == 1) {
7946      this.delegate.scopeConnected(value);
7947    }
7948  }
7949  elementUnmatchedValue(element, value) {
7950    const referenceCount = this.scopeReferenceCounts.get(value);
7951    if (referenceCount) {
7952      this.scopeReferenceCounts.set(value, referenceCount - 1);
7953      if (referenceCount == 1) {
7954        this.delegate.scopeDisconnected(value);
7955      }
7956    }
7957  }
7958  fetchScopesByIdentifierForElement(element) {
7959    let scopesByIdentifier = this.scopesByIdentifierByElement.get(element);
7960    if (!scopesByIdentifier) {
7961      scopesByIdentifier = /* @__PURE__ */ new Map();
7962      this.scopesByIdentifierByElement.set(element, scopesByIdentifier);
7963    }
7964    return scopesByIdentifier;
7965  }
7966};
7967var Router = class {
7968  constructor(application2) {
7969    this.application = application2;
7970    this.scopeObserver = new ScopeObserver(this.element, this.schema, this);
7971    this.scopesByIdentifier = new Multimap();
7972    this.modulesByIdentifier = /* @__PURE__ */ new Map();
7973  }
7974  get element() {
7975    return this.application.element;
7976  }
7977  get schema() {
7978    return this.application.schema;
7979  }
7980  get logger() {
7981    return this.application.logger;
7982  }
7983  get controllerAttribute() {
7984    return this.schema.controllerAttribute;
7985  }
7986  get modules() {
7987    return Array.from(this.modulesByIdentifier.values());
7988  }
7989  get contexts() {
7990    return this.modules.reduce((contexts, module) => contexts.concat(module.contexts), []);
7991  }
7992  start() {
7993    this.scopeObserver.start();
7994  }
7995  stop() {
7996    this.scopeObserver.stop();
7997  }
7998  loadDefinition(definition) {
7999    this.unloadIdentifier(definition.identifier);
8000    const module = new Module(this.application, definition);
8001    this.connectModule(module);
8002    const afterLoad = definition.controllerConstructor.afterLoad;
8003    if (afterLoad) {
8004      afterLoad.call(definition.controllerConstructor, definition.identifier, this.application);
8005    }
8006  }
8007  unloadIdentifier(identifier) {
8008    const module = this.modulesByIdentifier.get(identifier);
8009    if (module) {
8010      this.disconnectModule(module);
8011    }
8012  }
8013  getContextForElementAndIdentifier(element, identifier) {
8014    const module = this.modulesByIdentifier.get(identifier);
8015    if (module) {
8016      return module.contexts.find((context) => context.element == element);
8017    }
8018  }
8019  proposeToConnectScopeForElementAndIdentifier(element, identifier) {
8020    const scope = this.scopeObserver.parseValueForElementAndIdentifier(element, identifier);
8021    if (scope) {
8022      this.scopeObserver.elementMatchedValue(scope.element, scope);
8023    } else {
8024      console.error(`Couldn't find or create scope for identifier: "${identifier}" and element:`, element);
8025    }
8026  }
8027  handleError(error2, message, detail) {
8028    this.application.handleError(error2, message, detail);
8029  }
8030  createScopeForElementAndIdentifier(element, identifier) {
8031    return new Scope(this.schema, element, identifier, this.logger);
8032  }
8033  scopeConnected(scope) {
8034    this.scopesByIdentifier.add(scope.identifier, scope);
8035    const module = this.modulesByIdentifier.get(scope.identifier);
8036    if (module) {
8037      module.connectContextForScope(scope);
8038    }
8039  }
8040  scopeDisconnected(scope) {
8041    this.scopesByIdentifier.delete(scope.identifier, scope);
8042    const module = this.modulesByIdentifier.get(scope.identifier);
8043    if (module) {
8044      module.disconnectContextForScope(scope);
8045    }
8046  }
8047  connectModule(module) {
8048    this.modulesByIdentifier.set(module.identifier, module);
8049    const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
8050    scopes.forEach((scope) => module.connectContextForScope(scope));
8051  }
8052  disconnectModule(module) {
8053    this.modulesByIdentifier.delete(module.identifier);
8054    const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
8055    scopes.forEach((scope) => module.disconnectContextForScope(scope));
8056  }
8057};
8058var defaultSchema = {
8059  controllerAttribute: "data-controller",
8060  actionAttribute: "data-action",
8061  targetAttribute: "data-target",
8062  targetAttributeForScope: (identifier) => `data-${identifier}-target`,
8063  outletAttributeForScope: (identifier, outlet) => `data-${identifier}-${outlet}-outlet`,
8064  keyMappings: Object.assign(Object.assign({ enter: "Enter", tab: "Tab", esc: "Escape", space: " ", up: "ArrowUp", down: "ArrowDown", left: "ArrowLeft", right: "ArrowRight", home: "Home", end: "End", page_up: "PageUp", page_down: "PageDown" }, objectFromEntries("abcdefghijklmnopqrstuvwxyz".split("").map((c) => [c, c]))), objectFromEntries("0123456789".split("").map((n) => [n, n])))
8065};
8066function objectFromEntries(array) {
8067  return array.reduce((memo, [k, v]) => Object.assign(Object.assign({}, memo), { [k]: v }), {});
8068}
8069var Application = class {
8070  constructor(element = document.documentElement, schema = defaultSchema) {
8071    this.logger = console;
8072    this.debug = false;
8073    this.logDebugActivity = (identifier, functionName, detail = {}) => {
8074      if (this.debug) {
8075        this.logFormattedMessage(identifier, functionName, detail);
8076      }
8077    };
8078    this.element = element;
8079    this.schema = schema;
8080    this.dispatcher = new Dispatcher(this);
8081    this.router = new Router(this);
8082    this.actionDescriptorFilters = Object.assign({}, defaultActionDescriptorFilters);
8083  }
8084  static start(element, schema) {
8085    const application2 = new this(element, schema);
8086    application2.start();
8087    return application2;
8088  }
8089  async start() {
8090    await domReady();
8091    this.logDebugActivity("application", "starting");
8092    this.dispatcher.start();
8093    this.router.start();
8094    this.logDebugActivity("application", "start");
8095  }
8096  stop() {
8097    this.logDebugActivity("application", "stopping");
8098    this.dispatcher.stop();
8099    this.router.stop();
8100    this.logDebugActivity("application", "stop");
8101  }
8102  register(identifier, controllerConstructor) {
8103    this.load({ identifier, controllerConstructor });
8104  }
8105  registerActionOption(name, filter) {
8106    this.actionDescriptorFilters[name] = filter;
8107  }
8108  load(head, ...rest) {
8109    const definitions = Array.isArray(head) ? head : [head, ...rest];
8110    definitions.forEach((definition) => {
8111      if (definition.controllerConstructor.shouldLoad) {
8112        this.router.loadDefinition(definition);
8113      }
8114    });
8115  }
8116  unload(head, ...rest) {
8117    const identifiers = Array.isArray(head) ? head : [head, ...rest];
8118    identifiers.forEach((identifier) => this.router.unloadIdentifier(identifier));
8119  }
8120  get controllers() {
8121    return this.router.contexts.map((context) => context.controller);
8122  }
8123  getControllerForElementAndIdentifier(element, identifier) {
8124    const context = this.router.getContextForElementAndIdentifier(element, identifier);
8125    return context ? context.controller : null;
8126  }
8127  handleError(error2, message, detail) {
8128    var _a;
8129    this.logger.error(`%s
8130
8131%o
8132
8133%o`, message, error2, detail);
8134    (_a = window.onerror) === null || _a === void 0 ? void 0 : _a.call(window, message, "", 0, 0, error2);
8135  }
8136  logFormattedMessage(identifier, functionName, detail = {}) {
8137    detail = Object.assign({ application: this }, detail);
8138    this.logger.groupCollapsed(`${identifier} #${functionName}`);
8139    this.logger.log("details:", Object.assign({}, detail));
8140    this.logger.groupEnd();
8141  }
8142};
8143function domReady() {
8144  return new Promise((resolve) => {
8145    if (document.readyState == "loading") {
8146      document.addEventListener("DOMContentLoaded", () => resolve());
8147    } else {
8148      resolve();
8149    }
8150  });
8151}
8152function ClassPropertiesBlessing(constructor) {
8153  const classes = readInheritableStaticArrayValues(constructor, "classes");
8154  return classes.reduce((properties, classDefinition) => {
8155    return Object.assign(properties, propertiesForClassDefinition(classDefinition));
8156  }, {});
8157}
8158function propertiesForClassDefinition(key) {
8159  return {
8160    [`${key}Class`]: {
8161      get() {
8162        const { classes } = this;
8163        if (classes.has(key)) {
8164          return classes.get(key);
8165        } else {
8166          const attribute = classes.getAttributeName(key);
8167          throw new Error(`Missing attribute "${attribute}"`);
8168        }
8169      }
8170    },
8171    [`${key}Classes`]: {
8172      get() {
8173        return this.classes.getAll(key);
8174      }
8175    },
8176    [`has${capitalize(key)}Class`]: {
8177      get() {
8178        return this.classes.has(key);
8179      }
8180    }
8181  };
8182}
8183function OutletPropertiesBlessing(constructor) {
8184  const outlets = readInheritableStaticArrayValues(constructor, "outlets");
8185  return outlets.reduce((properties, outletDefinition) => {
8186    return Object.assign(properties, propertiesForOutletDefinition(outletDefinition));
8187  }, {});
8188}
8189function getOutletController(controller, element, identifier) {
8190  return controller.application.getControllerForElementAndIdentifier(element, identifier);
8191}
8192function getControllerAndEnsureConnectedScope(controller, element, outletName) {
8193  let outletController = getOutletController(controller, element, outletName);
8194  if (outletController)
8195    return outletController;
8196  controller.application.router.proposeToConnectScopeForElementAndIdentifier(element, outletName);
8197  outletController = getOutletController(controller, element, outletName);
8198  if (outletController)
8199    return outletController;
8200}
8201function propertiesForOutletDefinition(name) {
8202  const camelizedName = namespaceCamelize(name);
8203  return {
8204    [`${camelizedName}Outlet`]: {
8205      get() {
8206        const outletElement = this.outlets.find(name);
8207        const selector = this.outlets.getSelectorForOutletName(name);
8208        if (outletElement) {
8209          const outletController = getControllerAndEnsureConnectedScope(this, outletElement, name);
8210          if (outletController)
8211            return outletController;
8212          throw new Error(`The provided outlet element is missing an outlet controller "${name}" instance for host controller "${this.identifier}"`);
8213        }
8214        throw new Error(`Missing outlet element "${name}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${selector}".`);
8215      }
8216    },
8217    [`${camelizedName}Outlets`]: {
8218      get() {
8219        const outlets = this.outlets.findAll(name);
8220        if (outlets.length > 0) {
8221          return outlets.map((outletElement) => {
8222            const outletController = getControllerAndEnsureConnectedScope(this, outletElement, name);
8223            if (outletController)
8224              return outletController;
8225            console.warn(`The provided outlet element is missing an outlet controller "${name}" instance for host controller "${this.identifier}"`, outletElement);
8226          }).filter((controller) => controller);
8227        }
8228        return [];
8229      }
8230    },
8231    [`${camelizedName}OutletElement`]: {
8232      get() {
8233        const outletElement = this.outlets.find(name);
8234        const selector = this.outlets.getSelectorForOutletName(name);
8235        if (outletElement) {
8236          return outletElement;
8237        } else {
8238          throw new Error(`Missing outlet element "${name}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${selector}".`);
8239        }
8240      }
8241    },
8242    [`${camelizedName}OutletElements`]: {
8243      get() {
8244        return this.outlets.findAll(name);
8245      }
8246    },
8247    [`has${capitalize(camelizedName)}Outlet`]: {
8248      get() {
8249        return this.outlets.has(name);
8250      }
8251    }
8252  };
8253}
8254function TargetPropertiesBlessing(constructor) {
8255  const targets = readInheritableStaticArrayValues(constructor, "targets");
8256  return targets.reduce((properties, targetDefinition) => {
8257    return Object.assign(properties, propertiesForTargetDefinition(targetDefinition));
8258  }, {});
8259}
8260function propertiesForTargetDefinition(name) {
8261  return {
8262    [`${name}Target`]: {
8263      get() {
8264        const target = this.targets.find(name);
8265        if (target) {
8266          return target;
8267        } else {
8268          throw new Error(`Missing target element "${name}" for "${this.identifier}" controller`);
8269        }
8270      }
8271    },
8272    [`${name}Targets`]: {
8273      get() {
8274        return this.targets.findAll(name);
8275      }
8276    },
8277    [`has${capitalize(name)}Target`]: {
8278      get() {
8279        return this.targets.has(name);
8280      }
8281    }
8282  };
8283}
8284function ValuePropertiesBlessing(constructor) {
8285  const valueDefinitionPairs = readInheritableStaticObjectPairs(constructor, "values");
8286  const propertyDescriptorMap = {
8287    valueDescriptorMap: {
8288      get() {
8289        return valueDefinitionPairs.reduce((result, valueDefinitionPair) => {
8290          const valueDescriptor = parseValueDefinitionPair(valueDefinitionPair, this.identifier);
8291          const attributeName = this.data.getAttributeNameForKey(valueDescriptor.key);
8292          return Object.assign(result, { [attributeName]: valueDescriptor });
8293        }, {});
8294      }
8295    }
8296  };
8297  return valueDefinitionPairs.reduce((properties, valueDefinitionPair) => {
8298    return Object.assign(properties, propertiesForValueDefinitionPair(valueDefinitionPair));
8299  }, propertyDescriptorMap);
8300}
8301function propertiesForValueDefinitionPair(valueDefinitionPair, controller) {
8302  const definition = parseValueDefinitionPair(valueDefinitionPair, controller);
8303  const { key, name, reader: read, writer: write } = definition;
8304  return {
8305    [name]: {
8306      get() {
8307        const value = this.data.get(key);
8308        if (value !== null) {
8309          return read(value);
8310        } else {
8311          return definition.defaultValue;
8312        }
8313      },
8314      set(value) {
8315        if (value === void 0) {
8316          this.data.delete(key);
8317        } else {
8318          this.data.set(key, write(value));
8319        }
8320      }
8321    },
8322    [`has${capitalize(name)}`]: {
8323      get() {
8324        return this.data.has(key) || definition.hasCustomDefaultValue;
8325      }
8326    }
8327  };
8328}
8329function parseValueDefinitionPair([token, typeDefinition], controller) {
8330  return valueDescriptorForTokenAndTypeDefinition({
8331    controller,
8332    token,
8333    typeDefinition
8334  });
8335}
8336function parseValueTypeConstant(constant) {
8337  switch (constant) {
8338    case Array:
8339      return "array";
8340    case Boolean:
8341      return "boolean";
8342    case Number:
8343      return "number";
8344    case Object:
8345      return "object";
8346    case String:
8347      return "string";
8348  }
8349}
8350function parseValueTypeDefault(defaultValue) {
8351  switch (typeof defaultValue) {
8352    case "boolean":
8353      return "boolean";
8354    case "number":
8355      return "number";
8356    case "string":
8357      return "string";
8358  }
8359  if (Array.isArray(defaultValue))
8360    return "array";
8361  if (Object.prototype.toString.call(defaultValue) === "[object Object]")
8362    return "object";
8363}
8364function parseValueTypeObject(payload) {
8365  const { controller, token, typeObject } = payload;
8366  const hasType = isSomething(typeObject.type);
8367  const hasDefault = isSomething(typeObject.default);
8368  const fullObject = hasType && hasDefault;
8369  const onlyType = hasType && !hasDefault;
8370  const onlyDefault = !hasType && hasDefault;
8371  const typeFromObject = parseValueTypeConstant(typeObject.type);
8372  const typeFromDefaultValue = parseValueTypeDefault(payload.typeObject.default);
8373  if (onlyType)
8374    return typeFromObject;
8375  if (onlyDefault)
8376    return typeFromDefaultValue;
8377  if (typeFromObject !== typeFromDefaultValue) {
8378    const propertyPath = controller ? `${controller}.${token}` : token;
8379    throw new Error(`The specified default value for the Stimulus Value "${propertyPath}" must match the defined type "${typeFromObject}". The provided default value of "${typeObject.default}" is of type "${typeFromDefaultValue}".`);
8380  }
8381  if (fullObject)
8382    return typeFromObject;
8383}
8384function parseValueTypeDefinition(payload) {
8385  const { controller, token, typeDefinition } = payload;
8386  const typeObject = { controller, token, typeObject: typeDefinition };
8387  const typeFromObject = parseValueTypeObject(typeObject);
8388  const typeFromDefaultValue = parseValueTypeDefault(typeDefinition);
8389  const typeFromConstant = parseValueTypeConstant(typeDefinition);
8390  const type = typeFromObject || typeFromDefaultValue || typeFromConstant;
8391  if (type)
8392    return type;
8393  const propertyPath = controller ? `${controller}.${typeDefinition}` : token;
8394  throw new Error(`Unknown value type "${propertyPath}" for "${token}" value`);
8395}
8396function defaultValueForDefinition(typeDefinition) {
8397  const constant = parseValueTypeConstant(typeDefinition);
8398  if (constant)
8399    return defaultValuesByType[constant];
8400  const hasDefault = hasProperty(typeDefinition, "default");
8401  const hasType = hasProperty(typeDefinition, "type");
8402  const typeObject = typeDefinition;
8403  if (hasDefault)
8404    return typeObject.default;
8405  if (hasType) {
8406    const { type } = typeObject;
8407    const constantFromType = parseValueTypeConstant(type);
8408    if (constantFromType)
8409      return defaultValuesByType[constantFromType];
8410  }
8411  return typeDefinition;
8412}
8413function valueDescriptorForTokenAndTypeDefinition(payload) {
8414  const { token, typeDefinition } = payload;
8415  const key = `${dasherize(token)}-value`;
8416  const type = parseValueTypeDefinition(payload);
8417  return {
8418    type,
8419    key,
8420    name: camelize(key),
8421    get defaultValue() {
8422      return defaultValueForDefinition(typeDefinition);
8423    },
8424    get hasCustomDefaultValue() {
8425      return parseValueTypeDefault(typeDefinition) !== void 0;
8426    },
8427    reader: readers[type],
8428    writer: writers[type] || writers.default
8429  };
8430}
8431var defaultValuesByType = {
8432  get array() {
8433    return [];
8434  },
8435  boolean: false,
8436  number: 0,
8437  get object() {
8438    return {};
8439  },
8440  string: ""
8441};
8442var readers = {
8443  array(value) {
8444    const array = JSON.parse(value);
8445    if (!Array.isArray(array)) {
8446      throw new TypeError(`expected value of type "array" but instead got value "${value}" of type "${parseValueTypeDefault(array)}"`);
8447    }
8448    return array;
8449  },
8450  boolean(value) {
8451    return !(value == "0" || String(value).toLowerCase() == "false");
8452  },
8453  number(value) {
8454    return Number(value.replace(/_/g, ""));
8455  },
8456  object(value) {
8457    const object = JSON.parse(value);
8458    if (object === null || typeof object != "object" || Array.isArray(object)) {
8459      throw new TypeError(`expected value of type "object" but instead got value "${value}" of type "${parseValueTypeDefault(object)}"`);
8460    }
8461    return object;
8462  },
8463  string(value) {
8464    return value;
8465  }
8466};
8467var writers = {
8468  default: writeString,
8469  array: writeJSON,
8470  object: writeJSON
8471};
8472function writeJSON(value) {
8473  return JSON.stringify(value);
8474}
8475function writeString(value) {
8476  return `${value}`;
8477}
8478var Controller = class {
8479  constructor(context) {
8480    this.context = context;
8481  }
8482  static get shouldLoad() {
8483    return true;
8484  }
8485  static afterLoad(_identifier, _application) {
8486    return;
8487  }
8488  get application() {
8489    return this.context.application;
8490  }
8491  get scope() {
8492    return this.context.scope;
8493  }
8494  get element() {
8495    return this.scope.element;
8496  }
8497  get identifier() {
8498    return this.scope.identifier;
8499  }
8500  get targets() {
8501    return this.scope.targets;
8502  }
8503  get outlets() {
8504    return this.scope.outlets;
8505  }
8506  get classes() {
8507    return this.scope.classes;
8508  }
8509  get data() {
8510    return this.scope.data;
8511  }
8512  initialize() {
8513  }
8514  connect() {
8515  }
8516  disconnect() {
8517  }
8518  dispatch(eventName, { target = this.element, detail = {}, prefix = this.identifier, bubbles = true, cancelable = true } = {}) {
8519    const type = prefix ? `${prefix}:${eventName}` : eventName;
8520    const event = new CustomEvent(type, { detail, bubbles, cancelable });
8521    target.dispatchEvent(event);
8522    return event;
8523  }
8524};
8525Controller.blessings = [
8526  ClassPropertiesBlessing,
8527  TargetPropertiesBlessing,
8528  ValuePropertiesBlessing,
8529  OutletPropertiesBlessing
8530];
8531Controller.targets = [];
8532Controller.outlets = [];
8533Controller.values = {};
8534
8535// app/javascript/controllers/infinite_scroll_controller.js
8536var infinite_scroll_controller_default = class extends Controller {
8537  static values = {
8538    url: String,
8539    page: { type: Number, default: 1 }
8540  };
8541  connect() {
8542    this.hidePagination();
8543    this.createObserver();
8544  }
8545  disconnect() {
8546    if (this.observer) {
8547      this.observer.disconnect();
8548    }
8549    this.hideLoadingSpinner();
8550  }
8551  hidePagination() {
8552    const paginationElements = document.querySelectorAll('.pagination, [class*="paginate"]');
8553    paginationElements.forEach((element) => {
8554      if (element.querySelector("a") || element.classList.contains("pagination")) {
8555        element.style.display = "none";
8556      }
8557    });
8558  }
8559  createObserver() {
8560    this.observer = new IntersectionObserver(
8561      (entries) => {
8562        entries.forEach((entry) => {
8563          if (entry.isIntersecting && !this.loading) {
8564            this.loadMore();
8565          }
8566        });
8567      },
8568      {
8569        rootMargin: "300px"
8570      }
8571    );
8572    const sentinel = this.element.parentElement.querySelector("#scroll-sentinel");
8573    if (sentinel) {
8574      this.observer.observe(sentinel);
8575    }
8576  }
8577  showLoadingSpinner() {
8578    this.hideLoadingSpinner();
8579    const spinner = document.createElement("div");
8580    spinner.id = "infinite-scroll-spinner";
8581    spinner.className = "flex justify-center items-center py-8";
8582    spinner.innerHTML = `
8583      <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
8584      <span class="ml-3 text-gray-600">Loading more videos...</span>
8585    `;
8586    this.element.appendChild(spinner);
8587  }
8588  hideLoadingSpinner() {
8589    const spinner = this.element.querySelector("#infinite-scroll-spinner");
8590    if (spinner) {
8591      spinner.remove();
8592    }
8593  }
8594  async loadMore() {
8595    if (this.loading) return;
8596    this.loading = true;
8597    this.showLoadingSpinner();
8598    this.pageValue++;
8599    try {
8600      const url = new URL(this.urlValue, window.location.origin);
8601      url.searchParams.set("page", this.pageValue);
8602      const response = await fetch(url, {
8603        headers: {
8604          "Accept": "text/vnd.turbo-stream.html"
8605        }
8606      });
8607      if (response.ok) {
8608        const turboStream = await response.text();
8609        Turbo.renderStreamMessage(turboStream);
8610      } else {
8611        this.disconnect();
8612      }
8613    } catch (error2) {
8614      console.error("Error loading more videos:", error2);
8615    } finally {
8616      this.loading = false;
8617      this.hideLoadingSpinner();
8618    }
8619  }
8620};
8621
8622// app/javascript/controllers/dropdown_controller.js
8623var dropdown_controller_default = class extends Controller {
8624  static targets = ["menu"];
8625  toggle() {
8626    this.menuTarget.classList.toggle("hidden");
8627  }
8628  hide(event) {
8629    if (!this.element.contains(event.target)) {
8630      this.menuTarget.classList.add("hidden");
8631    }
8632  }
8633  connect() {
8634    document.addEventListener("click", this.hide.bind(this));
8635  }
8636  disconnect() {
8637    document.removeEventListener("click", this.hide.bind(this));
8638  }
8639};
8640
8641// app/javascript/controllers/modal_controller.js
8642var modal_controller_default = class extends Controller {
8643  static targets = ["modal"];
8644  connect() {
8645    this.escapeHandler = this.escape.bind(this);
8646    document.addEventListener("keydown", this.escapeHandler);
8647  }
8648  disconnect() {
8649    document.removeEventListener("keydown", this.escapeHandler);
8650  }
8651  open() {
8652    this.modalTarget.classList.remove("hidden");
8653    document.body.style.overflow = "hidden";
8654  }
8655  close() {
8656    this.modalTarget.classList.add("hidden");
8657    document.body.style.overflow = "";
8658  }
8659  escape(event) {
8660    if (event.key === "Escape" && !this.modalTarget.classList.contains("hidden")) {
8661      this.close();
8662    }
8663  }
8664  // Close modal when clicking on backdrop
8665  backdropClose(event) {
8666    if (event.target === this.modalTarget) {
8667      this.close();
8668    }
8669  }
8670};
8671
8672// app/javascript/controllers/playlist_filter_controller.js
8673var playlist_filter_controller_default = class extends Controller {
8674  static targets = ["search", "playlist"];
8675  connect() {
8676    this.filter();
8677  }
8678  filter() {
8679    const query = this.searchTarget.value.toLowerCase().trim();
8680    this.playlistTargets.forEach((playlist) => {
8681      const title = playlist.dataset.title;
8682      const shouldShow = query === "" || title.includes(query);
8683      playlist.style.display = shouldShow ? "" : "none";
8684    });
8685  }
8686};
8687
8688// app/javascript/controllers/deep_dive_controller.js
8689var deep_dive_controller_default = class extends Controller {
8690  static targets = ["chatMessages", "elapsedText", "channelName", "progressFill", "drillDown", "breadcrumb", "questionList", "menu", "menuButton"];
8691  static values = {
8692    videoId: String,
8693    startTime: Number,
8694    stopTime: Number,
8695    duration: Number,
8696    pollUrl: String,
8697    drillDownUrl: String
8698  };
8699  connect() {
8700    this._startTimer();
8701    this._startPoller();
8702    this._drillDownCache = {};
8703    this._breadcrumbPath = [];
8704    this._handleEscape = this._handleEscape.bind(this);
8705    this._handleClickOutside = this._handleClickOutside.bind(this);
8706    document.addEventListener("keydown", this._handleEscape);
8707    document.addEventListener("click", this._handleClickOutside);
8708  }
8709  disconnect() {
8710    this._stopTimer();
8711    this._stopPoller();
8712    document.removeEventListener("keydown", this._handleEscape);
8713    document.removeEventListener("click", this._handleClickOutside);
8714  }
8715  // -- Menu Toggle ------------------------------------------------------------
8716  toggleMenu() {
8717    if (!this.hasMenuTarget) return;
8718    const isOpen = this.menuTarget.classList.toggle("nf-ss-menu--open");
8719    this.menuTarget.inert = !isOpen;
8720    if (this.hasMenuButtonTarget) {
8721      this.menuButtonTarget.setAttribute("aria-expanded", String(isOpen));
8722    }
8723  }
8724  closeMenu() {
8725    if (!this.hasMenuTarget) return;
8726    this.menuTarget.classList.remove("nf-ss-menu--open");
8727    this.menuTarget.inert = true;
8728    if (this.hasMenuButtonTarget) {
8729      this.menuButtonTarget.setAttribute("aria-expanded", "false");
8730    }
8731  }
8732  _handleEscape(event) {
8733    if (event.key === "Escape") {
8734      this.closeMenu();
8735    }
8736  }
8737  _handleClickOutside(event) {
8738    if (!this.hasMenuTarget) return;
8739    const menuOpen = this.menuTarget.classList.contains("nf-ss-menu--open");
8740    if (!menuOpen) return;
8741    const clickedInsideMenu = this.menuTarget.contains(event.target);
8742    const clickedMenuButton = this.hasMenuButtonTarget && this.menuButtonTarget.contains(event.target);
8743    if (!clickedInsideMenu && !clickedMenuButton) {
8744      this.closeMenu();
8745    }
8746  }
8747  // -- Drill-Down Q&A --------------------------------------------------------
8748  expandQuestion(event) {
8749    const button = event.currentTarget;
8750    const pathKey = button.dataset.pathKey;
8751    const question = button.dataset.question;
8752    const preloadedAnswer = button.dataset.answer;
8753    const parentQuestion = button.dataset.parentQuestion || "";
8754    const parentAnswer = button.dataset.parentAnswer || "";
8755    const questionTrail = this._parseQuestionTrail(button.dataset.questionTrail);
8756    const existingThread = document.getElementById(`drill-thread-${pathKey}`);
8757    if (existingThread) {
8758      existingThread.remove();
8759      button.classList.remove("nf-ss-question-chip--expanded");
8760      const isInBreadcrumb = this._breadcrumbPath.some((entry) => entry.pathKey === pathKey || entry.pathKey.startsWith(pathKey + "."));
8761      if (isInBreadcrumb) {
8762        const depth = pathKey.split(".").length;
8763        this._breadcrumbPath = this._breadcrumbPath.slice(0, depth - 1);
8764        this._renderBreadcrumb();
8765      }
8766      return;
8767    }
8768    button.classList.add("nf-ss-question-chip--expanded");
8769    const thread = this._buildThread(pathKey);
8770    button.insertAdjacentElement("afterend", thread);
8771    if (preloadedAnswer) {
8772      const trailForFetch = [...questionTrail, question];
8773      this._insertAnswer(thread, preloadedAnswer);
8774      this._addLoadingPlaceholder(thread, pathKey);
8775      this._updateBreadcrumb(pathKey, question);
8776      this._fetchSubQuestions(pathKey, question, parentQuestion || question, parentAnswer || preloadedAnswer, trailForFetch);
8777    } else {
8778      const trailForFetch = [...questionTrail, question];
8779      this._addLoadingPlaceholder(thread, pathKey);
8780      this._updateBreadcrumb(pathKey, question);
8781      this._fetchDrillDown(pathKey, question, parentQuestion, parentAnswer, trailForFetch);
8782    }
8783    this._scrollToElement(thread);
8784  }
8785  retryDrillDown(event) {
8786    const button = event.currentTarget;
8787    const pathKey = button.dataset.pathKey;
8788    const question = button.dataset.question;
8789    const parentQuestion = button.dataset.parentQuestion || "";
8790    const parentAnswer = button.dataset.parentAnswer || "";
8791    const questionTrail = this._parseQuestionTrail(button.dataset.questionTrail);
8792    const error2 = document.getElementById(`drill-error-${pathKey}`);
8793    if (error2) error2.remove();
8794    const thread = document.getElementById(`drill-thread-${pathKey}`);
8795    if (!thread) return;
8796    const subContainer = thread.querySelector(".nf-ss-drill-sub-container");
8797    if (subContainer) {
8798      this._addLoadingToContainer(subContainer, pathKey);
8799    }
8800    const answerBubble = thread.querySelector(".nf-ss-bubble-companion");
8801    if (answerBubble) {
8802      this._fetchSubQuestions(pathKey, question, parentQuestion, parentAnswer, questionTrail);
8803    } else {
8804      this._fetchDrillDown(pathKey, question, parentQuestion, parentAnswer, questionTrail);
8805    }
8806  }
8807  navigateBreadcrumb(event) {
8808    const targetDepth = parseInt(event.currentTarget.dataset.depth, 10);
8809    if (this.hasDrillDownTarget) {
8810      const allThreads = this.drillDownTarget.querySelectorAll(".nf-ss-drill-thread");
8811      allThreads.forEach((thread) => {
8812        const threadPathKey = thread.dataset.pathKey;
8813        const threadDepth = threadPathKey.split(".").length;
8814        if (threadDepth > targetDepth) {
8815          const parentButton = this.drillDownTarget.querySelector(
8816            `.nf-ss-question-chip--expanded[data-path-key="${threadPathKey}"]`
8817          );
8818          if (parentButton) {
8819            parentButton.classList.remove("nf-ss-question-chip--expanded");
8820          }
8821          thread.remove();
8822        }
8823      });
8824    }
8825    this._breadcrumbPath = this._breadcrumbPath.slice(0, targetDepth);
8826    this._renderBreadcrumb();
8827  }
8828  // -- Drill-Down Private Helpers ---------------------------------------------
8829  async _fetchDrillDown(pathKey, question, parentQuestion, parentAnswer, questionTrail) {
8830    const cached = this._drillDownCache[pathKey];
8831    if (cached) {
8832      this._handleDrillDownResult(pathKey, question, parentQuestion, parentAnswer, cached, questionTrail);
8833      return;
8834    }
8835    try {
8836      const response = await fetch(this.drillDownUrlValue, {
8837        method: "POST",
8838        headers: {
8839          "Content-Type": "application/json",
8840          "Accept": "application/json",
8841          "X-CSRF-Token": this._csrfToken()
8842        },
8843        body: JSON.stringify({
8844          video_id: this.videoIdValue,
8845          path_key: pathKey,
8846          question,
8847          parent_question: parentQuestion,
8848          parent_answer: parentAnswer,
8849          question_trail: questionTrail
8850        })
8851      });
8852      if (!response.ok) {
8853        this._showDrillDownError(pathKey, question, parentQuestion, parentAnswer, questionTrail);
8854        return;
8855      }
8856      const data = await response.json();
8857      if (data.error === true) {
8858        this._showDrillDownError(pathKey, question, parentQuestion, parentAnswer, questionTrail);
8859        return;
8860      }
8861      this._drillDownCache[pathKey] = data;
8862      this._handleDrillDownResult(pathKey, question, parentQuestion, parentAnswer, data, questionTrail);
8863    } catch (_error) {
8864      this._showDrillDownError(pathKey, question, parentQuestion, parentAnswer, questionTrail);
8865    }
8866  }
8867  async _fetchSubQuestions(pathKey, question, parentQuestion, parentAnswer, questionTrail) {
8868    const cached = this._drillDownCache[pathKey];
8869    if (cached && cached.sub_questions && cached.sub_questions.length > 0) {
8870      this._removeLoadingPlaceholder(pathKey);
8871      this._insertSubQuestions(pathKey, cached.sub_questions, question, cached.answer || parentAnswer, questionTrail);
8872      return;
8873    }
8874    try {
8875      const response = await fetch(this.drillDownUrlValue, {
8876        method: "POST",
8877        headers: {
8878          "Content-Type": "application/json",
8879          "Accept": "application/json",
8880          "X-CSRF-Token": this._csrfToken()
8881        },
8882        body: JSON.stringify({
8883          video_id: this.videoIdValue,
8884          path_key: pathKey,
8885          question,
8886          parent_question: parentQuestion,
8887          parent_answer: parentAnswer,
8888          question_trail: questionTrail
8889        })
8890      });
8891      if (!response.ok) {
8892        this._removeLoadingPlaceholder(pathKey);
8893        this._showDrillDownError(pathKey, question, parentQuestion, parentAnswer, questionTrail);
8894        return;
8895      }
8896      const data = await response.json();
8897      if (!data.sub_questions || data.sub_questions.length === 0) {
8898        this._removeLoadingPlaceholder(pathKey);
8899        this._showDrillDownError(pathKey, question, parentQuestion, parentAnswer, questionTrail);
8900        return;
8901      }
8902      this._drillDownCache[pathKey] = data;
8903      this._removeLoadingPlaceholder(pathKey);
8904      this._insertSubQuestions(pathKey, data.sub_questions, question, data.answer || parentAnswer, questionTrail);
8905    } catch (_error) {
8906      this._removeLoadingPlaceholder(pathKey);
8907      this._showDrillDownError(pathKey, question, parentQuestion, parentAnswer, questionTrail);
8908    }
8909  }
8910  _handleDrillDownResult(pathKey, question, parentQuestion, parentAnswer, data, questionTrail) {
8911    const thread = document.getElementById(`drill-thread-${pathKey}`);
8912    if (!thread) return;
8913    this._removeLoadingPlaceholder(pathKey);
8914    const existingAnswer = thread.querySelector(".nf-ss-bubble-companion");
8915    if (!existingAnswer && data.answer) {
8916      this._insertAnswer(thread, data.answer, data.answer_html);
8917    }
8918    if (data.sub_questions && data.sub_questions.length > 0) {
8919      this._insertSubQuestions(pathKey, data.sub_questions, question, data.answer || parentAnswer, questionTrail);
8920    }
8921  }
8922  _buildThread(pathKey) {
8923    const thread = document.createElement("div");
8924    thread.classList.add("nf-ss-drill-thread", "nf-ss-animate-in");
8925    thread.id = `drill-thread-${pathKey}`;
8926    thread.dataset.pathKey = pathKey;
8927    const depth = pathKey.split(".").length;
8928    if (depth > 1) {
8929      thread.classList.add("nf-ss-drill-thread--nested");
8930      thread.style.setProperty("--drill-depth", depth);
8931    }
8932    const subContainer = document.createElement("div");
8933    subContainer.classList.add("nf-ss-drill-sub-container");
8934    thread.appendChild(subContainer);
8935    return thread;
8936  }
8937  _insertAnswer(thread, answerText, answerHtml) {
8938    const subContainer = thread.querySelector(".nf-ss-drill-sub-container");
8939    const bubble = document.createElement("div");
8940    bubble.classList.add("nf-ss-bubble-companion", "nf-ss-animate-in");
8941    if (answerHtml) {
8942      bubble.innerHTML = answerHtml;
8943    } else {
8944      const paragraphs = answerText.split(/\n\n+/);
8945      bubble.innerHTML = paragraphs.map((p) => `<p>${this._escapeHtml(p.trim())}</p>`).join("");
8946    }
8947    if (subContainer) {
8948      subContainer.insertBefore(bubble, subContainer.firstChild);
8949    } else {
8950      thread.appendChild(bubble);
8951    }
8952  }
8953  _addLoadingPlaceholder(thread, pathKey) {
8954    const subContainer = thread.querySelector(".nf-ss-drill-sub-container");
8955    if (subContainer) {
8956      this._addLoadingToContainer(subContainer, pathKey);
8957    }
8958  }
8959  _addLoadingToContainer(container, pathKey) {
8960    const loading = document.createElement("div");
8961    loading.classList.add("nf-ss-skeleton-chips");
8962    loading.id = `drill-loading-${pathKey}`;
8963    loading.setAttribute("aria-label", "Loading follow-up questions");
8964    for (let i = 0; i < 3; i++) {
8965      const chip = document.createElement("div");
8966      chip.classList.add("nf-ss-skeleton-chip");
8967      loading.appendChild(chip);
8968    }
8969    container.appendChild(loading);
8970  }
8971  _removeLoadingPlaceholder(pathKey) {
8972    const loading = document.getElementById(`drill-loading-${pathKey}`);
8973    if (loading) loading.remove();
8974  }
8975  _insertSubQuestions(pathKey, subQuestions, parentQuestion, parentAnswer, questionTrail) {
8976    const thread = document.getElementById(`drill-thread-${pathKey}`);
8977    if (!thread) return;
8978    const subContainer = thread.querySelector(".nf-ss-drill-sub-container");
8979    if (!subContainer) return;
8980    if (subContainer.querySelector(".nf-ss-question-list")) return;
8981    const list = document.createElement("div");
8982    list.classList.add("nf-ss-question-list", "nf-ss-animate-in");
8983    const childTrail = Array.isArray(questionTrail) ? questionTrail : [];
8984    subQuestions.forEach((subQ, index) => {
8985      const subPathKey = `${pathKey}.${index}`;
8986      const button = document.createElement("button");
8987      button.classList.add("nf-ss-question-chip");
8988      button.dataset.action = "click->deep-dive#expandQuestion";
8989      button.dataset.pathKey = subPathKey;
8990      button.dataset.question = subQ;
8991      button.dataset.answer = "";
8992      button.dataset.parentQuestion = parentQuestion;
8993      button.dataset.parentAnswer = parentAnswer;
8994      button.dataset.questionTrail = JSON.stringify(childTrail);
8995      const textSpan = document.createElement("span");
8996      textSpan.classList.add("nf-ss-question-chip-text");
8997      textSpan.textContent = subQ;
8998      button.appendChild(textSpan);
8999      const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
9000      svg.classList.add("nf-ss-question-chip-chevron");
9001      svg.setAttribute("width", "16");
9002      svg.setAttribute("height", "16");
9003      svg.setAttribute("viewBox", "0 0 16 16");
9004      svg.setAttribute("fill", "none");
9005      svg.setAttribute("aria-hidden", "true");
9006      const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
9007      path.setAttribute("d", "M6 4l4 4-4 4");
9008      path.setAttribute("stroke", "currentColor");
9009      path.setAttribute("stroke-width", "1.5");
9010      path.setAttribute("stroke-linecap", "round");
9011      path.setAttribute("stroke-linejoin", "round");
9012      svg.appendChild(path);
9013      button.appendChild(svg);
9014      list.appendChild(button);
9015    });
9016    subContainer.appendChild(list);
9017  }
9018  _showDrillDownError(pathKey, question, parentQuestion, parentAnswer, questionTrail) {
9019    const existing = document.getElementById(`drill-error-${pathKey}`);
9020    if (existing) existing.remove();
9021    const thread = document.getElementById(`drill-thread-${pathKey}`);
9022    if (!thread) return;
9023    const subContainer = thread.querySelector(".nf-ss-drill-sub-container");
9024    const target = subContainer || thread;
9025    const error2 = document.createElement("div");
9026    error2.classList.add("nf-ss-drill-error", "nf-ss-animate-in");
9027    error2.id = `drill-error-${pathKey}`;
9028    const message = document.createElement("span");
9029    message.textContent = "Couldn't load follow-up questions \u2014 try again.";
9030    error2.appendChild(message);
9031    const retryBtn = document.createElement("button");
9032    retryBtn.classList.add("nf-ss-drill-retry");
9033    retryBtn.textContent = "Retry";
9034    retryBtn.dataset.action = "click->deep-dive#retryDrillDown";
9035    retryBtn.dataset.pathKey = pathKey;
9036    retryBtn.dataset.question = question;
9037    retryBtn.dataset.parentQuestion = parentQuestion;
9038    retryBtn.dataset.parentAnswer = parentAnswer;
9039    retryBtn.dataset.questionTrail = JSON.stringify(Array.isArray(questionTrail) ? questionTrail : []);
9040    error2.appendChild(retryBtn);
9041    target.appendChild(error2);
9042  }
9043  _updateBreadcrumb(pathKey, question) {
9044    const depth = pathKey.split(".").length;
9045    this._breadcrumbPath = this._breadcrumbPath.slice(0, depth - 1);
9046    this._breadcrumbPath.push({ pathKey, question });
9047    this._renderBreadcrumb();
9048  }
9049  _renderBreadcrumb() {
9050    if (!this.hasBreadcrumbTarget) return;
9051    const breadcrumb = this.breadcrumbTarget;
9052    if (this._breadcrumbPath.length === 0) {
9053      breadcrumb.hidden = true;
9054      return;
9055    }
9056    breadcrumb.hidden = false;
9057    breadcrumb.innerHTML = "";
9058    const root = document.createElement("span");
9059    root.classList.add("nf-ss-breadcrumb-item");
9060    root.dataset.action = "click->deep-dive#navigateBreadcrumb";
9061    root.dataset.depth = "0";
9062    root.textContent = "Questions";
9063    breadcrumb.appendChild(root);
9064    this._breadcrumbPath.forEach((entry, index) => {
9065      const sep = document.createElement("span");
9066      sep.classList.add("nf-ss-breadcrumb-separator");
9067      sep.textContent = "\u203A";
9068      sep.setAttribute("aria-hidden", "true");
9069      breadcrumb.appendChild(sep);
9070      const item = document.createElement("span");
9071      const isLast = index === this._breadcrumbPath.length - 1;
9072      item.classList.add("nf-ss-breadcrumb-item");
9073      if (isLast) {
9074        item.classList.add("nf-ss-breadcrumb-item--active");
9075      }
9076      item.dataset.action = "click->deep-dive#navigateBreadcrumb";
9077      item.dataset.depth = String(index + 1);
9078      item.textContent = this._truncate(entry.question, 30);
9079      breadcrumb.appendChild(item);
9080    });
9081  }
9082  _parseQuestionTrail(raw) {
9083    if (!raw) return [];
9084    try {
9085      const parsed = JSON.parse(raw);
9086      return Array.isArray(parsed) ? parsed : [];
9087    } catch (_e) {
9088      return [];
9089    }
9090  }
9091  _truncate(text, maxLength) {
9092    if (!text || text.length <= maxLength) return text;
9093    return text.substring(0, maxLength - 1) + "\u2026";
9094  }
9095  _scrollToElement(element) {
9096    requestAnimationFrame(() => {
9097      element.scrollIntoView({ behavior: "smooth", block: "nearest" });
9098    });
9099  }
9100  _csrfToken() {
9101    const meta = document.querySelector('meta[name="csrf-token"]');
9102    return meta ? meta.getAttribute("content") : "";
9103  }
9104  // -- Progress Timer --------------------------------------------------------
9105  _startTimer() {
9106    if (!this.hasDurationValue || this.durationValue === 0) return;
9107    if (!this.hasStartTimeValue || !this.hasStopTimeValue) return;
9108    this._programmeEnded = false;
9109    this._updateTimer();
9110    this._timerInterval = setInterval(() => this._updateTimer(), 1e3);
9111  }
9112  _stopTimer() {
9113    if (this._timerInterval) {
9114      clearInterval(this._timerInterval);
9115      this._timerInterval = null;
9116    }
9117  }
9118  _updateTimer() {
9119    const nowSeconds = Math.floor(Date.now() / 1e3);
9120    const elapsed = Math.max(0, nowSeconds - this.startTimeValue);
9121    const remaining = Math.max(0, this.stopTimeValue - nowSeconds);
9122    const duration = this.durationValue;
9123    if (elapsed >= duration || remaining <= 0) {
9124      this._showEnded();
9125      return;
9126    }
9127    const elapsedMin = Math.round(elapsed / 60);
9128    const totalMin = Math.round(duration / 60);
9129    const percentage = Math.min(100, elapsed / duration * 100);
9130    if (this.hasElapsedTextTarget) {
9131      this.elapsedTextTarget.textContent = `${elapsedMin} min in of ${totalMin}`;
9132    }
9133    if (this.hasProgressFillTarget) {
9134      this.progressFillTarget.style.width = `${percentage.toFixed(1)}%`;
9135    }
9136  }
9137  _showEnded() {
9138    if (this._programmeEnded) return;
9139    this._programmeEnded = true;
9140    this._stopTimer();
9141    if (this.hasElapsedTextTarget) {
9142      this.elapsedTextTarget.textContent = "Ended";
9143    }
9144    if (this.hasProgressFillTarget) {
9145      this.progressFillTarget.style.width = "100%";
9146    }
9147    this._pollForProgrammeChange();
9148  }
9149  // -- Programme Change Polling ----------------------------------------------
9150  _startPoller() {
9151    if (!this.hasPollUrlValue || this.pollUrlValue === "") return;
9152    this._pollInterval = setInterval(() => this._pollForProgrammeChange(), 3e4);
9153  }
9154  _stopPoller() {
9155    if (this._pollInterval) {
9156      clearInterval(this._pollInterval);
9157      this._pollInterval = null;
9158    }
9159  }
9160  async _pollForProgrammeChange() {
9161    try {
9162      const response = await fetch(this.pollUrlValue, {
9163        headers: { "Accept": "application/json" }
9164      });
9165      if (!response.ok) return;
9166      const data = await response.json();
9167      this._handlePollResponse(data);
9168    } catch (_error) {
9169    }
9170  }
9171  _handlePollResponse(data) {
9172    if (!data.playing) {
9173      if (this.hasVideoIdValue && this.videoIdValue !== "") {
9174        this._showBanner("The current programme has ended.", null, null);
9175      }
9176      return;
9177    }
9178    if (!this.hasVideoIdValue || this.videoIdValue === "") {
9179      this._showBanner(`Now Playing: ${data.title}`, "Watch along", this._buildShowUrl());
9180      return;
9181    }
9182    if (data.zype_video_id !== this.videoIdValue) {
9183      this._showBanner(`Now Playing: ${data.title}`, "Switch", this._buildShowUrl());
9184    }
9185  }
9186  _showBanner(message, buttonText, buttonUrl) {
9187    if (!this.hasChatMessagesTarget) return;
9188    const existing = this.chatMessagesTarget.querySelector(".nf-ss-programme-change");
9189    if (existing) existing.remove();
9190    const banner = document.createElement("div");
9191    banner.classList.add("nf-ss-programme-change", "nf-ss-animate-in");
9192    let html = `<span class="nf-ss-programme-change-title">${this._escapeHtml(message)}</span>`;
9193    if (buttonText && buttonUrl) {
9194      html += `<a href="${this._escapeHtml(buttonUrl)}" class="nf-ss-programme-change-btn" data-turbo-action="replace">${this._escapeHtml(buttonText)}</a>`;
9195    }
9196    banner.innerHTML = html;
9197    this.chatMessagesTarget.insertBefore(banner, this.chatMessagesTarget.firstChild);
9198  }
9199  _buildShowUrl() {
9200    return window.location.pathname;
9201  }
9202  _escapeHtml(text) {
9203    const div = document.createElement("div");
9204    div.textContent = text;
9205    return div.innerHTML;
9206  }
9207};
9208
9209// app/javascript/lib/sse_reader.js
9210function parseFrame(rawFrame) {
9211  let event = "message";
9212  const dataLines = [];
9213  for (const line of rawFrame.split("\n")) {
9214    if (line.startsWith("event:")) {
9215      event = line.slice("event:".length).trim();
9216    } else if (line.startsWith("data:")) {
9217      dataLines.push(line.slice("data:".length).replace(/^ /, ""));
9218    }
9219  }
9220  if (dataLines.length === 0) return null;
9221  let data = null;
9222  try {
9223    data = JSON.parse(dataLines.join("\n"));
9224  } catch (_e) {
9225    data = null;
9226  }
9227  return { event, data };
9228}
9229async function readSse(body, onFrame) {
9230  const reader = body.getReader();
9231  const decoder = new TextDecoder();
9232  let buffer = "";
9233  try {
9234    for (; ; ) {
9235      const { value, done } = await reader.read();
9236      if (done) break;
9237      buffer += decoder.decode(value, { stream: true });
9238      let delimiter;
9239      while ((delimiter = indexOfFrameBreak(buffer)) !== -1) {
9240        const rawFrame = buffer.slice(0, delimiter.index);
9241        buffer = buffer.slice(delimiter.index + delimiter.length);
9242        const frame = parseFrame(rawFrame);
9243        if (frame) onFrame(frame);
9244      }
9245    }
9246  } finally {
9247    reader.releaseLock();
9248  }
9249}
9250function indexOfFrameBreak(buffer) {
9251  const lf = buffer.indexOf("\n\n");
9252  const crlf = buffer.indexOf("\r\n\r\n");
9253  if (lf === -1 && crlf === -1) return -1;
9254  if (crlf === -1 || lf !== -1 && lf < crlf) return { index: lf, length: 2 };
9255  return { index: crlf, length: 4 };
9256}
9257
9258// app/javascript/controllers/deep_dive_stream_controller.js
9259var deep_dive_stream_controller_default = class extends Controller {
9260  static targets = ["streaming"];
9261  static values = {
9262    mode: String,
9263    url: String,
9264    fallback: String
9265  };
9266  connect() {
9267    this._abortController = new AbortController();
9268    this._streamBuffer = "";
9269    this._open();
9270  }
9271  disconnect() {
9272    if (this._abortController) {
9273      this._abortController.abort();
9274      this._abortController = null;
9275    }
9276  }
9277  async _open() {
9278    try {
9279      const response = await fetch(this.urlValue, {
9280        headers: {
9281          "Accept": "text/event-stream",
9282          "X-CSRF-Token": this._csrfToken()
9283        },
9284        signal: this._abortController.signal
9285      });
9286      if (!response.ok || !response.body) {
9287        this._handleError();
9288        return;
9289      }
9290      const contentType = response.headers.get("Content-Type") || "";
9291      if (response.redirected || !contentType.includes("text/event-stream")) {
9292        this._handleError();
9293        return;
9294      }
9295      await readSse(response.body, (frame) => this._handleFrame(frame));
9296    } catch (error2) {
9297      if (error2 && error2.name === "AbortError") return;
9298      this._handleError();
9299    }
9300  }
9301  _handleFrame({ event, data }) {
9302    switch (event) {
9303      case "delta":
9304        this._appendDelta(data);
9305        break;
9306      case "done":
9307        this._finalise(data);
9308        break;
9309      case "error":
9310        this._handleError(data);
9311        break;
9312    }
9313  }
9314  _appendDelta(data) {
9315    if (!data || typeof data.text !== "string") return;
9316    if (!this.hasStreamingTarget) return;
9317    this._streamBuffer += data.text;
9318    this._renderParagraphs(this._streamBuffer);
9319  }
9320  // Renders the accumulated prose as <p> tags split on blank lines, so the
9321  // progressive preview shows paragraph breaks instead of one run-on block.
9322  // Uses textContent per paragraph (XSS-safe); the `done` event later swaps the
9323  // whole element for the canonical server-sanitized HTML.
9324  _renderParagraphs(text) {
9325    let paragraphs = text.split(/\n{2,}/);
9326    if (paragraphs.length > 1 && paragraphs[paragraphs.length - 1].trim() === "") {
9327      paragraphs = paragraphs.slice(0, -1);
9328    }
9329    const target = this.streamingTarget;
9330    paragraphs.forEach((para, i) => {
9331      let p = target.children[i];
9332      if (!p) {
9333        p = document.createElement("p");
9334        target.appendChild(p);
9335      }
9336      if (p.textContent !== para) p.textContent = para;
9337    });
9338    while (target.children.length > paragraphs.length) {
9339      target.removeChild(target.lastChild);
9340    }
9341  }
9342  // Swaps the live preview for the canonical sanitized HTML and enables the
9343  // collapsible-bubble behaviour against the final DOM.
9344  _finalise(data) {
9345    if (this.modeValue === "brief" && data && typeof data.html === "string") {
9346      this.element.innerHTML = data.html;
9347      this._attachCollapsibleBubble();
9348    }
9349  }
9350  _handleError(data) {
9351    if (!this.hasStreamingTarget) return;
9352    const message = data && data.message || this.fallbackValue || "";
9353    this.streamingTarget.textContent = message;
9354  }
9355  // Appends (rather than replaces) so this controller stays attached; Stimulus
9356  // observes the mutated data-controller list and connects collapsible-bubble,
9357  // whose connect() runs once against the now-final HTML.
9358  _attachCollapsibleBubble() {
9359    const controllers = (this.element.getAttribute("data-controller") || "").split(/\s+/);
9360    if (!controllers.includes("collapsible-bubble")) {
9361      controllers.push("collapsible-bubble");
9362    }
9363    this.element.setAttribute("data-controller", controllers.join(" ").trim());
9364    this.element.setAttribute(
9365      "data-action",
9366      "click->collapsible-bubble#toggle keydown->collapsible-bubble#toggleOnKey"
9367    );
9368  }
9369  _csrfToken() {
9370    const meta = document.querySelector('meta[name="csrf-token"]');
9371    return meta ? meta.getAttribute("content") : "";
9372  }
9373};
9374
9375// app/javascript/controllers/collapsible_bubble_controller.js
9376var COLLAPSED_CLASS = "nf-ss-bubble-companion--collapsed";
9377var EXTRAS_CLASS = "nf-ss-bubble-extra";
9378var EXTRAS_INNER_CLASS = "nf-ss-bubble-extra-inner";
9379var CHEVRON_CLASS = "nf-ss-bubble-companion-chevron";
9380var collapsible_bubble_controller_default = class extends Controller {
9381  static values = {
9382    initial: { type: String, default: "expanded" }
9383  };
9384  connect() {
9385    this._wrapExtras();
9386    this._injectChevron();
9387    this._applyAria();
9388    this._applyInitialState();
9389  }
9390  disconnect() {
9391    const chevron = this.element.querySelector(`:scope > .${CHEVRON_CLASS}`);
9392    if (chevron) chevron.remove();
9393    const extras = this.element.querySelector(`:scope > .${EXTRAS_CLASS}`);
9394    if (extras) {
9395      const inner = extras.querySelector(`:scope > .${EXTRAS_INNER_CLASS}`);
9396      if (inner) {
9397        while (inner.firstChild) {
9398          this.element.insertBefore(inner.firstChild, extras);
9399        }
9400        inner.remove();
9401      }
9402      extras.remove();
9403    }
9404  }
9405  toggle(event) {
9406    if (event && event.target.closest("a")) return;
9407    const collapsed = this.element.classList.toggle(COLLAPSED_CLASS);
9408    this.element.setAttribute("aria-expanded", collapsed ? "false" : "true");
9409  }
9410  toggleOnKey(event) {
9411    if (event.key !== "Enter" && event.key !== " ") return;
9412    if (event.target.closest("a")) return;
9413    event.preventDefault();
9414    this.toggle(event);
9415  }
9416  _wrapExtras() {
9417    const children = Array.from(this.element.children);
9418    const outer = document.createElement("div");
9419    outer.classList.add(EXTRAS_CLASS);
9420    const inner = document.createElement("div");
9421    inner.classList.add(EXTRAS_INNER_CLASS);
9422    outer.appendChild(inner);
9423    children.slice(1).forEach((child) => inner.appendChild(child));
9424    this.element.appendChild(outer);
9425  }
9426  _injectChevron() {
9427    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
9428    svg.classList.add(CHEVRON_CLASS);
9429    svg.setAttribute("width", "16");
9430    svg.setAttribute("height", "16");
9431    svg.setAttribute("viewBox", "0 0 16 16");
9432    svg.setAttribute("fill", "none");
9433    svg.setAttribute("aria-hidden", "true");
9434    const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
9435    path.setAttribute("d", "M4 6l4 4 4-4");
9436    path.setAttribute("stroke", "currentColor");
9437    path.setAttribute("stroke-width", "1.5");
9438    path.setAttribute("stroke-linecap", "round");
9439    path.setAttribute("stroke-linejoin", "round");
9440    svg.appendChild(path);
9441    this.element.appendChild(svg);
9442  }
9443  _applyAria() {
9444    if (!this.element.hasAttribute("role")) {
9445      this.element.setAttribute("role", "button");
9446    }
9447    if (!this.element.hasAttribute("tabindex")) {
9448      this.element.setAttribute("tabindex", "0");
9449    }
9450  }
9451  _applyInitialState() {
9452    const collapsed = this.initialValue === "collapsed";
9453    this.element.classList.toggle(COLLAPSED_CLASS, collapsed);
9454    this.element.setAttribute("aria-expanded", collapsed ? "false" : "true");
9455  }
9456};
9457
9458// app/javascript/controllers/admin_transcriptions_controller.js
9459var admin_transcriptions_controller_default = class extends Controller {
9460  static targets = [
9461    "toggleConfirmCheckbox",
9462    "toggleConfirmedField",
9463    "bulkButton",
9464    "bulkForm",
9465    "batchSize",
9466    "rowTriggerButton",
9467    "rowTriggerForm"
9468  ];
9469  static values = {
9470    canaryFresh: Boolean,
9471    softConfirmThreshold: { type: Number, default: 50 }
9472  };
9473  connect() {
9474    this.syncControls();
9475  }
9476  toggleChanged() {
9477    this.syncControls();
9478  }
9479  syncControls() {
9480    const toggleConfirmed = this.hasToggleConfirmCheckboxTarget ? this.toggleConfirmCheckboxTarget.checked : false;
9481    if (this.hasToggleConfirmedFieldTarget) {
9482      this.toggleConfirmedFieldTarget.value = toggleConfirmed ? "1" : "0";
9483    }
9484    if (this.hasBulkButtonTarget) {
9485      const bulkEnabled = this.canaryFreshValue && toggleConfirmed;
9486      this.bulkButtonTarget.disabled = !bulkEnabled;
9487    }
9488    if (this.hasRowTriggerFormTarget) {
9489      this.rowTriggerFormTargets.forEach((form) => {
9490        const hidden = form.querySelector('input[name="toggle_confirmed"]');
9491        if (hidden) hidden.value = toggleConfirmed ? "1" : "0";
9492      });
9493    }
9494    if (this.hasRowTriggerButtonTarget) {
9495      this.rowTriggerButtonTargets.forEach((btn) => {
9496        btn.disabled = !toggleConfirmed;
9497      });
9498    }
9499  }
9500  // Submits the enclosing GET form when an input changes (e.g. the
9501  // "Show failed videos" checkbox), reloading the candidate list with the
9502  // updated query-string params.
9503  submitOnChange(event) {
9504    const form = event.target.closest("form");
9505    if (form) form.requestSubmit();
9506  }
9507  confirmBulk(event) {
9508    if (this.hasToggleConfirmedFieldTarget && this.hasToggleConfirmCheckboxTarget) {
9509      this.toggleConfirmedFieldTarget.value = this.toggleConfirmCheckboxTarget.checked ? "1" : "0";
9510    }
9511    if (!this.hasBatchSizeTarget) return;
9512    const batchSize = parseInt(this.batchSizeTarget.value, 10) || 0;
9513    if (batchSize < this.softConfirmThresholdValue) return;
9514    const estimatedSeconds = Math.round(batchSize * 1.25);
9515    const minutes = Math.max(1, Math.round(estimatedSeconds / 60));
9516    const message = `You are about to trigger ${batchSize} videos at roughly 1.25s/video \u2248 ${minutes} min wall-clock. Continue?`;
9517    if (!window.confirm(message)) {
9518      event.preventDefault();
9519    }
9520  }
9521};
9522
9523// app/javascript/controllers/recaptcha_v3_controller.js
9524var recaptcha_v3_controller_default = class extends Controller {
9525  static values = { siteKey: String, action: { type: String, default: "login" } };
9526  connect() {
9527    this.submitting = false;
9528  }
9529  execute(event) {
9530    if (this.submitting) return;
9531    const field = this.tokenField();
9532    if (!field) return;
9533    if (typeof grecaptcha === "undefined") return;
9534    event.preventDefault();
9535    let done = false;
9536    const submitForm = () => {
9537      if (done) return;
9538      done = true;
9539      this.submitting = true;
9540      this.element.requestSubmit();
9541    };
9542    const timeout = setTimeout(submitForm, 3e3);
9543    grecaptcha.ready(() => {
9544      grecaptcha.execute(this.siteKeyValue, { action: this.actionValue }).then((token) => {
9545        field.value = token;
9546      }).catch(() => {
9547      }).finally(() => {
9548        clearTimeout(timeout);
9549        submitForm();
9550      });
9551    });
9552  }
9553  tokenField() {
9554    return this.element.querySelector(
9555      `input[name="g-recaptcha-response-data[${this.actionValue}]"]`
9556    );
9557  }
9558};
9559
9560// app/javascript/controllers/video_player_seek_controller.js
9561var POLL_INTERVAL_MS = 200;
9562var MAX_POLL_MS = 15e3;
9563var video_player_seek_controller_default = class extends Controller {
9564  static values = { t: Number };
9565  connect() {
9566    this._seconds = this.hasTValue ? this.tValue : 0;
9567    if (this._seconds <= 0) return;
9568    this._seeked = false;
9569    this._pollElapsed = 0;
9570    this._pollTimer = setInterval(() => this._pollForPlayer(), POLL_INTERVAL_MS);
9571  }
9572  disconnect() {
9573    this._clearPoll();
9574    this._detachListeners();
9575  }
9576  _pollForPlayer() {
9577    this._pollElapsed += POLL_INTERVAL_MS;
9578    if (this._pollElapsed >= MAX_POLL_MS) {
9579      this._clearPoll();
9580      return;
9581    }
9582    const player = window.zypePlayer;
9583    if (!player || typeof player.on !== "function") return;
9584    this._clearPoll();
9585    this._attachListeners(player);
9586  }
9587  _attachListeners(player) {
9588    this._player = player;
9589    this._onPlayback = () => this._maybeSeek(player);
9590    try {
9591      player.on("playing", this._onPlayback);
9592      player.on("timechanged", this._onPlayback);
9593    } catch (e) {
9594    }
9595  }
9596  _maybeSeek(player) {
9597    if (this._seeked) return;
9598    if (this._adActive(player)) return;
9599    try {
9600      if (typeof player.seek === "function") {
9601        player.seek(this._seconds);
9602        this._seeked = true;
9603        this._detachListeners();
9604      }
9605    } catch (e) {
9606    }
9607  }
9608  _adActive(player) {
9609    try {
9610      return !!(player.ads && typeof player.ads.isLinearAdActive === "function" && player.ads.isLinearAdActive());
9611    } catch (e) {
9612      return false;
9613    }
9614  }
9615  _detachListeners() {
9616    if (!this._player || !this._onPlayback) return;
9617    try {
9618      this._player.off("playing", this._onPlayback);
9619      this._player.off("timechanged", this._onPlayback);
9620    } catch (e) {
9621    }
9622    this._player = null;
9623    this._onPlayback = null;
9624  }
9625  _clearPoll() {
9626    if (this._pollTimer) {
9627      clearInterval(this._pollTimer);
9628      this._pollTimer = null;
9629    }
9630  }
9631};
9632
9633// app/javascript/controllers/index.js
9634var application = Application.start();
9635application.debug = false;
9636window.Stimulus = application;
9637application.register("infinite-scroll", infinite_scroll_controller_default);
9638application.register("dropdown", dropdown_controller_default);
9639application.register("modal", modal_controller_default);
9640application.register("playlist-filter", playlist_filter_controller_default);
9641application.register("deep-dive", deep_dive_controller_default);
9642application.register("deep-dive-stream", deep_dive_stream_controller_default);
9643application.register("collapsible-bubble", collapsible_bubble_controller_default);
9644application.register("admin-transcriptions", admin_transcriptions_controller_default);
9645application.register("recaptcha-v3", recaptcha_v3_controller_default);
9646application.register("video-player-seek", video_player_seek_controller_default);
9647
9648// app/javascript/application-eb.js
9649Turbo.config.forms.mode = "optin";
9650/*! Bundled license information:
9651
9652@hotwired/turbo/dist/turbo.es2017-esm.js:
9653  (*!
9654  Turbo 8.0.13
9655  Copyright © 2025 37signals LLC
9656   *)
9657*/
9658;

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.