[Rt-commit] rt branch 6.0/turbo created. rt-5.0.3-510-gdf5f224ce8
BPS Git Server
git at git.bestpractical.com
Mon May 1 20:56:49 UTC 2023
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt".
The branch, 6.0/turbo has been created
at df5f224ce84572cfe55823bdebcb79aff9f1844d (commit)
- Log -----------------------------------------------------------------
commit df5f224ce84572cfe55823bdebcb79aff9f1844d
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Mon May 1 16:42:50 2023 -0400
Return 303 on POST requests to work with Turbo
Turbo expects to get a 303 back on POST requests:
https://turbo.hotwired.dev/handbook/drive#redirecting-after-a-form-submission
Update the standard handling of POST to implement this
for all RT forms.
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 3725041c4b..dd457ae98e 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -314,6 +314,15 @@ sub HandleRequest {
ValidateWebConfig();
+ # Load before ARGS processing because we might pull some ARGS
+ # from the session.
+ $HTML::Mason::Commands::m->comp( '/Elements/SetupSessionCookie', %$ARGS );
+ SendSessionCookie();
+
+ # Respond to POST requests with a 303, or load ARGS
+ # from a previous request.
+ $ARGS = ProcessPostRedirectGet($ARGS);
+
DecodeARGS($ARGS);
local $HTML::Mason::Commands::DECODED_ARGS = $ARGS;
PreprocessTimeUpdates($ARGS);
@@ -342,9 +351,6 @@ sub HandleRequest {
MaybeRebuildCustomRolesCache();
RT->System->MaybeRebuildLifecycleCache();
- $HTML::Mason::Commands::m->comp( '/Elements/SetupSessionCookie', %$ARGS );
- SendSessionCookie();
-
if ( _UserLoggedIn() ) {
# make user info up to date
$HTML::Mason::Commands::session{'CurrentUser'}
@@ -735,6 +741,45 @@ sub InitializeMenu {
}
+sub ProcessPostRedirectGet {
+ my $ARGS = shift;
+
+ # Respond immediately to a POST with a 303. This helps avoid
+ # double-submits on forms. See:
+ # https://en.wikipedia.org/wiki/Post/Redirect/Get
+ # It's also required for Turbo-submitted forms.
+
+ if ( RequestENV('REQUEST_METHOD') eq 'POST' ) {
+ # Stash the submitted args in the session
+ my $post_args_ref = $HTML::Mason::Commands::m->request_args();
+
+ my $key = Digest::MD5::md5_hex( rand(1024) );
+ RT::Interface::Web::Session::Set(
+ Key => 'POST_ARGSRef',
+ SubKey => $key,
+ Value => $post_args_ref,
+ );
+
+ HTML::Mason::Commands::MaybeRedirectForResults( Force => 1, Key => $key );
+ }
+
+ if ( RequestENV('REQUEST_METHOD') eq 'GET'
+ && $ARGS->{'results'}
+ && $HTML::Mason::Commands::session{'POST_ARGSRef'}{$ARGS->{'results'}} ) {
+ # This is a request after a POST 303.
+ # Reload %ARGS from the session so they can be processed.
+ my %ARGS = %{$HTML::Mason::Commands::session{'POST_ARGSRef'}{$ARGS->{'results'}}};
+
+ RT::Interface::Web::Session::Delete(
+ Key => 'POST_ARGSRef',
+ SubKey => $ARGS{'results'},
+ );
+
+ return \%ARGS;
+ }
+
+ return $ARGS;
+}
=head2 ShowRequestedPage \%ARGS
@@ -2361,6 +2406,7 @@ sub MaybeRedirectForResults {
Anchor => undef,
Actions => undef,
Force => 0,
+ Key => undef,
@_
);
my $has_actions = $args{'Actions'} && grep( defined, @{ $args{'Actions'} } );
@@ -2369,7 +2415,7 @@ sub MaybeRedirectForResults {
my %arguments = %{ $args{'Arguments'} };
if ( $has_actions ) {
- my $key = Digest::MD5::md5_hex( rand(1024) );
+ my $key = $args{'Key'} // Digest::MD5::md5_hex( rand(1024) );
my $actions_ref = [];
if ( $session{"Actions"}{ $key } ) {
$actions_ref = $session{"Actions"}{ $key };
@@ -2384,6 +2430,9 @@ sub MaybeRedirectForResults {
$arguments{'results'} = $key;
}
+ elsif ( $args{'Key'} ) {
+ $arguments{'results'} = $args{'Key'};
+ }
$args{'Path'} =~ s!^/+!!;
my $url = RT->Config->Get('WebURL') . $args{Path};
commit 09683383e0746e7c16904276d1a1a953036bf2bf
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Mon May 1 13:04:34 2023 -0400
Refresh dropdowns after turbo loads them
diff --git a/share/html/Elements/HeaderJavascript b/share/html/Elements/HeaderJavascript
index 23fbc61ce0..7d28258280 100644
--- a/share/html/Elements/HeaderJavascript
+++ b/share/html/Elements/HeaderJavascript
@@ -54,6 +54,7 @@ $onload => undef
<script type="text/javascript" src="<%RT->Config->Get('WebPath')%><% $jsfile %>"></script>
% }
<script type="module" src="/static/js/turbo.es2017-esm.min.js"></script>
+<script type="text/javascript" src="/static/js/rt-turbo.js"></script>
<script type="text/javascript"><!--
% if ( $focus ) {
diff --git a/share/static/js/rt-turbo.js b/share/static/js/rt-turbo.js
new file mode 100644
index 0000000000..ab02785573
--- /dev/null
+++ b/share/static/js/rt-turbo.js
@@ -0,0 +1,10 @@
+jQuery(function() {
+ jQuery(document).on('turbo:render', function(e) {
+ // Refresh any dropdowns after they are loaded by turbo
+ jQuery('.selectpicker').selectpicker('refresh');
+ });
+ jQuery(document).on('turbo:frame-render', function(e) {
+ // Refresh any dropdowns after they are loaded by turbo
+ jQuery('.selectpicker').selectpicker('refresh');
+ });
+});
commit 693af426264d5cfc2e5d6a13daebf84b46267668
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri Apr 28 14:43:19 2023 -0400
Add turbo frames to saved searches and components on MyRT
diff --git a/lib/RT/Dashboard.pm b/lib/RT/Dashboard.pm
index a2ca8782fe..734d37cf17 100644
--- a/lib/RT/Dashboard.pm
+++ b/lib/RT/Dashboard.pm
@@ -223,7 +223,17 @@ sub ShowSearchName {
return Name => $portlet->{description};
}
- return SavedSearch => join('-', $portlet->{privacy}, 'SavedSearch', $portlet->{id});
+ return SavedSearch => GetSavedSearchName(Privacy => $portlet->{privacy}, ObjectId => $portlet->{id});
+}
+
+sub GetSavedSearchName {
+ my %args = (
+ Privacy => '',
+ ObjectId => '',
+ @_,
+ );
+
+ return join('-', $args{Privacy}, 'SavedSearch', $args{ObjectId});
}
=head2 PossibleHiddenSearches
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 4cdfef0bc6..3725041c4b 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -446,7 +446,10 @@ sub HandleRequest {
# Process per-page final cleanup callbacks
$HTML::Mason::Commands::m->callback( %$ARGS, CallbackName => 'Final', CallbackPage => '/autohandler' );
- $HTML::Mason::Commands::m->comp( '/Elements/Footer', %$ARGS );
+ # Don't show the footer for Turbo components
+ if ( $HTML::Mason::Commands::m->request_path !~ /^\/Views/ ) {
+ $HTML::Mason::Commands::m->comp( '/Elements/Footer', %$ARGS );
+ }
}
sub _ForceLogout {
diff --git a/share/html/Elements/Login b/share/html/Elements/Login
index e2fe6cfdc6..dc5dbf2c30 100644
--- a/share/html/Elements/Login
+++ b/share/html/Elements/Login
@@ -61,7 +61,7 @@
% $m->callback( %ARGS, CallbackName => 'BeforeForm' );
<div id="login-box">
-<&| /Widgets/TitleBox, title => loc('Login'), titleright => $RT::VERSION, hideable => 0 &>
+<&| /Widgets/TitleBox, title => loc('Login'), titleright => $RT::VERSION, hideable => 0, turbo_frame => 0 &>
<& LoginRedirectWarning, %ARGS &>
diff --git a/share/html/Elements/MyRT b/share/html/Elements/MyRT
index ffb6b77144..8e4d3a1177 100644
--- a/share/html/Elements/MyRT
+++ b/share/html/Elements/MyRT
@@ -129,10 +129,12 @@ $show_cb = sub {
}
}
else {
+ # Add turbo tags for components
+ $m->notes->{ComponentForTitle} = $name;
$m->comp( $name, %{ $entry->{arguments} || {} } );
}
} elsif ( $type eq 'search' ) {
- $m->comp( '/Elements/ShowSearch', RT::Dashboard->ShowSearchName($entry), Override => { Rows => $Rows } );
+ $m->comp( '/Elements/ShowSearch', RT::Dashboard->ShowSearchName($entry), TurboFrame => 1, Override => { Rows => $Rows } );
} elsif ( $type eq 'dashboard' ) {
my $current_dashboard = RT::Dashboard->new($session{CurrentUser});
my ($ok, $msg) = $current_dashboard->LoadById($entry->{id});
diff --git a/share/html/Elements/ShowSearch b/share/html/Elements/ShowSearch
index 04349c796a..8169e1f332 100644
--- a/share/html/Elements/ShowSearch
+++ b/share/html/Elements/ShowSearch
@@ -54,7 +54,10 @@
titleright_raw => $customize ? qq[<span class="fas fa-cog icon-bordered fa-2x" alt="$alt" data-toggle="tooltip" data-placement="top" data-original-title="$alt"></span>] : '',
titleright_href => $customize,
hideable => $hideable,
- class => 'fullwidth' &>
+ class => 'fullwidth',
+ turbo_id => 'savedsearch-' . $search_turbo_name,
+ turbo_src => '/Views/Component/SavedSearch?SavedSearch=' . $search_turbo_name . $turbo_query_args,
+ turbo_frame => $TurboFrame &>
<& $query_display_component, hideable => $hideable, %$ProcessedSearchArg, ShowNavigation => 0, Class => $class, HasResults => $HasResults, PreferOrderBy => 1 &>
</&>
<%init>
@@ -230,6 +233,18 @@ if ($ShowCount) {
$ProcessedSearchArg->{Collection} = $collection;
$ProcessedSearchArg->{TotalFound} = $count;
}
+
+# Generate the turbo name from the privacy setting for this search for the turbo URL
+my $search_turbo_name = RT::Dashboard::GetSavedSearchName(
+ Privacy => RT::SharedSetting->_build_privacy( $search->ObjectType, $search->ObjectId ),
+ ObjectId => $search->Id,
+ );
+
+my $turbo_query_args = '';
+if ( exists $Override{'Rows'} ) {
+ $turbo_query_args = "&Rows=" . $Override{'Rows'};
+}
+
</%init>
<%ARGS>
$Name => undef
@@ -240,4 +255,5 @@ $hideable => 1
$ShowCustomize => 1
$ShowCount => RT->Config->Get('ShowSearchResultCount')
$HasResults => undef
+$TurboFrame => 0 # Pass 1 to render just the titlebox with turbo frame
</%ARGS>
diff --git a/share/html/Widgets/TitleBox b/share/html/Views/Component/dhandler
similarity index 76%
copy from share/html/Widgets/TitleBox
copy to share/html/Views/Component/dhandler
index 042ca49604..afb783fc23 100644
--- a/share/html/Widgets/TitleBox
+++ b/share/html/Views/Component/dhandler
@@ -45,21 +45,28 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<div class="<% $class %>">
-% if ($hide_chrome) {
- <% $content | n %>
+% if ( $component_name eq 'SavedSearch' ) {
+<turbo-frame id="savedsearch-<%lc($ARGS{SavedSearch})%>">
+% $m->comp( "/Elements/ShowSearch", %ARGS, );
% } else {
- <& TitleBoxStart, %ARGS &><% $content | n %><& TitleBoxEnd &>
+<turbo-frame id="component-<%$component_name%>">
+% $m->comp( "/Elements/$component_name" );
% }
-</div>
-<%ARGS>
-$class => ''
-$hide_empty => 0
-$hide_chrome => 0
-</%ARGS>
-<%INIT>
-my $content = $m->content;
-$m->callback( CallbackName => "ModifyContent", ARGSRef => \%ARGS, Content => \$content,
- Class => \$class, HideEmpty => \$hide_empty, HideChrome => \$hide_chrome );
-return if $hide_empty && $content =~ /^\s*$/s;
-</%INIT>
+</turbo-frame>
+
+<%init>
+my ($component_name) = $m->dhandler_arg;
+
+if ( $component_name eq 'SavedSearch' ) {
+ # Put Override args in the correct structure
+ $ARGS{Override} = {};
+ foreach my $override ( qw(Rows) ) {
+ if ( $ARGS{$override} ) {
+ $ARGS{Override}->{$override} = $ARGS{$override};
+ delete $ARGS{$override};
+ }
+ }
+}
+</%init>
+<%args>
+</%args>
diff --git a/share/html/Widgets/TitleBox b/share/html/Widgets/TitleBox
index 042ca49604..e000d08ea2 100644
--- a/share/html/Widgets/TitleBox
+++ b/share/html/Widgets/TitleBox
@@ -45,20 +45,75 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
+
+% if ( not $turbo_frame ) {
<div class="<% $class %>">
-% if ($hide_chrome) {
- <% $content | n %>
-% } else {
- <& TitleBoxStart, %ARGS &><% $content | n %><& TitleBoxEnd &>
% }
+
+% if ( not $hide_chrome ) {
+ <& TitleBoxStart, %ARGS &>
+% }
+
+% if ( $turbo_frame ) {
+ <& TurboFrameStart, turbo_id => $turbo_id, turbo_src => $turbo_src &>
+% }
+
+<% $content | n %>
+
+% if ( $turbo_frame ) {
+ <& TurboFrameEnd &>
+% }
+
+% if ( not $hide_chrome ) {
+ <& TitleBoxEnd &>
+% }
+
+% if ( not $turbo_frame ) {
</div>
+% }
+
<%ARGS>
$class => ''
$hide_empty => 0
$hide_chrome => 0
+$turbo_frame => 0
+$turbo_id => undef
+$turbo_src => undef
</%ARGS>
<%INIT>
-my $content = $m->content;
+
+# With $turbo_frame => 1, TitleBox builds the box and the title on the
+# initial load with a turbo frame inside and no content. Turbo will then
+# issue another request to load the body.
+
+# Pass $turbo_frame => 0 to build an entire component on the initial
+# render like pre-turbo RT.
+
+my $content;
+
+if ( $m->request_path =~ /^\/Views/ ) {
+ # Turbo refresh, send content with no titlebox
+ $m->out( $m->content );
+ return;
+}
+elsif ( $m->notes('ComponentForTitle') ) {
+ # Request for a mason component, set up turbo tags
+ my $name = $m->notes('ComponentForTitle');
+ $turbo_frame = 1;
+ $turbo_id = 'component-' . lc($name);
+ $turbo_src = '/Views/Component/' . $name;
+ $m->notes->{'ComponentForTitle'} = 0;
+}
+elsif ( $m->request_path =~ /^\/index\.html$/ ) {
+ # Homepage load that isn't a search or a component
+ $content = '';
+}
+
+# Regular request, show the content
+unless ( length $content ) {
+ $content = $m->content;
+}
+
$m->callback( CallbackName => "ModifyContent", ARGSRef => \%ARGS, Content => \$content,
Class => \$class, HideEmpty => \$hide_empty, HideChrome => \$hide_chrome );
return if $hide_empty && $content =~ /^\s*$/s;
diff --git a/share/html/Widgets/TitleBox b/share/html/Widgets/TurboFrameEnd
similarity index 81%
copy from share/html/Widgets/TitleBox
copy to share/html/Widgets/TurboFrameEnd
index 042ca49604..31f8e483a9 100644
--- a/share/html/Widgets/TitleBox
+++ b/share/html/Widgets/TurboFrameEnd
@@ -45,21 +45,6 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<div class="<% $class %>">
-% if ($hide_chrome) {
- <% $content | n %>
-% } else {
- <& TitleBoxStart, %ARGS &><% $content | n %><& TitleBoxEnd &>
-% }
-</div>
+</turbo-frame>
<%ARGS>
-$class => ''
-$hide_empty => 0
-$hide_chrome => 0
</%ARGS>
-<%INIT>
-my $content = $m->content;
-$m->callback( CallbackName => "ModifyContent", ARGSRef => \%ARGS, Content => \$content,
- Class => \$class, HideEmpty => \$hide_empty, HideChrome => \$hide_chrome );
-return if $hide_empty && $content =~ /^\s*$/s;
-</%INIT>
diff --git a/share/html/Widgets/TitleBox b/share/html/Widgets/TurboFrameStart
similarity index 77%
copy from share/html/Widgets/TitleBox
copy to share/html/Widgets/TurboFrameStart
index 042ca49604..39d8063018 100644
--- a/share/html/Widgets/TitleBox
+++ b/share/html/Widgets/TurboFrameStart
@@ -45,21 +45,18 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<div class="<% $class %>">
-% if ($hide_chrome) {
- <% $content | n %>
-% } else {
- <& TitleBoxStart, %ARGS &><% $content | n %><& TitleBoxEnd &>
+<turbo-frame id="<% $turbo_id %>" src="<% $turbo_src %>" <% $lazy_load ? 'loading=lazy ' : '' %>target="_top">
+% if ( $show_spinner ) {
+<div class="text-center"><div class="spinner-border text-secondary" role="status"><span class="sr-only">Loading...</span></div></div>
% }
-</div>
<%ARGS>
-$class => ''
-$hide_empty => 0
-$hide_chrome => 0
+$turbo_id => undef
+$turbo_src => undef
+$show_spinner => 1
+$lazy_load => 1
</%ARGS>
-<%INIT>
-my $content = $m->content;
-$m->callback( CallbackName => "ModifyContent", ARGSRef => \%ARGS, Content => \$content,
- Class => \$class, HideEmpty => \$hide_empty, HideChrome => \$hide_chrome );
-return if $hide_empty && $content =~ /^\s*$/s;
-</%INIT>
+
+<%init>
+RT->Logger->error("No turbo_id provided. Turbo needs an id to find the correct frame to replace.") unless $turbo_id;
+RT->Logger->error("No turbo_src provided. Turbo needs a src path to issue the request for content.") unless $turbo_src;
+</%init>
commit 303c3a0c3dcb7494926281676fba608ab3537b9b
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Mon Apr 24 09:30:05 2023 -0400
Remove backward compatibility code for RTIR 5.0.1
RT 6 will support only RTIR 6, so no need for RTIR 5 compatibility.
diff --git a/share/html/Elements/MyRT b/share/html/Elements/MyRT
index f4df16448d..ffb6b77144 100644
--- a/share/html/Elements/MyRT
+++ b/share/html/Elements/MyRT
@@ -114,66 +114,38 @@ $show_cb = sub {
my $type;
my $name;
- # Back compat for RTIR older than 5.0.2
- if ( defined $RT::IR::VERSION
- && RT::Handle::cmp_version($RT::IR::VERSION, '5.0.1') <= 0
- && $m->callers(1)->path eq '/RTIR/index.html' ) {
+ # Normal handling for RT 5.0.2 and newer
+ my $depth = shift || 0;
+ Abort("Possible recursive dashboard detected.", SuppressHeader => 1) if $depth > 8;
- $type = $entry->{type};
- $name = $entry->{'name'};
- if ( $type eq 'component' ) {
- if (!$allowed_components{$name}) {
- $m->out( $m->interp->apply_escapes( loc("Invalid portlet [_1]", $name), "h" ) );
- RT->Logger->info("Invalid portlet $name found on user " . $user->Name . "'s homepage");
- if ($name eq 'QueueList' && $allowed_components{Quicksearch}) {
- RT->Logger->warning("You may need to replace the component 'Quicksearch' in the HomepageComponents config with 'QueueList'. See the UPGRADING-4.4 document.");
- }
+ $type = $entry->{portlet_type};
+ $name = $entry->{component};
+ if ( $type eq 'component' ) {
+ if (!$allowed_components{$name}) {
+ $m->out( $m->interp->apply_escapes( loc("Invalid portlet [_1]", $name), "h" ) );
+ RT->Logger->info("Invalid portlet $name found on user " . $user->Name . "'s homepage");
+ if ($name eq 'QueueList' && $allowed_components{Quicksearch}) {
+ RT->Logger->warning("You may need to replace the component 'Quicksearch' in the HomepageComponents config with 'QueueList'. See the UPGRADING-4.4 document.");
}
- else {
- $m->comp( $name, %{ $entry->{arguments} || {} } );
- }
- } elsif ( $type eq 'system' ) {
- $m->comp( '/Elements/ShowSearch', Name => $name, Override => { Rows => $Rows } );
- } elsif ( $type eq 'saved' ) {
- $m->comp( '/Elements/ShowSearch', SavedSearch => $name, Override => { Rows => $Rows } );
- } else {
- $RT::Logger->error("unknown portlet type '$type'");
}
- }
- else {
- # Normal handling for RT 5.0.2 and newer
- my $depth = shift || 0;
- Abort("Possible recursive dashboard detected.", SuppressHeader => 1) if $depth > 8;
-
- $type = $entry->{portlet_type};
- $name = $entry->{component};
- if ( $type eq 'component' ) {
- if (!$allowed_components{$name}) {
- $m->out( $m->interp->apply_escapes( loc("Invalid portlet [_1]", $name), "h" ) );
- RT->Logger->info("Invalid portlet $name found on user " . $user->Name . "'s homepage");
- if ($name eq 'QueueList' && $allowed_components{Quicksearch}) {
- RT->Logger->warning("You may need to replace the component 'Quicksearch' in the HomepageComponents config with 'QueueList'. See the UPGRADING-4.4 document.");
- }
- }
- else {
- $m->comp( $name, %{ $entry->{arguments} || {} } );
- }
- } elsif ( $type eq 'search' ) {
- $m->comp( '/Elements/ShowSearch', RT::Dashboard->ShowSearchName($entry), Override => { Rows => $Rows } );
- } elsif ( $type eq 'dashboard' ) {
- my $current_dashboard = RT::Dashboard->new($session{CurrentUser});
- my ($ok, $msg) = $current_dashboard->LoadById($entry->{id});
- if (!$ok) {
- $m->out($msg);
- return;
- }
- my @panes = @{ $current_dashboard->Panes->{$entry->{pane}} || [] };
- for my $portlet (@panes) {
- $show_cb->($portlet, $depth + 1);
- }
- } else {
- $RT::Logger->error("unknown portlet type '$type'");
+ else {
+ $m->comp( $name, %{ $entry->{arguments} || {} } );
+ }
+ } elsif ( $type eq 'search' ) {
+ $m->comp( '/Elements/ShowSearch', RT::Dashboard->ShowSearchName($entry), Override => { Rows => $Rows } );
+ } elsif ( $type eq 'dashboard' ) {
+ my $current_dashboard = RT::Dashboard->new($session{CurrentUser});
+ my ($ok, $msg) = $current_dashboard->LoadById($entry->{id});
+ if (!$ok) {
+ $m->out($msg);
+ return;
+ }
+ my @panes = @{ $current_dashboard->Panes->{$entry->{pane}} || [] };
+ for my $portlet (@panes) {
+ $show_cb->($portlet, $depth + 1);
}
+ } else {
+ $RT::Logger->error("unknown portlet type '$type'");
}
};
commit 69b81036855b3e16f39375afdb63b6dce1d71ee8
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri Apr 21 08:50:04 2023 -0400
Change redirect status code for turbo
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index bcdf354ada..4cdfef0bc6 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -1129,11 +1129,12 @@ sub Redirect {
# not sure why, but on some systems without this call mason doesn't
# set status to 302, but 200 instead and people see blank pages
- $HTML::Mason::Commands::r->status(302);
+ # Turbo wants this to be 303
+ $HTML::Mason::Commands::r->status(303);
# Perlbal expects a status message, but Mason's default redirect status
# doesn't provide one. See also rt.cpan.org #36689.
- $HTML::Mason::Commands::m->redirect( $uri->canonical, "302 Found" );
+ $HTML::Mason::Commands::m->redirect( $uri->canonical, "303 See other" );
$HTML::Mason::Commands::m->abort;
}
commit 3d57b7f65eaff74d4675e3d5df0254b121c37b9b
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Thu Apr 20 14:29:22 2023 -0400
Add turbo JS files
diff --git a/devel/third-party/README b/devel/third-party/README
index 96c46c405d..9126344da5 100644
--- a/devel/third-party/README
+++ b/devel/third-party/README
@@ -105,3 +105,8 @@ License: MIT and GPL
Description: bubble-like multiple user inputs
Origin: https://selectize.github.io/selectize.js/
License: Apache
+
+* turbo-7.3.0
+Description: JS framework to speed up web sites
+Origin: https://github.com/hotwired/turbo
+License: MIT
diff --git a/devel/third-party/turbo-7.3.0/MIT-LICENSE b/devel/third-party/turbo-7.3.0/MIT-LICENSE
new file mode 100644
index 0000000000..3d7308cf91
--- /dev/null
+++ b/devel/third-party/turbo-7.3.0/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 37signals
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/devel/third-party/turbo-7.3.0/README b/devel/third-party/turbo-7.3.0/README
new file mode 100644
index 0000000000..67b9a7424c
--- /dev/null
+++ b/devel/third-party/turbo-7.3.0/README
@@ -0,0 +1,16 @@
+Use the steps below to build the turbo JS file for RT. If you don't have
+them, you will need to install recent versions of: yarn, node, tsc, jsmin
+and all dependencies.
+
+1) Download the tarball from Releases on GitHub for the version you want.
+2) Unpack.
+3) In the unpacked turbo directory, run:
+
+ yarn run build
+
+ That should create dist/turbo.es2017-esm.js
+4) To create a minified version:
+
+ jsmin <dist/turbo.es2017-esm.js > dist/turbo.es2017-esm.min.js
+
+5) Copy the new file to RT's share/static/js.
diff --git a/devel/third-party/turbo-7.3.0/turbo.es2017-esm.js b/devel/third-party/turbo-7.3.0/turbo.es2017-esm.js
new file mode 100644
index 0000000000..cfe408484e
--- /dev/null
+++ b/devel/third-party/turbo-7.3.0/turbo.es2017-esm.js
@@ -0,0 +1,3969 @@
+/*
+Turbo 7.3.0
+Copyright © 2023 37signals LLC
+ */
+(function () {
+ if (window.Reflect === undefined ||
+ window.customElements === undefined ||
+ window.customElements.polyfillWrapFlushCallback) {
+ return;
+ }
+ const BuiltInHTMLElement = HTMLElement;
+ const wrapperForTheName = {
+ HTMLElement: function HTMLElement() {
+ return Reflect.construct(BuiltInHTMLElement, [], this.constructor);
+ },
+ };
+ window.HTMLElement = wrapperForTheName["HTMLElement"];
+ HTMLElement.prototype = BuiltInHTMLElement.prototype;
+ HTMLElement.prototype.constructor = HTMLElement;
+ Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
+})();
+
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Javan Makhmali
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+(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);
+
+const submittersByForm = 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 === null || candidate === void 0 ? void 0 : 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 && /Apple Computer/.test(navigator.vendor)) {
+ prototype = window.SubmitEvent.prototype;
+ }
+ else if ("SubmitEvent" in window) {
+ 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;
+(function (FrameLoadingStyle) {
+ FrameLoadingStyle["eager"] = "eager";
+ FrameLoadingStyle["lazy"] = "lazy";
+})(FrameLoadingStyle || (FrameLoadingStyle = {}));
+class FrameElement extends HTMLElement {
+ static get observedAttributes() {
+ return ["disabled", "complete", "loading", "src"];
+ }
+ constructor() {
+ super();
+ this.loaded = Promise.resolve();
+ 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 == "complete") {
+ this.delegate.completeChanged();
+ }
+ else if (name == "src") {
+ this.delegate.sourceURLChanged();
+ }
+ else {
+ this.delegate.disabledChanged();
+ }
+ }
+ get src() {
+ return this.getAttribute("src");
+ }
+ set src(value) {
+ if (value) {
+ this.setAttribute("src", value);
+ }
+ else {
+ this.removeAttribute("src");
+ }
+ }
+ get loading() {
+ return frameLoadingStyleFromString(this.getAttribute("loading") || "");
+ }
+ set loading(value) {
+ if (value) {
+ this.setAttribute("loading", value);
+ }
+ else {
+ this.removeAttribute("loading");
+ }
+ }
+ get disabled() {
+ return this.hasAttribute("disabled");
+ }
+ set disabled(value) {
+ if (value) {
+ this.setAttribute("disabled", "");
+ }
+ else {
+ this.removeAttribute("disabled");
+ }
+ }
+ get autoscroll() {
+ return this.hasAttribute("autoscroll");
+ }
+ set autoscroll(value) {
+ if (value) {
+ this.setAttribute("autoscroll", "");
+ }
+ else {
+ this.removeAttribute("autoscroll");
+ }
+ }
+ get complete() {
+ return !this.delegate.isLoading;
+ }
+ get isActive() {
+ return this.ownerDocument === document && !this.isPreview;
+ }
+ get isPreview() {
+ var _a, _b;
+ return (_b = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.documentElement) === null || _b === void 0 ? void 0 : _b.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(form, submitter) {
+ const action = (submitter === null || submitter === void 0 ? void 0 : 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(location, rootLocation) {
+ return isPrefixedBy(location, rootLocation) && isHTML(location);
+}
+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 + "/";
+}
+
+class FetchResponse {
+ 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(undefined);
+ }
+ }
+ 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 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] == undefined ? "" : 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 === null || element === void 0 ? void 0 : 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 = 2000) {
+ 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) {
+ var _a;
+ if (element instanceof Element) {
+ return (element.closest(selector) ||
+ findClosestRecursively(element.assignedSlot || ((_a = element.getRootNode()) === null || _a === void 0 ? void 0 : _a.host), selector));
+ }
+}
+
+var FetchMethod;
+(function (FetchMethod) {
+ FetchMethod[FetchMethod["get"] = 0] = "get";
+ FetchMethod[FetchMethod["post"] = 1] = "post";
+ FetchMethod[FetchMethod["put"] = 2] = "put";
+ FetchMethod[FetchMethod["patch"] = 3] = "patch";
+ FetchMethod[FetchMethod["delete"] = 4] = "delete";
+})(FetchMethod || (FetchMethod = {}));
+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;
+ }
+}
+class FetchRequest {
+ constructor(delegate, method, location, body = new URLSearchParams(), target = null) {
+ this.abortController = new AbortController();
+ this.resolveRequestPromise = (_value) => { };
+ this.delegate = delegate;
+ this.method = method;
+ this.headers = this.defaultHeaders;
+ this.body = body;
+ this.url = location;
+ this.target = target;
+ }
+ 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);
+ await this.allowRequestToBeIntercepted(fetchOptions);
+ try {
+ this.delegate.requestStarted(this);
+ const response = await fetch(this.url.href, fetchOptions);
+ return await this.receive(response);
+ }
+ catch (error) {
+ if (error.name !== "AbortError") {
+ if (this.willDelegateErrorHandling(error)) {
+ this.delegate.requestErrored(this, error);
+ }
+ throw error;
+ }
+ }
+ 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 fetchOptions() {
+ var _a;
+ return {
+ method: FetchMethod[this.method].toUpperCase(),
+ credentials: "same-origin",
+ headers: this.headers,
+ redirect: "follow",
+ body: this.isSafe ? null : this.body,
+ signal: this.abortSignal,
+ referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href,
+ };
+ }
+ get defaultHeaders() {
+ return {
+ Accept: "text/html, application/xhtml+xml",
+ };
+ }
+ get isSafe() {
+ return this.method === FetchMethod.get;
+ }
+ 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,
+ });
+ if (event.defaultPrevented)
+ await requestInterception;
+ }
+ willDelegateErrorHandling(error) {
+ const event = dispatch("turbo:fetch-request-error", {
+ target: this.target,
+ cancelable: true,
+ detail: { request: this, error: error },
+ });
+ return !event.defaultPrevented;
+ }
+}
+
+class AppearanceObserver {
+ constructor(delegate, element) {
+ this.started = false;
+ this.intersect = (entries) => {
+ const lastEntry = entries.slice(-1)[0];
+ if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
+ this.delegate.elementAppearedInViewport(this.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);
+ }
+ }
+}
+
+class StreamMessage {
+ static wrap(message) {
+ if (typeof message == "string") {
+ return new this(createDocumentFragment(message));
+ }
+ else {
+ return message;
+ }
+ }
+ constructor(fragment) {
+ this.fragment = importStreamElements(fragment);
+ }
+}
+StreamMessage.contentType = "text/vnd.turbo-stream.html";
+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 FormSubmissionState;
+(function (FormSubmissionState) {
+ FormSubmissionState[FormSubmissionState["initialized"] = 0] = "initialized";
+ FormSubmissionState[FormSubmissionState["requesting"] = 1] = "requesting";
+ FormSubmissionState[FormSubmissionState["waiting"] = 2] = "waiting";
+ FormSubmissionState[FormSubmissionState["receiving"] = 3] = "receiving";
+ FormSubmissionState[FormSubmissionState["stopping"] = 4] = "stopping";
+ FormSubmissionState[FormSubmissionState["stopped"] = 5] = "stopped";
+})(FormSubmissionState || (FormSubmissionState = {}));
+var FormEnctype;
+(function (FormEnctype) {
+ FormEnctype["urlEncoded"] = "application/x-www-form-urlencoded";
+ FormEnctype["multipart"] = "multipart/form-data";
+ FormEnctype["plain"] = "text/plain";
+})(FormEnctype || (FormEnctype = {}));
+function formEnctypeFromString(encoding) {
+ switch (encoding.toLowerCase()) {
+ case FormEnctype.multipart:
+ return FormEnctype.multipart;
+ case FormEnctype.plain:
+ return FormEnctype.plain;
+ default:
+ return FormEnctype.urlEncoded;
+ }
+}
+class FormSubmission {
+ static confirmMethod(message, _element, _submitter) {
+ return Promise.resolve(confirm(message));
+ }
+ constructor(delegate, formElement, submitter, mustRedirect = false) {
+ this.state = FormSubmissionState.initialized;
+ this.delegate = delegate;
+ this.formElement = formElement;
+ this.submitter = submitter;
+ this.formData = buildFormData(formElement, submitter);
+ this.location = expandURL(this.action);
+ if (this.method == FetchMethod.get) {
+ mergeFormDataEntries(this.location, [...this.body.entries()]);
+ }
+ this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
+ this.mustRedirect = mustRedirect;
+ }
+ get method() {
+ var _a;
+ const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.getAttribute("method") || "";
+ return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get;
+ }
+ get action() {
+ var _a;
+ const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
+ if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
+ return this.submitter.getAttribute("formaction") || "";
+ }
+ else {
+ return this.formElement.getAttribute("action") || formElementAction || "";
+ }
+ }
+ get body() {
+ if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
+ return new URLSearchParams(this.stringFormData);
+ }
+ else {
+ return this.formData;
+ }
+ }
+ get enctype() {
+ var _a;
+ return formEnctypeFromString(((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formenctype")) || this.formElement.enctype);
+ }
+ get isSafe() {
+ return this.fetchRequest.isSafe;
+ }
+ get stringFormData() {
+ return [...this.formData].reduce((entries, [name, value]) => {
+ return entries.concat(typeof value == "string" ? [[name, value]] : []);
+ }, []);
+ }
+ 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;
+ }
+ }
+ 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) {
+ var _a;
+ this.state = FormSubmissionState.waiting;
+ (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
+ this.setSubmitsWith();
+ dispatch("turbo:submit-start", {
+ target: this.formElement,
+ detail: { formSubmission: this },
+ });
+ this.delegate.formSubmissionStarted(this);
+ }
+ requestPreventedHandlingResponse(request, response) {
+ this.result = { success: response.succeeded, fetchResponse: response };
+ }
+ requestSucceededWithResponse(request, response) {
+ if (response.clientError || response.serverError) {
+ this.delegate.formSubmissionFailedWithResponse(this, response);
+ }
+ else if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) {
+ const error = new Error("Form responses must redirect to another location");
+ this.delegate.formSubmissionErrored(this, error);
+ }
+ 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, error) {
+ this.result = { success: false, error };
+ this.delegate.formSubmissionErrored(this, error);
+ }
+ requestFinished(_request) {
+ var _a;
+ this.state = FormSubmissionState.stopped;
+ (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
+ this.resetSubmitterText();
+ dispatch("turbo:submit-end", {
+ target: this.formElement,
+ detail: Object.assign({ formSubmission: this }, this.result),
+ });
+ this.delegate.formSubmissionFinished(this);
+ }
+ 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() {
+ var _a;
+ return (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-submits-with");
+ }
+}
+function buildFormData(formElement, submitter) {
+ const formData = new FormData(formElement);
+ const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
+ const value = submitter === null || submitter === void 0 ? void 0 : 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((cookie) => cookie.startsWith(cookieName));
+ if (cookie) {
+ const value = cookie.split("=").slice(1).join("=");
+ return value ? decodeURIComponent(value) : undefined;
+ }
+ }
+}
+function responseSucceededWithoutRedirect(response) {
+ return response.statusCode == 200 && !response.redirected;
+}
+function mergeFormDataEntries(url, entries) {
+ const searchParams = new URLSearchParams();
+ for (const [name, value] of entries) {
+ if (value instanceof File)
+ continue;
+ searchParams.append(name, value);
+ }
+ url.search = searchParams.toString();
+ return url;
+}
+
+class Snapshot {
+ 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() {
+ const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
+ for (const element of this.element.querySelectorAll("[autofocus]")) {
+ if (element.closest(inertDisabledOrHidden) == null)
+ return element;
+ else
+ continue;
+ }
+ return null;
+ }
+ 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]");
+}
+
+class FormSubmitObserver {
+ constructor(delegate, eventTarget) {
+ this.started = false;
+ this.submitCaptured = () => {
+ this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
+ this.eventTarget.addEventListener("submit", this.submitBubbled, false);
+ };
+ this.submitBubbled = ((event) => {
+ if (!event.defaultPrevented) {
+ const form = event.target instanceof HTMLFormElement ? event.target : undefined;
+ const submitter = event.submitter || undefined;
+ if (form &&
+ submissionDoesNotDismissDialog(form, submitter) &&
+ submissionDoesNotTargetIFrame(form, submitter) &&
+ this.delegate.willSubmitForm(form, submitter)) {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ this.delegate.formSubmitted(form, submitter);
+ }
+ }
+ });
+ 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;
+ }
+ }
+}
+function submissionDoesNotDismissDialog(form, submitter) {
+ const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
+ return method != "dialog";
+}
+function submissionDoesNotTargetIFrame(form, submitter) {
+ if ((submitter === null || submitter === void 0 ? void 0 : submitter.hasAttribute("formtarget")) || form.hasAttribute("target")) {
+ const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
+ for (const element of document.getElementsByName(target)) {
+ if (element instanceof HTMLIFrameElement)
+ return false;
+ }
+ return true;
+ }
+ else {
+ return true;
+ }
+}
+
+class View {
+ constructor(delegate, element) {
+ this.resolveRenderPromise = (_value) => { };
+ this.resolveInterceptionPromise = (_value) => { };
+ this.delegate = delegate;
+ this.element = element;
+ }
+ scrollToAnchor(anchor) {
+ const element = this.snapshot.getElementForAnchor(anchor);
+ if (element) {
+ this.scrollToElement(element);
+ this.focusElement(element);
+ }
+ else {
+ this.scrollToPosition({ x: 0, y: 0 });
+ }
+ }
+ scrollToAnchorFromLocation(location) {
+ this.scrollToAnchor(getAnchor(location));
+ }
+ 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;
+ }
+ async render(renderer) {
+ const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
+ 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 };
+ const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
+ if (!immediateRender)
+ await renderInterception;
+ await this.renderSnapshot(renderer);
+ this.delegate.viewRenderedSnapshot(snapshot, isPreview);
+ this.delegate.preloadOnLoadLinksForView(this.element);
+ this.finishRenderingSnapshot(renderer);
+ }
+ finally {
+ delete this.renderer;
+ this.resolveRenderPromise(undefined);
+ delete this.renderPromise;
+ }
+ }
+ else {
+ 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");
+ }
+ }
+ async renderSnapshot(renderer) {
+ await renderer.render();
+ }
+ finishRenderingSnapshot(renderer) {
+ renderer.finishRendering();
+ }
+}
+
+class FrameView extends View {
+ missing() {
+ this.element.innerHTML = `<strong class="turbo-frame-error">Content missing</strong>`;
+ }
+ get snapshot() {
+ return new Snapshot(this.element);
+ }
+}
+
+class LinkInterceptor {
+ constructor(delegate, element) {
+ this.clickBubbled = (event) => {
+ if (this.respondsToEventTarget(event.target)) {
+ this.clickEvent = event;
+ }
+ else {
+ delete this.clickEvent;
+ }
+ };
+ this.linkClicked = ((event) => {
+ if (this.clickEvent && this.respondsToEventTarget(event.target) && event.target instanceof Element) {
+ 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;
+ });
+ this.willVisit = ((_event) => {
+ delete this.clickEvent;
+ });
+ 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);
+ }
+ respondsToEventTarget(target) {
+ const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
+ return element && element.closest("turbo-frame, html") == this.element;
+ }
+}
+
+class LinkClickObserver {
+ constructor(delegate, eventTarget) {
+ this.started = false;
+ this.clickCaptured = () => {
+ this.eventTarget.removeEventListener("click", this.clickBubbled, false);
+ this.eventTarget.addEventListener("click", this.clickBubbled, false);
+ };
+ this.clickBubbled = (event) => {
+ if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
+ const target = (event.composedPath && event.composedPath()[0]) || event.target;
+ const link = this.findLinkFromClickTarget(target);
+ if (link && doesNotTargetIFrame(link)) {
+ const location = this.getLocationForLink(link);
+ if (this.delegate.willFollowLinkToLocation(link, location, event)) {
+ event.preventDefault();
+ this.delegate.followedLinkToLocation(link, location);
+ }
+ }
+ }
+ };
+ 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;
+ }
+ }
+ clickEventIsSignificant(event) {
+ return !((event.target && event.target.isContentEditable) ||
+ event.defaultPrevented ||
+ event.which > 1 ||
+ event.altKey ||
+ event.ctrlKey ||
+ event.metaKey ||
+ event.shiftKey);
+ }
+ findLinkFromClickTarget(target) {
+ return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
+ }
+ getLocationForLink(link) {
+ return expandURL(link.getAttribute("href") || "");
+ }
+}
+function doesNotTargetIFrame(anchor) {
+ if (anchor.hasAttribute("target")) {
+ for (const element of document.getElementsByName(anchor.target)) {
+ if (element instanceof HTMLIFrameElement)
+ return false;
+ }
+ return true;
+ }
+ else {
+ return true;
+ }
+}
+
+class FormLinkClickObserver {
+ constructor(delegate, element) {
+ this.delegate = delegate;
+ this.linkInterceptor = new LinkClickObserver(this, element);
+ }
+ start() {
+ this.linkInterceptor.start();
+ }
+ stop() {
+ this.linkInterceptor.stop();
+ }
+ willFollowLinkToLocation(link, location, originalEvent) {
+ return (this.delegate.willSubmitFormLinkToLocation(link, location, originalEvent) &&
+ link.hasAttribute("data-turbo-method"));
+ }
+ followedLinkToLocation(link, location) {
+ const form = document.createElement("form");
+ const type = "hidden";
+ for (const [name, value] of location.searchParams) {
+ form.append(Object.assign(document.createElement("input"), { type, name, value }));
+ }
+ const action = Object.assign(location, { 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, location, form);
+ document.body.appendChild(form);
+ form.addEventListener("turbo:submit-end", () => form.remove(), { once: true });
+ requestAnimationFrame(() => form.requestSubmit());
+ }
+}
+
+class Bardo {
+ 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 === null || placeholder === void 0 ? void 0 : 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;
+}
+
+class Renderer {
+ constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
+ this.activeElement = null;
+ 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 reloadReason() {
+ return;
+ }
+ prepareToRender() {
+ return;
+ }
+ finishRendering() {
+ if (this.resolvingFunctions) {
+ this.resolvingFunctions.resolve();
+ delete this.resolvingFunctions;
+ }
+ }
+ async preservingPermanentElements(callback) {
+ await Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
+ }
+ focusFirstAutofocusableElement() {
+ const element = this.connectedSnapshot.firstAutofocusableElement;
+ if (elementIsFocusable(element)) {
+ element.focus();
+ }
+ }
+ 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);
+ }
+}
+function elementIsFocusable(element) {
+ return element && typeof element.focus == "function";
+}
+
+class FrameRenderer extends Renderer {
+ static renderElement(currentElement, newElement) {
+ var _a;
+ const destinationRange = document.createRange();
+ destinationRange.selectNodeContents(currentElement);
+ destinationRange.deleteContents();
+ const frameElement = newElement;
+ const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.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 nextAnimationFrame();
+ this.preservingPermanentElements(() => {
+ this.loadFrameElement();
+ });
+ this.scrollFrameIntoView();
+ await nextAnimationFrame();
+ this.focusFirstAutofocusableElement();
+ await nextAnimationFrame();
+ 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;
+ }
+}
+
+class ProgressBar {
+ 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);
+ }
+ `;
+ }
+ constructor() {
+ this.hiding = false;
+ this.value = 0;
+ this.visible = false;
+ this.trickle = () => {
+ this.setValue(this.value + Math.random() / 100);
+ };
+ 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();
+ }
+ 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;
+ }
+ 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");
+ }
+}
+ProgressBar.animationDuration = 300;
+
+class HeadSnapshot extends Snapshot {
+ constructor() {
+ super(...arguments);
+ this.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 Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, 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;
+ }, undefined);
+ }
+}
+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;
+}
+
+class PageSnapshot extends Snapshot {
+ static fromHTMLString(html = "") {
+ return this.fromDocument(parseHTMLDocument(html));
+ }
+ static fromElement(element) {
+ return this.fromDocument(element.ownerDocument);
+ }
+ static fromDocument({ head, body }) {
+ return new this(body, new HeadSnapshot(head));
+ }
+ constructor(element, headSnapshot) {
+ super(element);
+ 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(clonedElement, this.headSnapshot);
+ }
+ get headElement() {
+ return this.headSnapshot.element;
+ }
+ get rootLocation() {
+ var _a;
+ const root = (_a = this.getSetting("root")) !== null && _a !== void 0 ? _a : "/";
+ 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";
+ }
+ getSetting(name) {
+ return this.headSnapshot.getMetaValue(`turbo-${name}`);
+ }
+}
+
+var TimingMetric;
+(function (TimingMetric) {
+ TimingMetric["visitStart"] = "visitStart";
+ TimingMetric["requestStart"] = "requestStart";
+ TimingMetric["requestEnd"] = "requestEnd";
+ TimingMetric["visitEnd"] = "visitEnd";
+})(TimingMetric || (TimingMetric = {}));
+var VisitState;
+(function (VisitState) {
+ VisitState["initialized"] = "initialized";
+ VisitState["started"] = "started";
+ VisitState["canceled"] = "canceled";
+ VisitState["failed"] = "failed";
+ VisitState["completed"] = "completed";
+})(VisitState || (VisitState = {}));
+const defaultOptions = {
+ action: "advance",
+ historyChanged: false,
+ visitCachedSnapshot: () => { },
+ willRender: true,
+ updateHistory: true,
+ shouldCacheSnapshot: true,
+ acceptsStreamResponse: false,
+};
+var SystemStatusCode;
+(function (SystemStatusCode) {
+ SystemStatusCode[SystemStatusCode["networkFailure"] = 0] = "networkFailure";
+ SystemStatusCode[SystemStatusCode["timeoutFailure"] = -1] = "timeoutFailure";
+ SystemStatusCode[SystemStatusCode["contentTypeMismatch"] = -2] = "contentTypeMismatch";
+})(SystemStatusCode || (SystemStatusCode = {}));
+class Visit {
+ constructor(delegate, location, restorationIdentifier, options = {}) {
+ this.identifier = uuid();
+ this.timingMetrics = {};
+ this.followedRedirect = false;
+ this.historyChanged = false;
+ this.scrolled = false;
+ this.shouldCacheSnapshot = true;
+ this.acceptsStreamResponse = false;
+ this.snapshotCached = false;
+ this.state = VisitState.initialized;
+ this.delegate = delegate;
+ this.location = location;
+ this.restorationIdentifier = restorationIdentifier || uuid();
+ const { action, historyChanged, referrer, snapshot, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, } = Object.assign(Object.assign({}, 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.visitCachedSnapshot = visitCachedSnapshot;
+ this.willRender = willRender;
+ this.updateHistory = updateHistory;
+ this.scrolled = !willRender;
+ this.shouldCacheSnapshot = shouldCacheSnapshot;
+ this.acceptsStreamResponse = acceptsStreamResponse;
+ }
+ 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.state = VisitState.completed;
+ this.followRedirect();
+ if (!this.followedRedirect) {
+ this.adapter.visitCompleted(this);
+ this.delegate.visitCompleted(this);
+ }
+ }
+ }
+ fail() {
+ if (this.state == VisitState.started) {
+ this.state = VisitState.failed;
+ this.adapter.visitFailed(this);
+ }
+ }
+ changeHistory() {
+ var _a;
+ if (!this.historyChanged && this.updateHistory) {
+ const actionForHistory = this.location.href === ((_a = this.referrer) === null || _a === void 0 ? void 0 : _a.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) {
+ await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
+ this.performScroll();
+ 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.adapter.visitRendered(this);
+ }
+ else {
+ if (this.view.renderPromise)
+ await this.view.renderPromise;
+ await this.view.renderPage(snapshot, isPreview, this.willRender, this);
+ this.performScroll();
+ this.adapter.visitRendered(this);
+ if (!isPreview) {
+ this.complete();
+ }
+ }
+ });
+ }
+ }
+ followRedirect() {
+ var _a;
+ if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.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);
+ });
+ }
+ }
+ 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 == undefined) {
+ this.recordResponse({
+ statusCode: SystemStatusCode.contentTypeMismatch,
+ redirected,
+ });
+ }
+ else {
+ this.redirectedToLocation = response.redirected ? response.location : undefined;
+ this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
+ }
+ }
+ async requestFailedWithResponse(request, response) {
+ const responseHTML = await response.responseHTML;
+ const { redirected, statusCode } = response;
+ if (responseHTML == undefined) {
+ this.recordResponse({
+ statusCode: SystemStatusCode.contentTypeMismatch,
+ redirected,
+ });
+ }
+ else {
+ this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
+ }
+ }
+ requestErrored(_request, _error) {
+ this.recordResponse({
+ statusCode: SystemStatusCode.networkFailure,
+ redirected: false,
+ });
+ }
+ requestFinished() {
+ this.finishRequest();
+ }
+ performScroll() {
+ if (!this.scrolled && !this.view.forceReloaded) {
+ 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;
+ }
+ }
+ recordTimingMetric(metric) {
+ this.timingMetrics[metric] = new Date().getTime();
+ }
+ getTimingMetrics() {
+ return Object.assign({}, this.timingMetrics);
+ }
+ 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();
+ await new Promise((resolve) => {
+ this.frame = requestAnimationFrame(() => resolve());
+ });
+ await callback();
+ delete this.frame;
+ }
+ cancelRender() {
+ if (this.frame) {
+ cancelAnimationFrame(this.frame);
+ delete this.frame;
+ }
+ }
+}
+function isSuccessful(statusCode) {
+ return statusCode >= 200 && statusCode < 300;
+}
+
+class BrowserAdapter {
+ constructor(session) {
+ this.progressBar = new ProgressBar();
+ this.showProgressBar = () => {
+ this.progressBar.show();
+ };
+ this.session = session;
+ }
+ visitProposedToLocation(location, options) {
+ this.navigator.startVisit(location, (options === null || options === void 0 ? void 0 : options.restorationIdentifier) || uuid(), options);
+ }
+ visitStarted(visit) {
+ this.location = visit.location;
+ visit.loadCachedSnapshot();
+ visit.issueRequest();
+ visit.goToSamePageAnchor();
+ }
+ visitRequestStarted(visit) {
+ this.progressBar.setValue(0);
+ if (visit.hasCachedSnapshot() || visit.action != "restore") {
+ this.showVisitProgressBarAfterDelay();
+ }
+ else {
+ this.showProgressBar();
+ }
+ }
+ visitRequestCompleted(visit) {
+ visit.loadResponse();
+ }
+ visitRequestFailedWithStatusCode(visit, statusCode) {
+ switch (statusCode) {
+ case SystemStatusCode.networkFailure:
+ case SystemStatusCode.timeoutFailure:
+ case SystemStatusCode.contentTypeMismatch:
+ return this.reload({
+ reason: "request_failed",
+ context: {
+ statusCode,
+ },
+ });
+ default:
+ return visit.loadResponse();
+ }
+ }
+ visitRequestFinished(_visit) {
+ this.progressBar.setValue(1);
+ this.hideVisitProgressBar();
+ }
+ visitCompleted(_visit) { }
+ pageInvalidated(reason) {
+ this.reload(reason);
+ }
+ visitFailed(_visit) { }
+ visitRendered(_visit) { }
+ formSubmissionStarted(_formSubmission) {
+ this.progressBar.setValue(0);
+ this.showFormProgressBarAfterDelay();
+ }
+ formSubmissionFinished(_formSubmission) {
+ this.progressBar.setValue(1);
+ this.hideFormProgressBar();
+ }
+ 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;
+ }
+ }
+ reload(reason) {
+ var _a;
+ dispatch("turbo:reload", { detail: reason });
+ window.location.href = ((_a = this.location) === null || _a === void 0 ? void 0 : _a.toString()) || window.location.href;
+ }
+ get navigator() {
+ return this.session.navigator;
+ }
+}
+
+class CacheObserver {
+ constructor() {
+ this.selector = "[data-turbo-temporary]";
+ this.deprecatedSelector = "[data-turbo-cache=false]";
+ this.started = false;
+ this.removeTemporaryElements = ((_event) => {
+ for (const element of this.temporaryElements) {
+ element.remove();
+ }
+ });
+ }
+ 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);
+ }
+ }
+ 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];
+ }
+}
+
+class FrameRedirector {
+ constructor(session, element) {
+ this.session = session;
+ 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();
+ }
+ 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);
+ }
+ }
+ 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) {
+ var _a;
+ const action = getAction(form, submitter);
+ const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
+ const rootLocation = expandURL((_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/");
+ 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 === null || submitter === void 0 ? void 0 : 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;
+ }
+ }
+ }
+}
+
+class History {
+ constructor(delegate) {
+ this.restorationIdentifier = uuid();
+ this.restorationData = {};
+ this.started = false;
+ this.pageLoaded = false;
+ this.onPopState = (event) => {
+ if (this.shouldHandlePopState()) {
+ const { turbo } = event.state || {};
+ if (turbo) {
+ this.location = new URL(window.location.href);
+ const { restorationIdentifier } = turbo;
+ this.restorationIdentifier = restorationIdentifier;
+ this.delegate.historyPoppedToLocationWithRestorationIdentifier(this.location, restorationIdentifier);
+ }
+ }
+ };
+ this.onPageLoad = async (_event) => {
+ await nextMicrotask();
+ this.pageLoaded = true;
+ };
+ this.delegate = delegate;
+ }
+ start() {
+ if (!this.started) {
+ addEventListener("popstate", this.onPopState, false);
+ addEventListener("load", this.onPageLoad, false);
+ 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(location, restorationIdentifier) {
+ this.update(history.pushState, location, restorationIdentifier);
+ }
+ replace(location, restorationIdentifier) {
+ this.update(history.replaceState, location, restorationIdentifier);
+ }
+ update(method, location, restorationIdentifier = uuid()) {
+ const state = { turbo: { restorationIdentifier } };
+ method.call(history, state, "", location.href);
+ this.location = location;
+ this.restorationIdentifier = restorationIdentifier;
+ }
+ getRestorationDataForIdentifier(restorationIdentifier) {
+ return this.restorationData[restorationIdentifier] || {};
+ }
+ updateRestorationData(additionalData) {
+ const { restorationIdentifier } = this;
+ const restorationData = this.restorationData[restorationIdentifier];
+ this.restorationData[restorationIdentifier] = Object.assign(Object.assign({}, restorationData), additionalData);
+ }
+ assumeControlOfScrollRestoration() {
+ var _a;
+ if (!this.previousScrollRestoration) {
+ this.previousScrollRestoration = (_a = history.scrollRestoration) !== null && _a !== void 0 ? _a : "auto";
+ history.scrollRestoration = "manual";
+ }
+ }
+ relinquishControlOfScrollRestoration() {
+ if (this.previousScrollRestoration) {
+ history.scrollRestoration = this.previousScrollRestoration;
+ delete this.previousScrollRestoration;
+ }
+ }
+ shouldHandlePopState() {
+ return this.pageIsLoaded();
+ }
+ pageIsLoaded() {
+ return this.pageLoaded || document.readyState == "complete";
+ }
+}
+
+class Navigator {
+ constructor(delegate) {
+ this.delegate = delegate;
+ }
+ proposeVisit(location, options = {}) {
+ if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
+ if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
+ this.delegate.visitProposedToLocation(location, options);
+ }
+ else {
+ window.location.href = location.toString();
+ }
+ }
+ }
+ startVisit(locatable, restorationIdentifier, options = {}) {
+ this.stop();
+ this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ 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 history() {
+ return this.delegate.history;
+ }
+ 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);
+ 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);
+ }
+ this.view.scrollToTop();
+ this.view.clearSnapshotCache();
+ }
+ }
+ formSubmissionErrored(formSubmission, error) {
+ console.error(error);
+ }
+ formSubmissionFinished(formSubmission) {
+ if (typeof this.adapter.formSubmissionFinished === "function") {
+ this.adapter.formSubmissionFinished(formSubmission);
+ }
+ }
+ visitStarted(visit) {
+ this.delegate.visitStarted(visit);
+ }
+ visitCompleted(visit) {
+ this.delegate.visitCompleted(visit);
+ }
+ locationWithActionIsSamePage(location, action) {
+ const anchor = getAnchor(location);
+ const currentAnchor = getAnchor(this.view.lastRenderedLocation);
+ const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
+ return (action !== "replace" &&
+ getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) &&
+ (isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
+ }
+ visitScrolledToSamePageLocation(oldURL, newURL) {
+ this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
+ }
+ get location() {
+ return this.history.location;
+ }
+ get restorationIdentifier() {
+ return this.history.restorationIdentifier;
+ }
+ getActionForFormSubmission({ submitter, formElement }) {
+ return getVisitAction(submitter, formElement) || "advance";
+ }
+}
+
+var PageStage;
+(function (PageStage) {
+ PageStage[PageStage["initial"] = 0] = "initial";
+ PageStage[PageStage["loading"] = 1] = "loading";
+ PageStage[PageStage["interactive"] = 2] = "interactive";
+ PageStage[PageStage["complete"] = 3] = "complete";
+})(PageStage || (PageStage = {}));
+class PageObserver {
+ constructor(delegate) {
+ this.stage = PageStage.initial;
+ this.started = false;
+ this.interpretReadyState = () => {
+ const { readyState } = this;
+ if (readyState == "interactive") {
+ this.pageIsInteractive();
+ }
+ else if (readyState == "complete") {
+ this.pageIsComplete();
+ }
+ };
+ this.pageWillUnload = () => {
+ this.delegate.pageWillUnload();
+ };
+ 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;
+ }
+ }
+ 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();
+ }
+ }
+ get readyState() {
+ return document.readyState;
+ }
+}
+
+class ScrollObserver {
+ constructor(delegate) {
+ this.started = false;
+ this.onScroll = () => {
+ this.updatePosition({ x: window.pageXOffset, y: window.pageYOffset });
+ };
+ 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;
+ }
+ }
+ updatePosition(position) {
+ this.delegate.scrollPositionChanged(position);
+ }
+}
+
+class StreamMessageRenderer {
+ render({ fragment }) {
+ Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => document.documentElement.appendChild(fragment));
+ }
+ 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;
+}
+
+class StreamObserver {
+ constructor(delegate) {
+ this.sources = new Set();
+ this.started = false;
+ this.inspectFetchResponse = ((event) => {
+ const response = fetchResponseFromEvent(event);
+ if (response && fetchResponseIsStream(response)) {
+ event.preventDefault();
+ this.receiveMessageResponse(response);
+ }
+ });
+ this.receiveMessageEvent = (event) => {
+ if (this.started && typeof event.data == "string") {
+ this.receiveMessageHTML(event.data);
+ }
+ };
+ 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);
+ }
+ async receiveMessageResponse(response) {
+ const html = await response.responseHTML;
+ if (html) {
+ this.receiveMessageHTML(html);
+ }
+ }
+ receiveMessageHTML(html) {
+ this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
+ }
+}
+function fetchResponseFromEvent(event) {
+ var _a;
+ const fetchResponse = (_a = event.detail) === null || _a === void 0 ? void 0 : _a.fetchResponse;
+ if (fetchResponse instanceof FetchResponse) {
+ return fetchResponse;
+ }
+}
+function fetchResponseIsStream(response) {
+ var _a;
+ const contentType = (_a = response.contentType) !== null && _a !== void 0 ? _a : "";
+ return contentType.startsWith(StreamMessage.contentType);
+}
+
+class ErrorRenderer 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");
+ }
+}
+
+class PageRenderer extends Renderer {
+ static renderElement(currentElement, newElement) {
+ if (document.body && newElement instanceof HTMLBodyElement) {
+ document.body.replaceWith(newElement);
+ }
+ else {
+ document.documentElement.appendChild(newElement);
+ }
+ }
+ get shouldRender() {
+ return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
+ }
+ get reloadReason() {
+ if (!this.newSnapshot.isVisitable) {
+ return {
+ reason: "turbo_visit_control_is_reload",
+ };
+ }
+ if (!this.trackedElementsAreIdentical) {
+ return {
+ reason: "tracked_element_mismatch",
+ };
+ }
+ }
+ async prepareToRender() {
+ await this.mergeHead();
+ }
+ async render() {
+ if (this.willRender) {
+ await this.replaceBody();
+ }
+ }
+ finishRendering() {
+ super.finishRendering();
+ if (!this.isPreview) {
+ this.focusFirstAutofocusableElement();
+ }
+ }
+ get currentHeadSnapshot() {
+ return this.currentSnapshot.headSnapshot;
+ }
+ get newHeadSnapshot() {
+ return this.newSnapshot.headSnapshot;
+ }
+ get newElement() {
+ return this.newSnapshot.element;
+ }
+ async mergeHead() {
+ const mergedHeadElements = this.mergeProvisionalElements();
+ const newStylesheetElements = this.copyNewHeadStylesheetElements();
+ this.copyNewHeadScriptElements();
+ await mergedHeadElements;
+ await newStylesheetElements;
+ }
+ async replaceBody() {
+ await this.preservingPermanentElements(async () => {
+ this.activateNewBody();
+ await this.assignNewBody();
+ });
+ }
+ get trackedElementsAreIdentical() {
+ return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature;
+ }
+ async copyNewHeadStylesheetElements() {
+ const loadingElements = [];
+ for (const element of this.newHeadStylesheetElements) {
+ loadingElements.push(waitForLoad(element));
+ document.head.appendChild(element);
+ }
+ await Promise.all(loadingElements);
+ }
+ copyNewHeadScriptElements() {
+ for (const element of this.newHeadScriptElements) {
+ document.head.appendChild(activateScriptElement(element));
+ }
+ }
+ async mergeProvisionalElements() {
+ const newHeadElements = [...this.newHeadProvisionalElements];
+ for (const element of this.currentHeadProvisionalElements) {
+ if (!this.isCurrentElementInElementList(element, newHeadElements)) {
+ document.head.removeChild(element);
+ }
+ }
+ for (const element of newHeadElements) {
+ document.head.appendChild(element);
+ }
+ }
+ isCurrentElementInElementList(element, elementList) {
+ for (const [index, newElement] of elementList.entries()) {
+ if (element.tagName == "TITLE") {
+ if (newElement.tagName != "TITLE") {
+ continue;
+ }
+ if (element.innerHTML == newElement.innerHTML) {
+ elementList.splice(index, 1);
+ return true;
+ }
+ }
+ if (newElement.isEqualNode(element)) {
+ elementList.splice(index, 1);
+ return true;
+ }
+ }
+ return false;
+ }
+ removeCurrentHeadProvisionalElements() {
+ for (const element of this.currentHeadProvisionalElements) {
+ document.head.removeChild(element);
+ }
+ }
+ copyNewHeadProvisionalElements() {
+ for (const element of this.newHeadProvisionalElements) {
+ document.head.appendChild(element);
+ }
+ }
+ activateNewBody() {
+ document.adoptNode(this.newElement);
+ this.activateNewBodyScriptElements();
+ }
+ activateNewBodyScriptElements() {
+ for (const inertScriptElement of this.newBodyScriptElements) {
+ const activatedScriptElement = activateScriptElement(inertScriptElement);
+ inertScriptElement.replaceWith(activatedScriptElement);
+ }
+ }
+ async assignNewBody() {
+ await this.renderElement(this.currentElement, this.newElement);
+ }
+ get newHeadStylesheetElements() {
+ return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
+ }
+ get newHeadScriptElements() {
+ return this.newHeadSnapshot.getScriptElementsNotInSnapshot(this.currentHeadSnapshot);
+ }
+ get currentHeadProvisionalElements() {
+ return this.currentHeadSnapshot.provisionalElements;
+ }
+ get newHeadProvisionalElements() {
+ return this.newHeadSnapshot.provisionalElements;
+ }
+ get newBodyScriptElements() {
+ return this.newElement.querySelectorAll("script");
+ }
+}
+
+class SnapshotCache {
+ constructor(size) {
+ this.keys = [];
+ this.snapshots = {};
+ this.size = size;
+ }
+ has(location) {
+ return toCacheKey(location) in this.snapshots;
+ }
+ get(location) {
+ if (this.has(location)) {
+ const snapshot = this.read(location);
+ this.touch(location);
+ return snapshot;
+ }
+ }
+ put(location, snapshot) {
+ this.write(location, snapshot);
+ this.touch(location);
+ return snapshot;
+ }
+ clear() {
+ this.snapshots = {};
+ }
+ read(location) {
+ return this.snapshots[toCacheKey(location)];
+ }
+ write(location, snapshot) {
+ this.snapshots[toCacheKey(location)] = snapshot;
+ }
+ touch(location) {
+ const key = toCacheKey(location);
+ const index = this.keys.indexOf(key);
+ if (index > -1)
+ this.keys.splice(index, 1);
+ this.keys.unshift(key);
+ this.trim();
+ }
+ trim() {
+ for (const key of this.keys.splice(this.size)) {
+ delete this.snapshots[key];
+ }
+ }
+}
+
+class PageView extends View {
+ constructor() {
+ super(...arguments);
+ this.snapshotCache = new SnapshotCache(10);
+ this.lastRenderedLocation = new URL(location.href);
+ this.forceReloaded = false;
+ }
+ renderPage(snapshot, isPreview = false, willRender = true, visit) {
+ const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
+ if (!renderer.shouldRender) {
+ this.forceReloaded = true;
+ }
+ else {
+ visit === null || visit === void 0 ? void 0 : visit.changeHistory();
+ }
+ return this.render(renderer);
+ }
+ renderError(snapshot, visit) {
+ visit === null || visit === void 0 ? void 0 : visit.changeHistory();
+ const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
+ return this.render(renderer);
+ }
+ clearSnapshotCache() {
+ this.snapshotCache.clear();
+ }
+ async cacheSnapshot(snapshot = this.snapshot) {
+ if (snapshot.isCacheable) {
+ this.delegate.viewWillCacheSnapshot();
+ const { lastRenderedLocation: location } = this;
+ await nextEventLoopTick();
+ const cachedSnapshot = snapshot.clone();
+ this.snapshotCache.put(location, cachedSnapshot);
+ return cachedSnapshot;
+ }
+ }
+ getCachedSnapshotForLocation(location) {
+ return this.snapshotCache.get(location);
+ }
+ get snapshot() {
+ return PageSnapshot.fromElement(this.element);
+ }
+}
+
+class Preloader {
+ constructor(delegate) {
+ this.selector = "a[data-turbo-preload]";
+ this.delegate = delegate;
+ }
+ get snapshotCache() {
+ return this.delegate.navigator.view.snapshotCache;
+ }
+ start() {
+ if (document.readyState === "loading") {
+ return document.addEventListener("DOMContentLoaded", () => {
+ this.preloadOnLoadLinksForView(document.body);
+ });
+ }
+ else {
+ this.preloadOnLoadLinksForView(document.body);
+ }
+ }
+ preloadOnLoadLinksForView(element) {
+ for (const link of element.querySelectorAll(this.selector)) {
+ this.preloadURL(link);
+ }
+ }
+ async preloadURL(link) {
+ const location = new URL(link.href);
+ if (this.snapshotCache.has(location)) {
+ return;
+ }
+ try {
+ const response = await fetch(location.toString(), { headers: { "VND.PREFETCH": "true", Accept: "text/html" } });
+ const responseText = await response.text();
+ const snapshot = PageSnapshot.fromHTMLString(responseText);
+ this.snapshotCache.put(location, snapshot);
+ }
+ catch (_) {
+ }
+ }
+}
+
+class Session {
+ constructor() {
+ this.navigator = new Navigator(this);
+ this.history = new History(this);
+ this.preloader = new Preloader(this);
+ this.view = new PageView(this, document.documentElement);
+ this.adapter = new BrowserAdapter(this);
+ this.pageObserver = new PageObserver(this);
+ this.cacheObserver = new CacheObserver();
+ this.linkClickObserver = new LinkClickObserver(this, window);
+ this.formSubmitObserver = new FormSubmitObserver(this, document);
+ this.scrollObserver = new ScrollObserver(this);
+ this.streamObserver = new StreamObserver(this);
+ this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
+ this.frameRedirector = new FrameRedirector(this, document.documentElement);
+ this.streamMessageRenderer = new StreamMessageRenderer();
+ this.drive = true;
+ this.enabled = true;
+ this.progressBarDelay = 500;
+ this.started = false;
+ this.formMode = "on";
+ }
+ start() {
+ if (!this.started) {
+ this.pageObserver.start();
+ this.cacheObserver.start();
+ this.formLinkClickObserver.start();
+ this.linkClickObserver.start();
+ this.formSubmitObserver.start();
+ this.scrollObserver.start();
+ this.streamObserver.start();
+ this.frameRedirector.start();
+ this.history.start();
+ this.preloader.start();
+ this.started = true;
+ this.enabled = true;
+ }
+ }
+ disable() {
+ this.enabled = false;
+ }
+ stop() {
+ if (this.started) {
+ this.pageObserver.stop();
+ this.cacheObserver.stop();
+ this.formLinkClickObserver.stop();
+ this.linkClickObserver.stop();
+ this.formSubmitObserver.stop();
+ this.scrollObserver.stop();
+ this.streamObserver.stop();
+ this.frameRedirector.stop();
+ this.history.stop();
+ this.started = false;
+ }
+ }
+ registerAdapter(adapter) {
+ this.adapter = adapter;
+ }
+ visit(location, options = {}) {
+ const frameElement = options.frame ? document.getElementById(options.frame) : null;
+ if (frameElement instanceof FrameElement) {
+ frameElement.src = location.toString();
+ frameElement.loaded;
+ }
+ else {
+ this.navigator.proposeVisit(expandURL(location), options);
+ }
+ }
+ connectStreamSource(source) {
+ this.streamObserver.connectStreamSource(source);
+ }
+ disconnectStreamSource(source) {
+ this.streamObserver.disconnectStreamSource(source);
+ }
+ renderStreamMessage(message) {
+ this.streamMessageRenderer.render(StreamMessage.wrap(message));
+ }
+ clearCache() {
+ this.view.clearSnapshotCache();
+ }
+ setProgressBarDelay(delay) {
+ this.progressBarDelay = delay;
+ }
+ setFormMode(mode) {
+ this.formMode = mode;
+ }
+ get location() {
+ return this.history.location;
+ }
+ get restorationIdentifier() {
+ return this.history.restorationIdentifier;
+ }
+ historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
+ if (this.enabled) {
+ this.navigator.startVisit(location, restorationIdentifier, {
+ action: "restore",
+ historyChanged: true,
+ });
+ }
+ else {
+ this.adapter.pageInvalidated({
+ reason: "turbo_disabled",
+ });
+ }
+ }
+ scrollPositionChanged(position) {
+ this.history.updateRestorationData({ scrollPosition: position });
+ }
+ willSubmitFormLinkToLocation(link, location) {
+ return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation);
+ }
+ submittedFormLinkToLocation() { }
+ willFollowLinkToLocation(link, location, event) {
+ return (this.elementIsNavigatable(link) &&
+ locationIsVisitable(location, this.snapshot.rootLocation) &&
+ this.applicationAllowsFollowingLinkToLocation(link, location, event));
+ }
+ followedLinkToLocation(link, location) {
+ const action = this.getActionForLink(link);
+ const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
+ this.visit(location.href, { action, acceptsStreamResponse });
+ }
+ allowsVisitingLocationWithAction(location, action) {
+ return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
+ }
+ visitProposedToLocation(location, options) {
+ extendURLWithDeprecatedProperties(location);
+ this.adapter.visitProposedToLocation(location, options);
+ }
+ visitStarted(visit) {
+ if (!visit.acceptsStreamResponse) {
+ markAsBusy(document.documentElement);
+ }
+ extendURLWithDeprecatedProperties(visit.location);
+ if (!visit.silent) {
+ this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
+ }
+ }
+ visitCompleted(visit) {
+ clearBusyState(document.documentElement);
+ this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
+ }
+ locationWithActionIsSamePage(location, action) {
+ return this.navigator.locationWithActionIsSamePage(location, action);
+ }
+ visitScrolledToSamePageLocation(oldURL, newURL) {
+ this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
+ }
+ willSubmitForm(form, submitter) {
+ const action = getAction(form, submitter);
+ return (this.submissionIsNavigatable(form, submitter) &&
+ locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
+ }
+ formSubmitted(form, submitter) {
+ this.navigator.submitForm(form, submitter);
+ }
+ pageBecameInteractive() {
+ this.view.lastRenderedLocation = this.location;
+ this.notifyApplicationAfterPageLoad();
+ }
+ pageLoaded() {
+ this.history.assumeControlOfScrollRestoration();
+ }
+ pageWillUnload() {
+ this.history.relinquishControlOfScrollRestoration();
+ }
+ receivedMessageFromStream(message) {
+ this.renderStreamMessage(message);
+ }
+ viewWillCacheSnapshot() {
+ var _a;
+ if (!((_a = this.navigator.currentVisit) === null || _a === void 0 ? void 0 : _a.silent)) {
+ this.notifyApplicationBeforeCachingSnapshot();
+ }
+ }
+ allowsImmediateRender({ element }, options) {
+ const event = this.notifyApplicationBeforeRender(element, options);
+ const { defaultPrevented, detail: { render }, } = event;
+ if (this.view.renderer && render) {
+ this.view.renderer.renderElement = render;
+ }
+ return !defaultPrevented;
+ }
+ viewRenderedSnapshot(_snapshot, _isPreview) {
+ this.view.lastRenderedLocation = this.history.location;
+ this.notifyApplicationAfterRender();
+ }
+ preloadOnLoadLinksForView(element) {
+ this.preloader.preloadOnLoadLinksForView(element);
+ }
+ viewInvalidated(reason) {
+ this.adapter.pageInvalidated(reason);
+ }
+ frameLoaded(frame) {
+ this.notifyApplicationAfterFrameLoad(frame);
+ }
+ frameRendered(fetchResponse, frame) {
+ this.notifyApplicationAfterFrameRender(fetchResponse, frame);
+ }
+ applicationAllowsFollowingLinkToLocation(link, location, ev) {
+ const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
+ return !event.defaultPrevented;
+ }
+ applicationAllowsVisitingLocation(location) {
+ const event = this.notifyApplicationBeforeVisitingLocation(location);
+ return !event.defaultPrevented;
+ }
+ notifyApplicationAfterClickingLinkToLocation(link, location, event) {
+ return dispatch("turbo:click", {
+ target: link,
+ detail: { url: location.href, originalEvent: event },
+ cancelable: true,
+ });
+ }
+ notifyApplicationBeforeVisitingLocation(location) {
+ return dispatch("turbo:before-visit", {
+ detail: { url: location.href },
+ cancelable: true,
+ });
+ }
+ notifyApplicationAfterVisitingLocation(location, action) {
+ return dispatch("turbo:visit", { detail: { url: location.href, action } });
+ }
+ notifyApplicationBeforeCachingSnapshot() {
+ return dispatch("turbo:before-cache");
+ }
+ notifyApplicationBeforeRender(newBody, options) {
+ return dispatch("turbo:before-render", {
+ detail: Object.assign({ newBody }, options),
+ cancelable: true,
+ });
+ }
+ notifyApplicationAfterRender() {
+ return dispatch("turbo:render");
+ }
+ notifyApplicationAfterPageLoad(timing = {}) {
+ return dispatch("turbo:load", {
+ detail: { url: this.location.href, timing },
+ });
+ }
+ notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
+ dispatchEvent(new HashChangeEvent("hashchange", {
+ oldURL: oldURL.toString(),
+ newURL: newURL.toString(),
+ }));
+ }
+ notifyApplicationAfterFrameLoad(frame) {
+ return dispatch("turbo:frame-load", { target: frame });
+ }
+ notifyApplicationAfterFrameRender(fetchResponse, frame) {
+ return dispatch("turbo:frame-render", {
+ detail: { fetchResponse },
+ target: frame,
+ cancelable: true,
+ });
+ }
+ submissionIsNavigatable(form, submitter) {
+ if (this.formMode == "off") {
+ return false;
+ }
+ else {
+ const submitterIsNavigatable = submitter ? this.elementIsNavigatable(submitter) : true;
+ if (this.formMode == "optin") {
+ return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null;
+ }
+ else {
+ return submitterIsNavigatable && this.elementIsNavigatable(form);
+ }
+ }
+ }
+ elementIsNavigatable(element) {
+ const container = findClosestRecursively(element, "[data-turbo]");
+ const withinFrame = findClosestRecursively(element, "turbo-frame");
+ if (this.drive || withinFrame) {
+ if (container) {
+ return container.getAttribute("data-turbo") != "false";
+ }
+ else {
+ return true;
+ }
+ }
+ else {
+ if (container) {
+ return container.getAttribute("data-turbo") == "true";
+ }
+ else {
+ return false;
+ }
+ }
+ }
+ getActionForLink(link) {
+ return getVisitAction(link) || "advance";
+ }
+ get snapshot() {
+ return this.view.snapshot;
+ }
+}
+function extendURLWithDeprecatedProperties(url) {
+ Object.defineProperties(url, deprecatedLocationPropertyDescriptors);
+}
+const deprecatedLocationPropertyDescriptors = {
+ absoluteURL: {
+ get() {
+ return this.toString();
+ },
+ },
+};
+
+class Cache {
+ constructor(session) {
+ this.session = session;
+ }
+ clear() {
+ this.session.clearCache();
+ }
+ resetCacheControl() {
+ this.setCacheControl("");
+ }
+ exemptPageFromCache() {
+ this.setCacheControl("no-cache");
+ }
+ exemptPageFromPreview() {
+ this.setCacheControl("no-preview");
+ }
+ setCacheControl(value) {
+ setMetaContent("turbo-cache-control", value);
+ }
+}
+
+const StreamActions = {
+ after() {
+ this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
+ },
+ append() {
+ this.removeDuplicateTargetChildren();
+ this.targetElements.forEach((e) => e.append(this.templateContent));
+ },
+ before() {
+ this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
+ },
+ prepend() {
+ this.removeDuplicateTargetChildren();
+ this.targetElements.forEach((e) => e.prepend(this.templateContent));
+ },
+ remove() {
+ this.targetElements.forEach((e) => e.remove());
+ },
+ replace() {
+ this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
+ },
+ update() {
+ this.targetElements.forEach((targetElement) => {
+ targetElement.innerHTML = "";
+ targetElement.append(this.templateContent);
+ });
+ },
+};
+
+const session = new Session();
+const cache = new Cache(session);
+const { navigator: navigator$1 } = session;
+function start() {
+ session.start();
+}
+function registerAdapter(adapter) {
+ session.registerAdapter(adapter);
+}
+function visit(location, options) {
+ session.visit(location, options);
+}
+function connectStreamSource(source) {
+ session.connectStreamSource(source);
+}
+function disconnectStreamSource(source) {
+ session.disconnectStreamSource(source);
+}
+function renderStreamMessage(message) {
+ session.renderStreamMessage(message);
+}
+function clearCache() {
+ console.warn("Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`");
+ session.clearCache();
+}
+function setProgressBarDelay(delay) {
+ session.setProgressBarDelay(delay);
+}
+function setConfirmMethod(confirmMethod) {
+ FormSubmission.confirmMethod = confirmMethod;
+}
+function setFormMode(mode) {
+ session.setFormMode(mode);
+}
+
+var Turbo = /*#__PURE__*/Object.freeze({
+ __proto__: null,
+ navigator: navigator$1,
+ session: session,
+ cache: cache,
+ PageRenderer: PageRenderer,
+ PageSnapshot: PageSnapshot,
+ FrameRenderer: FrameRenderer,
+ start: start,
+ registerAdapter: registerAdapter,
+ visit: visit,
+ connectStreamSource: connectStreamSource,
+ disconnectStreamSource: disconnectStreamSource,
+ renderStreamMessage: renderStreamMessage,
+ clearCache: clearCache,
+ setProgressBarDelay: setProgressBarDelay,
+ setConfirmMethod: setConfirmMethod,
+ setFormMode: setFormMode,
+ StreamActions: StreamActions
+});
+
+class TurboFrameMissingError extends Error {
+}
+
+class FrameController {
+ constructor(element) {
+ this.fetchResponseLoaded = (_fetchResponse) => { };
+ this.currentFetchRequest = null;
+ this.resolveVisitPromise = () => { };
+ this.connected = false;
+ this.hasBeenLoaded = false;
+ this.ignoredAttributes = new Set();
+ this.action = null;
+ this.visitCachedSnapshot = ({ element }) => {
+ const frame = element.querySelector("#" + this.element.id);
+ if (frame && this.previousFrameElement) {
+ frame.replaceChildren(...this.previousFrameElement.children);
+ }
+ delete this.previousFrameElement;
+ };
+ this.element = element;
+ this.view = new FrameView(this, this.element);
+ this.appearanceObserver = new AppearanceObserver(this, this.element);
+ this.formLinkClickObserver = new FormLinkClickObserver(this, this.element);
+ this.linkInterceptor = new LinkInterceptor(this, this.element);
+ this.restorationIdentifier = uuid();
+ this.formSubmitObserver = new FormSubmitObserver(this, this.element);
+ }
+ connect() {
+ if (!this.connected) {
+ this.connected = true;
+ if (this.loadingStyle == FrameLoadingStyle.lazy) {
+ this.appearanceObserver.start();
+ }
+ else {
+ this.loadSourceURL();
+ }
+ this.formLinkClickObserver.start();
+ this.linkInterceptor.start();
+ this.formSubmitObserver.start();
+ }
+ }
+ disconnect() {
+ if (this.connected) {
+ this.connected = false;
+ this.appearanceObserver.stop();
+ this.formLinkClickObserver.stop();
+ this.linkInterceptor.stop();
+ this.formSubmitObserver.stop();
+ }
+ }
+ disabledChanged() {
+ if (this.loadingStyle == FrameLoadingStyle.eager) {
+ this.loadSourceURL();
+ }
+ }
+ sourceURLChanged() {
+ if (this.isIgnoringChangesTo("src"))
+ return;
+ if (this.element.isConnected) {
+ this.complete = false;
+ }
+ if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
+ this.loadSourceURL();
+ }
+ }
+ sourceURLReloaded() {
+ const { src } = this.element;
+ this.ignoringChangesToAttribute("complete", () => {
+ this.element.removeAttribute("complete");
+ });
+ this.element.src = null;
+ this.element.src = src;
+ return this.element.loaded;
+ }
+ completeChanged() {
+ if (this.isIgnoringChangesTo("complete"))
+ return;
+ this.loadSourceURL();
+ }
+ loadingStyleChanged() {
+ if (this.loadingStyle == FrameLoadingStyle.lazy) {
+ this.appearanceObserver.start();
+ }
+ else {
+ this.appearanceObserver.stop();
+ this.loadSourceURL();
+ }
+ }
+ async loadSourceURL() {
+ if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
+ this.element.loaded = this.visit(expandURL(this.sourceURL));
+ this.appearanceObserver.stop();
+ await this.element.loaded;
+ this.hasBeenLoaded = true;
+ }
+ }
+ async loadResponse(fetchResponse) {
+ if (fetchResponse.redirected || (fetchResponse.succeeded && fetchResponse.isHTML)) {
+ this.sourceURL = fetchResponse.response.url;
+ }
+ try {
+ const html = await fetchResponse.responseHTML;
+ if (html) {
+ const document = parseHTMLDocument(html);
+ const pageSnapshot = PageSnapshot.fromDocument(document);
+ if (pageSnapshot.isVisitable) {
+ await this.loadFrameResponse(fetchResponse, document);
+ }
+ else {
+ await this.handleUnvisitableFrameResponse(fetchResponse);
+ }
+ }
+ }
+ finally {
+ this.fetchResponseLoaded = () => { };
+ }
+ }
+ elementAppearedInViewport(element) {
+ this.proposeVisitIfNavigatedWithAction(element, element);
+ this.loadSourceURL();
+ }
+ willSubmitFormLinkToLocation(link) {
+ return this.shouldInterceptNavigation(link);
+ }
+ submittedFormLinkToLocation(link, _location, form) {
+ const frame = this.findFrameElement(link);
+ if (frame)
+ form.setAttribute("data-turbo-frame", frame.id);
+ }
+ shouldInterceptLinkClick(element, _location, _event) {
+ return this.shouldInterceptNavigation(element);
+ }
+ linkClickIntercepted(element, location) {
+ this.navigateFrame(element, location);
+ }
+ willSubmitForm(element, submitter) {
+ return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter);
+ }
+ formSubmitted(element, submitter) {
+ if (this.formSubmission) {
+ this.formSubmission.stop();
+ }
+ this.formSubmission = new FormSubmission(this, element, submitter);
+ const { fetchRequest } = this.formSubmission;
+ this.prepareRequest(fetchRequest);
+ this.formSubmission.start();
+ }
+ prepareRequest(request) {
+ var _a;
+ request.headers["Turbo-Frame"] = this.id;
+ if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
+ request.acceptResponseType(StreamMessage.contentType);
+ }
+ }
+ requestStarted(_request) {
+ markAsBusy(this.element);
+ }
+ requestPreventedHandlingResponse(_request, _response) {
+ this.resolveVisitPromise();
+ }
+ async requestSucceededWithResponse(request, response) {
+ await this.loadResponse(response);
+ this.resolveVisitPromise();
+ }
+ async requestFailedWithResponse(request, response) {
+ await this.loadResponse(response);
+ this.resolveVisitPromise();
+ }
+ requestErrored(request, error) {
+ console.error(error);
+ this.resolveVisitPromise();
+ }
+ requestFinished(_request) {
+ clearBusyState(this.element);
+ }
+ formSubmissionStarted({ formElement }) {
+ markAsBusy(formElement, this.findFrameElement(formElement));
+ }
+ formSubmissionSucceededWithResponse(formSubmission, response) {
+ const frame = this.findFrameElement(formSubmission.formElement, formSubmission.submitter);
+ frame.delegate.proposeVisitIfNavigatedWithAction(frame, formSubmission.formElement, formSubmission.submitter);
+ frame.delegate.loadResponse(response);
+ if (!formSubmission.isSafe) {
+ session.clearCache();
+ }
+ }
+ formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
+ this.element.delegate.loadResponse(fetchResponse);
+ session.clearCache();
+ }
+ formSubmissionErrored(formSubmission, error) {
+ console.error(error);
+ }
+ formSubmissionFinished({ formElement }) {
+ clearBusyState(formElement, this.findFrameElement(formElement));
+ }
+ allowsImmediateRender({ element: newFrame }, options) {
+ const event = dispatch("turbo:before-frame-render", {
+ target: this.element,
+ detail: Object.assign({ newFrame }, options),
+ cancelable: true,
+ });
+ const { defaultPrevented, detail: { render }, } = event;
+ if (this.view.renderer && render) {
+ this.view.renderer.renderElement = render;
+ }
+ return !defaultPrevented;
+ }
+ viewRenderedSnapshot(_snapshot, _isPreview) { }
+ preloadOnLoadLinksForView(element) {
+ session.preloadOnLoadLinksForView(element);
+ }
+ viewInvalidated() { }
+ willRenderFrame(currentElement, _newElement) {
+ this.previousFrameElement = currentElement.cloneNode(true);
+ }
+ async loadFrameResponse(fetchResponse, document) {
+ const newFrameElement = await this.extractForeignFrameElement(document.body);
+ if (newFrameElement) {
+ const snapshot = new Snapshot(newFrameElement);
+ const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
+ if (this.view.renderPromise)
+ await this.view.renderPromise;
+ this.changeHistory();
+ await this.view.render(renderer);
+ this.complete = true;
+ session.frameRendered(fetchResponse, this.element);
+ session.frameLoaded(this.element);
+ this.fetchResponseLoaded(fetchResponse);
+ }
+ else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
+ this.handleFrameMissingFromResponse(fetchResponse);
+ }
+ }
+ async visit(url) {
+ var _a;
+ const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
+ (_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
+ this.currentFetchRequest = request;
+ return new Promise((resolve) => {
+ this.resolveVisitPromise = () => {
+ this.resolveVisitPromise = () => { };
+ this.currentFetchRequest = null;
+ resolve();
+ };
+ request.perform();
+ });
+ }
+ navigateFrame(element, url, submitter) {
+ const frame = this.findFrameElement(element, submitter);
+ frame.delegate.proposeVisitIfNavigatedWithAction(frame, element, submitter);
+ this.withCurrentNavigationElement(element, () => {
+ frame.src = url;
+ });
+ }
+ proposeVisitIfNavigatedWithAction(frame, element, submitter) {
+ this.action = getVisitAction(submitter, element, frame);
+ if (this.action) {
+ const pageSnapshot = PageSnapshot.fromElement(frame).clone();
+ const { visitCachedSnapshot } = frame.delegate;
+ frame.delegate.fetchResponseLoaded = (fetchResponse) => {
+ if (frame.src) {
+ const { statusCode, redirected } = fetchResponse;
+ const responseHTML = frame.ownerDocument.documentElement.outerHTML;
+ const response = { statusCode, redirected, responseHTML };
+ const options = {
+ response,
+ visitCachedSnapshot,
+ willRender: false,
+ updateHistory: false,
+ restorationIdentifier: this.restorationIdentifier,
+ snapshot: pageSnapshot,
+ };
+ if (this.action)
+ options.action = this.action;
+ session.visit(frame.src, options);
+ }
+ };
+ }
+ }
+ changeHistory() {
+ if (this.action) {
+ const method = getHistoryMethodForAction(this.action);
+ session.history.update(method, expandURL(this.element.src || ""), this.restorationIdentifier);
+ }
+ }
+ async handleUnvisitableFrameResponse(fetchResponse) {
+ console.warn(`The response (${fetchResponse.statusCode}) from <turbo-frame id="${this.element.id}"> is performing a full page visit due to turbo-visit-control.`);
+ await this.visitResponse(fetchResponse.response);
+ }
+ willHandleFrameMissingFromResponse(fetchResponse) {
+ this.element.setAttribute("complete", "");
+ const response = fetchResponse.response;
+ const visit = async (url, options = {}) => {
+ if (url instanceof Response) {
+ this.visitResponse(url);
+ }
+ else {
+ session.visit(url, options);
+ }
+ };
+ const event = dispatch("turbo:frame-missing", {
+ target: this.element,
+ detail: { response, visit },
+ cancelable: true,
+ });
+ return !event.defaultPrevented;
+ }
+ handleFrameMissingFromResponse(fetchResponse) {
+ this.view.missing();
+ this.throwFrameMissingError(fetchResponse);
+ }
+ throwFrameMissingError(fetchResponse) {
+ const message = `The response (${fetchResponse.statusCode}) did not contain the expected <turbo-frame id="${this.element.id}"> and will be ignored. To perform a full page visit instead, set turbo-visit-control to reload.`;
+ throw new TurboFrameMissingError(message);
+ }
+ async visitResponse(response) {
+ const wrapped = new FetchResponse(response);
+ const responseHTML = await wrapped.responseHTML;
+ const { location, redirected, statusCode } = wrapped;
+ return session.visit(location, { response: { redirected, statusCode, responseHTML } });
+ }
+ findFrameElement(element, submitter) {
+ var _a;
+ const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
+ return (_a = getFrameElementById(id)) !== null && _a !== void 0 ? _a : this.element;
+ }
+ async extractForeignFrameElement(container) {
+ let element;
+ const id = CSS.escape(this.id);
+ try {
+ element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
+ if (element) {
+ return element;
+ }
+ element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL);
+ if (element) {
+ await element.loaded;
+ return await this.extractForeignFrameElement(element);
+ }
+ }
+ catch (error) {
+ console.error(error);
+ return new FrameElement();
+ }
+ return null;
+ }
+ formActionIsVisitable(form, submitter) {
+ const action = getAction(form, submitter);
+ return locationIsVisitable(expandURL(action), this.rootLocation);
+ }
+ shouldInterceptNavigation(element, submitter) {
+ const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
+ if (element instanceof HTMLFormElement && !this.formActionIsVisitable(element, submitter)) {
+ return false;
+ }
+ if (!this.enabled || id == "_top") {
+ return false;
+ }
+ if (id) {
+ const frameElement = getFrameElementById(id);
+ if (frameElement) {
+ return !frameElement.disabled;
+ }
+ }
+ if (!session.elementIsNavigatable(element)) {
+ return false;
+ }
+ if (submitter && !session.elementIsNavigatable(submitter)) {
+ return false;
+ }
+ return true;
+ }
+ get id() {
+ return this.element.id;
+ }
+ get enabled() {
+ return !this.element.disabled;
+ }
+ get sourceURL() {
+ if (this.element.src) {
+ return this.element.src;
+ }
+ }
+ set sourceURL(sourceURL) {
+ this.ignoringChangesToAttribute("src", () => {
+ this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
+ });
+ }
+ get loadingStyle() {
+ return this.element.loading;
+ }
+ get isLoading() {
+ return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
+ }
+ get complete() {
+ return this.element.hasAttribute("complete");
+ }
+ set complete(value) {
+ this.ignoringChangesToAttribute("complete", () => {
+ if (value) {
+ this.element.setAttribute("complete", "");
+ }
+ else {
+ this.element.removeAttribute("complete");
+ }
+ });
+ }
+ get isActive() {
+ return this.element.isActive && this.connected;
+ }
+ get rootLocation() {
+ var _a;
+ const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
+ const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
+ return expandURL(root);
+ }
+ isIgnoringChangesTo(attributeName) {
+ return this.ignoredAttributes.has(attributeName);
+ }
+ ignoringChangesToAttribute(attributeName, callback) {
+ this.ignoredAttributes.add(attributeName);
+ callback();
+ this.ignoredAttributes.delete(attributeName);
+ }
+ withCurrentNavigationElement(element, callback) {
+ this.currentNavigationElement = element;
+ callback();
+ delete this.currentNavigationElement;
+ }
+}
+function getFrameElementById(id) {
+ if (id != null) {
+ const element = document.getElementById(id);
+ if (element instanceof FrameElement) {
+ return element;
+ }
+ }
+}
+function activateElement(element, currentURL) {
+ if (element) {
+ const src = element.getAttribute("src");
+ if (src != null && currentURL != null && urlsAreEqual(src, currentURL)) {
+ throw new Error(`Matching <turbo-frame id="${element.id}"> element has a source URL which references itself`);
+ }
+ if (element.ownerDocument !== document) {
+ element = document.importNode(element, true);
+ }
+ if (element instanceof FrameElement) {
+ element.connectedCallback();
+ element.disconnectedCallback();
+ return element;
+ }
+ }
+}
+
+class StreamElement extends HTMLElement {
+ static async renderElement(newElement) {
+ await newElement.performAction();
+ }
+ async connectedCallback() {
+ try {
+ await this.render();
+ }
+ catch (error) {
+ console.error(error);
+ }
+ finally {
+ this.disconnect();
+ }
+ }
+ async render() {
+ var _a;
+ return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
+ const event = this.beforeRenderEvent;
+ if (this.dispatchEvent(event)) {
+ await nextAnimationFrame();
+ await event.detail.render(this);
+ }
+ })()));
+ }
+ disconnect() {
+ try {
+ this.remove();
+ }
+ catch (_a) { }
+ }
+ removeDuplicateTargetChildren() {
+ this.duplicateChildren.forEach((c) => c.remove());
+ }
+ get duplicateChildren() {
+ var _a;
+ const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id);
+ const newChildrenIds = [...(((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || [])].filter((c) => !!c.id).map((c) => c.id);
+ return existingChildren.filter((c) => newChildrenIds.includes(c.id));
+ }
+ get performAction() {
+ if (this.action) {
+ const actionFunction = StreamActions[this.action];
+ if (actionFunction) {
+ return actionFunction;
+ }
+ this.raise("unknown action");
+ }
+ this.raise("action attribute is missing");
+ }
+ get targetElements() {
+ if (this.target) {
+ return this.targetElementsById;
+ }
+ else if (this.targets) {
+ return this.targetElementsByQuery;
+ }
+ else {
+ this.raise("target or targets attribute is missing");
+ }
+ }
+ get templateContent() {
+ return this.templateElement.content.cloneNode(true);
+ }
+ get templateElement() {
+ if (this.firstElementChild === null) {
+ const template = this.ownerDocument.createElement("template");
+ this.appendChild(template);
+ return template;
+ }
+ else if (this.firstElementChild instanceof HTMLTemplateElement) {
+ return this.firstElementChild;
+ }
+ this.raise("first child element must be a <template> element");
+ }
+ get action() {
+ return this.getAttribute("action");
+ }
+ get target() {
+ return this.getAttribute("target");
+ }
+ get targets() {
+ return this.getAttribute("targets");
+ }
+ raise(message) {
+ throw new Error(`${this.description}: ${message}`);
+ }
+ get description() {
+ var _a, _b;
+ return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
+ }
+ get beforeRenderEvent() {
+ return new CustomEvent("turbo:before-stream-render", {
+ bubbles: true,
+ cancelable: true,
+ detail: { newStream: this, render: StreamElement.renderElement },
+ });
+ }
+ get targetElementsById() {
+ var _a;
+ const element = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.getElementById(this.target);
+ if (element !== null) {
+ return [element];
+ }
+ else {
+ return [];
+ }
+ }
+ get targetElementsByQuery() {
+ var _a;
+ const elements = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.querySelectorAll(this.targets);
+ if (elements.length !== 0) {
+ return Array.prototype.slice.call(elements);
+ }
+ else {
+ return [];
+ }
+ }
+}
+
+class StreamSourceElement extends HTMLElement {
+ constructor() {
+ super(...arguments);
+ this.streamSource = null;
+ }
+ connectedCallback() {
+ this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
+ connectStreamSource(this.streamSource);
+ }
+ disconnectedCallback() {
+ if (this.streamSource) {
+ disconnectStreamSource(this.streamSource);
+ }
+ }
+ get src() {
+ return this.getAttribute("src") || "";
+ }
+}
+
+FrameElement.delegateConstructor = FrameController;
+if (customElements.get("turbo-frame") === undefined) {
+ customElements.define("turbo-frame", FrameElement);
+}
+if (customElements.get("turbo-stream") === undefined) {
+ customElements.define("turbo-stream", StreamElement);
+}
+if (customElements.get("turbo-stream-source") === undefined) {
+ customElements.define("turbo-stream-source", StreamSourceElement);
+}
+
+(() => {
+ let element = document.currentScript;
+ if (!element)
+ return;
+ if (element.hasAttribute("data-turbo-suppress-warning"))
+ return;
+ element = element.parentElement;
+ while (element) {
+ if (element == document.body) {
+ return console.warn(unindent `
+ You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
+
+ Load your application’s JavaScript bundle inside the <head> element instead. <script> elements in <body> are evaluated with each page change.
+
+ For more information, see: https://turbo.hotwired.dev/handbook/building#working-with-script-elements
+
+ ——
+ Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
+ `, element.outerHTML);
+ }
+ element = element.parentElement;
+ }
+})();
+
+window.Turbo = Turbo;
+start();
+
+export { FrameElement, FrameLoadingStyle, FrameRenderer, PageRenderer, PageSnapshot, StreamActions, StreamElement, StreamSourceElement, cache, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };
diff --git a/share/html/Elements/HeaderJavascript b/share/html/Elements/HeaderJavascript
index 9bb99bb4d9..23fbc61ce0 100644
--- a/share/html/Elements/HeaderJavascript
+++ b/share/html/Elements/HeaderJavascript
@@ -53,6 +53,7 @@ $onload => undef
% for my $jsfile ( @js_files ) {
<script type="text/javascript" src="<%RT->Config->Get('WebPath')%><% $jsfile %>"></script>
% }
+<script type="module" src="/static/js/turbo.es2017-esm.min.js"></script>
<script type="text/javascript"><!--
% if ( $focus ) {
diff --git a/share/static/js/turbo.es2017-esm.min.js b/share/static/js/turbo.es2017-esm.min.js
new file mode 100644
index 0000000000..b80a58107c
--- /dev/null
+++ b/share/static/js/turbo.es2017-esm.min.js
@@ -0,0 +1,792 @@
+
+(function(){if(window.Reflect===undefined||window.customElements===undefined||window.customElements.polyfillWrapFlushCallback){return;}
+const BuiltInHTMLElement=HTMLElement;const wrapperForTheName={HTMLElement:function HTMLElement(){return Reflect.construct(BuiltInHTMLElement,[],this.constructor);},};window.HTMLElement=wrapperForTheName["HTMLElement"];HTMLElement.prototype=BuiltInHTMLElement.prototype;HTMLElement.prototype.constructor=HTMLElement;Object.setPrototypeOf(HTMLElement,BuiltInHTMLElement);})();(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);const submittersByForm=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===null||candidate===void 0?void 0: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&&/Apple Computer/.test(navigator.vendor)){prototype=window.SubmitEvent.prototype;}
+else if("SubmitEvent"in window){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;(function(FrameLoadingStyle){FrameLoadingStyle["eager"]="eager";FrameLoadingStyle["lazy"]="lazy";})(FrameLoadingStyle||(FrameLoadingStyle={}));class FrameElement extends HTMLElement{static get observedAttributes(){return["disabled","complete","loading","src"];}
+constructor(){super();this.loaded=Promise.resolve();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=="complete"){this.delegate.completeChanged();}
+else if(name=="src"){this.delegate.sourceURLChanged();}
+else{this.delegate.disabledChanged();}}
+get src(){return this.getAttribute("src");}
+set src(value){if(value){this.setAttribute("src",value);}
+else{this.removeAttribute("src");}}
+get loading(){return frameLoadingStyleFromString(this.getAttribute("loading")||"");}
+set loading(value){if(value){this.setAttribute("loading",value);}
+else{this.removeAttribute("loading");}}
+get disabled(){return this.hasAttribute("disabled");}
+set disabled(value){if(value){this.setAttribute("disabled","");}
+else{this.removeAttribute("disabled");}}
+get autoscroll(){return this.hasAttribute("autoscroll");}
+set autoscroll(value){if(value){this.setAttribute("autoscroll","");}
+else{this.removeAttribute("autoscroll");}}
+get complete(){return!this.delegate.isLoading;}
+get isActive(){return this.ownerDocument===document&&!this.isPreview;}
+get isPreview(){var _a,_b;return(_b=(_a=this.ownerDocument)===null||_a===void 0?void 0:_a.documentElement)===null||_b===void 0?void 0:_b.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(form,submitter){const action=(submitter===null||submitter===void 0?void 0: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(location,rootLocation){return isPrefixedBy(location,rootLocation)&&isHTML(location);}
+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+"/";}
+class FetchResponse{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(undefined);}}
+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 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]==undefined?"":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===null||element===void 0?void 0: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=2000){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){var _a;if(element instanceof Element){return(element.closest(selector)||findClosestRecursively(element.assignedSlot||((_a=element.getRootNode())===null||_a===void 0?void 0:_a.host),selector));}}
+var FetchMethod;(function(FetchMethod){FetchMethod[FetchMethod["get"]=0]="get";FetchMethod[FetchMethod["post"]=1]="post";FetchMethod[FetchMethod["put"]=2]="put";FetchMethod[FetchMethod["patch"]=3]="patch";FetchMethod[FetchMethod["delete"]=4]="delete";})(FetchMethod||(FetchMethod={}));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;}}
+class FetchRequest{constructor(delegate,method,location,body=new URLSearchParams(),target=null){this.abortController=new AbortController();this.resolveRequestPromise=(_value)=>{};this.delegate=delegate;this.method=method;this.headers=this.defaultHeaders;this.body=body;this.url=location;this.target=target;}
+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);await this.allowRequestToBeIntercepted(fetchOptions);try{this.delegate.requestStarted(this);const response=await fetch(this.url.href,fetchOptions);return await this.receive(response);}
+catch(error){if(error.name!=="AbortError"){if(this.willDelegateErrorHandling(error)){this.delegate.requestErrored(this,error);}
+throw error;}}
+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 fetchOptions(){var _a;return{method:FetchMethod[this.method].toUpperCase(),credentials:"same-origin",headers:this.headers,redirect:"follow",body:this.isSafe?null:this.body,signal:this.abortSignal,referrer:(_a=this.delegate.referrer)===null||_a===void 0?void 0:_a.href,};}
+get defaultHeaders(){return{Accept:"text/html, application/xhtml+xml",};}
+get isSafe(){return this.method===FetchMethod.get;}
+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,});if(event.defaultPrevented)
+await requestInterception;}
+willDelegateErrorHandling(error){const event=dispatch("turbo:fetch-request-error",{target:this.target,cancelable:true,detail:{request:this,error:error},});return!event.defaultPrevented;}}
+class AppearanceObserver{constructor(delegate,element){this.started=false;this.intersect=(entries)=>{const lastEntry=entries.slice(-1)[0];if(lastEntry===null||lastEntry===void 0?void 0:lastEntry.isIntersecting){this.delegate.elementAppearedInViewport(this.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);}}}
+class StreamMessage{static wrap(message){if(typeof message=="string"){return new this(createDocumentFragment(message));}
+else{return message;}}
+constructor(fragment){this.fragment=importStreamElements(fragment);}}
+StreamMessage.contentType="text/vnd.turbo-stream.html";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 FormSubmissionState;(function(FormSubmissionState){FormSubmissionState[FormSubmissionState["initialized"]=0]="initialized";FormSubmissionState[FormSubmissionState["requesting"]=1]="requesting";FormSubmissionState[FormSubmissionState["waiting"]=2]="waiting";FormSubmissionState[FormSubmissionState["receiving"]=3]="receiving";FormSubmissionState[FormSubmissionState["stopping"]=4]="stopping";FormSubmissionState[FormSubmissionState["stopped"]=5]="stopped";})(FormSubmissionState||(FormSubmissionState={}));var FormEnctype;(function(FormEnctype){FormEnctype["urlEncoded"]="application/x-www-form-urlencoded";FormEnctype["multipart"]="multipart/form-data";FormEnctype["plain"]="text/plain";})(FormEnctype||(FormEnctype={}));function formEnctypeFromString(encoding){switch(encoding.toLowerCase()){case FormEnctype.multipart:return FormEnctype.multipart;case FormEnctype.plain:return FormEnctype.plain;default:return FormEnctype.urlEncoded;}}
+class FormSubmission{static confirmMethod(message,_element,_submitter){return Promise.resolve(confirm(message));}
+constructor(delegate,formElement,submitter,mustRedirect=false){this.state=FormSubmissionState.initialized;this.delegate=delegate;this.formElement=formElement;this.submitter=submitter;this.formData=buildFormData(formElement,submitter);this.location=expandURL(this.action);if(this.method==FetchMethod.get){mergeFormDataEntries(this.location,[...this.body.entries()]);}
+this.fetchRequest=new FetchRequest(this,this.method,this.location,this.body,this.formElement);this.mustRedirect=mustRedirect;}
+get method(){var _a;const method=((_a=this.submitter)===null||_a===void 0?void 0:_a.getAttribute("formmethod"))||this.formElement.getAttribute("method")||"";return fetchMethodFromString(method.toLowerCase())||FetchMethod.get;}
+get action(){var _a;const formElementAction=typeof this.formElement.action==="string"?this.formElement.action:null;if((_a=this.submitter)===null||_a===void 0?void 0:_a.hasAttribute("formaction")){return this.submitter.getAttribute("formaction")||"";}
+else{return this.formElement.getAttribute("action")||formElementAction||"";}}
+get body(){if(this.enctype==FormEnctype.urlEncoded||this.method==FetchMethod.get){return new URLSearchParams(this.stringFormData);}
+else{return this.formData;}}
+get enctype(){var _a;return formEnctypeFromString(((_a=this.submitter)===null||_a===void 0?void 0:_a.getAttribute("formenctype"))||this.formElement.enctype);}
+get isSafe(){return this.fetchRequest.isSafe;}
+get stringFormData(){return[...this.formData].reduce((entries,[name,value])=>{return entries.concat(typeof value=="string"?[[name,value]]:[]);},[]);}
+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;}}
+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){var _a;this.state=FormSubmissionState.waiting;(_a=this.submitter)===null||_a===void 0?void 0:_a.setAttribute("disabled","");this.setSubmitsWith();dispatch("turbo:submit-start",{target:this.formElement,detail:{formSubmission:this},});this.delegate.formSubmissionStarted(this);}
+requestPreventedHandlingResponse(request,response){this.result={success:response.succeeded,fetchResponse:response};}
+requestSucceededWithResponse(request,response){if(response.clientError||response.serverError){this.delegate.formSubmissionFailedWithResponse(this,response);}
+else if(this.requestMustRedirect(request)&&responseSucceededWithoutRedirect(response)){const error=new Error("Form responses must redirect to another location");this.delegate.formSubmissionErrored(this,error);}
+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,error){this.result={success:false,error};this.delegate.formSubmissionErrored(this,error);}
+requestFinished(_request){var _a;this.state=FormSubmissionState.stopped;(_a=this.submitter)===null||_a===void 0?void 0:_a.removeAttribute("disabled");this.resetSubmitterText();dispatch("turbo:submit-end",{target:this.formElement,detail:Object.assign({formSubmission:this},this.result),});this.delegate.formSubmissionFinished(this);}
+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(){var _a;return(_a=this.submitter)===null||_a===void 0?void 0:_a.getAttribute("data-turbo-submits-with");}}
+function buildFormData(formElement,submitter){const formData=new FormData(formElement);const name=submitter===null||submitter===void 0?void 0:submitter.getAttribute("name");const value=submitter===null||submitter===void 0?void 0: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((cookie)=>cookie.startsWith(cookieName));if(cookie){const value=cookie.split("=").slice(1).join("=");return value?decodeURIComponent(value):undefined;}}}
+function responseSucceededWithoutRedirect(response){return response.statusCode==200&&!response.redirected;}
+function mergeFormDataEntries(url,entries){const searchParams=new URLSearchParams();for(const[name,value]of entries){if(value instanceof File)
+continue;searchParams.append(name,value);}
+url.search=searchParams.toString();return url;}
+class Snapshot{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(){const inertDisabledOrHidden="[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";for(const element of this.element.querySelectorAll("[autofocus]")){if(element.closest(inertDisabledOrHidden)==null)
+return element;else
+continue;}
+return null;}
+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]");}
+class FormSubmitObserver{constructor(delegate,eventTarget){this.started=false;this.submitCaptured=()=>{this.eventTarget.removeEventListener("submit",this.submitBubbled,false);this.eventTarget.addEventListener("submit",this.submitBubbled,false);};this.submitBubbled=((event)=>{if(!event.defaultPrevented){const form=event.target instanceof HTMLFormElement?event.target:undefined;const submitter=event.submitter||undefined;if(form&&submissionDoesNotDismissDialog(form,submitter)&&submissionDoesNotTargetIFrame(form,submitter)&&this.delegate.willSubmitForm(form,submitter)){event.preventDefault();event.stopImmediatePropagation();this.delegate.formSubmitted(form,submitter);}}});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;}}}
+function submissionDoesNotDismissDialog(form,submitter){const method=(submitter===null||submitter===void 0?void 0:submitter.getAttribute("formmethod"))||form.getAttribute("method");return method!="dialog";}
+function submissionDoesNotTargetIFrame(form,submitter){if((submitter===null||submitter===void 0?void 0:submitter.hasAttribute("formtarget"))||form.hasAttribute("target")){const target=(submitter===null||submitter===void 0?void 0:submitter.getAttribute("formtarget"))||form.target;for(const element of document.getElementsByName(target)){if(element instanceof HTMLIFrameElement)
+return false;}
+return true;}
+else{return true;}}
+class View{constructor(delegate,element){this.resolveRenderPromise=(_value)=>{};this.resolveInterceptionPromise=(_value)=>{};this.delegate=delegate;this.element=element;}
+scrollToAnchor(anchor){const element=this.snapshot.getElementForAnchor(anchor);if(element){this.scrollToElement(element);this.focusElement(element);}
+else{this.scrollToPosition({x:0,y:0});}}
+scrollToAnchorFromLocation(location){this.scrollToAnchor(getAnchor(location));}
+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;}
+async render(renderer){const{isPreview,shouldRender,newSnapshot:snapshot}=renderer;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};const immediateRender=this.delegate.allowsImmediateRender(snapshot,options);if(!immediateRender)
+await renderInterception;await this.renderSnapshot(renderer);this.delegate.viewRenderedSnapshot(snapshot,isPreview);this.delegate.preloadOnLoadLinksForView(this.element);this.finishRenderingSnapshot(renderer);}
+finally{delete this.renderer;this.resolveRenderPromise(undefined);delete this.renderPromise;}}
+else{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");}}
+async renderSnapshot(renderer){await renderer.render();}
+finishRenderingSnapshot(renderer){renderer.finishRendering();}}
+class FrameView extends View{missing(){this.element.innerHTML=`<strong class="turbo-frame-error">Content missing</strong>`;}
+get snapshot(){return new Snapshot(this.element);}}
+class LinkInterceptor{constructor(delegate,element){this.clickBubbled=(event)=>{if(this.respondsToEventTarget(event.target)){this.clickEvent=event;}
+else{delete this.clickEvent;}};this.linkClicked=((event)=>{if(this.clickEvent&&this.respondsToEventTarget(event.target)&&event.target instanceof Element){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;});this.willVisit=((_event)=>{delete this.clickEvent;});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);}
+respondsToEventTarget(target){const element=target instanceof Element?target:target instanceof Node?target.parentElement:null;return element&&element.closest("turbo-frame, html")==this.element;}}
+class LinkClickObserver{constructor(delegate,eventTarget){this.started=false;this.clickCaptured=()=>{this.eventTarget.removeEventListener("click",this.clickBubbled,false);this.eventTarget.addEventListener("click",this.clickBubbled,false);};this.clickBubbled=(event)=>{if(event instanceof MouseEvent&&this.clickEventIsSignificant(event)){const target=(event.composedPath&&event.composedPath()[0])||event.target;const link=this.findLinkFromClickTarget(target);if(link&&doesNotTargetIFrame(link)){const location=this.getLocationForLink(link);if(this.delegate.willFollowLinkToLocation(link,location,event)){event.preventDefault();this.delegate.followedLinkToLocation(link,location);}}}};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;}}
+clickEventIsSignificant(event){return!((event.target&&event.target.isContentEditable)||event.defaultPrevented||event.which>1||event.altKey||event.ctrlKey||event.metaKey||event.shiftKey);}
+findLinkFromClickTarget(target){return findClosestRecursively(target,"a[href]:not([target^=_]):not([download])");}
+getLocationForLink(link){return expandURL(link.getAttribute("href")||"");}}
+function doesNotTargetIFrame(anchor){if(anchor.hasAttribute("target")){for(const element of document.getElementsByName(anchor.target)){if(element instanceof HTMLIFrameElement)
+return false;}
+return true;}
+else{return true;}}
+class FormLinkClickObserver{constructor(delegate,element){this.delegate=delegate;this.linkInterceptor=new LinkClickObserver(this,element);}
+start(){this.linkInterceptor.start();}
+stop(){this.linkInterceptor.stop();}
+willFollowLinkToLocation(link,location,originalEvent){return(this.delegate.willSubmitFormLinkToLocation(link,location,originalEvent)&&link.hasAttribute("data-turbo-method"));}
+followedLinkToLocation(link,location){const form=document.createElement("form");const type="hidden";for(const[name,value]of location.searchParams){form.append(Object.assign(document.createElement("input"),{type,name,value}));}
+const action=Object.assign(location,{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,location,form);document.body.appendChild(form);form.addEventListener("turbo:submit-end",()=>form.remove(),{once:true});requestAnimationFrame(()=>form.requestSubmit());}}
+class Bardo{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===null||placeholder===void 0?void 0: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;}
+class Renderer{constructor(currentSnapshot,newSnapshot,renderElement,isPreview,willRender=true){this.activeElement=null;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 reloadReason(){return;}
+prepareToRender(){return;}
+finishRendering(){if(this.resolvingFunctions){this.resolvingFunctions.resolve();delete this.resolvingFunctions;}}
+async preservingPermanentElements(callback){await Bardo.preservingPermanentElements(this,this.permanentElementMap,callback);}
+focusFirstAutofocusableElement(){const element=this.connectedSnapshot.firstAutofocusableElement;if(elementIsFocusable(element)){element.focus();}}
+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);}}
+function elementIsFocusable(element){return element&&typeof element.focus=="function";}
+class FrameRenderer extends Renderer{static renderElement(currentElement,newElement){var _a;const destinationRange=document.createRange();destinationRange.selectNodeContents(currentElement);destinationRange.deleteContents();const frameElement=newElement;const sourceRange=(_a=frameElement.ownerDocument)===null||_a===void 0?void 0:_a.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 nextAnimationFrame();this.preservingPermanentElements(()=>{this.loadFrameElement();});this.scrollFrameIntoView();await nextAnimationFrame();this.focusFirstAutofocusableElement();await nextAnimationFrame();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;}}
+class ProgressBar{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);
+ }
+ `;}
+constructor(){this.hiding=false;this.value=0;this.visible=false;this.trickle=()=>{this.setValue(this.value+Math.random()/100);};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();}
+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;}
+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");}}
+ProgressBar.animationDuration=300;class HeadSnapshot extends Snapshot{constructor(){super(...arguments);this.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 Object.assign(Object.assign({},result),{[outerHTML]:Object.assign(Object.assign({},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;},undefined);}}
+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;}
+class PageSnapshot extends Snapshot{static fromHTMLString(html=""){return this.fromDocument(parseHTMLDocument(html));}
+static fromElement(element){return this.fromDocument(element.ownerDocument);}
+static fromDocument({head,body}){return new this(body,new HeadSnapshot(head));}
+constructor(element,headSnapshot){super(element);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(clonedElement,this.headSnapshot);}
+get headElement(){return this.headSnapshot.element;}
+get rootLocation(){var _a;const root=(_a=this.getSetting("root"))!==null&&_a!==void 0?_a:"/";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";}
+getSetting(name){return this.headSnapshot.getMetaValue(`turbo-${name}`);}}
+var TimingMetric;(function(TimingMetric){TimingMetric["visitStart"]="visitStart";TimingMetric["requestStart"]="requestStart";TimingMetric["requestEnd"]="requestEnd";TimingMetric["visitEnd"]="visitEnd";})(TimingMetric||(TimingMetric={}));var VisitState;(function(VisitState){VisitState["initialized"]="initialized";VisitState["started"]="started";VisitState["canceled"]="canceled";VisitState["failed"]="failed";VisitState["completed"]="completed";})(VisitState||(VisitState={}));const defaultOptions={action:"advance",historyChanged:false,visitCachedSnapshot:()=>{},willRender:true,updateHistory:true,shouldCacheSnapshot:true,acceptsStreamResponse:false,};var SystemStatusCode;(function(SystemStatusCode){SystemStatusCode[SystemStatusCode["networkFailure"]=0]="networkFailure";SystemStatusCode[SystemStatusCode["timeoutFailure"]=-1]="timeoutFailure";SystemStatusCode[SystemStatusCode["contentTypeMismatch"]=-2]="contentTypeMismatch";})(SystemStatusCode||(SystemStatusCode={}));class Visit{construct
or(delegate,location,restorationIdentifier,options={}){this.identifier=uuid();this.timingMetrics={};this.followedRedirect=false;this.historyChanged=false;this.scrolled=false;this.shouldCacheSnapshot=true;this.acceptsStreamResponse=false;this.snapshotCached=false;this.state=VisitState.initialized;this.delegate=delegate;this.location=location;this.restorationIdentifier=restorationIdentifier||uuid();const{action,historyChanged,referrer,snapshot,snapshotHTML,response,visitCachedSnapshot,willRender,updateHistory,shouldCacheSnapshot,acceptsStreamResponse,}=Object.assign(Object.assign({},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.visitCachedSnapshot=visitCachedSnapshot;this.willRender=willRender;this.updateHistory=updateHistory;this.scrolled=!willRender;this.shouldCa
cheSnapshot=shouldCacheSnapshot;this.acceptsStreamResponse=acceptsStreamResponse;}
+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.state=VisitState.completed;this.followRedirect();if(!this.followedRedirect){this.adapter.visitCompleted(this);this.delegate.visitCompleted(this);}}}
+fail(){if(this.state==VisitState.started){this.state=VisitState.failed;this.adapter.visitFailed(this);}}
+changeHistory(){var _a;if(!this.historyChanged&&this.updateHistory){const actionForHistory=this.location.href===((_a=this.referrer)===null||_a===void 0?void 0:_a.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){await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML),false,this.willRender,this);this.performScroll();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.adapter.visitRendered(this);}
+else{if(this.view.renderPromise)
+await this.view.renderPromise;await this.view.renderPage(snapshot,isPreview,this.willRender,this);this.performScroll();this.adapter.visitRendered(this);if(!isPreview){this.complete();}}});}}
+followRedirect(){var _a;if(this.redirectedToLocation&&!this.followedRedirect&&((_a=this.response)===null||_a===void 0?void 0:_a.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);});}}
+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==undefined){this.recordResponse({statusCode:SystemStatusCode.contentTypeMismatch,redirected,});}
+else{this.redirectedToLocation=response.redirected?response.location:undefined;this.recordResponse({statusCode:statusCode,responseHTML,redirected});}}
+async requestFailedWithResponse(request,response){const responseHTML=await response.responseHTML;const{redirected,statusCode}=response;if(responseHTML==undefined){this.recordResponse({statusCode:SystemStatusCode.contentTypeMismatch,redirected,});}
+else{this.recordResponse({statusCode:statusCode,responseHTML,redirected});}}
+requestErrored(_request,_error){this.recordResponse({statusCode:SystemStatusCode.networkFailure,redirected:false,});}
+requestFinished(){this.finishRequest();}
+performScroll(){if(!this.scrolled&&!this.view.forceReloaded){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;}}
+recordTimingMetric(metric){this.timingMetrics[metric]=new Date().getTime();}
+getTimingMetrics(){return Object.assign({},this.timingMetrics);}
+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();await new Promise((resolve)=>{this.frame=requestAnimationFrame(()=>resolve());});await callback();delete this.frame;}
+cancelRender(){if(this.frame){cancelAnimationFrame(this.frame);delete this.frame;}}}
+function isSuccessful(statusCode){return statusCode>=200&&statusCode<300;}
+class BrowserAdapter{constructor(session){this.progressBar=new ProgressBar();this.showProgressBar=()=>{this.progressBar.show();};this.session=session;}
+visitProposedToLocation(location,options){this.navigator.startVisit(location,(options===null||options===void 0?void 0:options.restorationIdentifier)||uuid(),options);}
+visitStarted(visit){this.location=visit.location;visit.loadCachedSnapshot();visit.issueRequest();visit.goToSamePageAnchor();}
+visitRequestStarted(visit){this.progressBar.setValue(0);if(visit.hasCachedSnapshot()||visit.action!="restore"){this.showVisitProgressBarAfterDelay();}
+else{this.showProgressBar();}}
+visitRequestCompleted(visit){visit.loadResponse();}
+visitRequestFailedWithStatusCode(visit,statusCode){switch(statusCode){case SystemStatusCode.networkFailure:case SystemStatusCode.timeoutFailure:case SystemStatusCode.contentTypeMismatch:return this.reload({reason:"request_failed",context:{statusCode,},});default:return visit.loadResponse();}}
+visitRequestFinished(_visit){this.progressBar.setValue(1);this.hideVisitProgressBar();}
+visitCompleted(_visit){}
+pageInvalidated(reason){this.reload(reason);}
+visitFailed(_visit){}
+visitRendered(_visit){}
+formSubmissionStarted(_formSubmission){this.progressBar.setValue(0);this.showFormProgressBarAfterDelay();}
+formSubmissionFinished(_formSubmission){this.progressBar.setValue(1);this.hideFormProgressBar();}
+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;}}
+reload(reason){var _a;dispatch("turbo:reload",{detail:reason});window.location.href=((_a=this.location)===null||_a===void 0?void 0:_a.toString())||window.location.href;}
+get navigator(){return this.session.navigator;}}
+class CacheObserver{constructor(){this.selector="[data-turbo-temporary]";this.deprecatedSelector="[data-turbo-cache=false]";this.started=false;this.removeTemporaryElements=((_event)=>{for(const element of this.temporaryElements){element.remove();}});}
+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);}}
+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];}}
+class FrameRedirector{constructor(session,element){this.session=session;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();}
+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);}}
+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){var _a;const action=getAction(form,submitter);const meta=this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);const rootLocation=expandURL((_a=meta===null||meta===void 0?void 0:meta.content)!==null&&_a!==void 0?_a:"/");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===null||submitter===void 0?void 0: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;}}}}
+class History{constructor(delegate){this.restorationIdentifier=uuid();this.restorationData={};this.started=false;this.pageLoaded=false;this.onPopState=(event)=>{if(this.shouldHandlePopState()){const{turbo}=event.state||{};if(turbo){this.location=new URL(window.location.href);const{restorationIdentifier}=turbo;this.restorationIdentifier=restorationIdentifier;this.delegate.historyPoppedToLocationWithRestorationIdentifier(this.location,restorationIdentifier);}}};this.onPageLoad=async(_event)=>{await nextMicrotask();this.pageLoaded=true;};this.delegate=delegate;}
+start(){if(!this.started){addEventListener("popstate",this.onPopState,false);addEventListener("load",this.onPageLoad,false);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(location,restorationIdentifier){this.update(history.pushState,location,restorationIdentifier);}
+replace(location,restorationIdentifier){this.update(history.replaceState,location,restorationIdentifier);}
+update(method,location,restorationIdentifier=uuid()){const state={turbo:{restorationIdentifier}};method.call(history,state,"",location.href);this.location=location;this.restorationIdentifier=restorationIdentifier;}
+getRestorationDataForIdentifier(restorationIdentifier){return this.restorationData[restorationIdentifier]||{};}
+updateRestorationData(additionalData){const{restorationIdentifier}=this;const restorationData=this.restorationData[restorationIdentifier];this.restorationData[restorationIdentifier]=Object.assign(Object.assign({},restorationData),additionalData);}
+assumeControlOfScrollRestoration(){var _a;if(!this.previousScrollRestoration){this.previousScrollRestoration=(_a=history.scrollRestoration)!==null&&_a!==void 0?_a:"auto";history.scrollRestoration="manual";}}
+relinquishControlOfScrollRestoration(){if(this.previousScrollRestoration){history.scrollRestoration=this.previousScrollRestoration;delete this.previousScrollRestoration;}}
+shouldHandlePopState(){return this.pageIsLoaded();}
+pageIsLoaded(){return this.pageLoaded||document.readyState=="complete";}}
+class Navigator{constructor(delegate){this.delegate=delegate;}
+proposeVisit(location,options={}){if(this.delegate.allowsVisitingLocationWithAction(location,options.action)){if(locationIsVisitable(location,this.view.snapshot.rootLocation)){this.delegate.visitProposedToLocation(location,options);}
+else{window.location.href=location.toString();}}}
+startVisit(locatable,restorationIdentifier,options={}){this.stop();this.currentVisit=new Visit(this,expandURL(locatable),restorationIdentifier,Object.assign({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 history(){return this.delegate.history;}
+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);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);}
+this.view.scrollToTop();this.view.clearSnapshotCache();}}
+formSubmissionErrored(formSubmission,error){console.error(error);}
+formSubmissionFinished(formSubmission){if(typeof this.adapter.formSubmissionFinished==="function"){this.adapter.formSubmissionFinished(formSubmission);}}
+visitStarted(visit){this.delegate.visitStarted(visit);}
+visitCompleted(visit){this.delegate.visitCompleted(visit);}
+locationWithActionIsSamePage(location,action){const anchor=getAnchor(location);const currentAnchor=getAnchor(this.view.lastRenderedLocation);const isRestorationToTop=action==="restore"&&typeof anchor==="undefined";return(action!=="replace"&&getRequestURL(location)===getRequestURL(this.view.lastRenderedLocation)&&(isRestorationToTop||(anchor!=null&&anchor!==currentAnchor)));}
+visitScrolledToSamePageLocation(oldURL,newURL){this.delegate.visitScrolledToSamePageLocation(oldURL,newURL);}
+get location(){return this.history.location;}
+get restorationIdentifier(){return this.history.restorationIdentifier;}
+getActionForFormSubmission({submitter,formElement}){return getVisitAction(submitter,formElement)||"advance";}}
+var PageStage;(function(PageStage){PageStage[PageStage["initial"]=0]="initial";PageStage[PageStage["loading"]=1]="loading";PageStage[PageStage["interactive"]=2]="interactive";PageStage[PageStage["complete"]=3]="complete";})(PageStage||(PageStage={}));class PageObserver{constructor(delegate){this.stage=PageStage.initial;this.started=false;this.interpretReadyState=()=>{const{readyState}=this;if(readyState=="interactive"){this.pageIsInteractive();}
+else if(readyState=="complete"){this.pageIsComplete();}};this.pageWillUnload=()=>{this.delegate.pageWillUnload();};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;}}
+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();}}
+get readyState(){return document.readyState;}}
+class ScrollObserver{constructor(delegate){this.started=false;this.onScroll=()=>{this.updatePosition({x:window.pageXOffset,y:window.pageYOffset});};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;}}
+updatePosition(position){this.delegate.scrollPositionChanged(position);}}
+class StreamMessageRenderer{render({fragment}){Bardo.preservingPermanentElements(this,getPermanentElementMapForFragment(fragment),()=>document.documentElement.appendChild(fragment));}
+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;}
+class StreamObserver{constructor(delegate){this.sources=new Set();this.started=false;this.inspectFetchResponse=((event)=>{const response=fetchResponseFromEvent(event);if(response&&fetchResponseIsStream(response)){event.preventDefault();this.receiveMessageResponse(response);}});this.receiveMessageEvent=(event)=>{if(this.started&&typeof event.data=="string"){this.receiveMessageHTML(event.data);}};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);}
+async receiveMessageResponse(response){const html=await response.responseHTML;if(html){this.receiveMessageHTML(html);}}
+receiveMessageHTML(html){this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));}}
+function fetchResponseFromEvent(event){var _a;const fetchResponse=(_a=event.detail)===null||_a===void 0?void 0:_a.fetchResponse;if(fetchResponse instanceof FetchResponse){return fetchResponse;}}
+function fetchResponseIsStream(response){var _a;const contentType=(_a=response.contentType)!==null&&_a!==void 0?_a:"";return contentType.startsWith(StreamMessage.contentType);}
+class ErrorRenderer 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");}}
+class PageRenderer extends Renderer{static renderElement(currentElement,newElement){if(document.body&&newElement instanceof HTMLBodyElement){document.body.replaceWith(newElement);}
+else{document.documentElement.appendChild(newElement);}}
+get shouldRender(){return this.newSnapshot.isVisitable&&this.trackedElementsAreIdentical;}
+get reloadReason(){if(!this.newSnapshot.isVisitable){return{reason:"turbo_visit_control_is_reload",};}
+if(!this.trackedElementsAreIdentical){return{reason:"tracked_element_mismatch",};}}
+async prepareToRender(){await this.mergeHead();}
+async render(){if(this.willRender){await this.replaceBody();}}
+finishRendering(){super.finishRendering();if(!this.isPreview){this.focusFirstAutofocusableElement();}}
+get currentHeadSnapshot(){return this.currentSnapshot.headSnapshot;}
+get newHeadSnapshot(){return this.newSnapshot.headSnapshot;}
+get newElement(){return this.newSnapshot.element;}
+async mergeHead(){const mergedHeadElements=this.mergeProvisionalElements();const newStylesheetElements=this.copyNewHeadStylesheetElements();this.copyNewHeadScriptElements();await mergedHeadElements;await newStylesheetElements;}
+async replaceBody(){await this.preservingPermanentElements(async()=>{this.activateNewBody();await this.assignNewBody();});}
+get trackedElementsAreIdentical(){return this.currentHeadSnapshot.trackedElementSignature==this.newHeadSnapshot.trackedElementSignature;}
+async copyNewHeadStylesheetElements(){const loadingElements=[];for(const element of this.newHeadStylesheetElements){loadingElements.push(waitForLoad(element));document.head.appendChild(element);}
+await Promise.all(loadingElements);}
+copyNewHeadScriptElements(){for(const element of this.newHeadScriptElements){document.head.appendChild(activateScriptElement(element));}}
+async mergeProvisionalElements(){const newHeadElements=[...this.newHeadProvisionalElements];for(const element of this.currentHeadProvisionalElements){if(!this.isCurrentElementInElementList(element,newHeadElements)){document.head.removeChild(element);}}
+for(const element of newHeadElements){document.head.appendChild(element);}}
+isCurrentElementInElementList(element,elementList){for(const[index,newElement]of elementList.entries()){if(element.tagName=="TITLE"){if(newElement.tagName!="TITLE"){continue;}
+if(element.innerHTML==newElement.innerHTML){elementList.splice(index,1);return true;}}
+if(newElement.isEqualNode(element)){elementList.splice(index,1);return true;}}
+return false;}
+removeCurrentHeadProvisionalElements(){for(const element of this.currentHeadProvisionalElements){document.head.removeChild(element);}}
+copyNewHeadProvisionalElements(){for(const element of this.newHeadProvisionalElements){document.head.appendChild(element);}}
+activateNewBody(){document.adoptNode(this.newElement);this.activateNewBodyScriptElements();}
+activateNewBodyScriptElements(){for(const inertScriptElement of this.newBodyScriptElements){const activatedScriptElement=activateScriptElement(inertScriptElement);inertScriptElement.replaceWith(activatedScriptElement);}}
+async assignNewBody(){await this.renderElement(this.currentElement,this.newElement);}
+get newHeadStylesheetElements(){return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);}
+get newHeadScriptElements(){return this.newHeadSnapshot.getScriptElementsNotInSnapshot(this.currentHeadSnapshot);}
+get currentHeadProvisionalElements(){return this.currentHeadSnapshot.provisionalElements;}
+get newHeadProvisionalElements(){return this.newHeadSnapshot.provisionalElements;}
+get newBodyScriptElements(){return this.newElement.querySelectorAll("script");}}
+class SnapshotCache{constructor(size){this.keys=[];this.snapshots={};this.size=size;}
+has(location){return toCacheKey(location)in this.snapshots;}
+get(location){if(this.has(location)){const snapshot=this.read(location);this.touch(location);return snapshot;}}
+put(location,snapshot){this.write(location,snapshot);this.touch(location);return snapshot;}
+clear(){this.snapshots={};}
+read(location){return this.snapshots[toCacheKey(location)];}
+write(location,snapshot){this.snapshots[toCacheKey(location)]=snapshot;}
+touch(location){const key=toCacheKey(location);const index=this.keys.indexOf(key);if(index>-1)
+this.keys.splice(index,1);this.keys.unshift(key);this.trim();}
+trim(){for(const key of this.keys.splice(this.size)){delete this.snapshots[key];}}}
+class PageView extends View{constructor(){super(...arguments);this.snapshotCache=new SnapshotCache(10);this.lastRenderedLocation=new URL(location.href);this.forceReloaded=false;}
+renderPage(snapshot,isPreview=false,willRender=true,visit){const renderer=new PageRenderer(this.snapshot,snapshot,PageRenderer.renderElement,isPreview,willRender);if(!renderer.shouldRender){this.forceReloaded=true;}
+else{visit===null||visit===void 0?void 0:visit.changeHistory();}
+return this.render(renderer);}
+renderError(snapshot,visit){visit===null||visit===void 0?void 0:visit.changeHistory();const renderer=new ErrorRenderer(this.snapshot,snapshot,ErrorRenderer.renderElement,false);return this.render(renderer);}
+clearSnapshotCache(){this.snapshotCache.clear();}
+async cacheSnapshot(snapshot=this.snapshot){if(snapshot.isCacheable){this.delegate.viewWillCacheSnapshot();const{lastRenderedLocation:location}=this;await nextEventLoopTick();const cachedSnapshot=snapshot.clone();this.snapshotCache.put(location,cachedSnapshot);return cachedSnapshot;}}
+getCachedSnapshotForLocation(location){return this.snapshotCache.get(location);}
+get snapshot(){return PageSnapshot.fromElement(this.element);}}
+class Preloader{constructor(delegate){this.selector="a[data-turbo-preload]";this.delegate=delegate;}
+get snapshotCache(){return this.delegate.navigator.view.snapshotCache;}
+start(){if(document.readyState==="loading"){return document.addEventListener("DOMContentLoaded",()=>{this.preloadOnLoadLinksForView(document.body);});}
+else{this.preloadOnLoadLinksForView(document.body);}}
+preloadOnLoadLinksForView(element){for(const link of element.querySelectorAll(this.selector)){this.preloadURL(link);}}
+async preloadURL(link){const location=new URL(link.href);if(this.snapshotCache.has(location)){return;}
+try{const response=await fetch(location.toString(),{headers:{"VND.PREFETCH":"true",Accept:"text/html"}});const responseText=await response.text();const snapshot=PageSnapshot.fromHTMLString(responseText);this.snapshotCache.put(location,snapshot);}
+catch(_){}}}
+class Session{constructor(){this.navigator=new Navigator(this);this.history=new History(this);this.preloader=new Preloader(this);this.view=new PageView(this,document.documentElement);this.adapter=new BrowserAdapter(this);this.pageObserver=new PageObserver(this);this.cacheObserver=new CacheObserver();this.linkClickObserver=new LinkClickObserver(this,window);this.formSubmitObserver=new FormSubmitObserver(this,document);this.scrollObserver=new ScrollObserver(this);this.streamObserver=new StreamObserver(this);this.formLinkClickObserver=new FormLinkClickObserver(this,document.documentElement);this.frameRedirector=new FrameRedirector(this,document.documentElement);this.streamMessageRenderer=new StreamMessageRenderer();this.drive=true;this.enabled=true;this.progressBarDelay=500;this.started=false;this.formMode="on";}
+start(){if(!this.started){this.pageObserver.start();this.cacheObserver.start();this.formLinkClickObserver.start();this.linkClickObserver.start();this.formSubmitObserver.start();this.scrollObserver.start();this.streamObserver.start();this.frameRedirector.start();this.history.start();this.preloader.start();this.started=true;this.enabled=true;}}
+disable(){this.enabled=false;}
+stop(){if(this.started){this.pageObserver.stop();this.cacheObserver.stop();this.formLinkClickObserver.stop();this.linkClickObserver.stop();this.formSubmitObserver.stop();this.scrollObserver.stop();this.streamObserver.stop();this.frameRedirector.stop();this.history.stop();this.started=false;}}
+registerAdapter(adapter){this.adapter=adapter;}
+visit(location,options={}){const frameElement=options.frame?document.getElementById(options.frame):null;if(frameElement instanceof FrameElement){frameElement.src=location.toString();frameElement.loaded;}
+else{this.navigator.proposeVisit(expandURL(location),options);}}
+connectStreamSource(source){this.streamObserver.connectStreamSource(source);}
+disconnectStreamSource(source){this.streamObserver.disconnectStreamSource(source);}
+renderStreamMessage(message){this.streamMessageRenderer.render(StreamMessage.wrap(message));}
+clearCache(){this.view.clearSnapshotCache();}
+setProgressBarDelay(delay){this.progressBarDelay=delay;}
+setFormMode(mode){this.formMode=mode;}
+get location(){return this.history.location;}
+get restorationIdentifier(){return this.history.restorationIdentifier;}
+historyPoppedToLocationWithRestorationIdentifier(location,restorationIdentifier){if(this.enabled){this.navigator.startVisit(location,restorationIdentifier,{action:"restore",historyChanged:true,});}
+else{this.adapter.pageInvalidated({reason:"turbo_disabled",});}}
+scrollPositionChanged(position){this.history.updateRestorationData({scrollPosition:position});}
+willSubmitFormLinkToLocation(link,location){return this.elementIsNavigatable(link)&&locationIsVisitable(location,this.snapshot.rootLocation);}
+submittedFormLinkToLocation(){}
+willFollowLinkToLocation(link,location,event){return(this.elementIsNavigatable(link)&&locationIsVisitable(location,this.snapshot.rootLocation)&&this.applicationAllowsFollowingLinkToLocation(link,location,event));}
+followedLinkToLocation(link,location){const action=this.getActionForLink(link);const acceptsStreamResponse=link.hasAttribute("data-turbo-stream");this.visit(location.href,{action,acceptsStreamResponse});}
+allowsVisitingLocationWithAction(location,action){return this.locationWithActionIsSamePage(location,action)||this.applicationAllowsVisitingLocation(location);}
+visitProposedToLocation(location,options){extendURLWithDeprecatedProperties(location);this.adapter.visitProposedToLocation(location,options);}
+visitStarted(visit){if(!visit.acceptsStreamResponse){markAsBusy(document.documentElement);}
+extendURLWithDeprecatedProperties(visit.location);if(!visit.silent){this.notifyApplicationAfterVisitingLocation(visit.location,visit.action);}}
+visitCompleted(visit){clearBusyState(document.documentElement);this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());}
+locationWithActionIsSamePage(location,action){return this.navigator.locationWithActionIsSamePage(location,action);}
+visitScrolledToSamePageLocation(oldURL,newURL){this.notifyApplicationAfterVisitingSamePageLocation(oldURL,newURL);}
+willSubmitForm(form,submitter){const action=getAction(form,submitter);return(this.submissionIsNavigatable(form,submitter)&&locationIsVisitable(expandURL(action),this.snapshot.rootLocation));}
+formSubmitted(form,submitter){this.navigator.submitForm(form,submitter);}
+pageBecameInteractive(){this.view.lastRenderedLocation=this.location;this.notifyApplicationAfterPageLoad();}
+pageLoaded(){this.history.assumeControlOfScrollRestoration();}
+pageWillUnload(){this.history.relinquishControlOfScrollRestoration();}
+receivedMessageFromStream(message){this.renderStreamMessage(message);}
+viewWillCacheSnapshot(){var _a;if(!((_a=this.navigator.currentVisit)===null||_a===void 0?void 0:_a.silent)){this.notifyApplicationBeforeCachingSnapshot();}}
+allowsImmediateRender({element},options){const event=this.notifyApplicationBeforeRender(element,options);const{defaultPrevented,detail:{render},}=event;if(this.view.renderer&&render){this.view.renderer.renderElement=render;}
+return!defaultPrevented;}
+viewRenderedSnapshot(_snapshot,_isPreview){this.view.lastRenderedLocation=this.history.location;this.notifyApplicationAfterRender();}
+preloadOnLoadLinksForView(element){this.preloader.preloadOnLoadLinksForView(element);}
+viewInvalidated(reason){this.adapter.pageInvalidated(reason);}
+frameLoaded(frame){this.notifyApplicationAfterFrameLoad(frame);}
+frameRendered(fetchResponse,frame){this.notifyApplicationAfterFrameRender(fetchResponse,frame);}
+applicationAllowsFollowingLinkToLocation(link,location,ev){const event=this.notifyApplicationAfterClickingLinkToLocation(link,location,ev);return!event.defaultPrevented;}
+applicationAllowsVisitingLocation(location){const event=this.notifyApplicationBeforeVisitingLocation(location);return!event.defaultPrevented;}
+notifyApplicationAfterClickingLinkToLocation(link,location,event){return dispatch("turbo:click",{target:link,detail:{url:location.href,originalEvent:event},cancelable:true,});}
+notifyApplicationBeforeVisitingLocation(location){return dispatch("turbo:before-visit",{detail:{url:location.href},cancelable:true,});}
+notifyApplicationAfterVisitingLocation(location,action){return dispatch("turbo:visit",{detail:{url:location.href,action}});}
+notifyApplicationBeforeCachingSnapshot(){return dispatch("turbo:before-cache");}
+notifyApplicationBeforeRender(newBody,options){return dispatch("turbo:before-render",{detail:Object.assign({newBody},options),cancelable:true,});}
+notifyApplicationAfterRender(){return dispatch("turbo:render");}
+notifyApplicationAfterPageLoad(timing={}){return dispatch("turbo:load",{detail:{url:this.location.href,timing},});}
+notifyApplicationAfterVisitingSamePageLocation(oldURL,newURL){dispatchEvent(new HashChangeEvent("hashchange",{oldURL:oldURL.toString(),newURL:newURL.toString(),}));}
+notifyApplicationAfterFrameLoad(frame){return dispatch("turbo:frame-load",{target:frame});}
+notifyApplicationAfterFrameRender(fetchResponse,frame){return dispatch("turbo:frame-render",{detail:{fetchResponse},target:frame,cancelable:true,});}
+submissionIsNavigatable(form,submitter){if(this.formMode=="off"){return false;}
+else{const submitterIsNavigatable=submitter?this.elementIsNavigatable(submitter):true;if(this.formMode=="optin"){return submitterIsNavigatable&&form.closest('[data-turbo="true"]')!=null;}
+else{return submitterIsNavigatable&&this.elementIsNavigatable(form);}}}
+elementIsNavigatable(element){const container=findClosestRecursively(element,"[data-turbo]");const withinFrame=findClosestRecursively(element,"turbo-frame");if(this.drive||withinFrame){if(container){return container.getAttribute("data-turbo")!="false";}
+else{return true;}}
+else{if(container){return container.getAttribute("data-turbo")=="true";}
+else{return false;}}}
+getActionForLink(link){return getVisitAction(link)||"advance";}
+get snapshot(){return this.view.snapshot;}}
+function extendURLWithDeprecatedProperties(url){Object.defineProperties(url,deprecatedLocationPropertyDescriptors);}
+const deprecatedLocationPropertyDescriptors={absoluteURL:{get(){return this.toString();},},};class Cache{constructor(session){this.session=session;}
+clear(){this.session.clearCache();}
+resetCacheControl(){this.setCacheControl("");}
+exemptPageFromCache(){this.setCacheControl("no-cache");}
+exemptPageFromPreview(){this.setCacheControl("no-preview");}
+setCacheControl(value){setMetaContent("turbo-cache-control",value);}}
+const StreamActions={after(){this.targetElements.forEach((e)=>{var _a;return(_a=e.parentElement)===null||_a===void 0?void 0:_a.insertBefore(this.templateContent,e.nextSibling);});},append(){this.removeDuplicateTargetChildren();this.targetElements.forEach((e)=>e.append(this.templateContent));},before(){this.targetElements.forEach((e)=>{var _a;return(_a=e.parentElement)===null||_a===void 0?void 0:_a.insertBefore(this.templateContent,e);});},prepend(){this.removeDuplicateTargetChildren();this.targetElements.forEach((e)=>e.prepend(this.templateContent));},remove(){this.targetElements.forEach((e)=>e.remove());},replace(){this.targetElements.forEach((e)=>e.replaceWith(this.templateContent));},update(){this.targetElements.forEach((targetElement)=>{targetElement.innerHTML="";targetElement.append(this.templateContent);});},};const session=new Session();const cache=new Cache(session);const{navigator:navigator$1}=session;function start(){session.start();}
+function registerAdapter(adapter){session.registerAdapter(adapter);}
+function visit(location,options){session.visit(location,options);}
+function connectStreamSource(source){session.connectStreamSource(source);}
+function disconnectStreamSource(source){session.disconnectStreamSource(source);}
+function renderStreamMessage(message){session.renderStreamMessage(message);}
+function clearCache(){console.warn("Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`");session.clearCache();}
+function setProgressBarDelay(delay){session.setProgressBarDelay(delay);}
+function setConfirmMethod(confirmMethod){FormSubmission.confirmMethod=confirmMethod;}
+function setFormMode(mode){session.setFormMode(mode);}
+var Turbo=Object.freeze({__proto__:null,navigator:navigator$1,session:session,cache:cache,PageRenderer:PageRenderer,PageSnapshot:PageSnapshot,FrameRenderer:FrameRenderer,start:start,registerAdapter:registerAdapter,visit:visit,connectStreamSource:connectStreamSource,disconnectStreamSource:disconnectStreamSource,renderStreamMessage:renderStreamMessage,clearCache:clearCache,setProgressBarDelay:setProgressBarDelay,setConfirmMethod:setConfirmMethod,setFormMode:setFormMode,StreamActions:StreamActions});class TurboFrameMissingError extends Error{}
+class FrameController{constructor(element){this.fetchResponseLoaded=(_fetchResponse)=>{};this.currentFetchRequest=null;this.resolveVisitPromise=()=>{};this.connected=false;this.hasBeenLoaded=false;this.ignoredAttributes=new Set();this.action=null;this.visitCachedSnapshot=({element})=>{const frame=element.querySelector("#"+this.element.id);if(frame&&this.previousFrameElement){frame.replaceChildren(...this.previousFrameElement.children);}
+delete this.previousFrameElement;};this.element=element;this.view=new FrameView(this,this.element);this.appearanceObserver=new AppearanceObserver(this,this.element);this.formLinkClickObserver=new FormLinkClickObserver(this,this.element);this.linkInterceptor=new LinkInterceptor(this,this.element);this.restorationIdentifier=uuid();this.formSubmitObserver=new FormSubmitObserver(this,this.element);}
+connect(){if(!this.connected){this.connected=true;if(this.loadingStyle==FrameLoadingStyle.lazy){this.appearanceObserver.start();}
+else{this.loadSourceURL();}
+this.formLinkClickObserver.start();this.linkInterceptor.start();this.formSubmitObserver.start();}}
+disconnect(){if(this.connected){this.connected=false;this.appearanceObserver.stop();this.formLinkClickObserver.stop();this.linkInterceptor.stop();this.formSubmitObserver.stop();}}
+disabledChanged(){if(this.loadingStyle==FrameLoadingStyle.eager){this.loadSourceURL();}}
+sourceURLChanged(){if(this.isIgnoringChangesTo("src"))
+return;if(this.element.isConnected){this.complete=false;}
+if(this.loadingStyle==FrameLoadingStyle.eager||this.hasBeenLoaded){this.loadSourceURL();}}
+sourceURLReloaded(){const{src}=this.element;this.ignoringChangesToAttribute("complete",()=>{this.element.removeAttribute("complete");});this.element.src=null;this.element.src=src;return this.element.loaded;}
+completeChanged(){if(this.isIgnoringChangesTo("complete"))
+return;this.loadSourceURL();}
+loadingStyleChanged(){if(this.loadingStyle==FrameLoadingStyle.lazy){this.appearanceObserver.start();}
+else{this.appearanceObserver.stop();this.loadSourceURL();}}
+async loadSourceURL(){if(this.enabled&&this.isActive&&!this.complete&&this.sourceURL){this.element.loaded=this.visit(expandURL(this.sourceURL));this.appearanceObserver.stop();await this.element.loaded;this.hasBeenLoaded=true;}}
+async loadResponse(fetchResponse){if(fetchResponse.redirected||(fetchResponse.succeeded&&fetchResponse.isHTML)){this.sourceURL=fetchResponse.response.url;}
+try{const html=await fetchResponse.responseHTML;if(html){const document=parseHTMLDocument(html);const pageSnapshot=PageSnapshot.fromDocument(document);if(pageSnapshot.isVisitable){await this.loadFrameResponse(fetchResponse,document);}
+else{await this.handleUnvisitableFrameResponse(fetchResponse);}}}
+finally{this.fetchResponseLoaded=()=>{};}}
+elementAppearedInViewport(element){this.proposeVisitIfNavigatedWithAction(element,element);this.loadSourceURL();}
+willSubmitFormLinkToLocation(link){return this.shouldInterceptNavigation(link);}
+submittedFormLinkToLocation(link,_location,form){const frame=this.findFrameElement(link);if(frame)
+form.setAttribute("data-turbo-frame",frame.id);}
+shouldInterceptLinkClick(element,_location,_event){return this.shouldInterceptNavigation(element);}
+linkClickIntercepted(element,location){this.navigateFrame(element,location);}
+willSubmitForm(element,submitter){return element.closest("turbo-frame")==this.element&&this.shouldInterceptNavigation(element,submitter);}
+formSubmitted(element,submitter){if(this.formSubmission){this.formSubmission.stop();}
+this.formSubmission=new FormSubmission(this,element,submitter);const{fetchRequest}=this.formSubmission;this.prepareRequest(fetchRequest);this.formSubmission.start();}
+prepareRequest(request){var _a;request.headers["Turbo-Frame"]=this.id;if((_a=this.currentNavigationElement)===null||_a===void 0?void 0:_a.hasAttribute("data-turbo-stream")){request.acceptResponseType(StreamMessage.contentType);}}
+requestStarted(_request){markAsBusy(this.element);}
+requestPreventedHandlingResponse(_request,_response){this.resolveVisitPromise();}
+async requestSucceededWithResponse(request,response){await this.loadResponse(response);this.resolveVisitPromise();}
+async requestFailedWithResponse(request,response){await this.loadResponse(response);this.resolveVisitPromise();}
+requestErrored(request,error){console.error(error);this.resolveVisitPromise();}
+requestFinished(_request){clearBusyState(this.element);}
+formSubmissionStarted({formElement}){markAsBusy(formElement,this.findFrameElement(formElement));}
+formSubmissionSucceededWithResponse(formSubmission,response){const frame=this.findFrameElement(formSubmission.formElement,formSubmission.submitter);frame.delegate.proposeVisitIfNavigatedWithAction(frame,formSubmission.formElement,formSubmission.submitter);frame.delegate.loadResponse(response);if(!formSubmission.isSafe){session.clearCache();}}
+formSubmissionFailedWithResponse(formSubmission,fetchResponse){this.element.delegate.loadResponse(fetchResponse);session.clearCache();}
+formSubmissionErrored(formSubmission,error){console.error(error);}
+formSubmissionFinished({formElement}){clearBusyState(formElement,this.findFrameElement(formElement));}
+allowsImmediateRender({element:newFrame},options){const event=dispatch("turbo:before-frame-render",{target:this.element,detail:Object.assign({newFrame},options),cancelable:true,});const{defaultPrevented,detail:{render},}=event;if(this.view.renderer&&render){this.view.renderer.renderElement=render;}
+return!defaultPrevented;}
+viewRenderedSnapshot(_snapshot,_isPreview){}
+preloadOnLoadLinksForView(element){session.preloadOnLoadLinksForView(element);}
+viewInvalidated(){}
+willRenderFrame(currentElement,_newElement){this.previousFrameElement=currentElement.cloneNode(true);}
+async loadFrameResponse(fetchResponse,document){const newFrameElement=await this.extractForeignFrameElement(document.body);if(newFrameElement){const snapshot=new Snapshot(newFrameElement);const renderer=new FrameRenderer(this,this.view.snapshot,snapshot,FrameRenderer.renderElement,false,false);if(this.view.renderPromise)
+await this.view.renderPromise;this.changeHistory();await this.view.render(renderer);this.complete=true;session.frameRendered(fetchResponse,this.element);session.frameLoaded(this.element);this.fetchResponseLoaded(fetchResponse);}
+else if(this.willHandleFrameMissingFromResponse(fetchResponse)){this.handleFrameMissingFromResponse(fetchResponse);}}
+async visit(url){var _a;const request=new FetchRequest(this,FetchMethod.get,url,new URLSearchParams(),this.element);(_a=this.currentFetchRequest)===null||_a===void 0?void 0:_a.cancel();this.currentFetchRequest=request;return new Promise((resolve)=>{this.resolveVisitPromise=()=>{this.resolveVisitPromise=()=>{};this.currentFetchRequest=null;resolve();};request.perform();});}
+navigateFrame(element,url,submitter){const frame=this.findFrameElement(element,submitter);frame.delegate.proposeVisitIfNavigatedWithAction(frame,element,submitter);this.withCurrentNavigationElement(element,()=>{frame.src=url;});}
+proposeVisitIfNavigatedWithAction(frame,element,submitter){this.action=getVisitAction(submitter,element,frame);if(this.action){const pageSnapshot=PageSnapshot.fromElement(frame).clone();const{visitCachedSnapshot}=frame.delegate;frame.delegate.fetchResponseLoaded=(fetchResponse)=>{if(frame.src){const{statusCode,redirected}=fetchResponse;const responseHTML=frame.ownerDocument.documentElement.outerHTML;const response={statusCode,redirected,responseHTML};const options={response,visitCachedSnapshot,willRender:false,updateHistory:false,restorationIdentifier:this.restorationIdentifier,snapshot:pageSnapshot,};if(this.action)
+options.action=this.action;session.visit(frame.src,options);}};}}
+changeHistory(){if(this.action){const method=getHistoryMethodForAction(this.action);session.history.update(method,expandURL(this.element.src||""),this.restorationIdentifier);}}
+async handleUnvisitableFrameResponse(fetchResponse){console.warn(`The response (${fetchResponse.statusCode}) from <turbo-frame id="${this.element.id}"> is performing a full page visit due to turbo-visit-control.`);await this.visitResponse(fetchResponse.response);}
+willHandleFrameMissingFromResponse(fetchResponse){this.element.setAttribute("complete","");const response=fetchResponse.response;const visit=async(url,options={})=>{if(url instanceof Response){this.visitResponse(url);}
+else{session.visit(url,options);}};const event=dispatch("turbo:frame-missing",{target:this.element,detail:{response,visit},cancelable:true,});return!event.defaultPrevented;}
+handleFrameMissingFromResponse(fetchResponse){this.view.missing();this.throwFrameMissingError(fetchResponse);}
+throwFrameMissingError(fetchResponse){const message=`The response (${fetchResponse.statusCode}) did not contain the expected <turbo-frame id="${this.element.id}"> and will be ignored. To perform a full page visit instead, set turbo-visit-control to reload.`;throw new TurboFrameMissingError(message);}
+async visitResponse(response){const wrapped=new FetchResponse(response);const responseHTML=await wrapped.responseHTML;const{location,redirected,statusCode}=wrapped;return session.visit(location,{response:{redirected,statusCode,responseHTML}});}
+findFrameElement(element,submitter){var _a;const id=getAttribute("data-turbo-frame",submitter,element)||this.element.getAttribute("target");return(_a=getFrameElementById(id))!==null&&_a!==void 0?_a:this.element;}
+async extractForeignFrameElement(container){let element;const id=CSS.escape(this.id);try{element=activateElement(container.querySelector(`turbo-frame#${id}`),this.sourceURL);if(element){return element;}
+element=activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`),this.sourceURL);if(element){await element.loaded;return await this.extractForeignFrameElement(element);}}
+catch(error){console.error(error);return new FrameElement();}
+return null;}
+formActionIsVisitable(form,submitter){const action=getAction(form,submitter);return locationIsVisitable(expandURL(action),this.rootLocation);}
+shouldInterceptNavigation(element,submitter){const id=getAttribute("data-turbo-frame",submitter,element)||this.element.getAttribute("target");if(element instanceof HTMLFormElement&&!this.formActionIsVisitable(element,submitter)){return false;}
+if(!this.enabled||id=="_top"){return false;}
+if(id){const frameElement=getFrameElementById(id);if(frameElement){return!frameElement.disabled;}}
+if(!session.elementIsNavigatable(element)){return false;}
+if(submitter&&!session.elementIsNavigatable(submitter)){return false;}
+return true;}
+get id(){return this.element.id;}
+get enabled(){return!this.element.disabled;}
+get sourceURL(){if(this.element.src){return this.element.src;}}
+set sourceURL(sourceURL){this.ignoringChangesToAttribute("src",()=>{this.element.src=sourceURL!==null&&sourceURL!==void 0?sourceURL:null;});}
+get loadingStyle(){return this.element.loading;}
+get isLoading(){return this.formSubmission!==undefined||this.resolveVisitPromise()!==undefined;}
+get complete(){return this.element.hasAttribute("complete");}
+set complete(value){this.ignoringChangesToAttribute("complete",()=>{if(value){this.element.setAttribute("complete","");}
+else{this.element.removeAttribute("complete");}});}
+get isActive(){return this.element.isActive&&this.connected;}
+get rootLocation(){var _a;const meta=this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);const root=(_a=meta===null||meta===void 0?void 0:meta.content)!==null&&_a!==void 0?_a:"/";return expandURL(root);}
+isIgnoringChangesTo(attributeName){return this.ignoredAttributes.has(attributeName);}
+ignoringChangesToAttribute(attributeName,callback){this.ignoredAttributes.add(attributeName);callback();this.ignoredAttributes.delete(attributeName);}
+withCurrentNavigationElement(element,callback){this.currentNavigationElement=element;callback();delete this.currentNavigationElement;}}
+function getFrameElementById(id){if(id!=null){const element=document.getElementById(id);if(element instanceof FrameElement){return element;}}}
+function activateElement(element,currentURL){if(element){const src=element.getAttribute("src");if(src!=null&¤tURL!=null&&urlsAreEqual(src,currentURL)){throw new Error(`Matching <turbo-frame id="${element.id}"> element has a source URL which references itself`);}
+if(element.ownerDocument!==document){element=document.importNode(element,true);}
+if(element instanceof FrameElement){element.connectedCallback();element.disconnectedCallback();return element;}}}
+class StreamElement extends HTMLElement{static async renderElement(newElement){await newElement.performAction();}
+async connectedCallback(){try{await this.render();}
+catch(error){console.error(error);}
+finally{this.disconnect();}}
+async render(){var _a;return((_a=this.renderPromise)!==null&&_a!==void 0?_a:(this.renderPromise=(async()=>{const event=this.beforeRenderEvent;if(this.dispatchEvent(event)){await nextAnimationFrame();await event.detail.render(this);}})()));}
+disconnect(){try{this.remove();}
+catch(_a){}}
+removeDuplicateTargetChildren(){this.duplicateChildren.forEach((c)=>c.remove());}
+get duplicateChildren(){var _a;const existingChildren=this.targetElements.flatMap((e)=>[...e.children]).filter((c)=>!!c.id);const newChildrenIds=[...(((_a=this.templateContent)===null||_a===void 0?void 0:_a.children)||[])].filter((c)=>!!c.id).map((c)=>c.id);return existingChildren.filter((c)=>newChildrenIds.includes(c.id));}
+get performAction(){if(this.action){const actionFunction=StreamActions[this.action];if(actionFunction){return actionFunction;}
+this.raise("unknown action");}
+this.raise("action attribute is missing");}
+get targetElements(){if(this.target){return this.targetElementsById;}
+else if(this.targets){return this.targetElementsByQuery;}
+else{this.raise("target or targets attribute is missing");}}
+get templateContent(){return this.templateElement.content.cloneNode(true);}
+get templateElement(){if(this.firstElementChild===null){const template=this.ownerDocument.createElement("template");this.appendChild(template);return template;}
+else if(this.firstElementChild instanceof HTMLTemplateElement){return this.firstElementChild;}
+this.raise("first child element must be a <template> element");}
+get action(){return this.getAttribute("action");}
+get target(){return this.getAttribute("target");}
+get targets(){return this.getAttribute("targets");}
+raise(message){throw new Error(`${this.description}: ${message}`);}
+get description(){var _a,_b;return(_b=((_a=this.outerHTML.match(/<[^>]+>/))!==null&&_a!==void 0?_a:[])[0])!==null&&_b!==void 0?_b:"<turbo-stream>";}
+get beforeRenderEvent(){return new CustomEvent("turbo:before-stream-render",{bubbles:true,cancelable:true,detail:{newStream:this,render:StreamElement.renderElement},});}
+get targetElementsById(){var _a;const element=(_a=this.ownerDocument)===null||_a===void 0?void 0:_a.getElementById(this.target);if(element!==null){return[element];}
+else{return[];}}
+get targetElementsByQuery(){var _a;const elements=(_a=this.ownerDocument)===null||_a===void 0?void 0:_a.querySelectorAll(this.targets);if(elements.length!==0){return Array.prototype.slice.call(elements);}
+else{return[];}}}
+class StreamSourceElement extends HTMLElement{constructor(){super(...arguments);this.streamSource=null;}
+connectedCallback(){this.streamSource=this.src.match(/^ws{1,2}:/)?new WebSocket(this.src):new EventSource(this.src);connectStreamSource(this.streamSource);}
+disconnectedCallback(){if(this.streamSource){disconnectStreamSource(this.streamSource);}}
+get src(){return this.getAttribute("src")||"";}}
+FrameElement.delegateConstructor=FrameController;if(customElements.get("turbo-frame")===undefined){customElements.define("turbo-frame",FrameElement);}
+if(customElements.get("turbo-stream")===undefined){customElements.define("turbo-stream",StreamElement);}
+if(customElements.get("turbo-stream-source")===undefined){customElements.define("turbo-stream-source",StreamSourceElement);}
+(()=>{let element=document.currentScript;if(!element)
+return;if(element.hasAttribute("data-turbo-suppress-warning"))
+return;element=element.parentElement;while(element){if(element==document.body){return console.warn(unindent`
+ You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
+
+ Load your application’s JavaScript bundle inside the <head> element instead. <script> elements in <body> are evaluated with each page change.
+
+ For more information, see: https://turbo.hotwired.dev/handbook/building#working-with-script-elements
+
+ ——
+ Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
+ `,element.outerHTML);}
+element=element.parentElement;}})();window.Turbo=Turbo;start();export{FrameElement,FrameLoadingStyle,FrameRenderer,PageRenderer,PageSnapshot,StreamActions,StreamElement,StreamSourceElement,cache,clearCache,connectStreamSource,disconnectStreamSource,navigator$1 as navigator,registerAdapter,renderStreamMessage,session,setConfirmMethod,setFormMode,setProgressBarDelay,start,visit};
\ No newline at end of file
-----------------------------------------------------------------------
hooks/post-receive
--
rt
More information about the rt-commit
mailing list