[Rt-commit] rt branch 6.0/turbo created. rt-5.0.3-510-gdf5f224ce8

BPS Git Server git at git.bestpractical.com
Mon May 1 20:56:49 UTC 2023


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt".

The branch, 6.0/turbo has been created
        at  df5f224ce84572cfe55823bdebcb79aff9f1844d (commit)

- Log -----------------------------------------------------------------
commit df5f224ce84572cfe55823bdebcb79aff9f1844d
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Mon May 1 16:42:50 2023 -0400

    Return 303 on POST requests to work with Turbo
    
    Turbo expects to get a 303 back on POST requests:
    
    https://turbo.hotwired.dev/handbook/drive#redirecting-after-a-form-submission
    
    Update the standard handling of POST to implement this
    for all RT forms.

diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 3725041c4b..dd457ae98e 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -314,6 +314,15 @@ sub HandleRequest {
 
     ValidateWebConfig();
 
+    # Load before ARGS processing because we might pull some ARGS
+    # from the session.
+    $HTML::Mason::Commands::m->comp( '/Elements/SetupSessionCookie', %$ARGS );
+    SendSessionCookie();
+
+    # Respond to POST requests with a 303, or load ARGS
+    # from a previous request.
+    $ARGS = ProcessPostRedirectGet($ARGS);
+
     DecodeARGS($ARGS);
     local $HTML::Mason::Commands::DECODED_ARGS = $ARGS;
     PreprocessTimeUpdates($ARGS);
@@ -342,9 +351,6 @@ sub HandleRequest {
     MaybeRebuildCustomRolesCache();
     RT->System->MaybeRebuildLifecycleCache();
 
-    $HTML::Mason::Commands::m->comp( '/Elements/SetupSessionCookie', %$ARGS );
-    SendSessionCookie();
-
     if ( _UserLoggedIn() ) {
         # make user info up to date
         $HTML::Mason::Commands::session{'CurrentUser'}
@@ -735,6 +741,45 @@ sub InitializeMenu {
 
 }
 
+sub ProcessPostRedirectGet {
+    my $ARGS = shift;
+
+    # Respond immediately to a POST with a 303. This helps avoid
+    # double-submits on forms. See:
+    # https://en.wikipedia.org/wiki/Post/Redirect/Get
+    # It's also required for Turbo-submitted forms.
+
+    if ( RequestENV('REQUEST_METHOD') eq 'POST' ) {
+        # Stash the submitted args in the session
+        my $post_args_ref = $HTML::Mason::Commands::m->request_args();
+
+        my $key = Digest::MD5::md5_hex( rand(1024) );
+        RT::Interface::Web::Session::Set(
+            Key    => 'POST_ARGSRef',
+            SubKey => $key,
+            Value  => $post_args_ref,
+        );
+
+        HTML::Mason::Commands::MaybeRedirectForResults( Force => 1, Key => $key );
+    }
+
+    if ( RequestENV('REQUEST_METHOD') eq 'GET'
+         && $ARGS->{'results'}
+         && $HTML::Mason::Commands::session{'POST_ARGSRef'}{$ARGS->{'results'}} ) {
+        # This is a request after a POST 303.
+        # Reload %ARGS from the session so they can be processed.
+        my %ARGS = %{$HTML::Mason::Commands::session{'POST_ARGSRef'}{$ARGS->{'results'}}};
+
+        RT::Interface::Web::Session::Delete(
+            Key    => 'POST_ARGSRef',
+            SubKey => $ARGS{'results'},
+        );
+
+        return \%ARGS;
+    }
+
+    return $ARGS;
+}
 
 =head2 ShowRequestedPage  \%ARGS
 
@@ -2361,6 +2406,7 @@ sub MaybeRedirectForResults {
         Anchor    => undef,
         Actions   => undef,
         Force     => 0,
+        Key       => undef,
         @_
     );
     my $has_actions = $args{'Actions'} && grep( defined, @{ $args{'Actions'} } );
@@ -2369,7 +2415,7 @@ sub MaybeRedirectForResults {
     my %arguments = %{ $args{'Arguments'} };
 
     if ( $has_actions ) {
-        my $key = Digest::MD5::md5_hex( rand(1024) );
+        my $key = $args{'Key'} // Digest::MD5::md5_hex( rand(1024) );
         my $actions_ref = [];
         if ( $session{"Actions"}{ $key } ) {
             $actions_ref = $session{"Actions"}{ $key };
@@ -2384,6 +2430,9 @@ sub MaybeRedirectForResults {
 
         $arguments{'results'} = $key;
     }
+    elsif ( $args{'Key'} ) {
+        $arguments{'results'} = $args{'Key'};
+    }
 
     $args{'Path'} =~ s!^/+!!;
     my $url = RT->Config->Get('WebURL') . $args{Path};

commit 09683383e0746e7c16904276d1a1a953036bf2bf
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Mon May 1 13:04:34 2023 -0400

    Refresh dropdowns after turbo loads them

diff --git a/share/html/Elements/HeaderJavascript b/share/html/Elements/HeaderJavascript
index 23fbc61ce0..7d28258280 100644
--- a/share/html/Elements/HeaderJavascript
+++ b/share/html/Elements/HeaderJavascript
@@ -54,6 +54,7 @@ $onload => undef
 <script type="text/javascript" src="<%RT->Config->Get('WebPath')%><% $jsfile %>"></script>
 % }
 <script type="module" src="/static/js/turbo.es2017-esm.min.js"></script>
+<script type="text/javascript" src="/static/js/rt-turbo.js"></script>
 
 <script type="text/javascript"><!--
 % if ( $focus ) {
diff --git a/share/static/js/rt-turbo.js b/share/static/js/rt-turbo.js
new file mode 100644
index 0000000000..ab02785573
--- /dev/null
+++ b/share/static/js/rt-turbo.js
@@ -0,0 +1,10 @@
+jQuery(function() {
+    jQuery(document).on('turbo:render', function(e) {
+        // Refresh any dropdowns after they are loaded by turbo
+        jQuery('.selectpicker').selectpicker('refresh');
+    });
+    jQuery(document).on('turbo:frame-render', function(e) {
+        // Refresh any dropdowns after they are loaded by turbo
+        jQuery('.selectpicker').selectpicker('refresh');
+    });
+});

commit 693af426264d5cfc2e5d6a13daebf84b46267668
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Apr 28 14:43:19 2023 -0400

    Add turbo frames to saved searches and components on MyRT

diff --git a/lib/RT/Dashboard.pm b/lib/RT/Dashboard.pm
index a2ca8782fe..734d37cf17 100644
--- a/lib/RT/Dashboard.pm
+++ b/lib/RT/Dashboard.pm
@@ -223,7 +223,17 @@ sub ShowSearchName {
         return Name => $portlet->{description};
     }
 
-    return SavedSearch => join('-', $portlet->{privacy}, 'SavedSearch', $portlet->{id});
+    return SavedSearch => GetSavedSearchName(Privacy => $portlet->{privacy}, ObjectId => $portlet->{id});
+}
+
+sub GetSavedSearchName {
+    my %args = (
+        Privacy   => '',
+        ObjectId  => '',
+        @_,
+    );
+
+    return join('-', $args{Privacy}, 'SavedSearch', $args{ObjectId});
 }
 
 =head2 PossibleHiddenSearches
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 4cdfef0bc6..3725041c4b 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -446,7 +446,10 @@ sub HandleRequest {
     # Process per-page final cleanup callbacks
     $HTML::Mason::Commands::m->callback( %$ARGS, CallbackName => 'Final', CallbackPage => '/autohandler' );
 
-    $HTML::Mason::Commands::m->comp( '/Elements/Footer', %$ARGS );
+    # Don't show the footer for Turbo components
+    if ( $HTML::Mason::Commands::m->request_path !~ /^\/Views/ ) {
+        $HTML::Mason::Commands::m->comp( '/Elements/Footer', %$ARGS );
+    }
 }
 
 sub _ForceLogout {
diff --git a/share/html/Elements/Login b/share/html/Elements/Login
index e2fe6cfdc6..dc5dbf2c30 100644
--- a/share/html/Elements/Login
+++ b/share/html/Elements/Login
@@ -61,7 +61,7 @@
 % $m->callback( %ARGS, CallbackName => 'BeforeForm' );
 
 <div id="login-box">
-<&| /Widgets/TitleBox, title => loc('Login'), titleright => $RT::VERSION, hideable => 0 &>
+<&| /Widgets/TitleBox, title => loc('Login'), titleright => $RT::VERSION, hideable => 0, turbo_frame => 0 &>
 
 <& LoginRedirectWarning, %ARGS &>
 
diff --git a/share/html/Elements/MyRT b/share/html/Elements/MyRT
index ffb6b77144..8e4d3a1177 100644
--- a/share/html/Elements/MyRT
+++ b/share/html/Elements/MyRT
@@ -129,10 +129,12 @@ $show_cb = sub {
             }
         }
         else {
+            # Add turbo tags for components
+            $m->notes->{ComponentForTitle} = $name;
             $m->comp( $name, %{ $entry->{arguments} || {} } );
         }
     } elsif ( $type eq 'search' ) {
-        $m->comp( '/Elements/ShowSearch', RT::Dashboard->ShowSearchName($entry), Override => { Rows => $Rows } );
+        $m->comp( '/Elements/ShowSearch', RT::Dashboard->ShowSearchName($entry), TurboFrame => 1, Override => { Rows => $Rows } );
     } elsif ( $type eq 'dashboard' ) {
         my $current_dashboard = RT::Dashboard->new($session{CurrentUser});
         my ($ok, $msg) = $current_dashboard->LoadById($entry->{id});
diff --git a/share/html/Elements/ShowSearch b/share/html/Elements/ShowSearch
index 04349c796a..8169e1f332 100644
--- a/share/html/Elements/ShowSearch
+++ b/share/html/Elements/ShowSearch
@@ -54,7 +54,10 @@
     titleright_raw => $customize ? qq[<span class="fas fa-cog icon-bordered fa-2x" alt="$alt" data-toggle="tooltip" data-placement="top" data-original-title="$alt"></span>] : '',
     titleright_href => $customize,
     hideable => $hideable,
-    class => 'fullwidth' &>
+    class => 'fullwidth',
+    turbo_id => 'savedsearch-' . $search_turbo_name,
+    turbo_src => '/Views/Component/SavedSearch?SavedSearch=' . $search_turbo_name . $turbo_query_args,
+    turbo_frame => $TurboFrame &>
 <& $query_display_component, hideable => $hideable, %$ProcessedSearchArg, ShowNavigation => 0, Class => $class, HasResults => $HasResults, PreferOrderBy => 1 &>
 </&>
 <%init>
@@ -230,6 +233,18 @@ if ($ShowCount) {
     $ProcessedSearchArg->{Collection} = $collection;
     $ProcessedSearchArg->{TotalFound} = $count;
 }
+
+# Generate the turbo name from the privacy setting for this search for the turbo URL
+my $search_turbo_name = RT::Dashboard::GetSavedSearchName(
+        Privacy  => RT::SharedSetting->_build_privacy( $search->ObjectType, $search->ObjectId ),
+        ObjectId => $search->Id,
+    );
+
+my $turbo_query_args = '';
+if ( exists $Override{'Rows'} ) {
+    $turbo_query_args = "&Rows=" . $Override{'Rows'};
+}
+
 </%init>
 <%ARGS>
 $Name           => undef
@@ -240,4 +255,5 @@ $hideable       => 1
 $ShowCustomize  => 1
 $ShowCount      => RT->Config->Get('ShowSearchResultCount')
 $HasResults     => undef
+$TurboFrame     => 0 # Pass 1 to render just the titlebox with turbo frame
 </%ARGS>
diff --git a/share/html/Widgets/TitleBox b/share/html/Views/Component/dhandler
similarity index 76%
copy from share/html/Widgets/TitleBox
copy to share/html/Views/Component/dhandler
index 042ca49604..afb783fc23 100644
--- a/share/html/Widgets/TitleBox
+++ b/share/html/Views/Component/dhandler
@@ -45,21 +45,28 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<div class="<% $class %>">
-% if ($hide_chrome) {
-  <% $content | n %>
+% if ( $component_name eq 'SavedSearch' ) {
+<turbo-frame id="savedsearch-<%lc($ARGS{SavedSearch})%>">
+% $m->comp( "/Elements/ShowSearch", %ARGS,  );
 % } else {
-  <& TitleBoxStart, %ARGS &><% $content | n %><& TitleBoxEnd &>
+<turbo-frame id="component-<%$component_name%>">
+% $m->comp( "/Elements/$component_name" );
 % }
-</div>
-<%ARGS>
-$class => ''
-$hide_empty => 0
-$hide_chrome => 0
-</%ARGS>
-<%INIT>
-my $content = $m->content;
-$m->callback( CallbackName => "ModifyContent", ARGSRef => \%ARGS, Content => \$content,
-    Class => \$class, HideEmpty => \$hide_empty, HideChrome => \$hide_chrome );
-return if $hide_empty && $content =~ /^\s*$/s;
-</%INIT>
+</turbo-frame>
+
+<%init>
+my ($component_name) = $m->dhandler_arg;
+
+if ( $component_name eq 'SavedSearch' ) {
+    # Put Override args in the correct structure
+    $ARGS{Override} = {};
+    foreach my $override ( qw(Rows) ) {
+        if ( $ARGS{$override} ) {
+            $ARGS{Override}->{$override} = $ARGS{$override};
+            delete $ARGS{$override};
+        }
+    }
+}
+</%init>
+<%args>
+</%args>
diff --git a/share/html/Widgets/TitleBox b/share/html/Widgets/TitleBox
index 042ca49604..e000d08ea2 100644
--- a/share/html/Widgets/TitleBox
+++ b/share/html/Widgets/TitleBox
@@ -45,20 +45,75 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
+
+% if ( not $turbo_frame ) {
 <div class="<% $class %>">
-% if ($hide_chrome) {
-  <% $content | n %>
-% } else {
-  <& TitleBoxStart, %ARGS &><% $content | n %><& TitleBoxEnd &>
 % }
+
+% if ( not $hide_chrome ) {
+  <& TitleBoxStart, %ARGS &>
+% }
+
+%   if ( $turbo_frame ) {
+      <& TurboFrameStart, turbo_id => $turbo_id, turbo_src => $turbo_src &>
+%  }
+
+<% $content | n %>
+
+%   if ( $turbo_frame ) {
+      <& TurboFrameEnd &>
+%  }
+
+% if ( not $hide_chrome ) {
+  <& TitleBoxEnd &>
+% }
+
+% if ( not $turbo_frame ) {
 </div>
+% }
+
 <%ARGS>
 $class => ''
 $hide_empty => 0
 $hide_chrome => 0
+$turbo_frame => 0
+$turbo_id => undef
+$turbo_src => undef
 </%ARGS>
 <%INIT>
-my $content = $m->content;
+
+# With $turbo_frame => 1, TitleBox builds the box and the title on the
+# initial load with a turbo frame inside and no content. Turbo will then
+# issue another request to load the body.
+
+# Pass $turbo_frame => 0 to build an entire component on the initial
+# render like pre-turbo RT.
+
+my $content;
+
+if ( $m->request_path =~ /^\/Views/ ) {
+    # Turbo refresh, send content with no titlebox
+    $m->out( $m->content );
+    return;
+}
+elsif ( $m->notes('ComponentForTitle') ) {
+    # Request for a mason component, set up turbo tags
+    my $name = $m->notes('ComponentForTitle');
+    $turbo_frame = 1;
+    $turbo_id = 'component-' . lc($name);
+    $turbo_src = '/Views/Component/' . $name;
+    $m->notes->{'ComponentForTitle'} = 0;
+}
+elsif ( $m->request_path =~ /^\/index\.html$/ ) {
+    # Homepage load that isn't a search or a component
+    $content = '';
+}
+
+# Regular request, show the content
+unless ( length $content ) {
+    $content = $m->content;
+}
+
 $m->callback( CallbackName => "ModifyContent", ARGSRef => \%ARGS, Content => \$content,
     Class => \$class, HideEmpty => \$hide_empty, HideChrome => \$hide_chrome );
 return if $hide_empty && $content =~ /^\s*$/s;
diff --git a/share/html/Widgets/TitleBox b/share/html/Widgets/TurboFrameEnd
similarity index 81%
copy from share/html/Widgets/TitleBox
copy to share/html/Widgets/TurboFrameEnd
index 042ca49604..31f8e483a9 100644
--- a/share/html/Widgets/TitleBox
+++ b/share/html/Widgets/TurboFrameEnd
@@ -45,21 +45,6 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<div class="<% $class %>">
-% if ($hide_chrome) {
-  <% $content | n %>
-% } else {
-  <& TitleBoxStart, %ARGS &><% $content | n %><& TitleBoxEnd &>
-% }
-</div>
+</turbo-frame>
 <%ARGS>
-$class => ''
-$hide_empty => 0
-$hide_chrome => 0
 </%ARGS>
-<%INIT>
-my $content = $m->content;
-$m->callback( CallbackName => "ModifyContent", ARGSRef => \%ARGS, Content => \$content,
-    Class => \$class, HideEmpty => \$hide_empty, HideChrome => \$hide_chrome );
-return if $hide_empty && $content =~ /^\s*$/s;
-</%INIT>
diff --git a/share/html/Widgets/TitleBox b/share/html/Widgets/TurboFrameStart
similarity index 77%
copy from share/html/Widgets/TitleBox
copy to share/html/Widgets/TurboFrameStart
index 042ca49604..39d8063018 100644
--- a/share/html/Widgets/TitleBox
+++ b/share/html/Widgets/TurboFrameStart
@@ -45,21 +45,18 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<div class="<% $class %>">
-% if ($hide_chrome) {
-  <% $content | n %>
-% } else {
-  <& TitleBoxStart, %ARGS &><% $content | n %><& TitleBoxEnd &>
+<turbo-frame id="<% $turbo_id %>" src="<% $turbo_src %>" <% $lazy_load ? 'loading=lazy ' : '' %>target="_top">
+% if ( $show_spinner ) {
+<div class="text-center"><div class="spinner-border text-secondary" role="status"><span class="sr-only">Loading...</span></div></div>
 % }
-</div>
 <%ARGS>
-$class => ''
-$hide_empty => 0
-$hide_chrome => 0
+$turbo_id => undef
+$turbo_src => undef
+$show_spinner => 1
+$lazy_load => 1
 </%ARGS>
-<%INIT>
-my $content = $m->content;
-$m->callback( CallbackName => "ModifyContent", ARGSRef => \%ARGS, Content => \$content,
-    Class => \$class, HideEmpty => \$hide_empty, HideChrome => \$hide_chrome );
-return if $hide_empty && $content =~ /^\s*$/s;
-</%INIT>
+
+<%init>
+RT->Logger->error("No turbo_id provided. Turbo needs an id to find the correct frame to replace.") unless $turbo_id;
+RT->Logger->error("No turbo_src provided. Turbo needs a src path to issue the request for content.") unless $turbo_src;
+</%init>

commit 303c3a0c3dcb7494926281676fba608ab3537b9b
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Mon Apr 24 09:30:05 2023 -0400

    Remove backward compatibility code for RTIR 5.0.1
    
    RT 6 will support only RTIR 6, so no need for RTIR 5 compatibility.

diff --git a/share/html/Elements/MyRT b/share/html/Elements/MyRT
index f4df16448d..ffb6b77144 100644
--- a/share/html/Elements/MyRT
+++ b/share/html/Elements/MyRT
@@ -114,66 +114,38 @@ $show_cb = sub {
     my $type;
     my $name;
 
-    # Back compat for RTIR older than 5.0.2
-    if ( defined $RT::IR::VERSION
-         && RT::Handle::cmp_version($RT::IR::VERSION, '5.0.1') <= 0
-         && $m->callers(1)->path eq '/RTIR/index.html' ) {
+    # Normal handling for RT 5.0.2 and newer
+    my $depth = shift || 0;
+    Abort("Possible recursive dashboard detected.", SuppressHeader => 1) if $depth > 8;
 
-        $type  = $entry->{type};
-        $name = $entry->{'name'};
-        if ( $type eq 'component' ) {
-            if (!$allowed_components{$name}) {
-                $m->out( $m->interp->apply_escapes( loc("Invalid portlet [_1]", $name), "h" ) );
-                RT->Logger->info("Invalid portlet $name found on user " . $user->Name . "'s homepage");
-                if ($name eq 'QueueList' && $allowed_components{Quicksearch}) {
-                    RT->Logger->warning("You may need to replace the component 'Quicksearch' in the HomepageComponents config with 'QueueList'. See the UPGRADING-4.4 document.");
-                }
+    $type  = $entry->{portlet_type};
+    $name = $entry->{component};
+    if ( $type eq 'component' ) {
+        if (!$allowed_components{$name}) {
+            $m->out( $m->interp->apply_escapes( loc("Invalid portlet [_1]", $name), "h" ) );
+            RT->Logger->info("Invalid portlet $name found on user " . $user->Name . "'s homepage");
+            if ($name eq 'QueueList' && $allowed_components{Quicksearch}) {
+                RT->Logger->warning("You may need to replace the component 'Quicksearch' in the HomepageComponents config with 'QueueList'. See the UPGRADING-4.4 document.");
             }
-            else {
-                $m->comp( $name, %{ $entry->{arguments} || {} } );
-            }
-        } elsif ( $type eq 'system' ) {
-            $m->comp( '/Elements/ShowSearch', Name => $name, Override => { Rows => $Rows } );
-        } elsif ( $type eq 'saved' ) {
-            $m->comp( '/Elements/ShowSearch', SavedSearch => $name, Override => { Rows => $Rows } );
-        } else {
-            $RT::Logger->error("unknown portlet type '$type'");
         }
-    }
-    else {
-        # Normal handling for RT 5.0.2 and newer
-        my $depth = shift || 0;
-        Abort("Possible recursive dashboard detected.", SuppressHeader => 1) if $depth > 8;
-
-        $type  = $entry->{portlet_type};
-        $name = $entry->{component};
-        if ( $type eq 'component' ) {
-            if (!$allowed_components{$name}) {
-                $m->out( $m->interp->apply_escapes( loc("Invalid portlet [_1]", $name), "h" ) );
-                RT->Logger->info("Invalid portlet $name found on user " . $user->Name . "'s homepage");
-                if ($name eq 'QueueList' && $allowed_components{Quicksearch}) {
-                    RT->Logger->warning("You may need to replace the component 'Quicksearch' in the HomepageComponents config with 'QueueList'. See the UPGRADING-4.4 document.");
-                }
-            }
-            else {
-                $m->comp( $name, %{ $entry->{arguments} || {} } );
-            }
-        } elsif ( $type eq 'search' ) {
-            $m->comp( '/Elements/ShowSearch', RT::Dashboard->ShowSearchName($entry), Override => { Rows => $Rows } );
-        } elsif ( $type eq 'dashboard' ) {
-            my $current_dashboard = RT::Dashboard->new($session{CurrentUser});
-            my ($ok, $msg) = $current_dashboard->LoadById($entry->{id});
-            if (!$ok) {
-                $m->out($msg);
-                return;
-            }
-            my @panes = @{ $current_dashboard->Panes->{$entry->{pane}} || [] };
-            for my $portlet (@panes) {
-                $show_cb->($portlet, $depth + 1);
-            }
-        } else {
-            $RT::Logger->error("unknown portlet type '$type'");
+        else {
+            $m->comp( $name, %{ $entry->{arguments} || {} } );
+        }
+    } elsif ( $type eq 'search' ) {
+        $m->comp( '/Elements/ShowSearch', RT::Dashboard->ShowSearchName($entry), Override => { Rows => $Rows } );
+    } elsif ( $type eq 'dashboard' ) {
+        my $current_dashboard = RT::Dashboard->new($session{CurrentUser});
+        my ($ok, $msg) = $current_dashboard->LoadById($entry->{id});
+        if (!$ok) {
+            $m->out($msg);
+            return;
+        }
+        my @panes = @{ $current_dashboard->Panes->{$entry->{pane}} || [] };
+        for my $portlet (@panes) {
+            $show_cb->($portlet, $depth + 1);
         }
+    } else {
+        $RT::Logger->error("unknown portlet type '$type'");
     }
 };
 

commit 69b81036855b3e16f39375afdb63b6dce1d71ee8
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Apr 21 08:50:04 2023 -0400

    Change redirect status code for turbo

diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index bcdf354ada..4cdfef0bc6 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -1129,11 +1129,12 @@ sub Redirect {
 
     # not sure why, but on some systems without this call mason doesn't
     # set status to 302, but 200 instead and people see blank pages
-    $HTML::Mason::Commands::r->status(302);
+    # Turbo wants this to be 303
+    $HTML::Mason::Commands::r->status(303);
 
     # Perlbal expects a status message, but Mason's default redirect status
     # doesn't provide one. See also rt.cpan.org #36689.
-    $HTML::Mason::Commands::m->redirect( $uri->canonical, "302 Found" );
+    $HTML::Mason::Commands::m->redirect( $uri->canonical, "303 See other" );
 
     $HTML::Mason::Commands::m->abort;
 }

commit 3d57b7f65eaff74d4675e3d5df0254b121c37b9b
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Thu Apr 20 14:29:22 2023 -0400

    Add turbo JS files

diff --git a/devel/third-party/README b/devel/third-party/README
index 96c46c405d..9126344da5 100644
--- a/devel/third-party/README
+++ b/devel/third-party/README
@@ -105,3 +105,8 @@ License: MIT and GPL
 Description: bubble-like multiple user inputs
 Origin: https://selectize.github.io/selectize.js/
 License: Apache
+
+* turbo-7.3.0
+Description: JS framework to speed up web sites
+Origin: https://github.com/hotwired/turbo
+License: MIT
diff --git a/devel/third-party/turbo-7.3.0/MIT-LICENSE b/devel/third-party/turbo-7.3.0/MIT-LICENSE
new file mode 100644
index 0000000000..3d7308cf91
--- /dev/null
+++ b/devel/third-party/turbo-7.3.0/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 37signals
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/devel/third-party/turbo-7.3.0/README b/devel/third-party/turbo-7.3.0/README
new file mode 100644
index 0000000000..67b9a7424c
--- /dev/null
+++ b/devel/third-party/turbo-7.3.0/README
@@ -0,0 +1,16 @@
+Use the steps below to build the turbo JS file for RT. If you don't have
+them, you will need to install recent versions of: yarn, node, tsc, jsmin
+and all dependencies.
+
+1) Download the tarball from Releases on GitHub for the version you want.
+2) Unpack.
+3) In the unpacked turbo directory, run:
+
+    yarn run build
+
+   That should create dist/turbo.es2017-esm.js
+4) To create a minified version:
+
+    jsmin <dist/turbo.es2017-esm.js > dist/turbo.es2017-esm.min.js
+
+5) Copy the new file to RT's share/static/js.
diff --git a/devel/third-party/turbo-7.3.0/turbo.es2017-esm.js b/devel/third-party/turbo-7.3.0/turbo.es2017-esm.js
new file mode 100644
index 0000000000..cfe408484e
--- /dev/null
+++ b/devel/third-party/turbo-7.3.0/turbo.es2017-esm.js
@@ -0,0 +1,3969 @@
+/*
+Turbo 7.3.0
+Copyright © 2023 37signals LLC
+ */
+(function () {
+    if (window.Reflect === undefined ||
+        window.customElements === undefined ||
+        window.customElements.polyfillWrapFlushCallback) {
+        return;
+    }
+    const BuiltInHTMLElement = HTMLElement;
+    const wrapperForTheName = {
+        HTMLElement: function HTMLElement() {
+            return Reflect.construct(BuiltInHTMLElement, [], this.constructor);
+        },
+    };
+    window.HTMLElement = wrapperForTheName["HTMLElement"];
+    HTMLElement.prototype = BuiltInHTMLElement.prototype;
+    HTMLElement.prototype.constructor = HTMLElement;
+    Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
+})();
+
+/**
+ * The MIT License (MIT)
+ * 
+ * Copyright (c) 2019 Javan Makhmali
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+(function(prototype) {
+  if (typeof prototype.requestSubmit == "function") return
+
+  prototype.requestSubmit = function(submitter) {
+    if (submitter) {
+      validateSubmitter(submitter, this);
+      submitter.click();
+    } else {
+      submitter = document.createElement("input");
+      submitter.type = "submit";
+      submitter.hidden = true;
+      this.appendChild(submitter);
+      submitter.click();
+      this.removeChild(submitter);
+    }
+  };
+
+  function validateSubmitter(submitter, form) {
+    submitter instanceof HTMLElement || raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
+    submitter.type == "submit" || raise(TypeError, "The specified element is not a submit button");
+    submitter.form == form || raise(DOMException, "The specified element is not owned by this form element", "NotFoundError");
+  }
+
+  function raise(errorConstructor, message, name) {
+    throw new errorConstructor("Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".", name)
+  }
+})(HTMLFormElement.prototype);
+
+const submittersByForm = new WeakMap();
+function findSubmitterFromClickTarget(target) {
+    const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
+    const candidate = element ? element.closest("input, button") : null;
+    return (candidate === null || candidate === void 0 ? void 0 : candidate.type) == "submit" ? candidate : null;
+}
+function clickCaptured(event) {
+    const submitter = findSubmitterFromClickTarget(event.target);
+    if (submitter && submitter.form) {
+        submittersByForm.set(submitter.form, submitter);
+    }
+}
+(function () {
+    if ("submitter" in Event.prototype)
+        return;
+    let prototype = window.Event.prototype;
+    if ("SubmitEvent" in window && /Apple Computer/.test(navigator.vendor)) {
+        prototype = window.SubmitEvent.prototype;
+    }
+    else if ("SubmitEvent" in window) {
+        return;
+    }
+    addEventListener("click", clickCaptured, true);
+    Object.defineProperty(prototype, "submitter", {
+        get() {
+            if (this.type == "submit" && this.target instanceof HTMLFormElement) {
+                return submittersByForm.get(this.target);
+            }
+        },
+    });
+})();
+
+var FrameLoadingStyle;
+(function (FrameLoadingStyle) {
+    FrameLoadingStyle["eager"] = "eager";
+    FrameLoadingStyle["lazy"] = "lazy";
+})(FrameLoadingStyle || (FrameLoadingStyle = {}));
+class FrameElement extends HTMLElement {
+    static get observedAttributes() {
+        return ["disabled", "complete", "loading", "src"];
+    }
+    constructor() {
+        super();
+        this.loaded = Promise.resolve();
+        this.delegate = new FrameElement.delegateConstructor(this);
+    }
+    connectedCallback() {
+        this.delegate.connect();
+    }
+    disconnectedCallback() {
+        this.delegate.disconnect();
+    }
+    reload() {
+        return this.delegate.sourceURLReloaded();
+    }
+    attributeChangedCallback(name) {
+        if (name == "loading") {
+            this.delegate.loadingStyleChanged();
+        }
+        else if (name == "complete") {
+            this.delegate.completeChanged();
+        }
+        else if (name == "src") {
+            this.delegate.sourceURLChanged();
+        }
+        else {
+            this.delegate.disabledChanged();
+        }
+    }
+    get src() {
+        return this.getAttribute("src");
+    }
+    set src(value) {
+        if (value) {
+            this.setAttribute("src", value);
+        }
+        else {
+            this.removeAttribute("src");
+        }
+    }
+    get loading() {
+        return frameLoadingStyleFromString(this.getAttribute("loading") || "");
+    }
+    set loading(value) {
+        if (value) {
+            this.setAttribute("loading", value);
+        }
+        else {
+            this.removeAttribute("loading");
+        }
+    }
+    get disabled() {
+        return this.hasAttribute("disabled");
+    }
+    set disabled(value) {
+        if (value) {
+            this.setAttribute("disabled", "");
+        }
+        else {
+            this.removeAttribute("disabled");
+        }
+    }
+    get autoscroll() {
+        return this.hasAttribute("autoscroll");
+    }
+    set autoscroll(value) {
+        if (value) {
+            this.setAttribute("autoscroll", "");
+        }
+        else {
+            this.removeAttribute("autoscroll");
+        }
+    }
+    get complete() {
+        return !this.delegate.isLoading;
+    }
+    get isActive() {
+        return this.ownerDocument === document && !this.isPreview;
+    }
+    get isPreview() {
+        var _a, _b;
+        return (_b = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.documentElement) === null || _b === void 0 ? void 0 : _b.hasAttribute("data-turbo-preview");
+    }
+}
+function frameLoadingStyleFromString(style) {
+    switch (style.toLowerCase()) {
+        case "lazy":
+            return FrameLoadingStyle.lazy;
+        default:
+            return FrameLoadingStyle.eager;
+    }
+}
+
+function expandURL(locatable) {
+    return new URL(locatable.toString(), document.baseURI);
+}
+function getAnchor(url) {
+    let anchorMatch;
+    if (url.hash) {
+        return url.hash.slice(1);
+    }
+    else if ((anchorMatch = url.href.match(/#(.*)$/))) {
+        return anchorMatch[1];
+    }
+}
+function getAction(form, submitter) {
+    const action = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formaction")) || form.getAttribute("action") || form.action;
+    return expandURL(action);
+}
+function getExtension(url) {
+    return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || "";
+}
+function isHTML(url) {
+    return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml|php))$/);
+}
+function isPrefixedBy(baseURL, url) {
+    const prefix = getPrefix(url);
+    return baseURL.href === expandURL(prefix).href || baseURL.href.startsWith(prefix);
+}
+function locationIsVisitable(location, rootLocation) {
+    return isPrefixedBy(location, rootLocation) && isHTML(location);
+}
+function getRequestURL(url) {
+    const anchor = getAnchor(url);
+    return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href;
+}
+function toCacheKey(url) {
+    return getRequestURL(url);
+}
+function urlsAreEqual(left, right) {
+    return expandURL(left).href == expandURL(right).href;
+}
+function getPathComponents(url) {
+    return url.pathname.split("/").slice(1);
+}
+function getLastPathComponent(url) {
+    return getPathComponents(url).slice(-1)[0];
+}
+function getPrefix(url) {
+    return addTrailingSlash(url.origin + url.pathname);
+}
+function addTrailingSlash(value) {
+    return value.endsWith("/") ? value : value + "/";
+}
+
+class FetchResponse {
+    constructor(response) {
+        this.response = response;
+    }
+    get succeeded() {
+        return this.response.ok;
+    }
+    get failed() {
+        return !this.succeeded;
+    }
+    get clientError() {
+        return this.statusCode >= 400 && this.statusCode <= 499;
+    }
+    get serverError() {
+        return this.statusCode >= 500 && this.statusCode <= 599;
+    }
+    get redirected() {
+        return this.response.redirected;
+    }
+    get location() {
+        return expandURL(this.response.url);
+    }
+    get isHTML() {
+        return this.contentType && this.contentType.match(/^(?:text\/([^\s;,]+\b)?html|application\/xhtml\+xml)\b/);
+    }
+    get statusCode() {
+        return this.response.status;
+    }
+    get contentType() {
+        return this.header("Content-Type");
+    }
+    get responseText() {
+        return this.response.clone().text();
+    }
+    get responseHTML() {
+        if (this.isHTML) {
+            return this.response.clone().text();
+        }
+        else {
+            return Promise.resolve(undefined);
+        }
+    }
+    header(name) {
+        return this.response.headers.get(name);
+    }
+}
+
+function activateScriptElement(element) {
+    if (element.getAttribute("data-turbo-eval") == "false") {
+        return element;
+    }
+    else {
+        const createdScriptElement = document.createElement("script");
+        const cspNonce = getMetaContent("csp-nonce");
+        if (cspNonce) {
+            createdScriptElement.nonce = cspNonce;
+        }
+        createdScriptElement.textContent = element.textContent;
+        createdScriptElement.async = false;
+        copyElementAttributes(createdScriptElement, element);
+        return createdScriptElement;
+    }
+}
+function copyElementAttributes(destinationElement, sourceElement) {
+    for (const { name, value } of sourceElement.attributes) {
+        destinationElement.setAttribute(name, value);
+    }
+}
+function createDocumentFragment(html) {
+    const template = document.createElement("template");
+    template.innerHTML = html;
+    return template.content;
+}
+function dispatch(eventName, { target, cancelable, detail } = {}) {
+    const event = new CustomEvent(eventName, {
+        cancelable,
+        bubbles: true,
+        composed: true,
+        detail,
+    });
+    if (target && target.isConnected) {
+        target.dispatchEvent(event);
+    }
+    else {
+        document.documentElement.dispatchEvent(event);
+    }
+    return event;
+}
+function nextAnimationFrame() {
+    return new Promise((resolve) => requestAnimationFrame(() => resolve()));
+}
+function nextEventLoopTick() {
+    return new Promise((resolve) => setTimeout(() => resolve(), 0));
+}
+function nextMicrotask() {
+    return Promise.resolve();
+}
+function parseHTMLDocument(html = "") {
+    return new DOMParser().parseFromString(html, "text/html");
+}
+function unindent(strings, ...values) {
+    const lines = interpolate(strings, values).replace(/^\n/, "").split("\n");
+    const match = lines[0].match(/^\s+/);
+    const indent = match ? match[0].length : 0;
+    return lines.map((line) => line.slice(indent)).join("\n");
+}
+function interpolate(strings, values) {
+    return strings.reduce((result, string, i) => {
+        const value = values[i] == undefined ? "" : values[i];
+        return result + string + value;
+    }, "");
+}
+function uuid() {
+    return Array.from({ length: 36 })
+        .map((_, i) => {
+        if (i == 8 || i == 13 || i == 18 || i == 23) {
+            return "-";
+        }
+        else if (i == 14) {
+            return "4";
+        }
+        else if (i == 19) {
+            return (Math.floor(Math.random() * 4) + 8).toString(16);
+        }
+        else {
+            return Math.floor(Math.random() * 15).toString(16);
+        }
+    })
+        .join("");
+}
+function getAttribute(attributeName, ...elements) {
+    for (const value of elements.map((element) => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
+        if (typeof value == "string")
+            return value;
+    }
+    return null;
+}
+function hasAttribute(attributeName, ...elements) {
+    return elements.some((element) => element && element.hasAttribute(attributeName));
+}
+function markAsBusy(...elements) {
+    for (const element of elements) {
+        if (element.localName == "turbo-frame") {
+            element.setAttribute("busy", "");
+        }
+        element.setAttribute("aria-busy", "true");
+    }
+}
+function clearBusyState(...elements) {
+    for (const element of elements) {
+        if (element.localName == "turbo-frame") {
+            element.removeAttribute("busy");
+        }
+        element.removeAttribute("aria-busy");
+    }
+}
+function waitForLoad(element, timeoutInMilliseconds = 2000) {
+    return new Promise((resolve) => {
+        const onComplete = () => {
+            element.removeEventListener("error", onComplete);
+            element.removeEventListener("load", onComplete);
+            resolve();
+        };
+        element.addEventListener("load", onComplete, { once: true });
+        element.addEventListener("error", onComplete, { once: true });
+        setTimeout(resolve, timeoutInMilliseconds);
+    });
+}
+function getHistoryMethodForAction(action) {
+    switch (action) {
+        case "replace":
+            return history.replaceState;
+        case "advance":
+        case "restore":
+            return history.pushState;
+    }
+}
+function isAction(action) {
+    return action == "advance" || action == "replace" || action == "restore";
+}
+function getVisitAction(...elements) {
+    const action = getAttribute("data-turbo-action", ...elements);
+    return isAction(action) ? action : null;
+}
+function getMetaElement(name) {
+    return document.querySelector(`meta[name="${name}"]`);
+}
+function getMetaContent(name) {
+    const element = getMetaElement(name);
+    return element && element.content;
+}
+function setMetaContent(name, content) {
+    let element = getMetaElement(name);
+    if (!element) {
+        element = document.createElement("meta");
+        element.setAttribute("name", name);
+        document.head.appendChild(element);
+    }
+    element.setAttribute("content", content);
+    return element;
+}
+function findClosestRecursively(element, selector) {
+    var _a;
+    if (element instanceof Element) {
+        return (element.closest(selector) ||
+            findClosestRecursively(element.assignedSlot || ((_a = element.getRootNode()) === null || _a === void 0 ? void 0 : _a.host), selector));
+    }
+}
+
+var FetchMethod;
+(function (FetchMethod) {
+    FetchMethod[FetchMethod["get"] = 0] = "get";
+    FetchMethod[FetchMethod["post"] = 1] = "post";
+    FetchMethod[FetchMethod["put"] = 2] = "put";
+    FetchMethod[FetchMethod["patch"] = 3] = "patch";
+    FetchMethod[FetchMethod["delete"] = 4] = "delete";
+})(FetchMethod || (FetchMethod = {}));
+function fetchMethodFromString(method) {
+    switch (method.toLowerCase()) {
+        case "get":
+            return FetchMethod.get;
+        case "post":
+            return FetchMethod.post;
+        case "put":
+            return FetchMethod.put;
+        case "patch":
+            return FetchMethod.patch;
+        case "delete":
+            return FetchMethod.delete;
+    }
+}
+class FetchRequest {
+    constructor(delegate, method, location, body = new URLSearchParams(), target = null) {
+        this.abortController = new AbortController();
+        this.resolveRequestPromise = (_value) => { };
+        this.delegate = delegate;
+        this.method = method;
+        this.headers = this.defaultHeaders;
+        this.body = body;
+        this.url = location;
+        this.target = target;
+    }
+    get location() {
+        return this.url;
+    }
+    get params() {
+        return this.url.searchParams;
+    }
+    get entries() {
+        return this.body ? Array.from(this.body.entries()) : [];
+    }
+    cancel() {
+        this.abortController.abort();
+    }
+    async perform() {
+        const { fetchOptions } = this;
+        this.delegate.prepareRequest(this);
+        await this.allowRequestToBeIntercepted(fetchOptions);
+        try {
+            this.delegate.requestStarted(this);
+            const response = await fetch(this.url.href, fetchOptions);
+            return await this.receive(response);
+        }
+        catch (error) {
+            if (error.name !== "AbortError") {
+                if (this.willDelegateErrorHandling(error)) {
+                    this.delegate.requestErrored(this, error);
+                }
+                throw error;
+            }
+        }
+        finally {
+            this.delegate.requestFinished(this);
+        }
+    }
+    async receive(response) {
+        const fetchResponse = new FetchResponse(response);
+        const event = dispatch("turbo:before-fetch-response", {
+            cancelable: true,
+            detail: { fetchResponse },
+            target: this.target,
+        });
+        if (event.defaultPrevented) {
+            this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
+        }
+        else if (fetchResponse.succeeded) {
+            this.delegate.requestSucceededWithResponse(this, fetchResponse);
+        }
+        else {
+            this.delegate.requestFailedWithResponse(this, fetchResponse);
+        }
+        return fetchResponse;
+    }
+    get fetchOptions() {
+        var _a;
+        return {
+            method: FetchMethod[this.method].toUpperCase(),
+            credentials: "same-origin",
+            headers: this.headers,
+            redirect: "follow",
+            body: this.isSafe ? null : this.body,
+            signal: this.abortSignal,
+            referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href,
+        };
+    }
+    get defaultHeaders() {
+        return {
+            Accept: "text/html, application/xhtml+xml",
+        };
+    }
+    get isSafe() {
+        return this.method === FetchMethod.get;
+    }
+    get abortSignal() {
+        return this.abortController.signal;
+    }
+    acceptResponseType(mimeType) {
+        this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", ");
+    }
+    async allowRequestToBeIntercepted(fetchOptions) {
+        const requestInterception = new Promise((resolve) => (this.resolveRequestPromise = resolve));
+        const event = dispatch("turbo:before-fetch-request", {
+            cancelable: true,
+            detail: {
+                fetchOptions,
+                url: this.url,
+                resume: this.resolveRequestPromise,
+            },
+            target: this.target,
+        });
+        if (event.defaultPrevented)
+            await requestInterception;
+    }
+    willDelegateErrorHandling(error) {
+        const event = dispatch("turbo:fetch-request-error", {
+            target: this.target,
+            cancelable: true,
+            detail: { request: this, error: error },
+        });
+        return !event.defaultPrevented;
+    }
+}
+
+class AppearanceObserver {
+    constructor(delegate, element) {
+        this.started = false;
+        this.intersect = (entries) => {
+            const lastEntry = entries.slice(-1)[0];
+            if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
+                this.delegate.elementAppearedInViewport(this.element);
+            }
+        };
+        this.delegate = delegate;
+        this.element = element;
+        this.intersectionObserver = new IntersectionObserver(this.intersect);
+    }
+    start() {
+        if (!this.started) {
+            this.started = true;
+            this.intersectionObserver.observe(this.element);
+        }
+    }
+    stop() {
+        if (this.started) {
+            this.started = false;
+            this.intersectionObserver.unobserve(this.element);
+        }
+    }
+}
+
+class StreamMessage {
+    static wrap(message) {
+        if (typeof message == "string") {
+            return new this(createDocumentFragment(message));
+        }
+        else {
+            return message;
+        }
+    }
+    constructor(fragment) {
+        this.fragment = importStreamElements(fragment);
+    }
+}
+StreamMessage.contentType = "text/vnd.turbo-stream.html";
+function importStreamElements(fragment) {
+    for (const element of fragment.querySelectorAll("turbo-stream")) {
+        const streamElement = document.importNode(element, true);
+        for (const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")) {
+            inertScriptElement.replaceWith(activateScriptElement(inertScriptElement));
+        }
+        element.replaceWith(streamElement);
+    }
+    return fragment;
+}
+
+var FormSubmissionState;
+(function (FormSubmissionState) {
+    FormSubmissionState[FormSubmissionState["initialized"] = 0] = "initialized";
+    FormSubmissionState[FormSubmissionState["requesting"] = 1] = "requesting";
+    FormSubmissionState[FormSubmissionState["waiting"] = 2] = "waiting";
+    FormSubmissionState[FormSubmissionState["receiving"] = 3] = "receiving";
+    FormSubmissionState[FormSubmissionState["stopping"] = 4] = "stopping";
+    FormSubmissionState[FormSubmissionState["stopped"] = 5] = "stopped";
+})(FormSubmissionState || (FormSubmissionState = {}));
+var FormEnctype;
+(function (FormEnctype) {
+    FormEnctype["urlEncoded"] = "application/x-www-form-urlencoded";
+    FormEnctype["multipart"] = "multipart/form-data";
+    FormEnctype["plain"] = "text/plain";
+})(FormEnctype || (FormEnctype = {}));
+function formEnctypeFromString(encoding) {
+    switch (encoding.toLowerCase()) {
+        case FormEnctype.multipart:
+            return FormEnctype.multipart;
+        case FormEnctype.plain:
+            return FormEnctype.plain;
+        default:
+            return FormEnctype.urlEncoded;
+    }
+}
+class FormSubmission {
+    static confirmMethod(message, _element, _submitter) {
+        return Promise.resolve(confirm(message));
+    }
+    constructor(delegate, formElement, submitter, mustRedirect = false) {
+        this.state = FormSubmissionState.initialized;
+        this.delegate = delegate;
+        this.formElement = formElement;
+        this.submitter = submitter;
+        this.formData = buildFormData(formElement, submitter);
+        this.location = expandURL(this.action);
+        if (this.method == FetchMethod.get) {
+            mergeFormDataEntries(this.location, [...this.body.entries()]);
+        }
+        this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
+        this.mustRedirect = mustRedirect;
+    }
+    get method() {
+        var _a;
+        const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.getAttribute("method") || "";
+        return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get;
+    }
+    get action() {
+        var _a;
+        const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
+        if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
+            return this.submitter.getAttribute("formaction") || "";
+        }
+        else {
+            return this.formElement.getAttribute("action") || formElementAction || "";
+        }
+    }
+    get body() {
+        if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
+            return new URLSearchParams(this.stringFormData);
+        }
+        else {
+            return this.formData;
+        }
+    }
+    get enctype() {
+        var _a;
+        return formEnctypeFromString(((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formenctype")) || this.formElement.enctype);
+    }
+    get isSafe() {
+        return this.fetchRequest.isSafe;
+    }
+    get stringFormData() {
+        return [...this.formData].reduce((entries, [name, value]) => {
+            return entries.concat(typeof value == "string" ? [[name, value]] : []);
+        }, []);
+    }
+    async start() {
+        const { initialized, requesting } = FormSubmissionState;
+        const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
+        if (typeof confirmationMessage === "string") {
+            const answer = await FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter);
+            if (!answer) {
+                return;
+            }
+        }
+        if (this.state == initialized) {
+            this.state = requesting;
+            return this.fetchRequest.perform();
+        }
+    }
+    stop() {
+        const { stopping, stopped } = FormSubmissionState;
+        if (this.state != stopping && this.state != stopped) {
+            this.state = stopping;
+            this.fetchRequest.cancel();
+            return true;
+        }
+    }
+    prepareRequest(request) {
+        if (!request.isSafe) {
+            const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token");
+            if (token) {
+                request.headers["X-CSRF-Token"] = token;
+            }
+        }
+        if (this.requestAcceptsTurboStreamResponse(request)) {
+            request.acceptResponseType(StreamMessage.contentType);
+        }
+    }
+    requestStarted(_request) {
+        var _a;
+        this.state = FormSubmissionState.waiting;
+        (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
+        this.setSubmitsWith();
+        dispatch("turbo:submit-start", {
+            target: this.formElement,
+            detail: { formSubmission: this },
+        });
+        this.delegate.formSubmissionStarted(this);
+    }
+    requestPreventedHandlingResponse(request, response) {
+        this.result = { success: response.succeeded, fetchResponse: response };
+    }
+    requestSucceededWithResponse(request, response) {
+        if (response.clientError || response.serverError) {
+            this.delegate.formSubmissionFailedWithResponse(this, response);
+        }
+        else if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) {
+            const error = new Error("Form responses must redirect to another location");
+            this.delegate.formSubmissionErrored(this, error);
+        }
+        else {
+            this.state = FormSubmissionState.receiving;
+            this.result = { success: true, fetchResponse: response };
+            this.delegate.formSubmissionSucceededWithResponse(this, response);
+        }
+    }
+    requestFailedWithResponse(request, response) {
+        this.result = { success: false, fetchResponse: response };
+        this.delegate.formSubmissionFailedWithResponse(this, response);
+    }
+    requestErrored(request, error) {
+        this.result = { success: false, error };
+        this.delegate.formSubmissionErrored(this, error);
+    }
+    requestFinished(_request) {
+        var _a;
+        this.state = FormSubmissionState.stopped;
+        (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
+        this.resetSubmitterText();
+        dispatch("turbo:submit-end", {
+            target: this.formElement,
+            detail: Object.assign({ formSubmission: this }, this.result),
+        });
+        this.delegate.formSubmissionFinished(this);
+    }
+    setSubmitsWith() {
+        if (!this.submitter || !this.submitsWith)
+            return;
+        if (this.submitter.matches("button")) {
+            this.originalSubmitText = this.submitter.innerHTML;
+            this.submitter.innerHTML = this.submitsWith;
+        }
+        else if (this.submitter.matches("input")) {
+            const input = this.submitter;
+            this.originalSubmitText = input.value;
+            input.value = this.submitsWith;
+        }
+    }
+    resetSubmitterText() {
+        if (!this.submitter || !this.originalSubmitText)
+            return;
+        if (this.submitter.matches("button")) {
+            this.submitter.innerHTML = this.originalSubmitText;
+        }
+        else if (this.submitter.matches("input")) {
+            const input = this.submitter;
+            input.value = this.originalSubmitText;
+        }
+    }
+    requestMustRedirect(request) {
+        return !request.isSafe && this.mustRedirect;
+    }
+    requestAcceptsTurboStreamResponse(request) {
+        return !request.isSafe || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
+    }
+    get submitsWith() {
+        var _a;
+        return (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-submits-with");
+    }
+}
+function buildFormData(formElement, submitter) {
+    const formData = new FormData(formElement);
+    const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
+    const value = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("value");
+    if (name) {
+        formData.append(name, value || "");
+    }
+    return formData;
+}
+function getCookieValue(cookieName) {
+    if (cookieName != null) {
+        const cookies = document.cookie ? document.cookie.split("; ") : [];
+        const cookie = cookies.find((cookie) => cookie.startsWith(cookieName));
+        if (cookie) {
+            const value = cookie.split("=").slice(1).join("=");
+            return value ? decodeURIComponent(value) : undefined;
+        }
+    }
+}
+function responseSucceededWithoutRedirect(response) {
+    return response.statusCode == 200 && !response.redirected;
+}
+function mergeFormDataEntries(url, entries) {
+    const searchParams = new URLSearchParams();
+    for (const [name, value] of entries) {
+        if (value instanceof File)
+            continue;
+        searchParams.append(name, value);
+    }
+    url.search = searchParams.toString();
+    return url;
+}
+
+class Snapshot {
+    constructor(element) {
+        this.element = element;
+    }
+    get activeElement() {
+        return this.element.ownerDocument.activeElement;
+    }
+    get children() {
+        return [...this.element.children];
+    }
+    hasAnchor(anchor) {
+        return this.getElementForAnchor(anchor) != null;
+    }
+    getElementForAnchor(anchor) {
+        return anchor ? this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`) : null;
+    }
+    get isConnected() {
+        return this.element.isConnected;
+    }
+    get firstAutofocusableElement() {
+        const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
+        for (const element of this.element.querySelectorAll("[autofocus]")) {
+            if (element.closest(inertDisabledOrHidden) == null)
+                return element;
+            else
+                continue;
+        }
+        return null;
+    }
+    get permanentElements() {
+        return queryPermanentElementsAll(this.element);
+    }
+    getPermanentElementById(id) {
+        return getPermanentElementById(this.element, id);
+    }
+    getPermanentElementMapForSnapshot(snapshot) {
+        const permanentElementMap = {};
+        for (const currentPermanentElement of this.permanentElements) {
+            const { id } = currentPermanentElement;
+            const newPermanentElement = snapshot.getPermanentElementById(id);
+            if (newPermanentElement) {
+                permanentElementMap[id] = [currentPermanentElement, newPermanentElement];
+            }
+        }
+        return permanentElementMap;
+    }
+}
+function getPermanentElementById(node, id) {
+    return node.querySelector(`#${id}[data-turbo-permanent]`);
+}
+function queryPermanentElementsAll(node) {
+    return node.querySelectorAll("[id][data-turbo-permanent]");
+}
+
+class FormSubmitObserver {
+    constructor(delegate, eventTarget) {
+        this.started = false;
+        this.submitCaptured = () => {
+            this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
+            this.eventTarget.addEventListener("submit", this.submitBubbled, false);
+        };
+        this.submitBubbled = ((event) => {
+            if (!event.defaultPrevented) {
+                const form = event.target instanceof HTMLFormElement ? event.target : undefined;
+                const submitter = event.submitter || undefined;
+                if (form &&
+                    submissionDoesNotDismissDialog(form, submitter) &&
+                    submissionDoesNotTargetIFrame(form, submitter) &&
+                    this.delegate.willSubmitForm(form, submitter)) {
+                    event.preventDefault();
+                    event.stopImmediatePropagation();
+                    this.delegate.formSubmitted(form, submitter);
+                }
+            }
+        });
+        this.delegate = delegate;
+        this.eventTarget = eventTarget;
+    }
+    start() {
+        if (!this.started) {
+            this.eventTarget.addEventListener("submit", this.submitCaptured, true);
+            this.started = true;
+        }
+    }
+    stop() {
+        if (this.started) {
+            this.eventTarget.removeEventListener("submit", this.submitCaptured, true);
+            this.started = false;
+        }
+    }
+}
+function submissionDoesNotDismissDialog(form, submitter) {
+    const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
+    return method != "dialog";
+}
+function submissionDoesNotTargetIFrame(form, submitter) {
+    if ((submitter === null || submitter === void 0 ? void 0 : submitter.hasAttribute("formtarget")) || form.hasAttribute("target")) {
+        const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
+        for (const element of document.getElementsByName(target)) {
+            if (element instanceof HTMLIFrameElement)
+                return false;
+        }
+        return true;
+    }
+    else {
+        return true;
+    }
+}
+
+class View {
+    constructor(delegate, element) {
+        this.resolveRenderPromise = (_value) => { };
+        this.resolveInterceptionPromise = (_value) => { };
+        this.delegate = delegate;
+        this.element = element;
+    }
+    scrollToAnchor(anchor) {
+        const element = this.snapshot.getElementForAnchor(anchor);
+        if (element) {
+            this.scrollToElement(element);
+            this.focusElement(element);
+        }
+        else {
+            this.scrollToPosition({ x: 0, y: 0 });
+        }
+    }
+    scrollToAnchorFromLocation(location) {
+        this.scrollToAnchor(getAnchor(location));
+    }
+    scrollToElement(element) {
+        element.scrollIntoView();
+    }
+    focusElement(element) {
+        if (element instanceof HTMLElement) {
+            if (element.hasAttribute("tabindex")) {
+                element.focus();
+            }
+            else {
+                element.setAttribute("tabindex", "-1");
+                element.focus();
+                element.removeAttribute("tabindex");
+            }
+        }
+    }
+    scrollToPosition({ x, y }) {
+        this.scrollRoot.scrollTo(x, y);
+    }
+    scrollToTop() {
+        this.scrollToPosition({ x: 0, y: 0 });
+    }
+    get scrollRoot() {
+        return window;
+    }
+    async render(renderer) {
+        const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
+        if (shouldRender) {
+            try {
+                this.renderPromise = new Promise((resolve) => (this.resolveRenderPromise = resolve));
+                this.renderer = renderer;
+                await this.prepareToRenderSnapshot(renderer);
+                const renderInterception = new Promise((resolve) => (this.resolveInterceptionPromise = resolve));
+                const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement };
+                const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
+                if (!immediateRender)
+                    await renderInterception;
+                await this.renderSnapshot(renderer);
+                this.delegate.viewRenderedSnapshot(snapshot, isPreview);
+                this.delegate.preloadOnLoadLinksForView(this.element);
+                this.finishRenderingSnapshot(renderer);
+            }
+            finally {
+                delete this.renderer;
+                this.resolveRenderPromise(undefined);
+                delete this.renderPromise;
+            }
+        }
+        else {
+            this.invalidate(renderer.reloadReason);
+        }
+    }
+    invalidate(reason) {
+        this.delegate.viewInvalidated(reason);
+    }
+    async prepareToRenderSnapshot(renderer) {
+        this.markAsPreview(renderer.isPreview);
+        await renderer.prepareToRender();
+    }
+    markAsPreview(isPreview) {
+        if (isPreview) {
+            this.element.setAttribute("data-turbo-preview", "");
+        }
+        else {
+            this.element.removeAttribute("data-turbo-preview");
+        }
+    }
+    async renderSnapshot(renderer) {
+        await renderer.render();
+    }
+    finishRenderingSnapshot(renderer) {
+        renderer.finishRendering();
+    }
+}
+
+class FrameView extends View {
+    missing() {
+        this.element.innerHTML = `<strong class="turbo-frame-error">Content missing</strong>`;
+    }
+    get snapshot() {
+        return new Snapshot(this.element);
+    }
+}
+
+class LinkInterceptor {
+    constructor(delegate, element) {
+        this.clickBubbled = (event) => {
+            if (this.respondsToEventTarget(event.target)) {
+                this.clickEvent = event;
+            }
+            else {
+                delete this.clickEvent;
+            }
+        };
+        this.linkClicked = ((event) => {
+            if (this.clickEvent && this.respondsToEventTarget(event.target) && event.target instanceof Element) {
+                if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url, event.detail.originalEvent)) {
+                    this.clickEvent.preventDefault();
+                    event.preventDefault();
+                    this.delegate.linkClickIntercepted(event.target, event.detail.url, event.detail.originalEvent);
+                }
+            }
+            delete this.clickEvent;
+        });
+        this.willVisit = ((_event) => {
+            delete this.clickEvent;
+        });
+        this.delegate = delegate;
+        this.element = element;
+    }
+    start() {
+        this.element.addEventListener("click", this.clickBubbled);
+        document.addEventListener("turbo:click", this.linkClicked);
+        document.addEventListener("turbo:before-visit", this.willVisit);
+    }
+    stop() {
+        this.element.removeEventListener("click", this.clickBubbled);
+        document.removeEventListener("turbo:click", this.linkClicked);
+        document.removeEventListener("turbo:before-visit", this.willVisit);
+    }
+    respondsToEventTarget(target) {
+        const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
+        return element && element.closest("turbo-frame, html") == this.element;
+    }
+}
+
+class LinkClickObserver {
+    constructor(delegate, eventTarget) {
+        this.started = false;
+        this.clickCaptured = () => {
+            this.eventTarget.removeEventListener("click", this.clickBubbled, false);
+            this.eventTarget.addEventListener("click", this.clickBubbled, false);
+        };
+        this.clickBubbled = (event) => {
+            if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
+                const target = (event.composedPath && event.composedPath()[0]) || event.target;
+                const link = this.findLinkFromClickTarget(target);
+                if (link && doesNotTargetIFrame(link)) {
+                    const location = this.getLocationForLink(link);
+                    if (this.delegate.willFollowLinkToLocation(link, location, event)) {
+                        event.preventDefault();
+                        this.delegate.followedLinkToLocation(link, location);
+                    }
+                }
+            }
+        };
+        this.delegate = delegate;
+        this.eventTarget = eventTarget;
+    }
+    start() {
+        if (!this.started) {
+            this.eventTarget.addEventListener("click", this.clickCaptured, true);
+            this.started = true;
+        }
+    }
+    stop() {
+        if (this.started) {
+            this.eventTarget.removeEventListener("click", this.clickCaptured, true);
+            this.started = false;
+        }
+    }
+    clickEventIsSignificant(event) {
+        return !((event.target && event.target.isContentEditable) ||
+            event.defaultPrevented ||
+            event.which > 1 ||
+            event.altKey ||
+            event.ctrlKey ||
+            event.metaKey ||
+            event.shiftKey);
+    }
+    findLinkFromClickTarget(target) {
+        return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
+    }
+    getLocationForLink(link) {
+        return expandURL(link.getAttribute("href") || "");
+    }
+}
+function doesNotTargetIFrame(anchor) {
+    if (anchor.hasAttribute("target")) {
+        for (const element of document.getElementsByName(anchor.target)) {
+            if (element instanceof HTMLIFrameElement)
+                return false;
+        }
+        return true;
+    }
+    else {
+        return true;
+    }
+}
+
+class FormLinkClickObserver {
+    constructor(delegate, element) {
+        this.delegate = delegate;
+        this.linkInterceptor = new LinkClickObserver(this, element);
+    }
+    start() {
+        this.linkInterceptor.start();
+    }
+    stop() {
+        this.linkInterceptor.stop();
+    }
+    willFollowLinkToLocation(link, location, originalEvent) {
+        return (this.delegate.willSubmitFormLinkToLocation(link, location, originalEvent) &&
+            link.hasAttribute("data-turbo-method"));
+    }
+    followedLinkToLocation(link, location) {
+        const form = document.createElement("form");
+        const type = "hidden";
+        for (const [name, value] of location.searchParams) {
+            form.append(Object.assign(document.createElement("input"), { type, name, value }));
+        }
+        const action = Object.assign(location, { search: "" });
+        form.setAttribute("data-turbo", "true");
+        form.setAttribute("action", action.href);
+        form.setAttribute("hidden", "");
+        const method = link.getAttribute("data-turbo-method");
+        if (method)
+            form.setAttribute("method", method);
+        const turboFrame = link.getAttribute("data-turbo-frame");
+        if (turboFrame)
+            form.setAttribute("data-turbo-frame", turboFrame);
+        const turboAction = getVisitAction(link);
+        if (turboAction)
+            form.setAttribute("data-turbo-action", turboAction);
+        const turboConfirm = link.getAttribute("data-turbo-confirm");
+        if (turboConfirm)
+            form.setAttribute("data-turbo-confirm", turboConfirm);
+        const turboStream = link.hasAttribute("data-turbo-stream");
+        if (turboStream)
+            form.setAttribute("data-turbo-stream", "");
+        this.delegate.submittedFormLinkToLocation(link, location, form);
+        document.body.appendChild(form);
+        form.addEventListener("turbo:submit-end", () => form.remove(), { once: true });
+        requestAnimationFrame(() => form.requestSubmit());
+    }
+}
+
+class Bardo {
+    static async preservingPermanentElements(delegate, permanentElementMap, callback) {
+        const bardo = new this(delegate, permanentElementMap);
+        bardo.enter();
+        await callback();
+        bardo.leave();
+    }
+    constructor(delegate, permanentElementMap) {
+        this.delegate = delegate;
+        this.permanentElementMap = permanentElementMap;
+    }
+    enter() {
+        for (const id in this.permanentElementMap) {
+            const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
+            this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
+            this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
+        }
+    }
+    leave() {
+        for (const id in this.permanentElementMap) {
+            const [currentPermanentElement] = this.permanentElementMap[id];
+            this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
+            this.replacePlaceholderWithPermanentElement(currentPermanentElement);
+            this.delegate.leavingBardo(currentPermanentElement);
+        }
+    }
+    replaceNewPermanentElementWithPlaceholder(permanentElement) {
+        const placeholder = createPlaceholderForPermanentElement(permanentElement);
+        permanentElement.replaceWith(placeholder);
+    }
+    replaceCurrentPermanentElementWithClone(permanentElement) {
+        const clone = permanentElement.cloneNode(true);
+        permanentElement.replaceWith(clone);
+    }
+    replacePlaceholderWithPermanentElement(permanentElement) {
+        const placeholder = this.getPlaceholderById(permanentElement.id);
+        placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
+    }
+    getPlaceholderById(id) {
+        return this.placeholders.find((element) => element.content == id);
+    }
+    get placeholders() {
+        return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
+    }
+}
+function createPlaceholderForPermanentElement(permanentElement) {
+    const element = document.createElement("meta");
+    element.setAttribute("name", "turbo-permanent-placeholder");
+    element.setAttribute("content", permanentElement.id);
+    return element;
+}
+
+class Renderer {
+    constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
+        this.activeElement = null;
+        this.currentSnapshot = currentSnapshot;
+        this.newSnapshot = newSnapshot;
+        this.isPreview = isPreview;
+        this.willRender = willRender;
+        this.renderElement = renderElement;
+        this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
+    }
+    get shouldRender() {
+        return true;
+    }
+    get reloadReason() {
+        return;
+    }
+    prepareToRender() {
+        return;
+    }
+    finishRendering() {
+        if (this.resolvingFunctions) {
+            this.resolvingFunctions.resolve();
+            delete this.resolvingFunctions;
+        }
+    }
+    async preservingPermanentElements(callback) {
+        await Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
+    }
+    focusFirstAutofocusableElement() {
+        const element = this.connectedSnapshot.firstAutofocusableElement;
+        if (elementIsFocusable(element)) {
+            element.focus();
+        }
+    }
+    enteringBardo(currentPermanentElement) {
+        if (this.activeElement)
+            return;
+        if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
+            this.activeElement = this.currentSnapshot.activeElement;
+        }
+    }
+    leavingBardo(currentPermanentElement) {
+        if (currentPermanentElement.contains(this.activeElement) && this.activeElement instanceof HTMLElement) {
+            this.activeElement.focus();
+            this.activeElement = null;
+        }
+    }
+    get connectedSnapshot() {
+        return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
+    }
+    get currentElement() {
+        return this.currentSnapshot.element;
+    }
+    get newElement() {
+        return this.newSnapshot.element;
+    }
+    get permanentElementMap() {
+        return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
+    }
+}
+function elementIsFocusable(element) {
+    return element && typeof element.focus == "function";
+}
+
+class FrameRenderer extends Renderer {
+    static renderElement(currentElement, newElement) {
+        var _a;
+        const destinationRange = document.createRange();
+        destinationRange.selectNodeContents(currentElement);
+        destinationRange.deleteContents();
+        const frameElement = newElement;
+        const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
+        if (sourceRange) {
+            sourceRange.selectNodeContents(frameElement);
+            currentElement.appendChild(sourceRange.extractContents());
+        }
+    }
+    constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
+        super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
+        this.delegate = delegate;
+    }
+    get shouldRender() {
+        return true;
+    }
+    async render() {
+        await nextAnimationFrame();
+        this.preservingPermanentElements(() => {
+            this.loadFrameElement();
+        });
+        this.scrollFrameIntoView();
+        await nextAnimationFrame();
+        this.focusFirstAutofocusableElement();
+        await nextAnimationFrame();
+        this.activateScriptElements();
+    }
+    loadFrameElement() {
+        this.delegate.willRenderFrame(this.currentElement, this.newElement);
+        this.renderElement(this.currentElement, this.newElement);
+    }
+    scrollFrameIntoView() {
+        if (this.currentElement.autoscroll || this.newElement.autoscroll) {
+            const element = this.currentElement.firstElementChild;
+            const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
+            const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
+            if (element) {
+                element.scrollIntoView({ block, behavior });
+                return true;
+            }
+        }
+        return false;
+    }
+    activateScriptElements() {
+        for (const inertScriptElement of this.newScriptElements) {
+            const activatedScriptElement = activateScriptElement(inertScriptElement);
+            inertScriptElement.replaceWith(activatedScriptElement);
+        }
+    }
+    get newScriptElements() {
+        return this.currentElement.querySelectorAll("script");
+    }
+}
+function readScrollLogicalPosition(value, defaultValue) {
+    if (value == "end" || value == "start" || value == "center" || value == "nearest") {
+        return value;
+    }
+    else {
+        return defaultValue;
+    }
+}
+function readScrollBehavior(value, defaultValue) {
+    if (value == "auto" || value == "smooth") {
+        return value;
+    }
+    else {
+        return defaultValue;
+    }
+}
+
+class ProgressBar {
+    static get defaultCSS() {
+        return unindent `
+      .turbo-progress-bar {
+        position: fixed;
+        display: block;
+        top: 0;
+        left: 0;
+        height: 3px;
+        background: #0076ff;
+        z-index: 2147483647;
+        transition:
+          width ${ProgressBar.animationDuration}ms ease-out,
+          opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
+        transform: translate3d(0, 0, 0);
+      }
+    `;
+    }
+    constructor() {
+        this.hiding = false;
+        this.value = 0;
+        this.visible = false;
+        this.trickle = () => {
+            this.setValue(this.value + Math.random() / 100);
+        };
+        this.stylesheetElement = this.createStylesheetElement();
+        this.progressElement = this.createProgressElement();
+        this.installStylesheetElement();
+        this.setValue(0);
+    }
+    show() {
+        if (!this.visible) {
+            this.visible = true;
+            this.installProgressElement();
+            this.startTrickling();
+        }
+    }
+    hide() {
+        if (this.visible && !this.hiding) {
+            this.hiding = true;
+            this.fadeProgressElement(() => {
+                this.uninstallProgressElement();
+                this.stopTrickling();
+                this.visible = false;
+                this.hiding = false;
+            });
+        }
+    }
+    setValue(value) {
+        this.value = value;
+        this.refresh();
+    }
+    installStylesheetElement() {
+        document.head.insertBefore(this.stylesheetElement, document.head.firstChild);
+    }
+    installProgressElement() {
+        this.progressElement.style.width = "0";
+        this.progressElement.style.opacity = "1";
+        document.documentElement.insertBefore(this.progressElement, document.body);
+        this.refresh();
+    }
+    fadeProgressElement(callback) {
+        this.progressElement.style.opacity = "0";
+        setTimeout(callback, ProgressBar.animationDuration * 1.5);
+    }
+    uninstallProgressElement() {
+        if (this.progressElement.parentNode) {
+            document.documentElement.removeChild(this.progressElement);
+        }
+    }
+    startTrickling() {
+        if (!this.trickleInterval) {
+            this.trickleInterval = window.setInterval(this.trickle, ProgressBar.animationDuration);
+        }
+    }
+    stopTrickling() {
+        window.clearInterval(this.trickleInterval);
+        delete this.trickleInterval;
+    }
+    refresh() {
+        requestAnimationFrame(() => {
+            this.progressElement.style.width = `${10 + this.value * 90}%`;
+        });
+    }
+    createStylesheetElement() {
+        const element = document.createElement("style");
+        element.type = "text/css";
+        element.textContent = ProgressBar.defaultCSS;
+        if (this.cspNonce) {
+            element.nonce = this.cspNonce;
+        }
+        return element;
+    }
+    createProgressElement() {
+        const element = document.createElement("div");
+        element.className = "turbo-progress-bar";
+        return element;
+    }
+    get cspNonce() {
+        return getMetaContent("csp-nonce");
+    }
+}
+ProgressBar.animationDuration = 300;
+
+class HeadSnapshot extends Snapshot {
+    constructor() {
+        super(...arguments);
+        this.detailsByOuterHTML = this.children
+            .filter((element) => !elementIsNoscript(element))
+            .map((element) => elementWithoutNonce(element))
+            .reduce((result, element) => {
+            const { outerHTML } = element;
+            const details = outerHTML in result
+                ? result[outerHTML]
+                : {
+                    type: elementType(element),
+                    tracked: elementIsTracked(element),
+                    elements: [],
+                };
+            return Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, details), { elements: [...details.elements, element] }) });
+        }, {});
+    }
+    get trackedElementSignature() {
+        return Object.keys(this.detailsByOuterHTML)
+            .filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked)
+            .join("");
+    }
+    getScriptElementsNotInSnapshot(snapshot) {
+        return this.getElementsMatchingTypeNotInSnapshot("script", snapshot);
+    }
+    getStylesheetElementsNotInSnapshot(snapshot) {
+        return this.getElementsMatchingTypeNotInSnapshot("stylesheet", snapshot);
+    }
+    getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
+        return Object.keys(this.detailsByOuterHTML)
+            .filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML))
+            .map((outerHTML) => this.detailsByOuterHTML[outerHTML])
+            .filter(({ type }) => type == matchedType)
+            .map(({ elements: [element] }) => element);
+    }
+    get provisionalElements() {
+        return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
+            const { type, tracked, elements } = this.detailsByOuterHTML[outerHTML];
+            if (type == null && !tracked) {
+                return [...result, ...elements];
+            }
+            else if (elements.length > 1) {
+                return [...result, ...elements.slice(1)];
+            }
+            else {
+                return result;
+            }
+        }, []);
+    }
+    getMetaValue(name) {
+        const element = this.findMetaElementByName(name);
+        return element ? element.getAttribute("content") : null;
+    }
+    findMetaElementByName(name) {
+        return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
+            const { elements: [element], } = this.detailsByOuterHTML[outerHTML];
+            return elementIsMetaElementWithName(element, name) ? element : result;
+        }, undefined);
+    }
+}
+function elementType(element) {
+    if (elementIsScript(element)) {
+        return "script";
+    }
+    else if (elementIsStylesheet(element)) {
+        return "stylesheet";
+    }
+}
+function elementIsTracked(element) {
+    return element.getAttribute("data-turbo-track") == "reload";
+}
+function elementIsScript(element) {
+    const tagName = element.localName;
+    return tagName == "script";
+}
+function elementIsNoscript(element) {
+    const tagName = element.localName;
+    return tagName == "noscript";
+}
+function elementIsStylesheet(element) {
+    const tagName = element.localName;
+    return tagName == "style" || (tagName == "link" && element.getAttribute("rel") == "stylesheet");
+}
+function elementIsMetaElementWithName(element, name) {
+    const tagName = element.localName;
+    return tagName == "meta" && element.getAttribute("name") == name;
+}
+function elementWithoutNonce(element) {
+    if (element.hasAttribute("nonce")) {
+        element.setAttribute("nonce", "");
+    }
+    return element;
+}
+
+class PageSnapshot extends Snapshot {
+    static fromHTMLString(html = "") {
+        return this.fromDocument(parseHTMLDocument(html));
+    }
+    static fromElement(element) {
+        return this.fromDocument(element.ownerDocument);
+    }
+    static fromDocument({ head, body }) {
+        return new this(body, new HeadSnapshot(head));
+    }
+    constructor(element, headSnapshot) {
+        super(element);
+        this.headSnapshot = headSnapshot;
+    }
+    clone() {
+        const clonedElement = this.element.cloneNode(true);
+        const selectElements = this.element.querySelectorAll("select");
+        const clonedSelectElements = clonedElement.querySelectorAll("select");
+        for (const [index, source] of selectElements.entries()) {
+            const clone = clonedSelectElements[index];
+            for (const option of clone.selectedOptions)
+                option.selected = false;
+            for (const option of source.selectedOptions)
+                clone.options[option.index].selected = true;
+        }
+        for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
+            clonedPasswordInput.value = "";
+        }
+        return new PageSnapshot(clonedElement, this.headSnapshot);
+    }
+    get headElement() {
+        return this.headSnapshot.element;
+    }
+    get rootLocation() {
+        var _a;
+        const root = (_a = this.getSetting("root")) !== null && _a !== void 0 ? _a : "/";
+        return expandURL(root);
+    }
+    get cacheControlValue() {
+        return this.getSetting("cache-control");
+    }
+    get isPreviewable() {
+        return this.cacheControlValue != "no-preview";
+    }
+    get isCacheable() {
+        return this.cacheControlValue != "no-cache";
+    }
+    get isVisitable() {
+        return this.getSetting("visit-control") != "reload";
+    }
+    getSetting(name) {
+        return this.headSnapshot.getMetaValue(`turbo-${name}`);
+    }
+}
+
+var TimingMetric;
+(function (TimingMetric) {
+    TimingMetric["visitStart"] = "visitStart";
+    TimingMetric["requestStart"] = "requestStart";
+    TimingMetric["requestEnd"] = "requestEnd";
+    TimingMetric["visitEnd"] = "visitEnd";
+})(TimingMetric || (TimingMetric = {}));
+var VisitState;
+(function (VisitState) {
+    VisitState["initialized"] = "initialized";
+    VisitState["started"] = "started";
+    VisitState["canceled"] = "canceled";
+    VisitState["failed"] = "failed";
+    VisitState["completed"] = "completed";
+})(VisitState || (VisitState = {}));
+const defaultOptions = {
+    action: "advance",
+    historyChanged: false,
+    visitCachedSnapshot: () => { },
+    willRender: true,
+    updateHistory: true,
+    shouldCacheSnapshot: true,
+    acceptsStreamResponse: false,
+};
+var SystemStatusCode;
+(function (SystemStatusCode) {
+    SystemStatusCode[SystemStatusCode["networkFailure"] = 0] = "networkFailure";
+    SystemStatusCode[SystemStatusCode["timeoutFailure"] = -1] = "timeoutFailure";
+    SystemStatusCode[SystemStatusCode["contentTypeMismatch"] = -2] = "contentTypeMismatch";
+})(SystemStatusCode || (SystemStatusCode = {}));
+class Visit {
+    constructor(delegate, location, restorationIdentifier, options = {}) {
+        this.identifier = uuid();
+        this.timingMetrics = {};
+        this.followedRedirect = false;
+        this.historyChanged = false;
+        this.scrolled = false;
+        this.shouldCacheSnapshot = true;
+        this.acceptsStreamResponse = false;
+        this.snapshotCached = false;
+        this.state = VisitState.initialized;
+        this.delegate = delegate;
+        this.location = location;
+        this.restorationIdentifier = restorationIdentifier || uuid();
+        const { action, historyChanged, referrer, snapshot, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, } = Object.assign(Object.assign({}, defaultOptions), options);
+        this.action = action;
+        this.historyChanged = historyChanged;
+        this.referrer = referrer;
+        this.snapshot = snapshot;
+        this.snapshotHTML = snapshotHTML;
+        this.response = response;
+        this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
+        this.visitCachedSnapshot = visitCachedSnapshot;
+        this.willRender = willRender;
+        this.updateHistory = updateHistory;
+        this.scrolled = !willRender;
+        this.shouldCacheSnapshot = shouldCacheSnapshot;
+        this.acceptsStreamResponse = acceptsStreamResponse;
+    }
+    get adapter() {
+        return this.delegate.adapter;
+    }
+    get view() {
+        return this.delegate.view;
+    }
+    get history() {
+        return this.delegate.history;
+    }
+    get restorationData() {
+        return this.history.getRestorationDataForIdentifier(this.restorationIdentifier);
+    }
+    get silent() {
+        return this.isSamePage;
+    }
+    start() {
+        if (this.state == VisitState.initialized) {
+            this.recordTimingMetric(TimingMetric.visitStart);
+            this.state = VisitState.started;
+            this.adapter.visitStarted(this);
+            this.delegate.visitStarted(this);
+        }
+    }
+    cancel() {
+        if (this.state == VisitState.started) {
+            if (this.request) {
+                this.request.cancel();
+            }
+            this.cancelRender();
+            this.state = VisitState.canceled;
+        }
+    }
+    complete() {
+        if (this.state == VisitState.started) {
+            this.recordTimingMetric(TimingMetric.visitEnd);
+            this.state = VisitState.completed;
+            this.followRedirect();
+            if (!this.followedRedirect) {
+                this.adapter.visitCompleted(this);
+                this.delegate.visitCompleted(this);
+            }
+        }
+    }
+    fail() {
+        if (this.state == VisitState.started) {
+            this.state = VisitState.failed;
+            this.adapter.visitFailed(this);
+        }
+    }
+    changeHistory() {
+        var _a;
+        if (!this.historyChanged && this.updateHistory) {
+            const actionForHistory = this.location.href === ((_a = this.referrer) === null || _a === void 0 ? void 0 : _a.href) ? "replace" : this.action;
+            const method = getHistoryMethodForAction(actionForHistory);
+            this.history.update(method, this.location, this.restorationIdentifier);
+            this.historyChanged = true;
+        }
+    }
+    issueRequest() {
+        if (this.hasPreloadedResponse()) {
+            this.simulateRequest();
+        }
+        else if (this.shouldIssueRequest() && !this.request) {
+            this.request = new FetchRequest(this, FetchMethod.get, this.location);
+            this.request.perform();
+        }
+    }
+    simulateRequest() {
+        if (this.response) {
+            this.startRequest();
+            this.recordResponse();
+            this.finishRequest();
+        }
+    }
+    startRequest() {
+        this.recordTimingMetric(TimingMetric.requestStart);
+        this.adapter.visitRequestStarted(this);
+    }
+    recordResponse(response = this.response) {
+        this.response = response;
+        if (response) {
+            const { statusCode } = response;
+            if (isSuccessful(statusCode)) {
+                this.adapter.visitRequestCompleted(this);
+            }
+            else {
+                this.adapter.visitRequestFailedWithStatusCode(this, statusCode);
+            }
+        }
+    }
+    finishRequest() {
+        this.recordTimingMetric(TimingMetric.requestEnd);
+        this.adapter.visitRequestFinished(this);
+    }
+    loadResponse() {
+        if (this.response) {
+            const { statusCode, responseHTML } = this.response;
+            this.render(async () => {
+                if (this.shouldCacheSnapshot)
+                    this.cacheSnapshot();
+                if (this.view.renderPromise)
+                    await this.view.renderPromise;
+                if (isSuccessful(statusCode) && responseHTML != null) {
+                    await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
+                    this.performScroll();
+                    this.adapter.visitRendered(this);
+                    this.complete();
+                }
+                else {
+                    await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
+                    this.adapter.visitRendered(this);
+                    this.fail();
+                }
+            });
+        }
+    }
+    getCachedSnapshot() {
+        const snapshot = this.view.getCachedSnapshotForLocation(this.location) || this.getPreloadedSnapshot();
+        if (snapshot && (!getAnchor(this.location) || snapshot.hasAnchor(getAnchor(this.location)))) {
+            if (this.action == "restore" || snapshot.isPreviewable) {
+                return snapshot;
+            }
+        }
+    }
+    getPreloadedSnapshot() {
+        if (this.snapshotHTML) {
+            return PageSnapshot.fromHTMLString(this.snapshotHTML);
+        }
+    }
+    hasCachedSnapshot() {
+        return this.getCachedSnapshot() != null;
+    }
+    loadCachedSnapshot() {
+        const snapshot = this.getCachedSnapshot();
+        if (snapshot) {
+            const isPreview = this.shouldIssueRequest();
+            this.render(async () => {
+                this.cacheSnapshot();
+                if (this.isSamePage) {
+                    this.adapter.visitRendered(this);
+                }
+                else {
+                    if (this.view.renderPromise)
+                        await this.view.renderPromise;
+                    await this.view.renderPage(snapshot, isPreview, this.willRender, this);
+                    this.performScroll();
+                    this.adapter.visitRendered(this);
+                    if (!isPreview) {
+                        this.complete();
+                    }
+                }
+            });
+        }
+    }
+    followRedirect() {
+        var _a;
+        if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
+            this.adapter.visitProposedToLocation(this.redirectedToLocation, {
+                action: "replace",
+                response: this.response,
+                shouldCacheSnapshot: false,
+                willRender: false,
+            });
+            this.followedRedirect = true;
+        }
+    }
+    goToSamePageAnchor() {
+        if (this.isSamePage) {
+            this.render(async () => {
+                this.cacheSnapshot();
+                this.performScroll();
+                this.changeHistory();
+                this.adapter.visitRendered(this);
+            });
+        }
+    }
+    prepareRequest(request) {
+        if (this.acceptsStreamResponse) {
+            request.acceptResponseType(StreamMessage.contentType);
+        }
+    }
+    requestStarted() {
+        this.startRequest();
+    }
+    requestPreventedHandlingResponse(_request, _response) { }
+    async requestSucceededWithResponse(request, response) {
+        const responseHTML = await response.responseHTML;
+        const { redirected, statusCode } = response;
+        if (responseHTML == undefined) {
+            this.recordResponse({
+                statusCode: SystemStatusCode.contentTypeMismatch,
+                redirected,
+            });
+        }
+        else {
+            this.redirectedToLocation = response.redirected ? response.location : undefined;
+            this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
+        }
+    }
+    async requestFailedWithResponse(request, response) {
+        const responseHTML = await response.responseHTML;
+        const { redirected, statusCode } = response;
+        if (responseHTML == undefined) {
+            this.recordResponse({
+                statusCode: SystemStatusCode.contentTypeMismatch,
+                redirected,
+            });
+        }
+        else {
+            this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
+        }
+    }
+    requestErrored(_request, _error) {
+        this.recordResponse({
+            statusCode: SystemStatusCode.networkFailure,
+            redirected: false,
+        });
+    }
+    requestFinished() {
+        this.finishRequest();
+    }
+    performScroll() {
+        if (!this.scrolled && !this.view.forceReloaded) {
+            if (this.action == "restore") {
+                this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
+            }
+            else {
+                this.scrollToAnchor() || this.view.scrollToTop();
+            }
+            if (this.isSamePage) {
+                this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation, this.location);
+            }
+            this.scrolled = true;
+        }
+    }
+    scrollToRestoredPosition() {
+        const { scrollPosition } = this.restorationData;
+        if (scrollPosition) {
+            this.view.scrollToPosition(scrollPosition);
+            return true;
+        }
+    }
+    scrollToAnchor() {
+        const anchor = getAnchor(this.location);
+        if (anchor != null) {
+            this.view.scrollToAnchor(anchor);
+            return true;
+        }
+    }
+    recordTimingMetric(metric) {
+        this.timingMetrics[metric] = new Date().getTime();
+    }
+    getTimingMetrics() {
+        return Object.assign({}, this.timingMetrics);
+    }
+    getHistoryMethodForAction(action) {
+        switch (action) {
+            case "replace":
+                return history.replaceState;
+            case "advance":
+            case "restore":
+                return history.pushState;
+        }
+    }
+    hasPreloadedResponse() {
+        return typeof this.response == "object";
+    }
+    shouldIssueRequest() {
+        if (this.isSamePage) {
+            return false;
+        }
+        else if (this.action == "restore") {
+            return !this.hasCachedSnapshot();
+        }
+        else {
+            return this.willRender;
+        }
+    }
+    cacheSnapshot() {
+        if (!this.snapshotCached) {
+            this.view.cacheSnapshot(this.snapshot).then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
+            this.snapshotCached = true;
+        }
+    }
+    async render(callback) {
+        this.cancelRender();
+        await new Promise((resolve) => {
+            this.frame = requestAnimationFrame(() => resolve());
+        });
+        await callback();
+        delete this.frame;
+    }
+    cancelRender() {
+        if (this.frame) {
+            cancelAnimationFrame(this.frame);
+            delete this.frame;
+        }
+    }
+}
+function isSuccessful(statusCode) {
+    return statusCode >= 200 && statusCode < 300;
+}
+
+class BrowserAdapter {
+    constructor(session) {
+        this.progressBar = new ProgressBar();
+        this.showProgressBar = () => {
+            this.progressBar.show();
+        };
+        this.session = session;
+    }
+    visitProposedToLocation(location, options) {
+        this.navigator.startVisit(location, (options === null || options === void 0 ? void 0 : options.restorationIdentifier) || uuid(), options);
+    }
+    visitStarted(visit) {
+        this.location = visit.location;
+        visit.loadCachedSnapshot();
+        visit.issueRequest();
+        visit.goToSamePageAnchor();
+    }
+    visitRequestStarted(visit) {
+        this.progressBar.setValue(0);
+        if (visit.hasCachedSnapshot() || visit.action != "restore") {
+            this.showVisitProgressBarAfterDelay();
+        }
+        else {
+            this.showProgressBar();
+        }
+    }
+    visitRequestCompleted(visit) {
+        visit.loadResponse();
+    }
+    visitRequestFailedWithStatusCode(visit, statusCode) {
+        switch (statusCode) {
+            case SystemStatusCode.networkFailure:
+            case SystemStatusCode.timeoutFailure:
+            case SystemStatusCode.contentTypeMismatch:
+                return this.reload({
+                    reason: "request_failed",
+                    context: {
+                        statusCode,
+                    },
+                });
+            default:
+                return visit.loadResponse();
+        }
+    }
+    visitRequestFinished(_visit) {
+        this.progressBar.setValue(1);
+        this.hideVisitProgressBar();
+    }
+    visitCompleted(_visit) { }
+    pageInvalidated(reason) {
+        this.reload(reason);
+    }
+    visitFailed(_visit) { }
+    visitRendered(_visit) { }
+    formSubmissionStarted(_formSubmission) {
+        this.progressBar.setValue(0);
+        this.showFormProgressBarAfterDelay();
+    }
+    formSubmissionFinished(_formSubmission) {
+        this.progressBar.setValue(1);
+        this.hideFormProgressBar();
+    }
+    showVisitProgressBarAfterDelay() {
+        this.visitProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
+    }
+    hideVisitProgressBar() {
+        this.progressBar.hide();
+        if (this.visitProgressBarTimeout != null) {
+            window.clearTimeout(this.visitProgressBarTimeout);
+            delete this.visitProgressBarTimeout;
+        }
+    }
+    showFormProgressBarAfterDelay() {
+        if (this.formProgressBarTimeout == null) {
+            this.formProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
+        }
+    }
+    hideFormProgressBar() {
+        this.progressBar.hide();
+        if (this.formProgressBarTimeout != null) {
+            window.clearTimeout(this.formProgressBarTimeout);
+            delete this.formProgressBarTimeout;
+        }
+    }
+    reload(reason) {
+        var _a;
+        dispatch("turbo:reload", { detail: reason });
+        window.location.href = ((_a = this.location) === null || _a === void 0 ? void 0 : _a.toString()) || window.location.href;
+    }
+    get navigator() {
+        return this.session.navigator;
+    }
+}
+
+class CacheObserver {
+    constructor() {
+        this.selector = "[data-turbo-temporary]";
+        this.deprecatedSelector = "[data-turbo-cache=false]";
+        this.started = false;
+        this.removeTemporaryElements = ((_event) => {
+            for (const element of this.temporaryElements) {
+                element.remove();
+            }
+        });
+    }
+    start() {
+        if (!this.started) {
+            this.started = true;
+            addEventListener("turbo:before-cache", this.removeTemporaryElements, false);
+        }
+    }
+    stop() {
+        if (this.started) {
+            this.started = false;
+            removeEventListener("turbo:before-cache", this.removeTemporaryElements, false);
+        }
+    }
+    get temporaryElements() {
+        return [...document.querySelectorAll(this.selector), ...this.temporaryElementsWithDeprecation];
+    }
+    get temporaryElementsWithDeprecation() {
+        const elements = document.querySelectorAll(this.deprecatedSelector);
+        if (elements.length) {
+            console.warn(`The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.`);
+        }
+        return [...elements];
+    }
+}
+
+class FrameRedirector {
+    constructor(session, element) {
+        this.session = session;
+        this.element = element;
+        this.linkInterceptor = new LinkInterceptor(this, element);
+        this.formSubmitObserver = new FormSubmitObserver(this, element);
+    }
+    start() {
+        this.linkInterceptor.start();
+        this.formSubmitObserver.start();
+    }
+    stop() {
+        this.linkInterceptor.stop();
+        this.formSubmitObserver.stop();
+    }
+    shouldInterceptLinkClick(element, _location, _event) {
+        return this.shouldRedirect(element);
+    }
+    linkClickIntercepted(element, url, event) {
+        const frame = this.findFrameElement(element);
+        if (frame) {
+            frame.delegate.linkClickIntercepted(element, url, event);
+        }
+    }
+    willSubmitForm(element, submitter) {
+        return (element.closest("turbo-frame") == null &&
+            this.shouldSubmit(element, submitter) &&
+            this.shouldRedirect(element, submitter));
+    }
+    formSubmitted(element, submitter) {
+        const frame = this.findFrameElement(element, submitter);
+        if (frame) {
+            frame.delegate.formSubmitted(element, submitter);
+        }
+    }
+    shouldSubmit(form, submitter) {
+        var _a;
+        const action = getAction(form, submitter);
+        const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
+        const rootLocation = expandURL((_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/");
+        return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
+    }
+    shouldRedirect(element, submitter) {
+        const isNavigatable = element instanceof HTMLFormElement
+            ? this.session.submissionIsNavigatable(element, submitter)
+            : this.session.elementIsNavigatable(element);
+        if (isNavigatable) {
+            const frame = this.findFrameElement(element, submitter);
+            return frame ? frame != element.closest("turbo-frame") : false;
+        }
+        else {
+            return false;
+        }
+    }
+    findFrameElement(element, submitter) {
+        const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame");
+        if (id && id != "_top") {
+            const frame = this.element.querySelector(`#${id}:not([disabled])`);
+            if (frame instanceof FrameElement) {
+                return frame;
+            }
+        }
+    }
+}
+
+class History {
+    constructor(delegate) {
+        this.restorationIdentifier = uuid();
+        this.restorationData = {};
+        this.started = false;
+        this.pageLoaded = false;
+        this.onPopState = (event) => {
+            if (this.shouldHandlePopState()) {
+                const { turbo } = event.state || {};
+                if (turbo) {
+                    this.location = new URL(window.location.href);
+                    const { restorationIdentifier } = turbo;
+                    this.restorationIdentifier = restorationIdentifier;
+                    this.delegate.historyPoppedToLocationWithRestorationIdentifier(this.location, restorationIdentifier);
+                }
+            }
+        };
+        this.onPageLoad = async (_event) => {
+            await nextMicrotask();
+            this.pageLoaded = true;
+        };
+        this.delegate = delegate;
+    }
+    start() {
+        if (!this.started) {
+            addEventListener("popstate", this.onPopState, false);
+            addEventListener("load", this.onPageLoad, false);
+            this.started = true;
+            this.replace(new URL(window.location.href));
+        }
+    }
+    stop() {
+        if (this.started) {
+            removeEventListener("popstate", this.onPopState, false);
+            removeEventListener("load", this.onPageLoad, false);
+            this.started = false;
+        }
+    }
+    push(location, restorationIdentifier) {
+        this.update(history.pushState, location, restorationIdentifier);
+    }
+    replace(location, restorationIdentifier) {
+        this.update(history.replaceState, location, restorationIdentifier);
+    }
+    update(method, location, restorationIdentifier = uuid()) {
+        const state = { turbo: { restorationIdentifier } };
+        method.call(history, state, "", location.href);
+        this.location = location;
+        this.restorationIdentifier = restorationIdentifier;
+    }
+    getRestorationDataForIdentifier(restorationIdentifier) {
+        return this.restorationData[restorationIdentifier] || {};
+    }
+    updateRestorationData(additionalData) {
+        const { restorationIdentifier } = this;
+        const restorationData = this.restorationData[restorationIdentifier];
+        this.restorationData[restorationIdentifier] = Object.assign(Object.assign({}, restorationData), additionalData);
+    }
+    assumeControlOfScrollRestoration() {
+        var _a;
+        if (!this.previousScrollRestoration) {
+            this.previousScrollRestoration = (_a = history.scrollRestoration) !== null && _a !== void 0 ? _a : "auto";
+            history.scrollRestoration = "manual";
+        }
+    }
+    relinquishControlOfScrollRestoration() {
+        if (this.previousScrollRestoration) {
+            history.scrollRestoration = this.previousScrollRestoration;
+            delete this.previousScrollRestoration;
+        }
+    }
+    shouldHandlePopState() {
+        return this.pageIsLoaded();
+    }
+    pageIsLoaded() {
+        return this.pageLoaded || document.readyState == "complete";
+    }
+}
+
+class Navigator {
+    constructor(delegate) {
+        this.delegate = delegate;
+    }
+    proposeVisit(location, options = {}) {
+        if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
+            if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
+                this.delegate.visitProposedToLocation(location, options);
+            }
+            else {
+                window.location.href = location.toString();
+            }
+        }
+    }
+    startVisit(locatable, restorationIdentifier, options = {}) {
+        this.stop();
+        this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
+        this.currentVisit.start();
+    }
+    submitForm(form, submitter) {
+        this.stop();
+        this.formSubmission = new FormSubmission(this, form, submitter, true);
+        this.formSubmission.start();
+    }
+    stop() {
+        if (this.formSubmission) {
+            this.formSubmission.stop();
+            delete this.formSubmission;
+        }
+        if (this.currentVisit) {
+            this.currentVisit.cancel();
+            delete this.currentVisit;
+        }
+    }
+    get adapter() {
+        return this.delegate.adapter;
+    }
+    get view() {
+        return this.delegate.view;
+    }
+    get history() {
+        return this.delegate.history;
+    }
+    formSubmissionStarted(formSubmission) {
+        if (typeof this.adapter.formSubmissionStarted === "function") {
+            this.adapter.formSubmissionStarted(formSubmission);
+        }
+    }
+    async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) {
+        if (formSubmission == this.formSubmission) {
+            const responseHTML = await fetchResponse.responseHTML;
+            if (responseHTML) {
+                const shouldCacheSnapshot = formSubmission.isSafe;
+                if (!shouldCacheSnapshot) {
+                    this.view.clearSnapshotCache();
+                }
+                const { statusCode, redirected } = fetchResponse;
+                const action = this.getActionForFormSubmission(formSubmission);
+                const visitOptions = {
+                    action,
+                    shouldCacheSnapshot,
+                    response: { statusCode, responseHTML, redirected },
+                };
+                this.proposeVisit(fetchResponse.location, visitOptions);
+            }
+        }
+    }
+    async formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
+        const responseHTML = await fetchResponse.responseHTML;
+        if (responseHTML) {
+            const snapshot = PageSnapshot.fromHTMLString(responseHTML);
+            if (fetchResponse.serverError) {
+                await this.view.renderError(snapshot, this.currentVisit);
+            }
+            else {
+                await this.view.renderPage(snapshot, false, true, this.currentVisit);
+            }
+            this.view.scrollToTop();
+            this.view.clearSnapshotCache();
+        }
+    }
+    formSubmissionErrored(formSubmission, error) {
+        console.error(error);
+    }
+    formSubmissionFinished(formSubmission) {
+        if (typeof this.adapter.formSubmissionFinished === "function") {
+            this.adapter.formSubmissionFinished(formSubmission);
+        }
+    }
+    visitStarted(visit) {
+        this.delegate.visitStarted(visit);
+    }
+    visitCompleted(visit) {
+        this.delegate.visitCompleted(visit);
+    }
+    locationWithActionIsSamePage(location, action) {
+        const anchor = getAnchor(location);
+        const currentAnchor = getAnchor(this.view.lastRenderedLocation);
+        const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
+        return (action !== "replace" &&
+            getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) &&
+            (isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
+    }
+    visitScrolledToSamePageLocation(oldURL, newURL) {
+        this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
+    }
+    get location() {
+        return this.history.location;
+    }
+    get restorationIdentifier() {
+        return this.history.restorationIdentifier;
+    }
+    getActionForFormSubmission({ submitter, formElement }) {
+        return getVisitAction(submitter, formElement) || "advance";
+    }
+}
+
+var PageStage;
+(function (PageStage) {
+    PageStage[PageStage["initial"] = 0] = "initial";
+    PageStage[PageStage["loading"] = 1] = "loading";
+    PageStage[PageStage["interactive"] = 2] = "interactive";
+    PageStage[PageStage["complete"] = 3] = "complete";
+})(PageStage || (PageStage = {}));
+class PageObserver {
+    constructor(delegate) {
+        this.stage = PageStage.initial;
+        this.started = false;
+        this.interpretReadyState = () => {
+            const { readyState } = this;
+            if (readyState == "interactive") {
+                this.pageIsInteractive();
+            }
+            else if (readyState == "complete") {
+                this.pageIsComplete();
+            }
+        };
+        this.pageWillUnload = () => {
+            this.delegate.pageWillUnload();
+        };
+        this.delegate = delegate;
+    }
+    start() {
+        if (!this.started) {
+            if (this.stage == PageStage.initial) {
+                this.stage = PageStage.loading;
+            }
+            document.addEventListener("readystatechange", this.interpretReadyState, false);
+            addEventListener("pagehide", this.pageWillUnload, false);
+            this.started = true;
+        }
+    }
+    stop() {
+        if (this.started) {
+            document.removeEventListener("readystatechange", this.interpretReadyState, false);
+            removeEventListener("pagehide", this.pageWillUnload, false);
+            this.started = false;
+        }
+    }
+    pageIsInteractive() {
+        if (this.stage == PageStage.loading) {
+            this.stage = PageStage.interactive;
+            this.delegate.pageBecameInteractive();
+        }
+    }
+    pageIsComplete() {
+        this.pageIsInteractive();
+        if (this.stage == PageStage.interactive) {
+            this.stage = PageStage.complete;
+            this.delegate.pageLoaded();
+        }
+    }
+    get readyState() {
+        return document.readyState;
+    }
+}
+
+class ScrollObserver {
+    constructor(delegate) {
+        this.started = false;
+        this.onScroll = () => {
+            this.updatePosition({ x: window.pageXOffset, y: window.pageYOffset });
+        };
+        this.delegate = delegate;
+    }
+    start() {
+        if (!this.started) {
+            addEventListener("scroll", this.onScroll, false);
+            this.onScroll();
+            this.started = true;
+        }
+    }
+    stop() {
+        if (this.started) {
+            removeEventListener("scroll", this.onScroll, false);
+            this.started = false;
+        }
+    }
+    updatePosition(position) {
+        this.delegate.scrollPositionChanged(position);
+    }
+}
+
+class StreamMessageRenderer {
+    render({ fragment }) {
+        Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => document.documentElement.appendChild(fragment));
+    }
+    enteringBardo(currentPermanentElement, newPermanentElement) {
+        newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
+    }
+    leavingBardo() { }
+}
+function getPermanentElementMapForFragment(fragment) {
+    const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement);
+    const permanentElementMap = {};
+    for (const permanentElementInDocument of permanentElementsInDocument) {
+        const { id } = permanentElementInDocument;
+        for (const streamElement of fragment.querySelectorAll("turbo-stream")) {
+            const elementInStream = getPermanentElementById(streamElement.templateElement.content, id);
+            if (elementInStream) {
+                permanentElementMap[id] = [permanentElementInDocument, elementInStream];
+            }
+        }
+    }
+    return permanentElementMap;
+}
+
+class StreamObserver {
+    constructor(delegate) {
+        this.sources = new Set();
+        this.started = false;
+        this.inspectFetchResponse = ((event) => {
+            const response = fetchResponseFromEvent(event);
+            if (response && fetchResponseIsStream(response)) {
+                event.preventDefault();
+                this.receiveMessageResponse(response);
+            }
+        });
+        this.receiveMessageEvent = (event) => {
+            if (this.started && typeof event.data == "string") {
+                this.receiveMessageHTML(event.data);
+            }
+        };
+        this.delegate = delegate;
+    }
+    start() {
+        if (!this.started) {
+            this.started = true;
+            addEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
+        }
+    }
+    stop() {
+        if (this.started) {
+            this.started = false;
+            removeEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
+        }
+    }
+    connectStreamSource(source) {
+        if (!this.streamSourceIsConnected(source)) {
+            this.sources.add(source);
+            source.addEventListener("message", this.receiveMessageEvent, false);
+        }
+    }
+    disconnectStreamSource(source) {
+        if (this.streamSourceIsConnected(source)) {
+            this.sources.delete(source);
+            source.removeEventListener("message", this.receiveMessageEvent, false);
+        }
+    }
+    streamSourceIsConnected(source) {
+        return this.sources.has(source);
+    }
+    async receiveMessageResponse(response) {
+        const html = await response.responseHTML;
+        if (html) {
+            this.receiveMessageHTML(html);
+        }
+    }
+    receiveMessageHTML(html) {
+        this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
+    }
+}
+function fetchResponseFromEvent(event) {
+    var _a;
+    const fetchResponse = (_a = event.detail) === null || _a === void 0 ? void 0 : _a.fetchResponse;
+    if (fetchResponse instanceof FetchResponse) {
+        return fetchResponse;
+    }
+}
+function fetchResponseIsStream(response) {
+    var _a;
+    const contentType = (_a = response.contentType) !== null && _a !== void 0 ? _a : "";
+    return contentType.startsWith(StreamMessage.contentType);
+}
+
+class ErrorRenderer extends Renderer {
+    static renderElement(currentElement, newElement) {
+        const { documentElement, body } = document;
+        documentElement.replaceChild(newElement, body);
+    }
+    async render() {
+        this.replaceHeadAndBody();
+        this.activateScriptElements();
+    }
+    replaceHeadAndBody() {
+        const { documentElement, head } = document;
+        documentElement.replaceChild(this.newHead, head);
+        this.renderElement(this.currentElement, this.newElement);
+    }
+    activateScriptElements() {
+        for (const replaceableElement of this.scriptElements) {
+            const parentNode = replaceableElement.parentNode;
+            if (parentNode) {
+                const element = activateScriptElement(replaceableElement);
+                parentNode.replaceChild(element, replaceableElement);
+            }
+        }
+    }
+    get newHead() {
+        return this.newSnapshot.headSnapshot.element;
+    }
+    get scriptElements() {
+        return document.documentElement.querySelectorAll("script");
+    }
+}
+
+class PageRenderer extends Renderer {
+    static renderElement(currentElement, newElement) {
+        if (document.body && newElement instanceof HTMLBodyElement) {
+            document.body.replaceWith(newElement);
+        }
+        else {
+            document.documentElement.appendChild(newElement);
+        }
+    }
+    get shouldRender() {
+        return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
+    }
+    get reloadReason() {
+        if (!this.newSnapshot.isVisitable) {
+            return {
+                reason: "turbo_visit_control_is_reload",
+            };
+        }
+        if (!this.trackedElementsAreIdentical) {
+            return {
+                reason: "tracked_element_mismatch",
+            };
+        }
+    }
+    async prepareToRender() {
+        await this.mergeHead();
+    }
+    async render() {
+        if (this.willRender) {
+            await this.replaceBody();
+        }
+    }
+    finishRendering() {
+        super.finishRendering();
+        if (!this.isPreview) {
+            this.focusFirstAutofocusableElement();
+        }
+    }
+    get currentHeadSnapshot() {
+        return this.currentSnapshot.headSnapshot;
+    }
+    get newHeadSnapshot() {
+        return this.newSnapshot.headSnapshot;
+    }
+    get newElement() {
+        return this.newSnapshot.element;
+    }
+    async mergeHead() {
+        const mergedHeadElements = this.mergeProvisionalElements();
+        const newStylesheetElements = this.copyNewHeadStylesheetElements();
+        this.copyNewHeadScriptElements();
+        await mergedHeadElements;
+        await newStylesheetElements;
+    }
+    async replaceBody() {
+        await this.preservingPermanentElements(async () => {
+            this.activateNewBody();
+            await this.assignNewBody();
+        });
+    }
+    get trackedElementsAreIdentical() {
+        return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature;
+    }
+    async copyNewHeadStylesheetElements() {
+        const loadingElements = [];
+        for (const element of this.newHeadStylesheetElements) {
+            loadingElements.push(waitForLoad(element));
+            document.head.appendChild(element);
+        }
+        await Promise.all(loadingElements);
+    }
+    copyNewHeadScriptElements() {
+        for (const element of this.newHeadScriptElements) {
+            document.head.appendChild(activateScriptElement(element));
+        }
+    }
+    async mergeProvisionalElements() {
+        const newHeadElements = [...this.newHeadProvisionalElements];
+        for (const element of this.currentHeadProvisionalElements) {
+            if (!this.isCurrentElementInElementList(element, newHeadElements)) {
+                document.head.removeChild(element);
+            }
+        }
+        for (const element of newHeadElements) {
+            document.head.appendChild(element);
+        }
+    }
+    isCurrentElementInElementList(element, elementList) {
+        for (const [index, newElement] of elementList.entries()) {
+            if (element.tagName == "TITLE") {
+                if (newElement.tagName != "TITLE") {
+                    continue;
+                }
+                if (element.innerHTML == newElement.innerHTML) {
+                    elementList.splice(index, 1);
+                    return true;
+                }
+            }
+            if (newElement.isEqualNode(element)) {
+                elementList.splice(index, 1);
+                return true;
+            }
+        }
+        return false;
+    }
+    removeCurrentHeadProvisionalElements() {
+        for (const element of this.currentHeadProvisionalElements) {
+            document.head.removeChild(element);
+        }
+    }
+    copyNewHeadProvisionalElements() {
+        for (const element of this.newHeadProvisionalElements) {
+            document.head.appendChild(element);
+        }
+    }
+    activateNewBody() {
+        document.adoptNode(this.newElement);
+        this.activateNewBodyScriptElements();
+    }
+    activateNewBodyScriptElements() {
+        for (const inertScriptElement of this.newBodyScriptElements) {
+            const activatedScriptElement = activateScriptElement(inertScriptElement);
+            inertScriptElement.replaceWith(activatedScriptElement);
+        }
+    }
+    async assignNewBody() {
+        await this.renderElement(this.currentElement, this.newElement);
+    }
+    get newHeadStylesheetElements() {
+        return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
+    }
+    get newHeadScriptElements() {
+        return this.newHeadSnapshot.getScriptElementsNotInSnapshot(this.currentHeadSnapshot);
+    }
+    get currentHeadProvisionalElements() {
+        return this.currentHeadSnapshot.provisionalElements;
+    }
+    get newHeadProvisionalElements() {
+        return this.newHeadSnapshot.provisionalElements;
+    }
+    get newBodyScriptElements() {
+        return this.newElement.querySelectorAll("script");
+    }
+}
+
+class SnapshotCache {
+    constructor(size) {
+        this.keys = [];
+        this.snapshots = {};
+        this.size = size;
+    }
+    has(location) {
+        return toCacheKey(location) in this.snapshots;
+    }
+    get(location) {
+        if (this.has(location)) {
+            const snapshot = this.read(location);
+            this.touch(location);
+            return snapshot;
+        }
+    }
+    put(location, snapshot) {
+        this.write(location, snapshot);
+        this.touch(location);
+        return snapshot;
+    }
+    clear() {
+        this.snapshots = {};
+    }
+    read(location) {
+        return this.snapshots[toCacheKey(location)];
+    }
+    write(location, snapshot) {
+        this.snapshots[toCacheKey(location)] = snapshot;
+    }
+    touch(location) {
+        const key = toCacheKey(location);
+        const index = this.keys.indexOf(key);
+        if (index > -1)
+            this.keys.splice(index, 1);
+        this.keys.unshift(key);
+        this.trim();
+    }
+    trim() {
+        for (const key of this.keys.splice(this.size)) {
+            delete this.snapshots[key];
+        }
+    }
+}
+
+class PageView extends View {
+    constructor() {
+        super(...arguments);
+        this.snapshotCache = new SnapshotCache(10);
+        this.lastRenderedLocation = new URL(location.href);
+        this.forceReloaded = false;
+    }
+    renderPage(snapshot, isPreview = false, willRender = true, visit) {
+        const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
+        if (!renderer.shouldRender) {
+            this.forceReloaded = true;
+        }
+        else {
+            visit === null || visit === void 0 ? void 0 : visit.changeHistory();
+        }
+        return this.render(renderer);
+    }
+    renderError(snapshot, visit) {
+        visit === null || visit === void 0 ? void 0 : visit.changeHistory();
+        const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
+        return this.render(renderer);
+    }
+    clearSnapshotCache() {
+        this.snapshotCache.clear();
+    }
+    async cacheSnapshot(snapshot = this.snapshot) {
+        if (snapshot.isCacheable) {
+            this.delegate.viewWillCacheSnapshot();
+            const { lastRenderedLocation: location } = this;
+            await nextEventLoopTick();
+            const cachedSnapshot = snapshot.clone();
+            this.snapshotCache.put(location, cachedSnapshot);
+            return cachedSnapshot;
+        }
+    }
+    getCachedSnapshotForLocation(location) {
+        return this.snapshotCache.get(location);
+    }
+    get snapshot() {
+        return PageSnapshot.fromElement(this.element);
+    }
+}
+
+class Preloader {
+    constructor(delegate) {
+        this.selector = "a[data-turbo-preload]";
+        this.delegate = delegate;
+    }
+    get snapshotCache() {
+        return this.delegate.navigator.view.snapshotCache;
+    }
+    start() {
+        if (document.readyState === "loading") {
+            return document.addEventListener("DOMContentLoaded", () => {
+                this.preloadOnLoadLinksForView(document.body);
+            });
+        }
+        else {
+            this.preloadOnLoadLinksForView(document.body);
+        }
+    }
+    preloadOnLoadLinksForView(element) {
+        for (const link of element.querySelectorAll(this.selector)) {
+            this.preloadURL(link);
+        }
+    }
+    async preloadURL(link) {
+        const location = new URL(link.href);
+        if (this.snapshotCache.has(location)) {
+            return;
+        }
+        try {
+            const response = await fetch(location.toString(), { headers: { "VND.PREFETCH": "true", Accept: "text/html" } });
+            const responseText = await response.text();
+            const snapshot = PageSnapshot.fromHTMLString(responseText);
+            this.snapshotCache.put(location, snapshot);
+        }
+        catch (_) {
+        }
+    }
+}
+
+class Session {
+    constructor() {
+        this.navigator = new Navigator(this);
+        this.history = new History(this);
+        this.preloader = new Preloader(this);
+        this.view = new PageView(this, document.documentElement);
+        this.adapter = new BrowserAdapter(this);
+        this.pageObserver = new PageObserver(this);
+        this.cacheObserver = new CacheObserver();
+        this.linkClickObserver = new LinkClickObserver(this, window);
+        this.formSubmitObserver = new FormSubmitObserver(this, document);
+        this.scrollObserver = new ScrollObserver(this);
+        this.streamObserver = new StreamObserver(this);
+        this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
+        this.frameRedirector = new FrameRedirector(this, document.documentElement);
+        this.streamMessageRenderer = new StreamMessageRenderer();
+        this.drive = true;
+        this.enabled = true;
+        this.progressBarDelay = 500;
+        this.started = false;
+        this.formMode = "on";
+    }
+    start() {
+        if (!this.started) {
+            this.pageObserver.start();
+            this.cacheObserver.start();
+            this.formLinkClickObserver.start();
+            this.linkClickObserver.start();
+            this.formSubmitObserver.start();
+            this.scrollObserver.start();
+            this.streamObserver.start();
+            this.frameRedirector.start();
+            this.history.start();
+            this.preloader.start();
+            this.started = true;
+            this.enabled = true;
+        }
+    }
+    disable() {
+        this.enabled = false;
+    }
+    stop() {
+        if (this.started) {
+            this.pageObserver.stop();
+            this.cacheObserver.stop();
+            this.formLinkClickObserver.stop();
+            this.linkClickObserver.stop();
+            this.formSubmitObserver.stop();
+            this.scrollObserver.stop();
+            this.streamObserver.stop();
+            this.frameRedirector.stop();
+            this.history.stop();
+            this.started = false;
+        }
+    }
+    registerAdapter(adapter) {
+        this.adapter = adapter;
+    }
+    visit(location, options = {}) {
+        const frameElement = options.frame ? document.getElementById(options.frame) : null;
+        if (frameElement instanceof FrameElement) {
+            frameElement.src = location.toString();
+            frameElement.loaded;
+        }
+        else {
+            this.navigator.proposeVisit(expandURL(location), options);
+        }
+    }
+    connectStreamSource(source) {
+        this.streamObserver.connectStreamSource(source);
+    }
+    disconnectStreamSource(source) {
+        this.streamObserver.disconnectStreamSource(source);
+    }
+    renderStreamMessage(message) {
+        this.streamMessageRenderer.render(StreamMessage.wrap(message));
+    }
+    clearCache() {
+        this.view.clearSnapshotCache();
+    }
+    setProgressBarDelay(delay) {
+        this.progressBarDelay = delay;
+    }
+    setFormMode(mode) {
+        this.formMode = mode;
+    }
+    get location() {
+        return this.history.location;
+    }
+    get restorationIdentifier() {
+        return this.history.restorationIdentifier;
+    }
+    historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
+        if (this.enabled) {
+            this.navigator.startVisit(location, restorationIdentifier, {
+                action: "restore",
+                historyChanged: true,
+            });
+        }
+        else {
+            this.adapter.pageInvalidated({
+                reason: "turbo_disabled",
+            });
+        }
+    }
+    scrollPositionChanged(position) {
+        this.history.updateRestorationData({ scrollPosition: position });
+    }
+    willSubmitFormLinkToLocation(link, location) {
+        return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation);
+    }
+    submittedFormLinkToLocation() { }
+    willFollowLinkToLocation(link, location, event) {
+        return (this.elementIsNavigatable(link) &&
+            locationIsVisitable(location, this.snapshot.rootLocation) &&
+            this.applicationAllowsFollowingLinkToLocation(link, location, event));
+    }
+    followedLinkToLocation(link, location) {
+        const action = this.getActionForLink(link);
+        const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
+        this.visit(location.href, { action, acceptsStreamResponse });
+    }
+    allowsVisitingLocationWithAction(location, action) {
+        return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
+    }
+    visitProposedToLocation(location, options) {
+        extendURLWithDeprecatedProperties(location);
+        this.adapter.visitProposedToLocation(location, options);
+    }
+    visitStarted(visit) {
+        if (!visit.acceptsStreamResponse) {
+            markAsBusy(document.documentElement);
+        }
+        extendURLWithDeprecatedProperties(visit.location);
+        if (!visit.silent) {
+            this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
+        }
+    }
+    visitCompleted(visit) {
+        clearBusyState(document.documentElement);
+        this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
+    }
+    locationWithActionIsSamePage(location, action) {
+        return this.navigator.locationWithActionIsSamePage(location, action);
+    }
+    visitScrolledToSamePageLocation(oldURL, newURL) {
+        this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
+    }
+    willSubmitForm(form, submitter) {
+        const action = getAction(form, submitter);
+        return (this.submissionIsNavigatable(form, submitter) &&
+            locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
+    }
+    formSubmitted(form, submitter) {
+        this.navigator.submitForm(form, submitter);
+    }
+    pageBecameInteractive() {
+        this.view.lastRenderedLocation = this.location;
+        this.notifyApplicationAfterPageLoad();
+    }
+    pageLoaded() {
+        this.history.assumeControlOfScrollRestoration();
+    }
+    pageWillUnload() {
+        this.history.relinquishControlOfScrollRestoration();
+    }
+    receivedMessageFromStream(message) {
+        this.renderStreamMessage(message);
+    }
+    viewWillCacheSnapshot() {
+        var _a;
+        if (!((_a = this.navigator.currentVisit) === null || _a === void 0 ? void 0 : _a.silent)) {
+            this.notifyApplicationBeforeCachingSnapshot();
+        }
+    }
+    allowsImmediateRender({ element }, options) {
+        const event = this.notifyApplicationBeforeRender(element, options);
+        const { defaultPrevented, detail: { render }, } = event;
+        if (this.view.renderer && render) {
+            this.view.renderer.renderElement = render;
+        }
+        return !defaultPrevented;
+    }
+    viewRenderedSnapshot(_snapshot, _isPreview) {
+        this.view.lastRenderedLocation = this.history.location;
+        this.notifyApplicationAfterRender();
+    }
+    preloadOnLoadLinksForView(element) {
+        this.preloader.preloadOnLoadLinksForView(element);
+    }
+    viewInvalidated(reason) {
+        this.adapter.pageInvalidated(reason);
+    }
+    frameLoaded(frame) {
+        this.notifyApplicationAfterFrameLoad(frame);
+    }
+    frameRendered(fetchResponse, frame) {
+        this.notifyApplicationAfterFrameRender(fetchResponse, frame);
+    }
+    applicationAllowsFollowingLinkToLocation(link, location, ev) {
+        const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
+        return !event.defaultPrevented;
+    }
+    applicationAllowsVisitingLocation(location) {
+        const event = this.notifyApplicationBeforeVisitingLocation(location);
+        return !event.defaultPrevented;
+    }
+    notifyApplicationAfterClickingLinkToLocation(link, location, event) {
+        return dispatch("turbo:click", {
+            target: link,
+            detail: { url: location.href, originalEvent: event },
+            cancelable: true,
+        });
+    }
+    notifyApplicationBeforeVisitingLocation(location) {
+        return dispatch("turbo:before-visit", {
+            detail: { url: location.href },
+            cancelable: true,
+        });
+    }
+    notifyApplicationAfterVisitingLocation(location, action) {
+        return dispatch("turbo:visit", { detail: { url: location.href, action } });
+    }
+    notifyApplicationBeforeCachingSnapshot() {
+        return dispatch("turbo:before-cache");
+    }
+    notifyApplicationBeforeRender(newBody, options) {
+        return dispatch("turbo:before-render", {
+            detail: Object.assign({ newBody }, options),
+            cancelable: true,
+        });
+    }
+    notifyApplicationAfterRender() {
+        return dispatch("turbo:render");
+    }
+    notifyApplicationAfterPageLoad(timing = {}) {
+        return dispatch("turbo:load", {
+            detail: { url: this.location.href, timing },
+        });
+    }
+    notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
+        dispatchEvent(new HashChangeEvent("hashchange", {
+            oldURL: oldURL.toString(),
+            newURL: newURL.toString(),
+        }));
+    }
+    notifyApplicationAfterFrameLoad(frame) {
+        return dispatch("turbo:frame-load", { target: frame });
+    }
+    notifyApplicationAfterFrameRender(fetchResponse, frame) {
+        return dispatch("turbo:frame-render", {
+            detail: { fetchResponse },
+            target: frame,
+            cancelable: true,
+        });
+    }
+    submissionIsNavigatable(form, submitter) {
+        if (this.formMode == "off") {
+            return false;
+        }
+        else {
+            const submitterIsNavigatable = submitter ? this.elementIsNavigatable(submitter) : true;
+            if (this.formMode == "optin") {
+                return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null;
+            }
+            else {
+                return submitterIsNavigatable && this.elementIsNavigatable(form);
+            }
+        }
+    }
+    elementIsNavigatable(element) {
+        const container = findClosestRecursively(element, "[data-turbo]");
+        const withinFrame = findClosestRecursively(element, "turbo-frame");
+        if (this.drive || withinFrame) {
+            if (container) {
+                return container.getAttribute("data-turbo") != "false";
+            }
+            else {
+                return true;
+            }
+        }
+        else {
+            if (container) {
+                return container.getAttribute("data-turbo") == "true";
+            }
+            else {
+                return false;
+            }
+        }
+    }
+    getActionForLink(link) {
+        return getVisitAction(link) || "advance";
+    }
+    get snapshot() {
+        return this.view.snapshot;
+    }
+}
+function extendURLWithDeprecatedProperties(url) {
+    Object.defineProperties(url, deprecatedLocationPropertyDescriptors);
+}
+const deprecatedLocationPropertyDescriptors = {
+    absoluteURL: {
+        get() {
+            return this.toString();
+        },
+    },
+};
+
+class Cache {
+    constructor(session) {
+        this.session = session;
+    }
+    clear() {
+        this.session.clearCache();
+    }
+    resetCacheControl() {
+        this.setCacheControl("");
+    }
+    exemptPageFromCache() {
+        this.setCacheControl("no-cache");
+    }
+    exemptPageFromPreview() {
+        this.setCacheControl("no-preview");
+    }
+    setCacheControl(value) {
+        setMetaContent("turbo-cache-control", value);
+    }
+}
+
+const StreamActions = {
+    after() {
+        this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
+    },
+    append() {
+        this.removeDuplicateTargetChildren();
+        this.targetElements.forEach((e) => e.append(this.templateContent));
+    },
+    before() {
+        this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
+    },
+    prepend() {
+        this.removeDuplicateTargetChildren();
+        this.targetElements.forEach((e) => e.prepend(this.templateContent));
+    },
+    remove() {
+        this.targetElements.forEach((e) => e.remove());
+    },
+    replace() {
+        this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
+    },
+    update() {
+        this.targetElements.forEach((targetElement) => {
+            targetElement.innerHTML = "";
+            targetElement.append(this.templateContent);
+        });
+    },
+};
+
+const session = new Session();
+const cache = new Cache(session);
+const { navigator: navigator$1 } = session;
+function start() {
+    session.start();
+}
+function registerAdapter(adapter) {
+    session.registerAdapter(adapter);
+}
+function visit(location, options) {
+    session.visit(location, options);
+}
+function connectStreamSource(source) {
+    session.connectStreamSource(source);
+}
+function disconnectStreamSource(source) {
+    session.disconnectStreamSource(source);
+}
+function renderStreamMessage(message) {
+    session.renderStreamMessage(message);
+}
+function clearCache() {
+    console.warn("Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`");
+    session.clearCache();
+}
+function setProgressBarDelay(delay) {
+    session.setProgressBarDelay(delay);
+}
+function setConfirmMethod(confirmMethod) {
+    FormSubmission.confirmMethod = confirmMethod;
+}
+function setFormMode(mode) {
+    session.setFormMode(mode);
+}
+
+var Turbo = /*#__PURE__*/Object.freeze({
+    __proto__: null,
+    navigator: navigator$1,
+    session: session,
+    cache: cache,
+    PageRenderer: PageRenderer,
+    PageSnapshot: PageSnapshot,
+    FrameRenderer: FrameRenderer,
+    start: start,
+    registerAdapter: registerAdapter,
+    visit: visit,
+    connectStreamSource: connectStreamSource,
+    disconnectStreamSource: disconnectStreamSource,
+    renderStreamMessage: renderStreamMessage,
+    clearCache: clearCache,
+    setProgressBarDelay: setProgressBarDelay,
+    setConfirmMethod: setConfirmMethod,
+    setFormMode: setFormMode,
+    StreamActions: StreamActions
+});
+
+class TurboFrameMissingError extends Error {
+}
+
+class FrameController {
+    constructor(element) {
+        this.fetchResponseLoaded = (_fetchResponse) => { };
+        this.currentFetchRequest = null;
+        this.resolveVisitPromise = () => { };
+        this.connected = false;
+        this.hasBeenLoaded = false;
+        this.ignoredAttributes = new Set();
+        this.action = null;
+        this.visitCachedSnapshot = ({ element }) => {
+            const frame = element.querySelector("#" + this.element.id);
+            if (frame && this.previousFrameElement) {
+                frame.replaceChildren(...this.previousFrameElement.children);
+            }
+            delete this.previousFrameElement;
+        };
+        this.element = element;
+        this.view = new FrameView(this, this.element);
+        this.appearanceObserver = new AppearanceObserver(this, this.element);
+        this.formLinkClickObserver = new FormLinkClickObserver(this, this.element);
+        this.linkInterceptor = new LinkInterceptor(this, this.element);
+        this.restorationIdentifier = uuid();
+        this.formSubmitObserver = new FormSubmitObserver(this, this.element);
+    }
+    connect() {
+        if (!this.connected) {
+            this.connected = true;
+            if (this.loadingStyle == FrameLoadingStyle.lazy) {
+                this.appearanceObserver.start();
+            }
+            else {
+                this.loadSourceURL();
+            }
+            this.formLinkClickObserver.start();
+            this.linkInterceptor.start();
+            this.formSubmitObserver.start();
+        }
+    }
+    disconnect() {
+        if (this.connected) {
+            this.connected = false;
+            this.appearanceObserver.stop();
+            this.formLinkClickObserver.stop();
+            this.linkInterceptor.stop();
+            this.formSubmitObserver.stop();
+        }
+    }
+    disabledChanged() {
+        if (this.loadingStyle == FrameLoadingStyle.eager) {
+            this.loadSourceURL();
+        }
+    }
+    sourceURLChanged() {
+        if (this.isIgnoringChangesTo("src"))
+            return;
+        if (this.element.isConnected) {
+            this.complete = false;
+        }
+        if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
+            this.loadSourceURL();
+        }
+    }
+    sourceURLReloaded() {
+        const { src } = this.element;
+        this.ignoringChangesToAttribute("complete", () => {
+            this.element.removeAttribute("complete");
+        });
+        this.element.src = null;
+        this.element.src = src;
+        return this.element.loaded;
+    }
+    completeChanged() {
+        if (this.isIgnoringChangesTo("complete"))
+            return;
+        this.loadSourceURL();
+    }
+    loadingStyleChanged() {
+        if (this.loadingStyle == FrameLoadingStyle.lazy) {
+            this.appearanceObserver.start();
+        }
+        else {
+            this.appearanceObserver.stop();
+            this.loadSourceURL();
+        }
+    }
+    async loadSourceURL() {
+        if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
+            this.element.loaded = this.visit(expandURL(this.sourceURL));
+            this.appearanceObserver.stop();
+            await this.element.loaded;
+            this.hasBeenLoaded = true;
+        }
+    }
+    async loadResponse(fetchResponse) {
+        if (fetchResponse.redirected || (fetchResponse.succeeded && fetchResponse.isHTML)) {
+            this.sourceURL = fetchResponse.response.url;
+        }
+        try {
+            const html = await fetchResponse.responseHTML;
+            if (html) {
+                const document = parseHTMLDocument(html);
+                const pageSnapshot = PageSnapshot.fromDocument(document);
+                if (pageSnapshot.isVisitable) {
+                    await this.loadFrameResponse(fetchResponse, document);
+                }
+                else {
+                    await this.handleUnvisitableFrameResponse(fetchResponse);
+                }
+            }
+        }
+        finally {
+            this.fetchResponseLoaded = () => { };
+        }
+    }
+    elementAppearedInViewport(element) {
+        this.proposeVisitIfNavigatedWithAction(element, element);
+        this.loadSourceURL();
+    }
+    willSubmitFormLinkToLocation(link) {
+        return this.shouldInterceptNavigation(link);
+    }
+    submittedFormLinkToLocation(link, _location, form) {
+        const frame = this.findFrameElement(link);
+        if (frame)
+            form.setAttribute("data-turbo-frame", frame.id);
+    }
+    shouldInterceptLinkClick(element, _location, _event) {
+        return this.shouldInterceptNavigation(element);
+    }
+    linkClickIntercepted(element, location) {
+        this.navigateFrame(element, location);
+    }
+    willSubmitForm(element, submitter) {
+        return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter);
+    }
+    formSubmitted(element, submitter) {
+        if (this.formSubmission) {
+            this.formSubmission.stop();
+        }
+        this.formSubmission = new FormSubmission(this, element, submitter);
+        const { fetchRequest } = this.formSubmission;
+        this.prepareRequest(fetchRequest);
+        this.formSubmission.start();
+    }
+    prepareRequest(request) {
+        var _a;
+        request.headers["Turbo-Frame"] = this.id;
+        if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
+            request.acceptResponseType(StreamMessage.contentType);
+        }
+    }
+    requestStarted(_request) {
+        markAsBusy(this.element);
+    }
+    requestPreventedHandlingResponse(_request, _response) {
+        this.resolveVisitPromise();
+    }
+    async requestSucceededWithResponse(request, response) {
+        await this.loadResponse(response);
+        this.resolveVisitPromise();
+    }
+    async requestFailedWithResponse(request, response) {
+        await this.loadResponse(response);
+        this.resolveVisitPromise();
+    }
+    requestErrored(request, error) {
+        console.error(error);
+        this.resolveVisitPromise();
+    }
+    requestFinished(_request) {
+        clearBusyState(this.element);
+    }
+    formSubmissionStarted({ formElement }) {
+        markAsBusy(formElement, this.findFrameElement(formElement));
+    }
+    formSubmissionSucceededWithResponse(formSubmission, response) {
+        const frame = this.findFrameElement(formSubmission.formElement, formSubmission.submitter);
+        frame.delegate.proposeVisitIfNavigatedWithAction(frame, formSubmission.formElement, formSubmission.submitter);
+        frame.delegate.loadResponse(response);
+        if (!formSubmission.isSafe) {
+            session.clearCache();
+        }
+    }
+    formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
+        this.element.delegate.loadResponse(fetchResponse);
+        session.clearCache();
+    }
+    formSubmissionErrored(formSubmission, error) {
+        console.error(error);
+    }
+    formSubmissionFinished({ formElement }) {
+        clearBusyState(formElement, this.findFrameElement(formElement));
+    }
+    allowsImmediateRender({ element: newFrame }, options) {
+        const event = dispatch("turbo:before-frame-render", {
+            target: this.element,
+            detail: Object.assign({ newFrame }, options),
+            cancelable: true,
+        });
+        const { defaultPrevented, detail: { render }, } = event;
+        if (this.view.renderer && render) {
+            this.view.renderer.renderElement = render;
+        }
+        return !defaultPrevented;
+    }
+    viewRenderedSnapshot(_snapshot, _isPreview) { }
+    preloadOnLoadLinksForView(element) {
+        session.preloadOnLoadLinksForView(element);
+    }
+    viewInvalidated() { }
+    willRenderFrame(currentElement, _newElement) {
+        this.previousFrameElement = currentElement.cloneNode(true);
+    }
+    async loadFrameResponse(fetchResponse, document) {
+        const newFrameElement = await this.extractForeignFrameElement(document.body);
+        if (newFrameElement) {
+            const snapshot = new Snapshot(newFrameElement);
+            const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
+            if (this.view.renderPromise)
+                await this.view.renderPromise;
+            this.changeHistory();
+            await this.view.render(renderer);
+            this.complete = true;
+            session.frameRendered(fetchResponse, this.element);
+            session.frameLoaded(this.element);
+            this.fetchResponseLoaded(fetchResponse);
+        }
+        else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
+            this.handleFrameMissingFromResponse(fetchResponse);
+        }
+    }
+    async visit(url) {
+        var _a;
+        const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
+        (_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
+        this.currentFetchRequest = request;
+        return new Promise((resolve) => {
+            this.resolveVisitPromise = () => {
+                this.resolveVisitPromise = () => { };
+                this.currentFetchRequest = null;
+                resolve();
+            };
+            request.perform();
+        });
+    }
+    navigateFrame(element, url, submitter) {
+        const frame = this.findFrameElement(element, submitter);
+        frame.delegate.proposeVisitIfNavigatedWithAction(frame, element, submitter);
+        this.withCurrentNavigationElement(element, () => {
+            frame.src = url;
+        });
+    }
+    proposeVisitIfNavigatedWithAction(frame, element, submitter) {
+        this.action = getVisitAction(submitter, element, frame);
+        if (this.action) {
+            const pageSnapshot = PageSnapshot.fromElement(frame).clone();
+            const { visitCachedSnapshot } = frame.delegate;
+            frame.delegate.fetchResponseLoaded = (fetchResponse) => {
+                if (frame.src) {
+                    const { statusCode, redirected } = fetchResponse;
+                    const responseHTML = frame.ownerDocument.documentElement.outerHTML;
+                    const response = { statusCode, redirected, responseHTML };
+                    const options = {
+                        response,
+                        visitCachedSnapshot,
+                        willRender: false,
+                        updateHistory: false,
+                        restorationIdentifier: this.restorationIdentifier,
+                        snapshot: pageSnapshot,
+                    };
+                    if (this.action)
+                        options.action = this.action;
+                    session.visit(frame.src, options);
+                }
+            };
+        }
+    }
+    changeHistory() {
+        if (this.action) {
+            const method = getHistoryMethodForAction(this.action);
+            session.history.update(method, expandURL(this.element.src || ""), this.restorationIdentifier);
+        }
+    }
+    async handleUnvisitableFrameResponse(fetchResponse) {
+        console.warn(`The response (${fetchResponse.statusCode}) from <turbo-frame id="${this.element.id}"> is performing a full page visit due to turbo-visit-control.`);
+        await this.visitResponse(fetchResponse.response);
+    }
+    willHandleFrameMissingFromResponse(fetchResponse) {
+        this.element.setAttribute("complete", "");
+        const response = fetchResponse.response;
+        const visit = async (url, options = {}) => {
+            if (url instanceof Response) {
+                this.visitResponse(url);
+            }
+            else {
+                session.visit(url, options);
+            }
+        };
+        const event = dispatch("turbo:frame-missing", {
+            target: this.element,
+            detail: { response, visit },
+            cancelable: true,
+        });
+        return !event.defaultPrevented;
+    }
+    handleFrameMissingFromResponse(fetchResponse) {
+        this.view.missing();
+        this.throwFrameMissingError(fetchResponse);
+    }
+    throwFrameMissingError(fetchResponse) {
+        const message = `The response (${fetchResponse.statusCode}) did not contain the expected <turbo-frame id="${this.element.id}"> and will be ignored. To perform a full page visit instead, set turbo-visit-control to reload.`;
+        throw new TurboFrameMissingError(message);
+    }
+    async visitResponse(response) {
+        const wrapped = new FetchResponse(response);
+        const responseHTML = await wrapped.responseHTML;
+        const { location, redirected, statusCode } = wrapped;
+        return session.visit(location, { response: { redirected, statusCode, responseHTML } });
+    }
+    findFrameElement(element, submitter) {
+        var _a;
+        const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
+        return (_a = getFrameElementById(id)) !== null && _a !== void 0 ? _a : this.element;
+    }
+    async extractForeignFrameElement(container) {
+        let element;
+        const id = CSS.escape(this.id);
+        try {
+            element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
+            if (element) {
+                return element;
+            }
+            element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL);
+            if (element) {
+                await element.loaded;
+                return await this.extractForeignFrameElement(element);
+            }
+        }
+        catch (error) {
+            console.error(error);
+            return new FrameElement();
+        }
+        return null;
+    }
+    formActionIsVisitable(form, submitter) {
+        const action = getAction(form, submitter);
+        return locationIsVisitable(expandURL(action), this.rootLocation);
+    }
+    shouldInterceptNavigation(element, submitter) {
+        const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
+        if (element instanceof HTMLFormElement && !this.formActionIsVisitable(element, submitter)) {
+            return false;
+        }
+        if (!this.enabled || id == "_top") {
+            return false;
+        }
+        if (id) {
+            const frameElement = getFrameElementById(id);
+            if (frameElement) {
+                return !frameElement.disabled;
+            }
+        }
+        if (!session.elementIsNavigatable(element)) {
+            return false;
+        }
+        if (submitter && !session.elementIsNavigatable(submitter)) {
+            return false;
+        }
+        return true;
+    }
+    get id() {
+        return this.element.id;
+    }
+    get enabled() {
+        return !this.element.disabled;
+    }
+    get sourceURL() {
+        if (this.element.src) {
+            return this.element.src;
+        }
+    }
+    set sourceURL(sourceURL) {
+        this.ignoringChangesToAttribute("src", () => {
+            this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
+        });
+    }
+    get loadingStyle() {
+        return this.element.loading;
+    }
+    get isLoading() {
+        return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
+    }
+    get complete() {
+        return this.element.hasAttribute("complete");
+    }
+    set complete(value) {
+        this.ignoringChangesToAttribute("complete", () => {
+            if (value) {
+                this.element.setAttribute("complete", "");
+            }
+            else {
+                this.element.removeAttribute("complete");
+            }
+        });
+    }
+    get isActive() {
+        return this.element.isActive && this.connected;
+    }
+    get rootLocation() {
+        var _a;
+        const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
+        const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
+        return expandURL(root);
+    }
+    isIgnoringChangesTo(attributeName) {
+        return this.ignoredAttributes.has(attributeName);
+    }
+    ignoringChangesToAttribute(attributeName, callback) {
+        this.ignoredAttributes.add(attributeName);
+        callback();
+        this.ignoredAttributes.delete(attributeName);
+    }
+    withCurrentNavigationElement(element, callback) {
+        this.currentNavigationElement = element;
+        callback();
+        delete this.currentNavigationElement;
+    }
+}
+function getFrameElementById(id) {
+    if (id != null) {
+        const element = document.getElementById(id);
+        if (element instanceof FrameElement) {
+            return element;
+        }
+    }
+}
+function activateElement(element, currentURL) {
+    if (element) {
+        const src = element.getAttribute("src");
+        if (src != null && currentURL != null && urlsAreEqual(src, currentURL)) {
+            throw new Error(`Matching <turbo-frame id="${element.id}"> element has a source URL which references itself`);
+        }
+        if (element.ownerDocument !== document) {
+            element = document.importNode(element, true);
+        }
+        if (element instanceof FrameElement) {
+            element.connectedCallback();
+            element.disconnectedCallback();
+            return element;
+        }
+    }
+}
+
+class StreamElement extends HTMLElement {
+    static async renderElement(newElement) {
+        await newElement.performAction();
+    }
+    async connectedCallback() {
+        try {
+            await this.render();
+        }
+        catch (error) {
+            console.error(error);
+        }
+        finally {
+            this.disconnect();
+        }
+    }
+    async render() {
+        var _a;
+        return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
+            const event = this.beforeRenderEvent;
+            if (this.dispatchEvent(event)) {
+                await nextAnimationFrame();
+                await event.detail.render(this);
+            }
+        })()));
+    }
+    disconnect() {
+        try {
+            this.remove();
+        }
+        catch (_a) { }
+    }
+    removeDuplicateTargetChildren() {
+        this.duplicateChildren.forEach((c) => c.remove());
+    }
+    get duplicateChildren() {
+        var _a;
+        const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id);
+        const newChildrenIds = [...(((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || [])].filter((c) => !!c.id).map((c) => c.id);
+        return existingChildren.filter((c) => newChildrenIds.includes(c.id));
+    }
+    get performAction() {
+        if (this.action) {
+            const actionFunction = StreamActions[this.action];
+            if (actionFunction) {
+                return actionFunction;
+            }
+            this.raise("unknown action");
+        }
+        this.raise("action attribute is missing");
+    }
+    get targetElements() {
+        if (this.target) {
+            return this.targetElementsById;
+        }
+        else if (this.targets) {
+            return this.targetElementsByQuery;
+        }
+        else {
+            this.raise("target or targets attribute is missing");
+        }
+    }
+    get templateContent() {
+        return this.templateElement.content.cloneNode(true);
+    }
+    get templateElement() {
+        if (this.firstElementChild === null) {
+            const template = this.ownerDocument.createElement("template");
+            this.appendChild(template);
+            return template;
+        }
+        else if (this.firstElementChild instanceof HTMLTemplateElement) {
+            return this.firstElementChild;
+        }
+        this.raise("first child element must be a <template> element");
+    }
+    get action() {
+        return this.getAttribute("action");
+    }
+    get target() {
+        return this.getAttribute("target");
+    }
+    get targets() {
+        return this.getAttribute("targets");
+    }
+    raise(message) {
+        throw new Error(`${this.description}: ${message}`);
+    }
+    get description() {
+        var _a, _b;
+        return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
+    }
+    get beforeRenderEvent() {
+        return new CustomEvent("turbo:before-stream-render", {
+            bubbles: true,
+            cancelable: true,
+            detail: { newStream: this, render: StreamElement.renderElement },
+        });
+    }
+    get targetElementsById() {
+        var _a;
+        const element = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.getElementById(this.target);
+        if (element !== null) {
+            return [element];
+        }
+        else {
+            return [];
+        }
+    }
+    get targetElementsByQuery() {
+        var _a;
+        const elements = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.querySelectorAll(this.targets);
+        if (elements.length !== 0) {
+            return Array.prototype.slice.call(elements);
+        }
+        else {
+            return [];
+        }
+    }
+}
+
+class StreamSourceElement extends HTMLElement {
+    constructor() {
+        super(...arguments);
+        this.streamSource = null;
+    }
+    connectedCallback() {
+        this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
+        connectStreamSource(this.streamSource);
+    }
+    disconnectedCallback() {
+        if (this.streamSource) {
+            disconnectStreamSource(this.streamSource);
+        }
+    }
+    get src() {
+        return this.getAttribute("src") || "";
+    }
+}
+
+FrameElement.delegateConstructor = FrameController;
+if (customElements.get("turbo-frame") === undefined) {
+    customElements.define("turbo-frame", FrameElement);
+}
+if (customElements.get("turbo-stream") === undefined) {
+    customElements.define("turbo-stream", StreamElement);
+}
+if (customElements.get("turbo-stream-source") === undefined) {
+    customElements.define("turbo-stream-source", StreamSourceElement);
+}
+
+(() => {
+    let element = document.currentScript;
+    if (!element)
+        return;
+    if (element.hasAttribute("data-turbo-suppress-warning"))
+        return;
+    element = element.parentElement;
+    while (element) {
+        if (element == document.body) {
+            return console.warn(unindent `
+        You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
+
+        Load your application’s JavaScript bundle inside the <head> element instead. <script> elements in <body> are evaluated with each page change.
+
+        For more information, see: https://turbo.hotwired.dev/handbook/building#working-with-script-elements
+
+        ——
+        Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
+      `, element.outerHTML);
+        }
+        element = element.parentElement;
+    }
+})();
+
+window.Turbo = Turbo;
+start();
+
+export { FrameElement, FrameLoadingStyle, FrameRenderer, PageRenderer, PageSnapshot, StreamActions, StreamElement, StreamSourceElement, cache, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };
diff --git a/share/html/Elements/HeaderJavascript b/share/html/Elements/HeaderJavascript
index 9bb99bb4d9..23fbc61ce0 100644
--- a/share/html/Elements/HeaderJavascript
+++ b/share/html/Elements/HeaderJavascript
@@ -53,6 +53,7 @@ $onload => undef
 % for my $jsfile ( @js_files ) {
 <script type="text/javascript" src="<%RT->Config->Get('WebPath')%><% $jsfile %>"></script>
 % }
