[Rt-commit] rt branch 6.0/turbo created. rt-5.0.3-515-g3243d32fe5
BPS Git Server
git at git.bestpractical.com
Fri May 5 21:00: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 3243d32fe50ab8f4ac3e09442af1d6cccf839cb0 (commit)
- Log -----------------------------------------------------------------
commit 3243d32fe50ab8f4ac3e09442af1d6cccf839cb0
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri May 5 16:59:45 2023 -0400
Update login tests for new results parameter
With the redirect, there is now a results param at
the end of the URL after a failed login. Update tests to
expect that.
diff --git a/t/web/redirect-after-login.t b/t/web/redirect-after-login.t
index eb2718cf36..334126e10a 100644
--- a/t/web/redirect-after-login.t
+++ b/t/web/redirect-after-login.t
@@ -49,7 +49,7 @@ diag $url if $ENV{TEST_VERBOSE};
is( $agent->status, 200, "Fetched the page ok");
ok( $agent->content =~ /Your username or password is incorrect/i, "Found the error message");
- like( $agent->uri, qr{/NoAuth/Login\.html$}, "now on /NoAuth/Login.html" );
+ like( $agent->uri, qr{/NoAuth/Login\.html\?results}, "now on /NoAuth/Login.html" );
$agent->warning_like(qr/FAILED LOGIN for root/, "got failed login warning");
$agent->logout();
@@ -112,7 +112,7 @@ for my $path (qw(Prefs/Other.html /Prefs/Other.html)) {
is( $agent->status, 200, "Fetched the page ok");
ok( $agent->content =~ /Your username or password is incorrect/i, "Found the error message");
- like( $agent->uri, qr{/NoAuth/Login\.html$}, "still on /NoAuth/Login.html" );
+ like( $agent->uri, qr{/NoAuth/Login\.html\?results}, "still on /NoAuth/Login.html" );
$agent->warning_like(qr/FAILED LOGIN for root/, "got failed login warning");
# try to login again
commit 7e58cedf802adb9654cb608dc5a1a007847ece82
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri May 5 16:58:56 2023 -0400
Skip turbo redirect handling for REST 1 requests
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index bddf23c105..c58fb9585a 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -744,6 +744,11 @@ sub InitializeMenu {
sub ProcessPostRedirectGet {
my $ARGS = shift;
+ if ($HTML::Mason::Commands::m->request_comp->path =~ m{^/REST/\d+\.\d+/}) {
+ # Don't redirect for REST, no Turbo needed
+ return $ARGS;
+ }
+
# Respond immediately to a POST with a 303. This helps avoid
# double-submits on forms. See:
# https://en.wikipedia.org/wiki/Post/Redirect/Get
commit 80106b38687f22e07a25676be30e162c18954db5
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri May 5 15:37:57 2023 -0400
Explicitly save submitted time args
For cases where pages are reloaded for a user, like adding
attachments on create, we want to restore all settings,
including the time units. Previously this worked because the
EditTimeValue looked directly at the original request_args.
With the new redirect, the originally submitted args aren't there,
so stash them explicitly in a new arg.
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index ae44ca6093..bddf23c105 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -1466,6 +1466,15 @@ sub PreprocessTimeUpdates {
# This code validates and canonicalizes time inputs(including hours into minutes)
foreach my $field ( keys %$ARGS ) {
+
+ if ( $field =~ /-TimeUnits$/i ) {
+ # Stash the original submitted time units so we can restore them
+ # if the page is re-rendered without a submit.
+ # Do this first so we can restore the original units regardless
+ # of the processing that follows.
+ $ARGS->{"Submitted-$field"} = $ARGS->{$field};
+ }
+
next unless $field =~ /^(.*)-TimeUnits$/i && $ARGS->{$1};
my $local = $1;
$ARGS->{$local} =~ s{\b (?: (\d+) \s+ )? (\d+)/(\d+) \b}
diff --git a/share/html/Elements/EditTimeValue b/share/html/Elements/EditTimeValue
index e6a5ecd0c5..e5141e8992 100644
--- a/share/html/Elements/EditTimeValue
+++ b/share/html/Elements/EditTimeValue
@@ -63,7 +63,6 @@ $InUnits => ''
<%INIT>
$ValueName ||= $Name;
$UnitName ||= ($Name||$ValueName) . '-TimeUnits';
-$InUnits ||= $m->request_args->{ $UnitName };
$InUnits ||= RT->Config->Get('DefaultTimeUnitsToHours', $session{'CurrentUser'}) ? 'hours' : 'minutes';
if ($Default && $InUnits eq 'hours') {
diff --git a/share/html/Ticket/Create.html b/share/html/Ticket/Create.html
index 0628017c27..0eae0af3d1 100644
--- a/share/html/Ticket/Create.html
+++ b/share/html/Ticket/Create.html
@@ -281,15 +281,18 @@
</&>
<&| /Elements/LabeledValue, Label => loc('Time Estimated') &>
- <& /Elements/EditTimeValue, Name => 'TimeEstimated', Default => $ARGS{TimeEstimated} || '' &>
+ <& /Elements/EditTimeValue, Name => 'TimeEstimated', Default => $ARGS{TimeEstimated} || '',
+ InUnits => $ARGS{'Submitted-TimeEstimated-TimeUnits'} || '' &>
</&>
<&| /Elements/LabeledValue, Label => loc('Time Worked') &>
-<& /Elements/EditTimeValue, Name => 'TimeWorked', Default => $ARGS{TimeWorked} || '' &>
+ <& /Elements/EditTimeValue, Name => 'TimeWorked', Default => $ARGS{TimeWorked} || '',
+ InUnits => $ARGS{'Submitted-TimeWorked-TimeUnits'} || '' &>
</&>
<&| /Elements/LabeledValue, Label => loc('Time Left') &>
- <& /Elements/EditTimeValue, Name => 'TimeLeft', Default => $ARGS{TimeLeft} || '' &>
+ <& /Elements/EditTimeValue, Name => 'TimeLeft', Default => $ARGS{TimeLeft} || '',
+ InUnits => $ARGS{'Submitted-TimeLeft-TimeUnits'} || '' &>
</&>
</div>
</&>
diff --git a/share/html/Ticket/Elements/EditBasics b/share/html/Ticket/Elements/EditBasics
index d3ed836bc1..fc692778f6 100644
--- a/share/html/Ticket/Elements/EditBasics
+++ b/share/html/Ticket/Elements/EditBasics
@@ -115,6 +115,7 @@ unless ( @fields ) {
args => {
Name => $field,
Default => $defaults{$field} || $TicketObj->$field,
+ InUnits => $defaults{'Submitted-' . $field . '-TimeUnits'} || '',
}
}
} ('Time Estimated', 'Time Worked', 'Time Left')
diff --git a/share/html/Ticket/Update.html b/share/html/Ticket/Update.html
index d08a43cecc..d27c482571 100644
--- a/share/html/Ticket/Update.html
+++ b/share/html/Ticket/Update.html
@@ -120,6 +120,7 @@
args => {
Name => 'UpdateTimeWorked',
Default => $ARGS{UpdateTimeWorked}||'',
+ InUnits => $ARGS{'Submitted-TimeWorked-TimeUnits'} || '',
}
},
]
commit 4c15a00a4ec43486a1531b61bcd16d17621a8f1c
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri May 5 15:35:05 2023 -0400
Process attachments before stashing ARGS and redirecting
Previously, attachments still were added to the session separately.
Move this processing earlier because submitted POST args
error when getting added to the session if the attachment is
still present.
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index dd457ae98e..ae44ca6093 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -750,14 +750,17 @@ sub ProcessPostRedirectGet {
# 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();
+ # Handle any Attachments first because they can't be stashed in the
+ # session as part of ARGS.
+ HTML::Mason::Commands::ProcessAttachments( ARGSRef => $ARGS, RemoveARG => 1 );
+
+ # Stash the submitted args in the session
my $key = Digest::MD5::md5_hex( rand(1024) );
RT::Interface::Web::Session::Set(
Key => 'POST_ARGSRef',
SubKey => $key,
- Value => $post_args_ref,
+ Value => $ARGS,
);
HTML::Mason::Commands::MaybeRedirectForResults( Force => 1, Key => $key );
@@ -2852,6 +2855,7 @@ sub ProcessAttachments {
# For back-compatibility, CheckSize is not enabled by default. But for
# callers that mean to check returned values, it's safe to enable.
CheckSize => wantarray ? 1 : 0,
+ RemoveARG => 0, # Remove the attachment from ARGS after processing?
@_
);
@@ -2904,6 +2908,10 @@ sub ProcessAttachments {
SubSubKey => $file_path,
Value => $attachment,
);
+
+ if ( $args{RemoveARG} ) {
+ delete $args{'ARGSRef'}{'Attach'};
+ }
}
return 1;
commit 3691f15f2b7eb8022c0f0296d62198dfbd025680
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Thu May 4 16:15:03 2023 -0400
Run RT page setup JS on turbo load
When turbo renders pages, the normal document.ready type
events in the DOM don't fire. Register the normal per-page
RT JS with turbo load event, which does run per page
load.
As an example, without this update, CKEditor didn't load on
ticket update pages.
diff --git a/share/static/js/util.js b/share/static/js/util.js
index 842ee3ecd9..9c3e6193b3 100644
--- a/share/static/js/util.js
+++ b/share/static/js/util.js
@@ -699,7 +699,7 @@ function loadCollapseStates() {
}
}
-jQuery(function() {
+document.addEventListener("turbo:load", function() {
ReplaceAllTextareas();
jQuery('select.chosen.CF-Edit').chosen({ width: '20em', placeholder_text_multiple: ' ', no_results_text: ' ', search_contains: true });
AddAttachmentWarning();
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