(() => {
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// node_modules/@rails/actioncable/src/adapters.js
var adapters_default;
var init_adapters = __esm({
"node_modules/@rails/actioncable/src/adapters.js"() {
adapters_default = {
logger: typeof console !== "undefined" ? console : void 0,
WebSocket: typeof WebSocket !== "undefined" ? WebSocket : void 0
};
}
});
// node_modules/@rails/actioncable/src/logger.js
var logger_default;
var init_logger = __esm({
"node_modules/@rails/actioncable/src/logger.js"() {
init_adapters();
logger_default = {
log(...messages) {
if (this.enabled) {
messages.push(Date.now());
adapters_default.logger.log("[ActionCable]", ...messages);
}
}
};
}
});
// node_modules/@rails/actioncable/src/connection_monitor.js
var now, secondsSince, ConnectionMonitor, connection_monitor_default;
var init_connection_monitor = __esm({
"node_modules/@rails/actioncable/src/connection_monitor.js"() {
init_logger();
now = () => (/* @__PURE__ */ new Date()).getTime();
secondsSince = (time) => (now() - time) / 1e3;
ConnectionMonitor = class {
constructor(connection) {
this.visibilityDidChange = this.visibilityDidChange.bind(this);
this.connection = connection;
this.reconnectAttempts = 0;
}
start() {
if (!this.isRunning()) {
this.startedAt = now();
delete this.stoppedAt;
this.startPolling();
addEventListener("visibilitychange", this.visibilityDidChange);
logger_default.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`);
}
}
stop() {
if (this.isRunning()) {
this.stoppedAt = now();
this.stopPolling();
removeEventListener("visibilitychange", this.visibilityDidChange);
logger_default.log("ConnectionMonitor stopped");
}
}
isRunning() {
return this.startedAt && !this.stoppedAt;
}
recordMessage() {
this.pingedAt = now();
}
recordConnect() {
this.reconnectAttempts = 0;
delete this.disconnectedAt;
logger_default.log("ConnectionMonitor recorded connect");
}
recordDisconnect() {
this.disconnectedAt = now();
logger_default.log("ConnectionMonitor recorded disconnect");
}
// Private
startPolling() {
this.stopPolling();
this.poll();
}
stopPolling() {
clearTimeout(this.pollTimeout);
}
poll() {
this.pollTimeout = setTimeout(
() => {
this.reconnectIfStale();
this.poll();
},
this.getPollInterval()
);
}
getPollInterval() {
const { staleThreshold, reconnectionBackoffRate } = this.constructor;
const backoff = Math.pow(1 + reconnectionBackoffRate, Math.min(this.reconnectAttempts, 10));
const jitterMax = this.reconnectAttempts === 0 ? 1 : reconnectionBackoffRate;
const jitter = jitterMax * Math.random();
return staleThreshold * 1e3 * backoff * (1 + jitter);
}
reconnectIfStale() {
if (this.connectionIsStale()) {
logger_default.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${secondsSince(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`);
this.reconnectAttempts++;
if (this.disconnectedRecently()) {
logger_default.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${secondsSince(this.disconnectedAt)} s`);
} else {
logger_default.log("ConnectionMonitor reopening");
this.connection.reopen();
}
}
}
get refreshedAt() {
return this.pingedAt ? this.pingedAt : this.startedAt;
}
connectionIsStale() {
return secondsSince(this.refreshedAt) > this.constructor.staleThreshold;
}
disconnectedRecently() {
return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
}
visibilityDidChange() {
if (document.visibilityState === "visible") {
setTimeout(
() => {
if (this.connectionIsStale() || !this.connection.isOpen()) {
logger_default.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`);
this.connection.reopen();
}
},
200
);
}
}
};
ConnectionMonitor.staleThreshold = 6;
ConnectionMonitor.reconnectionBackoffRate = 0.15;
connection_monitor_default = ConnectionMonitor;
}
});
// node_modules/@rails/actioncable/src/internal.js
var internal_default;
var init_internal = __esm({
"node_modules/@rails/actioncable/src/internal.js"() {
internal_default = {
"message_types": {
"welcome": "welcome",
"disconnect": "disconnect",
"ping": "ping",
"confirmation": "confirm_subscription",
"rejection": "reject_subscription"
},
"disconnect_reasons": {
"unauthorized": "unauthorized",
"invalid_request": "invalid_request",
"server_restart": "server_restart",
"remote": "remote"
},
"default_mount_path": "/cable",
"protocols": [
"actioncable-v1-json",
"actioncable-unsupported"
]
};
}
});
// node_modules/@rails/actioncable/src/connection.js
var message_types, protocols, supportedProtocols, indexOf, Connection, connection_default;
var init_connection = __esm({
"node_modules/@rails/actioncable/src/connection.js"() {
init_adapters();
init_connection_monitor();
init_internal();
init_logger();
({ message_types, protocols } = internal_default);
supportedProtocols = protocols.slice(0, protocols.length - 1);
indexOf = [].indexOf;
Connection = class {
constructor(consumer2) {
this.open = this.open.bind(this);
this.consumer = consumer2;
this.subscriptions = this.consumer.subscriptions;
this.monitor = new connection_monitor_default(this);
this.disconnected = true;
}
send(data) {
if (this.isOpen()) {
this.webSocket.send(JSON.stringify(data));
return true;
} else {
return false;
}
}
open() {
if (this.isActive()) {
logger_default.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
return false;
} else {
const socketProtocols = [...protocols, ...this.consumer.subprotocols || []];
logger_default.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
if (this.webSocket) {
this.uninstallEventHandlers();
}
this.webSocket = new adapters_default.WebSocket(this.consumer.url, socketProtocols);
this.installEventHandlers();
this.monitor.start();
return true;
}
}
close({ allowReconnect } = { allowReconnect: true }) {
if (!allowReconnect) {
this.monitor.stop();
}
if (this.isOpen()) {
return this.webSocket.close();
}
}
reopen() {
logger_default.log(`Reopening WebSocket, current state is ${this.getState()}`);
if (this.isActive()) {
try {
return this.close();
} catch (error2) {
logger_default.log("Failed to reopen WebSocket", error2);
} finally {
logger_default.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`);
setTimeout(this.open, this.constructor.reopenDelay);
}
} else {
return this.open();
}
}
getProtocol() {
if (this.webSocket) {
return this.webSocket.protocol;
}
}
isOpen() {
return this.isState("open");
}
isActive() {
return this.isState("open", "connecting");
}
triedToReconnect() {
return this.monitor.reconnectAttempts > 0;
}
// Private
isProtocolSupported() {
return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
}
isState(...states) {
return indexOf.call(states, this.getState()) >= 0;
}
getState() {
if (this.webSocket) {
for (let state in adapters_default.WebSocket) {
if (adapters_default.WebSocket[state] === this.webSocket.readyState) {
return state.toLowerCase();
}
}
}
return null;
}
installEventHandlers() {
for (let eventName in this.events) {
const handler = this.events[eventName].bind(this);
this.webSocket[`on${eventName}`] = handler;
}
}
uninstallEventHandlers() {
for (let eventName in this.events) {
this.webSocket[`on${eventName}`] = function() {
};
}
}
};
Connection.reopenDelay = 500;
Connection.prototype.events = {
message(event) {
if (!this.isProtocolSupported()) {
return;
}
const { identifier, message, reason, reconnect, type } = JSON.parse(event.data);
this.monitor.recordMessage();
switch (type) {
case message_types.welcome:
if (this.triedToReconnect()) {
this.reconnectAttempted = true;
}
this.monitor.recordConnect();
return this.subscriptions.reload();
case message_types.disconnect:
logger_default.log(`Disconnecting. Reason: ${reason}`);
return this.close({ allowReconnect: reconnect });
case message_types.ping:
return null;
case message_types.confirmation:
this.subscriptions.confirmSubscription(identifier);
if (this.reconnectAttempted) {
this.reconnectAttempted = false;
return this.subscriptions.notify(identifier, "connected", { reconnected: true });
} else {
return this.subscriptions.notify(identifier, "connected", { reconnected: false });
}
case message_types.rejection:
return this.subscriptions.reject(identifier);
default:
return this.subscriptions.notify(identifier, "received", message);
}
},
open() {
logger_default.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`);
this.disconnected = false;
if (!this.isProtocolSupported()) {
logger_default.log("Protocol is unsupported. Stopping monitor and disconnecting.");
return this.close({ allowReconnect: false });
}
},
close(event) {
logger_default.log("WebSocket onclose event");
if (this.disconnected) {
return;
}
this.disconnected = true;
this.monitor.recordDisconnect();
return this.subscriptions.notifyAll("disconnected", { willAttemptReconnect: this.monitor.isRunning() });
},
error() {
logger_default.log("WebSocket onerror event");
}
};
connection_default = Connection;
}
});
// node_modules/@rails/actioncable/src/subscription.js
var extend, Subscription;
var init_subscription = __esm({
"node_modules/@rails/actioncable/src/subscription.js"() {
extend = function(object, properties) {
if (properties != null) {
for (let key in properties) {
const value = properties[key];
object[key] = value;
}
}
return object;
};
Subscription = class {
constructor(consumer2, params = {}, mixin) {
this.consumer = consumer2;
this.identifier = JSON.stringify(params);
extend(this, mixin);
}
// Perform a channel action with the optional data passed as an attribute
perform(action, data = {}) {
data.action = action;
return this.send(data);
}
send(data) {
return this.consumer.send({ command: "message", identifier: this.identifier, data: JSON.stringify(data) });
}
unsubscribe() {
return this.consumer.subscriptions.remove(this);
}
};
}
});
// node_modules/@rails/actioncable/src/subscription_guarantor.js
var SubscriptionGuarantor, subscription_guarantor_default;
var init_subscription_guarantor = __esm({
"node_modules/@rails/actioncable/src/subscription_guarantor.js"() {
init_logger();
SubscriptionGuarantor = class {
constructor(subscriptions) {
this.subscriptions = subscriptions;
this.pendingSubscriptions = [];
}
guarantee(subscription) {
if (this.pendingSubscriptions.indexOf(subscription) == -1) {
logger_default.log(`SubscriptionGuarantor guaranteeing ${subscription.identifier}`);
this.pendingSubscriptions.push(subscription);
} else {
logger_default.log(`SubscriptionGuarantor already guaranteeing ${subscription.identifier}`);
}
this.startGuaranteeing();
}
forget(subscription) {
logger_default.log(`SubscriptionGuarantor forgetting ${subscription.identifier}`);
this.pendingSubscriptions = this.pendingSubscriptions.filter((s) => s !== subscription);
}
startGuaranteeing() {
this.stopGuaranteeing();
this.retrySubscribing();
}
stopGuaranteeing() {
clearTimeout(this.retryTimeout);
}
retrySubscribing() {
this.retryTimeout = setTimeout(
() => {
if (this.subscriptions && typeof this.subscriptions.subscribe === "function") {
this.pendingSubscriptions.map((subscription) => {
logger_default.log(`SubscriptionGuarantor resubscribing ${subscription.identifier}`);
this.subscriptions.subscribe(subscription);
});
}
},
500
);
}
};
subscription_guarantor_default = SubscriptionGuarantor;
}
});
// node_modules/@rails/actioncable/src/subscriptions.js
var Subscriptions;
var init_subscriptions = __esm({
"node_modules/@rails/actioncable/src/subscriptions.js"() {
init_subscription();
init_subscription_guarantor();
init_logger();
Subscriptions = class {
constructor(consumer2) {
this.consumer = consumer2;
this.guarantor = new subscription_guarantor_default(this);
this.subscriptions = [];
}
create(channelName, mixin) {
const channel = channelName;
const params = typeof channel === "object" ? channel : { channel };
const subscription = new Subscription(this.consumer, params, mixin);
return this.add(subscription);
}
// Private
add(subscription) {
this.subscriptions.push(subscription);
this.consumer.ensureActiveConnection();
this.notify(subscription, "initialized");
this.subscribe(subscription);
return subscription;
}
remove(subscription) {
this.forget(subscription);
if (!this.findAll(subscription.identifier).length) {
this.sendCommand(subscription, "unsubscribe");
}
return subscription;
}
reject(identifier) {
return this.findAll(identifier).map((subscription) => {
this.forget(subscription);
this.notify(subscription, "rejected");
return subscription;
});
}
forget(subscription) {
this.guarantor.forget(subscription);
this.subscriptions = this.subscriptions.filter((s) => s !== subscription);
return subscription;
}
findAll(identifier) {
return this.subscriptions.filter((s) => s.identifier === identifier);
}
reload() {
return this.subscriptions.map((subscription) => this.subscribe(subscription));
}
notifyAll(callbackName, ...args) {
return this.subscriptions.map((subscription) => this.notify(subscription, callbackName, ...args));
}
notify(subscription, callbackName, ...args) {
let subscriptions;
if (typeof subscription === "string") {
subscriptions = this.findAll(subscription);
} else {
subscriptions = [subscription];
}
return subscriptions.map((subscription2) => typeof subscription2[callbackName] === "function" ? subscription2[callbackName](...args) : void 0);
}
subscribe(subscription) {
if (this.sendCommand(subscription, "subscribe")) {
this.guarantor.guarantee(subscription);
}
}
confirmSubscription(identifier) {
logger_default.log(`Subscription confirmed ${identifier}`);
this.findAll(identifier).map((subscription) => this.guarantor.forget(subscription));
}
sendCommand(subscription, command) {
const { identifier } = subscription;
return this.consumer.send({ command, identifier });
}
};
}
});
// node_modules/@rails/actioncable/src/consumer.js
function createWebSocketURL(url) {
if (typeof url === "function") {
url = url();
}
if (url && !/^wss?:/i.test(url)) {
const a = document.createElement("a");
a.href = url;
a.href = a.href;
a.protocol = a.protocol.replace("http", "ws");
return a.href;
} else {
return url;
}
}
var Consumer;
var init_consumer = __esm({
"node_modules/@rails/actioncable/src/consumer.js"() {
init_connection();
init_subscriptions();
Consumer = class {
constructor(url) {
this._url = url;
this.subscriptions = new Subscriptions(this);
this.connection = new connection_default(this);
this.subprotocols = [];
}
get url() {
return createWebSocketURL(this._url);
}
send(data) {
return this.connection.send(data);
}
connect() {
return this.connection.open();
}
disconnect() {
return this.connection.close({ allowReconnect: false });
}
ensureActiveConnection() {
if (!this.connection.isActive()) {
return this.connection.open();
}
}
addSubProtocol(subprotocol) {
this.subprotocols = [...this.subprotocols, subprotocol];
}
};
}
});
// node_modules/@rails/actioncable/src/index.js
var src_exports = {};
__export(src_exports, {
Connection: () => connection_default,
ConnectionMonitor: () => connection_monitor_default,
Consumer: () => Consumer,
INTERNAL: () => internal_default,
Subscription: () => Subscription,
SubscriptionGuarantor: () => subscription_guarantor_default,
Subscriptions: () => Subscriptions,
adapters: () => adapters_default,
createConsumer: () => createConsumer,
createWebSocketURL: () => createWebSocketURL,
getConfig: () => getConfig,
logger: () => logger_default
});
function createConsumer(url = getConfig("url") || internal_default.default_mount_path) {
return new Consumer(url);
}
function getConfig(name) {
const element = document.head.querySelector(`meta[name='action-cable-${name}']`);
if (element) {
return element.getAttribute("content");
}
}
var init_src = __esm({
"node_modules/@rails/actioncable/src/index.js"() {
init_connection();
init_connection_monitor();
init_consumer();
init_internal();
init_subscription();
init_subscriptions();
init_subscription_guarantor();
init_adapters();
init_logger();
}
});
// node_modules/@hotwired/turbo/dist/turbo.es2017-esm.js
var turbo_es2017_esm_exports = {};
__export(turbo_es2017_esm_exports, {
FetchEnctype: () => FetchEnctype,
FetchMethod: () => FetchMethod,
FetchRequest: () => FetchRequest,
FetchResponse: () => FetchResponse,
FrameElement: () => FrameElement,
FrameLoadingStyle: () => FrameLoadingStyle,
FrameRenderer: () => FrameRenderer,
PageRenderer: () => PageRenderer,
PageSnapshot: () => PageSnapshot,
StreamActions: () => StreamActions,
StreamElement: () => StreamElement,
StreamSourceElement: () => StreamSourceElement,
cache: () => cache,
clearCache: () => clearCache,
connectStreamSource: () => connectStreamSource,
disconnectStreamSource: () => disconnectStreamSource,
fetch: () => fetchWithTurboHeaders,
fetchEnctypeFromString: () => fetchEnctypeFromString,
fetchMethodFromString: () => fetchMethodFromString,
isSafe: () => isSafe,
navigator: () => navigator$1,
registerAdapter: () => registerAdapter,
renderStreamMessage: () => renderStreamMessage,
session: () => session,
setConfirmMethod: () => setConfirmMethod,
setFormMode: () => setFormMode,
setProgressBarDelay: () => setProgressBarDelay,
start: () => start,
visit: () => visit
});
(function(prototype) {
if (typeof prototype.requestSubmit == "function") return;
prototype.requestSubmit = function(submitter) {
if (submitter) {
validateSubmitter(submitter, this);
submitter.click();
} else {
submitter = document.createElement("input");
submitter.type = "submit";
submitter.hidden = true;
this.appendChild(submitter);
submitter.click();
this.removeChild(submitter);
}
};
function validateSubmitter(submitter, form) {
submitter instanceof HTMLElement || raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
submitter.type == "submit" || raise(TypeError, "The specified element is not a submit button");
submitter.form == form || raise(DOMException, "The specified element is not owned by this form element", "NotFoundError");
}
function raise(errorConstructor, message, name) {
throw new errorConstructor("Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".", name);
}
})(HTMLFormElement.prototype);
var submittersByForm = /* @__PURE__ */ new WeakMap();
function findSubmitterFromClickTarget(target) {
const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
const candidate = element ? element.closest("input, button") : null;
return candidate?.type == "submit" ? candidate : null;
}
function clickCaptured(event) {
const submitter = findSubmitterFromClickTarget(event.target);
if (submitter && submitter.form) {
submittersByForm.set(submitter.form, submitter);
}
}
(function() {
if ("submitter" in Event.prototype) return;
let prototype = window.Event.prototype;
if ("SubmitEvent" in window) {
const prototypeOfSubmitEvent = window.SubmitEvent.prototype;
if (/Apple Computer/.test(navigator.vendor) && !("submitter" in prototypeOfSubmitEvent)) {
prototype = prototypeOfSubmitEvent;
} else {
return;
}
}
addEventListener("click", clickCaptured, true);
Object.defineProperty(prototype, "submitter", {
get() {
if (this.type == "submit" && this.target instanceof HTMLFormElement) {
return submittersByForm.get(this.target);
}
}
});
})();
var FrameLoadingStyle = {
eager: "eager",
lazy: "lazy"
};
var FrameElement = class _FrameElement extends HTMLElement {
static delegateConstructor = void 0;
loaded = Promise.resolve();
static get observedAttributes() {
return ["disabled", "loading", "src"];
}
constructor() {
super();
this.delegate = new _FrameElement.delegateConstructor(this);
}
connectedCallback() {
this.delegate.connect();
}
disconnectedCallback() {
this.delegate.disconnect();
}
reload() {
return this.delegate.sourceURLReloaded();
}
attributeChangedCallback(name) {
if (name == "loading") {
this.delegate.loadingStyleChanged();
} else if (name == "src") {
this.delegate.sourceURLChanged();
} else if (name == "disabled") {
this.delegate.disabledChanged();
}
}
/**
* Gets the URL to lazily load source HTML from
*/
get src() {
return this.getAttribute("src");
}
/**
* Sets the URL to lazily load source HTML from
*/
set src(value) {
if (value) {
this.setAttribute("src", value);
} else {
this.removeAttribute("src");
}
}
/**
* Gets the refresh mode for the frame.
*/
get refresh() {
return this.getAttribute("refresh");
}
/**
* Sets the refresh mode for the frame.
*/
set refresh(value) {
if (value) {
this.setAttribute("refresh", value);
} else {
this.removeAttribute("refresh");
}
}
/**
* Determines if the element is loading
*/
get loading() {
return frameLoadingStyleFromString(this.getAttribute("loading") || "");
}
/**
* Sets the value of if the element is loading
*/
set loading(value) {
if (value) {
this.setAttribute("loading", value);
} else {
this.removeAttribute("loading");
}
}
/**
* Gets the disabled state of the frame.
*
* If disabled, no requests will be intercepted by the frame.
*/
get disabled() {
return this.hasAttribute("disabled");
}
/**
* Sets the disabled state of the frame.
*
* If disabled, no requests will be intercepted by the frame.
*/
set disabled(value) {
if (value) {
this.setAttribute("disabled", "");
} else {
this.removeAttribute("disabled");
}
}
/**
* Gets the autoscroll state of the frame.
*
* If true, the frame will be scrolled into view automatically on update.
*/
get autoscroll() {
return this.hasAttribute("autoscroll");
}
/**
* Sets the autoscroll state of the frame.
*
* If true, the frame will be scrolled into view automatically on update.
*/
set autoscroll(value) {
if (value) {
this.setAttribute("autoscroll", "");
} else {
this.removeAttribute("autoscroll");
}
}
/**
* Determines if the element has finished loading
*/
get complete() {
return !this.delegate.isLoading;
}
/**
* Gets the active state of the frame.
*
* If inactive, source changes will not be observed.
*/
get isActive() {
return this.ownerDocument === document && !this.isPreview;
}
/**
* Sets the active state of the frame.
*
* If inactive, source changes will not be observed.
*/
get isPreview() {
return this.ownerDocument?.documentElement?.hasAttribute("data-turbo-preview");
}
};
function frameLoadingStyleFromString(style) {
switch (style.toLowerCase()) {
case "lazy":
return FrameLoadingStyle.lazy;
default:
return FrameLoadingStyle.eager;
}
}
function expandURL(locatable) {
return new URL(locatable.toString(), document.baseURI);
}
function getAnchor(url) {
let anchorMatch;
if (url.hash) {
return url.hash.slice(1);
} else if (anchorMatch = url.href.match(/#(.*)$/)) {
return anchorMatch[1];
}
}
function getAction$1(form, submitter) {
const action = submitter?.getAttribute("formaction") || form.getAttribute("action") || form.action;
return expandURL(action);
}
function getExtension(url) {
return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || "";
}
function isHTML(url) {
return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml|php))$/);
}
function isPrefixedBy(baseURL, url) {
const prefix = getPrefix(url);
return baseURL.href === expandURL(prefix).href || baseURL.href.startsWith(prefix);
}
function locationIsVisitable(location2, rootLocation) {
return isPrefixedBy(location2, rootLocation) && isHTML(location2);
}
function getRequestURL(url) {
const anchor = getAnchor(url);
return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href;
}
function toCacheKey(url) {
return getRequestURL(url);
}
function urlsAreEqual(left, right) {
return expandURL(left).href == expandURL(right).href;
}
function getPathComponents(url) {
return url.pathname.split("/").slice(1);
}
function getLastPathComponent(url) {
return getPathComponents(url).slice(-1)[0];
}
function getPrefix(url) {
return addTrailingSlash(url.origin + url.pathname);
}
function addTrailingSlash(value) {
return value.endsWith("/") ? value : value + "/";
}
var FetchResponse = class {
constructor(response) {
this.response = response;
}
get succeeded() {
return this.response.ok;
}
get failed() {
return !this.succeeded;
}
get clientError() {
return this.statusCode >= 400 && this.statusCode <= 499;
}
get serverError() {
return this.statusCode >= 500 && this.statusCode <= 599;
}
get redirected() {
return this.response.redirected;
}
get location() {
return expandURL(this.response.url);
}
get isHTML() {
return this.contentType && this.contentType.match(/^(?:text\/([^\s;,]+\b)?html|application\/xhtml\+xml)\b/);
}
get statusCode() {
return this.response.status;
}
get contentType() {
return this.header("Content-Type");
}
get responseText() {
return this.response.clone().text();
}
get responseHTML() {
if (this.isHTML) {
return this.response.clone().text();
} else {
return Promise.resolve(void 0);
}
}
header(name) {
return this.response.headers.get(name);
}
};
function activateScriptElement(element) {
if (element.getAttribute("data-turbo-eval") == "false") {
return element;
} else {
const createdScriptElement = document.createElement("script");
const cspNonce = getMetaContent("csp-nonce");
if (cspNonce) {
createdScriptElement.nonce = cspNonce;
}
createdScriptElement.textContent = element.textContent;
createdScriptElement.async = false;
copyElementAttributes(createdScriptElement, element);
return createdScriptElement;
}
}
function copyElementAttributes(destinationElement, sourceElement) {
for (const { name, value } of sourceElement.attributes) {
destinationElement.setAttribute(name, value);
}
}
function createDocumentFragment(html) {
const template = document.createElement("template");
template.innerHTML = html;
return template.content;
}
function dispatch(eventName, { target, cancelable, detail } = {}) {
const event = new CustomEvent(eventName, {
cancelable,
bubbles: true,
composed: true,
detail
});
if (target && target.isConnected) {
target.dispatchEvent(event);
} else {
document.documentElement.dispatchEvent(event);
}
return event;
}
function nextRepaint() {
if (document.visibilityState === "hidden") {
return nextEventLoopTick();
} else {
return nextAnimationFrame();
}
}
function nextAnimationFrame() {
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
}
function nextEventLoopTick() {
return new Promise((resolve) => setTimeout(() => resolve(), 0));
}
function nextMicrotask() {
return Promise.resolve();
}
function parseHTMLDocument(html = "") {
return new DOMParser().parseFromString(html, "text/html");
}
function unindent(strings, ...values) {
const lines = interpolate(strings, values).replace(/^\n/, "").split("\n");
const match = lines[0].match(/^\s+/);
const indent = match ? match[0].length : 0;
return lines.map((line) => line.slice(indent)).join("\n");
}
function interpolate(strings, values) {
return strings.reduce((result, string, i) => {
const value = values[i] == void 0 ? "" : values[i];
return result + string + value;
}, "");
}
function uuid() {
return Array.from({ length: 36 }).map((_, i) => {
if (i == 8 || i == 13 || i == 18 || i == 23) {
return "-";
} else if (i == 14) {
return "4";
} else if (i == 19) {
return (Math.floor(Math.random() * 4) + 8).toString(16);
} else {
return Math.floor(Math.random() * 15).toString(16);
}
}).join("");
}
function getAttribute(attributeName, ...elements) {
for (const value of elements.map((element) => element?.getAttribute(attributeName))) {
if (typeof value == "string") return value;
}
return null;
}
function hasAttribute(attributeName, ...elements) {
return elements.some((element) => element && element.hasAttribute(attributeName));
}
function markAsBusy(...elements) {
for (const element of elements) {
if (element.localName == "turbo-frame") {
element.setAttribute("busy", "");
}
element.setAttribute("aria-busy", "true");
}
}
function clearBusyState(...elements) {
for (const element of elements) {
if (element.localName == "turbo-frame") {
element.removeAttribute("busy");
}
element.removeAttribute("aria-busy");
}
}
function waitForLoad(element, timeoutInMilliseconds = 2e3) {
return new Promise((resolve) => {
const onComplete = () => {
element.removeEventListener("error", onComplete);
element.removeEventListener("load", onComplete);
resolve();
};
element.addEventListener("load", onComplete, { once: true });
element.addEventListener("error", onComplete, { once: true });
setTimeout(resolve, timeoutInMilliseconds);
});
}
function getHistoryMethodForAction(action) {
switch (action) {
case "replace":
return history.replaceState;
case "advance":
case "restore":
return history.pushState;
}
}
function isAction(action) {
return action == "advance" || action == "replace" || action == "restore";
}
function getVisitAction(...elements) {
const action = getAttribute("data-turbo-action", ...elements);
return isAction(action) ? action : null;
}
function getMetaElement(name) {
return document.querySelector(`meta[name="${name}"]`);
}
function getMetaContent(name) {
const element = getMetaElement(name);
return element && element.content;
}
function setMetaContent(name, content) {
let element = getMetaElement(name);
if (!element) {
element = document.createElement("meta");
element.setAttribute("name", name);
document.head.appendChild(element);
}
element.setAttribute("content", content);
return element;
}
function findClosestRecursively(element, selector) {
if (element instanceof Element) {
return element.closest(selector) || findClosestRecursively(element.assignedSlot || element.getRootNode()?.host, selector);
}
}
function elementIsFocusable(element) {
const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
return !!element && element.closest(inertDisabledOrHidden) == null && typeof element.focus == "function";
}
function queryAutofocusableElement(elementOrDocumentFragment) {
return Array.from(elementOrDocumentFragment.querySelectorAll("[autofocus]")).find(elementIsFocusable);
}
async function around(callback, reader) {
const before = reader();
callback();
await nextAnimationFrame();
const after = reader();
return [before, after];
}
function doesNotTargetIFrame(name) {
if (name === "_blank") {
return false;
} else if (name) {
for (const element of document.getElementsByName(name)) {
if (element instanceof HTMLIFrameElement) return false;
}
return true;
} else {
return true;
}
}
function findLinkFromClickTarget(target) {
return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
}
function getLocationForLink(link) {
return expandURL(link.getAttribute("href") || "");
}
function debounce(fn, delay) {
let timeoutId = null;
return (...args) => {
const callback = () => fn.apply(this, args);
clearTimeout(timeoutId);
timeoutId = setTimeout(callback, delay);
};
}
var LimitedSet = class extends Set {
constructor(maxSize) {
super();
this.maxSize = maxSize;
}
add(value) {
if (this.size >= this.maxSize) {
const iterator = this.values();
const oldestValue = iterator.next().value;
this.delete(oldestValue);
}
super.add(value);
}
};
var recentRequests = new LimitedSet(20);
var nativeFetch = window.fetch;
function fetchWithTurboHeaders(url, options = {}) {
const modifiedHeaders = new Headers(options.headers || {});
const requestUID = uuid();
recentRequests.add(requestUID);
modifiedHeaders.append("X-Turbo-Request-Id", requestUID);
return nativeFetch(url, {
...options,
headers: modifiedHeaders
});
}
function fetchMethodFromString(method) {
switch (method.toLowerCase()) {
case "get":
return FetchMethod.get;
case "post":
return FetchMethod.post;
case "put":
return FetchMethod.put;
case "patch":
return FetchMethod.patch;
case "delete":
return FetchMethod.delete;
}
}
var FetchMethod = {
get: "get",
post: "post",
put: "put",
patch: "patch",
delete: "delete"
};
function fetchEnctypeFromString(encoding) {
switch (encoding.toLowerCase()) {
case FetchEnctype.multipart:
return FetchEnctype.multipart;
case FetchEnctype.plain:
return FetchEnctype.plain;
default:
return FetchEnctype.urlEncoded;
}
}
var FetchEnctype = {
urlEncoded: "application/x-www-form-urlencoded",
multipart: "multipart/form-data",
plain: "text/plain"
};
var FetchRequest = class {
abortController = new AbortController();
#resolveRequestPromise = (_value) => {
};
constructor(delegate, method, location2, requestBody = new URLSearchParams(), target = null, enctype = FetchEnctype.urlEncoded) {
const [url, body] = buildResourceAndBody(expandURL(location2), method, requestBody, enctype);
this.delegate = delegate;
this.url = url;
this.target = target;
this.fetchOptions = {
credentials: "same-origin",
redirect: "follow",
method: method.toUpperCase(),
headers: { ...this.defaultHeaders },
body,
signal: this.abortSignal,
referrer: this.delegate.referrer?.href
};
this.enctype = enctype;
}
get method() {
return this.fetchOptions.method;
}
set method(value) {
const fetchBody = this.isSafe ? this.url.searchParams : this.fetchOptions.body || new FormData();
const fetchMethod = fetchMethodFromString(value) || FetchMethod.get;
this.url.search = "";
const [url, body] = buildResourceAndBody(this.url, fetchMethod, fetchBody, this.enctype);
this.url = url;
this.fetchOptions.body = body;
this.fetchOptions.method = fetchMethod.toUpperCase();
}
get headers() {
return this.fetchOptions.headers;
}
set headers(value) {
this.fetchOptions.headers = value;
}
get body() {
if (this.isSafe) {
return this.url.searchParams;
} else {
return this.fetchOptions.body;
}
}
set body(value) {
this.fetchOptions.body = value;
}
get location() {
return this.url;
}
get params() {
return this.url.searchParams;
}
get entries() {
return this.body ? Array.from(this.body.entries()) : [];
}
cancel() {
this.abortController.abort();
}
async perform() {
const { fetchOptions } = this;
this.delegate.prepareRequest(this);
const event = await this.#allowRequestToBeIntercepted(fetchOptions);
try {
this.delegate.requestStarted(this);
if (event.detail.fetchRequest) {
this.response = event.detail.fetchRequest.response;
} else {
this.response = fetchWithTurboHeaders(this.url.href, fetchOptions);
}
const response = await this.response;
return await this.receive(response);
} catch (error2) {
if (error2.name !== "AbortError") {
if (this.#willDelegateErrorHandling(error2)) {
this.delegate.requestErrored(this, error2);
}
throw error2;
}
} finally {
this.delegate.requestFinished(this);
}
}
async receive(response) {
const fetchResponse = new FetchResponse(response);
const event = dispatch("turbo:before-fetch-response", {
cancelable: true,
detail: { fetchResponse },
target: this.target
});
if (event.defaultPrevented) {
this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
} else if (fetchResponse.succeeded) {
this.delegate.requestSucceededWithResponse(this, fetchResponse);
} else {
this.delegate.requestFailedWithResponse(this, fetchResponse);
}
return fetchResponse;
}
get defaultHeaders() {
return {
Accept: "text/html, application/xhtml+xml"
};
}
get isSafe() {
return isSafe(this.method);
}
get abortSignal() {
return this.abortController.signal;
}
acceptResponseType(mimeType) {
this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", ");
}
async #allowRequestToBeIntercepted(fetchOptions) {
const requestInterception = new Promise((resolve) => this.#resolveRequestPromise = resolve);
const event = dispatch("turbo:before-fetch-request", {
cancelable: true,
detail: {
fetchOptions,
url: this.url,
resume: this.#resolveRequestPromise
},
target: this.target
});
this.url = event.detail.url;
if (event.defaultPrevented) await requestInterception;
return event;
}
#willDelegateErrorHandling(error2) {
const event = dispatch("turbo:fetch-request-error", {
target: this.target,
cancelable: true,
detail: { request: this, error: error2 }
});
return !event.defaultPrevented;
}
};
function isSafe(fetchMethod) {
return fetchMethodFromString(fetchMethod) == FetchMethod.get;
}
function buildResourceAndBody(resource, method, requestBody, enctype) {
const searchParams = Array.from(requestBody).length > 0 ? new URLSearchParams(entriesExcludingFiles(requestBody)) : resource.searchParams;
if (isSafe(method)) {
return [mergeIntoURLSearchParams(resource, searchParams), null];
} else if (enctype == FetchEnctype.urlEncoded) {
return [resource, searchParams];
} else {
return [resource, requestBody];
}
}
function entriesExcludingFiles(requestBody) {
const entries = [];
for (const [name, value] of requestBody) {
if (value instanceof File) continue;
else entries.push([name, value]);
}
return entries;
}
function mergeIntoURLSearchParams(url, requestBody) {
const searchParams = new URLSearchParams(entriesExcludingFiles(requestBody));
url.search = searchParams.toString();
return url;
}
var AppearanceObserver = class {
started = false;
constructor(delegate, element) {
this.delegate = delegate;
this.element = element;
this.intersectionObserver = new IntersectionObserver(this.intersect);
}
start() {
if (!this.started) {
this.started = true;
this.intersectionObserver.observe(this.element);
}
}
stop() {
if (this.started) {
this.started = false;
this.intersectionObserver.unobserve(this.element);
}
}
intersect = (entries) => {
const lastEntry = entries.slice(-1)[0];
if (lastEntry?.isIntersecting) {
this.delegate.elementAppearedInViewport(this.element);
}
};
};
var StreamMessage = class {
static contentType = "text/vnd.turbo-stream.html";
static wrap(message) {
if (typeof message == "string") {
return new this(createDocumentFragment(message));
} else {
return message;
}
}
constructor(fragment) {
this.fragment = importStreamElements(fragment);
}
};
function importStreamElements(fragment) {
for (const element of fragment.querySelectorAll("turbo-stream")) {
const streamElement = document.importNode(element, true);
for (const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")) {
inertScriptElement.replaceWith(activateScriptElement(inertScriptElement));
}
element.replaceWith(streamElement);
}
return fragment;
}
var PREFETCH_DELAY = 100;
var PrefetchCache = class {
#prefetchTimeout = null;
#prefetched = null;
get(url) {
if (this.#prefetched && this.#prefetched.url === url && this.#prefetched.expire > Date.now()) {
return this.#prefetched.request;
}
}
setLater(url, request, ttl) {
this.clear();
this.#prefetchTimeout = setTimeout(() => {
request.perform();
this.set(url, request, ttl);
this.#prefetchTimeout = null;
}, PREFETCH_DELAY);
}
set(url, request, ttl) {
this.#prefetched = { url, request, expire: new Date((/* @__PURE__ */ new Date()).getTime() + ttl) };
}
clear() {
if (this.#prefetchTimeout) clearTimeout(this.#prefetchTimeout);
this.#prefetched = null;
}
};
var cacheTtl = 10 * 1e3;
var prefetchCache = new PrefetchCache();
var FormSubmissionState = {
initialized: "initialized",
requesting: "requesting",
waiting: "waiting",
receiving: "receiving",
stopping: "stopping",
stopped: "stopped"
};
var FormSubmission = class _FormSubmission {
state = FormSubmissionState.initialized;
static confirmMethod(message, _element, _submitter) {
return Promise.resolve(confirm(message));
}
constructor(delegate, formElement, submitter, mustRedirect = false) {
const method = getMethod(formElement, submitter);
const action = getAction(getFormAction(formElement, submitter), method);
const body = buildFormData(formElement, submitter);
const enctype = getEnctype(formElement, submitter);
this.delegate = delegate;
this.formElement = formElement;
this.submitter = submitter;
this.fetchRequest = new FetchRequest(this, method, action, body, formElement, enctype);
this.mustRedirect = mustRedirect;
}
get method() {
return this.fetchRequest.method;
}
set method(value) {
this.fetchRequest.method = value;
}
get action() {
return this.fetchRequest.url.toString();
}
set action(value) {
this.fetchRequest.url = expandURL(value);
}
get body() {
return this.fetchRequest.body;
}
get enctype() {
return this.fetchRequest.enctype;
}
get isSafe() {
return this.fetchRequest.isSafe;
}
get location() {
return this.fetchRequest.url;
}
// The submission process
async start() {
const { initialized, requesting } = FormSubmissionState;
const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
if (typeof confirmationMessage === "string") {
const answer = await _FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter);
if (!answer) {
return;
}
}
if (this.state == initialized) {
this.state = requesting;
return this.fetchRequest.perform();
}
}
stop() {
const { stopping, stopped } = FormSubmissionState;
if (this.state != stopping && this.state != stopped) {
this.state = stopping;
this.fetchRequest.cancel();
return true;
}
}
// Fetch request delegate
prepareRequest(request) {
if (!request.isSafe) {
const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token");
if (token) {
request.headers["X-CSRF-Token"] = token;
}
}
if (this.requestAcceptsTurboStreamResponse(request)) {
request.acceptResponseType(StreamMessage.contentType);
}
}
requestStarted(_request) {
this.state = FormSubmissionState.waiting;
this.submitter?.setAttribute("disabled", "");
this.setSubmitsWith();
markAsBusy(this.formElement);
dispatch("turbo:submit-start", {
target: this.formElement,
detail: { formSubmission: this }
});
this.delegate.formSubmissionStarted(this);
}
requestPreventedHandlingResponse(request, response) {
prefetchCache.clear();
this.result = { success: response.succeeded, fetchResponse: response };
}
requestSucceededWithResponse(request, response) {
if (response.clientError || response.serverError) {
this.delegate.formSubmissionFailedWithResponse(this, response);
return;
}
prefetchCache.clear();
if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) {
const error2 = new Error("Form responses must redirect to another location");
this.delegate.formSubmissionErrored(this, error2);
} else {
this.state = FormSubmissionState.receiving;
this.result = { success: true, fetchResponse: response };
this.delegate.formSubmissionSucceededWithResponse(this, response);
}
}
requestFailedWithResponse(request, response) {
this.result = { success: false, fetchResponse: response };
this.delegate.formSubmissionFailedWithResponse(this, response);
}
requestErrored(request, error2) {
this.result = { success: false, error: error2 };
this.delegate.formSubmissionErrored(this, error2);
}
requestFinished(_request) {
this.state = FormSubmissionState.stopped;
this.submitter?.removeAttribute("disabled");
this.resetSubmitterText();
clearBusyState(this.formElement);
dispatch("turbo:submit-end", {
target: this.formElement,
detail: { formSubmission: this, ...this.result }
});
this.delegate.formSubmissionFinished(this);
}
// Private
setSubmitsWith() {
if (!this.submitter || !this.submitsWith) return;
if (this.submitter.matches("button")) {
this.originalSubmitText = this.submitter.innerHTML;
this.submitter.innerHTML = this.submitsWith;
} else if (this.submitter.matches("input")) {
const input = this.submitter;
this.originalSubmitText = input.value;
input.value = this.submitsWith;
}
}
resetSubmitterText() {
if (!this.submitter || !this.originalSubmitText) return;
if (this.submitter.matches("button")) {
this.submitter.innerHTML = this.originalSubmitText;
} else if (this.submitter.matches("input")) {
const input = this.submitter;
input.value = this.originalSubmitText;
}
}
requestMustRedirect(request) {
return !request.isSafe && this.mustRedirect;
}
requestAcceptsTurboStreamResponse(request) {
return !request.isSafe || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
}
get submitsWith() {
return this.submitter?.getAttribute("data-turbo-submits-with");
}
};
function buildFormData(formElement, submitter) {
const formData = new FormData(formElement);
const name = submitter?.getAttribute("name");
const value = submitter?.getAttribute("value");
if (name) {
formData.append(name, value || "");
}
return formData;
}
function getCookieValue(cookieName) {
if (cookieName != null) {
const cookies = document.cookie ? document.cookie.split("; ") : [];
const cookie = cookies.find((cookie2) => cookie2.startsWith(cookieName));
if (cookie) {
const value = cookie.split("=").slice(1).join("=");
return value ? decodeURIComponent(value) : void 0;
}
}
}
function responseSucceededWithoutRedirect(response) {
return response.statusCode == 200 && !response.redirected;
}
function getFormAction(formElement, submitter) {
const formElementAction = typeof formElement.action === "string" ? formElement.action : null;
if (submitter?.hasAttribute("formaction")) {
return submitter.getAttribute("formaction") || "";
} else {
return formElement.getAttribute("action") || formElementAction || "";
}
}
function getAction(formAction, fetchMethod) {
const action = expandURL(formAction);
if (isSafe(fetchMethod)) {
action.search = "";
}
return action;
}
function getMethod(formElement, submitter) {
const method = submitter?.getAttribute("formmethod") || formElement.getAttribute("method") || "";
return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get;
}
function getEnctype(formElement, submitter) {
return fetchEnctypeFromString(submitter?.getAttribute("formenctype") || formElement.enctype);
}
var Snapshot = class {
constructor(element) {
this.element = element;
}
get activeElement() {
return this.element.ownerDocument.activeElement;
}
get children() {
return [...this.element.children];
}
hasAnchor(anchor) {
return this.getElementForAnchor(anchor) != null;
}
getElementForAnchor(anchor) {
return anchor ? this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`) : null;
}
get isConnected() {
return this.element.isConnected;
}
get firstAutofocusableElement() {
return queryAutofocusableElement(this.element);
}
get permanentElements() {
return queryPermanentElementsAll(this.element);
}
getPermanentElementById(id) {
return getPermanentElementById(this.element, id);
}
getPermanentElementMapForSnapshot(snapshot) {
const permanentElementMap = {};
for (const currentPermanentElement of this.permanentElements) {
const { id } = currentPermanentElement;
const newPermanentElement = snapshot.getPermanentElementById(id);
if (newPermanentElement) {
permanentElementMap[id] = [currentPermanentElement, newPermanentElement];
}
}
return permanentElementMap;
}
};
function getPermanentElementById(node, id) {
return node.querySelector(`#${id}[data-turbo-permanent]`);
}
function queryPermanentElementsAll(node) {
return node.querySelectorAll("[id][data-turbo-permanent]");
}
var FormSubmitObserver = class {
started = false;
constructor(delegate, eventTarget) {
this.delegate = delegate;
this.eventTarget = eventTarget;
}
start() {
if (!this.started) {
this.eventTarget.addEventListener("submit", this.submitCaptured, true);
this.started = true;
}
}
stop() {
if (this.started) {
this.eventTarget.removeEventListener("submit", this.submitCaptured, true);
this.started = false;
}
}
submitCaptured = () => {
this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
this.eventTarget.addEventListener("submit", this.submitBubbled, false);
};
submitBubbled = (event) => {
if (!event.defaultPrevented) {
const form = event.target instanceof HTMLFormElement ? event.target : void 0;
const submitter = event.submitter || void 0;
if (form && submissionDoesNotDismissDialog(form, submitter) && submissionDoesNotTargetIFrame(form, submitter) && this.delegate.willSubmitForm(form, submitter)) {
event.preventDefault();
event.stopImmediatePropagation();
this.delegate.formSubmitted(form, submitter);
}
}
};
};
function submissionDoesNotDismissDialog(form, submitter) {
const method = submitter?.getAttribute("formmethod") || form.getAttribute("method");
return method != "dialog";
}
function submissionDoesNotTargetIFrame(form, submitter) {
const target = submitter?.getAttribute("formtarget") || form.getAttribute("target");
return doesNotTargetIFrame(target);
}
var View = class {
#resolveRenderPromise = (_value) => {
};
#resolveInterceptionPromise = (_value) => {
};
constructor(delegate, element) {
this.delegate = delegate;
this.element = element;
}
// Scrolling
scrollToAnchor(anchor) {
const element = this.snapshot.getElementForAnchor(anchor);
if (element) {
this.scrollToElement(element);
this.focusElement(element);
} else {
this.scrollToPosition({ x: 0, y: 0 });
}
}
scrollToAnchorFromLocation(location2) {
this.scrollToAnchor(getAnchor(location2));
}
scrollToElement(element) {
element.scrollIntoView();
}
focusElement(element) {
if (element instanceof HTMLElement) {
if (element.hasAttribute("tabindex")) {
element.focus();
} else {
element.setAttribute("tabindex", "-1");
element.focus();
element.removeAttribute("tabindex");
}
}
}
scrollToPosition({ x, y }) {
this.scrollRoot.scrollTo(x, y);
}
scrollToTop() {
this.scrollToPosition({ x: 0, y: 0 });
}
get scrollRoot() {
return window;
}
// Rendering
async render(renderer) {
const { isPreview, shouldRender, willRender, newSnapshot: snapshot } = renderer;
const shouldInvalidate = willRender;
if (shouldRender) {
try {
this.renderPromise = new Promise((resolve) => this.#resolveRenderPromise = resolve);
this.renderer = renderer;
await this.prepareToRenderSnapshot(renderer);
const renderInterception = new Promise((resolve) => this.#resolveInterceptionPromise = resolve);
const options = { resume: this.#resolveInterceptionPromise, render: this.renderer.renderElement, renderMethod: this.renderer.renderMethod };
const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
if (!immediateRender) await renderInterception;
await this.renderSnapshot(renderer);
this.delegate.viewRenderedSnapshot(snapshot, isPreview, this.renderer.renderMethod);
this.delegate.preloadOnLoadLinksForView(this.element);
this.finishRenderingSnapshot(renderer);
} finally {
delete this.renderer;
this.#resolveRenderPromise(void 0);
delete this.renderPromise;
}
} else if (shouldInvalidate) {
this.invalidate(renderer.reloadReason);
}
}
invalidate(reason) {
this.delegate.viewInvalidated(reason);
}
async prepareToRenderSnapshot(renderer) {
this.markAsPreview(renderer.isPreview);
await renderer.prepareToRender();
}
markAsPreview(isPreview) {
if (isPreview) {
this.element.setAttribute("data-turbo-preview", "");
} else {
this.element.removeAttribute("data-turbo-preview");
}
}
markVisitDirection(direction) {
this.element.setAttribute("data-turbo-visit-direction", direction);
}
unmarkVisitDirection() {
this.element.removeAttribute("data-turbo-visit-direction");
}
async renderSnapshot(renderer) {
await renderer.render();
}
finishRenderingSnapshot(renderer) {
renderer.finishRendering();
}
};
var FrameView = class extends View {
missing() {
this.element.innerHTML = `Content missing`;
}
get snapshot() {
return new Snapshot(this.element);
}
};
var LinkInterceptor = class {
constructor(delegate, element) {
this.delegate = delegate;
this.element = element;
}
start() {
this.element.addEventListener("click", this.clickBubbled);
document.addEventListener("turbo:click", this.linkClicked);
document.addEventListener("turbo:before-visit", this.willVisit);
}
stop() {
this.element.removeEventListener("click", this.clickBubbled);
document.removeEventListener("turbo:click", this.linkClicked);
document.removeEventListener("turbo:before-visit", this.willVisit);
}
clickBubbled = (event) => {
if (this.clickEventIsSignificant(event)) {
this.clickEvent = event;
} else {
delete this.clickEvent;
}
};
linkClicked = (event) => {
if (this.clickEvent && this.clickEventIsSignificant(event)) {
if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url, event.detail.originalEvent)) {
this.clickEvent.preventDefault();
event.preventDefault();
this.delegate.linkClickIntercepted(event.target, event.detail.url, event.detail.originalEvent);
}
}
delete this.clickEvent;
};
willVisit = (_event) => {
delete this.clickEvent;
};
clickEventIsSignificant(event) {
const target = event.composed ? event.target?.parentElement : event.target;
const element = findLinkFromClickTarget(target) || target;
return element instanceof Element && element.closest("turbo-frame, html") == this.element;
}
};
var LinkClickObserver = class {
started = false;
constructor(delegate, eventTarget) {
this.delegate = delegate;
this.eventTarget = eventTarget;
}
start() {
if (!this.started) {
this.eventTarget.addEventListener("click", this.clickCaptured, true);
this.started = true;
}
}
stop() {
if (this.started) {
this.eventTarget.removeEventListener("click", this.clickCaptured, true);
this.started = false;
}
}
clickCaptured = () => {
this.eventTarget.removeEventListener("click", this.clickBubbled, false);
this.eventTarget.addEventListener("click", this.clickBubbled, false);
};
clickBubbled = (event) => {
if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
const target = event.composedPath && event.composedPath()[0] || event.target;
const link = findLinkFromClickTarget(target);
if (link && doesNotTargetIFrame(link.target)) {
const location2 = getLocationForLink(link);
if (this.delegate.willFollowLinkToLocation(link, location2, event)) {
event.preventDefault();
this.delegate.followedLinkToLocation(link, location2);
}
}
}
};
clickEventIsSignificant(event) {
return !(event.target && event.target.isContentEditable || event.defaultPrevented || event.which > 1 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey);
}
};
var FormLinkClickObserver = class {
constructor(delegate, element) {
this.delegate = delegate;
this.linkInterceptor = new LinkClickObserver(this, element);
}
start() {
this.linkInterceptor.start();
}
stop() {
this.linkInterceptor.stop();
}
// Link hover observer delegate
canPrefetchRequestToLocation(link, location2) {
return false;
}
prefetchAndCacheRequestToLocation(link, location2) {
return;
}
// Link click observer delegate
willFollowLinkToLocation(link, location2, originalEvent) {
return this.delegate.willSubmitFormLinkToLocation(link, location2, originalEvent) && (link.hasAttribute("data-turbo-method") || link.hasAttribute("data-turbo-stream"));
}
followedLinkToLocation(link, location2) {
const form = document.createElement("form");
const type = "hidden";
for (const [name, value] of location2.searchParams) {
form.append(Object.assign(document.createElement("input"), { type, name, value }));
}
const action = Object.assign(location2, { search: "" });
form.setAttribute("data-turbo", "true");
form.setAttribute("action", action.href);
form.setAttribute("hidden", "");
const method = link.getAttribute("data-turbo-method");
if (method) form.setAttribute("method", method);
const turboFrame = link.getAttribute("data-turbo-frame");
if (turboFrame) form.setAttribute("data-turbo-frame", turboFrame);
const turboAction = getVisitAction(link);
if (turboAction) form.setAttribute("data-turbo-action", turboAction);
const turboConfirm = link.getAttribute("data-turbo-confirm");
if (turboConfirm) form.setAttribute("data-turbo-confirm", turboConfirm);
const turboStream = link.hasAttribute("data-turbo-stream");
if (turboStream) form.setAttribute("data-turbo-stream", "");
this.delegate.submittedFormLinkToLocation(link, location2, form);
document.body.appendChild(form);
form.addEventListener("turbo:submit-end", () => form.remove(), { once: true });
requestAnimationFrame(() => form.requestSubmit());
}
};
var Bardo = class {
static async preservingPermanentElements(delegate, permanentElementMap, callback) {
const bardo = new this(delegate, permanentElementMap);
bardo.enter();
await callback();
bardo.leave();
}
constructor(delegate, permanentElementMap) {
this.delegate = delegate;
this.permanentElementMap = permanentElementMap;
}
enter() {
for (const id in this.permanentElementMap) {
const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
}
}
leave() {
for (const id in this.permanentElementMap) {
const [currentPermanentElement] = this.permanentElementMap[id];
this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
this.replacePlaceholderWithPermanentElement(currentPermanentElement);
this.delegate.leavingBardo(currentPermanentElement);
}
}
replaceNewPermanentElementWithPlaceholder(permanentElement) {
const placeholder = createPlaceholderForPermanentElement(permanentElement);
permanentElement.replaceWith(placeholder);
}
replaceCurrentPermanentElementWithClone(permanentElement) {
const clone = permanentElement.cloneNode(true);
permanentElement.replaceWith(clone);
}
replacePlaceholderWithPermanentElement(permanentElement) {
const placeholder = this.getPlaceholderById(permanentElement.id);
placeholder?.replaceWith(permanentElement);
}
getPlaceholderById(id) {
return this.placeholders.find((element) => element.content == id);
}
get placeholders() {
return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
}
};
function createPlaceholderForPermanentElement(permanentElement) {
const element = document.createElement("meta");
element.setAttribute("name", "turbo-permanent-placeholder");
element.setAttribute("content", permanentElement.id);
return element;
}
var Renderer = class {
#activeElement = null;
constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
this.currentSnapshot = currentSnapshot;
this.newSnapshot = newSnapshot;
this.isPreview = isPreview;
this.willRender = willRender;
this.renderElement = renderElement;
this.promise = new Promise((resolve, reject) => this.resolvingFunctions = { resolve, reject });
}
get shouldRender() {
return true;
}
get shouldAutofocus() {
return true;
}
get reloadReason() {
return;
}
prepareToRender() {
return;
}
render() {
}
finishRendering() {
if (this.resolvingFunctions) {
this.resolvingFunctions.resolve();
delete this.resolvingFunctions;
}
}
async preservingPermanentElements(callback) {
await Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
}
focusFirstAutofocusableElement() {
if (this.shouldAutofocus) {
const element = this.connectedSnapshot.firstAutofocusableElement;
if (element) {
element.focus();
}
}
}
// Bardo delegate
enteringBardo(currentPermanentElement) {
if (this.#activeElement) return;
if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
this.#activeElement = this.currentSnapshot.activeElement;
}
}
leavingBardo(currentPermanentElement) {
if (currentPermanentElement.contains(this.#activeElement) && this.#activeElement instanceof HTMLElement) {
this.#activeElement.focus();
this.#activeElement = null;
}
}
get connectedSnapshot() {
return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
}
get currentElement() {
return this.currentSnapshot.element;
}
get newElement() {
return this.newSnapshot.element;
}
get permanentElementMap() {
return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
}
get renderMethod() {
return "replace";
}
};
var FrameRenderer = class extends Renderer {
static renderElement(currentElement, newElement) {
const destinationRange = document.createRange();
destinationRange.selectNodeContents(currentElement);
destinationRange.deleteContents();
const frameElement = newElement;
const sourceRange = frameElement.ownerDocument?.createRange();
if (sourceRange) {
sourceRange.selectNodeContents(frameElement);
currentElement.appendChild(sourceRange.extractContents());
}
}
constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
this.delegate = delegate;
}
get shouldRender() {
return true;
}
async render() {
await nextRepaint();
this.preservingPermanentElements(() => {
this.loadFrameElement();
});
this.scrollFrameIntoView();
await nextRepaint();
this.focusFirstAutofocusableElement();
await nextRepaint();
this.activateScriptElements();
}
loadFrameElement() {
this.delegate.willRenderFrame(this.currentElement, this.newElement);
this.renderElement(this.currentElement, this.newElement);
}
scrollFrameIntoView() {
if (this.currentElement.autoscroll || this.newElement.autoscroll) {
const element = this.currentElement.firstElementChild;
const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
if (element) {
element.scrollIntoView({ block, behavior });
return true;
}
}
return false;
}
activateScriptElements() {
for (const inertScriptElement of this.newScriptElements) {
const activatedScriptElement = activateScriptElement(inertScriptElement);
inertScriptElement.replaceWith(activatedScriptElement);
}
}
get newScriptElements() {
return this.currentElement.querySelectorAll("script");
}
};
function readScrollLogicalPosition(value, defaultValue) {
if (value == "end" || value == "start" || value == "center" || value == "nearest") {
return value;
} else {
return defaultValue;
}
}
function readScrollBehavior(value, defaultValue) {
if (value == "auto" || value == "smooth") {
return value;
} else {
return defaultValue;
}
}
var ProgressBar = class _ProgressBar {
static animationDuration = 300;
/*ms*/
static get defaultCSS() {
return unindent`
.turbo-progress-bar {
position: fixed;
display: block;
top: 0;
left: 0;
height: 3px;
background: #0076ff;
z-index: 2147483647;
transition:
width ${_ProgressBar.animationDuration}ms ease-out,
opacity ${_ProgressBar.animationDuration / 2}ms ${_ProgressBar.animationDuration / 2}ms ease-in;
transform: translate3d(0, 0, 0);
}
`;
}
hiding = false;
value = 0;
visible = false;
constructor() {
this.stylesheetElement = this.createStylesheetElement();
this.progressElement = this.createProgressElement();
this.installStylesheetElement();
this.setValue(0);
}
show() {
if (!this.visible) {
this.visible = true;
this.installProgressElement();
this.startTrickling();
}
}
hide() {
if (this.visible && !this.hiding) {
this.hiding = true;
this.fadeProgressElement(() => {
this.uninstallProgressElement();
this.stopTrickling();
this.visible = false;
this.hiding = false;
});
}
}
setValue(value) {
this.value = value;
this.refresh();
}
// Private
installStylesheetElement() {
document.head.insertBefore(this.stylesheetElement, document.head.firstChild);
}
installProgressElement() {
this.progressElement.style.width = "0";
this.progressElement.style.opacity = "1";
document.documentElement.insertBefore(this.progressElement, document.body);
this.refresh();
}
fadeProgressElement(callback) {
this.progressElement.style.opacity = "0";
setTimeout(callback, _ProgressBar.animationDuration * 1.5);
}
uninstallProgressElement() {
if (this.progressElement.parentNode) {
document.documentElement.removeChild(this.progressElement);
}
}
startTrickling() {
if (!this.trickleInterval) {
this.trickleInterval = window.setInterval(this.trickle, _ProgressBar.animationDuration);
}
}
stopTrickling() {
window.clearInterval(this.trickleInterval);
delete this.trickleInterval;
}
trickle = () => {
this.setValue(this.value + Math.random() / 100);
};
refresh() {
requestAnimationFrame(() => {
this.progressElement.style.width = `${10 + this.value * 90}%`;
});
}
createStylesheetElement() {
const element = document.createElement("style");
element.type = "text/css";
element.textContent = _ProgressBar.defaultCSS;
if (this.cspNonce) {
element.nonce = this.cspNonce;
}
return element;
}
createProgressElement() {
const element = document.createElement("div");
element.className = "turbo-progress-bar";
return element;
}
get cspNonce() {
return getMetaContent("csp-nonce");
}
};
var HeadSnapshot = class extends Snapshot {
detailsByOuterHTML = this.children.filter((element) => !elementIsNoscript(element)).map((element) => elementWithoutNonce(element)).reduce((result, element) => {
const { outerHTML } = element;
const details = outerHTML in result ? result[outerHTML] : {
type: elementType(element),
tracked: elementIsTracked(element),
elements: []
};
return {
...result,
[outerHTML]: {
...details,
elements: [...details.elements, element]
}
};
}, {});
get trackedElementSignature() {
return Object.keys(this.detailsByOuterHTML).filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked).join("");
}
getScriptElementsNotInSnapshot(snapshot) {
return this.getElementsMatchingTypeNotInSnapshot("script", snapshot);
}
getStylesheetElementsNotInSnapshot(snapshot) {
return this.getElementsMatchingTypeNotInSnapshot("stylesheet", snapshot);
}
getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
return Object.keys(this.detailsByOuterHTML).filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML)).map((outerHTML) => this.detailsByOuterHTML[outerHTML]).filter(({ type }) => type == matchedType).map(({ elements: [element] }) => element);
}
get provisionalElements() {
return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
const { type, tracked, elements } = this.detailsByOuterHTML[outerHTML];
if (type == null && !tracked) {
return [...result, ...elements];
} else if (elements.length > 1) {
return [...result, ...elements.slice(1)];
} else {
return result;
}
}, []);
}
getMetaValue(name) {
const element = this.findMetaElementByName(name);
return element ? element.getAttribute("content") : null;
}
findMetaElementByName(name) {
return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
const {
elements: [element]
} = this.detailsByOuterHTML[outerHTML];
return elementIsMetaElementWithName(element, name) ? element : result;
}, void 0 | void 0);
}
};
function elementType(element) {
if (elementIsScript(element)) {
return "script";
} else if (elementIsStylesheet(element)) {
return "stylesheet";
}
}
function elementIsTracked(element) {
return element.getAttribute("data-turbo-track") == "reload";
}
function elementIsScript(element) {
const tagName = element.localName;
return tagName == "script";
}
function elementIsNoscript(element) {
const tagName = element.localName;
return tagName == "noscript";
}
function elementIsStylesheet(element) {
const tagName = element.localName;
return tagName == "style" || tagName == "link" && element.getAttribute("rel") == "stylesheet";
}
function elementIsMetaElementWithName(element, name) {
const tagName = element.localName;
return tagName == "meta" && element.getAttribute("name") == name;
}
function elementWithoutNonce(element) {
if (element.hasAttribute("nonce")) {
element.setAttribute("nonce", "");
}
return element;
}
var PageSnapshot = class _PageSnapshot extends Snapshot {
static fromHTMLString(html = "") {
return this.fromDocument(parseHTMLDocument(html));
}
static fromElement(element) {
return this.fromDocument(element.ownerDocument);
}
static fromDocument({ documentElement, body, head }) {
return new this(documentElement, body, new HeadSnapshot(head));
}
constructor(documentElement, body, headSnapshot) {
super(body);
this.documentElement = documentElement;
this.headSnapshot = headSnapshot;
}
clone() {
const clonedElement = this.element.cloneNode(true);
const selectElements = this.element.querySelectorAll("select");
const clonedSelectElements = clonedElement.querySelectorAll("select");
for (const [index, source] of selectElements.entries()) {
const clone = clonedSelectElements[index];
for (const option of clone.selectedOptions) option.selected = false;
for (const option of source.selectedOptions) clone.options[option.index].selected = true;
}
for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
clonedPasswordInput.value = "";
}
return new _PageSnapshot(this.documentElement, clonedElement, this.headSnapshot);
}
get lang() {
return this.documentElement.getAttribute("lang");
}
get headElement() {
return this.headSnapshot.element;
}
get rootLocation() {
const root = this.getSetting("root") ?? "/";
return expandURL(root);
}
get cacheControlValue() {
return this.getSetting("cache-control");
}
get isPreviewable() {
return this.cacheControlValue != "no-preview";
}
get isCacheable() {
return this.cacheControlValue != "no-cache";
}
get isVisitable() {
return this.getSetting("visit-control") != "reload";
}
get prefersViewTransitions() {
return this.headSnapshot.getMetaValue("view-transition") === "same-origin";
}
get shouldMorphPage() {
return this.getSetting("refresh-method") === "morph";
}
get shouldPreserveScrollPosition() {
return this.getSetting("refresh-scroll") === "preserve";
}
// Private
getSetting(name) {
return this.headSnapshot.getMetaValue(`turbo-${name}`);
}
};
var ViewTransitioner = class {
#viewTransitionStarted = false;
#lastOperation = Promise.resolve();
renderChange(useViewTransition, render) {
if (useViewTransition && this.viewTransitionsAvailable && !this.#viewTransitionStarted) {
this.#viewTransitionStarted = true;
this.#lastOperation = this.#lastOperation.then(async () => {
await document.startViewTransition(render).finished;
});
} else {
this.#lastOperation = this.#lastOperation.then(render);
}
return this.#lastOperation;
}
get viewTransitionsAvailable() {
return document.startViewTransition;
}
};
var defaultOptions = {
action: "advance",
historyChanged: false,
visitCachedSnapshot: () => {
},
willRender: true,
updateHistory: true,
shouldCacheSnapshot: true,
acceptsStreamResponse: false
};
var TimingMetric = {
visitStart: "visitStart",
requestStart: "requestStart",
requestEnd: "requestEnd",
visitEnd: "visitEnd"
};
var VisitState = {
initialized: "initialized",
started: "started",
canceled: "canceled",
failed: "failed",
completed: "completed"
};
var SystemStatusCode = {
networkFailure: 0,
timeoutFailure: -1,
contentTypeMismatch: -2
};
var Direction = {
advance: "forward",
restore: "back",
replace: "none"
};
var Visit = class {
identifier = uuid();
// Required by turbo-ios
timingMetrics = {};
followedRedirect = false;
historyChanged = false;
scrolled = false;
shouldCacheSnapshot = true;
acceptsStreamResponse = false;
snapshotCached = false;
state = VisitState.initialized;
viewTransitioner = new ViewTransitioner();
constructor(delegate, location2, restorationIdentifier, options = {}) {
this.delegate = delegate;
this.location = location2;
this.restorationIdentifier = restorationIdentifier || uuid();
const {
action,
historyChanged,
referrer,
snapshot,
snapshotHTML,
response,
visitCachedSnapshot,
willRender,
updateHistory,
shouldCacheSnapshot,
acceptsStreamResponse,
direction
} = {
...defaultOptions,
...options
};
this.action = action;
this.historyChanged = historyChanged;
this.referrer = referrer;
this.snapshot = snapshot;
this.snapshotHTML = snapshotHTML;
this.response = response;
this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
this.isPageRefresh = this.view.isPageRefresh(this);
this.visitCachedSnapshot = visitCachedSnapshot;
this.willRender = willRender;
this.updateHistory = updateHistory;
this.scrolled = !willRender;
this.shouldCacheSnapshot = shouldCacheSnapshot;
this.acceptsStreamResponse = acceptsStreamResponse;
this.direction = direction || Direction[action];
}
get adapter() {
return this.delegate.adapter;
}
get view() {
return this.delegate.view;
}
get history() {
return this.delegate.history;
}
get restorationData() {
return this.history.getRestorationDataForIdentifier(this.restorationIdentifier);
}
get silent() {
return this.isSamePage;
}
start() {
if (this.state == VisitState.initialized) {
this.recordTimingMetric(TimingMetric.visitStart);
this.state = VisitState.started;
this.adapter.visitStarted(this);
this.delegate.visitStarted(this);
}
}
cancel() {
if (this.state == VisitState.started) {
if (this.request) {
this.request.cancel();
}
this.cancelRender();
this.state = VisitState.canceled;
}
}
complete() {
if (this.state == VisitState.started) {
this.recordTimingMetric(TimingMetric.visitEnd);
this.adapter.visitCompleted(this);
this.state = VisitState.completed;
this.followRedirect();
if (!this.followedRedirect) {
this.delegate.visitCompleted(this);
}
}
}
fail() {
if (this.state == VisitState.started) {
this.state = VisitState.failed;
this.adapter.visitFailed(this);
this.delegate.visitCompleted(this);
}
}
changeHistory() {
if (!this.historyChanged && this.updateHistory) {
const actionForHistory = this.location.href === this.referrer?.href ? "replace" : this.action;
const method = getHistoryMethodForAction(actionForHistory);
this.history.update(method, this.location, this.restorationIdentifier);
this.historyChanged = true;
}
}
issueRequest() {
if (this.hasPreloadedResponse()) {
this.simulateRequest();
} else if (this.shouldIssueRequest() && !this.request) {
this.request = new FetchRequest(this, FetchMethod.get, this.location);
this.request.perform();
}
}
simulateRequest() {
if (this.response) {
this.startRequest();
this.recordResponse();
this.finishRequest();
}
}
startRequest() {
this.recordTimingMetric(TimingMetric.requestStart);
this.adapter.visitRequestStarted(this);
}
recordResponse(response = this.response) {
this.response = response;
if (response) {
const { statusCode } = response;
if (isSuccessful(statusCode)) {
this.adapter.visitRequestCompleted(this);
} else {
this.adapter.visitRequestFailedWithStatusCode(this, statusCode);
}
}
}
finishRequest() {
this.recordTimingMetric(TimingMetric.requestEnd);
this.adapter.visitRequestFinished(this);
}
loadResponse() {
if (this.response) {
const { statusCode, responseHTML } = this.response;
this.render(async () => {
if (this.shouldCacheSnapshot) this.cacheSnapshot();
if (this.view.renderPromise) await this.view.renderPromise;
if (isSuccessful(statusCode) && responseHTML != null) {
const snapshot = PageSnapshot.fromHTMLString(responseHTML);
await this.renderPageSnapshot(snapshot, false);
this.adapter.visitRendered(this);
this.complete();
} else {
await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
this.adapter.visitRendered(this);
this.fail();
}
});
}
}
getCachedSnapshot() {
const snapshot = this.view.getCachedSnapshotForLocation(this.location) || this.getPreloadedSnapshot();
if (snapshot && (!getAnchor(this.location) || snapshot.hasAnchor(getAnchor(this.location)))) {
if (this.action == "restore" || snapshot.isPreviewable) {
return snapshot;
}
}
}
getPreloadedSnapshot() {
if (this.snapshotHTML) {
return PageSnapshot.fromHTMLString(this.snapshotHTML);
}
}
hasCachedSnapshot() {
return this.getCachedSnapshot() != null;
}
loadCachedSnapshot() {
const snapshot = this.getCachedSnapshot();
if (snapshot) {
const isPreview = this.shouldIssueRequest();
this.render(async () => {
this.cacheSnapshot();
if (this.isSamePage || this.isPageRefresh) {
this.adapter.visitRendered(this);
} else {
if (this.view.renderPromise) await this.view.renderPromise;
await this.renderPageSnapshot(snapshot, isPreview);
this.adapter.visitRendered(this);
if (!isPreview) {
this.complete();
}
}
});
}
}
followRedirect() {
if (this.redirectedToLocation && !this.followedRedirect && this.response?.redirected) {
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
action: "replace",
response: this.response,
shouldCacheSnapshot: false,
willRender: false
});
this.followedRedirect = true;
}
}
goToSamePageAnchor() {
if (this.isSamePage) {
this.render(async () => {
this.cacheSnapshot();
this.performScroll();
this.changeHistory();
this.adapter.visitRendered(this);
});
}
}
// Fetch request delegate
prepareRequest(request) {
if (this.acceptsStreamResponse) {
request.acceptResponseType(StreamMessage.contentType);
}
}
requestStarted() {
this.startRequest();
}
requestPreventedHandlingResponse(_request, _response) {
}
async requestSucceededWithResponse(request, response) {
const responseHTML = await response.responseHTML;
const { redirected, statusCode } = response;
if (responseHTML == void 0) {
this.recordResponse({
statusCode: SystemStatusCode.contentTypeMismatch,
redirected
});
} else {
this.redirectedToLocation = response.redirected ? response.location : void 0;
this.recordResponse({ statusCode, responseHTML, redirected });
}
}
async requestFailedWithResponse(request, response) {
const responseHTML = await response.responseHTML;
const { redirected, statusCode } = response;
if (responseHTML == void 0) {
this.recordResponse({
statusCode: SystemStatusCode.contentTypeMismatch,
redirected
});
} else {
this.recordResponse({ statusCode, responseHTML, redirected });
}
}
requestErrored(_request, _error) {
this.recordResponse({
statusCode: SystemStatusCode.networkFailure,
redirected: false
});
}
requestFinished() {
this.finishRequest();
}
// Scrolling
performScroll() {
if (!this.scrolled && !this.view.forceReloaded && !this.view.shouldPreserveScrollPosition(this)) {
if (this.action == "restore") {
this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
} else {
this.scrollToAnchor() || this.view.scrollToTop();
}
if (this.isSamePage) {
this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation, this.location);
}
this.scrolled = true;
}
}
scrollToRestoredPosition() {
const { scrollPosition } = this.restorationData;
if (scrollPosition) {
this.view.scrollToPosition(scrollPosition);
return true;
}
}
scrollToAnchor() {
const anchor = getAnchor(this.location);
if (anchor != null) {
this.view.scrollToAnchor(anchor);
return true;
}
}
// Instrumentation
recordTimingMetric(metric) {
this.timingMetrics[metric] = (/* @__PURE__ */ new Date()).getTime();
}
getTimingMetrics() {
return { ...this.timingMetrics };
}
// Private
getHistoryMethodForAction(action) {
switch (action) {
case "replace":
return history.replaceState;
case "advance":
case "restore":
return history.pushState;
}
}
hasPreloadedResponse() {
return typeof this.response == "object";
}
shouldIssueRequest() {
if (this.isSamePage) {
return false;
} else if (this.action == "restore") {
return !this.hasCachedSnapshot();
} else {
return this.willRender;
}
}
cacheSnapshot() {
if (!this.snapshotCached) {
this.view.cacheSnapshot(this.snapshot).then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
this.snapshotCached = true;
}
}
async render(callback) {
this.cancelRender();
this.frame = await nextRepaint();
await callback();
delete this.frame;
}
async renderPageSnapshot(snapshot, isPreview) {
await this.viewTransitioner.renderChange(this.view.shouldTransitionTo(snapshot), async () => {
await this.view.renderPage(snapshot, isPreview, this.willRender, this);
this.performScroll();
});
}
cancelRender() {
if (this.frame) {
cancelAnimationFrame(this.frame);
delete this.frame;
}
}
};
function isSuccessful(statusCode) {
return statusCode >= 200 && statusCode < 300;
}
var BrowserAdapter = class {
progressBar = new ProgressBar();
constructor(session2) {
this.session = session2;
}
visitProposedToLocation(location2, options) {
if (locationIsVisitable(location2, this.navigator.rootLocation)) {
this.navigator.startVisit(location2, options?.restorationIdentifier || uuid(), options);
} else {
window.location.href = location2.toString();
}
}
visitStarted(visit2) {
this.location = visit2.location;
visit2.loadCachedSnapshot();
visit2.issueRequest();
visit2.goToSamePageAnchor();
}
visitRequestStarted(visit2) {
this.progressBar.setValue(0);
if (visit2.hasCachedSnapshot() || visit2.action != "restore") {
this.showVisitProgressBarAfterDelay();
} else {
this.showProgressBar();
}
}
visitRequestCompleted(visit2) {
visit2.loadResponse();
}
visitRequestFailedWithStatusCode(visit2, statusCode) {
switch (statusCode) {
case SystemStatusCode.networkFailure:
case SystemStatusCode.timeoutFailure:
case SystemStatusCode.contentTypeMismatch:
return this.reload({
reason: "request_failed",
context: {
statusCode
}
});
default:
return visit2.loadResponse();
}
}
visitRequestFinished(_visit) {
}
visitCompleted(_visit) {
this.progressBar.setValue(1);
this.hideVisitProgressBar();
}
pageInvalidated(reason) {
this.reload(reason);
}
visitFailed(_visit) {
this.progressBar.setValue(1);
this.hideVisitProgressBar();
}
visitRendered(_visit) {
}
// Form Submission Delegate
formSubmissionStarted(_formSubmission) {
this.progressBar.setValue(0);
this.showFormProgressBarAfterDelay();
}
formSubmissionFinished(_formSubmission) {
this.progressBar.setValue(1);
this.hideFormProgressBar();
}
// Private
showVisitProgressBarAfterDelay() {
this.visitProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
}
hideVisitProgressBar() {
this.progressBar.hide();
if (this.visitProgressBarTimeout != null) {
window.clearTimeout(this.visitProgressBarTimeout);
delete this.visitProgressBarTimeout;
}
}
showFormProgressBarAfterDelay() {
if (this.formProgressBarTimeout == null) {
this.formProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
}
}
hideFormProgressBar() {
this.progressBar.hide();
if (this.formProgressBarTimeout != null) {
window.clearTimeout(this.formProgressBarTimeout);
delete this.formProgressBarTimeout;
}
}
showProgressBar = () => {
this.progressBar.show();
};
reload(reason) {
dispatch("turbo:reload", { detail: reason });
window.location.href = this.location?.toString() || window.location.href;
}
get navigator() {
return this.session.navigator;
}
};
var CacheObserver = class {
selector = "[data-turbo-temporary]";
deprecatedSelector = "[data-turbo-cache=false]";
started = false;
start() {
if (!this.started) {
this.started = true;
addEventListener("turbo:before-cache", this.removeTemporaryElements, false);
}
}
stop() {
if (this.started) {
this.started = false;
removeEventListener("turbo:before-cache", this.removeTemporaryElements, false);
}
}
removeTemporaryElements = (_event) => {
for (const element of this.temporaryElements) {
element.remove();
}
};
get temporaryElements() {
return [...document.querySelectorAll(this.selector), ...this.temporaryElementsWithDeprecation];
}
get temporaryElementsWithDeprecation() {
const elements = document.querySelectorAll(this.deprecatedSelector);
if (elements.length) {
console.warn(
`The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.`
);
}
return [...elements];
}
};
var FrameRedirector = class {
constructor(session2, element) {
this.session = session2;
this.element = element;
this.linkInterceptor = new LinkInterceptor(this, element);
this.formSubmitObserver = new FormSubmitObserver(this, element);
}
start() {
this.linkInterceptor.start();
this.formSubmitObserver.start();
}
stop() {
this.linkInterceptor.stop();
this.formSubmitObserver.stop();
}
// Link interceptor delegate
shouldInterceptLinkClick(element, _location, _event) {
return this.#shouldRedirect(element);
}
linkClickIntercepted(element, url, event) {
const frame = this.#findFrameElement(element);
if (frame) {
frame.delegate.linkClickIntercepted(element, url, event);
}
}
// Form submit observer delegate
willSubmitForm(element, submitter) {
return element.closest("turbo-frame") == null && this.#shouldSubmit(element, submitter) && this.#shouldRedirect(element, submitter);
}
formSubmitted(element, submitter) {
const frame = this.#findFrameElement(element, submitter);
if (frame) {
frame.delegate.formSubmitted(element, submitter);
}
}
#shouldSubmit(form, submitter) {
const action = getAction$1(form, submitter);
const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
const rootLocation = expandURL(meta?.content ?? "/");
return this.#shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
}
#shouldRedirect(element, submitter) {
const isNavigatable = element instanceof HTMLFormElement ? this.session.submissionIsNavigatable(element, submitter) : this.session.elementIsNavigatable(element);
if (isNavigatable) {
const frame = this.#findFrameElement(element, submitter);
return frame ? frame != element.closest("turbo-frame") : false;
} else {
return false;
}
}
#findFrameElement(element, submitter) {
const id = submitter?.getAttribute("data-turbo-frame") || element.getAttribute("data-turbo-frame");
if (id && id != "_top") {
const frame = this.element.querySelector(`#${id}:not([disabled])`);
if (frame instanceof FrameElement) {
return frame;
}
}
}
};
var History = class {
location;
restorationIdentifier = uuid();
restorationData = {};
started = false;
pageLoaded = false;
currentIndex = 0;
constructor(delegate) {
this.delegate = delegate;
}
start() {
if (!this.started) {
addEventListener("popstate", this.onPopState, false);
addEventListener("load", this.onPageLoad, false);
this.currentIndex = history.state?.turbo?.restorationIndex || 0;
this.started = true;
this.replace(new URL(window.location.href));
}
}
stop() {
if (this.started) {
removeEventListener("popstate", this.onPopState, false);
removeEventListener("load", this.onPageLoad, false);
this.started = false;
}
}
push(location2, restorationIdentifier) {
this.update(history.pushState, location2, restorationIdentifier);
}
replace(location2, restorationIdentifier) {
this.update(history.replaceState, location2, restorationIdentifier);
}
update(method, location2, restorationIdentifier = uuid()) {
if (method === history.pushState) ++this.currentIndex;
const state = { turbo: { restorationIdentifier, restorationIndex: this.currentIndex } };
method.call(history, state, "", location2.href);
this.location = location2;
this.restorationIdentifier = restorationIdentifier;
}
// Restoration data
getRestorationDataForIdentifier(restorationIdentifier) {
return this.restorationData[restorationIdentifier] || {};
}
updateRestorationData(additionalData) {
const { restorationIdentifier } = this;
const restorationData = this.restorationData[restorationIdentifier];
this.restorationData[restorationIdentifier] = {
...restorationData,
...additionalData
};
}
// Scroll restoration
assumeControlOfScrollRestoration() {
if (!this.previousScrollRestoration) {
this.previousScrollRestoration = history.scrollRestoration ?? "auto";
history.scrollRestoration = "manual";
}
}
relinquishControlOfScrollRestoration() {
if (this.previousScrollRestoration) {
history.scrollRestoration = this.previousScrollRestoration;
delete this.previousScrollRestoration;
}
}
// Event handlers
onPopState = (event) => {
if (this.shouldHandlePopState()) {
const { turbo } = event.state || {};
if (turbo) {
this.location = new URL(window.location.href);
const { restorationIdentifier, restorationIndex } = turbo;
this.restorationIdentifier = restorationIdentifier;
const direction = restorationIndex > this.currentIndex ? "forward" : "back";
this.delegate.historyPoppedToLocationWithRestorationIdentifierAndDirection(this.location, restorationIdentifier, direction);
this.currentIndex = restorationIndex;
}
}
};
onPageLoad = async (_event) => {
await nextMicrotask();
this.pageLoaded = true;
};
// Private
shouldHandlePopState() {
return this.pageIsLoaded();
}
pageIsLoaded() {
return this.pageLoaded || document.readyState == "complete";
}
};
var LinkPrefetchObserver = class {
started = false;
#prefetchedLink = null;
constructor(delegate, eventTarget) {
this.delegate = delegate;
this.eventTarget = eventTarget;
}
start() {
if (this.started) return;
if (this.eventTarget.readyState === "loading") {
this.eventTarget.addEventListener("DOMContentLoaded", this.#enable, { once: true });
} else {
this.#enable();
}
}
stop() {
if (!this.started) return;
this.eventTarget.removeEventListener("mouseenter", this.#tryToPrefetchRequest, {
capture: true,
passive: true
});
this.eventTarget.removeEventListener("mouseleave", this.#cancelRequestIfObsolete, {
capture: true,
passive: true
});
this.eventTarget.removeEventListener("turbo:before-fetch-request", this.#tryToUsePrefetchedRequest, true);
this.started = false;
}
#enable = () => {
this.eventTarget.addEventListener("mouseenter", this.#tryToPrefetchRequest, {
capture: true,
passive: true
});
this.eventTarget.addEventListener("mouseleave", this.#cancelRequestIfObsolete, {
capture: true,
passive: true
});
this.eventTarget.addEventListener("turbo:before-fetch-request", this.#tryToUsePrefetchedRequest, true);
this.started = true;
};
#tryToPrefetchRequest = (event) => {
if (getMetaContent("turbo-prefetch") === "false") return;
const target = event.target;
const isLink = target.matches && target.matches("a[href]:not([target^=_]):not([download])");
if (isLink && this.#isPrefetchable(target)) {
const link = target;
const location2 = getLocationForLink(link);
if (this.delegate.canPrefetchRequestToLocation(link, location2)) {
this.#prefetchedLink = link;
const fetchRequest = new FetchRequest(
this,
FetchMethod.get,
location2,
new URLSearchParams(),
target
);
prefetchCache.setLater(location2.toString(), fetchRequest, this.#cacheTtl);
}
}
};
#cancelRequestIfObsolete = (event) => {
if (event.target === this.#prefetchedLink) this.#cancelPrefetchRequest();
};
#cancelPrefetchRequest = () => {
prefetchCache.clear();
this.#prefetchedLink = null;
};
#tryToUsePrefetchedRequest = (event) => {
if (event.target.tagName !== "FORM" && event.detail.fetchOptions.method === "GET") {
const cached = prefetchCache.get(event.detail.url.toString());
if (cached) {
event.detail.fetchRequest = cached;
}
prefetchCache.clear();
}
};
prepareRequest(request) {
const link = request.target;
request.headers["X-Sec-Purpose"] = "prefetch";
const turboFrame = link.closest("turbo-frame");
const turboFrameTarget = link.getAttribute("data-turbo-frame") || turboFrame?.getAttribute("target") || turboFrame?.id;
if (turboFrameTarget && turboFrameTarget !== "_top") {
request.headers["Turbo-Frame"] = turboFrameTarget;
}
}
// Fetch request interface
requestSucceededWithResponse() {
}
requestStarted(fetchRequest) {
}
requestErrored(fetchRequest) {
}
requestFinished(fetchRequest) {
}
requestPreventedHandlingResponse(fetchRequest, fetchResponse) {
}
requestFailedWithResponse(fetchRequest, fetchResponse) {
}
get #cacheTtl() {
return Number(getMetaContent("turbo-prefetch-cache-time")) || cacheTtl;
}
#isPrefetchable(link) {
const href = link.getAttribute("href");
if (!href) return false;
if (unfetchableLink(link)) return false;
if (linkToTheSamePage(link)) return false;
if (linkOptsOut(link)) return false;
if (nonSafeLink(link)) return false;
if (eventPrevented(link)) return false;
return true;
}
};
var unfetchableLink = (link) => {
return link.origin !== document.location.origin || !["http:", "https:"].includes(link.protocol) || link.hasAttribute("target");
};
var linkToTheSamePage = (link) => {
return link.pathname + link.search === document.location.pathname + document.location.search || link.href.startsWith("#");
};
var linkOptsOut = (link) => {
if (link.getAttribute("data-turbo-prefetch") === "false") return true;
if (link.getAttribute("data-turbo") === "false") return true;
const turboPrefetchParent = findClosestRecursively(link, "[data-turbo-prefetch]");
if (turboPrefetchParent && turboPrefetchParent.getAttribute("data-turbo-prefetch") === "false") return true;
return false;
};
var nonSafeLink = (link) => {
const turboMethod = link.getAttribute("data-turbo-method");
if (turboMethod && turboMethod.toLowerCase() !== "get") return true;
if (isUJS(link)) return true;
if (link.hasAttribute("data-turbo-confirm")) return true;
if (link.hasAttribute("data-turbo-stream")) return true;
return false;
};
var isUJS = (link) => {
return link.hasAttribute("data-remote") || link.hasAttribute("data-behavior") || link.hasAttribute("data-confirm") || link.hasAttribute("data-method");
};
var eventPrevented = (link) => {
const event = dispatch("turbo:before-prefetch", { target: link, cancelable: true });
return event.defaultPrevented;
};
var Navigator = class {
constructor(delegate) {
this.delegate = delegate;
}
proposeVisit(location2, options = {}) {
if (this.delegate.allowsVisitingLocationWithAction(location2, options.action)) {
this.delegate.visitProposedToLocation(location2, options);
}
}
startVisit(locatable, restorationIdentifier, options = {}) {
this.stop();
this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, {
referrer: this.location,
...options
});
this.currentVisit.start();
}
submitForm(form, submitter) {
this.stop();
this.formSubmission = new FormSubmission(this, form, submitter, true);
this.formSubmission.start();
}
stop() {
if (this.formSubmission) {
this.formSubmission.stop();
delete this.formSubmission;
}
if (this.currentVisit) {
this.currentVisit.cancel();
delete this.currentVisit;
}
}
get adapter() {
return this.delegate.adapter;
}
get view() {
return this.delegate.view;
}
get rootLocation() {
return this.view.snapshot.rootLocation;
}
get history() {
return this.delegate.history;
}
// Form submission delegate
formSubmissionStarted(formSubmission) {
if (typeof this.adapter.formSubmissionStarted === "function") {
this.adapter.formSubmissionStarted(formSubmission);
}
}
async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) {
if (formSubmission == this.formSubmission) {
const responseHTML = await fetchResponse.responseHTML;
if (responseHTML) {
const shouldCacheSnapshot = formSubmission.isSafe;
if (!shouldCacheSnapshot) {
this.view.clearSnapshotCache();
}
const { statusCode, redirected } = fetchResponse;
const action = this.#getActionForFormSubmission(formSubmission, fetchResponse);
const visitOptions = {
action,
shouldCacheSnapshot,
response: { statusCode, responseHTML, redirected }
};
this.proposeVisit(fetchResponse.location, visitOptions);
}
}
}
async formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
const responseHTML = await fetchResponse.responseHTML;
if (responseHTML) {
const snapshot = PageSnapshot.fromHTMLString(responseHTML);
if (fetchResponse.serverError) {
await this.view.renderError(snapshot, this.currentVisit);
} else {
await this.view.renderPage(snapshot, false, true, this.currentVisit);
}
if (!snapshot.shouldPreserveScrollPosition) {
this.view.scrollToTop();
}
this.view.clearSnapshotCache();
}
}
formSubmissionErrored(formSubmission, error2) {
console.error(error2);
}
formSubmissionFinished(formSubmission) {
if (typeof this.adapter.formSubmissionFinished === "function") {
this.adapter.formSubmissionFinished(formSubmission);
}
}
// Visit delegate
visitStarted(visit2) {
this.delegate.visitStarted(visit2);
}
visitCompleted(visit2) {
this.delegate.visitCompleted(visit2);
delete this.currentVisit;
}
locationWithActionIsSamePage(location2, action) {
const anchor = getAnchor(location2);
const currentAnchor = getAnchor(this.view.lastRenderedLocation);
const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
return action !== "replace" && getRequestURL(location2) === getRequestURL(this.view.lastRenderedLocation) && (isRestorationToTop || anchor != null && anchor !== currentAnchor);
}
visitScrolledToSamePageLocation(oldURL, newURL) {
this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
}
// Visits
get location() {
return this.history.location;
}
get restorationIdentifier() {
return this.history.restorationIdentifier;
}
#getActionForFormSubmission(formSubmission, fetchResponse) {
const { submitter, formElement } = formSubmission;
return getVisitAction(submitter, formElement) || this.#getDefaultAction(fetchResponse);
}
#getDefaultAction(fetchResponse) {
const sameLocationRedirect = fetchResponse.redirected && fetchResponse.location.href === this.location?.href;
return sameLocationRedirect ? "replace" : "advance";
}
};
var PageStage = {
initial: 0,
loading: 1,
interactive: 2,
complete: 3
};
var PageObserver = class {
stage = PageStage.initial;
started = false;
constructor(delegate) {
this.delegate = delegate;
}
start() {
if (!this.started) {
if (this.stage == PageStage.initial) {
this.stage = PageStage.loading;
}
document.addEventListener("readystatechange", this.interpretReadyState, false);
addEventListener("pagehide", this.pageWillUnload, false);
this.started = true;
}
}
stop() {
if (this.started) {
document.removeEventListener("readystatechange", this.interpretReadyState, false);
removeEventListener("pagehide", this.pageWillUnload, false);
this.started = false;
}
}
interpretReadyState = () => {
const { readyState } = this;
if (readyState == "interactive") {
this.pageIsInteractive();
} else if (readyState == "complete") {
this.pageIsComplete();
}
};
pageIsInteractive() {
if (this.stage == PageStage.loading) {
this.stage = PageStage.interactive;
this.delegate.pageBecameInteractive();
}
}
pageIsComplete() {
this.pageIsInteractive();
if (this.stage == PageStage.interactive) {
this.stage = PageStage.complete;
this.delegate.pageLoaded();
}
}
pageWillUnload = () => {
this.delegate.pageWillUnload();
};
get readyState() {
return document.readyState;
}
};
var ScrollObserver = class {
started = false;
constructor(delegate) {
this.delegate = delegate;
}
start() {
if (!this.started) {
addEventListener("scroll", this.onScroll, false);
this.onScroll();
this.started = true;
}
}
stop() {
if (this.started) {
removeEventListener("scroll", this.onScroll, false);
this.started = false;
}
}
onScroll = () => {
this.updatePosition({ x: window.pageXOffset, y: window.pageYOffset });
};
// Private
updatePosition(position) {
this.delegate.scrollPositionChanged(position);
}
};
var StreamMessageRenderer = class {
render({ fragment }) {
Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => {
withAutofocusFromFragment(fragment, () => {
withPreservedFocus(() => {
document.documentElement.appendChild(fragment);
});
});
});
}
// Bardo delegate
enteringBardo(currentPermanentElement, newPermanentElement) {
newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
}
leavingBardo() {
}
};
function getPermanentElementMapForFragment(fragment) {
const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement);
const permanentElementMap = {};
for (const permanentElementInDocument of permanentElementsInDocument) {
const { id } = permanentElementInDocument;
for (const streamElement of fragment.querySelectorAll("turbo-stream")) {
const elementInStream = getPermanentElementById(streamElement.templateElement.content, id);
if (elementInStream) {
permanentElementMap[id] = [permanentElementInDocument, elementInStream];
}
}
}
return permanentElementMap;
}
async function withAutofocusFromFragment(fragment, callback) {
const generatedID = `turbo-stream-autofocus-${uuid()}`;
const turboStreams = fragment.querySelectorAll("turbo-stream");
const elementWithAutofocus = firstAutofocusableElementInStreams(turboStreams);
let willAutofocusId = null;
if (elementWithAutofocus) {
if (elementWithAutofocus.id) {
willAutofocusId = elementWithAutofocus.id;
} else {
willAutofocusId = generatedID;
}
elementWithAutofocus.id = willAutofocusId;
}
callback();
await nextRepaint();
const hasNoActiveElement = document.activeElement == null || document.activeElement == document.body;
if (hasNoActiveElement && willAutofocusId) {
const elementToAutofocus = document.getElementById(willAutofocusId);
if (elementIsFocusable(elementToAutofocus)) {
elementToAutofocus.focus();
}
if (elementToAutofocus && elementToAutofocus.id == generatedID) {
elementToAutofocus.removeAttribute("id");
}
}
}
async function withPreservedFocus(callback) {
const [activeElementBeforeRender, activeElementAfterRender] = await around(callback, () => document.activeElement);
const restoreFocusTo = activeElementBeforeRender && activeElementBeforeRender.id;
if (restoreFocusTo) {
const elementToFocus = document.getElementById(restoreFocusTo);
if (elementIsFocusable(elementToFocus) && elementToFocus != activeElementAfterRender) {
elementToFocus.focus();
}
}
}
function firstAutofocusableElementInStreams(nodeListOfStreamElements) {
for (const streamElement of nodeListOfStreamElements) {
const elementWithAutofocus = queryAutofocusableElement(streamElement.templateElement.content);
if (elementWithAutofocus) return elementWithAutofocus;
}
return null;
}
var StreamObserver = class {
sources = /* @__PURE__ */ new Set();
#started = false;
constructor(delegate) {
this.delegate = delegate;
}
start() {
if (!this.#started) {
this.#started = true;
addEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
}
}
stop() {
if (this.#started) {
this.#started = false;
removeEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
}
}
connectStreamSource(source) {
if (!this.streamSourceIsConnected(source)) {
this.sources.add(source);
source.addEventListener("message", this.receiveMessageEvent, false);
}
}
disconnectStreamSource(source) {
if (this.streamSourceIsConnected(source)) {
this.sources.delete(source);
source.removeEventListener("message", this.receiveMessageEvent, false);
}
}
streamSourceIsConnected(source) {
return this.sources.has(source);
}
inspectFetchResponse = (event) => {
const response = fetchResponseFromEvent(event);
if (response && fetchResponseIsStream(response)) {
event.preventDefault();
this.receiveMessageResponse(response);
}
};
receiveMessageEvent = (event) => {
if (this.#started && typeof event.data == "string") {
this.receiveMessageHTML(event.data);
}
};
async receiveMessageResponse(response) {
const html = await response.responseHTML;
if (html) {
this.receiveMessageHTML(html);
}
}
receiveMessageHTML(html) {
this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
}
};
function fetchResponseFromEvent(event) {
const fetchResponse = event.detail?.fetchResponse;
if (fetchResponse instanceof FetchResponse) {
return fetchResponse;
}
}
function fetchResponseIsStream(response) {
const contentType = response.contentType ?? "";
return contentType.startsWith(StreamMessage.contentType);
}
var ErrorRenderer = class extends Renderer {
static renderElement(currentElement, newElement) {
const { documentElement, body } = document;
documentElement.replaceChild(newElement, body);
}
async render() {
this.replaceHeadAndBody();
this.activateScriptElements();
}
replaceHeadAndBody() {
const { documentElement, head } = document;
documentElement.replaceChild(this.newHead, head);
this.renderElement(this.currentElement, this.newElement);
}
activateScriptElements() {
for (const replaceableElement of this.scriptElements) {
const parentNode = replaceableElement.parentNode;
if (parentNode) {
const element = activateScriptElement(replaceableElement);
parentNode.replaceChild(element, replaceableElement);
}
}
}
get newHead() {
return this.newSnapshot.headSnapshot.element;
}
get scriptElements() {
return document.documentElement.querySelectorAll("script");
}
};
var Idiomorph = /* @__PURE__ */ function() {
let EMPTY_SET = /* @__PURE__ */ new Set();
let defaults = {
morphStyle: "outerHTML",
callbacks: {
beforeNodeAdded: noOp,
afterNodeAdded: noOp,
beforeNodeMorphed: noOp,
afterNodeMorphed: noOp,
beforeNodeRemoved: noOp,
afterNodeRemoved: noOp,
beforeAttributeUpdated: noOp
},
head: {
style: "merge",
shouldPreserve: function(elt) {
return elt.getAttribute("im-preserve") === "true";
},
shouldReAppend: function(elt) {
return elt.getAttribute("im-re-append") === "true";
},
shouldRemove: noOp,
afterHeadMorphed: noOp
}
};
function morph(oldNode, newContent, config = {}) {
if (oldNode instanceof Document) {
oldNode = oldNode.documentElement;
}
if (typeof newContent === "string") {
newContent = parseContent(newContent);
}
let normalizedContent = normalizeContent(newContent);
let ctx = createMorphContext(oldNode, normalizedContent, config);
return morphNormalizedContent(oldNode, normalizedContent, ctx);
}
function morphNormalizedContent(oldNode, normalizedNewContent, ctx) {
if (ctx.head.block) {
let oldHead = oldNode.querySelector("head");
let newHead = normalizedNewContent.querySelector("head");
if (oldHead && newHead) {
let promises = handleHeadElement(newHead, oldHead, ctx);
Promise.all(promises).then(function() {
morphNormalizedContent(oldNode, normalizedNewContent, Object.assign(ctx, {
head: {
block: false,
ignore: true
}
}));
});
return;
}
}
if (ctx.morphStyle === "innerHTML") {
morphChildren2(normalizedNewContent, oldNode, ctx);
return oldNode.children;
} else if (ctx.morphStyle === "outerHTML" || ctx.morphStyle == null) {
let bestMatch = findBestNodeMatch(normalizedNewContent, oldNode, ctx);
let previousSibling = bestMatch?.previousSibling;
let nextSibling = bestMatch?.nextSibling;
let morphedNode = morphOldNodeTo(oldNode, bestMatch, ctx);
if (bestMatch) {
return insertSiblings(previousSibling, morphedNode, nextSibling);
} else {
return [];
}
} else {
throw "Do not understand how to morph style " + ctx.morphStyle;
}
}
function ignoreValueOfActiveElement(possibleActiveElement, ctx) {
return ctx.ignoreActiveValue && possibleActiveElement === document.activeElement && possibleActiveElement !== document.body;
}
function morphOldNodeTo(oldNode, newContent, ctx) {
if (ctx.ignoreActive && oldNode === document.activeElement) ;
else if (newContent == null) {
if (ctx.callbacks.beforeNodeRemoved(oldNode) === false) return oldNode;
oldNode.remove();
ctx.callbacks.afterNodeRemoved(oldNode);
return null;
} else if (!isSoftMatch(oldNode, newContent)) {
if (ctx.callbacks.beforeNodeRemoved(oldNode) === false) return oldNode;
if (ctx.callbacks.beforeNodeAdded(newContent) === false) return oldNode;
oldNode.parentElement.replaceChild(newContent, oldNode);
ctx.callbacks.afterNodeAdded(newContent);
ctx.callbacks.afterNodeRemoved(oldNode);
return newContent;
} else {
if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false) return oldNode;
if (oldNode instanceof HTMLHeadElement && ctx.head.ignore) ;
else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") {
handleHeadElement(newContent, oldNode, ctx);
} else {
syncNodeFrom(newContent, oldNode, ctx);
if (!ignoreValueOfActiveElement(oldNode, ctx)) {
morphChildren2(newContent, oldNode, ctx);
}
}
ctx.callbacks.afterNodeMorphed(oldNode, newContent);
return oldNode;
}
}
function morphChildren2(newParent, oldParent, ctx) {
let nextNewChild = newParent.firstChild;
let insertionPoint = oldParent.firstChild;
let newChild;
while (nextNewChild) {
newChild = nextNewChild;
nextNewChild = newChild.nextSibling;
if (insertionPoint == null) {
if (ctx.callbacks.beforeNodeAdded(newChild) === false) return;
oldParent.appendChild(newChild);
ctx.callbacks.afterNodeAdded(newChild);
removeIdsFromConsideration(ctx, newChild);
continue;
}
if (isIdSetMatch(newChild, insertionPoint, ctx)) {
morphOldNodeTo(insertionPoint, newChild, ctx);
insertionPoint = insertionPoint.nextSibling;
removeIdsFromConsideration(ctx, newChild);
continue;
}
let idSetMatch = findIdSetMatch(newParent, oldParent, newChild, insertionPoint, ctx);
if (idSetMatch) {
insertionPoint = removeNodesBetween(insertionPoint, idSetMatch, ctx);
morphOldNodeTo(idSetMatch, newChild, ctx);
removeIdsFromConsideration(ctx, newChild);
continue;
}
let softMatch = findSoftMatch(newParent, oldParent, newChild, insertionPoint, ctx);
if (softMatch) {
insertionPoint = removeNodesBetween(insertionPoint, softMatch, ctx);
morphOldNodeTo(softMatch, newChild, ctx);
removeIdsFromConsideration(ctx, newChild);
continue;
}
if (ctx.callbacks.beforeNodeAdded(newChild) === false) return;
oldParent.insertBefore(newChild, insertionPoint);
ctx.callbacks.afterNodeAdded(newChild);
removeIdsFromConsideration(ctx, newChild);
}
while (insertionPoint !== null) {
let tempNode = insertionPoint;
insertionPoint = insertionPoint.nextSibling;
removeNode(tempNode, ctx);
}
}
function ignoreAttribute(attr, to, updateType, ctx) {
if (attr === "value" && ctx.ignoreActiveValue && to === document.activeElement) {
return true;
}
return ctx.callbacks.beforeAttributeUpdated(attr, to, updateType) === false;
}
function syncNodeFrom(from, to, ctx) {
let type = from.nodeType;
if (type === 1) {
const fromAttributes = from.attributes;
const toAttributes = to.attributes;
for (const fromAttribute of fromAttributes) {
if (ignoreAttribute(fromAttribute.name, to, "update", ctx)) {
continue;
}
if (to.getAttribute(fromAttribute.name) !== fromAttribute.value) {
to.setAttribute(fromAttribute.name, fromAttribute.value);
}
}
for (let i = toAttributes.length - 1; 0 <= i; i--) {
const toAttribute = toAttributes[i];
if (ignoreAttribute(toAttribute.name, to, "remove", ctx)) {
continue;
}
if (!from.hasAttribute(toAttribute.name)) {
to.removeAttribute(toAttribute.name);
}
}
}
if (type === 8 || type === 3) {
if (to.nodeValue !== from.nodeValue) {
to.nodeValue = from.nodeValue;
}
}
if (!ignoreValueOfActiveElement(to, ctx)) {
syncInputValue(from, to, ctx);
}
}
function syncBooleanAttribute(from, to, attributeName, ctx) {
if (from[attributeName] !== to[attributeName]) {
let ignoreUpdate = ignoreAttribute(attributeName, to, "update", ctx);
if (!ignoreUpdate) {
to[attributeName] = from[attributeName];
}
if (from[attributeName]) {
if (!ignoreUpdate) {
to.setAttribute(attributeName, from[attributeName]);
}
} else {
if (!ignoreAttribute(attributeName, to, "remove", ctx)) {
to.removeAttribute(attributeName);
}
}
}
}
function syncInputValue(from, to, ctx) {
if (from instanceof HTMLInputElement && to instanceof HTMLInputElement && from.type !== "file") {
let fromValue = from.value;
let toValue = to.value;
syncBooleanAttribute(from, to, "checked", ctx);
syncBooleanAttribute(from, to, "disabled", ctx);
if (!from.hasAttribute("value")) {
if (!ignoreAttribute("value", to, "remove", ctx)) {
to.value = "";
to.removeAttribute("value");
}
} else if (fromValue !== toValue) {
if (!ignoreAttribute("value", to, "update", ctx)) {
to.setAttribute("value", fromValue);
to.value = fromValue;
}
}
} else if (from instanceof HTMLOptionElement) {
syncBooleanAttribute(from, to, "selected", ctx);
} else if (from instanceof HTMLTextAreaElement && to instanceof HTMLTextAreaElement) {
let fromValue = from.value;
let toValue = to.value;
if (ignoreAttribute("value", to, "update", ctx)) {
return;
}
if (fromValue !== toValue) {
to.value = fromValue;
}
if (to.firstChild && to.firstChild.nodeValue !== fromValue) {
to.firstChild.nodeValue = fromValue;
}
}
}
function handleHeadElement(newHeadTag, currentHead, ctx) {
let added = [];
let removed = [];
let preserved = [];
let nodesToAppend = [];
let headMergeStyle = ctx.head.style;
let srcToNewHeadNodes = /* @__PURE__ */ new Map();
for (const newHeadChild of newHeadTag.children) {
srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild);
}
for (const currentHeadElt of currentHead.children) {
let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML);
let isReAppended = ctx.head.shouldReAppend(currentHeadElt);
let isPreserved = ctx.head.shouldPreserve(currentHeadElt);
if (inNewContent || isPreserved) {
if (isReAppended) {
removed.push(currentHeadElt);
} else {
srcToNewHeadNodes.delete(currentHeadElt.outerHTML);
preserved.push(currentHeadElt);
}
} else {
if (headMergeStyle === "append") {
if (isReAppended) {
removed.push(currentHeadElt);
nodesToAppend.push(currentHeadElt);
}
} else {
if (ctx.head.shouldRemove(currentHeadElt) !== false) {
removed.push(currentHeadElt);
}
}
}
}
nodesToAppend.push(...srcToNewHeadNodes.values());
let promises = [];
for (const newNode of nodesToAppend) {
let newElt = document.createRange().createContextualFragment(newNode.outerHTML).firstChild;
if (ctx.callbacks.beforeNodeAdded(newElt) !== false) {
if (newElt.href || newElt.src) {
let resolve = null;
let promise = new Promise(function(_resolve) {
resolve = _resolve;
});
newElt.addEventListener("load", function() {
resolve();
});
promises.push(promise);
}
currentHead.appendChild(newElt);
ctx.callbacks.afterNodeAdded(newElt);
added.push(newElt);
}
}
for (const removedElement of removed) {
if (ctx.callbacks.beforeNodeRemoved(removedElement) !== false) {
currentHead.removeChild(removedElement);
ctx.callbacks.afterNodeRemoved(removedElement);
}
}
ctx.head.afterHeadMorphed(currentHead, { added, kept: preserved, removed });
return promises;
}
function noOp() {
}
function mergeDefaults(config) {
let finalConfig = {};
Object.assign(finalConfig, defaults);
Object.assign(finalConfig, config);
finalConfig.callbacks = {};
Object.assign(finalConfig.callbacks, defaults.callbacks);
Object.assign(finalConfig.callbacks, config.callbacks);
finalConfig.head = {};
Object.assign(finalConfig.head, defaults.head);
Object.assign(finalConfig.head, config.head);
return finalConfig;
}
function createMorphContext(oldNode, newContent, config) {
config = mergeDefaults(config);
return {
target: oldNode,
newContent,
config,
morphStyle: config.morphStyle,
ignoreActive: config.ignoreActive,
ignoreActiveValue: config.ignoreActiveValue,
idMap: createIdMap(oldNode, newContent),
deadIds: /* @__PURE__ */ new Set(),
callbacks: config.callbacks,
head: config.head
};
}
function isIdSetMatch(node1, node2, ctx) {
if (node1 == null || node2 == null) {
return false;
}
if (node1.nodeType === node2.nodeType && node1.tagName === node2.tagName) {
if (node1.id !== "" && node1.id === node2.id) {
return true;
} else {
return getIdIntersectionCount(ctx, node1, node2) > 0;
}
}
return false;
}
function isSoftMatch(node1, node2) {
if (node1 == null || node2 == null) {
return false;
}
return node1.nodeType === node2.nodeType && node1.tagName === node2.tagName;
}
function removeNodesBetween(startInclusive, endExclusive, ctx) {
while (startInclusive !== endExclusive) {
let tempNode = startInclusive;
startInclusive = startInclusive.nextSibling;
removeNode(tempNode, ctx);
}
removeIdsFromConsideration(ctx, endExclusive);
return endExclusive.nextSibling;
}
function findIdSetMatch(newContent, oldParent, newChild, insertionPoint, ctx) {
let newChildPotentialIdCount = getIdIntersectionCount(ctx, newChild, oldParent);
let potentialMatch = null;
if (newChildPotentialIdCount > 0) {
let potentialMatch2 = insertionPoint;
let otherMatchCount = 0;
while (potentialMatch2 != null) {
if (isIdSetMatch(newChild, potentialMatch2, ctx)) {
return potentialMatch2;
}
otherMatchCount += getIdIntersectionCount(ctx, potentialMatch2, newContent);
if (otherMatchCount > newChildPotentialIdCount) {
return null;
}
potentialMatch2 = potentialMatch2.nextSibling;
}
}
return potentialMatch;
}
function findSoftMatch(newContent, oldParent, newChild, insertionPoint, ctx) {
let potentialSoftMatch = insertionPoint;
let nextSibling = newChild.nextSibling;
let siblingSoftMatchCount = 0;
while (potentialSoftMatch != null) {
if (getIdIntersectionCount(ctx, potentialSoftMatch, newContent) > 0) {
return null;
}
if (isSoftMatch(newChild, potentialSoftMatch)) {
return potentialSoftMatch;
}
if (isSoftMatch(nextSibling, potentialSoftMatch)) {
siblingSoftMatchCount++;
nextSibling = nextSibling.nextSibling;
if (siblingSoftMatchCount >= 2) {
return null;
}
}
potentialSoftMatch = potentialSoftMatch.nextSibling;
}
return potentialSoftMatch;
}
function parseContent(newContent) {
let parser = new DOMParser();
let contentWithSvgsRemoved = newContent.replace(/