+<script type="module" src="/static/js/turbo.es2017-esm.min.js"></script>
 
 <script type="text/javascript"><!--
 % if ( $focus ) {
diff --git a/share/static/js/turbo.es2017-esm.min.js b/share/static/js/turbo.es2017-esm.min.js
new file mode 100644
index 0000000000..b80a58107c
--- /dev/null
+++ b/share/static/js/turbo.es2017-esm.min.js
@@ -0,0 +1,792 @@
+
+(function(){if(window.Reflect===undefined||window.customElements===undefined||window.customElements.polyfillWrapFlushCallback){return;}
+const BuiltInHTMLElement=HTMLElement;const wrapperForTheName={HTMLElement:function HTMLElement(){return Reflect.construct(BuiltInHTMLElement,[],this.constructor);},};window.HTMLElement=wrapperForTheName["HTMLElement"];HTMLElement.prototype=BuiltInHTMLElement.prototype;HTMLElement.prototype.constructor=HTMLElement;Object.setPrototypeOf(HTMLElement,BuiltInHTMLElement);})();(function(prototype){if(typeof prototype.requestSubmit=="function")return
+prototype.requestSubmit=function(submitter){if(submitter){validateSubmitter(submitter,this);submitter.click();}else{submitter=document.createElement("input");submitter.type="submit";submitter.hidden=true;this.appendChild(submitter);submitter.click();this.removeChild(submitter);}};function validateSubmitter(submitter,form){submitter instanceof HTMLElement||raise(TypeError,"parameter 1 is not of type 'HTMLElement'");submitter.type=="submit"||raise(TypeError,"The specified element is not a submit button");submitter.form==form||raise(DOMException,"The specified element is not owned by this form element","NotFoundError");}
+function raise(errorConstructor,message,name){throw new errorConstructor("Failed to execute 'requestSubmit' on 'HTMLFormElement': "+message+".",name)}})(HTMLFormElement.prototype);const submittersByForm=new WeakMap();function findSubmitterFromClickTarget(target){const element=target instanceof Element?target:target instanceof Node?target.parentElement:null;const candidate=element?element.closest("input, button"):null;return(candidate===null||candidate===void 0?void 0:candidate.type)=="submit"?candidate:null;}
+function clickCaptured(event){const submitter=findSubmitterFromClickTarget(event.target);if(submitter&&submitter.form){submittersByForm.set(submitter.form,submitter);}}
+(function(){if("submitter"in Event.prototype)
+return;let prototype=window.Event.prototype;if("SubmitEvent"in window&&/Apple Computer/.test(navigator.vendor)){prototype=window.SubmitEvent.prototype;}
+else if("SubmitEvent"in window){return;}
+addEventListener("click",clickCaptured,true);Object.defineProperty(prototype,"submitter",{get(){if(this.type=="submit"&&this.target instanceof HTMLFormElement){return submittersByForm.get(this.target);}},});})();var FrameLoadingStyle;(function(FrameLoadingStyle){FrameLoadingStyle["eager"]="eager";FrameLoadingStyle["lazy"]="lazy";})(FrameLoadingStyle||(FrameLoadingStyle={}));class FrameElement extends HTMLElement{static get observedAttributes(){return["disabled","complete","loading","src"];}
+constructor(){super();this.loaded=Promise.resolve();this.delegate=new FrameElement.delegateConstructor(this);}
+connectedCallback(){this.delegate.connect();}
+disconnectedCallback(){this.delegate.disconnect();}
+reload(){return this.delegate.sourceURLReloaded();}
+attributeChangedCallback(name){if(name=="loading"){this.delegate.loadingStyleChanged();}
+else if(name=="complete"){this.delegate.completeChanged();}
+else if(name=="src"){this.delegate.sourceURLChanged();}
+else{this.delegate.disabledChanged();}}
+get src(){return this.getAttribute("src");}
+set src(value){if(value){this.setAttribute("src",value);}
+else{this.removeAttribute("src");}}
+get loading(){return frameLoadingStyleFromString(this.getAttribute("loading")||"");}
+set loading(value){if(value){this.setAttribute("loading",value);}
+else{this.removeAttribute("loading");}}
+get disabled(){return this.hasAttribute("disabled");}
+set disabled(value){if(value){this.setAttribute("disabled","");}
+else{this.removeAttribute("disabled");}}
+get autoscroll(){return this.hasAttribute("autoscroll");}
+set autoscroll(value){if(value){this.setAttribute("autoscroll","");}
+else{this.removeAttribute("autoscroll");}}
+get complete(){return!this.delegate.isLoading;}
+get isActive(){return this.ownerDocument===document&&!this.isPreview;}
+get isPreview(){var _a,_b;return(_b=(_a=this.ownerDocument)===null||_a===void 0?void 0:_a.documentElement)===null||_b===void 0?void 0:_b.hasAttribute("data-turbo-preview");}}
+function frameLoadingStyleFromString(style){switch(style.toLowerCase()){case"lazy":return FrameLoadingStyle.lazy;default:return FrameLoadingStyle.eager;}}
+function expandURL(locatable){return new URL(locatable.toString(),document.baseURI);}
+function getAnchor(url){let anchorMatch;if(url.hash){return url.hash.slice(1);}
+else if((anchorMatch=url.href.match(/#(.*)$/))){return anchorMatch[1];}}
+function getAction(form,submitter){const action=(submitter===null||submitter===void 0?void 0:submitter.getAttribute("formaction"))||form.getAttribute("action")||form.action;return expandURL(action);}
+function getExtension(url){return(getLastPathComponent(url).match(/\.[^.]*$/)||[])[0]||"";}
+function isHTML(url){return!!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml|php))$/);}
+function isPrefixedBy(baseURL,url){const prefix=getPrefix(url);return baseURL.href===expandURL(prefix).href||baseURL.href.startsWith(prefix);}
+function locationIsVisitable(location,rootLocation){return isPrefixedBy(location,rootLocation)&&isHTML(location);}
+function getRequestURL(url){const anchor=getAnchor(url);return anchor!=null?url.href.slice(0,-(anchor.length+1)):url.href;}
+function toCacheKey(url){return getRequestURL(url);}
+function urlsAreEqual(left,right){return expandURL(left).href==expandURL(right).href;}
+function getPathComponents(url){return url.pathname.split("/").slice(1);}
+function getLastPathComponent(url){return getPathComponents(url).slice(-1)[0];}
+function getPrefix(url){return addTrailingSlash(url.origin+url.pathname);}
+function addTrailingSlash(value){return value.endsWith("/")?value:value+"/";}
+class FetchResponse{constructor(response){this.response=response;}
+get succeeded(){return this.response.ok;}
+get failed(){return!this.succeeded;}
+get clientError(){return this.statusCode>=400&&this.statusCode<=499;}
+get serverError(){return this.statusCode>=500&&this.statusCode<=599;}
+get redirected(){return this.response.redirected;}
+get location(){return expandURL(this.response.url);}
+get isHTML(){return this.contentType&&this.contentType.match(/^(?:text\/([^\s;,]+\b)?html|application\/xhtml\+xml)\b/);}
+get statusCode(){return this.response.status;}
+get contentType(){return this.header("Content-Type");}
+get responseText(){return this.response.clone().text();}
+get responseHTML(){if(this.isHTML){return this.response.clone().text();}
+else{return Promise.resolve(undefined);}}
+header(name){return this.response.headers.get(name);}}
+function activateScriptElement(element){if(element.getAttribute("data-turbo-eval")=="false"){return element;}
+else{const createdScriptElement=document.createElement("script");const cspNonce=getMetaContent("csp-nonce");if(cspNonce){createdScriptElement.nonce=cspNonce;}
+createdScriptElement.textContent=element.textContent;createdScriptElement.async=false;copyElementAttributes(createdScriptElement,element);return createdScriptElement;}}
+function copyElementAttributes(destinationElement,sourceElement){for(const{name,value}of sourceElement.attributes){destinationElement.setAttribute(name,value);}}
+function createDocumentFragment(html){const template=document.createElement("template");template.innerHTML=html;return template.content;}
+function dispatch(eventName,{target,cancelable,detail}={}){const event=new CustomEvent(eventName,{cancelable,bubbles:true,composed:true,detail,});if(target&&target.isConnected){target.dispatchEvent(event);}
+else{document.documentElement.dispatchEvent(event);}
+return event;}
+function nextAnimationFrame(){return new Promise((resolve)=>requestAnimationFrame(()=>resolve()));}
+function nextEventLoopTick(){return new Promise((resolve)=>setTimeout(()=>resolve(),0));}
+function nextMicrotask(){return Promise.resolve();}
+function parseHTMLDocument(html=""){return new DOMParser().parseFromString(html,"text/html");}
+function unindent(strings,...values){const lines=interpolate(strings,values).replace(/^\n/,"").split("\n");const match=lines[0].match(/^\s+/);const indent=match?match[0].length:0;return lines.map((line)=>line.slice(indent)).join("\n");}
+function interpolate(strings,values){return strings.reduce((result,string,i)=>{const value=values[i]==undefined?"":values[i];return result+string+value;},"");}
+function uuid(){return Array.from({length:36}).map((_,i)=>{if(i==8||i==13||i==18||i==23){return"-";}
+else if(i==14){return"4";}
+else if(i==19){return(Math.floor(Math.random()*4)+8).toString(16);}
+else{return Math.floor(Math.random()*15).toString(16);}}).join("");}
+function getAttribute(attributeName,...elements){for(const value of elements.map((element)=>element===null||element===void 0?void 0:element.getAttribute(attributeName))){if(typeof value=="string")
+return value;}
+return null;}
+function hasAttribute(attributeName,...elements){return elements.some((element)=>element&&element.hasAttribute(attributeName));}
+function markAsBusy(...elements){for(const element of elements){if(element.localName=="turbo-frame"){element.setAttribute("busy","");}
+element.setAttribute("aria-busy","true");}}
+function clearBusyState(...elements){for(const element of elements){if(element.localName=="turbo-frame"){element.removeAttribute("busy");}
+element.removeAttribute("aria-busy");}}
+function waitForLoad(element,timeoutInMilliseconds=2000){return new Promise((resolve)=>{const onComplete=()=>{element.removeEventListener("error",onComplete);element.removeEventListener("load",onComplete);resolve();};element.addEventListener("load",onComplete,{once:true});element.addEventListener("error",onComplete,{once:true});setTimeout(resolve,timeoutInMilliseconds);});}
+function getHistoryMethodForAction(action){switch(action){case"replace":return history.replaceState;case"advance":case"restore":return history.pushState;}}
+function isAction(action){return action=="advance"||action=="replace"||action=="restore";}
+function getVisitAction(...elements){const action=getAttribute("data-turbo-action",...elements);return isAction(action)?action:null;}
+function getMetaElement(name){return document.querySelector(`meta[name="${name}"]`);}
+function getMetaContent(name){const element=getMetaElement(name);return element&&element.content;}
+function setMetaContent(name,content){let element=getMetaElement(name);if(!element){element=document.createElement("meta");element.setAttribute("name",name);document.head.appendChild(element);}
+element.setAttribute("content",content);return element;}
+function findClosestRecursively(element,selector){var _a;if(element instanceof Element){return(element.closest(selector)||findClosestRecursively(element.assignedSlot||((_a=element.getRootNode())===null||_a===void 0?void 0:_a.host),selector));}}
+var FetchMethod;(function(FetchMethod){FetchMethod[FetchMethod["get"]=0]="get";FetchMethod[FetchMethod["post"]=1]="post";FetchMethod[FetchMethod["put"]=2]="put";FetchMethod[FetchMethod["patch"]=3]="patch";FetchMethod[FetchMethod["delete"]=4]="delete";})(FetchMethod||(FetchMethod={}));function fetchMethodFromString(method){switch(method.toLowerCase()){case"get":return FetchMethod.get;case"post":return FetchMethod.post;case"put":return FetchMethod.put;case"patch":return FetchMethod.patch;case"delete":return FetchMethod.delete;}}
+class FetchRequest{constructor(delegate,method,location,body=new URLSearchParams(),target=null){this.abortController=new AbortController();this.resolveRequestPromise=(_value)=>{};this.delegate=delegate;this.method=method;this.headers=this.defaultHeaders;this.body=body;this.url=location;this.target=target;}
+get location(){return this.url;}
+get params(){return this.url.searchParams;}
+get entries(){return this.body?Array.from(this.body.entries()):[];}
+cancel(){this.abortController.abort();}
+async perform(){const{fetchOptions}=this;this.delegate.prepareRequest(this);await this.allowRequestToBeIntercepted(fetchOptions);try{this.delegate.requestStarted(this);const response=await fetch(this.url.href,fetchOptions);return await this.receive(response);}
+catch(error){if(error.name!=="AbortError"){if(this.willDelegateErrorHandling(error)){this.delegate.requestErrored(this,error);}
+throw error;}}
+finally{this.delegate.requestFinished(this);}}
+async receive(response){const fetchResponse=new FetchResponse(response);const event=dispatch("turbo:before-fetch-response",{cancelable:true,detail:{fetchResponse},target:this.target,});if(event.defaultPrevented){this.delegate.requestPreventedHandlingResponse(this,fetchResponse);}
+else if(fetchResponse.succeeded){this.delegate.requestSucceededWithResponse(this,fetchResponse);}
+else{this.delegate.requestFailedWithResponse(this,fetchResponse);}
+return fetchResponse;}
+get fetchOptions(){var _a;return{method:FetchMethod[this.method].toUpperCase(),credentials:"same-origin",headers:this.headers,redirect:"follow",body:this.isSafe?null:this.body,signal:this.abortSignal,referrer:(_a=this.delegate.referrer)===null||_a===void 0?void 0:_a.href,};}
+get defaultHeaders(){return{Accept:"text/html, application/xhtml+xml",};}
+get isSafe(){return this.method===FetchMethod.get;}
+get abortSignal(){return this.abortController.signal;}
+acceptResponseType(mimeType){this.headers["Accept"]=[mimeType,this.headers["Accept"]].join(", ");}
+async allowRequestToBeIntercepted(fetchOptions){const requestInterception=new Promise((resolve)=>(this.resolveRequestPromise=resolve));const event=dispatch("turbo:before-fetch-request",{cancelable:true,detail:{fetchOptions,url:this.url,resume:this.resolveRequestPromise,},target:this.target,});if(event.defaultPrevented)
+await requestInterception;}
+willDelegateErrorHandling(error){const event=dispatch("turbo:fetch-request-error",{target:this.target,cancelable:true,detail:{request:this,error:error},});return!event.defaultPrevented;}}
+class AppearanceObserver{constructor(delegate,element){this.started=false;this.intersect=(entries)=>{const lastEntry=entries.slice(-1)[0];if(lastEntry===null||lastEntry===void 0?void 0:lastEntry.isIntersecting){this.delegate.elementAppearedInViewport(this.element);}};this.delegate=delegate;this.element=element;this.intersectionObserver=new IntersectionObserver(this.intersect);}
+start(){if(!this.started){this.started=true;this.intersectionObserver.observe(this.element);}}
+stop(){if(this.started){this.started=false;this.intersectionObserver.unobserve(this.element);}}}
+class StreamMessage{static wrap(message){if(typeof message=="string"){return new this(createDocumentFragment(message));}
+else{return message;}}
+constructor(fragment){this.fragment=importStreamElements(fragment);}}
+StreamMessage.contentType="text/vnd.turbo-stream.html";function importStreamElements(fragment){for(const element of fragment.querySelectorAll("turbo-stream")){const streamElement=document.importNode(element,true);for(const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")){inertScriptElement.replaceWith(activateScriptElement(inertScriptElement));}
+element.replaceWith(streamElement);}
+return fragment;}
+var FormSubmissionState;(function(FormSubmissionState){FormSubmissionState[FormSubmissionState["initialized"]=0]="initialized";FormSubmissionState[FormSubmissionState["requesting"]=1]="requesting";FormSubmissionState[FormSubmissionState["waiting"]=2]="waiting";FormSubmissionState[FormSubmissionState["receiving"]=3]="receiving";FormSubmissionState[FormSubmissionState["stopping"]=4]="stopping";FormSubmissionState[FormSubmissionState["stopped"]=5]="stopped";})(FormSubmissionState||(FormSubmissionState={}));var FormEnctype;(function(FormEnctype){FormEnctype["urlEncoded"]="application/x-www-form-urlencoded";FormEnctype["multipart"]="multipart/form-data";FormEnctype["plain"]="text/plain";})(FormEnctype||(FormEnctype={}));function formEnctypeFromString(encoding){switch(encoding.toLowerCase()){case FormEnctype.multipart:return FormEnctype.multipart;case FormEnctype.plain:return FormEnctype.plain;default:return FormEnctype.urlEncoded;}}
+class FormSubmission{static confirmMethod(message,_element,_submitter){return Promise.resolve(confirm(message));}
+constructor(delegate,formElement,submitter,mustRedirect=false){this.state=FormSubmissionState.initialized;this.delegate=delegate;this.formElement=formElement;this.submitter=submitter;this.formData=buildFormData(formElement,submitter);this.location=expandURL(this.action);if(this.method==FetchMethod.get){mergeFormDataEntries(this.location,[...this.body.entries()]);}
+this.fetchRequest=new FetchRequest(this,this.method,this.location,this.body,this.formElement);this.mustRedirect=mustRedirect;}
+get method(){var _a;const method=((_a=this.submitter)===null||_a===void 0?void 0:_a.getAttribute("formmethod"))||this.formElement.getAttribute("method")||"";return fetchMethodFromString(method.toLowerCase())||FetchMethod.get;}
+get action(){var _a;const formElementAction=typeof this.formElement.action==="string"?this.formElement.action:null;if((_a=this.submitter)===null||_a===void 0?void 0:_a.hasAttribute("formaction")){return this.submitter.getAttribute("formaction")||"";}
+else{return this.formElement.getAttribute("action")||formElementAction||"";}}
+get body(){if(this.enctype==FormEnctype.urlEncoded||this.method==FetchMethod.get){return new URLSearchParams(this.stringFormData);}
+else{return this.formData;}}
+get enctype(){var _a;return formEnctypeFromString(((_a=this.submitter)===null||_a===void 0?void 0:_a.getAttribute("formenctype"))||this.formElement.enctype);}
+get isSafe(){return this.fetchRequest.isSafe;}
+get stringFormData(){return[...this.formData].reduce((entries,[name,value])=>{return entries.concat(typeof value=="string"?[[name,value]]:[]);},[]);}
+async start(){const{initialized,requesting}=FormSubmissionState;const confirmationMessage=getAttribute("data-turbo-confirm",this.submitter,this.formElement);if(typeof confirmationMessage==="string"){const answer=await FormSubmission.confirmMethod(confirmationMessage,this.formElement,this.submitter);if(!answer){return;}}
+if(this.state==initialized){this.state=requesting;return this.fetchRequest.perform();}}
+stop(){const{stopping,stopped}=FormSubmissionState;if(this.state!=stopping&&this.state!=stopped){this.state=stopping;this.fetchRequest.cancel();return true;}}
+prepareRequest(request){if(!request.isSafe){const token=getCookieValue(getMetaContent("csrf-param"))||getMetaContent("csrf-token");if(token){request.headers["X-CSRF-Token"]=token;}}
+if(this.requestAcceptsTurboStreamResponse(request)){request.acceptResponseType(StreamMessage.contentType);}}
+requestStarted(_request){var _a;this.state=FormSubmissionState.waiting;(_a=this.submitter)===null||_a===void 0?void 0:_a.setAttribute("disabled","");this.setSubmitsWith();dispatch("turbo:submit-start",{target:this.formElement,detail:{formSubmission:this},});this.delegate.formSubmissionStarted(this);}
+requestPreventedHandlingResponse(request,response){this.result={success:response.succeeded,fetchResponse:response};}
+requestSucceededWithResponse(request,response){if(response.clientError||response.serverError){this.delegate.formSubmissionFailedWithResponse(this,response);}
+else if(this.requestMustRedirect(request)&&responseSucceededWithoutRedirect(response)){const error=new Error("Form responses must redirect to another location");this.delegate.formSubmissionErrored(this,error);}
+else{this.state=FormSubmissionState.receiving;this.result={success:true,fetchResponse:response};this.delegate.formSubmissionSucceededWithResponse(this,response);}}
+requestFailedWithResponse(request,response){this.result={success:false,fetchResponse:response};this.delegate.formSubmissionFailedWithResponse(this,response);}
+requestErrored(request,error){this.result={success:false,error};this.delegate.formSubmissionErrored(this,error);}
+requestFinished(_request){var _a;this.state=FormSubmissionState.stopped;(_a=this.submitter)===null||_a===void 0?void 0:_a.removeAttribute("disabled");this.resetSubmitterText();dispatch("turbo:submit-end",{target:this.formElement,detail:Object.assign({formSubmission:this},this.result),});this.delegate.formSubmissionFinished(this);}
+setSubmitsWith(){if(!this.submitter||!this.submitsWith)
+return;if(this.submitter.matches("button")){this.originalSubmitText=this.submitter.innerHTML;this.submitter.innerHTML=this.submitsWith;}
+else if(this.submitter.matches("input")){const input=this.submitter;this.originalSubmitText=input.value;input.value=this.submitsWith;}}
+resetSubmitterText(){if(!this.submitter||!this.originalSubmitText)
+return;if(this.submitter.matches("button")){this.submitter.innerHTML=this.originalSubmitText;}
+else if(this.submitter.matches("input")){const input=this.submitter;input.value=this.originalSubmitText;}}
+requestMustRedirect(request){return!request.isSafe&&this.mustRedirect;}
+requestAcceptsTurboStreamResponse(request){return!request.isSafe||hasAttribute("data-turbo-stream",this.submitter,this.formElement);}
+get submitsWith(){var _a;return(_a=this.submitter)===null||_a===void 0?void 0:_a.getAttribute("data-turbo-submits-with");}}
+function buildFormData(formElement,submitter){const formData=new FormData(formElement);const name=submitter===null||submitter===void 0?void 0:submitter.getAttribute("name");const value=submitter===null||submitter===void 0?void 0:submitter.getAttribute("value");if(name){formData.append(name,value||"");}
+return formData;}
+function getCookieValue(cookieName){if(cookieName!=null){const cookies=document.cookie?document.cookie.split("; "):[];const cookie=cookies.find((cookie)=>cookie.startsWith(cookieName));if(cookie){const value=cookie.split("=").slice(1).join("=");return value?decodeURIComponent(value):undefined;}}}
+function responseSucceededWithoutRedirect(response){return response.statusCode==200&&!response.redirected;}
+function mergeFormDataEntries(url,entries){const searchParams=new URLSearchParams();for(const[name,value]of entries){if(value instanceof File)
+continue;searchParams.append(name,value);}
+url.search=searchParams.toString();return url;}
+class Snapshot{constructor(element){this.element=element;}
+get activeElement(){return this.element.ownerDocument.activeElement;}
+get children(){return[...this.element.children];}
+hasAnchor(anchor){return this.getElementForAnchor(anchor)!=null;}
+getElementForAnchor(anchor){return anchor?this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`):null;}
+get isConnected(){return this.element.isConnected;}
+get firstAutofocusableElement(){const inertDisabledOrHidden="[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";for(const element of this.element.querySelectorAll("[autofocus]")){if(element.closest(inertDisabledOrHidden)==null)
+return element;else
+continue;}
+return null;}
+get permanentElements(){return queryPermanentElementsAll(this.element);}
+getPermanentElementById(id){return getPermanentElementById(this.element,id);}
+getPermanentElementMapForSnapshot(snapshot){const permanentElementMap={};for(const currentPermanentElement of this.permanentElements){const{id}=currentPermanentElement;const newPermanentElement=snapshot.getPermanentElementById(id);if(newPermanentElement){permanentElementMap[id]=[currentPermanentElement,newPermanentElement];}}
+return permanentElementMap;}}
+function getPermanentElementById(node,id){return node.querySelector(`#${id}[data-turbo-permanent]`);}
+function queryPermanentElementsAll(node){return node.querySelectorAll("[id][data-turbo-permanent]");}
+class FormSubmitObserver{constructor(delegate,eventTarget){this.started=false;this.submitCaptured=()=>{this.eventTarget.removeEventListener("submit",this.submitBubbled,false);this.eventTarget.addEventListener("submit",this.submitBubbled,false);};this.submitBubbled=((event)=>{if(!event.defaultPrevented){const form=event.target instanceof HTMLFormElement?event.target:undefined;const submitter=event.submitter||undefined;if(form&&submissionDoesNotDismissDialog(form,submitter)&&submissionDoesNotTargetIFrame(form,submitter)&&this.delegate.willSubmitForm(form,submitter)){event.preventDefault();event.stopImmediatePropagation();this.delegate.formSubmitted(form,submitter);}}});this.delegate=delegate;this.eventTarget=eventTarget;}
+start(){if(!this.started){this.eventTarget.addEventListener("submit",this.submitCaptured,true);this.started=true;}}
+stop(){if(this.started){this.eventTarget.removeEventListener("submit",this.submitCaptured,true);this.started=false;}}}
+function submissionDoesNotDismissDialog(form,submitter){const method=(submitter===null||submitter===void 0?void 0:submitter.getAttribute("formmethod"))||form.getAttribute("method");return method!="dialog";}
+function submissionDoesNotTargetIFrame(form,submitter){if((submitter===null||submitter===void 0?void 0:submitter.hasAttribute("formtarget"))||form.hasAttribute("target")){const target=(submitter===null||submitter===void 0?void 0:submitter.getAttribute("formtarget"))||form.target;for(const element of document.getElementsByName(target)){if(element instanceof HTMLIFrameElement)
+return false;}
+return true;}
+else{return true;}}
+class View{constructor(delegate,element){this.resolveRenderPromise=(_value)=>{};this.resolveInterceptionPromise=(_value)=>{};this.delegate=delegate;this.element=element;}
+scrollToAnchor(anchor){const element=this.snapshot.getElementForAnchor(anchor);if(element){this.scrollToElement(element);this.focusElement(element);}
+else{this.scrollToPosition({x:0,y:0});}}
+scrollToAnchorFromLocation(location){this.scrollToAnchor(getAnchor(location));}
+scrollToElement(element){element.scrollIntoView();}
+focusElement(element){if(element instanceof HTMLElement){if(element.hasAttribute("tabindex")){element.focus();}
+else{element.setAttribute("tabindex","-1");element.focus();element.removeAttribute("tabindex");}}}
+scrollToPosition({x,y}){this.scrollRoot.scrollTo(x,y);}
+scrollToTop(){this.scrollToPosition({x:0,y:0});}
+get scrollRoot(){return window;}
+async render(renderer){const{isPreview,shouldRender,newSnapshot:snapshot}=renderer;if(shouldRender){try{this.renderPromise=new Promise((resolve)=>(this.resolveRenderPromise=resolve));this.renderer=renderer;await this.prepareToRenderSnapshot(renderer);const renderInterception=new Promise((resolve)=>(this.resolveInterceptionPromise=resolve));const options={resume:this.resolveInterceptionPromise,render:this.renderer.renderElement};const immediateRender=this.delegate.allowsImmediateRender(snapshot,options);if(!immediateRender)
+await renderInterception;await this.renderSnapshot(renderer);this.delegate.viewRenderedSnapshot(snapshot,isPreview);this.delegate.preloadOnLoadLinksForView(this.element);this.finishRenderingSnapshot(renderer);}
+finally{delete this.renderer;this.resolveRenderPromise(undefined);delete this.renderPromise;}}
+else{this.invalidate(renderer.reloadReason);}}
+invalidate(reason){this.delegate.viewInvalidated(reason);}
+async prepareToRenderSnapshot(renderer){this.markAsPreview(renderer.isPreview);await renderer.prepareToRender();}
+markAsPreview(isPreview){if(isPreview){this.element.setAttribute("data-turbo-preview","");}
+else{this.element.removeAttribute("data-turbo-preview");}}
+async renderSnapshot(renderer){await renderer.render();}
+finishRenderingSnapshot(renderer){renderer.finishRendering();}}
+class FrameView extends View{missing(){this.element.innerHTML=`<strong class="turbo-frame-error">Content missing</strong>`;}
+get snapshot(){return new Snapshot(this.element);}}
+class LinkInterceptor{constructor(delegate,element){this.clickBubbled=(event)=>{if(this.respondsToEventTarget(event.target)){this.clickEvent=event;}
+else{delete this.clickEvent;}};this.linkClicked=((event)=>{if(this.clickEvent&&this.respondsToEventTarget(event.target)&&event.target instanceof Element){if(this.delegate.shouldInterceptLinkClick(event.target,event.detail.url,event.detail.originalEvent)){this.clickEvent.preventDefault();event.preventDefault();this.delegate.linkClickIntercepted(event.target,event.detail.url,event.detail.originalEvent);}}
+delete this.clickEvent;});this.willVisit=((_event)=>{delete this.clickEvent;});this.delegate=delegate;this.element=element;}
+start(){this.element.addEventListener("click",this.clickBubbled);document.addEventListener("turbo:click",this.linkClicked);document.addEventListener("turbo:before-visit",this.willVisit);}
+stop(){this.element.removeEventListener("click",this.clickBubbled);document.removeEventListener("turbo:click",this.linkClicked);document.removeEventListener("turbo:before-visit",this.willVisit);}
+respondsToEventTarget(target){const element=target instanceof Element?target:target instanceof Node?target.parentElement:null;return element&&element.closest("turbo-frame, html")==this.element;}}
+class LinkClickObserver{constructor(delegate,eventTarget){this.started=false;this.clickCaptured=()=>{this.eventTarget.removeEventListener("click",this.clickBubbled,false);this.eventTarget.addEventListener("click",this.clickBubbled,false);};this.clickBubbled=(event)=>{if(event instanceof MouseEvent&&this.clickEventIsSignificant(event)){const target=(event.composedPath&&event.composedPath()[0])||event.target;const link=this.findLinkFromClickTarget(target);if(link&&doesNotTargetIFrame(link)){const location=this.getLocationForLink(link);if(this.delegate.willFollowLinkToLocation(link,location,event)){event.preventDefault();this.delegate.followedLinkToLocation(link,location);}}}};this.delegate=delegate;this.eventTarget=eventTarget;}
+start(){if(!this.started){this.eventTarget.addEventListener("click",this.clickCaptured,true);this.started=true;}}
+stop(){if(this.started){this.eventTarget.removeEventListener("click",this.clickCaptured,true);this.started=false;}}
+clickEventIsSignificant(event){return!((event.target&&event.target.isContentEditable)||event.defaultPrevented||event.which>1||event.altKey||event.ctrlKey||event.metaKey||event.shiftKey);}
+findLinkFromClickTarget(target){return findClosestRecursively(target,"a[href]:not([target^=_]):not([download])");}
+getLocationForLink(link){return expandURL(link.getAttribute("href")||"");}}
+function doesNotTargetIFrame(anchor){if(anchor.hasAttribute("target")){for(const element of document.getElementsByName(anchor.target)){if(element instanceof HTMLIFrameElement)
+return false;}
+return true;}
+else{return true;}}
+class FormLinkClickObserver{constructor(delegate,element){this.delegate=delegate;this.linkInterceptor=new LinkClickObserver(this,element);}
+start(){this.linkInterceptor.start();}
+stop(){this.linkInterceptor.stop();}
+willFollowLinkToLocation(link,location,originalEvent){return(this.delegate.willSubmitFormLinkToLocation(link,location,originalEvent)&&link.hasAttribute("data-turbo-method"));}
+followedLinkToLocation(link,location){const form=document.createElement("form");const type="hidden";for(const[name,value]of location.searchParams){form.append(Object.assign(document.createElement("input"),{type,name,value}));}
+const action=Object.assign(location,{search:""});form.setAttribute("data-turbo","true");form.setAttribute("action",action.href);form.setAttribute("hidden","");const method=link.getAttribute("data-turbo-method");if(method)
+form.setAttribute("method",method);const turboFrame=link.getAttribute("data-turbo-frame");if(turboFrame)
+form.setAttribute("data-turbo-frame",turboFrame);const turboAction=getVisitAction(link);if(turboAction)
+form.setAttribute("data-turbo-action",turboAction);const turboConfirm=link.getAttribute("data-turbo-confirm");if(turboConfirm)
+form.setAttribute("data-turbo-confirm",turboConfirm);const turboStream=link.hasAttribute("data-turbo-stream");if(turboStream)
+form.setAttribute("data-turbo-stream","");this.delegate.submittedFormLinkToLocation(link,location,form);document.body.appendChild(form);form.addEventListener("turbo:submit-end",()=>form.remove(),{once:true});requestAnimationFrame(()=>form.requestSubmit());}}
+class Bardo{static async preservingPermanentElements(delegate,permanentElementMap,callback){const bardo=new this(delegate,permanentElementMap);bardo.enter();await callback();bardo.leave();}
+constructor(delegate,permanentElementMap){this.delegate=delegate;this.permanentElementMap=permanentElementMap;}
+enter(){for(const id in this.permanentElementMap){const[currentPermanentElement,newPermanentElement]=this.permanentElementMap[id];this.delegate.enteringBardo(currentPermanentElement,newPermanentElement);this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);}}
+leave(){for(const id in this.permanentElementMap){const[currentPermanentElement]=this.permanentElementMap[id];this.replaceCurrentPermanentElementWithClone(currentPermanentElement);this.replacePlaceholderWithPermanentElement(currentPermanentElement);this.delegate.leavingBardo(currentPermanentElement);}}
+replaceNewPermanentElementWithPlaceholder(permanentElement){const placeholder=createPlaceholderForPermanentElement(permanentElement);permanentElement.replaceWith(placeholder);}
+replaceCurrentPermanentElementWithClone(permanentElement){const clone=permanentElement.cloneNode(true);permanentElement.replaceWith(clone);}
+replacePlaceholderWithPermanentElement(permanentElement){const placeholder=this.getPlaceholderById(permanentElement.id);placeholder===null||placeholder===void 0?void 0:placeholder.replaceWith(permanentElement);}
+getPlaceholderById(id){return this.placeholders.find((element)=>element.content==id);}
+get placeholders(){return[...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];}}
+function createPlaceholderForPermanentElement(permanentElement){const element=document.createElement("meta");element.setAttribute("name","turbo-permanent-placeholder");element.setAttribute("content",permanentElement.id);return element;}
+class Renderer{constructor(currentSnapshot,newSnapshot,renderElement,isPreview,willRender=true){this.activeElement=null;this.currentSnapshot=currentSnapshot;this.newSnapshot=newSnapshot;this.isPreview=isPreview;this.willRender=willRender;this.renderElement=renderElement;this.promise=new Promise((resolve,reject)=>(this.resolvingFunctions={resolve,reject}));}
+get shouldRender(){return true;}
+get reloadReason(){return;}
+prepareToRender(){return;}
+finishRendering(){if(this.resolvingFunctions){this.resolvingFunctions.resolve();delete this.resolvingFunctions;}}
+async preservingPermanentElements(callback){await Bardo.preservingPermanentElements(this,this.permanentElementMap,callback);}
+focusFirstAutofocusableElement(){const element=this.connectedSnapshot.firstAutofocusableElement;if(elementIsFocusable(element)){element.focus();}}
+enteringBardo(currentPermanentElement){if(this.activeElement)
+return;if(currentPermanentElement.contains(this.currentSnapshot.activeElement)){this.activeElement=this.currentSnapshot.activeElement;}}
+leavingBardo(currentPermanentElement){if(currentPermanentElement.contains(this.activeElement)&&this.activeElement instanceof HTMLElement){this.activeElement.focus();this.activeElement=null;}}
+get connectedSnapshot(){return this.newSnapshot.isConnected?this.newSnapshot:this.currentSnapshot;}
+get currentElement(){return this.currentSnapshot.element;}
+get newElement(){return this.newSnapshot.element;}
+get permanentElementMap(){return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);}}
+function elementIsFocusable(element){return element&&typeof element.focus=="function";}
+class FrameRenderer extends Renderer{static renderElement(currentElement,newElement){var _a;const destinationRange=document.createRange();destinationRange.selectNodeContents(currentElement);destinationRange.deleteContents();const frameElement=newElement;const sourceRange=(_a=frameElement.ownerDocument)===null||_a===void 0?void 0:_a.createRange();if(sourceRange){sourceRange.selectNodeContents(frameElement);currentElement.appendChild(sourceRange.extractContents());}}
+constructor(delegate,currentSnapshot,newSnapshot,renderElement,isPreview,willRender=true){super(currentSnapshot,newSnapshot,renderElement,isPreview,willRender);this.delegate=delegate;}
+get shouldRender(){return true;}
+async render(){await nextAnimationFrame();this.preservingPermanentElements(()=>{this.loadFrameElement();});this.scrollFrameIntoView();await nextAnimationFrame();this.focusFirstAutofocusableElement();await nextAnimationFrame();this.activateScriptElements();}
+loadFrameElement(){this.delegate.willRenderFrame(this.currentElement,this.newElement);this.renderElement(this.currentElement,this.newElement);}
+scrollFrameIntoView(){if(this.currentElement.autoscroll||this.newElement.autoscroll){const element=this.currentElement.firstElementChild;const block=readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"),"end");const behavior=readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"),"auto");if(element){element.scrollIntoView({block,behavior});return true;}}
+return false;}
+activateScriptElements(){for(const inertScriptElement of this.newScriptElements){const activatedScriptElement=activateScriptElement(inertScriptElement);inertScriptElement.replaceWith(activatedScriptElement);}}
+get newScriptElements(){return this.currentElement.querySelectorAll("script");}}
+function readScrollLogicalPosition(value,defaultValue){if(value=="end"||value=="start"||value=="center"||value=="nearest"){return value;}
+else{return defaultValue;}}
+function readScrollBehavior(value,defaultValue){if(value=="auto"||value=="smooth"){return value;}
+else{return defaultValue;}}
+class ProgressBar{static get defaultCSS(){return unindent`
+      .turbo-progress-bar {
+        position: fixed;
+        display: block;
+        top: 0;
+        left: 0;
+        height: 3px;
+        background: #0076ff;
+        z-index: 2147483647;
+        transition:
+          width ${ProgressBar.animationDuration}ms ease-out,
+          opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
+        transform: translate3d(0, 0, 0);
+      }
+    `;}
+constructor(){this.hiding=false;this.value=0;this.visible=false;this.trickle=()=>{this.setValue(this.value+Math.random()/100);};this.stylesheetElement=this.createStylesheetElement();this.progressElement=this.createProgressElement();this.installStylesheetElement();this.setValue(0);}
+show(){if(!this.visible){this.visible=true;this.installProgressElement();this.startTrickling();}}
+hide(){if(this.visible&&!this.hiding){this.hiding=true;this.fadeProgressElement(()=>{this.uninstallProgressElement();this.stopTrickling();this.visible=false;this.hiding=false;});}}
+setValue(value){this.value=value;this.refresh();}
+installStylesheetElement(){document.head.insertBefore(this.stylesheetElement,document.head.firstChild);}
+installProgressElement(){this.progressElement.style.width="0";this.progressElement.style.opacity="1";document.documentElement.insertBefore(this.progressElement,document.body);this.refresh();}
+fadeProgressElement(callback){this.progressElement.style.opacity="0";setTimeout(callback,ProgressBar.animationDuration*1.5);}
+uninstallProgressElement(){if(this.progressElement.parentNode){document.documentElement.removeChild(this.progressElement);}}
+startTrickling(){if(!this.trickleInterval){this.trickleInterval=window.setInterval(this.trickle,ProgressBar.animationDuration);}}
+stopTrickling(){window.clearInterval(this.trickleInterval);delete this.trickleInterval;}
+refresh(){requestAnimationFrame(()=>{this.progressElement.style.width=`${10 + this.value * 90}%`;});}
+createStylesheetElement(){const element=document.createElement("style");element.type="text/css";element.textContent=ProgressBar.defaultCSS;if(this.cspNonce){element.nonce=this.cspNonce;}
+return element;}
+createProgressElement(){const element=document.createElement("div");element.className="turbo-progress-bar";return element;}
+get cspNonce(){return getMetaContent("csp-nonce");}}
+ProgressBar.animationDuration=300;class HeadSnapshot extends Snapshot{constructor(){super(...arguments);this.detailsByOuterHTML=this.children.filter((element)=>!elementIsNoscript(element)).map((element)=>elementWithoutNonce(element)).reduce((result,element)=>{const{outerHTML}=element;const details=outerHTML in result?result[outerHTML]:{type:elementType(element),tracked:elementIsTracked(element),elements:[],};return Object.assign(Object.assign({},result),{[outerHTML]:Object.assign(Object.assign({},details),{elements:[...details.elements,element]})});},{});}
+get trackedElementSignature(){return Object.keys(this.detailsByOuterHTML).filter((outerHTML)=>this.detailsByOuterHTML[outerHTML].tracked).join("");}
+getScriptElementsNotInSnapshot(snapshot){return this.getElementsMatchingTypeNotInSnapshot("script",snapshot);}
+getStylesheetElementsNotInSnapshot(snapshot){return this.getElementsMatchingTypeNotInSnapshot("stylesheet",snapshot);}
+getElementsMatchingTypeNotInSnapshot(matchedType,snapshot){return Object.keys(this.detailsByOuterHTML).filter((outerHTML)=>!(outerHTML in snapshot.detailsByOuterHTML)).map((outerHTML)=>this.detailsByOuterHTML[outerHTML]).filter(({type})=>type==matchedType).map(({elements:[element]})=>element);}
+get provisionalElements(){return Object.keys(this.detailsByOuterHTML).reduce((result,outerHTML)=>{const{type,tracked,elements}=this.detailsByOuterHTML[outerHTML];if(type==null&&!tracked){return[...result,...elements];}
+else if(elements.length>1){return[...result,...elements.slice(1)];}
+else{return result;}},[]);}
+getMetaValue(name){const element=this.findMetaElementByName(name);return element?element.getAttribute("content"):null;}
+findMetaElementByName(name){return Object.keys(this.detailsByOuterHTML).reduce((result,outerHTML)=>{const{elements:[element],}=this.detailsByOuterHTML[outerHTML];return elementIsMetaElementWithName(element,name)?element:result;},undefined);}}
+function elementType(element){if(elementIsScript(element)){return"script";}
+else if(elementIsStylesheet(element)){return"stylesheet";}}
+function elementIsTracked(element){return element.getAttribute("data-turbo-track")=="reload";}
+function elementIsScript(element){const tagName=element.localName;return tagName=="script";}
+function elementIsNoscript(element){const tagName=element.localName;return tagName=="noscript";}
+function elementIsStylesheet(element){const tagName=element.localName;return tagName=="style"||(tagName=="link"&&element.getAttribute("rel")=="stylesheet");}
+function elementIsMetaElementWithName(element,name){const tagName=element.localName;return tagName=="meta"&&element.getAttribute("name")==name;}
+function elementWithoutNonce(element){if(element.hasAttribute("nonce")){element.setAttribute("nonce","");}
+return element;}
+class PageSnapshot extends Snapshot{static fromHTMLString(html=""){return this.fromDocument(parseHTMLDocument(html));}
+static fromElement(element){return this.fromDocument(element.ownerDocument);}
+static fromDocument({head,body}){return new this(body,new HeadSnapshot(head));}
+constructor(element,headSnapshot){super(element);this.headSnapshot=headSnapshot;}
+clone(){const clonedElement=this.element.cloneNode(true);const selectElements=this.element.querySelectorAll("select");const clonedSelectElements=clonedElement.querySelectorAll("select");for(const[index,source]of selectElements.entries()){const clone=clonedSelectElements[index];for(const option of clone.selectedOptions)
+option.selected=false;for(const option of source.selectedOptions)
+clone.options[option.index].selected=true;}
+for(const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')){clonedPasswordInput.value="";}
+return new PageSnapshot(clonedElement,this.headSnapshot);}
+get headElement(){return this.headSnapshot.element;}
+get rootLocation(){var _a;const root=(_a=this.getSetting("root"))!==null&&_a!==void 0?_a:"/";return expandURL(root);}
+get cacheControlValue(){return this.getSetting("cache-control");}
+get isPreviewable(){return this.cacheControlValue!="no-preview";}
+get isCacheable(){return this.cacheControlValue!="no-cache";}
+get isVisitable(){return this.getSetting("visit-control")!="reload";}
+getSetting(name){return this.headSnapshot.getMetaValue(`turbo-${name}`);}}
+var TimingMetric;(function(TimingMetric){TimingMetric["visitStart"]="visitStart";TimingMetric["requestStart"]="requestStart";TimingMetric["requestEnd"]="requestEnd";TimingMetric["visitEnd"]="visitEnd";})(TimingMetric||(TimingMetric={}));var VisitState;(function(VisitState){VisitState["initialized"]="initialized";VisitState["started"]="started";VisitState["canceled"]="canceled";VisitState["failed"]="failed";VisitState["completed"]="completed";})(VisitState||(VisitState={}));const defaultOptions={action:"advance",historyChanged:false,visitCachedSnapshot:()=>{},willRender:true,updateHistory:true,shouldCacheSnapshot:true,acceptsStreamResponse:false,};var SystemStatusCode;(function(SystemStatusCode){SystemStatusCode[SystemStatusCode["networkFailure"]=0]="networkFailure";SystemStatusCode[SystemStatusCode["timeoutFailure"]=-1]="timeoutFailure";SystemStatusCode[SystemStatusCode["contentTypeMismatch"]=-2]="contentTypeMismatch";})(SystemStatusCode||(SystemStatusCode={}));class Visit{construct
 or(delegate,location,restorationIdentifier,options={}){this.identifier=uuid();this.timingMetrics={};this.followedRedirect=false;this.historyChanged=false;this.scrolled=false;this.shouldCacheSnapshot=true;this.acceptsStreamResponse=false;this.snapshotCached=false;this.state=VisitState.initialized;this.delegate=delegate;this.location=location;this.restorationIdentifier=restorationIdentifier||uuid();const{action,historyChanged,referrer,snapshot,snapshotHTML,response,visitCachedSnapshot,willRender,updateHistory,shouldCacheSnapshot,acceptsStreamResponse,}=Object.assign(Object.assign({},defaultOptions),options);this.action=action;this.historyChanged=historyChanged;this.referrer=referrer;this.snapshot=snapshot;this.snapshotHTML=snapshotHTML;this.response=response;this.isSamePage=this.delegate.locationWithActionIsSamePage(this.location,this.action);this.visitCachedSnapshot=visitCachedSnapshot;this.willRender=willRender;this.updateHistory=updateHistory;this.scrolled=!willRender;this.shouldCa
 cheSnapshot=shouldCacheSnapshot;this.acceptsStreamResponse=acceptsStreamResponse;}
+get adapter(){return this.delegate.adapter;}
+get view(){return this.delegate.view;}
+get history(){return this.delegate.history;}
+get restorationData(){return this.history.getRestorationDataForIdentifier(this.restorationIdentifier);}
+get silent(){return this.isSamePage;}
+start(){if(this.state==VisitState.initialized){this.recordTimingMetric(TimingMetric.visitStart);this.state=VisitState.started;this.adapter.visitStarted(this);this.delegate.visitStarted(this);}}
+cancel(){if(this.state==VisitState.started){if(this.request){this.request.cancel();}
+this.cancelRender();this.state=VisitState.canceled;}}
+complete(){if(this.state==VisitState.started){this.recordTimingMetric(TimingMetric.visitEnd);this.state=VisitState.completed;this.followRedirect();if(!this.followedRedirect){this.adapter.visitCompleted(this);this.delegate.visitCompleted(this);}}}
+fail(){if(this.state==VisitState.started){this.state=VisitState.failed;this.adapter.visitFailed(this);}}
+changeHistory(){var _a;if(!this.historyChanged&&this.updateHistory){const actionForHistory=this.location.href===((_a=this.referrer)===null||_a===void 0?void 0:_a.href)?"replace":this.action;const method=getHistoryMethodForAction(actionForHistory);this.history.update(method,this.location,this.restorationIdentifier);this.historyChanged=true;}}
+issueRequest(){if(this.hasPreloadedResponse()){this.simulateRequest();}
+else if(this.shouldIssueRequest()&&!this.request){this.request=new FetchRequest(this,FetchMethod.get,this.location);this.request.perform();}}
+simulateRequest(){if(this.response){this.startRequest();this.recordResponse();this.finishRequest();}}
+startRequest(){this.recordTimingMetric(TimingMetric.requestStart);this.adapter.visitRequestStarted(this);}
+recordResponse(response=this.response){this.response=response;if(response){const{statusCode}=response;if(isSuccessful(statusCode)){this.adapter.visitRequestCompleted(this);}
+else{this.adapter.visitRequestFailedWithStatusCode(this,statusCode);}}}
+finishRequest(){this.recordTimingMetric(TimingMetric.requestEnd);this.adapter.visitRequestFinished(this);}
+loadResponse(){if(this.response){const{statusCode,responseHTML}=this.response;this.render(async()=>{if(this.shouldCacheSnapshot)
+this.cacheSnapshot();if(this.view.renderPromise)
+await this.view.renderPromise;if(isSuccessful(statusCode)&&responseHTML!=null){await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML),false,this.willRender,this);this.performScroll();this.adapter.visitRendered(this);this.complete();}
+else{await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML),this);this.adapter.visitRendered(this);this.fail();}});}}
+getCachedSnapshot(){const snapshot=this.view.getCachedSnapshotForLocation(this.location)||this.getPreloadedSnapshot();if(snapshot&&(!getAnchor(this.location)||snapshot.hasAnchor(getAnchor(this.location)))){if(this.action=="restore"||snapshot.isPreviewable){return snapshot;}}}
+getPreloadedSnapshot(){if(this.snapshotHTML){return PageSnapshot.fromHTMLString(this.snapshotHTML);}}
+hasCachedSnapshot(){return this.getCachedSnapshot()!=null;}
+loadCachedSnapshot(){const snapshot=this.getCachedSnapshot();if(snapshot){const isPreview=this.shouldIssueRequest();this.render(async()=>{this.cacheSnapshot();if(this.isSamePage){this.adapter.visitRendered(this);}
+else{if(this.view.renderPromise)
+await this.view.renderPromise;await this.view.renderPage(snapshot,isPreview,this.willRender,this);this.performScroll();this.adapter.visitRendered(this);if(!isPreview){this.complete();}}});}}
+followRedirect(){var _a;if(this.redirectedToLocation&&!this.followedRedirect&&((_a=this.response)===null||_a===void 0?void 0:_a.redirected)){this.adapter.visitProposedToLocation(this.redirectedToLocation,{action:"replace",response:this.response,shouldCacheSnapshot:false,willRender:false,});this.followedRedirect=true;}}
+goToSamePageAnchor(){if(this.isSamePage){this.render(async()=>{this.cacheSnapshot();this.performScroll();this.changeHistory();this.adapter.visitRendered(this);});}}
+prepareRequest(request){if(this.acceptsStreamResponse){request.acceptResponseType(StreamMessage.contentType);}}
+requestStarted(){this.startRequest();}
+requestPreventedHandlingResponse(_request,_response){}
+async requestSucceededWithResponse(request,response){const responseHTML=await response.responseHTML;const{redirected,statusCode}=response;if(responseHTML==undefined){this.recordResponse({statusCode:SystemStatusCode.contentTypeMismatch,redirected,});}
+else{this.redirectedToLocation=response.redirected?response.location:undefined;this.recordResponse({statusCode:statusCode,responseHTML,redirected});}}
+async requestFailedWithResponse(request,response){const responseHTML=await response.responseHTML;const{redirected,statusCode}=response;if(responseHTML==undefined){this.recordResponse({statusCode:SystemStatusCode.contentTypeMismatch,redirected,});}
+else{this.recordResponse({statusCode:statusCode,responseHTML,redirected});}}
+requestErrored(_request,_error){this.recordResponse({statusCode:SystemStatusCode.networkFailure,redirected:false,});}
+requestFinished(){this.finishRequest();}
+performScroll(){if(!this.scrolled&&!this.view.forceReloaded){if(this.action=="restore"){this.scrollToRestoredPosition()||this.scrollToAnchor()||this.view.scrollToTop();}
+else{this.scrollToAnchor()||this.view.scrollToTop();}
+if(this.isSamePage){this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation,this.location);}
+this.scrolled=true;}}
+scrollToRestoredPosition(){const{scrollPosition}=this.restorationData;if(scrollPosition){this.view.scrollToPosition(scrollPosition);return true;}}
+scrollToAnchor(){const anchor=getAnchor(this.location);if(anchor!=null){this.view.scrollToAnchor(anchor);return true;}}
+recordTimingMetric(metric){this.timingMetrics[metric]=new Date().getTime();}
+getTimingMetrics(){return Object.assign({},this.timingMetrics);}
+getHistoryMethodForAction(action){switch(action){case"replace":return history.replaceState;case"advance":case"restore":return history.pushState;}}
+hasPreloadedResponse(){return typeof this.response=="object";}
+shouldIssueRequest(){if(this.isSamePage){return false;}
+else if(this.action=="restore"){return!this.hasCachedSnapshot();}
+else{return this.willRender;}}
+cacheSnapshot(){if(!this.snapshotCached){this.view.cacheSnapshot(this.snapshot).then((snapshot)=>snapshot&&this.visitCachedSnapshot(snapshot));this.snapshotCached=true;}}
+async render(callback){this.cancelRender();await new Promise((resolve)=>{this.frame=requestAnimationFrame(()=>resolve());});await callback();delete this.frame;}
+cancelRender(){if(this.frame){cancelAnimationFrame(this.frame);delete this.frame;}}}
+function isSuccessful(statusCode){return statusCode>=200&&statusCode<300;}
+class BrowserAdapter{constructor(session){this.progressBar=new ProgressBar();this.showProgressBar=()=>{this.progressBar.show();};this.session=session;}
+visitProposedToLocation(location,options){this.navigator.startVisit(location,(options===null||options===void 0?void 0:options.restorationIdentifier)||uuid(),options);}
+visitStarted(visit){this.location=visit.location;visit.loadCachedSnapshot();visit.issueRequest();visit.goToSamePageAnchor();}
+visitRequestStarted(visit){this.progressBar.setValue(0);if(visit.hasCachedSnapshot()||visit.action!="restore"){this.showVisitProgressBarAfterDelay();}
+else{this.showProgressBar();}}
+visitRequestCompleted(visit){visit.loadResponse();}
+visitRequestFailedWithStatusCode(visit,statusCode){switch(statusCode){case SystemStatusCode.networkFailure:case SystemStatusCode.timeoutFailure:case SystemStatusCode.contentTypeMismatch:return this.reload({reason:"request_failed",context:{statusCode,},});default:return visit.loadResponse();}}
+visitRequestFinished(_visit){this.progressBar.setValue(1);this.hideVisitProgressBar();}
+visitCompleted(_visit){}
+pageInvalidated(reason){this.reload(reason);}
+visitFailed(_visit){}
+visitRendered(_visit){}
+formSubmissionStarted(_formSubmission){this.progressBar.setValue(0);this.showFormProgressBarAfterDelay();}
+formSubmissionFinished(_formSubmission){this.progressBar.setValue(1);this.hideFormProgressBar();}
+showVisitProgressBarAfterDelay(){this.visitProgressBarTimeout=window.setTimeout(this.showProgressBar,this.session.progressBarDelay);}
+hideVisitProgressBar(){this.progressBar.hide();if(this.visitProgressBarTimeout!=null){window.clearTimeout(this.visitProgressBarTimeout);delete this.visitProgressBarTimeout;}}
+showFormProgressBarAfterDelay(){if(this.formProgressBarTimeout==null){this.formProgressBarTimeout=window.setTimeout(this.showProgressBar,this.session.progressBarDelay);}}
+hideFormProgressBar(){this.progressBar.hide();if(this.formProgressBarTimeout!=null){window.clearTimeout(this.formProgressBarTimeout);delete this.formProgressBarTimeout;}}
+reload(reason){var _a;dispatch("turbo:reload",{detail:reason});window.location.href=((_a=this.location)===null||_a===void 0?void 0:_a.toString())||window.location.href;}
+get navigator(){return this.session.navigator;}}
+class CacheObserver{constructor(){this.selector="[data-turbo-temporary]";this.deprecatedSelector="[data-turbo-cache=false]";this.started=false;this.removeTemporaryElements=((_event)=>{for(const element of this.temporaryElements){element.remove();}});}
+start(){if(!this.started){this.started=true;addEventListener("turbo:before-cache",this.removeTemporaryElements,false);}}
+stop(){if(this.started){this.started=false;removeEventListener("turbo:before-cache",this.removeTemporaryElements,false);}}
+get temporaryElements(){return[...document.querySelectorAll(this.selector),...this.temporaryElementsWithDeprecation];}
+get temporaryElementsWithDeprecation(){const elements=document.querySelectorAll(this.deprecatedSelector);if(elements.length){console.warn(`The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.`);}
+return[...elements];}}
+class FrameRedirector{constructor(session,element){this.session=session;this.element=element;this.linkInterceptor=new LinkInterceptor(this,element);this.formSubmitObserver=new FormSubmitObserver(this,element);}
+start(){this.linkInterceptor.start();this.formSubmitObserver.start();}
+stop(){this.linkInterceptor.stop();this.formSubmitObserver.stop();}
+shouldInterceptLinkClick(element,_location,_event){return this.shouldRedirect(element);}
+linkClickIntercepted(element,url,event){const frame=this.findFrameElement(element);if(frame){frame.delegate.linkClickIntercepted(element,url,event);}}
+willSubmitForm(element,submitter){return(element.closest("turbo-frame")==null&&this.shouldSubmit(element,submitter)&&this.shouldRedirect(element,submitter));}
+formSubmitted(element,submitter){const frame=this.findFrameElement(element,submitter);if(frame){frame.delegate.formSubmitted(element,submitter);}}
+shouldSubmit(form,submitter){var _a;const action=getAction(form,submitter);const meta=this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);const rootLocation=expandURL((_a=meta===null||meta===void 0?void 0:meta.content)!==null&&_a!==void 0?_a:"/");return this.shouldRedirect(form,submitter)&&locationIsVisitable(action,rootLocation);}
+shouldRedirect(element,submitter){const isNavigatable=element instanceof HTMLFormElement?this.session.submissionIsNavigatable(element,submitter):this.session.elementIsNavigatable(element);if(isNavigatable){const frame=this.findFrameElement(element,submitter);return frame?frame!=element.closest("turbo-frame"):false;}
+else{return false;}}
+findFrameElement(element,submitter){const id=(submitter===null||submitter===void 0?void 0:submitter.getAttribute("data-turbo-frame"))||element.getAttribute("data-turbo-frame");if(id&&id!="_top"){const frame=this.element.querySelector(`#${id}:not([disabled])`);if(frame instanceof FrameElement){return frame;}}}}
+class History{constructor(delegate){this.restorationIdentifier=uuid();this.restorationData={};this.started=false;this.pageLoaded=false;this.onPopState=(event)=>{if(this.shouldHandlePopState()){const{turbo}=event.state||{};if(turbo){this.location=new URL(window.location.href);const{restorationIdentifier}=turbo;this.restorationIdentifier=restorationIdentifier;this.delegate.historyPoppedToLocationWithRestorationIdentifier(this.location,restorationIdentifier);}}};this.onPageLoad=async(_event)=>{await nextMicrotask();this.pageLoaded=true;};this.delegate=delegate;}
+start(){if(!this.started){addEventListener("popstate",this.onPopState,false);addEventListener("load",this.onPageLoad,false);this.started=true;this.replace(new URL(window.location.href));}}
+stop(){if(this.started){removeEventListener("popstate",this.onPopState,false);removeEventListener("load",this.onPageLoad,false);this.started=false;}}
+push(location,restorationIdentifier){this.update(history.pushState,location,restorationIdentifier);}
+replace(location,restorationIdentifier){this.update(history.replaceState,location,restorationIdentifier);}
+update(method,location,restorationIdentifier=uuid()){const state={turbo:{restorationIdentifier}};method.call(history,state,"",location.href);this.location=location;this.restorationIdentifier=restorationIdentifier;}
+getRestorationDataForIdentifier(restorationIdentifier){return this.restorationData[restorationIdentifier]||{};}
+updateRestorationData(additionalData){const{restorationIdentifier}=this;const restorationData=this.restorationData[restorationIdentifier];this.restorationData[restorationIdentifier]=Object.assign(Object.assign({},restorationData),additionalData);}
+assumeControlOfScrollRestoration(){var _a;if(!this.previousScrollRestoration){this.previousScrollRestoration=(_a=history.scrollRestoration)!==null&&_a!==void 0?_a:"auto";history.scrollRestoration="manual";}}
+relinquishControlOfScrollRestoration(){if(this.previousScrollRestoration){history.scrollRestoration=this.previousScrollRestoration;delete this.previousScrollRestoration;}}
+shouldHandlePopState(){return this.pageIsLoaded();}
+pageIsLoaded(){return this.pageLoaded||document.readyState=="complete";}}
+class Navigator{constructor(delegate){this.delegate=delegate;}
+proposeVisit(location,options={}){if(this.delegate.allowsVisitingLocationWithAction(location,options.action)){if(locationIsVisitable(location,this.view.snapshot.rootLocation)){this.delegate.visitProposedToLocation(location,options);}
+else{window.location.href=location.toString();}}}
+startVisit(locatable,restorationIdentifier,options={}){this.stop();this.currentVisit=new Visit(this,expandURL(locatable),restorationIdentifier,Object.assign({referrer:this.location},options));this.currentVisit.start();}
+submitForm(form,submitter){this.stop();this.formSubmission=new FormSubmission(this,form,submitter,true);this.formSubmission.start();}
+stop(){if(this.formSubmission){this.formSubmission.stop();delete this.formSubmission;}
+if(this.currentVisit){this.currentVisit.cancel();delete this.currentVisit;}}
+get adapter(){return this.delegate.adapter;}
+get view(){return this.delegate.view;}
+get history(){return this.delegate.history;}
+formSubmissionStarted(formSubmission){if(typeof this.adapter.formSubmissionStarted==="function"){this.adapter.formSubmissionStarted(formSubmission);}}
+async formSubmissionSucceededWithResponse(formSubmission,fetchResponse){if(formSubmission==this.formSubmission){const responseHTML=await fetchResponse.responseHTML;if(responseHTML){const shouldCacheSnapshot=formSubmission.isSafe;if(!shouldCacheSnapshot){this.view.clearSnapshotCache();}
+const{statusCode,redirected}=fetchResponse;const action=this.getActionForFormSubmission(formSubmission);const visitOptions={action,shouldCacheSnapshot,response:{statusCode,responseHTML,redirected},};this.proposeVisit(fetchResponse.location,visitOptions);}}}
+async formSubmissionFailedWithResponse(formSubmission,fetchResponse){const responseHTML=await fetchResponse.responseHTML;if(responseHTML){const snapshot=PageSnapshot.fromHTMLString(responseHTML);if(fetchResponse.serverError){await this.view.renderError(snapshot,this.currentVisit);}
+else{await this.view.renderPage(snapshot,false,true,this.currentVisit);}
+this.view.scrollToTop();this.view.clearSnapshotCache();}}
+formSubmissionErrored(formSubmission,error){console.error(error);}
+formSubmissionFinished(formSubmission){if(typeof this.adapter.formSubmissionFinished==="function"){this.adapter.formSubmissionFinished(formSubmission);}}
+visitStarted(visit){this.delegate.visitStarted(visit);}
+visitCompleted(visit){this.delegate.visitCompleted(visit);}
+locationWithActionIsSamePage(location,action){const anchor=getAnchor(location);const currentAnchor=getAnchor(this.view.lastRenderedLocation);const isRestorationToTop=action==="restore"&&typeof anchor==="undefined";return(action!=="replace"&&getRequestURL(location)===getRequestURL(this.view.lastRenderedLocation)&&(isRestorationToTop||(anchor!=null&&anchor!==currentAnchor)));}
+visitScrolledToSamePageLocation(oldURL,newURL){this.delegate.visitScrolledToSamePageLocation(oldURL,newURL);}
+get location(){return this.history.location;}
+get restorationIdentifier(){return this.history.restorationIdentifier;}
+getActionForFormSubmission({submitter,formElement}){return getVisitAction(submitter,formElement)||"advance";}}
+var PageStage;(function(PageStage){PageStage[PageStage["initial"]=0]="initial";PageStage[PageStage["loading"]=1]="loading";PageStage[PageStage["interactive"]=2]="interactive";PageStage[PageStage["complete"]=3]="complete";})(PageStage||(PageStage={}));class PageObserver{constructor(delegate){this.stage=PageStage.initial;this.started=false;this.interpretReadyState=()=>{const{readyState}=this;if(readyState=="interactive"){this.pageIsInteractive();}
+else if(readyState=="complete"){this.pageIsComplete();}};this.pageWillUnload=()=>{this.delegate.pageWillUnload();};this.delegate=delegate;}
+start(){if(!this.started){if(this.stage==PageStage.initial){this.stage=PageStage.loading;}
+document.addEventListener("readystatechange",this.interpretReadyState,false);addEventListener("pagehide",this.pageWillUnload,false);this.started=true;}}
+stop(){if(this.started){document.removeEventListener("readystatechange",this.interpretReadyState,false);removeEventListener("pagehide",this.pageWillUnload,false);this.started=false;}}
+pageIsInteractive(){if(this.stage==PageStage.loading){this.stage=PageStage.interactive;this.delegate.pageBecameInteractive();}}
+pageIsComplete(){this.pageIsInteractive();if(this.stage==PageStage.interactive){this.stage=PageStage.complete;this.delegate.pageLoaded();}}
+get readyState(){return document.readyState;}}
+class ScrollObserver{constructor(delegate){this.started=false;this.onScroll=()=>{this.updatePosition({x:window.pageXOffset,y:window.pageYOffset});};this.delegate=delegate;}
+start(){if(!this.started){addEventListener("scroll",this.onScroll,false);this.onScroll();this.started=true;}}
+stop(){if(this.started){removeEventListener("scroll",this.onScroll,false);this.started=false;}}
+updatePosition(position){this.delegate.scrollPositionChanged(position);}}
+class StreamMessageRenderer{render({fragment}){Bardo.preservingPermanentElements(this,getPermanentElementMapForFragment(fragment),()=>document.documentElement.appendChild(fragment));}
+enteringBardo(currentPermanentElement,newPermanentElement){newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));}
+leavingBardo(){}}
+function getPermanentElementMapForFragment(fragment){const permanentElementsInDocument=queryPermanentElementsAll(document.documentElement);const permanentElementMap={};for(const permanentElementInDocument of permanentElementsInDocument){const{id}=permanentElementInDocument;for(const streamElement of fragment.querySelectorAll("turbo-stream")){const elementInStream=getPermanentElementById(streamElement.templateElement.content,id);if(elementInStream){permanentElementMap[id]=[permanentElementInDocument,elementInStream];}}}
+return permanentElementMap;}
+class StreamObserver{constructor(delegate){this.sources=new Set();this.started=false;this.inspectFetchResponse=((event)=>{const response=fetchResponseFromEvent(event);if(response&&fetchResponseIsStream(response)){event.preventDefault();this.receiveMessageResponse(response);}});this.receiveMessageEvent=(event)=>{if(this.started&&typeof event.data=="string"){this.receiveMessageHTML(event.data);}};this.delegate=delegate;}
+start(){if(!this.started){this.started=true;addEventListener("turbo:before-fetch-response",this.inspectFetchResponse,false);}}
+stop(){if(this.started){this.started=false;removeEventListener("turbo:before-fetch-response",this.inspectFetchResponse,false);}}
+connectStreamSource(source){if(!this.streamSourceIsConnected(source)){this.sources.add(source);source.addEventListener("message",this.receiveMessageEvent,false);}}
+disconnectStreamSource(source){if(this.streamSourceIsConnected(source)){this.sources.delete(source);source.removeEventListener("message",this.receiveMessageEvent,false);}}
+streamSourceIsConnected(source){return this.sources.has(source);}
+async receiveMessageResponse(response){const html=await response.responseHTML;if(html){this.receiveMessageHTML(html);}}
+receiveMessageHTML(html){this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));}}
+function fetchResponseFromEvent(event){var _a;const fetchResponse=(_a=event.detail)===null||_a===void 0?void 0:_a.fetchResponse;if(fetchResponse instanceof FetchResponse){return fetchResponse;}}
+function fetchResponseIsStream(response){var _a;const contentType=(_a=response.contentType)!==null&&_a!==void 0?_a:"";return contentType.startsWith(StreamMessage.contentType);}
+class ErrorRenderer extends Renderer{static renderElement(currentElement,newElement){const{documentElement,body}=document;documentElement.replaceChild(newElement,body);}
+async render(){this.replaceHeadAndBody();this.activateScriptElements();}
+replaceHeadAndBody(){const{documentElement,head}=document;documentElement.replaceChild(this.newHead,head);this.renderElement(this.currentElement,this.newElement);}
+activateScriptElements(){for(const replaceableElement of this.scriptElements){const parentNode=replaceableElement.parentNode;if(parentNode){const element=activateScriptElement(replaceableElement);parentNode.replaceChild(element,replaceableElement);}}}
+get newHead(){return this.newSnapshot.headSnapshot.element;}
+get scriptElements(){return document.documentElement.querySelectorAll("script");}}
+class PageRenderer extends Renderer{static renderElement(currentElement,newElement){if(document.body&&newElement instanceof HTMLBodyElement){document.body.replaceWith(newElement);}
+else{document.documentElement.appendChild(newElement);}}
+get shouldRender(){return this.newSnapshot.isVisitable&&this.trackedElementsAreIdentical;}
+get reloadReason(){if(!this.newSnapshot.isVisitable){return{reason:"turbo_visit_control_is_reload",};}
+if(!this.trackedElementsAreIdentical){return{reason:"tracked_element_mismatch",};}}
+async prepareToRender(){await this.mergeHead();}
+async render(){if(this.willRender){await this.replaceBody();}}
+finishRendering(){super.finishRendering();if(!this.isPreview){this.focusFirstAutofocusableElement();}}
+get currentHeadSnapshot(){return this.currentSnapshot.headSnapshot;}
+get newHeadSnapshot(){return this.newSnapshot.headSnapshot;}
+get newElement(){return this.newSnapshot.element;}
+async mergeHead(){const mergedHeadElements=this.mergeProvisionalElements();const newStylesheetElements=this.copyNewHeadStylesheetElements();this.copyNewHeadScriptElements();await mergedHeadElements;await newStylesheetElements;}
+async replaceBody(){await this.preservingPermanentElements(async()=>{this.activateNewBody();await this.assignNewBody();});}
+get trackedElementsAreIdentical(){return this.currentHeadSnapshot.trackedElementSignature==this.newHeadSnapshot.trackedElementSignature;}
+async copyNewHeadStylesheetElements(){const loadingElements=[];for(const element of this.newHeadStylesheetElements){loadingElements.push(waitForLoad(element));document.head.appendChild(element);}
+await Promise.all(loadingElements);}
+copyNewHeadScriptElements(){for(const element of this.newHeadScriptElements){document.head.appendChild(activateScriptElement(element));}}
+async mergeProvisionalElements(){const newHeadElements=[...this.newHeadProvisionalElements];for(const element of this.currentHeadProvisionalElements){if(!this.isCurrentElementInElementList(element,newHeadElements)){document.head.removeChild(element);}}
+for(const element of newHeadElements){document.head.appendChild(element);}}
+isCurrentElementInElementList(element,elementList){for(const[index,newElement]of elementList.entries()){if(element.tagName=="TITLE"){if(newElement.tagName!="TITLE"){continue;}
+if(element.innerHTML==newElement.innerHTML){elementList.splice(index,1);return true;}}
+if(newElement.isEqualNode(element)){elementList.splice(index,1);return true;}}
+return false;}
+removeCurrentHeadProvisionalElements(){for(const element of this.currentHeadProvisionalElements){document.head.removeChild(element);}}
+copyNewHeadProvisionalElements(){for(const element of this.newHeadProvisionalElements){document.head.appendChild(element);}}
+activateNewBody(){document.adoptNode(this.newElement);this.activateNewBodyScriptElements();}
+activateNewBodyScriptElements(){for(const inertScriptElement of this.newBodyScriptElements){const activatedScriptElement=activateScriptElement(inertScriptElement);inertScriptElement.replaceWith(activatedScriptElement);}}
+async assignNewBody(){await this.renderElement(this.currentElement,this.newElement);}
+get newHeadStylesheetElements(){return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);}
+get newHeadScriptElements(){return this.newHeadSnapshot.getScriptElementsNotInSnapshot(this.currentHeadSnapshot);}
+get currentHeadProvisionalElements(){return this.currentHeadSnapshot.provisionalElements;}
+get newHeadProvisionalElements(){return this.newHeadSnapshot.provisionalElements;}
+get newBodyScriptElements(){return this.newElement.querySelectorAll("script");}}
+class SnapshotCache{constructor(size){this.keys=[];this.snapshots={};this.size=size;}
+has(location){return toCacheKey(location)in this.snapshots;}
+get(location){if(this.has(location)){const snapshot=this.read(location);this.touch(location);return snapshot;}}
+put(location,snapshot){this.write(location,snapshot);this.touch(location);return snapshot;}
+clear(){this.snapshots={};}
+read(location){return this.snapshots[toCacheKey(location)];}
+write(location,snapshot){this.snapshots[toCacheKey(location)]=snapshot;}
+touch(location){const key=toCacheKey(location);const index=this.keys.indexOf(key);if(index>-1)
+this.keys.splice(index,1);this.keys.unshift(key);this.trim();}
+trim(){for(const key of this.keys.splice(this.size)){delete this.snapshots[key];}}}
+class PageView extends View{constructor(){super(...arguments);this.snapshotCache=new SnapshotCache(10);this.lastRenderedLocation=new URL(location.href);this.forceReloaded=false;}
+renderPage(snapshot,isPreview=false,willRender=true,visit){const renderer=new PageRenderer(this.snapshot,snapshot,PageRenderer.renderElement,isPreview,willRender);if(!renderer.shouldRender){this.forceReloaded=true;}
+else{visit===null||visit===void 0?void 0:visit.changeHistory();}
+return this.render(renderer);}
+renderError(snapshot,visit){visit===null||visit===void 0?void 0:visit.changeHistory();const renderer=new ErrorRenderer(this.snapshot,snapshot,ErrorRenderer.renderElement,false);return this.render(renderer);}
+clearSnapshotCache(){this.snapshotCache.clear();}
+async cacheSnapshot(snapshot=this.snapshot){if(snapshot.isCacheable){this.delegate.viewWillCacheSnapshot();const{lastRenderedLocation:location}=this;await nextEventLoopTick();const cachedSnapshot=snapshot.clone();this.snapshotCache.put(location,cachedSnapshot);return cachedSnapshot;}}
+getCachedSnapshotForLocation(location){return this.snapshotCache.get(location);}
+get snapshot(){return PageSnapshot.fromElement(this.element);}}
+class Preloader{constructor(delegate){this.selector="a[data-turbo-preload]";this.delegate=delegate;}
+get snapshotCache(){return this.delegate.navigator.view.snapshotCache;}
+start(){if(document.readyState==="loading"){return document.addEventListener("DOMContentLoaded",()=>{this.preloadOnLoadLinksForView(document.body);});}
+else{this.preloadOnLoadLinksForView(document.body);}}
+preloadOnLoadLinksForView(element){for(const link of element.querySelectorAll(this.selector)){this.preloadURL(link);}}
+async preloadURL(link){const location=new URL(link.href);if(this.snapshotCache.has(location)){return;}
+try{const response=await fetch(location.toString(),{headers:{"VND.PREFETCH":"true",Accept:"text/html"}});const responseText=await response.text();const snapshot=PageSnapshot.fromHTMLString(responseText);this.snapshotCache.put(location,snapshot);}
+catch(_){}}}
+class Session{constructor(){this.navigator=new Navigator(this);this.history=new History(this);this.preloader=new Preloader(this);this.view=new PageView(this,document.documentElement);this.adapter=new BrowserAdapter(this);this.pageObserver=new PageObserver(this);this.cacheObserver=new CacheObserver();this.linkClickObserver=new LinkClickObserver(this,window);this.formSubmitObserver=new FormSubmitObserver(this,document);this.scrollObserver=new ScrollObserver(this);this.streamObserver=new StreamObserver(this);this.formLinkClickObserver=new FormLinkClickObserver(this,document.documentElement);this.frameRedirector=new FrameRedirector(this,document.documentElement);this.streamMessageRenderer=new StreamMessageRenderer();this.drive=true;this.enabled=true;this.progressBarDelay=500;this.started=false;this.formMode="on";}
+start(){if(!this.started){this.pageObserver.start();this.cacheObserver.start();this.formLinkClickObserver.start();this.linkClickObserver.start();this.formSubmitObserver.start();this.scrollObserver.start();this.streamObserver.start();this.frameRedirector.start();this.history.start();this.preloader.start();this.started=true;this.enabled=true;}}
+disable(){this.enabled=false;}
+stop(){if(this.started){this.pageObserver.stop();this.cacheObserver.stop();this.formLinkClickObserver.stop();this.linkClickObserver.stop();this.formSubmitObserver.stop();this.scrollObserver.stop();this.streamObserver.stop();this.frameRedirector.stop();this.history.stop();this.started=false;}}
+registerAdapter(adapter){this.adapter=adapter;}
+visit(location,options={}){const frameElement=options.frame?document.getElementById(options.frame):null;if(frameElement instanceof FrameElement){frameElement.src=location.toString();frameElement.loaded;}
+else{this.navigator.proposeVisit(expandURL(location),options);}}
+connectStreamSource(source){this.streamObserver.connectStreamSource(source);}
+disconnectStreamSource(source){this.streamObserver.disconnectStreamSource(source);}
+renderStreamMessage(message){this.streamMessageRenderer.render(StreamMessage.wrap(message));}
+clearCache(){this.view.clearSnapshotCache();}
+setProgressBarDelay(delay){this.progressBarDelay=delay;}
+setFormMode(mode){this.formMode=mode;}
+get location(){return this.history.location;}
+get restorationIdentifier(){return this.history.restorationIdentifier;}
+historyPoppedToLocationWithRestorationIdentifier(location,restorationIdentifier){if(this.enabled){this.navigator.startVisit(location,restorationIdentifier,{action:"restore",historyChanged:true,});}
+else{this.adapter.pageInvalidated({reason:"turbo_disabled",});}}
+scrollPositionChanged(position){this.history.updateRestorationData({scrollPosition:position});}
+willSubmitFormLinkToLocation(link,location){return this.elementIsNavigatable(link)&&locationIsVisitable(location,this.snapshot.rootLocation);}
+submittedFormLinkToLocation(){}
+willFollowLinkToLocation(link,location,event){return(this.elementIsNavigatable(link)&&locationIsVisitable(location,this.snapshot.rootLocation)&&this.applicationAllowsFollowingLinkToLocation(link,location,event));}
+followedLinkToLocation(link,location){const action=this.getActionForLink(link);const acceptsStreamResponse=link.hasAttribute("data-turbo-stream");this.visit(location.href,{action,acceptsStreamResponse});}
+allowsVisitingLocationWithAction(location,action){return this.locationWithActionIsSamePage(location,action)||this.applicationAllowsVisitingLocation(location);}
+visitProposedToLocation(location,options){extendURLWithDeprecatedProperties(location);this.adapter.visitProposedToLocation(location,options);}
+visitStarted(visit){if(!visit.acceptsStreamResponse){markAsBusy(document.documentElement);}
+extendURLWithDeprecatedProperties(visit.location);if(!visit.silent){this.notifyApplicationAfterVisitingLocation(visit.location,visit.action);}}
+visitCompleted(visit){clearBusyState(document.documentElement);this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());}
+locationWithActionIsSamePage(location,action){return this.navigator.locationWithActionIsSamePage(location,action);}
+visitScrolledToSamePageLocation(oldURL,newURL){this.notifyApplicationAfterVisitingSamePageLocation(oldURL,newURL);}
+willSubmitForm(form,submitter){const action=getAction(form,submitter);return(this.submissionIsNavigatable(form,submitter)&&locationIsVisitable(expandURL(action),this.snapshot.rootLocation));}
+formSubmitted(form,submitter){this.navigator.submitForm(form,submitter);}
+pageBecameInteractive(){this.view.lastRenderedLocation=this.location;this.notifyApplicationAfterPageLoad();}
+pageLoaded(){this.history.assumeControlOfScrollRestoration();}
+pageWillUnload(){this.history.relinquishControlOfScrollRestoration();}
+receivedMessageFromStream(message){this.renderStreamMessage(message);}
+viewWillCacheSnapshot(){var _a;if(!((_a=this.navigator.currentVisit)===null||_a===void 0?void 0:_a.silent)){this.notifyApplicationBeforeCachingSnapshot();}}
+allowsImmediateRender({element},options){const event=this.notifyApplicationBeforeRender(element,options);const{defaultPrevented,detail:{render},}=event;if(this.view.renderer&&render){this.view.renderer.renderElement=render;}
+return!defaultPrevented;}
+viewRenderedSnapshot(_snapshot,_isPreview){this.view.lastRenderedLocation=this.history.location;this.notifyApplicationAfterRender();}
+preloadOnLoadLinksForView(element){this.preloader.preloadOnLoadLinksForView(element);}
+viewInvalidated(reason){this.adapter.pageInvalidated(reason);}
+frameLoaded(frame){this.notifyApplicationAfterFrameLoad(frame);}
+frameRendered(fetchResponse,frame){this.notifyApplicationAfterFrameRender(fetchResponse,frame);}
+applicationAllowsFollowingLinkToLocation(link,location,ev){const event=this.notifyApplicationAfterClickingLinkToLocation(link,location,ev);return!event.defaultPrevented;}
+applicationAllowsVisitingLocation(location){const event=this.notifyApplicationBeforeVisitingLocation(location);return!event.defaultPrevented;}
+notifyApplicationAfterClickingLinkToLocation(link,location,event){return dispatch("turbo:click",{target:link,detail:{url:location.href,originalEvent:event},cancelable:true,});}
+notifyApplicationBeforeVisitingLocation(location){return dispatch("turbo:before-visit",{detail:{url:location.href},cancelable:true,});}
+notifyApplicationAfterVisitingLocation(location,action){return dispatch("turbo:visit",{detail:{url:location.href,action}});}
+notifyApplicationBeforeCachingSnapshot(){return dispatch("turbo:before-cache");}
+notifyApplicationBeforeRender(newBody,options){return dispatch("turbo:before-render",{detail:Object.assign({newBody},options),cancelable:true,});}
+notifyApplicationAfterRender(){return dispatch("turbo:render");}
+notifyApplicationAfterPageLoad(timing={}){return dispatch("turbo:load",{detail:{url:this.location.href,timing},});}
+notifyApplicationAfterVisitingSamePageLocation(oldURL,newURL){dispatchEvent(new HashChangeEvent("hashchange",{oldURL:oldURL.toString(),newURL:newURL.toString(),}));}
+notifyApplicationAfterFrameLoad(frame){return dispatch("turbo:frame-load",{target:frame});}
+notifyApplicationAfterFrameRender(fetchResponse,frame){return dispatch("turbo:frame-render",{detail:{fetchResponse},target:frame,cancelable:true,});}
+submissionIsNavigatable(form,submitter){if(this.formMode=="off"){return false;}
+else{const submitterIsNavigatable=submitter?this.elementIsNavigatable(submitter):true;if(this.formMode=="optin"){return submitterIsNavigatable&&form.closest('[data-turbo="true"]')!=null;}
+else{return submitterIsNavigatable&&this.elementIsNavigatable(form);}}}
+elementIsNavigatable(element){const container=findClosestRecursively(element,"[data-turbo]");const withinFrame=findClosestRecursively(element,"turbo-frame");if(this.drive||withinFrame){if(container){return container.getAttribute("data-turbo")!="false";}
+else{return true;}}
+else{if(container){return container.getAttribute("data-turbo")=="true";}
+else{return false;}}}
+getActionForLink(link){return getVisitAction(link)||"advance";}
+get snapshot(){return this.view.snapshot;}}
+function extendURLWithDeprecatedProperties(url){Object.defineProperties(url,deprecatedLocationPropertyDescriptors);}
+const deprecatedLocationPropertyDescriptors={absoluteURL:{get(){return this.toString();},},};class Cache{constructor(session){this.session=session;}
+clear(){this.session.clearCache();}
+resetCacheControl(){this.setCacheControl("");}
+exemptPageFromCache(){this.setCacheControl("no-cache");}
+exemptPageFromPreview(){this.setCacheControl("no-preview");}
+setCacheControl(value){setMetaContent("turbo-cache-control",value);}}
+const StreamActions={after(){this.targetElements.forEach((e)=>{var _a;return(_a=e.parentElement)===null||_a===void 0?void 0:_a.insertBefore(this.templateContent,e.nextSibling);});},append(){this.removeDuplicateTargetChildren();this.targetElements.forEach((e)=>e.append(this.templateContent));},before(){this.targetElements.forEach((e)=>{var _a;return(_a=e.parentElement)===null||_a===void 0?void 0:_a.insertBefore(this.templateContent,e);});},prepend(){this.removeDuplicateTargetChildren();this.targetElements.forEach((e)=>e.prepend(this.templateContent));},remove(){this.targetElements.forEach((e)=>e.remove());},replace(){this.targetElements.forEach((e)=>e.replaceWith(this.templateContent));},update(){this.targetElements.forEach((targetElement)=>{targetElement.innerHTML="";targetElement.append(this.templateContent);});},};const session=new Session();const cache=new Cache(session);const{navigator:navigator$1}=session;function start(){session.start();}
+function registerAdapter(adapter){session.registerAdapter(adapter);}
+function visit(location,options){session.visit(location,options);}
+function connectStreamSource(source){session.connectStreamSource(source);}
+function disconnectStreamSource(source){session.disconnectStreamSource(source);}
+function renderStreamMessage(message){session.renderStreamMessage(message);}
+function clearCache(){console.warn("Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`");session.clearCache();}
+function setProgressBarDelay(delay){session.setProgressBarDelay(delay);}
+function setConfirmMethod(confirmMethod){FormSubmission.confirmMethod=confirmMethod;}
+function setFormMode(mode){session.setFormMode(mode);}
+var Turbo=Object.freeze({__proto__:null,navigator:navigator$1,session:session,cache:cache,PageRenderer:PageRenderer,PageSnapshot:PageSnapshot,FrameRenderer:FrameRenderer,start:start,registerAdapter:registerAdapter,visit:visit,connectStreamSource:connectStreamSource,disconnectStreamSource:disconnectStreamSource,renderStreamMessage:renderStreamMessage,clearCache:clearCache,setProgressBarDelay:setProgressBarDelay,setConfirmMethod:setConfirmMethod,setFormMode:setFormMode,StreamActions:StreamActions});class TurboFrameMissingError extends Error{}
+class FrameController{constructor(element){this.fetchResponseLoaded=(_fetchResponse)=>{};this.currentFetchRequest=null;this.resolveVisitPromise=()=>{};this.connected=false;this.hasBeenLoaded=false;this.ignoredAttributes=new Set();this.action=null;this.visitCachedSnapshot=({element})=>{const frame=element.querySelector("#"+this.element.id);if(frame&&this.previousFrameElement){frame.replaceChildren(...this.previousFrameElement.children);}
+delete this.previousFrameElement;};this.element=element;this.view=new FrameView(this,this.element);this.appearanceObserver=new AppearanceObserver(this,this.element);this.formLinkClickObserver=new FormLinkClickObserver(this,this.element);this.linkInterceptor=new LinkInterceptor(this,this.element);this.restorationIdentifier=uuid();this.formSubmitObserver=new FormSubmitObserver(this,this.element);}
+connect(){if(!this.connected){this.connected=true;if(this.loadingStyle==FrameLoadingStyle.lazy){this.appearanceObserver.start();}
+else{this.loadSourceURL();}
+this.formLinkClickObserver.start();this.linkInterceptor.start();this.formSubmitObserver.start();}}
+disconnect(){if(this.connected){this.connected=false;this.appearanceObserver.stop();this.formLinkClickObserver.stop();this.linkInterceptor.stop();this.formSubmitObserver.stop();}}
+disabledChanged(){if(this.loadingStyle==FrameLoadingStyle.eager){this.loadSourceURL();}}
+sourceURLChanged(){if(this.isIgnoringChangesTo("src"))
+return;if(this.element.isConnected){this.complete=false;}
+if(this.loadingStyle==FrameLoadingStyle.eager||this.hasBeenLoaded){this.loadSourceURL();}}
+sourceURLReloaded(){const{src}=this.element;this.ignoringChangesToAttribute("complete",()=>{this.element.removeAttribute("complete");});this.element.src=null;this.element.src=src;return this.element.loaded;}
+completeChanged(){if(this.isIgnoringChangesTo("complete"))
+return;this.loadSourceURL();}
+loadingStyleChanged(){if(this.loadingStyle==FrameLoadingStyle.lazy){this.appearanceObserver.start();}
+else{this.appearanceObserver.stop();this.loadSourceURL();}}
+async loadSourceURL(){if(this.enabled&&this.isActive&&!this.complete&&this.sourceURL){this.element.loaded=this.visit(expandURL(this.sourceURL));this.appearanceObserver.stop();await this.element.loaded;this.hasBeenLoaded=true;}}
+async loadResponse(fetchResponse){if(fetchResponse.redirected||(fetchResponse.succeeded&&fetchResponse.isHTML)){this.sourceURL=fetchResponse.response.url;}
+try{const html=await fetchResponse.responseHTML;if(html){const document=parseHTMLDocument(html);const pageSnapshot=PageSnapshot.fromDocument(document);if(pageSnapshot.isVisitable){await this.loadFrameResponse(fetchResponse,document);}
+else{await this.handleUnvisitableFrameResponse(fetchResponse);}}}
+finally{this.fetchResponseLoaded=()=>{};}}
+elementAppearedInViewport(element){this.proposeVisitIfNavigatedWithAction(element,element);this.loadSourceURL();}
+willSubmitFormLinkToLocation(link){return this.shouldInterceptNavigation(link);}
+submittedFormLinkToLocation(link,_location,form){const frame=this.findFrameElement(link);if(frame)
+form.setAttribute("data-turbo-frame",frame.id);}
+shouldInterceptLinkClick(element,_location,_event){return this.shouldInterceptNavigation(element);}
+linkClickIntercepted(element,location){this.navigateFrame(element,location);}
+willSubmitForm(element,submitter){return element.closest("turbo-frame")==this.element&&this.shouldInterceptNavigation(element,submitter);}
+formSubmitted(element,submitter){if(this.formSubmission){this.formSubmission.stop();}
+this.formSubmission=new FormSubmission(this,element,submitter);const{fetchRequest}=this.formSubmission;this.prepareRequest(fetchRequest);this.formSubmission.start();}
+prepareRequest(request){var _a;request.headers["Turbo-Frame"]=this.id;if((_a=this.currentNavigationElement)===null||_a===void 0?void 0:_a.hasAttribute("data-turbo-stream")){request.acceptResponseType(StreamMessage.contentType);}}
+requestStarted(_request){markAsBusy(this.element);}
+requestPreventedHandlingResponse(_request,_response){this.resolveVisitPromise();}
+async requestSucceededWithResponse(request,response){await this.loadResponse(response);this.resolveVisitPromise();}
+async requestFailedWithResponse(request,response){await this.loadResponse(response);this.resolveVisitPromise();}
+requestErrored(request,error){console.error(error);this.resolveVisitPromise();}
+requestFinished(_request){clearBusyState(this.element);}
+formSubmissionStarted({formElement}){markAsBusy(formElement,this.findFrameElement(formElement));}
+formSubmissionSucceededWithResponse(formSubmission,response){const frame=this.findFrameElement(formSubmission.formElement,formSubmission.submitter);frame.delegate.proposeVisitIfNavigatedWithAction(frame,formSubmission.formElement,formSubmission.submitter);frame.delegate.loadResponse(response);if(!formSubmission.isSafe){session.clearCache();}}
+formSubmissionFailedWithResponse(formSubmission,fetchResponse){this.element.delegate.loadResponse(fetchResponse);session.clearCache();}
+formSubmissionErrored(formSubmission,error){console.error(error);}
+formSubmissionFinished({formElement}){clearBusyState(formElement,this.findFrameElement(formElement));}
+allowsImmediateRender({element:newFrame},options){const event=dispatch("turbo:before-frame-render",{target:this.element,detail:Object.assign({newFrame},options),cancelable:true,});const{defaultPrevented,detail:{render},}=event;if(this.view.renderer&&render){this.view.renderer.renderElement=render;}
+return!defaultPrevented;}
+viewRenderedSnapshot(_snapshot,_isPreview){}
+preloadOnLoadLinksForView(element){session.preloadOnLoadLinksForView(element);}
+viewInvalidated(){}
+willRenderFrame(currentElement,_newElement){this.previousFrameElement=currentElement.cloneNode(true);}
+async loadFrameResponse(fetchResponse,document){const newFrameElement=await this.extractForeignFrameElement(document.body);if(newFrameElement){const snapshot=new Snapshot(newFrameElement);const renderer=new FrameRenderer(this,this.view.snapshot,snapshot,FrameRenderer.renderElement,false,false);if(this.view.renderPromise)
+await this.view.renderPromise;this.changeHistory();await this.view.render(renderer);this.complete=true;session.frameRendered(fetchResponse,this.element);session.frameLoaded(this.element);this.fetchResponseLoaded(fetchResponse);}
+else if(this.willHandleFrameMissingFromResponse(fetchResponse)){this.handleFrameMissingFromResponse(fetchResponse);}}
+async visit(url){var _a;const request=new FetchRequest(this,FetchMethod.get,url,new URLSearchParams(),this.element);(_a=this.currentFetchRequest)===null||_a===void 0?void 0:_a.cancel();this.currentFetchRequest=request;return new Promise((resolve)=>{this.resolveVisitPromise=()=>{this.resolveVisitPromise=()=>{};this.currentFetchRequest=null;resolve();};request.perform();});}
+navigateFrame(element,url,submitter){const frame=this.findFrameElement(element,submitter);frame.delegate.proposeVisitIfNavigatedWithAction(frame,element,submitter);this.withCurrentNavigationElement(element,()=>{frame.src=url;});}
+proposeVisitIfNavigatedWithAction(frame,element,submitter){this.action=getVisitAction(submitter,element,frame);if(this.action){const pageSnapshot=PageSnapshot.fromElement(frame).clone();const{visitCachedSnapshot}=frame.delegate;frame.delegate.fetchResponseLoaded=(fetchResponse)=>{if(frame.src){const{statusCode,redirected}=fetchResponse;const responseHTML=frame.ownerDocument.documentElement.outerHTML;const response={statusCode,redirected,responseHTML};const options={response,visitCachedSnapshot,willRender:false,updateHistory:false,restorationIdentifier:this.restorationIdentifier,snapshot:pageSnapshot,};if(this.action)
+options.action=this.action;session.visit(frame.src,options);}};}}
+changeHistory(){if(this.action){const method=getHistoryMethodForAction(this.action);session.history.update(method,expandURL(this.element.src||""),this.restorationIdentifier);}}
+async handleUnvisitableFrameResponse(fetchResponse){console.warn(`The response (${fetchResponse.statusCode}) from <turbo-frame id="${this.element.id}"> is performing a full page visit due to turbo-visit-control.`);await this.visitResponse(fetchResponse.response);}
+willHandleFrameMissingFromResponse(fetchResponse){this.element.setAttribute("complete","");const response=fetchResponse.response;const visit=async(url,options={})=>{if(url instanceof Response){this.visitResponse(url);}
+else{session.visit(url,options);}};const event=dispatch("turbo:frame-missing",{target:this.element,detail:{response,visit},cancelable:true,});return!event.defaultPrevented;}
+handleFrameMissingFromResponse(fetchResponse){this.view.missing();this.throwFrameMissingError(fetchResponse);}
+throwFrameMissingError(fetchResponse){const message=`The response (${fetchResponse.statusCode}) did not contain the expected <turbo-frame id="${this.element.id}"> and will be ignored. To perform a full page visit instead, set turbo-visit-control to reload.`;throw new TurboFrameMissingError(message);}
+async visitResponse(response){const wrapped=new FetchResponse(response);const responseHTML=await wrapped.responseHTML;const{location,redirected,statusCode}=wrapped;return session.visit(location,{response:{redirected,statusCode,responseHTML}});}
+findFrameElement(element,submitter){var _a;const id=getAttribute("data-turbo-frame",submitter,element)||this.element.getAttribute("target");return(_a=getFrameElementById(id))!==null&&_a!==void 0?_a:this.element;}
+async extractForeignFrameElement(container){let element;const id=CSS.escape(this.id);try{element=activateElement(container.querySelector(`turbo-frame#${id}`),this.sourceURL);if(element){return element;}
+element=activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`),this.sourceURL);if(element){await element.loaded;return await this.extractForeignFrameElement(element);}}
+catch(error){console.error(error);return new FrameElement();}
+return null;}
+formActionIsVisitable(form,submitter){const action=getAction(form,submitter);return locationIsVisitable(expandURL(action),this.rootLocation);}
+shouldInterceptNavigation(element,submitter){const id=getAttribute("data-turbo-frame",submitter,element)||this.element.getAttribute("target");if(element instanceof HTMLFormElement&&!this.formActionIsVisitable(element,submitter)){return false;}
+if(!this.enabled||id=="_top"){return false;}
+if(id){const frameElement=getFrameElementById(id);if(frameElement){return!frameElement.disabled;}}
+if(!session.elementIsNavigatable(element)){return false;}
+if(submitter&&!session.elementIsNavigatable(submitter)){return false;}
+return true;}
+get id(){return this.element.id;}
+get enabled(){return!this.element.disabled;}
+get sourceURL(){if(this.element.src){return this.element.src;}}
+set sourceURL(sourceURL){this.ignoringChangesToAttribute("src",()=>{this.element.src=sourceURL!==null&&sourceURL!==void 0?sourceURL:null;});}
+get loadingStyle(){return this.element.loading;}
+get isLoading(){return this.formSubmission!==undefined||this.resolveVisitPromise()!==undefined;}
+get complete(){return this.element.hasAttribute("complete");}
+set complete(value){this.ignoringChangesToAttribute("complete",()=>{if(value){this.element.setAttribute("complete","");}
+else{this.element.removeAttribute("complete");}});}
+get isActive(){return this.element.isActive&&this.connected;}
+get rootLocation(){var _a;const meta=this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);const root=(_a=meta===null||meta===void 0?void 0:meta.content)!==null&&_a!==void 0?_a:"/";return expandURL(root);}
+isIgnoringChangesTo(attributeName){return this.ignoredAttributes.has(attributeName);}
+ignoringChangesToAttribute(attributeName,callback){this.ignoredAttributes.add(attributeName);callback();this.ignoredAttributes.delete(attributeName);}
+withCurrentNavigationElement(element,callback){this.currentNavigationElement=element;callback();delete this.currentNavigationElement;}}
+function getFrameElementById(id){if(id!=null){const element=document.getElementById(id);if(element instanceof FrameElement){return element;}}}
+function activateElement(element,currentURL){if(element){const src=element.getAttribute("src");if(src!=null&&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};
\ No newline at end of file

-----------------------------------------------------------------------


hooks/post-receive
-- 
rt


More information about the rt-commit mailing list