roundhouse_ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +166 -0
- data/Rakefile +3 -0
- data/app/assets/javascripts/roundhouse_ui/turbo.min.js +35 -0
- data/app/assets/stylesheets/roundhouse_ui/application.css +15 -0
- data/app/controllers/concerns/roundhouse_ui/job_set_browsing.rb +41 -0
- data/app/controllers/roundhouse_ui/application_controller.rb +75 -0
- data/app/controllers/roundhouse_ui/assets_controller.rb +16 -0
- data/app/controllers/roundhouse_ui/audit_controller.rb +7 -0
- data/app/controllers/roundhouse_ui/busy_controller.rb +29 -0
- data/app/controllers/roundhouse_ui/capsules_controller.rb +27 -0
- data/app/controllers/roundhouse_ui/dashboard_controller.rb +26 -0
- data/app/controllers/roundhouse_ui/dead_controller.rb +46 -0
- data/app/controllers/roundhouse_ui/errors_controller.rb +50 -0
- data/app/controllers/roundhouse_ui/jobs_controller.rb +94 -0
- data/app/controllers/roundhouse_ui/metrics_controller.rb +8 -0
- data/app/controllers/roundhouse_ui/queues_controller.rb +40 -0
- data/app/controllers/roundhouse_ui/redis_controller.rb +21 -0
- data/app/controllers/roundhouse_ui/retries_controller.rb +34 -0
- data/app/controllers/roundhouse_ui/scheduled_controller.rb +34 -0
- data/app/controllers/roundhouse_ui/snapshots_controller.rb +26 -0
- data/app/controllers/roundhouse_ui/workers_controller.rb +33 -0
- data/app/helpers/roundhouse_ui/application_helper.rb +4 -0
- data/app/helpers/roundhouse_ui/nav_helper.rb +24 -0
- data/app/helpers/roundhouse_ui/observability_helper.rb +13 -0
- data/app/views/layouts/roundhouse_ui/application.html.erb +365 -0
- data/app/views/roundhouse_ui/audit/index.html.erb +21 -0
- data/app/views/roundhouse_ui/busy/index.html.erb +23 -0
- data/app/views/roundhouse_ui/capsules/index.html.erb +22 -0
- data/app/views/roundhouse_ui/dashboard/show.html.erb +68 -0
- data/app/views/roundhouse_ui/dead/index.html.erb +46 -0
- data/app/views/roundhouse_ui/errors/index.html.erb +28 -0
- data/app/views/roundhouse_ui/jobs/_form.html.erb +24 -0
- data/app/views/roundhouse_ui/jobs/edit.html.erb +2 -0
- data/app/views/roundhouse_ui/jobs/new.html.erb +2 -0
- data/app/views/roundhouse_ui/jobs/show.html.erb +33 -0
- data/app/views/roundhouse_ui/metrics/show.html.erb +49 -0
- data/app/views/roundhouse_ui/queues/index.html.erb +45 -0
- data/app/views/roundhouse_ui/redis/show.html.erb +59 -0
- data/app/views/roundhouse_ui/retries/index.html.erb +33 -0
- data/app/views/roundhouse_ui/scheduled/index.html.erb +29 -0
- data/app/views/roundhouse_ui/shared/_pager.html.erb +15 -0
- data/app/views/roundhouse_ui/snapshots/index.html.erb +25 -0
- data/app/views/roundhouse_ui/workers/index.html.erb +39 -0
- data/config/routes.rb +54 -0
- data/lib/roundhouse_ui/audit.rb +25 -0
- data/lib/roundhouse_ui/cancel_middleware.rb +19 -0
- data/lib/roundhouse_ui/cancellation.rb +37 -0
- data/lib/roundhouse_ui/engine.rb +5 -0
- data/lib/roundhouse_ui/fetch.rb +36 -0
- data/lib/roundhouse_ui/metrics.rb +51 -0
- data/lib/roundhouse_ui/observability.rb +46 -0
- data/lib/roundhouse_ui/pause.rb +59 -0
- data/lib/roundhouse_ui/redaction.rb +33 -0
- data/lib/roundhouse_ui/snapshots.rb +90 -0
- data/lib/roundhouse_ui/version.rb +3 -0
- data/lib/roundhouse_ui.rb +73 -0
- data/lib/tasks/roundhouse_ui_tasks.rake +4 -0
- metadata +131 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1dd0ae0f36eaaafd7bce6b40d6c420060fd7c35d632bee5da737821d081e8771
|
|
4
|
+
data.tar.gz: 4281ee8bf890c9819c73d4cf8a756e7dd7baa3f329456b1dcfe8750b9eb83147
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4a19cfd3a5c7ad395f2ac271eaac50a8b3f6965ee9a1d7b68a3cf0096d1acba7a2e7afffd29816968556cf54f3f3d3889ceb766f8c2d1a5b5d6c1bc7987195c8
|
|
7
|
+
data.tar.gz: d85573bfc800cbcd853c6187c2863862d7e5d812eaaedaee71523bc663c06d6d001b0cd5c2e3effe12cc40007655137ac88116e1548237e8a65e7ee6738ddf9b
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright R.J. Robinson
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<img width="3532" height="1956" alt="CleanShot 2026-06-27 at 21 07 23@2x" src="https://github.com/user-attachments/assets/7c27c997-d8d1-4e8e-b982-94c624ebfa2a" />
|
|
2
|
+
# Roundhouse
|
|
3
|
+
|
|
4
|
+
**A modern, real-time web UI for Sidekiq.**
|
|
5
|
+
|
|
6
|
+
[](https://github.com/rjrobinson/roundhouse_ui/actions/workflows/ci.yml)
|
|
7
|
+
[](https://rubygems.org/gems/roundhouse_ui)
|
|
8
|
+
[](https://www.ruby-lang.org)
|
|
9
|
+
[](https://rubyonrails.org)
|
|
10
|
+
[](MIT-LICENSE)
|
|
11
|
+
|
|
12
|
+
Roundhouse is a mountable Rails engine that replaces the stock Sidekiq Web UI with a
|
|
13
|
+
control plane built for the way you actually operate background jobs: live stats,
|
|
14
|
+
searchable sets, grouped errors, safe queue management, job inspection/editing, and
|
|
15
|
+
Redis health — all server-rendered with Turbo (no build step), and **no Sidekiq Pro
|
|
16
|
+
required**.
|
|
17
|
+
|
|
18
|
+
> Gem name is `roundhouse_ui`; the brand and mount path are **Roundhouse**.
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **Live dashboard** — throughput chart + stat cards refresh in place (no reload); polling pauses when the tab is hidden.
|
|
23
|
+
- **Search** — find a job across the dead/retry/scheduled sets by class, JID, error, or argument value.
|
|
24
|
+
- **Bulk actions** — select many dead jobs and retry/delete them at once (not 25-at-a-time).
|
|
25
|
+
- **Grouped errors** — failures fingerprinted by `class + error`, so one bad deploy is a single issue with a count.
|
|
26
|
+
- **Queue management** — pause/resume (OSS, see below), purge with an impact count, and **snapshot → restore**.
|
|
27
|
+
- **Job inspection & editing** — view a job's full args (with redaction), error, and backtrace; edit & re-enqueue, or enqueue a brand-new job (opt-in).
|
|
28
|
+
- **Workers** — process fleet with quiet/stop, threads, queues, and heartbeat.
|
|
29
|
+
- **Redis pressure** — memory, ops/sec, and the eviction-policy check that flags silent job loss.
|
|
30
|
+
- **Audit log** — every state-changing action recorded and attributable.
|
|
31
|
+
- **⌘K command palette**, light/dark themes, read-only mode, and a strict self-contained CSP.
|
|
32
|
+
|
|
33
|
+
Everything reads through Sidekiq's public API — **no database**.
|
|
34
|
+
|
|
35
|
+
## Requirements
|
|
36
|
+
|
|
37
|
+
- Ruby >= 3.1 · Rails >= 7.0 · Sidekiq >= 7.0
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
# Gemfile
|
|
43
|
+
gem "roundhouse_ui"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Mounting
|
|
47
|
+
|
|
48
|
+
Roundhouse is auth-agnostic — wrap the mount in whatever your app already uses.
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
# config/routes.rb
|
|
52
|
+
authenticate :user, ->(u) { u.admin? } do # Devise example
|
|
53
|
+
mount RoundhouseUi::Engine => "/roundhouse"
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
It ships **no authentication** — always mount it behind yours; it exposes operational
|
|
58
|
+
controls over your job system.
|
|
59
|
+
|
|
60
|
+
## Configuration
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
# config/initializers/roundhouse.rb
|
|
64
|
+
RoundhouseUi.configure do |c|
|
|
65
|
+
# Disable every destructive action (purge/retry/delete/edit) server-side.
|
|
66
|
+
c.read_only = !Rails.env.development?
|
|
67
|
+
|
|
68
|
+
# Enqueue new jobs and edit/re-enqueue existing ones from the UI (sharp tool — off by default).
|
|
69
|
+
c.allow_job_editing = Rails.env.development?
|
|
70
|
+
|
|
71
|
+
# Mask sensitive argument keys (case-insensitive substring) wherever args are displayed.
|
|
72
|
+
c.redact_args = %w[password token secret api_key authorization]
|
|
73
|
+
|
|
74
|
+
# Attribute audit entries to the signed-in user instead of "anonymous".
|
|
75
|
+
c.actor_resolver = ->(controller) { controller.current_user&.email }
|
|
76
|
+
|
|
77
|
+
# Deep-link jobs out to your APM (see Observability).
|
|
78
|
+
c.observability = RoundhouseUi::Observability::DatadogAdapter.new(service: "my-app")
|
|
79
|
+
|
|
80
|
+
# Where queue snapshots are stored (default: Redis). Swap for a file/S3 store.
|
|
81
|
+
# c.snapshot_store = MyS3SnapshotStore.new
|
|
82
|
+
end
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Pausing queues
|
|
86
|
+
|
|
87
|
+
Pause/resume is pure OSS. To make a pause actually stop a queue from being worked,
|
|
88
|
+
install Roundhouse's fetch strategy in your Sidekiq **server** config:
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
# config/initializers/sidekiq.rb
|
|
92
|
+
Sidekiq.configure_server do |config|
|
|
93
|
+
config[:fetch_class] = RoundhouseUi::Fetch
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`RoundhouseUi::Fetch` subclasses `Sidekiq::BasicFetch` and skips paused queues, inheriting
|
|
98
|
+
all of Sidekiq's weighting/ordering. Until it's installed, the Queues page records pauses
|
|
99
|
+
but **warns that they aren't enforced** (worker and web are separate processes, so
|
|
100
|
+
Roundhouse detects whether a fetcher has reported in).
|
|
101
|
+
|
|
102
|
+
## Cancelling jobs
|
|
103
|
+
|
|
104
|
+
Cancellation is cooperative — Ruby can't safely kill a running thread. Install the
|
|
105
|
+
middleware so a cancelled job is dropped before it runs:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
# config/initializers/sidekiq.rb
|
|
109
|
+
Sidekiq.configure_server do |config|
|
|
110
|
+
config.server_middleware { |chain| chain.add RoundhouseUi::CancelMiddleware }
|
|
111
|
+
end
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The **Busy** page's Cancel button flags a job's JID. A queued/scheduled/retrying job
|
|
115
|
+
is then skipped when it would next run; a *currently running* job stops only if it
|
|
116
|
+
checks in — e.g. a long loop can `break if RoundhouseUi.cancelled?(jid)`.
|
|
117
|
+
|
|
118
|
+
## Observability deep-links
|
|
119
|
+
|
|
120
|
+
The core depends on nothing — it asks the configured adapter for a URL and renders a link
|
|
121
|
+
only if one comes back. A Datadog adapter ships in the box; write your own by duck-typing
|
|
122
|
+
`job_url` / `queue_url` / `label`:
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
RoundhouseUi.observability = RoundhouseUi::Observability::DatadogAdapter.new(site: "datadoghq.com", service: "my-app")
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Snapshots
|
|
129
|
+
|
|
130
|
+
Back up a queue before purging it (the safety net for clearing a stuck queue), then restore.
|
|
131
|
+
Storage is pluggable via `RoundhouseUi.snapshot_store` (default: Redis). For large/stuck
|
|
132
|
+
queues use a file or S3 store so the backup doesn't sit in the Redis you're trying to relieve.
|
|
133
|
+
|
|
134
|
+
## Security
|
|
135
|
+
|
|
136
|
+
- All destructive actions are CSRF-protected `POST`s — never GET — and gated by `read_only`.
|
|
137
|
+
- Roundhouse sets its own strict, self-contained Content-Security-Policy on its responses
|
|
138
|
+
(nonce'd inline script, same-origin only), so it's safe even if the host sets no policy.
|
|
139
|
+
- Configure `redact_args` to keep tokens/PII out of the UI; the audit log records who did what.
|
|
140
|
+
|
|
141
|
+
## Keyboard
|
|
142
|
+
|
|
143
|
+
`⌘K` (or `Ctrl+K`) opens the command palette — jump to any view or action.
|
|
144
|
+
|
|
145
|
+
## Development
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
bin/rails test # full suite, ~1s, no Redis required (Sidekiq's API is stubbed)
|
|
149
|
+
bundle exec rubocop # lint
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The dummy app under `test/dummy` mounts the engine at `/roundhouse`; point it at a local
|
|
153
|
+
Redis and run `bin/rails server` to click around.
|
|
154
|
+
|
|
155
|
+
## Roadmap
|
|
156
|
+
|
|
157
|
+
- Multi-Redis / multi-cluster view (one pane across shards).
|
|
158
|
+
- Capsules and cron/periodic views.
|
|
159
|
+
|
|
160
|
+
## Contributing
|
|
161
|
+
|
|
162
|
+
Bug reports and pull requests welcome at https://github.com/rjrobinson/roundhouse_ui.
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
Available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minified by jsDelivr using Terser v5.37.0.
|
|
3
|
+
* Original file: /npm/@hotwired/turbo@8.0.12/dist/turbo.es2017-umd.js
|
|
4
|
+
*
|
|
5
|
+
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
|
6
|
+
*/
|
|
7
|
+
/*!
|
|
8
|
+
Turbo 8.0.12
|
|
9
|
+
Copyright © 2024 37signals LLC
|
|
10
|
+
*/
|
|
11
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Turbo={})}(this,(function(e){"use strict";!function(e){function t(e,t,s){throw new e("Failed to execute 'requestSubmit' on 'HTMLFormElement': "+t+".",s)}"function"!=typeof e.requestSubmit&&(e.requestSubmit=function(e){e?(!function(e,s){e instanceof HTMLElement||t(TypeError,"parameter 1 is not of type 'HTMLElement'"),"submit"==e.type||t(TypeError,"The specified element is not a submit button"),e.form==s||t(DOMException,"The specified element is not owned by this form element","NotFoundError")}(e,this),e.click()):((e=document.createElement("input")).type="submit",e.hidden=!0,this.appendChild(e),e.click(),this.removeChild(e))})}(HTMLFormElement.prototype);const t=new WeakMap;function s(e){const s=function(e){const t=e instanceof Element?e:e instanceof Node?e.parentElement:null,s=t?t.closest("input, button"):null;return"submit"==s?.type?s:null}(e.target);s&&s.form&&t.set(s.form,s)}!function(){if("submitter"in Event.prototype)return;let e=window.Event.prototype;if("SubmitEvent"in window){const t=window.SubmitEvent.prototype;if(!/Apple Computer/.test(navigator.vendor)||"submitter"in t)return;e=t}addEventListener("click",s,!0),Object.defineProperty(e,"submitter",{get(){if("submit"==this.type&&this.target instanceof HTMLFormElement)return t.get(this.target)}})}();const r={eager:"eager",lazy:"lazy"};class i extends HTMLElement{static delegateConstructor=void 0;loaded=Promise.resolve();static get observedAttributes(){return["disabled","loading","src"]}constructor(){super(),this.delegate=new i.delegateConstructor(this)}connectedCallback(){this.delegate.connect()}disconnectedCallback(){this.delegate.disconnect()}reload(){return this.delegate.sourceURLReloaded()}attributeChangedCallback(e){"loading"==e?this.delegate.loadingStyleChanged():"src"==e?this.delegate.sourceURLChanged():"disabled"==e&&this.delegate.disabledChanged()}get src(){return this.getAttribute("src")}set src(e){e?this.setAttribute("src",e):this.removeAttribute("src")}get refresh(){return this.getAttribute("refresh")}set refresh(e){e?this.setAttribute("refresh",e):this.removeAttribute("refresh")}get shouldReloadWithMorph(){return this.src&&"morph"===this.refresh}get loading(){return function(e){if("lazy"===e.toLowerCase())return r.lazy;return r.eager}(this.getAttribute("loading")||"")}set loading(e){e?this.setAttribute("loading",e):this.removeAttribute("loading")}get disabled(){return this.hasAttribute("disabled")}set disabled(e){e?this.setAttribute("disabled",""):this.removeAttribute("disabled")}get autoscroll(){return this.hasAttribute("autoscroll")}set autoscroll(e){e?this.setAttribute("autoscroll",""):this.removeAttribute("autoscroll")}get complete(){return!this.delegate.isLoading}get isActive(){return this.ownerDocument===document&&!this.isPreview}get isPreview(){return this.ownerDocument?.documentElement?.hasAttribute("data-turbo-preview")}}const n={enabled:!0,progressBarDelay:500,unvisitableExtensions:new Set([".7z",".aac",".apk",".avi",".bmp",".bz2",".css",".csv",".deb",".dmg",".doc",".docx",".exe",".gif",".gz",".heic",".heif",".ico",".iso",".jpeg",".jpg",".js",".json",".m4a",".mkv",".mov",".mp3",".mp4",".mpeg",".mpg",".msi",".ogg",".ogv",".pdf",".pkg",".png",".ppt",".pptx",".rar",".rtf",".svg",".tar",".tif",".tiff",".txt",".wav",".webm",".webp",".wma",".wmv",".xls",".xlsx",".xml",".zip"])};function o(e){if("false"==e.getAttribute("data-turbo-eval"))return e;{const t=document.createElement("script"),s=R();return s&&(t.nonce=s),t.textContent=e.textContent,t.async=!1,function(e,t){for(const{name:s,value:r}of t.attributes)e.setAttribute(s,r)}(t,e),t}}function a(e,{target:t,cancelable:s,detail:r}={}){const i=new CustomEvent(e,{cancelable:s,bubbles:!0,composed:!0,detail:r});return t&&t.isConnected?t.dispatchEvent(i):document.documentElement.dispatchEvent(i),i}function l(e){e.preventDefault(),e.stopImmediatePropagation()}function c(){return"hidden"===document.visibilityState?d():h()}function h(){return new Promise((e=>requestAnimationFrame((()=>e()))))}function d(){return new Promise((e=>setTimeout((()=>e()),0)))}function u(e=""){return(new DOMParser).parseFromString(e,"text/html")}function m(e,...t){const s=function(e,t){return e.reduce(((e,s,r)=>e+s+(null==t[r]?"":t[r])),"")}(e,t).replace(/^\n/,"").split("\n"),r=s[0].match(/^\s+/),i=r?r[0].length:0;return s.map((e=>e.slice(i))).join("\n")}function p(){return Array.from({length:36}).map(((e,t)=>8==t||13==t||18==t||23==t?"-":14==t?"4":19==t?(Math.floor(4*Math.random())+8).toString(16):Math.floor(15*Math.random()).toString(16))).join("")}function f(e,...t){for(const s of t.map((t=>t?.getAttribute(e))))if("string"==typeof s)return s;return null}function g(...e){for(const t of e)"turbo-frame"==t.localName&&t.setAttribute("busy",""),t.setAttribute("aria-busy","true")}function b(...e){for(const t of e)"turbo-frame"==t.localName&&t.removeAttribute("busy"),t.removeAttribute("aria-busy")}function v(e,t=2e3){return new Promise((s=>{const r=()=>{e.removeEventListener("error",r),e.removeEventListener("load",r),s()};e.addEventListener("load",r,{once:!0}),e.addEventListener("error",r,{once:!0}),setTimeout(s,t)}))}function S(e){switch(e){case"replace":return history.replaceState;case"advance":case"restore":return history.pushState}}function w(...e){const t=f("data-turbo-action",...e);return function(e){return"advance"==e||"replace"==e||"restore"==e}(t)?t:null}function E(e){return document.querySelector(`meta[name="${e}"]`)}function y(e){const t=E(e);return t&&t.content}function R(){const e=E("csp-nonce");if(e){const{nonce:t,content:s}=e;return""==t?s:t}}function L(e,t){if(e instanceof Element)return e.closest(t)||L(e.assignedSlot||e.getRootNode()?.host,t)}function T(e){return!!e&&null==e.closest("[inert], :disabled, [hidden], details:not([open]), dialog:not([open])")&&"function"==typeof e.focus}function A(e){return Array.from(e.querySelectorAll("[autofocus]")).find(T)}function P(e){if("_blank"===e)return!1;if(e){for(const t of document.getElementsByName(e))if(t instanceof HTMLIFrameElement)return!1;return!0}return!0}function C(e){return L(e,"a[href]:not([target^=_]):not([download])")}function k(e){return q(e.getAttribute("href")||"")}const M={"aria-disabled":{beforeSubmit:e=>{e.setAttribute("aria-disabled","true"),e.addEventListener("click",l)},afterSubmit:e=>{e.removeAttribute("aria-disabled"),e.removeEventListener("click",l)}},disabled:{beforeSubmit:e=>e.disabled=!0,afterSubmit:e=>e.disabled=!1}};const F=new class{#e=null;constructor(e){Object.assign(this,e)}get submitter(){return this.#e}set submitter(e){this.#e=M[e]||e}}({mode:"on",submitter:"disabled"}),I={drive:n,forms:F};function q(e){return new URL(e.toString(),document.baseURI)}function H(e){let t;return e.hash?e.hash.slice(1):(t=e.href.match(/#(.*)$/))?t[1]:void 0}function B(e,t){return q(t?.getAttribute("formaction")||e.getAttribute("action")||e.action)}function O(e){return(function(e){return function(e){return e.pathname.split("/").slice(1)}(e).slice(-1)[0]}(e).match(/\.[^.]*$/)||[])[0]||""}function N(e,t){const s=function(e){return t=e.origin+e.pathname,t.endsWith("/")?t:t+"/";var t}(t);return e.href===q(s).href||e.href.startsWith(s)}function x(e,t){return N(e,t)&&!I.drive.unvisitableExtensions.has(O(e))}function V(e){const t=H(e);return null!=t?e.href.slice(0,-(t.length+1)):e.href}function D(e){return V(e)}class W{constructor(e){this.response=e}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 q(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(){return this.isHTML?this.response.clone().text():Promise.resolve(void 0)}header(e){return this.response.headers.get(e)}}class U extends Set{constructor(e){super(),this.maxSize=e}add(e){if(this.size>=this.maxSize){const e=this.values().next().value;this.delete(e)}super.add(e)}}const j=new U(20),$=window.fetch;function z(e,t={}){const s=new Headers(t.headers||{}),r=p();return j.add(r),s.append("X-Turbo-Request-Id",r),$(e,{...t,headers:s})}function _(e){switch(e.toLowerCase()){case"get":return X.get;case"post":return X.post;case"put":return X.put;case"patch":return X.patch;case"delete":return X.delete}}const X={get:"get",post:"post",put:"put",patch:"patch",delete:"delete"};function K(e){switch(e.toLowerCase()){case Q.multipart:return Q.multipart;case Q.plain:return Q.plain;default:return Q.urlEncoded}}const Q={urlEncoded:"application/x-www-form-urlencoded",multipart:"multipart/form-data",plain:"text/plain"};class Y{abortController=new AbortController;#t=e=>{};constructor(e,t,s,r=new URLSearchParams,i=null,n=Q.urlEncoded){const[o,a]=J(q(s),t,r,n);this.delegate=e,this.url=o,this.target=i,this.fetchOptions={credentials:"same-origin",redirect:"follow",method:t.toUpperCase(),headers:{...this.defaultHeaders},body:a,signal:this.abortSignal,referrer:this.delegate.referrer?.href},this.enctype=n}get method(){return this.fetchOptions.method}set method(e){const t=this.isSafe?this.url.searchParams:this.fetchOptions.body||new FormData,s=_(e)||X.get;this.url.search="";const[r,i]=J(this.url,s,t,this.enctype);this.url=r,this.fetchOptions.body=i,this.fetchOptions.method=s.toUpperCase()}get headers(){return this.fetchOptions.headers}set headers(e){this.fetchOptions.headers=e}get body(){return this.isSafe?this.url.searchParams:this.fetchOptions.body}set body(e){this.fetchOptions.body=e}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:e}=this;this.delegate.prepareRequest(this);const t=await this.#s(e);try{this.delegate.requestStarted(this),t.detail.fetchRequest?this.response=t.detail.fetchRequest.response:this.response=z(this.url.href,e);const s=await this.response;return await this.receive(s)}catch(e){if("AbortError"!==e.name)throw this.#r(e)&&this.delegate.requestErrored(this,e),e}finally{this.delegate.requestFinished(this)}}async receive(e){const t=new W(e);return a("turbo:before-fetch-response",{cancelable:!0,detail:{fetchResponse:t},target:this.target}).defaultPrevented?this.delegate.requestPreventedHandlingResponse(this,t):t.succeeded?this.delegate.requestSucceededWithResponse(this,t):this.delegate.requestFailedWithResponse(this,t),t}get defaultHeaders(){return{Accept:"text/html, application/xhtml+xml"}}get isSafe(){return G(this.method)}get abortSignal(){return this.abortController.signal}acceptResponseType(e){this.headers.Accept=[e,this.headers.Accept].join(", ")}async#s(e){const t=new Promise((e=>this.#t=e)),s=a("turbo:before-fetch-request",{cancelable:!0,detail:{fetchOptions:e,url:this.url,resume:this.#t},target:this.target});return this.url=s.detail.url,s.defaultPrevented&&await t,s}#r(e){return!a("turbo:fetch-request-error",{target:this.target,cancelable:!0,detail:{request:this,error:e}}).defaultPrevented}}function G(e){return _(e)==X.get}function J(e,t,s,r){const i=Array.from(s).length>0?new URLSearchParams(Z(s)):e.searchParams;return G(t)?[ee(e,i),null]:r==Q.urlEncoded?[e,i]:[e,s]}function Z(e){const t=[];for(const[s,r]of e)r instanceof File||t.push([s,r]);return t}function ee(e,t){const s=new URLSearchParams(Z(t));return e.search=s.toString(),e}class te{started=!1;constructor(e,t){this.delegate=e,this.element=t,this.intersectionObserver=new IntersectionObserver(this.intersect)}start(){this.started||(this.started=!0,this.intersectionObserver.observe(this.element))}stop(){this.started&&(this.started=!1,this.intersectionObserver.unobserve(this.element))}intersect=e=>{const t=e.slice(-1)[0];t?.isIntersecting&&this.delegate.elementAppearedInViewport(this.element)}}class se{static contentType="text/vnd.turbo-stream.html";static wrap(e){return"string"==typeof e?new this(function(e){const t=document.createElement("template");return t.innerHTML=e,t.content}(e)):e}constructor(e){this.fragment=function(e){for(const t of e.querySelectorAll("turbo-stream")){const e=document.importNode(t,!0);for(const t of e.templateElement.content.querySelectorAll("script"))t.replaceWith(o(t));t.replaceWith(e)}return e}(e)}}const re=new class{#i=null;#n=null;get(e){if(this.#n&&this.#n.url===e&&this.#n.expire>Date.now())return this.#n.request}setLater(e,t,s){this.clear(),this.#i=setTimeout((()=>{t.perform(),this.set(e,t,s),this.#i=null}),100)}set(e,t,s){this.#n={url:e,request:t,expire:new Date((new Date).getTime()+s)}}clear(){this.#i&&clearTimeout(this.#i),this.#n=null}},ie={initialized:"initialized",requesting:"requesting",waiting:"waiting",receiving:"receiving",stopping:"stopping",stopped:"stopped"};class ne{state=ie.initialized;static confirmMethod(e){return Promise.resolve(confirm(e))}constructor(e,t,s,r=!1){const i=function(e,t){const s=t?.getAttribute("formmethod")||e.getAttribute("method")||"";return _(s.toLowerCase())||X.get}(t,s),n=function(e,t){const s=q(e);G(t)&&(s.search="");return s}(function(e,t){const s="string"==typeof e.action?e.action:null;return t?.hasAttribute("formaction")?t.getAttribute("formaction")||"":e.getAttribute("action")||s||""}(t,s),i),o=function(e,t){const s=new FormData(e),r=t?.getAttribute("name"),i=t?.getAttribute("value");r&&s.append(r,i||"");return s}(t,s),a=function(e,t){return K(t?.getAttribute("formenctype")||e.enctype)}(t,s);this.delegate=e,this.formElement=t,this.submitter=s,this.fetchRequest=new Y(this,i,n,o,t,a),this.mustRedirect=r}get method(){return this.fetchRequest.method}set method(e){this.fetchRequest.method=e}get action(){return this.fetchRequest.url.toString()}set action(e){this.fetchRequest.url=q(e)}get body(){return this.fetchRequest.body}get enctype(){return this.fetchRequest.enctype}get isSafe(){return this.fetchRequest.isSafe}get location(){return this.fetchRequest.url}async start(){const{initialized:e,requesting:t}=ie,s=f("data-turbo-confirm",this.submitter,this.formElement);if("string"==typeof s){const e="function"==typeof I.forms.confirm?I.forms.confirm:ne.confirmMethod;if(!await e(s,this.formElement,this.submitter))return}if(this.state==e)return this.state=t,this.fetchRequest.perform()}stop(){const{stopping:e,stopped:t}=ie;if(this.state!=e&&this.state!=t)return this.state=e,this.fetchRequest.cancel(),!0}prepareRequest(e){if(!e.isSafe){const t=function(e){if(null!=e){const t=(document.cookie?document.cookie.split("; "):[]).find((t=>t.startsWith(e)));if(t){const e=t.split("=").slice(1).join("=");return e?decodeURIComponent(e):void 0}}}(y("csrf-param"))||y("csrf-token");t&&(e.headers["X-CSRF-Token"]=t)}this.requestAcceptsTurboStreamResponse(e)&&e.acceptResponseType(se.contentType)}requestStarted(e){this.state=ie.waiting,this.submitter&&I.forms.submitter.beforeSubmit(this.submitter),this.setSubmitsWith(),g(this.formElement),a("turbo:submit-start",{target:this.formElement,detail:{formSubmission:this}}),this.delegate.formSubmissionStarted(this)}requestPreventedHandlingResponse(e,t){re.clear(),this.result={success:t.succeeded,fetchResponse:t}}requestSucceededWithResponse(e,t){if(t.clientError||t.serverError)this.delegate.formSubmissionFailedWithResponse(this,t);else if(re.clear(),this.requestMustRedirect(e)&&function(e){return 200==e.statusCode&&!e.redirected}(t)){const e=new Error("Form responses must redirect to another location");this.delegate.formSubmissionErrored(this,e)}else this.state=ie.receiving,this.result={success:!0,fetchResponse:t},this.delegate.formSubmissionSucceededWithResponse(this,t)}requestFailedWithResponse(e,t){this.result={success:!1,fetchResponse:t},this.delegate.formSubmissionFailedWithResponse(this,t)}requestErrored(e,t){this.result={success:!1,error:t},this.delegate.formSubmissionErrored(this,t)}requestFinished(e){this.state=ie.stopped,this.submitter&&I.forms.submitter.afterSubmit(this.submitter),this.resetSubmitterText(),b(this.formElement),a("turbo:submit-end",{target:this.formElement,detail:{formSubmission:this,...this.result}}),this.delegate.formSubmissionFinished(this)}setSubmitsWith(){if(this.submitter&&this.submitsWith)if(this.submitter.matches("button"))this.originalSubmitText=this.submitter.innerHTML,this.submitter.innerHTML=this.submitsWith;else if(this.submitter.matches("input")){const e=this.submitter;this.originalSubmitText=e.value,e.value=this.submitsWith}}resetSubmitterText(){if(this.submitter&&this.originalSubmitText)if(this.submitter.matches("button"))this.submitter.innerHTML=this.originalSubmitText;else if(this.submitter.matches("input")){this.submitter.value=this.originalSubmitText}}requestMustRedirect(e){return!e.isSafe&&this.mustRedirect}requestAcceptsTurboStreamResponse(e){return!e.isSafe||function(e,...t){return t.some((t=>t&&t.hasAttribute(e)))}("data-turbo-stream",this.submitter,this.formElement)}get submitsWith(){return this.submitter?.getAttribute("data-turbo-submits-with")}}class oe{constructor(e){this.element=e}get activeElement(){return this.element.ownerDocument.activeElement}get children(){return[...this.element.children]}hasAnchor(e){return null!=this.getElementForAnchor(e)}getElementForAnchor(e){return e?this.element.querySelector(`[id='${e}'], a[name='${e}']`):null}get isConnected(){return this.element.isConnected}get firstAutofocusableElement(){return A(this.element)}get permanentElements(){return le(this.element)}getPermanentElementById(e){return ae(this.element,e)}getPermanentElementMapForSnapshot(e){const t={};for(const s of this.permanentElements){const{id:r}=s,i=e.getPermanentElementById(r);i&&(t[r]=[s,i])}return t}}function ae(e,t){return e.querySelector(`#${t}[data-turbo-permanent]`)}function le(e){return e.querySelectorAll("[id][data-turbo-permanent]")}class ce{started=!1;constructor(e,t){this.delegate=e,this.eventTarget=t}start(){this.started||(this.eventTarget.addEventListener("submit",this.submitCaptured,!0),this.started=!0)}stop(){this.started&&(this.eventTarget.removeEventListener("submit",this.submitCaptured,!0),this.started=!1)}submitCaptured=()=>{this.eventTarget.removeEventListener("submit",this.submitBubbled,!1),this.eventTarget.addEventListener("submit",this.submitBubbled,!1)};submitBubbled=e=>{if(!e.defaultPrevented){const t=e.target instanceof HTMLFormElement?e.target:void 0,s=e.submitter||void 0;t&&function(e,t){const s=t?.getAttribute("formmethod")||e.getAttribute("method");return"dialog"!=s}(t,s)&&function(e,t){const s=t?.getAttribute("formtarget")||e.getAttribute("target");return P(s)}(t,s)&&this.delegate.willSubmitForm(t,s)&&(e.preventDefault(),e.stopImmediatePropagation(),this.delegate.formSubmitted(t,s))}}}class he{#o=e=>{};#a=e=>{};constructor(e,t){this.delegate=e,this.element=t}scrollToAnchor(e){const t=this.snapshot.getElementForAnchor(e);t?(this.scrollToElement(t),this.focusElement(t)):this.scrollToPosition({x:0,y:0})}scrollToAnchorFromLocation(e){this.scrollToAnchor(H(e))}scrollToElement(e){e.scrollIntoView()}focusElement(e){e instanceof HTMLElement&&(e.hasAttribute("tabindex")?e.focus():(e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")))}scrollToPosition({x:e,y:t}){this.scrollRoot.scrollTo(e,t)}scrollToTop(){this.scrollToPosition({x:0,y:0})}get scrollRoot(){return window}async render(e){const{isPreview:t,shouldRender:s,willRender:r,newSnapshot:i}=e,n=r;if(s)try{this.renderPromise=new Promise((e=>this.#o=e)),this.renderer=e,await this.prepareToRenderSnapshot(e);const s=new Promise((e=>this.#a=e)),r={resume:this.#a,render:this.renderer.renderElement,renderMethod:this.renderer.renderMethod};this.delegate.allowsImmediateRender(i,r)||await s,await this.renderSnapshot(e),this.delegate.viewRenderedSnapshot(i,t,this.renderer.renderMethod),this.delegate.preloadOnLoadLinksForView(this.element),this.finishRenderingSnapshot(e)}finally{delete this.renderer,this.#o(void 0),delete this.renderPromise}else n&&this.invalidate(e.reloadReason)}invalidate(e){this.delegate.viewInvalidated(e)}async prepareToRenderSnapshot(e){this.markAsPreview(e.isPreview),await e.prepareToRender()}markAsPreview(e){e?this.element.setAttribute("data-turbo-preview",""):this.element.removeAttribute("data-turbo-preview")}markVisitDirection(e){this.element.setAttribute("data-turbo-visit-direction",e)}unmarkVisitDirection(){this.element.removeAttribute("data-turbo-visit-direction")}async renderSnapshot(e){await e.render()}finishRenderingSnapshot(e){e.finishRendering()}}class de extends he{missing(){this.element.innerHTML='<strong class="turbo-frame-error">Content missing</strong>'}get snapshot(){return new oe(this.element)}}class ue{constructor(e,t){this.delegate=e,this.element=t}start(){this.element.addEventListener("click",this.clickBubbled),document.addEventListener("turbo:click",this.linkClicked),document.addEventListener("turbo:before-visit",this.willVisit)}stop(){this.element.removeEventListener("click",this.clickBubbled),document.removeEventListener("turbo:click",this.linkClicked),document.removeEventListener("turbo:before-visit",this.willVisit)}clickBubbled=e=>{this.clickEventIsSignificant(e)?this.clickEvent=e:delete this.clickEvent};linkClicked=e=>{this.clickEvent&&this.clickEventIsSignificant(e)&&this.delegate.shouldInterceptLinkClick(e.target,e.detail.url,e.detail.originalEvent)&&(this.clickEvent.preventDefault(),e.preventDefault(),this.delegate.linkClickIntercepted(e.target,e.detail.url,e.detail.originalEvent)),delete this.clickEvent};willVisit=e=>{delete this.clickEvent};clickEventIsSignificant(e){const t=e.composed?e.target?.parentElement:e.target,s=C(t)||t;return s instanceof Element&&s.closest("turbo-frame, html")==this.element}}class me{started=!1;constructor(e,t){this.delegate=e,this.eventTarget=t}start(){this.started||(this.eventTarget.addEventListener("click",this.clickCaptured,!0),this.started=!0)}stop(){this.started&&(this.eventTarget.removeEventListener("click",this.clickCaptured,!0),this.started=!1)}clickCaptured=()=>{this.eventTarget.removeEventListener("click",this.clickBubbled,!1),this.eventTarget.addEventListener("click",this.clickBubbled,!1)};clickBubbled=e=>{if(e instanceof MouseEvent&&this.clickEventIsSignificant(e)){const t=C(e.composedPath&&e.composedPath()[0]||e.target);if(t&&P(t.target)){const s=k(t);this.delegate.willFollowLinkToLocation(t,s,e)&&(e.preventDefault(),this.delegate.followedLinkToLocation(t,s))}}};clickEventIsSignificant(e){return!(e.target&&e.target.isContentEditable||e.defaultPrevented||e.which>1||e.altKey||e.ctrlKey||e.metaKey||e.shiftKey)}}class pe{constructor(e,t){this.delegate=e,this.linkInterceptor=new me(this,t)}start(){this.linkInterceptor.start()}stop(){this.linkInterceptor.stop()}canPrefetchRequestToLocation(e,t){return!1}prefetchAndCacheRequestToLocation(e,t){}willFollowLinkToLocation(e,t,s){return this.delegate.willSubmitFormLinkToLocation(e,t,s)&&(e.hasAttribute("data-turbo-method")||e.hasAttribute("data-turbo-stream"))}followedLinkToLocation(e,t){const s=document.createElement("form");for(const[e,r]of t.searchParams)s.append(Object.assign(document.createElement("input"),{type:"hidden",name:e,value:r}));const r=Object.assign(t,{search:""});s.setAttribute("data-turbo","true"),s.setAttribute("action",r.href),s.setAttribute("hidden","");const i=e.getAttribute("data-turbo-method");i&&s.setAttribute("method",i);const n=e.getAttribute("data-turbo-frame");n&&s.setAttribute("data-turbo-frame",n);const o=w(e);o&&s.setAttribute("data-turbo-action",o);const a=e.getAttribute("data-turbo-confirm");a&&s.setAttribute("data-turbo-confirm",a);e.hasAttribute("data-turbo-stream")&&s.setAttribute("data-turbo-stream",""),this.delegate.submittedFormLinkToLocation(e,t,s),document.body.appendChild(s),s.addEventListener("turbo:submit-end",(()=>s.remove()),{once:!0}),requestAnimationFrame((()=>s.requestSubmit()))}}class fe{static async preservingPermanentElements(e,t,s){const r=new this(e,t);r.enter(),await s(),r.leave()}constructor(e,t){this.delegate=e,this.permanentElementMap=t}enter(){for(const e in this.permanentElementMap){const[t,s]=this.permanentElementMap[e];this.delegate.enteringBardo(t,s),this.replaceNewPermanentElementWithPlaceholder(s)}}leave(){for(const e in this.permanentElementMap){const[t]=this.permanentElementMap[e];this.replaceCurrentPermanentElementWithClone(t),this.replacePlaceholderWithPermanentElement(t),this.delegate.leavingBardo(t)}}replaceNewPermanentElementWithPlaceholder(e){const t=function(e){const t=document.createElement("meta");return t.setAttribute("name","turbo-permanent-placeholder"),t.setAttribute("content",e.id),t}(e);e.replaceWith(t)}replaceCurrentPermanentElementWithClone(e){const t=e.cloneNode(!0);e.replaceWith(t)}replacePlaceholderWithPermanentElement(e){const t=this.getPlaceholderById(e.id);t?.replaceWith(e)}getPlaceholderById(e){return this.placeholders.find((t=>t.content==e))}get placeholders(){return[...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")]}}class ge{#l=null;static renderElement(e,t){}constructor(e,t,s,r=!0){this.currentSnapshot=e,this.newSnapshot=t,this.isPreview=s,this.willRender=r,this.renderElement=this.constructor.renderElement,this.promise=new Promise(((e,t)=>this.resolvingFunctions={resolve:e,reject:t}))}get shouldRender(){return!0}get shouldAutofocus(){return!0}get reloadReason(){}prepareToRender(){}render(){}finishRendering(){this.resolvingFunctions&&(this.resolvingFunctions.resolve(),delete this.resolvingFunctions)}async preservingPermanentElements(e){await fe.preservingPermanentElements(this,this.permanentElementMap,e)}focusFirstAutofocusableElement(){if(this.shouldAutofocus){const e=this.connectedSnapshot.firstAutofocusableElement;e&&e.focus()}}enteringBardo(e){this.#l||e.contains(this.currentSnapshot.activeElement)&&(this.#l=this.currentSnapshot.activeElement)}leavingBardo(e){e.contains(this.#l)&&this.#l instanceof HTMLElement&&(this.#l.focus(),this.#l=null)}get connectedSnapshot(){return this.newSnapshot.isConnected?this.newSnapshot:this.currentSnapshot}get currentElement(){return this.currentSnapshot.element}get newElement(){return this.newSnapshot.element}get permanentElementMap(){return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot)}get renderMethod(){return"replace"}}class be extends ge{static renderElement(e,t){const s=document.createRange();s.selectNodeContents(e),s.deleteContents();const r=t,i=r.ownerDocument?.createRange();i&&(i.selectNodeContents(r),e.appendChild(i.extractContents()))}constructor(e,t,s,r,i,n=!0){super(t,s,r,i,n),this.delegate=e}get shouldRender(){return!0}async render(){await c(),this.preservingPermanentElements((()=>{this.loadFrameElement()})),this.scrollFrameIntoView(),await c(),this.focusFirstAutofocusableElement(),await c(),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 s=this.currentElement.firstElementChild,r=(e=this.currentElement.getAttribute("data-autoscroll-block"),t="end","end"==e||"start"==e||"center"==e||"nearest"==e?e:t),i=function(e,t){return"auto"==e||"smooth"==e?e:t}(this.currentElement.getAttribute("data-autoscroll-behavior"),"auto");if(s)return s.scrollIntoView({block:r,behavior:i}),!0}var e,t;return!1}activateScriptElements(){for(const e of this.newScriptElements){const t=o(e);e.replaceWith(t)}}get newScriptElements(){return this.currentElement.querySelectorAll("script")}}var ve=function(){let e=new Set,t={morphStyle:"outerHTML",callbacks:{beforeNodeAdded:c,afterNodeAdded:c,beforeNodeMorphed:c,afterNodeMorphed:c,beforeNodeRemoved:c,afterNodeRemoved:c,beforeAttributeUpdated:c},head:{style:"merge",shouldPreserve:function(e){return"true"===e.getAttribute("im-preserve")},shouldReAppend:function(e){return"true"===e.getAttribute("im-re-append")},shouldRemove:c,afterHeadMorphed:c}};function s(e,t,r){if(r.head.block){let i=e.querySelector("head"),n=t.querySelector("head");if(i&&n){let o=l(n,i,r);return void Promise.all(o).then((function(){s(e,t,Object.assign(r,{head:{block:!1,ignore:!0}}))}))}}if("innerHTML"===r.morphStyle)return n(t,e,r),e.children;if("outerHTML"===r.morphStyle||null==r.morphStyle){let s=function(e,t,s){let r;r=e.firstChild;let i=r,n=0;for(;r;){let e=f(r,t,s);e>n&&(i=r,n=e),r=r.nextSibling}return i}(t,e,r),n=s?.previousSibling,o=s?.nextSibling,a=i(e,s,r);return s?function(e,t,s){let r=[],i=[];for(;null!=e;)r.push(e),e=e.previousSibling;for(;r.length>0;){let e=r.pop();i.push(e),t.parentElement.insertBefore(e,t)}i.push(t);for(;null!=s;)r.push(s),i.push(s),s=s.nextSibling;for(;r.length>0;)t.parentElement.insertBefore(r.pop(),t.nextSibling);return i}(n,a,o):[]}throw"Do not understand how to morph style "+r.morphStyle}function r(e,t){return t.ignoreActiveValue&&e===document.activeElement&&e!==document.body}function i(e,t,s){if(!s.ignoreActive||e!==document.activeElement)return null==t?!1===s.callbacks.beforeNodeRemoved(e)?e:(e.remove(),s.callbacks.afterNodeRemoved(e),null):d(e,t)?(!1===s.callbacks.beforeNodeMorphed(e,t)||(e instanceof HTMLHeadElement&&s.head.ignore||(e instanceof HTMLHeadElement&&"morph"!==s.head.style?l(t,e,s):(!function(e,t,s){let i=e.nodeType;if(1===i){const r=e.attributes,i=t.attributes;for(const e of r)o(e.name,t,"update",s)||t.getAttribute(e.name)!==e.value&&t.setAttribute(e.name,e.value);for(let r=i.length-1;0<=r;r--){const n=i[r];o(n.name,t,"remove",s)||(e.hasAttribute(n.name)||t.removeAttribute(n.name))}}8!==i&&3!==i||t.nodeValue!==e.nodeValue&&(t.nodeValue=e.nodeValue);r(t,s)||function(e,t,s){if(e instanceof HTMLInputElement&&t instanceof HTMLInputElement&&"file"!==e.type){let r=e.value,i=t.value;a(e,t,"checked",s),a(e,t,"disabled",s),e.hasAttribute("value")?r!==i&&(o("value",t,"update",s)||(t.setAttribute("value",r),t.value=r)):o("value",t,"remove",s)||(t.value="",t.removeAttribute("value"))}else if(e instanceof HTMLOptionElement)a(e,t,"selected",s);else if(e instanceof HTMLTextAreaElement&&t instanceof HTMLTextAreaElement){let r=e.value,i=t.value;if(o("value",t,"update",s))return;r!==i&&(t.value=r),t.firstChild&&t.firstChild.nodeValue!==r&&(t.firstChild.nodeValue=r)}}(e,t,s)}(t,e,s),r(e,s)||n(t,e,s))),s.callbacks.afterNodeMorphed(e,t)),e):!1===s.callbacks.beforeNodeRemoved(e)||!1===s.callbacks.beforeNodeAdded(t)?e:(e.parentElement.replaceChild(t,e),s.callbacks.afterNodeAdded(t),s.callbacks.afterNodeRemoved(e),t)}function n(e,t,s){let r,n=e.firstChild,o=t.firstChild;for(;n;){if(r=n,n=r.nextSibling,null==o){if(!1===s.callbacks.beforeNodeAdded(r))return;t.appendChild(r),s.callbacks.afterNodeAdded(r),S(s,r);continue}if(h(r,o,s)){i(o,r,s),o=o.nextSibling,S(s,r);continue}let a=m(e,t,r,o,s);if(a){o=u(o,a,s),i(a,r,s),S(s,r);continue}let l=p(e,t,r,o,s);if(l)o=u(o,l,s),i(l,r,s),S(s,r);else{if(!1===s.callbacks.beforeNodeAdded(r))return;t.insertBefore(r,o),s.callbacks.afterNodeAdded(r),S(s,r)}}for(;null!==o;){let e=o;o=o.nextSibling,g(e,s)}}function o(e,t,s,r){return!("value"!==e||!r.ignoreActiveValue||t!==document.activeElement)||!1===r.callbacks.beforeAttributeUpdated(e,t,s)}function a(e,t,s,r){if(e[s]!==t[s]){let i=o(s,t,"update",r);i||(t[s]=e[s]),e[s]?i||t.setAttribute(s,e[s]):o(s,t,"remove",r)||t.removeAttribute(s)}}function l(e,t,s){let r=[],i=[],n=[],o=[],a=s.head.style,l=new Map;for(const t of e.children)l.set(t.outerHTML,t);for(const e of t.children){let t=l.has(e.outerHTML),r=s.head.shouldReAppend(e),c=s.head.shouldPreserve(e);t||c?r?i.push(e):(l.delete(e.outerHTML),n.push(e)):"append"===a?r&&(i.push(e),o.push(e)):!1!==s.head.shouldRemove(e)&&i.push(e)}o.push(...l.values());let c=[];for(const e of o){let i=document.createRange().createContextualFragment(e.outerHTML).firstChild;if(!1!==s.callbacks.beforeNodeAdded(i)){if(i.href||i.src){let e=null,t=new Promise((function(t){e=t}));i.addEventListener("load",(function(){e()})),c.push(t)}t.appendChild(i),s.callbacks.afterNodeAdded(i),r.push(i)}}for(const e of i)!1!==s.callbacks.beforeNodeRemoved(e)&&(t.removeChild(e),s.callbacks.afterNodeRemoved(e));return s.head.afterHeadMorphed(t,{added:r,kept:n,removed:i}),c}function c(){}function h(e,t,s){return null!=e&&null!=t&&(e.nodeType===t.nodeType&&e.tagName===t.tagName&&(""!==e.id&&e.id===t.id||w(s,e,t)>0))}function d(e,t){return null!=e&&null!=t&&(e.nodeType===t.nodeType&&e.tagName===t.tagName)}function u(e,t,s){for(;e!==t;){let t=e;e=e.nextSibling,g(t,s)}return S(s,t),t.nextSibling}function m(e,t,s,r,i){let n=w(i,s,t);if(n>0){let t=r,o=0;for(;null!=t;){if(h(s,t,i))return t;if(o+=w(i,t,e),o>n)return null;t=t.nextSibling}}return null}function p(e,t,s,r,i){let n=r,o=s.nextSibling,a=0;for(;null!=n;){if(w(i,n,e)>0)return null;if(d(s,n))return n;if(d(o,n)&&(a++,o=o.nextSibling,a>=2))return null;n=n.nextSibling}return n}function f(e,t,s){return d(e,t)?.5+w(s,e,t):0}function g(e,t){S(t,e),!1!==t.callbacks.beforeNodeRemoved(e)&&(e.remove(),t.callbacks.afterNodeRemoved(e))}function b(e,t){return!e.deadIds.has(t)}function v(t,s,r){return(t.idMap.get(r)||e).has(s)}function S(t,s){let r=t.idMap.get(s)||e;for(const e of r)t.deadIds.add(e)}function w(t,s,r){let i=t.idMap.get(s)||e,n=0;for(const e of i)b(t,e)&&v(t,e,r)&&++n;return n}function E(e,t){let s=e.parentElement,r=e.querySelectorAll("[id]");for(const e of r){let r=e;for(;r!==s&&null!=r;){let s=t.get(r);null==s&&(s=new Set,t.set(r,s)),s.add(e.id),r=r.parentElement}}}function y(e,t){let s=new Map;return E(e,s),E(t,s),s}return{morph:function(e,r,i={}){e instanceof Document&&(e=e.documentElement),"string"==typeof r&&(r=function(e){let t=new DOMParser,s=e.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim,"");if(s.match(/<\/html>/)||s.match(/<\/head>/)||s.match(/<\/body>/)){let r=t.parseFromString(e,"text/html");if(s.match(/<\/html>/))return r.generatedByIdiomorph=!0,r;{let e=r.firstChild;return e?(e.generatedByIdiomorph=!0,e):null}}{let s=t.parseFromString("<body><template>"+e+"</template></body>","text/html").body.querySelector("template").content;return s.generatedByIdiomorph=!0,s}}(r));let n=function(e){if(null==e){return document.createElement("div")}if(e.generatedByIdiomorph)return e;if(e instanceof Node){const t=document.createElement("div");return t.append(e),t}{const t=document.createElement("div");for(const s of[...e])t.append(s);return t}}(r),o=function(e,s,r){return r=function(e){let s={};return Object.assign(s,t),Object.assign(s,e),s.callbacks={},Object.assign(s.callbacks,t.callbacks),Object.assign(s.callbacks,e.callbacks),s.head={},Object.assign(s.head,t.head),Object.assign(s.head,e.head),s}(r),{target:e,newContent:s,config:r,morphStyle:r.morphStyle,ignoreActive:r.ignoreActive,ignoreActiveValue:r.ignoreActiveValue,idMap:y(e,s),deadIds:new Set,callbacks:r.callbacks,head:r.head}}(e,n,i);return s(e,n,o)},defaults:t}}();function Se(e,t,{callbacks:s,...r}={}){ve.morph(e,t,{...r,callbacks:new Ee(s)})}function we(e,t){Se(e,t.children,{morphStyle:"innerHTML"})}class Ee{#c;constructor({beforeNodeMorphed:e}={}){this.#c=e||(()=>!0)}beforeNodeAdded=e=>!(e.id&&e.hasAttribute("data-turbo-permanent")&&document.getElementById(e.id));beforeNodeMorphed=(e,t)=>{if(e instanceof Element){if(!e.hasAttribute("data-turbo-permanent")&&this.#c(e,t)){return!a("turbo:before-morph-element",{cancelable:!0,target:e,detail:{currentElement:e,newElement:t}}).defaultPrevented}return!1}};beforeAttributeUpdated=(e,t,s)=>!a("turbo:before-morph-attribute",{cancelable:!0,target:t,detail:{attributeName:e,mutationType:s}}).defaultPrevented;beforeNodeRemoved=e=>this.beforeNodeMorphed(e);afterNodeMorphed=(e,t)=>{e instanceof Element&&a("turbo:morph-element",{target:e,detail:{currentElement:e,newElement:t}})}}class ye extends be{static renderElement(e,t){a("turbo:before-frame-morph",{target:e,detail:{currentElement:e,newElement:t}}),we(e,t)}async preservingPermanentElements(e){return await e()}}class Re{static animationDuration=300;static get defaultCSS(){return m`
|
|
12
|
+
.turbo-progress-bar {
|
|
13
|
+
position: fixed;
|
|
14
|
+
display: block;
|
|
15
|
+
top: 0;
|
|
16
|
+
left: 0;
|
|
17
|
+
height: 3px;
|
|
18
|
+
background: #0076ff;
|
|
19
|
+
z-index: 2147483647;
|
|
20
|
+
transition:
|
|
21
|
+
width ${Re.animationDuration}ms ease-out,
|
|
22
|
+
opacity ${Re.animationDuration/2}ms ${Re.animationDuration/2}ms ease-in;
|
|
23
|
+
transform: translate3d(0, 0, 0);
|
|
24
|
+
}
|
|
25
|
+
`}hiding=!1;value=0;visible=!1;constructor(){this.stylesheetElement=this.createStylesheetElement(),this.progressElement=this.createProgressElement(),this.installStylesheetElement(),this.setValue(0)}show(){this.visible||(this.visible=!0,this.installProgressElement(),this.startTrickling())}hide(){this.visible&&!this.hiding&&(this.hiding=!0,this.fadeProgressElement((()=>{this.uninstallProgressElement(),this.stopTrickling(),this.visible=!1,this.hiding=!1})))}setValue(e){this.value=e,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(e){this.progressElement.style.opacity="0",setTimeout(e,1.5*Re.animationDuration)}uninstallProgressElement(){this.progressElement.parentNode&&document.documentElement.removeChild(this.progressElement)}startTrickling(){this.trickleInterval||(this.trickleInterval=window.setInterval(this.trickle,Re.animationDuration))}stopTrickling(){window.clearInterval(this.trickleInterval),delete this.trickleInterval}trickle=()=>{this.setValue(this.value+Math.random()/100)};refresh(){requestAnimationFrame((()=>{this.progressElement.style.width=10+90*this.value+"%"}))}createStylesheetElement(){const e=document.createElement("style");e.type="text/css",e.textContent=Re.defaultCSS;const t=R();return t&&(e.nonce=t),e}createProgressElement(){const e=document.createElement("div");return e.className="turbo-progress-bar",e}}class Le extends oe{detailsByOuterHTML=this.children.filter((e=>!function(e){const t=e.localName;return"noscript"==t}(e))).map((e=>function(e){e.hasAttribute("nonce")&&e.setAttribute("nonce","");return e}(e))).reduce(((e,t)=>{const{outerHTML:s}=t,r=s in e?e[s]:{type:Te(t),tracked:Ae(t),elements:[]};return{...e,[s]:{...r,elements:[...r.elements,t]}}}),{});get trackedElementSignature(){return Object.keys(this.detailsByOuterHTML).filter((e=>this.detailsByOuterHTML[e].tracked)).join("")}getScriptElementsNotInSnapshot(e){return this.getElementsMatchingTypeNotInSnapshot("script",e)}getStylesheetElementsNotInSnapshot(e){return this.getElementsMatchingTypeNotInSnapshot("stylesheet",e)}getElementsMatchingTypeNotInSnapshot(e,t){return Object.keys(this.detailsByOuterHTML).filter((e=>!(e in t.detailsByOuterHTML))).map((e=>this.detailsByOuterHTML[e])).filter((({type:t})=>t==e)).map((({elements:[e]})=>e))}get provisionalElements(){return Object.keys(this.detailsByOuterHTML).reduce(((e,t)=>{const{type:s,tracked:r,elements:i}=this.detailsByOuterHTML[t];return null!=s||r?i.length>1?[...e,...i.slice(1)]:e:[...e,...i]}),[])}getMetaValue(e){const t=this.findMetaElementByName(e);return t?t.getAttribute("content"):null}findMetaElementByName(e){return Object.keys(this.detailsByOuterHTML).reduce(((t,s)=>{const{elements:[r]}=this.detailsByOuterHTML[s];return function(e,t){const s=e.localName;return"meta"==s&&e.getAttribute("name")==t}(r,e)?r:t}),0)}}function Te(e){return function(e){const t=e.localName;return"script"==t}(e)?"script":function(e){const t=e.localName;return"style"==t||"link"==t&&"stylesheet"==e.getAttribute("rel")}(e)?"stylesheet":void 0}function Ae(e){return"reload"==e.getAttribute("data-turbo-track")}class Pe extends oe{static fromHTMLString(e=""){return this.fromDocument(u(e))}static fromElement(e){return this.fromDocument(e.ownerDocument)}static fromDocument({documentElement:e,body:t,head:s}){return new this(e,t,new Le(s))}constructor(e,t,s){super(t),this.documentElement=e,this.headSnapshot=s}clone(){const e=this.element.cloneNode(!0),t=this.element.querySelectorAll("select"),s=e.querySelectorAll("select");for(const[e,r]of t.entries()){const t=s[e];for(const e of t.selectedOptions)e.selected=!1;for(const e of r.selectedOptions)t.options[e.index].selected=!0}for(const t of e.querySelectorAll('input[type="password"]'))t.value="";return new Pe(this.documentElement,e,this.headSnapshot)}get lang(){return this.documentElement.getAttribute("lang")}get headElement(){return this.headSnapshot.element}get rootLocation(){return q(this.getSetting("root")??"/")}get cacheControlValue(){return this.getSetting("cache-control")}get isPreviewable(){return"no-preview"!=this.cacheControlValue}get isCacheable(){return"no-cache"!=this.cacheControlValue}get isVisitable(){return"reload"!=this.getSetting("visit-control")}get prefersViewTransitions(){return"same-origin"===this.headSnapshot.getMetaValue("view-transition")}get shouldMorphPage(){return"morph"===this.getSetting("refresh-method")}get shouldPreserveScrollPosition(){return"preserve"===this.getSetting("refresh-scroll")}getSetting(e){return this.headSnapshot.getMetaValue(`turbo-${e}`)}}class Ce{#h=!1;#d=Promise.resolve();renderChange(e,t){return e&&this.viewTransitionsAvailable&&!this.#h?(this.#h=!0,this.#d=this.#d.then((async()=>{await document.startViewTransition(t).finished}))):this.#d=this.#d.then(t),this.#d}get viewTransitionsAvailable(){return document.startViewTransition}}const ke={action:"advance",historyChanged:!1,visitCachedSnapshot:()=>{},willRender:!0,updateHistory:!0,shouldCacheSnapshot:!0,acceptsStreamResponse:!1},Me="visitStart",Fe="requestStart",Ie="requestEnd",qe="visitEnd",He="initialized",Be="started",Oe="canceled",Ne="failed",xe="completed",Ve=0,De=-1,We=-2,Ue={advance:"forward",restore:"back",replace:"none"};class je{identifier=p();timingMetrics={};followedRedirect=!1;historyChanged=!1;scrolled=!1;shouldCacheSnapshot=!0;acceptsStreamResponse=!1;snapshotCached=!1;state=He;viewTransitioner=new Ce;constructor(e,t,s,r={}){this.delegate=e,this.location=t,this.restorationIdentifier=s||p();const{action:i,historyChanged:n,referrer:o,snapshot:a,snapshotHTML:l,response:c,visitCachedSnapshot:h,willRender:d,updateHistory:u,shouldCacheSnapshot:m,acceptsStreamResponse:f,direction:g}={...ke,...r};this.action=i,this.historyChanged=n,this.referrer=o,this.snapshot=a,this.snapshotHTML=l,this.response=c,this.isSamePage=this.delegate.locationWithActionIsSamePage(this.location,this.action),this.isPageRefresh=this.view.isPageRefresh(this),this.visitCachedSnapshot=h,this.willRender=d,this.updateHistory=u,this.scrolled=!d,this.shouldCacheSnapshot=m,this.acceptsStreamResponse=f,this.direction=g||Ue[i]}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(){this.state==He&&(this.recordTimingMetric(Me),this.state=Be,this.adapter.visitStarted(this),this.delegate.visitStarted(this))}cancel(){this.state==Be&&(this.request&&this.request.cancel(),this.cancelRender(),this.state=Oe)}complete(){this.state==Be&&(this.recordTimingMetric(qe),this.adapter.visitCompleted(this),this.state=xe,this.followRedirect(),this.followedRedirect||this.delegate.visitCompleted(this))}fail(){this.state==Be&&(this.state=Ne,this.adapter.visitFailed(this),this.delegate.visitCompleted(this))}changeHistory(){if(!this.historyChanged&&this.updateHistory){const e=S(this.location.href===this.referrer?.href?"replace":this.action);this.history.update(e,this.location,this.restorationIdentifier),this.historyChanged=!0}}issueRequest(){this.hasPreloadedResponse()?this.simulateRequest():this.shouldIssueRequest()&&!this.request&&(this.request=new Y(this,X.get,this.location),this.request.perform())}simulateRequest(){this.response&&(this.startRequest(),this.recordResponse(),this.finishRequest())}startRequest(){this.recordTimingMetric(Fe),this.adapter.visitRequestStarted(this)}recordResponse(e=this.response){if(this.response=e,e){const{statusCode:t}=e;$e(t)?this.adapter.visitRequestCompleted(this):this.adapter.visitRequestFailedWithStatusCode(this,t)}}finishRequest(){this.recordTimingMetric(Ie),this.adapter.visitRequestFinished(this)}loadResponse(){if(this.response){const{statusCode:e,responseHTML:t}=this.response;this.render((async()=>{if(this.shouldCacheSnapshot&&this.cacheSnapshot(),this.view.renderPromise&&await this.view.renderPromise,$e(e)&&null!=t){const e=Pe.fromHTMLString(t);await this.renderPageSnapshot(e,!1),this.adapter.visitRendered(this),this.complete()}else await this.view.renderError(Pe.fromHTMLString(t),this),this.adapter.visitRendered(this),this.fail()}))}}getCachedSnapshot(){const e=this.view.getCachedSnapshotForLocation(this.location)||this.getPreloadedSnapshot();if(e&&(!H(this.location)||e.hasAnchor(H(this.location)))&&("restore"==this.action||e.isPreviewable))return e}getPreloadedSnapshot(){if(this.snapshotHTML)return Pe.fromHTMLString(this.snapshotHTML)}hasCachedSnapshot(){return null!=this.getCachedSnapshot()}loadCachedSnapshot(){const e=this.getCachedSnapshot();if(e){const t=this.shouldIssueRequest();this.render((async()=>{this.cacheSnapshot(),this.isSamePage||this.isPageRefresh?this.adapter.visitRendered(this):(this.view.renderPromise&&await this.view.renderPromise,await this.renderPageSnapshot(e,t),this.adapter.visitRendered(this),t||this.complete())}))}}followRedirect(){this.redirectedToLocation&&!this.followedRedirect&&this.response?.redirected&&(this.adapter.visitProposedToLocation(this.redirectedToLocation,{action:"replace",response:this.response,shouldCacheSnapshot:!1,willRender:!1}),this.followedRedirect=!0)}goToSamePageAnchor(){this.isSamePage&&this.render((async()=>{this.cacheSnapshot(),this.performScroll(),this.changeHistory(),this.adapter.visitRendered(this)}))}prepareRequest(e){this.acceptsStreamResponse&&e.acceptResponseType(se.contentType)}requestStarted(){this.startRequest()}requestPreventedHandlingResponse(e,t){}async requestSucceededWithResponse(e,t){const s=await t.responseHTML,{redirected:r,statusCode:i}=t;null==s?this.recordResponse({statusCode:We,redirected:r}):(this.redirectedToLocation=t.redirected?t.location:void 0,this.recordResponse({statusCode:i,responseHTML:s,redirected:r}))}async requestFailedWithResponse(e,t){const s=await t.responseHTML,{redirected:r,statusCode:i}=t;null==s?this.recordResponse({statusCode:We,redirected:r}):this.recordResponse({statusCode:i,responseHTML:s,redirected:r})}requestErrored(e,t){this.recordResponse({statusCode:Ve,redirected:!1})}requestFinished(){this.finishRequest()}performScroll(){this.scrolled||this.view.forceReloaded||this.view.shouldPreserveScrollPosition(this)||("restore"==this.action?this.scrollToRestoredPosition()||this.scrollToAnchor()||this.view.scrollToTop():this.scrollToAnchor()||this.view.scrollToTop(),this.isSamePage&&this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation,this.location),this.scrolled=!0)}scrollToRestoredPosition(){const{scrollPosition:e}=this.restorationData;if(e)return this.view.scrollToPosition(e),!0}scrollToAnchor(){const e=H(this.location);if(null!=e)return this.view.scrollToAnchor(e),!0}recordTimingMetric(e){this.timingMetrics[e]=(new Date).getTime()}getTimingMetrics(){return{...this.timingMetrics}}getHistoryMethodForAction(e){switch(e){case"replace":return history.replaceState;case"advance":case"restore":return history.pushState}}hasPreloadedResponse(){return"object"==typeof this.response}shouldIssueRequest(){return!this.isSamePage&&("restore"==this.action?!this.hasCachedSnapshot():this.willRender)}cacheSnapshot(){this.snapshotCached||(this.view.cacheSnapshot(this.snapshot).then((e=>e&&this.visitCachedSnapshot(e))),this.snapshotCached=!0)}async render(e){this.cancelRender(),await new Promise((e=>{this.frame="hidden"===document.visibilityState?setTimeout((()=>e()),0):requestAnimationFrame((()=>e()))})),await e(),delete this.frame}async renderPageSnapshot(e,t){await this.viewTransitioner.renderChange(this.view.shouldTransitionTo(e),(async()=>{await this.view.renderPage(e,t,this.willRender,this),this.performScroll()}))}cancelRender(){this.frame&&(cancelAnimationFrame(this.frame),delete this.frame)}}function $e(e){return e>=200&&e<300}class ze{progressBar=new Re;constructor(e){this.session=e}visitProposedToLocation(e,t){x(e,this.navigator.rootLocation)?this.navigator.startVisit(e,t?.restorationIdentifier||p(),t):window.location.href=e.toString()}visitStarted(e){this.location=e.location,e.loadCachedSnapshot(),e.issueRequest(),e.goToSamePageAnchor()}visitRequestStarted(e){this.progressBar.setValue(0),e.hasCachedSnapshot()||"restore"!=e.action?this.showVisitProgressBarAfterDelay():this.showProgressBar()}visitRequestCompleted(e){e.loadResponse()}visitRequestFailedWithStatusCode(e,t){switch(t){case Ve:case De:case We:return this.reload({reason:"request_failed",context:{statusCode:t}});default:return e.loadResponse()}}visitRequestFinished(e){}visitCompleted(e){this.progressBar.setValue(1),this.hideVisitProgressBar()}pageInvalidated(e){this.reload(e)}visitFailed(e){this.progressBar.setValue(1),this.hideVisitProgressBar()}visitRendered(e){}formSubmissionStarted(e){this.progressBar.setValue(0),this.showFormProgressBarAfterDelay()}formSubmissionFinished(e){this.progressBar.setValue(1),this.hideFormProgressBar()}showVisitProgressBarAfterDelay(){this.visitProgressBarTimeout=window.setTimeout(this.showProgressBar,this.session.progressBarDelay)}hideVisitProgressBar(){this.progressBar.hide(),null!=this.visitProgressBarTimeout&&(window.clearTimeout(this.visitProgressBarTimeout),delete this.visitProgressBarTimeout)}showFormProgressBarAfterDelay(){null==this.formProgressBarTimeout&&(this.formProgressBarTimeout=window.setTimeout(this.showProgressBar,this.session.progressBarDelay))}hideFormProgressBar(){this.progressBar.hide(),null!=this.formProgressBarTimeout&&(window.clearTimeout(this.formProgressBarTimeout),delete this.formProgressBarTimeout)}showProgressBar=()=>{this.progressBar.show()};reload(e){a("turbo:reload",{detail:e}),window.location.href=this.location?.toString()||window.location.href}get navigator(){return this.session.navigator}}class _e{selector="[data-turbo-temporary]";deprecatedSelector="[data-turbo-cache=false]";started=!1;start(){this.started||(this.started=!0,addEventListener("turbo:before-cache",this.removeTemporaryElements,!1))}stop(){this.started&&(this.started=!1,removeEventListener("turbo:before-cache",this.removeTemporaryElements,!1))}removeTemporaryElements=e=>{for(const e of this.temporaryElements)e.remove()};get temporaryElements(){return[...document.querySelectorAll(this.selector),...this.temporaryElementsWithDeprecation]}get temporaryElementsWithDeprecation(){const e=document.querySelectorAll(this.deprecatedSelector);return e.length&&console.warn(`The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.`),[...e]}}class Xe{constructor(e,t){this.session=e,this.element=t,this.linkInterceptor=new ue(this,t),this.formSubmitObserver=new ce(this,t)}start(){this.linkInterceptor.start(),this.formSubmitObserver.start()}stop(){this.linkInterceptor.stop(),this.formSubmitObserver.stop()}shouldInterceptLinkClick(e,t,s){return this.#u(e)}linkClickIntercepted(e,t,s){const r=this.#m(e);r&&r.delegate.linkClickIntercepted(e,t,s)}willSubmitForm(e,t){return null==e.closest("turbo-frame")&&this.#p(e,t)&&this.#u(e,t)}formSubmitted(e,t){const s=this.#m(e,t);s&&s.delegate.formSubmitted(e,t)}#p(e,t){const s=B(e,t),r=this.element.ownerDocument.querySelector('meta[name="turbo-root"]'),i=q(r?.content??"/");return this.#u(e,t)&&x(s,i)}#u(e,t){if(e instanceof HTMLFormElement?this.session.submissionIsNavigatable(e,t):this.session.elementIsNavigatable(e)){const s=this.#m(e,t);return!!s&&s!=e.closest("turbo-frame")}return!1}#m(e,t){const s=t?.getAttribute("data-turbo-frame")||e.getAttribute("data-turbo-frame");if(s&&"_top"!=s){const e=this.element.querySelector(`#${s}:not([disabled])`);if(e instanceof i)return e}}}class Ke{location;restorationIdentifier=p();restorationData={};started=!1;pageLoaded=!1;currentIndex=0;constructor(e){this.delegate=e}start(){this.started||(addEventListener("popstate",this.onPopState,!1),addEventListener("load",this.onPageLoad,!1),this.currentIndex=history.state?.turbo?.restorationIndex||0,this.started=!0,this.replace(new URL(window.location.href)))}stop(){this.started&&(removeEventListener("popstate",this.onPopState,!1),removeEventListener("load",this.onPageLoad,!1),this.started=!1)}push(e,t){this.update(history.pushState,e,t)}replace(e,t){this.update(history.replaceState,e,t)}update(e,t,s=p()){e===history.pushState&&++this.currentIndex;const r={turbo:{restorationIdentifier:s,restorationIndex:this.currentIndex}};e.call(history,r,"",t.href),this.location=t,this.restorationIdentifier=s}getRestorationDataForIdentifier(e){return this.restorationData[e]||{}}updateRestorationData(e){const{restorationIdentifier:t}=this,s=this.restorationData[t];this.restorationData[t]={...s,...e}}assumeControlOfScrollRestoration(){this.previousScrollRestoration||(this.previousScrollRestoration=history.scrollRestoration??"auto",history.scrollRestoration="manual")}relinquishControlOfScrollRestoration(){this.previousScrollRestoration&&(history.scrollRestoration=this.previousScrollRestoration,delete this.previousScrollRestoration)}onPopState=e=>{if(this.shouldHandlePopState()){const{turbo:t}=e.state||{};if(t){this.location=new URL(window.location.href);const{restorationIdentifier:e,restorationIndex:s}=t;this.restorationIdentifier=e;const r=s>this.currentIndex?"forward":"back";this.delegate.historyPoppedToLocationWithRestorationIdentifierAndDirection(this.location,e,r),this.currentIndex=s}}};onPageLoad=async e=>{await Promise.resolve(),this.pageLoaded=!0};shouldHandlePopState(){return this.pageIsLoaded()}pageIsLoaded(){return this.pageLoaded||"complete"==document.readyState}}class Qe{started=!1;#f=null;constructor(e,t){this.delegate=e,this.eventTarget=t}start(){this.started||("loading"===this.eventTarget.readyState?this.eventTarget.addEventListener("DOMContentLoaded",this.#g,{once:!0}):this.#g())}stop(){this.started&&(this.eventTarget.removeEventListener("mouseenter",this.#b,{capture:!0,passive:!0}),this.eventTarget.removeEventListener("mouseleave",this.#v,{capture:!0,passive:!0}),this.eventTarget.removeEventListener("turbo:before-fetch-request",this.#S,!0),this.started=!1)}#g=()=>{this.eventTarget.addEventListener("mouseenter",this.#b,{capture:!0,passive:!0}),this.eventTarget.addEventListener("mouseleave",this.#v,{capture:!0,passive:!0}),this.eventTarget.addEventListener("turbo:before-fetch-request",this.#S,!0),this.started=!0};#b=e=>{if("false"===y("turbo-prefetch"))return;const t=e.target;if(t.matches&&t.matches("a[href]:not([target^=_]):not([download])")&&this.#w(t)){const e=t,s=k(e);if(this.delegate.canPrefetchRequestToLocation(e,s)){this.#f=e;const r=new Y(this,X.get,s,new URLSearchParams,t);re.setLater(s.toString(),r,this.#E)}}};#v=e=>{e.target===this.#f&&this.#y()};#y=()=>{re.clear(),this.#f=null};#S=e=>{if("FORM"!==e.target.tagName&&"GET"===e.detail.fetchOptions.method){const t=re.get(e.detail.url.toString());t&&(e.detail.fetchRequest=t),re.clear()}};prepareRequest(e){const t=e.target;e.headers["X-Sec-Purpose"]="prefetch";const s=t.closest("turbo-frame"),r=t.getAttribute("data-turbo-frame")||s?.getAttribute("target")||s?.id;r&&"_top"!==r&&(e.headers["Turbo-Frame"]=r)}requestSucceededWithResponse(){}requestStarted(e){}requestErrored(e){}requestFinished(e){}requestPreventedHandlingResponse(e,t){}requestFailedWithResponse(e,t){}get#E(){return Number(y("turbo-prefetch-cache-time"))||1e4}#w(e){return!!e.getAttribute("href")&&(!Ye(e)&&(!Ge(e)&&(!Je(e)&&(!Ze(e)&&!tt(e)))))}}const Ye=e=>e.origin!==document.location.origin||!["http:","https:"].includes(e.protocol)||e.hasAttribute("target"),Ge=e=>e.pathname+e.search===document.location.pathname+document.location.search||e.href.startsWith("#"),Je=e=>{if("false"===e.getAttribute("data-turbo-prefetch"))return!0;if("false"===e.getAttribute("data-turbo"))return!0;const t=L(e,"[data-turbo-prefetch]");return!(!t||"false"!==t.getAttribute("data-turbo-prefetch"))},Ze=e=>{const t=e.getAttribute("data-turbo-method");return!(!t||"get"===t.toLowerCase())||(!!et(e)||(!!e.hasAttribute("data-turbo-confirm")||!!e.hasAttribute("data-turbo-stream")))},et=e=>e.hasAttribute("data-remote")||e.hasAttribute("data-behavior")||e.hasAttribute("data-confirm")||e.hasAttribute("data-method"),tt=e=>a("turbo:before-prefetch",{target:e,cancelable:!0}).defaultPrevented;class st{constructor(e){this.delegate=e}proposeVisit(e,t={}){this.delegate.allowsVisitingLocationWithAction(e,t.action)&&this.delegate.visitProposedToLocation(e,t)}startVisit(e,t,s={}){this.stop(),this.currentVisit=new je(this,q(e),t,{referrer:this.location,...s}),this.currentVisit.start()}submitForm(e,t){this.stop(),this.formSubmission=new ne(this,e,t,!0),this.formSubmission.start()}stop(){this.formSubmission&&(this.formSubmission.stop(),delete this.formSubmission),this.currentVisit&&(this.currentVisit.cancel(),delete this.currentVisit)}get adapter(){return this.delegate.adapter}get view(){return this.delegate.view}get rootLocation(){return this.view.snapshot.rootLocation}get history(){return this.delegate.history}formSubmissionStarted(e){"function"==typeof this.adapter.formSubmissionStarted&&this.adapter.formSubmissionStarted(e)}async formSubmissionSucceededWithResponse(e,t){if(e==this.formSubmission){const s=await t.responseHTML;if(s){const r=e.isSafe;r||this.view.clearSnapshotCache();const{statusCode:i,redirected:n}=t,o={action:this.#R(e,t),shouldCacheSnapshot:r,response:{statusCode:i,responseHTML:s,redirected:n}};this.proposeVisit(t.location,o)}}}async formSubmissionFailedWithResponse(e,t){const s=await t.responseHTML;if(s){const e=Pe.fromHTMLString(s);t.serverError?await this.view.renderError(e,this.currentVisit):await this.view.renderPage(e,!1,!0,this.currentVisit),e.shouldPreserveScrollPosition||this.view.scrollToTop(),this.view.clearSnapshotCache()}}formSubmissionErrored(e,t){console.error(t)}formSubmissionFinished(e){"function"==typeof this.adapter.formSubmissionFinished&&this.adapter.formSubmissionFinished(e)}visitStarted(e){this.delegate.visitStarted(e)}visitCompleted(e){this.delegate.visitCompleted(e),delete this.currentVisit}locationWithActionIsSamePage(e,t){const s=H(e),r=H(this.view.lastRenderedLocation),i="restore"===t&&void 0===s;return"replace"!==t&&V(e)===V(this.view.lastRenderedLocation)&&(i||null!=s&&s!==r)}visitScrolledToSamePageLocation(e,t){this.delegate.visitScrolledToSamePageLocation(e,t)}get location(){return this.history.location}get restorationIdentifier(){return this.history.restorationIdentifier}#R(e,t){const{submitter:s,formElement:r}=e;return w(s,r)||this.#L(t)}#L(e){return e.redirected&&e.location.href===this.location?.href?"replace":"advance"}}const rt=0,it=1,nt=2,ot=3;class at{stage=rt;started=!1;constructor(e){this.delegate=e}start(){this.started||(this.stage==rt&&(this.stage=it),document.addEventListener("readystatechange",this.interpretReadyState,!1),addEventListener("pagehide",this.pageWillUnload,!1),this.started=!0)}stop(){this.started&&(document.removeEventListener("readystatechange",this.interpretReadyState,!1),removeEventListener("pagehide",this.pageWillUnload,!1),this.started=!1)}interpretReadyState=()=>{const{readyState:e}=this;"interactive"==e?this.pageIsInteractive():"complete"==e&&this.pageIsComplete()};pageIsInteractive(){this.stage==it&&(this.stage=nt,this.delegate.pageBecameInteractive())}pageIsComplete(){this.pageIsInteractive(),this.stage==nt&&(this.stage=ot,this.delegate.pageLoaded())}pageWillUnload=()=>{this.delegate.pageWillUnload()};get readyState(){return document.readyState}}class lt{started=!1;constructor(e){this.delegate=e}start(){this.started||(addEventListener("scroll",this.onScroll,!1),this.onScroll(),this.started=!0)}stop(){this.started&&(removeEventListener("scroll",this.onScroll,!1),this.started=!1)}onScroll=()=>{this.updatePosition({x:window.pageXOffset,y:window.pageYOffset})};updatePosition(e){this.delegate.scrollPositionChanged(e)}}class ct{render({fragment:e}){fe.preservingPermanentElements(this,function(e){const t=le(document.documentElement),s={};for(const r of t){const{id:t}=r;for(const i of e.querySelectorAll("turbo-stream")){const e=ae(i.templateElement.content,t);e&&(s[t]=[r,e])}}return s}(e),(()=>{!async function(e,t){const s=`turbo-stream-autofocus-${p()}`,r=e.querySelectorAll("turbo-stream"),i=function(e){for(const t of e){const e=A(t.templateElement.content);if(e)return e}return null}(r);let n=null;i&&(n=i.id?i.id:s,i.id=n);t(),await c();if((null==document.activeElement||document.activeElement==document.body)&&n){const e=document.getElementById(n);T(e)&&e.focus(),e&&e.id==s&&e.removeAttribute("id")}}(e,(()=>{!async function(e){const[t,s]=await async function(e,t){const s=t();return e(),await h(),[s,t()]}(e,(()=>document.activeElement)),r=t&&t.id;if(r){const e=document.getElementById(r);T(e)&&e!=s&&e.focus()}}((()=>{document.documentElement.appendChild(e)}))}))}))}enteringBardo(e,t){t.replaceWith(e.cloneNode(!0))}leavingBardo(){}}class ht{sources=new Set;#T=!1;constructor(e){this.delegate=e}start(){this.#T||(this.#T=!0,addEventListener("turbo:before-fetch-response",this.inspectFetchResponse,!1))}stop(){this.#T&&(this.#T=!1,removeEventListener("turbo:before-fetch-response",this.inspectFetchResponse,!1))}connectStreamSource(e){this.streamSourceIsConnected(e)||(this.sources.add(e),e.addEventListener("message",this.receiveMessageEvent,!1))}disconnectStreamSource(e){this.streamSourceIsConnected(e)&&(this.sources.delete(e),e.removeEventListener("message",this.receiveMessageEvent,!1))}streamSourceIsConnected(e){return this.sources.has(e)}inspectFetchResponse=e=>{const t=function(e){const t=e.detail?.fetchResponse;if(t instanceof W)return t}(e);t&&function(e){const t=e.contentType??"";return t.startsWith(se.contentType)}(t)&&(e.preventDefault(),this.receiveMessageResponse(t))};receiveMessageEvent=e=>{this.#T&&"string"==typeof e.data&&this.receiveMessageHTML(e.data)};async receiveMessageResponse(e){const t=await e.responseHTML;t&&this.receiveMessageHTML(t)}receiveMessageHTML(e){this.delegate.receivedMessageFromStream(se.wrap(e))}}class dt extends ge{static renderElement(e,t){const{documentElement:s,body:r}=document;s.replaceChild(t,r)}async render(){this.replaceHeadAndBody(),this.activateScriptElements()}replaceHeadAndBody(){const{documentElement:e,head:t}=document;e.replaceChild(this.newHead,t),this.renderElement(this.currentElement,this.newElement)}activateScriptElements(){for(const e of this.scriptElements){const t=e.parentNode;if(t){const s=o(e);t.replaceChild(s,e)}}}get newHead(){return this.newSnapshot.headSnapshot.element}get scriptElements(){return document.documentElement.querySelectorAll("script")}}class ut extends ge{static renderElement(e,t){document.body&&t instanceof HTMLBodyElement?document.body.replaceWith(t):document.documentElement.appendChild(t)}get shouldRender(){return this.newSnapshot.isVisitable&&this.trackedElementsAreIdentical}get reloadReason(){return this.newSnapshot.isVisitable?this.trackedElementsAreIdentical?void 0:{reason:"tracked_element_mismatch"}:{reason:"turbo_visit_control_is_reload"}}async prepareToRender(){this.#A(),await this.mergeHead()}async render(){this.willRender&&await this.replaceBody()}finishRendering(){super.finishRendering(),this.isPreview||this.focusFirstAutofocusableElement()}get currentHeadSnapshot(){return this.currentSnapshot.headSnapshot}get newHeadSnapshot(){return this.newSnapshot.headSnapshot}get newElement(){return this.newSnapshot.element}#A(){const{documentElement:e}=this.currentSnapshot,{lang:t}=this.newSnapshot;t?e.setAttribute("lang",t):e.removeAttribute("lang")}async mergeHead(){const e=this.mergeProvisionalElements(),t=this.copyNewHeadStylesheetElements();this.copyNewHeadScriptElements(),await e,await t,this.willRender&&this.removeUnusedDynamicStylesheetElements()}async replaceBody(){await this.preservingPermanentElements((async()=>{this.activateNewBody(),await this.assignNewBody()}))}get trackedElementsAreIdentical(){return this.currentHeadSnapshot.trackedElementSignature==this.newHeadSnapshot.trackedElementSignature}async copyNewHeadStylesheetElements(){const e=[];for(const t of this.newHeadStylesheetElements)e.push(v(t)),document.head.appendChild(t);await Promise.all(e)}copyNewHeadScriptElements(){for(const e of this.newHeadScriptElements)document.head.appendChild(o(e))}removeUnusedDynamicStylesheetElements(){for(const e of this.unusedDynamicStylesheetElements)document.head.removeChild(e)}async mergeProvisionalElements(){const e=[...this.newHeadProvisionalElements];for(const t of this.currentHeadProvisionalElements)this.isCurrentElementInElementList(t,e)||document.head.removeChild(t);for(const t of e)document.head.appendChild(t)}isCurrentElementInElementList(e,t){for(const[s,r]of t.entries()){if("TITLE"==e.tagName){if("TITLE"!=r.tagName)continue;if(e.innerHTML==r.innerHTML)return t.splice(s,1),!0}if(r.isEqualNode(e))return t.splice(s,1),!0}return!1}removeCurrentHeadProvisionalElements(){for(const e of this.currentHeadProvisionalElements)document.head.removeChild(e)}copyNewHeadProvisionalElements(){for(const e of this.newHeadProvisionalElements)document.head.appendChild(e)}activateNewBody(){document.adoptNode(this.newElement),this.activateNewBodyScriptElements()}activateNewBodyScriptElements(){for(const e of this.newBodyScriptElements){const t=o(e);e.replaceWith(t)}}async assignNewBody(){await this.renderElement(this.currentElement,this.newElement)}get unusedDynamicStylesheetElements(){return this.oldHeadStylesheetElements.filter((e=>"dynamic"===e.getAttribute("data-turbo-track")))}get oldHeadStylesheetElements(){return this.currentHeadSnapshot.getStylesheetElementsNotInSnapshot(this.newHeadSnapshot)}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 mt extends ut{static renderElement(e,t){Se(e,t,{callbacks:{beforeNodeMorphed:e=>!pt(e)}});for(const t of e.querySelectorAll("turbo-frame"))pt(t)&&t.reload();a("turbo:morph",{detail:{currentElement:e,newElement:t}})}async preservingPermanentElements(e){return await e()}get renderMethod(){return"morph"}get shouldAutofocus(){return!1}}function pt(e){return e instanceof i&&e.src&&"morph"===e.refresh&&!e.closest("[data-turbo-permanent]")}class ft{keys=[];snapshots={};constructor(e){this.size=e}has(e){return D(e)in this.snapshots}get(e){if(this.has(e)){const t=this.read(e);return this.touch(e),t}}put(e,t){return this.write(e,t),this.touch(e),t}clear(){this.snapshots={}}read(e){return this.snapshots[D(e)]}write(e,t){this.snapshots[D(e)]=t}touch(e){const t=D(e),s=this.keys.indexOf(t);s>-1&&this.keys.splice(s,1),this.keys.unshift(t),this.trim()}trim(){for(const e of this.keys.splice(this.size))delete this.snapshots[e]}}class gt extends he{snapshotCache=new ft(10);lastRenderedLocation=new URL(location.href);forceReloaded=!1;shouldTransitionTo(e){return this.snapshot.prefersViewTransitions&&e.prefersViewTransitions}renderPage(e,t=!1,s=!0,r){const i=new(this.isPageRefresh(r)&&this.snapshot.shouldMorphPage?mt:ut)(this.snapshot,e,t,s);return i.shouldRender?r?.changeHistory():this.forceReloaded=!0,this.render(i)}renderError(e,t){t?.changeHistory();const s=new dt(this.snapshot,e,!1);return this.render(s)}clearSnapshotCache(){this.snapshotCache.clear()}async cacheSnapshot(e=this.snapshot){if(e.isCacheable){this.delegate.viewWillCacheSnapshot();const{lastRenderedLocation:t}=this;await d();const s=e.clone();return this.snapshotCache.put(t,s),s}}getCachedSnapshotForLocation(e){return this.snapshotCache.get(e)}isPageRefresh(e){return!e||this.lastRenderedLocation.pathname===e.location.pathname&&"replace"===e.action}shouldPreserveScrollPosition(e){return this.isPageRefresh(e)&&this.snapshot.shouldPreserveScrollPosition}get snapshot(){return Pe.fromElement(this.element)}}class bt{selector="a[data-turbo-preload]";constructor(e,t){this.delegate=e,this.snapshotCache=t}start(){"loading"===document.readyState?document.addEventListener("DOMContentLoaded",this.#P):this.preloadOnLoadLinksForView(document.body)}stop(){document.removeEventListener("DOMContentLoaded",this.#P)}preloadOnLoadLinksForView(e){for(const t of e.querySelectorAll(this.selector))this.delegate.shouldPreloadLink(t)&&this.preloadURL(t)}async preloadURL(e){const t=new URL(e.href);if(this.snapshotCache.has(t))return;const s=new Y(this,X.get,t,new URLSearchParams,e);await s.perform()}prepareRequest(e){e.headers["X-Sec-Purpose"]="prefetch"}async requestSucceededWithResponse(e,t){try{const s=await t.responseHTML,r=Pe.fromHTMLString(s);this.snapshotCache.put(e.url,r)}catch(e){}}requestStarted(e){}requestErrored(e){}requestFinished(e){}requestPreventedHandlingResponse(e,t){}requestFailedWithResponse(e,t){}#P=()=>{this.preloadOnLoadLinksForView(document.body)}}class vt{constructor(e){this.session=e}clear(){this.session.clearCache()}resetCacheControl(){this.#C("")}exemptPageFromCache(){this.#C("no-cache")}exemptPageFromPreview(){this.#C("no-preview")}#C(e){!function(e,t){let s=E(e);s||(s=document.createElement("meta"),s.setAttribute("name",e),document.head.appendChild(s)),s.setAttribute("content",t)}("turbo-cache-control",e)}}function St(e){Object.defineProperties(e,wt)}const wt={absoluteURL:{get(){return this.toString()}}},Et=new class{navigator=new st(this);history=new Ke(this);view=new gt(this,document.documentElement);adapter=new ze(this);pageObserver=new at(this);cacheObserver=new _e;linkPrefetchObserver=new Qe(this,document);linkClickObserver=new me(this,window);formSubmitObserver=new ce(this,document);scrollObserver=new lt(this);streamObserver=new ht(this);formLinkClickObserver=new pe(this,document.documentElement);frameRedirector=new Xe(this,document.documentElement);streamMessageRenderer=new ct;cache=new vt(this);enabled=!0;started=!1;#k=150;constructor(e){this.recentRequests=e,this.preloader=new bt(this,this.view.snapshotCache),this.debouncedRefresh=this.refresh,this.pageRefreshDebouncePeriod=this.pageRefreshDebouncePeriod}start(){this.started||(this.pageObserver.start(),this.cacheObserver.start(),this.linkPrefetchObserver.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=!0,this.enabled=!0)}disable(){this.enabled=!1}stop(){this.started&&(this.pageObserver.stop(),this.cacheObserver.stop(),this.linkPrefetchObserver.stop(),this.formLinkClickObserver.stop(),this.linkClickObserver.stop(),this.formSubmitObserver.stop(),this.scrollObserver.stop(),this.streamObserver.stop(),this.frameRedirector.stop(),this.history.stop(),this.preloader.stop(),this.started=!1)}registerAdapter(e){this.adapter=e}visit(e,t={}){const s=t.frame?document.getElementById(t.frame):null;if(s instanceof i){const r=t.action||w(s);s.delegate.proposeVisitIfNavigatedWithAction(s,r),s.src=e.toString()}else this.navigator.proposeVisit(q(e),t)}refresh(e,t){t&&this.recentRequests.has(t)||this.navigator.currentVisit||this.visit(e,{action:"replace",shouldCacheSnapshot:!1})}connectStreamSource(e){this.streamObserver.connectStreamSource(e)}disconnectStreamSource(e){this.streamObserver.disconnectStreamSource(e)}renderStreamMessage(e){this.streamMessageRenderer.render(se.wrap(e))}clearCache(){this.view.clearSnapshotCache()}setProgressBarDelay(e){console.warn("Please replace `session.setProgressBarDelay(delay)` with `session.progressBarDelay = delay`. The function is deprecated and will be removed in a future version of Turbo.`"),this.progressBarDelay=e}set progressBarDelay(e){I.drive.progressBarDelay=e}get progressBarDelay(){return I.drive.progressBarDelay}set drive(e){I.drive.enabled=e}get drive(){return I.drive.enabled}set formMode(e){I.forms.mode=e}get formMode(){return I.forms.mode}get location(){return this.history.location}get restorationIdentifier(){return this.history.restorationIdentifier}get pageRefreshDebouncePeriod(){return this.#k}set pageRefreshDebouncePeriod(e){this.refresh=function(e,t){let s=null;return(...r)=>{clearTimeout(s),s=setTimeout((()=>e.apply(this,r)),t)}}(this.debouncedRefresh.bind(this),e),this.#k=e}shouldPreloadLink(e){const t=e.hasAttribute("data-turbo-method"),s=e.hasAttribute("data-turbo-stream"),r=e.getAttribute("data-turbo-frame"),n="_top"==r?null:document.getElementById(r)||L(e,"turbo-frame:not([disabled])");if(t||s||n instanceof i)return!1;{const t=new URL(e.href);return this.elementIsNavigatable(e)&&x(t,this.snapshot.rootLocation)}}historyPoppedToLocationWithRestorationIdentifierAndDirection(e,t,s){this.enabled?this.navigator.startVisit(e,t,{action:"restore",historyChanged:!0,direction:s}):this.adapter.pageInvalidated({reason:"turbo_disabled"})}scrollPositionChanged(e){this.history.updateRestorationData({scrollPosition:e})}willSubmitFormLinkToLocation(e,t){return this.elementIsNavigatable(e)&&x(t,this.snapshot.rootLocation)}submittedFormLinkToLocation(){}canPrefetchRequestToLocation(e,t){return this.elementIsNavigatable(e)&&x(t,this.snapshot.rootLocation)}willFollowLinkToLocation(e,t,s){return this.elementIsNavigatable(e)&&x(t,this.snapshot.rootLocation)&&this.applicationAllowsFollowingLinkToLocation(e,t,s)}followedLinkToLocation(e,t){const s=this.getActionForLink(e),r=e.hasAttribute("data-turbo-stream");this.visit(t.href,{action:s,acceptsStreamResponse:r})}allowsVisitingLocationWithAction(e,t){return this.locationWithActionIsSamePage(e,t)||this.applicationAllowsVisitingLocation(e)}visitProposedToLocation(e,t){St(e),this.adapter.visitProposedToLocation(e,t)}visitStarted(e){e.acceptsStreamResponse||(g(document.documentElement),this.view.markVisitDirection(e.direction)),St(e.location),e.silent||this.notifyApplicationAfterVisitingLocation(e.location,e.action)}visitCompleted(e){this.view.unmarkVisitDirection(),b(document.documentElement),this.notifyApplicationAfterPageLoad(e.getTimingMetrics())}locationWithActionIsSamePage(e,t){return this.navigator.locationWithActionIsSamePage(e,t)}visitScrolledToSamePageLocation(e,t){this.notifyApplicationAfterVisitingSamePageLocation(e,t)}willSubmitForm(e,t){const s=B(e,t);return this.submissionIsNavigatable(e,t)&&x(q(s),this.snapshot.rootLocation)}formSubmitted(e,t){this.navigator.submitForm(e,t)}pageBecameInteractive(){this.view.lastRenderedLocation=this.location,this.notifyApplicationAfterPageLoad()}pageLoaded(){this.history.assumeControlOfScrollRestoration()}pageWillUnload(){this.history.relinquishControlOfScrollRestoration()}receivedMessageFromStream(e){this.renderStreamMessage(e)}viewWillCacheSnapshot(){this.navigator.currentVisit?.silent||this.notifyApplicationBeforeCachingSnapshot()}allowsImmediateRender({element:e},t){const s=this.notifyApplicationBeforeRender(e,t),{defaultPrevented:r,detail:{render:i}}=s;return this.view.renderer&&i&&(this.view.renderer.renderElement=i),!r}viewRenderedSnapshot(e,t,s){this.view.lastRenderedLocation=this.history.location,this.notifyApplicationAfterRender(s)}preloadOnLoadLinksForView(e){this.preloader.preloadOnLoadLinksForView(e)}viewInvalidated(e){this.adapter.pageInvalidated(e)}frameLoaded(e){this.notifyApplicationAfterFrameLoad(e)}frameRendered(e,t){this.notifyApplicationAfterFrameRender(e,t)}applicationAllowsFollowingLinkToLocation(e,t,s){return!this.notifyApplicationAfterClickingLinkToLocation(e,t,s).defaultPrevented}applicationAllowsVisitingLocation(e){return!this.notifyApplicationBeforeVisitingLocation(e).defaultPrevented}notifyApplicationAfterClickingLinkToLocation(e,t,s){return a("turbo:click",{target:e,detail:{url:t.href,originalEvent:s},cancelable:!0})}notifyApplicationBeforeVisitingLocation(e){return a("turbo:before-visit",{detail:{url:e.href},cancelable:!0})}notifyApplicationAfterVisitingLocation(e,t){return a("turbo:visit",{detail:{url:e.href,action:t}})}notifyApplicationBeforeCachingSnapshot(){return a("turbo:before-cache")}notifyApplicationBeforeRender(e,t){return a("turbo:before-render",{detail:{newBody:e,...t},cancelable:!0})}notifyApplicationAfterRender(e){return a("turbo:render",{detail:{renderMethod:e}})}notifyApplicationAfterPageLoad(e={}){return a("turbo:load",{detail:{url:this.location.href,timing:e}})}notifyApplicationAfterVisitingSamePageLocation(e,t){dispatchEvent(new HashChangeEvent("hashchange",{oldURL:e.toString(),newURL:t.toString()}))}notifyApplicationAfterFrameLoad(e){return a("turbo:frame-load",{target:e})}notifyApplicationAfterFrameRender(e,t){return a("turbo:frame-render",{detail:{fetchResponse:e},target:t,cancelable:!0})}submissionIsNavigatable(e,t){if("off"==I.forms.mode)return!1;{const s=!t||this.elementIsNavigatable(t);return"optin"==I.forms.mode?s&&null!=e.closest('[data-turbo="true"]'):s&&this.elementIsNavigatable(e)}}elementIsNavigatable(e){const t=L(e,"[data-turbo]"),s=L(e,"turbo-frame");return I.drive.enabled||s?!t||"false"!=t.getAttribute("data-turbo"):!!t&&"true"==t.getAttribute("data-turbo")}getActionForLink(e){return w(e)||"advance"}get snapshot(){return this.view.snapshot}}(j),{cache:yt,navigator:Rt}=Et;function Lt(){Et.start()}function Tt(e){Et.registerAdapter(e)}function At(e,t){Et.visit(e,t)}function Pt(e){Et.connectStreamSource(e)}function Ct(e){Et.disconnectStreamSource(e)}function kt(e){Et.renderStreamMessage(e)}function Mt(){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.`"),Et.clearCache()}function Ft(e){console.warn("Please replace `Turbo.setProgressBarDelay(delay)` with `Turbo.config.drive.progressBarDelay = delay`. The top-level function is deprecated and will be removed in a future version of Turbo.`"),I.drive.progressBarDelay=e}function It(e){console.warn("Please replace `Turbo.setConfirmMethod(confirmMethod)` with `Turbo.config.forms.confirm = confirmMethod`. The top-level function is deprecated and will be removed in a future version of Turbo.`"),I.forms.confirm=e}function qt(e){console.warn("Please replace `Turbo.setFormMode(mode)` with `Turbo.config.forms.mode = mode`. The top-level function is deprecated and will be removed in a future version of Turbo.`"),I.forms.mode=e}var Ht=Object.freeze({__proto__:null,navigator:Rt,session:Et,cache:yt,PageRenderer:ut,PageSnapshot:Pe,FrameRenderer:be,fetch:z,config:I,start:Lt,registerAdapter:Tt,visit:At,connectStreamSource:Pt,disconnectStreamSource:Ct,renderStreamMessage:kt,clearCache:Mt,setProgressBarDelay:Ft,setConfirmMethod:It,setFormMode:qt});class Bt extends Error{}function Ot(e){if(null!=e){const t=document.getElementById(e);if(t instanceof i)return t}}function Nt(e,t){if(e){const r=e.getAttribute("src");if(null!=r&&null!=t&&(s=t,q(r).href==q(s).href))throw new Error(`Matching <turbo-frame id="${e.id}"> element has a source URL which references itself`);if(e.ownerDocument!==document&&(e=document.importNode(e,!0)),e instanceof i)return e.connectedCallback(),e.disconnectedCallback(),e}var s}const xt={after(){this.targetElements.forEach((e=>e.parentElement?.insertBefore(this.templateContent,e.nextSibling)))},append(){this.removeDuplicateTargetChildren(),this.targetElements.forEach((e=>e.append(this.templateContent)))},before(){this.targetElements.forEach((e=>e.parentElement?.insertBefore(this.templateContent,e)))},prepend(){this.removeDuplicateTargetChildren(),this.targetElements.forEach((e=>e.prepend(this.templateContent)))},remove(){this.targetElements.forEach((e=>e.remove()))},replace(){const e=this.getAttribute("method");this.targetElements.forEach((t=>{"morph"===e?Se(t,this.templateContent):t.replaceWith(this.templateContent)}))},update(){const e=this.getAttribute("method");this.targetElements.forEach((t=>{"morph"===e?we(t,this.templateContent):(t.innerHTML="",t.append(this.templateContent))}))},refresh(){Et.refresh(this.baseURI,this.requestId)}};class Vt extends HTMLElement{static async renderElement(e){await e.performAction()}async connectedCallback(){try{await this.render()}catch(e){console.error(e)}finally{this.disconnect()}}async render(){return this.renderPromise??=(async()=>{const e=this.beforeRenderEvent;this.dispatchEvent(e)&&(await c(),await e.detail.render(this))})()}disconnect(){try{this.remove()}catch{}}removeDuplicateTargetChildren(){this.duplicateChildren.forEach((e=>e.remove()))}get duplicateChildren(){const e=this.targetElements.flatMap((e=>[...e.children])).filter((e=>!!e.id)),t=[...this.templateContent?.children||[]].filter((e=>!!e.id)).map((e=>e.id));return e.filter((e=>t.includes(e.id)))}get performAction(){if(this.action){const e=xt[this.action];if(e)return e;this.#M("unknown action")}this.#M("action attribute is missing")}get targetElements(){return this.target?this.targetElementsById:this.targets?this.targetElementsByQuery:void this.#M("target or targets attribute is missing")}get templateContent(){return this.templateElement.content.cloneNode(!0)}get templateElement(){if(null===this.firstElementChild){const e=this.ownerDocument.createElement("template");return this.appendChild(e),e}if(this.firstElementChild instanceof HTMLTemplateElement)return this.firstElementChild;this.#M("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")}get requestId(){return this.getAttribute("request-id")}#M(e){throw new Error(`${this.description}: ${e}`)}get description(){return(this.outerHTML.match(/<[^>]+>/)??[])[0]??"<turbo-stream>"}get beforeRenderEvent(){return new CustomEvent("turbo:before-stream-render",{bubbles:!0,cancelable:!0,detail:{newStream:this,render:Vt.renderElement}})}get targetElementsById(){const e=this.ownerDocument?.getElementById(this.target);return null!==e?[e]:[]}get targetElementsByQuery(){const e=this.ownerDocument?.querySelectorAll(this.targets);return 0!==e.length?Array.prototype.slice.call(e):[]}}class Dt extends HTMLElement{streamSource=null;connectedCallback(){this.streamSource=this.src.match(/^ws{1,2}:/)?new WebSocket(this.src):new EventSource(this.src),Pt(this.streamSource)}disconnectedCallback(){this.streamSource&&(this.streamSource.close(),Ct(this.streamSource))}get src(){return this.getAttribute("src")||""}}i.delegateConstructor=class{fetchResponseLoaded=e=>Promise.resolve();#F=null;#I=()=>{};#q=!1;#H=!1;#B=new Set;#O=!1;action=null;constructor(e){this.element=e,this.view=new de(this,this.element),this.appearanceObserver=new te(this,this.element),this.formLinkClickObserver=new pe(this,this.element),this.linkInterceptor=new ue(this,this.element),this.restorationIdentifier=p(),this.formSubmitObserver=new ce(this,this.element)}connect(){this.#q||(this.#q=!0,this.loadingStyle==r.lazy?this.appearanceObserver.start():this.#N(),this.formLinkClickObserver.start(),this.linkInterceptor.start(),this.formSubmitObserver.start())}disconnect(){this.#q&&(this.#q=!1,this.appearanceObserver.stop(),this.formLinkClickObserver.stop(),this.linkInterceptor.stop(),this.formSubmitObserver.stop())}disabledChanged(){this.loadingStyle==r.eager&&this.#N()}sourceURLChanged(){this.#x("src")||(this.element.isConnected&&(this.complete=!1),(this.loadingStyle==r.eager||this.#H)&&this.#N())}sourceURLReloaded(){const{refresh:e,src:t}=this.element;return this.#O=t&&"morph"===e,this.element.removeAttribute("complete"),this.element.src=null,this.element.src=t,this.element.loaded}loadingStyleChanged(){this.loadingStyle==r.lazy?this.appearanceObserver.start():(this.appearanceObserver.stop(),this.#N())}async#N(){this.enabled&&this.isActive&&!this.complete&&this.sourceURL&&(this.element.loaded=this.#V(q(this.sourceURL)),this.appearanceObserver.stop(),await this.element.loaded,this.#H=!0)}async loadResponse(e){(e.redirected||e.succeeded&&e.isHTML)&&(this.sourceURL=e.response.url);try{const t=await e.responseHTML;if(t){const s=u(t);Pe.fromDocument(s).isVisitable?await this.#D(e,s):await this.#W(e)}}finally{this.#O=!1,this.fetchResponseLoaded=()=>Promise.resolve()}}elementAppearedInViewport(e){this.proposeVisitIfNavigatedWithAction(e,w(e)),this.#N()}willSubmitFormLinkToLocation(e){return this.#U(e)}submittedFormLinkToLocation(e,t,s){const r=this.#m(e);r&&s.setAttribute("data-turbo-frame",r.id)}shouldInterceptLinkClick(e,t,s){return this.#U(e)}linkClickIntercepted(e,t){this.#j(e,t)}willSubmitForm(e,t){return e.closest("turbo-frame")==this.element&&this.#U(e,t)}formSubmitted(e,t){this.formSubmission&&this.formSubmission.stop(),this.formSubmission=new ne(this,e,t);const{fetchRequest:s}=this.formSubmission;this.prepareRequest(s),this.formSubmission.start()}prepareRequest(e){e.headers["Turbo-Frame"]=this.id,this.currentNavigationElement?.hasAttribute("data-turbo-stream")&&e.acceptResponseType(se.contentType)}requestStarted(e){g(this.element)}requestPreventedHandlingResponse(e,t){this.#I()}async requestSucceededWithResponse(e,t){await this.loadResponse(t),this.#I()}async requestFailedWithResponse(e,t){await this.loadResponse(t),this.#I()}requestErrored(e,t){console.error(t),this.#I()}requestFinished(e){b(this.element)}formSubmissionStarted({formElement:e}){g(e,this.#m(e))}formSubmissionSucceededWithResponse(e,t){const s=this.#m(e.formElement,e.submitter);s.delegate.proposeVisitIfNavigatedWithAction(s,w(e.submitter,e.formElement,s)),s.delegate.loadResponse(t),e.isSafe||Et.clearCache()}formSubmissionFailedWithResponse(e,t){this.element.delegate.loadResponse(t),Et.clearCache()}formSubmissionErrored(e,t){console.error(t)}formSubmissionFinished({formElement:e}){b(e,this.#m(e))}allowsImmediateRender({element:e},t){const s=a("turbo:before-frame-render",{target:this.element,detail:{newFrame:e,...t},cancelable:!0}),{defaultPrevented:r,detail:{render:i}}=s;return this.view.renderer&&i&&(this.view.renderer.renderElement=i),!r}viewRenderedSnapshot(e,t,s){}preloadOnLoadLinksForView(e){Et.preloadOnLoadLinksForView(e)}viewInvalidated(){}willRenderFrame(e,t){this.previousFrameElement=e.cloneNode(!0)}visitCachedSnapshot=({element:e})=>{const t=e.querySelector("#"+this.element.id);t&&this.previousFrameElement&&t.replaceChildren(...this.previousFrameElement.children),delete this.previousFrameElement};async#D(e,t){const s=await this.extractForeignFrameElement(t.body),r=this.#O?ye:be;if(s){const t=new oe(s),i=new r(this,this.view.snapshot,t,!1,!1);this.view.renderPromise&&await this.view.renderPromise,this.changeHistory(),await this.view.render(i),this.complete=!0,Et.frameRendered(e,this.element),Et.frameLoaded(this.element),await this.fetchResponseLoaded(e)}else this.#$(e)&&this.#z(e)}async#V(e){const t=new Y(this,X.get,e,new URLSearchParams,this.element);return this.#F?.cancel(),this.#F=t,new Promise((e=>{this.#I=()=>{this.#I=()=>{},this.#F=null,e()},t.perform()}))}#j(e,t,s){const r=this.#m(e,s);r.delegate.proposeVisitIfNavigatedWithAction(r,w(s,e,r)),this.#_(e,(()=>{r.src=t}))}proposeVisitIfNavigatedWithAction(e,t=null){if(this.action=t,this.action){const t=Pe.fromElement(e).clone(),{visitCachedSnapshot:s}=e.delegate;e.delegate.fetchResponseLoaded=async r=>{if(e.src){const{statusCode:i,redirected:n}=r,o={response:{statusCode:i,redirected:n,responseHTML:await r.responseHTML},visitCachedSnapshot:s,willRender:!1,updateHistory:!1,restorationIdentifier:this.restorationIdentifier,snapshot:t};this.action&&(o.action=this.action),Et.visit(e.src,o)}}}}changeHistory(){if(this.action){const e=S(this.action);Et.history.update(e,q(this.element.src||""),this.restorationIdentifier)}}async#W(e){console.warn(`The response (${e.statusCode}) from <turbo-frame id="${this.element.id}"> is performing a full page visit due to turbo-visit-control.`),await this.#X(e.response)}#$(e){this.element.setAttribute("complete","");const t=e.response;return!a("turbo:frame-missing",{target:this.element,detail:{response:t,visit:async(e,t)=>{e instanceof Response?this.#X(e):Et.visit(e,t)}},cancelable:!0}).defaultPrevented}#z(e){this.view.missing(),this.#K(e)}#K(e){const t=`The response (${e.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 Bt(t)}async#X(e){const t=new W(e),s=await t.responseHTML,{location:r,redirected:i,statusCode:n}=t;return Et.visit(r,{response:{redirected:i,statusCode:n,responseHTML:s}})}#m(e,t){return Ot(f("data-turbo-frame",t,e)||this.element.getAttribute("target"))??this.element}async extractForeignFrameElement(e){let t;const s=CSS.escape(this.id);try{if(t=Nt(e.querySelector(`turbo-frame#${s}`),this.sourceURL),t)return t;if(t=Nt(e.querySelector(`turbo-frame[src][recurse~=${s}]`),this.sourceURL),t)return await t.loaded,await this.extractForeignFrameElement(t)}catch(e){return console.error(e),new i}return null}#Q(e,t){return x(q(B(e,t)),this.rootLocation)}#U(e,t){const s=f("data-turbo-frame",t,e)||this.element.getAttribute("target");if(e instanceof HTMLFormElement&&!this.#Q(e,t))return!1;if(!this.enabled||"_top"==s)return!1;if(s){const e=Ot(s);if(e)return!e.disabled}return!!Et.elementIsNavigatable(e)&&!(t&&!Et.elementIsNavigatable(t))}get id(){return this.element.id}get enabled(){return!this.element.disabled}get sourceURL(){if(this.element.src)return this.element.src}set sourceURL(e){this.#Y("src",(()=>{this.element.src=e??null}))}get loadingStyle(){return this.element.loading}get isLoading(){return void 0!==this.formSubmission||void 0!==this.#I()}get complete(){return this.element.hasAttribute("complete")}set complete(e){e?this.element.setAttribute("complete",""):this.element.removeAttribute("complete")}get isActive(){return this.element.isActive&&this.#q}get rootLocation(){const e=this.element.ownerDocument.querySelector('meta[name="turbo-root"]');return q(e?.content??"/")}#x(e){return this.#B.has(e)}#Y(e,t){this.#B.add(e),t(),this.#B.delete(e)}#_(e,t){this.currentNavigationElement=e,t(),delete this.currentNavigationElement}},void 0===customElements.get("turbo-frame")&&customElements.define("turbo-frame",i),void 0===customElements.get("turbo-stream")&&customElements.define("turbo-stream",Vt),void 0===customElements.get("turbo-stream-source")&&customElements.define("turbo-stream-source",Dt),(()=>{let e=document.currentScript;if(e&&!e.hasAttribute("data-turbo-suppress-warning"))for(e=e.parentElement;e;){if(e==document.body)return console.warn(m`
|
|
26
|
+
You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
|
|
27
|
+
|
|
28
|
+
Load your application’s JavaScript bundle inside the <head> element instead. <script> elements in <body> are evaluated with each page change.
|
|
29
|
+
|
|
30
|
+
For more information, see: https://turbo.hotwired.dev/handbook/building#working-with-script-elements
|
|
31
|
+
|
|
32
|
+
——
|
|
33
|
+
Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
|
|
34
|
+
`,e.outerHTML);e=e.parentElement}})(),window.Turbo={...Ht,StreamActions:xt},Lt(),e.FetchEnctype=Q,e.FetchMethod=X,e.FetchRequest=Y,e.FetchResponse=W,e.FrameElement=i,e.FrameLoadingStyle=r,e.FrameRenderer=be,e.PageRenderer=ut,e.PageSnapshot=Pe,e.StreamActions=xt,e.StreamElement=Vt,e.StreamSourceElement=Dt,e.cache=yt,e.clearCache=Mt,e.config=I,e.connectStreamSource=Pt,e.disconnectStreamSource=Ct,e.fetch=z,e.fetchEnctypeFromString=K,e.fetchMethodFromString=_,e.isSafe=G,e.navigator=Rt,e.registerAdapter=Tt,e.renderStreamMessage=kt,e.session=Et,e.setConfirmMethod=It,e.setFormMode=qt,e.setProgressBarDelay=Ft,e.start=Lt,e.visit=At,Object.defineProperty(e,"__esModule",{value:!0})}));
|
|
35
|
+
//# sourceMappingURL=/sm/e9536071dc67aba3f6050d0b7db4efdac3a1924e3883a69fc696c07cce63be26.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
|
3
|
+
* listed below.
|
|
4
|
+
*
|
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
|
7
|
+
*
|
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
|
11
|
+
* It is generally better to create a new file per style scope.
|
|
12
|
+
*
|
|
13
|
+
*= require_tree .
|
|
14
|
+
*= require_self
|
|
15
|
+
*/
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module RoundhouseUi
|
|
2
|
+
# Shared search + pagination over a Sidekiq job set (dead, retry, scheduled).
|
|
3
|
+
# Keeps the controllers from duplicating the scan/filter/window logic.
|
|
4
|
+
module JobSetBrowsing
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
PER_PAGE = 25
|
|
8
|
+
|
|
9
|
+
# Returns [entries_for_page, has_next?]. Scans only far enough to fill the
|
|
10
|
+
# requested page plus one (to know if a next page exists) — never loads the
|
|
11
|
+
# whole set, so a 50k dead set stays cheap to page through.
|
|
12
|
+
def browse(set, query, page, per = PER_PAGE)
|
|
13
|
+
start = (page - 1) * per
|
|
14
|
+
jobs = []
|
|
15
|
+
has_next = false
|
|
16
|
+
matched = 0
|
|
17
|
+
|
|
18
|
+
set.each do |entry|
|
|
19
|
+
next if query.present? && !entry_matches?(entry, query)
|
|
20
|
+
|
|
21
|
+
if matched < start
|
|
22
|
+
matched += 1
|
|
23
|
+
elsif jobs.size < per
|
|
24
|
+
jobs << entry
|
|
25
|
+
matched += 1
|
|
26
|
+
else
|
|
27
|
+
has_next = true
|
|
28
|
+
break
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
[ jobs, has_next ]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def entry_matches?(entry, query)
|
|
36
|
+
needle = query.downcase
|
|
37
|
+
[ entry.klass, entry.jid, entry.item["error_class"], entry.item["error_message"], entry.args.to_s ]
|
|
38
|
+
.any? { |hay| hay.to_s.downcase.include?(needle) }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require "securerandom"
|
|
2
|
+
|
|
3
|
+
module RoundhouseUi
|
|
4
|
+
class ApplicationController < ActionController::Base
|
|
5
|
+
# Isolated engines don't auto-include the host's helpers; include ours.
|
|
6
|
+
helper ObservabilityHelper
|
|
7
|
+
helper NavHelper
|
|
8
|
+
helper_method :content_nonce
|
|
9
|
+
|
|
10
|
+
# Self-contained CSP, set per-request on our own responses so Roundhouse is
|
|
11
|
+
# safe to mount even when the host sets no policy — and never weakens one it
|
|
12
|
+
# does (this header only applies to engine responses). Strict default; we
|
|
13
|
+
# enumerate exactly what our pages use (same-origin only, nonce'd inline JS).
|
|
14
|
+
after_action :set_content_security_policy
|
|
15
|
+
|
|
16
|
+
# Record every state-changing (POST) action. Actions halted by a
|
|
17
|
+
# before_action (e.g. read-only mode) never reach here, so we only log what
|
|
18
|
+
# actually ran.
|
|
19
|
+
AUDIT_VERBS = {
|
|
20
|
+
"purge" => "purged queue", "pause" => "paused queue", "resume" => "resumed queue",
|
|
21
|
+
"snapshot" => "snapshotted queue", "requeue" => "retried", "destroy" => "deleted",
|
|
22
|
+
"bulk" => "bulk action", "enqueue" => "enqueued now", "restore" => "restored snapshot",
|
|
23
|
+
"quiet" => "quieted process", "stop" => "stopped process",
|
|
24
|
+
"create" => "enqueued job", "update" => "edited & re-enqueued",
|
|
25
|
+
"cancel" => "requested cancel"
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
after_action :record_audit_event, if: -> { request.post? }
|
|
29
|
+
|
|
30
|
+
# Use 303 See Other after POSTs so Turbo treats form submissions as redirects
|
|
31
|
+
# (and visits the target in place) instead of re-issuing the POST.
|
|
32
|
+
def redirect_to(options = {}, response_options = {})
|
|
33
|
+
response_options[:status] ||= :see_other if request.post?
|
|
34
|
+
super
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def record_audit_event
|
|
40
|
+
target = params[:name] || params[:jid] || params[:id] || params[:job_class] ||
|
|
41
|
+
(params[:jids].presence && "#{Array(params[:jids]).size} jobs") || params[:op]
|
|
42
|
+
RoundhouseUi::Audit.record(
|
|
43
|
+
actor: current_actor,
|
|
44
|
+
action: AUDIT_VERBS[action_name] || "#{controller_name}##{action_name}",
|
|
45
|
+
target: target
|
|
46
|
+
)
|
|
47
|
+
rescue => e
|
|
48
|
+
Rails.logger.warn("[roundhouse] audit failed: #{e.message}")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def current_actor
|
|
52
|
+
resolver = RoundhouseUi.actor_resolver
|
|
53
|
+
(resolver && resolver.call(self)) || "anonymous"
|
|
54
|
+
rescue
|
|
55
|
+
"anonymous"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Memoized so the value rendered into the <script> tag matches the header.
|
|
59
|
+
def content_nonce
|
|
60
|
+
@content_nonce ||= SecureRandom.base64(16)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def set_content_security_policy
|
|
64
|
+
response.headers["Content-Security-Policy"] = [
|
|
65
|
+
"default-src 'none'",
|
|
66
|
+
"script-src 'self' 'nonce-#{content_nonce}'",
|
|
67
|
+
"style-src 'self' 'unsafe-inline'",
|
|
68
|
+
"connect-src 'self'",
|
|
69
|
+
"img-src 'self' data:",
|
|
70
|
+
"form-action 'self'",
|
|
71
|
+
"base-uri 'self'"
|
|
72
|
+
].join("; ")
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module RoundhouseUi
|
|
2
|
+
# Serves the vendored Turbo build same-origin so it passes the engine's CSP
|
|
3
|
+
# (script-src 'self') without a build step or asset pipeline.
|
|
4
|
+
class AssetsController < ApplicationController
|
|
5
|
+
# It's a public static asset — Rails' cross-origin-JS forgery guard would
|
|
6
|
+
# otherwise 422 the <script src> request.
|
|
7
|
+
skip_forgery_protection
|
|
8
|
+
|
|
9
|
+
TURBO_PATH = RoundhouseUi::Engine.root.join("app/assets/javascripts/roundhouse_ui/turbo.min.js").freeze
|
|
10
|
+
|
|
11
|
+
def turbo
|
|
12
|
+
response.headers["Cache-Control"] = "public, max-age=31536000, immutable"
|
|
13
|
+
send_file TURBO_PATH, type: "text/javascript", disposition: "inline"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|