diff --git a/.ruby-version b/.ruby-version index 2aa5131..c4e41f9 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.4.7 +4.0.3 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1baf46a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,527 @@ +# CHANGE LOG + +**12.06.2026** +- update to Ruby 4.x +- Security Updates from `bundle-audit --update` + - Rails (7.2.3) -> 8.1.3 + - addressable (2.8.8 )-> 2.9.0 + - erb (6.0.0) -> 6.0.4 + - jason (2.17.1) -> 2.19.9 + - net-imap (0.5.12) -> 0.6.4.1 + - nokogiri (1.18.10) -> 1.19.3 + - puma (7.1.0) -> 8.0.2 + - rack (3.2.4) -> 3.2.6 + - rack-session (2.1.1) -> 2.1.2 +- Issues and Solutions +```bash +❯ bundle-audit + +Name: actionview +Version: 7.2.3 +CVE: CVE-2026-33168 +GHSA: GHSA-v55j-83pf-r9cq +Criticality: Unknown +URL: https://github.com/rails/rails/security/advisories/GHSA-v55j-83pf-r9cq +Title: Rails has a possible XSS vulnerability in its Action View tag helpers +Solution: update to '~> 7.2.3, >= 7.2.3.1', '~> 8.0.4, >= 8.0.4.1', '>= 8.1.2.1' + +Name: activestorage +Version: 7.2.3 +CVE: CVE-2026-33173 +GHSA: GHSA-qcfx-2mfw-w4cg +Criticality: Unknown +URL: https://github.com/rails/rails/security/advisories/GHSA-qcfx-2mfw-w4cg +Title: Rails Active Storage has possible content type bypass via metadata in direct uploads +Solution: update to '~> 7.2.3, >= 7.2.3.1', '~> 8.0.4, >= 8.0.4.1', '>= 8.1.2.1' + +Name: activestorage +Version: 7.2.3 +CVE: CVE-2026-33174 +GHSA: GHSA-r46p-8f7g-vvvg +Criticality: Unknown +URL: https://github.com/rails/rails/security/advisories/GHSA-r46p-8f7g-vvvg +Title: Rails Active Storage has a possible DoS vulnerability when in proxy mode via Range requests +Solution: update to '~> 7.2.3, >= 7.2.3.1', '~> 8.0.4, >= 8.0.4.1', '>= 8.1.2.1' + +Name: activestorage +Version: 7.2.3 +CVE: CVE-2026-33195 +GHSA: GHSA-9xrj-h377-fr87 +Criticality: Unknown +URL: https://github.com/rails/rails/security/advisories/GHSA-9xrj-h377-fr87 +Title: Rails Active Storage has possible Path Traversal in DiskService +Solution: update to '~> 7.2.3, >= 7.2.3.1', '~> 8.0.4, >= 8.0.4.1', '>= 8.1.2.1' + +Name: activestorage +Version: 7.2.3 +CVE: CVE-2026-33202 +GHSA: GHSA-73f9-jhhh-hr5m +Criticality: Unknown +URL: https://github.com/rails/rails/security/advisories/GHSA-73f9-jhhh-hr5m +Title: Rails Active Storage has possible glob injection in its DiskService +Solution: update to '~> 7.2.3, >= 7.2.3.1', '~> 8.0.4, >= 8.0.4.1', '>= 8.1.2.1' + +Name: activestorage +Version: 7.2.3 +CVE: CVE-2026-33658 +GHSA: GHSA-p9fm-f462-ggrg +Criticality: Medium +URL: https://github.com/rails/rails/security/advisories/GHSA-p9fm-f462-ggrg +Title: Rails Active Storage has a possible DoS vulnerability in proxy mode via multi-range requests +Solution: update to '~> 7.2.3, >= 7.2.3.1', '~> 8.0.4, >= 8.0.4.1', '>= 8.1.2.1' + +Name: activesupport +Version: 7.2.3 +CVE: CVE-2026-33169 +GHSA: GHSA-cg4j-q9v8-6v38 +Criticality: Unknown +URL: https://github.com/rails/rails/security/advisories/GHSA-cg4j-q9v8-6v38 +Title: Rails Active Support has a possible ReDoS vulnerability in number_to_delimited +Solution: update to '~> 7.2.3, >= 7.2.3.1', '~> 8.0.4, >= 8.0.4.1', '>= 8.1.2.1' + +Name: activesupport +Version: 7.2.3 +CVE: CVE-2026-33170 +GHSA: GHSA-89vf-4333-qx8v +Criticality: Unknown +URL: https://github.com/rails/rails/security/advisories/GHSA-89vf-4333-qx8v +Title: Rails Active Support has a possible XSS vulnerability in SafeBuffer#% +Solution: update to '~> 7.2.3, >= 7.2.3.1', '~> 8.0.4, >= 8.0.4.1', '>= 8.1.2.1' + +Name: activesupport +Version: 7.2.3 +CVE: CVE-2026-33176 +GHSA: GHSA-2j26-frm8-cmj9 +Criticality: Unknown +URL: https://github.com/rails/rails/security/advisories/GHSA-2j26-frm8-cmj9 +Title: Rails Active Support has a possible DoS vulnerability in its number helpers +Solution: update to '~> 7.2.3, >= 7.2.3.1', '~> 8.0.4, >= 8.0.4.1', '>= 8.1.2.1' + +Name: addressable +Version: 2.8.8 +CVE: CVE-2026-35611 +GHSA: GHSA-h27x-rffw-24p4 +Criticality: High +URL: https://github.com/sporkmonger/addressable/security/advisories/GHSA-h27x-rffw-24p4 +Title: Addressable has a Regular Expression Denial of Service in Addressable templates +Solution: update to '>= 2.9.0' + +Name: erb +Version: 6.0.0 +CVE: CVE-2026-41316 +GHSA: GHSA-q339-8rmv-2mhv +Criticality: High +URL: https://nvd.nist.gov/vuln/detail/CVE-2026-41316 +Title: ERB has an @_init deserialization guard bypass via def_module / def_method / def_class +Solution: update to '~> 4.0.3.1', '~> 4.0.4.1', '~> 6.0.1.1', '>= 6.0.4' + +Name: json +Version: 2.17.1 +CVE: CVE-2026-33210 +GHSA: GHSA-3m6g-2423-7cp3 +Criticality: Unknown +URL: https://github.com/ruby/json/security/advisories/GHSA-3m6g-2423-7cp3 +Title: Ruby JSON has a format string injection vulnerability +Solution: update to '~> 2.15.2.1', '~> 2.17.1.2', '>= 2.19.2' + +Name: net-imap +Version: 0.5.12 +CVE: CVE-2026-42245 +GHSA: GHSA-q2mw-fvj9-vvcw +Criticality: Unknown +URL: https://github.com/ruby/net-imap/security/advisories/GHSA-q2mw-fvj9-vvcw +Title: net-imap has quadratic complexity when reading response literals +Solution: update to '~> 0.4.24', '~> 0.5.14', '>= 0.6.4' + +Name: net-imap +Version: 0.5.12 +CVE: CVE-2026-42246 +GHSA: GHSA-vcgp-9326-pqcp +Criticality: Unknown +URL: https://github.com/ruby/net-imap/security/advisories/GHSA-vcgp-9326-pqcp +Title: net-imap vulnerable to STARTTLS stripping via invalid response timing +Solution: update to '~> 0.3.10', '~> 0.4.24', '~> 0.5.14', '>= 0.6.4' + +Name: net-imap +Version: 0.5.12 +CVE: CVE-2026-42256 +GHSA: GHSA-87pf-fpwv-p7m7 +Criticality: Unknown +URL: https://github.com/ruby/net-imap/security/advisories/GHSA-87pf-fpwv-p7m7 +Title: net-imap vulnerable to denial of service via high iteration count for `SCRAM-*` authentication +Solution: update to '~> 0.4.24', '~> 0.5.14', '>= 0.6.4' + +Name: net-imap +Version: 0.5.12 +CVE: CVE-2026-42257 +GHSA: GHSA-hm49-wcqc-g2xg +Criticality: Unknown +URL: https://github.com/ruby/net-imap/security/advisories/GHSA-hm49-wcqc-g2xg +Title: net-imap vulnerable to command Injection via "raw" arguments to multiple commands +Solution: update to '~> 0.4.24', '~> 0.5.14', '>= 0.6.4' + +Name: net-imap +Version: 0.5.12 +CVE: CVE-2026-42258 +GHSA: GHSA-75xq-5h9v-w6px +Criticality: Unknown +URL: https://github.com/ruby/net-imap/security/advisories/GHSA-75xq-5h9v-w6px +Title: net-imap vulnerable to command Injection via unvalidated Symbol inputs +Solution: update to '~> 0.4.24', '~> 0.5.14', '>= 0.6.4' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-c4rq-3m3g-8wgx +Criticality: High +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-c4rq-3m3g-8wgx +Title: Nokogiri CSS selector tokenizer has regular expression backtracking +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-v2fc-qm4h-8hqv +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-v2fc-qm4h-8hqv +Title: Nokogiri XSLT transform has a memory leak +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-wx95-c6cv-8532 +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-wx95-c6cv-8532 +Title: Nokogiri does not check the return value from xmlC14NExecute +Solution: update to '>= 1.19.1' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-c4rq-3m3g-8wgx +Criticality: High +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-c4rq-3m3g-8wgx +Title: Nokogiri CSS selector tokenizer has regular expression backtracking +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-v2fc-qm4h-8hqv +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-v2fc-qm4h-8hqv +Title: Nokogiri XSLT transform has a memory leak +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-wx95-c6cv-8532 +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-wx95-c6cv-8532 +Title: Nokogiri does not check the return value from xmlC14NExecute +Solution: update to '>= 1.19.1' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-c4rq-3m3g-8wgx +Criticality: High +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-c4rq-3m3g-8wgx +Title: Nokogiri CSS selector tokenizer has regular expression backtracking +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-v2fc-qm4h-8hqv +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-v2fc-qm4h-8hqv +Title: Nokogiri XSLT transform has a memory leak +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-wx95-c6cv-8532 +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-wx95-c6cv-8532 +Title: Nokogiri does not check the return value from xmlC14NExecute +Solution: update to '>= 1.19.1' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-c4rq-3m3g-8wgx +Criticality: High +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-c4rq-3m3g-8wgx +Title: Nokogiri CSS selector tokenizer has regular expression backtracking +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-v2fc-qm4h-8hqv +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-v2fc-qm4h-8hqv +Title: Nokogiri XSLT transform has a memory leak +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-wx95-c6cv-8532 +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-wx95-c6cv-8532 +Title: Nokogiri does not check the return value from xmlC14NExecute +Solution: update to '>= 1.19.1' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-c4rq-3m3g-8wgx +Criticality: High +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-c4rq-3m3g-8wgx +Title: Nokogiri CSS selector tokenizer has regular expression backtracking +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-v2fc-qm4h-8hqv +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-v2fc-qm4h-8hqv +Title: Nokogiri XSLT transform has a memory leak +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-wx95-c6cv-8532 +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-wx95-c6cv-8532 +Title: Nokogiri does not check the return value from xmlC14NExecute +Solution: update to '>= 1.19.1' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-c4rq-3m3g-8wgx +Criticality: High +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-c4rq-3m3g-8wgx +Title: Nokogiri CSS selector tokenizer has regular expression backtracking +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-v2fc-qm4h-8hqv +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-v2fc-qm4h-8hqv +Title: Nokogiri XSLT transform has a memory leak +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-wx95-c6cv-8532 +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-wx95-c6cv-8532 +Title: Nokogiri does not check the return value from xmlC14NExecute +Solution: update to '>= 1.19.1' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-c4rq-3m3g-8wgx +Criticality: High +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-c4rq-3m3g-8wgx +Title: Nokogiri CSS selector tokenizer has regular expression backtracking +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-v2fc-qm4h-8hqv +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-v2fc-qm4h-8hqv +Title: Nokogiri XSLT transform has a memory leak +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-wx95-c6cv-8532 +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-wx95-c6cv-8532 +Title: Nokogiri does not check the return value from xmlC14NExecute +Solution: update to '>= 1.19.1' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-c4rq-3m3g-8wgx +Criticality: High +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-c4rq-3m3g-8wgx +Title: Nokogiri CSS selector tokenizer has regular expression backtracking +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-v2fc-qm4h-8hqv +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-v2fc-qm4h-8hqv +Title: Nokogiri XSLT transform has a memory leak +Solution: update to '>= 1.19.3' + +Name: nokogiri +Version: 1.18.10 +GHSA: GHSA-wx95-c6cv-8532 +Criticality: Medium +URL: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-wx95-c6cv-8532 +Title: Nokogiri does not check the return value from xmlC14NExecute +Solution: update to '>= 1.19.1' + +Name: puma +Version: 7.1.0 +CVE: CVE-2026-47736 +GHSA: GHSA-qpgp-93vx-g8v8 +Criticality: High +URL: https://www.cve.org/CVERecord?id=CVE-2026-47736 +Title: Puma PROXY Protocol v1 Parser Allows Remote Memory Exhaustion +Solution: update to '~> 7.2.1', '>= 8.0.2' + +Name: puma +Version: 7.1.0 +CVE: CVE-2026-47737 +GHSA: GHSA-2vqw-3mp8-cgmx +Criticality: High +URL: https://www.cve.org/CVERecord/SearchResults?query=CVE-2026-47737 +Title: Puma PROXY Protocol v1 Accepts Repeated Protocol Headers on Persistent Connections +Solution: update to '~> 7.2.1', '>= 8.0.2' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-22860 +GHSA: GHSA-mxw3-3hh2-x2mh +Criticality: High +URL: https://github.com/rack/rack/security/advisories/GHSA-mxw3-3hh2-x2mh +Title: Rack has a Directory Traversal via Rack:Directory +Solution: update to '~> 2.2.22', '~> 3.1.20', '>= 3.2.5' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-25500 +GHSA: GHSA-whrj-4476-wvmp +Criticality: Medium +URL: https://github.com/rack/rack/security/advisories/GHSA-whrj-4476-wvmp +Title: Stored XSS in Rack::Directory via javascript: filenames rendered into anchor href +Solution: update to '~> 2.2.22', '~> 3.1.20', '>= 3.2.5' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-26961 +GHSA: GHSA-vgpv-f759-9wx3 +Criticality: Low +URL: https://github.com/rack/rack/security/advisories/GHSA-vgpv-f759-9wx3 +Title: Rack's greedy multipart boundary parsing can cause parser differentials and WAF bypass. +Solution: update to '~> 2.2.23', '~> 3.1.21', '>= 3.2.6' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-26962 +GHSA: GHSA-rx22-g9mx-qrhv +Criticality: Medium +URL: https://github.com/rack/rack/security/advisories/GHSA-rx22-g9mx-qrhv +Title: Rack's improper unfolding of folded multipart headers preserves CRLF in parsed parameter values +Solution: update to '>= 3.2.6' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-32762 +GHSA: GHSA-qfgr-crr9-7r49 +Criticality: Medium +URL: https://github.com/rack/rack/security/advisories/GHSA-qfgr-crr9-7r49 +Title: Rack - Forwarded Header semicolon injection enables Host and Scheme spoofing +Solution: update to '~> 3.1.21', '>= 3.2.6' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-34230 +GHSA: GHSA-v569-hp3g-36wr +Criticality: Medium +URL: https://github.com/rack/rack/security/advisories/GHSA-v569-hp3g-36wr +Title: Rack has quadratic complexity in Rack::Utils.select_best_encoding via wildcard Accept-Encoding header +Solution: update to '~> 2.2.23', '~> 3.1.21', '>= 3.2.6' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-34763 +GHSA: GHSA-7mqq-6cf9-v2qp +Criticality: Medium +URL: https://github.com/rack/rack/security/advisories/GHSA-7mqq-6cf9-v2qp +Title: Rack has a root directory disclosure via unescaped regex interpolation in Rack::Directory +Solution: update to '~> 2.2.23', '~> 3.1.21', '>= 3.2.6' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-34785 +GHSA: GHSA-h2jq-g4cq-5ppq +Criticality: High +URL: https://github.com/rack/rack/security/advisories/GHSA-h2jq-g4cq-5ppq +Title: Rack::Static prefix matching can expose unintended files under the static root +Solution: update to '~> 2.2.23', '~> 3.1.21', '>= 3.2.6' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-34786 +GHSA: GHSA-q4qf-9j86-f5mh +Criticality: Medium +URL: https://github.com/rack/rack/security/advisories/GHSA-q4qf-9j86-f5mh +Title: Rack:: Static header_rules bypass via URL-encoded paths +Solution: update to '~> 2.2.23', '~> 3.1.21', '>= 3.2.6' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-34826 +GHSA: GHSA-x8cg-fq8g-mxfx +Criticality: Medium +URL: https://github.com/rack/rack/security/advisories/GHSA-x8cg-fq8g-mxfx +Title: Rack's multipart byte range processing allows denial of service via excessive overlapping ranges +Solution: update to '~> 2.2.23', '~> 3.1.21', '>= 3.2.6' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-34827 +GHSA: GHSA-v6x5-cg8r-vv6x +Criticality: High +URL: https://github.com/rack/rack/security/advisories/GHSA-v6x5-cg8r-vv6x +Title: Rack's multipart header parsing allows Denial of Service via escape-heavy quoted parameters +Solution: update to '~> 3.1.21', '>= 3.2.6' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-34829 +GHSA: GHSA-8vqr-qjwx-82mw +Criticality: High +URL: https://github.com/rack/rack/security/advisories/GHSA-8vqr-qjwx-82mw +Title: Rack's multipart parsing without Content-Length header allows unbounded chunked file uploads +Solution: update to '~> 2.2.23', '~> 3.1.21', '>= 3.2.6' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-34830 +GHSA: GHSA-qv7j-4883-hwh7 +Criticality: Medium +URL: https://github.com/rack/rack/security/advisories/GHSA-qv7j-4883-hwh7 +Title: Rack::Sendfile header-based X-Accel-Mapping regex injection enables unauthorized X-Accel-Redirect +Solution: update to '~> 2.2.23', '~> 3.1.21', '>= 3.2.6' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-34831 +GHSA: GHSA-q2ww-5357-x388 +Criticality: Medium +URL: https://github.com/rack/rack/security/advisories/GHSA-q2ww-5357-x388 +Title: Rack has Content-Length mismatch in Rack::Files error responses +Solution: update to '~> 2.2.23', '~> 3.1.21', '>= 3.2.6' + +Name: rack +Version: 3.2.4 +CVE: CVE-2026-34835 +GHSA: GHSA-g2pf-xv49-m2h5 +Criticality: Medium +URL: https://github.com/rack/rack/security/advisories/GHSA-g2pf-xv49-m2h5 +Title: Rack::Request accepts invalid Host characters, enabling host allowlist bypass +Solution: update to '~> 3.1.21', '>= 3.2.6' + +Name: rack-session +Version: 2.1.1 +CVE: CVE-2026-39324 +GHSA: GHSA-33qg-7wpp-89cq +Criticality: Unknown +URL: https://github.com/rack/rack-session/security/advisories/GHSA-33qg-7wpp-89cq +Title: Rack::Session::Cookie secrets: decrypt failure fallback enables secretless session forgery and Marshal deserialization +Solution: update to '>= 2.1.2' + +Vulnerabilities found! +``` diff --git a/Gemfile b/Gemfile index 6103f9d..30293b5 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source "https://rubygems.org" # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" -gem "rails", "~> 7.2.3" +gem "rails", "~> 8.0" # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] gem "sprockets-rails" # Use postgresql as the database for Active Record diff --git a/Gemfile.lock b/Gemfile.lock index 4d6e32f..605a5a3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,89 +1,89 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.2.3) - actionpack (= 7.2.3) - activesupport (= 7.2.3) + action_text-trix (2.1.19) + railties + actioncable (8.1.3) + actionpack (= 8.1.3) + activesupport (= 8.1.3) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.2.3) - actionpack (= 7.2.3) - activejob (= 7.2.3) - activerecord (= 7.2.3) - activestorage (= 7.2.3) - activesupport (= 7.2.3) + actionmailbox (8.1.3) + actionpack (= 8.1.3) + activejob (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) mail (>= 2.8.0) - actionmailer (7.2.3) - actionpack (= 7.2.3) - actionview (= 7.2.3) - activejob (= 7.2.3) - activesupport (= 7.2.3) + actionmailer (8.1.3) + actionpack (= 8.1.3) + actionview (= 8.1.3) + activejob (= 8.1.3) + activesupport (= 8.1.3) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.2.3) - actionview (= 7.2.3) - activesupport (= 7.2.3) - cgi + actionpack (8.1.3) + actionview (= 8.1.3) + activesupport (= 8.1.3) nokogiri (>= 1.8.5) - racc - rack (>= 2.2.4, < 3.3) + rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (7.2.3) - actionpack (= 7.2.3) - activerecord (= 7.2.3) - activestorage (= 7.2.3) - activesupport (= 7.2.3) + actiontext (8.1.3) + action_text-trix (~> 2.1.15) + actionpack (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.2.3) - activesupport (= 7.2.3) + actionview (8.1.3) + activesupport (= 8.1.3) builder (~> 3.1) - cgi erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.2.3) - activesupport (= 7.2.3) + activejob (8.1.3) + activesupport (= 8.1.3) globalid (>= 0.3.6) - activemodel (7.2.3) - activesupport (= 7.2.3) - activerecord (7.2.3) - activemodel (= 7.2.3) - activesupport (= 7.2.3) + activemodel (8.1.3) + activesupport (= 8.1.3) + activerecord (8.1.3) + activemodel (= 8.1.3) + activesupport (= 8.1.3) timeout (>= 0.4.0) - activestorage (7.2.3) - actionpack (= 7.2.3) - activejob (= 7.2.3) - activerecord (= 7.2.3) - activesupport (= 7.2.3) + activestorage (8.1.3) + actionpack (= 8.1.3) + activejob (= 8.1.3) + activerecord (= 8.1.3) + activesupport (= 8.1.3) marcel (~> 1.0) - activesupport (7.2.3) + activesupport (8.1.3) base64 - benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + json logger (>= 1.4.2) minitest (>= 5.1) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) - addressable (2.8.8) + uri (>= 0.13.1) + addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) ast (2.4.3) base64 (0.3.0) - benchmark (0.5.0) - bigdecimal (3.3.1) + bigdecimal (4.1.2) bindex (0.8.1) - bootsnap (1.19.0) + bootsnap (1.24.6) msgpack (~> 1.2) - brakeman (7.1.1) + brakeman (8.0.4) racc builder (3.3.0) capybara (3.40.0) @@ -95,38 +95,38 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - cgi (0.5.0) - concurrent-ruby (1.3.5) - connection_pool (2.5.5) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) crass (1.0.6) cssbundling-rails (1.4.3) railties (>= 6.0.0) - date (3.5.0) - debug (1.11.0) + date (3.5.1) + debug (1.11.1) irb (~> 1.10) reline (>= 0.3.8) drb (2.2.3) - erb (6.0.0) + erb (6.0.4) erubi (1.13.1) globalid (1.3.0) activesupport (>= 6.1) - i18n (1.14.7) + i18n (1.14.8) concurrent-ruby (~> 1.0) - io-console (0.8.1) - irb (1.15.3) + io-console (0.8.2) + irb (1.18.0) pp (>= 0.6.0) + prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) - jbuilder (2.14.1) + jbuilder (2.15.1) actionview (>= 7.0.0) activesupport (>= 7.0.0) jsbundling-rails (1.3.1) railties (>= 6.0.0) - json (2.17.1) + json (2.19.9) language_server-protocol (3.17.0.5) lint_roller (1.1.0) logger (1.7.0) - loofah (2.24.1) + loofah (2.25.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.9.0) @@ -135,12 +135,14 @@ GEM net-imap net-pop net-smtp - marcel (1.1.0) + marcel (1.2.1) matrix (0.4.3) mini_mime (1.1.5) - minitest (5.26.2) - msgpack (1.8.0) - net-imap (0.5.12) + minitest (6.0.6) + drb (~> 2.0) + prism (~> 1.5) + msgpack (1.8.3) + net-imap (0.6.4.1) date net-protocol net-pop (0.1.2) @@ -150,77 +152,76 @@ GEM net-smtp (0.5.1) net-protocol nio4r (2.7.5) - nokogiri (1.18.10-aarch64-linux-gnu) + nokogiri (1.19.3-aarch64-linux-gnu) racc (~> 1.4) - nokogiri (1.18.10-aarch64-linux-musl) + nokogiri (1.19.3-aarch64-linux-musl) racc (~> 1.4) - nokogiri (1.18.10-arm-linux-gnu) + nokogiri (1.19.3-arm-linux-gnu) racc (~> 1.4) - nokogiri (1.18.10-arm-linux-musl) + nokogiri (1.19.3-arm-linux-musl) racc (~> 1.4) - nokogiri (1.18.10-arm64-darwin) + nokogiri (1.19.3-arm64-darwin) racc (~> 1.4) - nokogiri (1.18.10-x86_64-darwin) + nokogiri (1.19.3-x86_64-darwin) racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-gnu) + nokogiri (1.19.3-x86_64-linux-gnu) racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-musl) + nokogiri (1.19.3-x86_64-linux-musl) racc (~> 1.4) - parallel (1.27.0) - parser (3.3.10.0) + parallel (2.1.0) + parser (3.3.11.1) ast (~> 2.4.1) racc - pg (1.6.2) - pg (1.6.2-aarch64-linux) - pg (1.6.2-aarch64-linux-musl) - pg (1.6.2-arm64-darwin) - pg (1.6.2-x86_64-darwin) - pg (1.6.2-x86_64-linux) - pg (1.6.2-x86_64-linux-musl) + pg (1.6.3) + pg (1.6.3-aarch64-linux) + pg (1.6.3-aarch64-linux-musl) + pg (1.6.3-arm64-darwin) + pg (1.6.3-x86_64-darwin) + pg (1.6.3-x86_64-linux) + pg (1.6.3-x86_64-linux-musl) pp (0.6.3) prettyprint prettyprint (0.2.0) - prism (1.6.0) - psych (5.2.6) + prism (1.9.0) + psych (5.4.0) date stringio - public_suffix (7.0.0) - puma (7.1.0) + public_suffix (7.0.5) + puma (8.0.2) nio4r (~> 2.0) racc (1.8.1) - rack (3.2.4) - rack-session (2.1.1) + rack (3.2.6) + rack-session (2.1.2) base64 (>= 0.1.0) rack (>= 3.0.0) rack-test (2.2.0) rack (>= 1.3) - rackup (2.2.1) + rackup (2.3.1) rack (>= 3) - rails (7.2.3) - actioncable (= 7.2.3) - actionmailbox (= 7.2.3) - actionmailer (= 7.2.3) - actionpack (= 7.2.3) - actiontext (= 7.2.3) - actionview (= 7.2.3) - activejob (= 7.2.3) - activemodel (= 7.2.3) - activerecord (= 7.2.3) - activestorage (= 7.2.3) - activesupport (= 7.2.3) + rails (8.1.3) + actioncable (= 8.1.3) + actionmailbox (= 8.1.3) + actionmailer (= 8.1.3) + actionpack (= 8.1.3) + actiontext (= 8.1.3) + actionview (= 8.1.3) + activejob (= 8.1.3) + activemodel (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) bundler (>= 1.15.0) - railties (= 7.2.3) + railties (= 8.1.3) rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.2) - loofah (~> 2.21) + rails-html-sanitizer (1.7.0) + loofah (~> 2.25) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (7.2.3) - actionpack (= 7.2.3) - activesupport (= 7.2.3) - cgi + railties (8.1.3) + actionpack (= 8.1.3) + activesupport (= 8.1.3) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -228,34 +229,34 @@ GEM tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.1) - rdoc (6.16.1) + rake (13.4.2) + rdoc (7.2.0) erb psych (>= 4.0.0) tsort - regexp_parser (2.11.3) + regexp_parser (2.12.0) reline (0.6.3) io-console (~> 0.5) rexml (3.4.4) - rubocop (1.81.7) + rubocop (1.87.0) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) - parallel (~> 1.10) + parallel (>= 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.47.1, < 2.0) + rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.48.0) + rubocop-ast (1.49.1) parser (>= 3.3.7.2) - prism (~> 1.4) + prism (~> 1.7) rubocop-performance (1.26.1) lint_roller (~> 1.1) rubocop (>= 1.75.0, < 2.0) rubocop-ast (>= 1.47.1, < 2.0) - rubocop-rails (2.34.2) + rubocop-rails (2.35.4) activesupport (>= 4.2.0) lint_roller (~> 1.1) rack (>= 1.1) @@ -266,9 +267,9 @@ GEM rubocop-performance (>= 1.24) rubocop-rails (>= 2.30) ruby-progressbar (1.13.0) - rubyzip (3.2.2) + rubyzip (3.3.1) securerandom (0.4.1) - selenium-webdriver (4.38.0) + selenium-webdriver (4.44.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -284,32 +285,32 @@ GEM sprockets (>= 3.0.0) stimulus-rails (1.3.4) railties (>= 6.0.0) - stringio (3.1.9) - thor (1.4.0) - timeout (0.4.4) + stringio (3.2.0) + thor (1.5.0) + timeout (0.6.1) tsort (0.2.0) - turbo-rails (2.0.20) + turbo-rails (2.0.23) actionpack (>= 7.1.0) railties (>= 7.1.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) - unicode-emoji (4.1.0) + unicode-emoji (4.2.0) + uri (1.1.1) useragent (0.16.11) - web-console (4.2.1) - actionview (>= 6.0.0) - activemodel (>= 6.0.0) + web-console (4.3.0) + actionview (>= 8.0.0) bindex (>= 0.4.0) - railties (>= 6.0.0) + railties (>= 8.0.0) websocket (1.2.11) - websocket-driver (0.8.0) + websocket-driver (0.8.1) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.7.3) + zeitwerk (2.8.2) PLATFORMS aarch64-linux @@ -333,7 +334,7 @@ DEPENDENCIES jsbundling-rails pg (~> 1.1) puma (>= 5.0) - rails (~> 7.2.3) + rails (~> 8.0) rubocop-rails-omakase selenium-webdriver sprockets-rails @@ -343,4 +344,4 @@ DEPENDENCIES web-console BUNDLED WITH - 2.6.9 + 4.0.14 diff --git a/app/assets/builds/application.css b/app/assets/builds/application.css index e865181..752cb0a 100644 --- a/app/assets/builds/application.css +++ b/app/assets/builds/application.css @@ -1,2 +1,2 @@ /*! tailwindcss v4.1.17 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.static{position:static}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.inline{display:inline}.border{border-style:var(--tw-border-style);border-width:1px}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid} \ No newline at end of file +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-font-weight:initial;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--spacing:.25rem;--container-6xl:72rem;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5/2.25);--font-weight-bold:700;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.static{position:static}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.mt-20{margin-top:calc(var(--spacing)*20)}.block{display:block}.contents{display:contents}.flex{display:flex}.hidden{display:none}.inline{display:inline}.max-w-6xl{max-width:var(--container-6xl)}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.justify-center{justify-content:center}.border{border-style:var(--tw-border-style);border-width:1px}.px-4{padding-inline:calc(var(--spacing)*4)}.text-center{text-align:center}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file diff --git a/app/assets/builds/application.js b/app/assets/builds/application.js index 3cda460..bb7f4e1 100644 --- a/app/assets/builds/application.js +++ b/app/assets/builds/application.js @@ -1 +1,8666 @@ +var __defProp = Object.defineProperty; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __esm = (fn, res) => function __init() { + return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; + +// node_modules/@rails/actioncable/src/adapters.js +var adapters_default; +var init_adapters = __esm({ + "node_modules/@rails/actioncable/src/adapters.js"() { + adapters_default = { + logger: typeof console !== "undefined" ? console : void 0, + WebSocket: typeof WebSocket !== "undefined" ? WebSocket : void 0 + }; + } +}); + +// node_modules/@rails/actioncable/src/logger.js +var logger_default; +var init_logger = __esm({ + "node_modules/@rails/actioncable/src/logger.js"() { + init_adapters(); + logger_default = { + log(...messages) { + if (this.enabled) { + messages.push(Date.now()); + adapters_default.logger.log("[ActionCable]", ...messages); + } + } + }; + } +}); + +// node_modules/@rails/actioncable/src/connection_monitor.js +var now, secondsSince, ConnectionMonitor, connection_monitor_default; +var init_connection_monitor = __esm({ + "node_modules/@rails/actioncable/src/connection_monitor.js"() { + init_logger(); + now = () => (/* @__PURE__ */ new Date()).getTime(); + secondsSince = (time) => (now() - time) / 1e3; + ConnectionMonitor = class { + constructor(connection) { + this.visibilityDidChange = this.visibilityDidChange.bind(this); + this.connection = connection; + this.reconnectAttempts = 0; + } + start() { + if (!this.isRunning()) { + this.startedAt = now(); + delete this.stoppedAt; + this.startPolling(); + addEventListener("visibilitychange", this.visibilityDidChange); + logger_default.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`); + } + } + stop() { + if (this.isRunning()) { + this.stoppedAt = now(); + this.stopPolling(); + removeEventListener("visibilitychange", this.visibilityDidChange); + logger_default.log("ConnectionMonitor stopped"); + } + } + isRunning() { + return this.startedAt && !this.stoppedAt; + } + recordMessage() { + this.pingedAt = now(); + } + recordConnect() { + this.reconnectAttempts = 0; + delete this.disconnectedAt; + logger_default.log("ConnectionMonitor recorded connect"); + } + recordDisconnect() { + this.disconnectedAt = now(); + logger_default.log("ConnectionMonitor recorded disconnect"); + } + // Private + startPolling() { + this.stopPolling(); + this.poll(); + } + stopPolling() { + clearTimeout(this.pollTimeout); + } + poll() { + this.pollTimeout = setTimeout( + () => { + this.reconnectIfStale(); + this.poll(); + }, + this.getPollInterval() + ); + } + getPollInterval() { + const { staleThreshold, reconnectionBackoffRate } = this.constructor; + const backoff = Math.pow(1 + reconnectionBackoffRate, Math.min(this.reconnectAttempts, 10)); + const jitterMax = this.reconnectAttempts === 0 ? 1 : reconnectionBackoffRate; + const jitter = jitterMax * Math.random(); + return staleThreshold * 1e3 * backoff * (1 + jitter); + } + reconnectIfStale() { + if (this.connectionIsStale()) { + logger_default.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${secondsSince(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`); + this.reconnectAttempts++; + if (this.disconnectedRecently()) { + logger_default.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${secondsSince(this.disconnectedAt)} s`); + } else { + logger_default.log("ConnectionMonitor reopening"); + this.connection.reopen(); + } + } + } + get refreshedAt() { + return this.pingedAt ? this.pingedAt : this.startedAt; + } + connectionIsStale() { + return secondsSince(this.refreshedAt) > this.constructor.staleThreshold; + } + disconnectedRecently() { + return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold; + } + visibilityDidChange() { + if (document.visibilityState === "visible") { + setTimeout( + () => { + if (this.connectionIsStale() || !this.connection.isOpen()) { + logger_default.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`); + this.connection.reopen(); + } + }, + 200 + ); + } + } + }; + ConnectionMonitor.staleThreshold = 6; + ConnectionMonitor.reconnectionBackoffRate = 0.15; + connection_monitor_default = ConnectionMonitor; + } +}); + +// node_modules/@rails/actioncable/src/internal.js +var internal_default; +var init_internal = __esm({ + "node_modules/@rails/actioncable/src/internal.js"() { + internal_default = { + "message_types": { + "welcome": "welcome", + "disconnect": "disconnect", + "ping": "ping", + "confirmation": "confirm_subscription", + "rejection": "reject_subscription" + }, + "disconnect_reasons": { + "unauthorized": "unauthorized", + "invalid_request": "invalid_request", + "server_restart": "server_restart", + "remote": "remote" + }, + "default_mount_path": "/cable", + "protocols": [ + "actioncable-v1-json", + "actioncable-unsupported" + ] + }; + } +}); + +// node_modules/@rails/actioncable/src/connection.js +var message_types, protocols, supportedProtocols, indexOf, Connection, connection_default; +var init_connection = __esm({ + "node_modules/@rails/actioncable/src/connection.js"() { + init_adapters(); + init_connection_monitor(); + init_internal(); + init_logger(); + ({ message_types, protocols } = internal_default); + supportedProtocols = protocols.slice(0, protocols.length - 1); + indexOf = [].indexOf; + Connection = class { + constructor(consumer2) { + this.open = this.open.bind(this); + this.consumer = consumer2; + this.subscriptions = this.consumer.subscriptions; + this.monitor = new connection_monitor_default(this); + this.disconnected = true; + } + send(data) { + if (this.isOpen()) { + this.webSocket.send(JSON.stringify(data)); + return true; + } else { + return false; + } + } + open() { + if (this.isActive()) { + logger_default.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`); + return false; + } else { + const socketProtocols = [...protocols, ...this.consumer.subprotocols || []]; + logger_default.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`); + if (this.webSocket) { + this.uninstallEventHandlers(); + } + this.webSocket = new adapters_default.WebSocket(this.consumer.url, socketProtocols); + this.installEventHandlers(); + this.monitor.start(); + return true; + } + } + close({ allowReconnect } = { allowReconnect: true }) { + if (!allowReconnect) { + this.monitor.stop(); + } + if (this.isOpen()) { + return this.webSocket.close(); + } + } + reopen() { + logger_default.log(`Reopening WebSocket, current state is ${this.getState()}`); + if (this.isActive()) { + try { + return this.close(); + } catch (error2) { + logger_default.log("Failed to reopen WebSocket", error2); + } finally { + logger_default.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`); + setTimeout(this.open, this.constructor.reopenDelay); + } + } else { + return this.open(); + } + } + getProtocol() { + if (this.webSocket) { + return this.webSocket.protocol; + } + } + isOpen() { + return this.isState("open"); + } + isActive() { + return this.isState("open", "connecting"); + } + triedToReconnect() { + return this.monitor.reconnectAttempts > 0; + } + // Private + isProtocolSupported() { + return indexOf.call(supportedProtocols, this.getProtocol()) >= 0; + } + isState(...states) { + return indexOf.call(states, this.getState()) >= 0; + } + getState() { + if (this.webSocket) { + for (let state in adapters_default.WebSocket) { + if (adapters_default.WebSocket[state] === this.webSocket.readyState) { + return state.toLowerCase(); + } + } + } + return null; + } + installEventHandlers() { + for (let eventName in this.events) { + const handler = this.events[eventName].bind(this); + this.webSocket[`on${eventName}`] = handler; + } + } + uninstallEventHandlers() { + for (let eventName in this.events) { + this.webSocket[`on${eventName}`] = function() { + }; + } + } + }; + Connection.reopenDelay = 500; + Connection.prototype.events = { + message(event) { + if (!this.isProtocolSupported()) { + return; + } + const { identifier, message, reason, reconnect, type } = JSON.parse(event.data); + this.monitor.recordMessage(); + switch (type) { + case message_types.welcome: + if (this.triedToReconnect()) { + this.reconnectAttempted = true; + } + this.monitor.recordConnect(); + return this.subscriptions.reload(); + case message_types.disconnect: + logger_default.log(`Disconnecting. Reason: ${reason}`); + return this.close({ allowReconnect: reconnect }); + case message_types.ping: + return null; + case message_types.confirmation: + this.subscriptions.confirmSubscription(identifier); + if (this.reconnectAttempted) { + this.reconnectAttempted = false; + return this.subscriptions.notify(identifier, "connected", { reconnected: true }); + } else { + return this.subscriptions.notify(identifier, "connected", { reconnected: false }); + } + case message_types.rejection: + return this.subscriptions.reject(identifier); + default: + return this.subscriptions.notify(identifier, "received", message); + } + }, + open() { + logger_default.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`); + this.disconnected = false; + if (!this.isProtocolSupported()) { + logger_default.log("Protocol is unsupported. Stopping monitor and disconnecting."); + return this.close({ allowReconnect: false }); + } + }, + close(event) { + logger_default.log("WebSocket onclose event"); + if (this.disconnected) { + return; + } + this.disconnected = true; + this.monitor.recordDisconnect(); + return this.subscriptions.notifyAll("disconnected", { willAttemptReconnect: this.monitor.isRunning() }); + }, + error() { + logger_default.log("WebSocket onerror event"); + } + }; + connection_default = Connection; + } +}); + +// node_modules/@rails/actioncable/src/subscription.js +var extend, Subscription; +var init_subscription = __esm({ + "node_modules/@rails/actioncable/src/subscription.js"() { + extend = function(object, properties) { + if (properties != null) { + for (let key in properties) { + const value = properties[key]; + object[key] = value; + } + } + return object; + }; + Subscription = class { + constructor(consumer2, params = {}, mixin) { + this.consumer = consumer2; + this.identifier = JSON.stringify(params); + extend(this, mixin); + } + // Perform a channel action with the optional data passed as an attribute + perform(action, data = {}) { + data.action = action; + return this.send(data); + } + send(data) { + return this.consumer.send({ command: "message", identifier: this.identifier, data: JSON.stringify(data) }); + } + unsubscribe() { + return this.consumer.subscriptions.remove(this); + } + }; + } +}); + +// node_modules/@rails/actioncable/src/subscription_guarantor.js +var SubscriptionGuarantor, subscription_guarantor_default; +var init_subscription_guarantor = __esm({ + "node_modules/@rails/actioncable/src/subscription_guarantor.js"() { + init_logger(); + SubscriptionGuarantor = class { + constructor(subscriptions) { + this.subscriptions = subscriptions; + this.pendingSubscriptions = []; + } + guarantee(subscription) { + if (this.pendingSubscriptions.indexOf(subscription) == -1) { + logger_default.log(`SubscriptionGuarantor guaranteeing ${subscription.identifier}`); + this.pendingSubscriptions.push(subscription); + } else { + logger_default.log(`SubscriptionGuarantor already guaranteeing ${subscription.identifier}`); + } + this.startGuaranteeing(); + } + forget(subscription) { + logger_default.log(`SubscriptionGuarantor forgetting ${subscription.identifier}`); + this.pendingSubscriptions = this.pendingSubscriptions.filter((s) => s !== subscription); + } + startGuaranteeing() { + this.stopGuaranteeing(); + this.retrySubscribing(); + } + stopGuaranteeing() { + clearTimeout(this.retryTimeout); + } + retrySubscribing() { + this.retryTimeout = setTimeout( + () => { + if (this.subscriptions && typeof this.subscriptions.subscribe === "function") { + this.pendingSubscriptions.map((subscription) => { + logger_default.log(`SubscriptionGuarantor resubscribing ${subscription.identifier}`); + this.subscriptions.subscribe(subscription); + }); + } + }, + 500 + ); + } + }; + subscription_guarantor_default = SubscriptionGuarantor; + } +}); + +// node_modules/@rails/actioncable/src/subscriptions.js +var Subscriptions; +var init_subscriptions = __esm({ + "node_modules/@rails/actioncable/src/subscriptions.js"() { + init_logger(); + init_subscription(); + init_subscription_guarantor(); + Subscriptions = class { + constructor(consumer2) { + this.consumer = consumer2; + this.guarantor = new subscription_guarantor_default(this); + this.subscriptions = []; + } + create(channelName, mixin) { + const channel = channelName; + const params = typeof channel === "object" ? channel : { channel }; + const subscription = new Subscription(this.consumer, params, mixin); + return this.add(subscription); + } + // Private + add(subscription) { + this.subscriptions.push(subscription); + this.consumer.ensureActiveConnection(); + this.notify(subscription, "initialized"); + this.subscribe(subscription); + return subscription; + } + remove(subscription) { + this.forget(subscription); + if (!this.findAll(subscription.identifier).length) { + this.sendCommand(subscription, "unsubscribe"); + } + return subscription; + } + reject(identifier) { + return this.findAll(identifier).map((subscription) => { + this.forget(subscription); + this.notify(subscription, "rejected"); + return subscription; + }); + } + forget(subscription) { + this.guarantor.forget(subscription); + this.subscriptions = this.subscriptions.filter((s) => s !== subscription); + return subscription; + } + findAll(identifier) { + return this.subscriptions.filter((s) => s.identifier === identifier); + } + reload() { + return this.subscriptions.map((subscription) => this.subscribe(subscription)); + } + notifyAll(callbackName, ...args) { + return this.subscriptions.map((subscription) => this.notify(subscription, callbackName, ...args)); + } + notify(subscription, callbackName, ...args) { + let subscriptions; + if (typeof subscription === "string") { + subscriptions = this.findAll(subscription); + } else { + subscriptions = [subscription]; + } + return subscriptions.map((subscription2) => typeof subscription2[callbackName] === "function" ? subscription2[callbackName](...args) : void 0); + } + subscribe(subscription) { + if (this.sendCommand(subscription, "subscribe")) { + this.guarantor.guarantee(subscription); + } + } + confirmSubscription(identifier) { + logger_default.log(`Subscription confirmed ${identifier}`); + this.findAll(identifier).map((subscription) => this.guarantor.forget(subscription)); + } + sendCommand(subscription, command) { + const { identifier } = subscription; + return this.consumer.send({ command, identifier }); + } + }; + } +}); + +// node_modules/@rails/actioncable/src/consumer.js +function createWebSocketURL(url) { + if (typeof url === "function") { + url = url(); + } + if (url && !/^wss?:/i.test(url)) { + const a = document.createElement("a"); + a.href = url; + a.href = a.href; + a.protocol = a.protocol.replace("http", "ws"); + return a.href; + } else { + return url; + } +} +var Consumer; +var init_consumer = __esm({ + "node_modules/@rails/actioncable/src/consumer.js"() { + init_connection(); + init_subscriptions(); + Consumer = class { + constructor(url) { + this._url = url; + this.subscriptions = new Subscriptions(this); + this.connection = new connection_default(this); + this.subprotocols = []; + } + get url() { + return createWebSocketURL(this._url); + } + send(data) { + return this.connection.send(data); + } + connect() { + return this.connection.open(); + } + disconnect() { + return this.connection.close({ allowReconnect: false }); + } + ensureActiveConnection() { + if (!this.connection.isActive()) { + return this.connection.open(); + } + } + addSubProtocol(subprotocol) { + this.subprotocols = [...this.subprotocols, subprotocol]; + } + }; + } +}); + +// node_modules/@rails/actioncable/src/index.js +var src_exports = {}; +__export(src_exports, { + Connection: () => connection_default, + ConnectionMonitor: () => connection_monitor_default, + Consumer: () => Consumer, + INTERNAL: () => internal_default, + Subscription: () => Subscription, + SubscriptionGuarantor: () => subscription_guarantor_default, + Subscriptions: () => Subscriptions, + adapters: () => adapters_default, + createConsumer: () => createConsumer, + createWebSocketURL: () => createWebSocketURL, + getConfig: () => getConfig, + logger: () => logger_default +}); +function createConsumer(url = getConfig("url") || internal_default.default_mount_path) { + return new Consumer(url); +} +function getConfig(name) { + const element = document.head.querySelector(`meta[name='action-cable-${name}']`); + if (element) { + return element.getAttribute("content"); + } +} +var init_src = __esm({ + "node_modules/@rails/actioncable/src/index.js"() { + init_adapters(); + init_connection(); + init_connection_monitor(); + init_consumer(); + init_internal(); + init_logger(); + init_subscription(); + init_subscription_guarantor(); + init_subscriptions(); + } +}); + +// node_modules/@hotwired/turbo/dist/turbo.es2017-esm.js +var turbo_es2017_esm_exports = {}; +__export(turbo_es2017_esm_exports, { + FetchEnctype: () => FetchEnctype, + FetchMethod: () => FetchMethod, + FetchRequest: () => FetchRequest, + FetchResponse: () => FetchResponse, + FrameElement: () => FrameElement, + FrameLoadingStyle: () => FrameLoadingStyle, + FrameRenderer: () => FrameRenderer, + PageRenderer: () => PageRenderer, + PageSnapshot: () => PageSnapshot, + StreamActions: () => StreamActions, + StreamElement: () => StreamElement, + StreamSourceElement: () => StreamSourceElement, + cache: () => cache, + clearCache: () => clearCache, + config: () => config, + connectStreamSource: () => connectStreamSource, + disconnectStreamSource: () => disconnectStreamSource, + fetch: () => fetchWithTurboHeaders, + fetchEnctypeFromString: () => fetchEnctypeFromString, + fetchMethodFromString: () => fetchMethodFromString, + isSafe: () => isSafe, + morphBodyElements: () => morphBodyElements, + morphChildren: () => morphChildren, + morphElements: () => morphElements, + morphTurboFrameElements: () => morphTurboFrameElements, + navigator: () => navigator$1, + registerAdapter: () => registerAdapter, + renderStreamMessage: () => renderStreamMessage, + session: () => session, + setConfirmMethod: () => setConfirmMethod, + setFormMode: () => setFormMode, + setProgressBarDelay: () => setProgressBarDelay, + start: () => start, + visit: () => visit +}); +(function(prototype) { + if (typeof prototype.requestSubmit == "function") return; + prototype.requestSubmit = function(submitter2) { + if (submitter2) { + validateSubmitter(submitter2, this); + submitter2.click(); + } else { + submitter2 = document.createElement("input"); + submitter2.type = "submit"; + submitter2.hidden = true; + this.appendChild(submitter2); + submitter2.click(); + this.removeChild(submitter2); + } + }; + function validateSubmitter(submitter2, form) { + submitter2 instanceof HTMLElement || raise(TypeError, "parameter 1 is not of type 'HTMLElement'"); + submitter2.type == "submit" || raise(TypeError, "The specified element is not a submit button"); + submitter2.form == form || raise(DOMException, "The specified element is not owned by this form element", "NotFoundError"); + } + function raise(errorConstructor, message, name) { + throw new errorConstructor("Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".", name); + } +})(HTMLFormElement.prototype); +var submittersByForm = /* @__PURE__ */ new WeakMap(); +function findSubmitterFromClickTarget(target) { + const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null; + const candidate = element ? element.closest("input, button") : null; + return candidate?.type == "submit" ? candidate : null; +} +function clickCaptured(event) { + const submitter2 = findSubmitterFromClickTarget(event.target); + if (submitter2 && submitter2.form) { + submittersByForm.set(submitter2.form, submitter2); + } +} +(function() { + if ("submitter" in Event.prototype) return; + let prototype = window.Event.prototype; + if ("SubmitEvent" in window) { + const prototypeOfSubmitEvent = window.SubmitEvent.prototype; + if (/Apple Computer/.test(navigator.vendor) && !("submitter" in prototypeOfSubmitEvent)) { + prototype = prototypeOfSubmitEvent; + } else { + return; + } + } + addEventListener("click", clickCaptured, true); + Object.defineProperty(prototype, "submitter", { + get() { + if (this.type == "submit" && this.target instanceof HTMLFormElement) { + return submittersByForm.get(this.target); + } + } + }); +})(); +var FrameLoadingStyle = { + eager: "eager", + lazy: "lazy" +}; +var FrameElement = class _FrameElement extends HTMLElement { + static delegateConstructor = void 0; + loaded = Promise.resolve(); + static get observedAttributes() { + return ["disabled", "loading", "src"]; + } + constructor() { + super(); + this.delegate = new _FrameElement.delegateConstructor(this); + } + connectedCallback() { + this.delegate.connect(); + } + disconnectedCallback() { + this.delegate.disconnect(); + } + reload() { + return this.delegate.sourceURLReloaded(); + } + attributeChangedCallback(name) { + if (name == "loading") { + this.delegate.loadingStyleChanged(); + } else if (name == "src") { + this.delegate.sourceURLChanged(); + } else if (name == "disabled") { + this.delegate.disabledChanged(); + } + } + /** + * Gets the URL to lazily load source HTML from + */ + get src() { + return this.getAttribute("src"); + } + /** + * Sets the URL to lazily load source HTML from + */ + set src(value) { + if (value) { + this.setAttribute("src", value); + } else { + this.removeAttribute("src"); + } + } + /** + * Gets the refresh mode for the frame. + */ + get refresh() { + return this.getAttribute("refresh"); + } + /** + * Sets the refresh mode for the frame. + */ + set refresh(value) { + if (value) { + this.setAttribute("refresh", value); + } else { + this.removeAttribute("refresh"); + } + } + get shouldReloadWithMorph() { + return this.src && this.refresh === "morph"; + } + /** + * Determines if the element is loading + */ + get loading() { + return frameLoadingStyleFromString(this.getAttribute("loading") || ""); + } + /** + * Sets the value of if the element is loading + */ + set loading(value) { + if (value) { + this.setAttribute("loading", value); + } else { + this.removeAttribute("loading"); + } + } + /** + * Gets the disabled state of the frame. + * + * If disabled, no requests will be intercepted by the frame. + */ + get disabled() { + return this.hasAttribute("disabled"); + } + /** + * Sets the disabled state of the frame. + * + * If disabled, no requests will be intercepted by the frame. + */ + set disabled(value) { + if (value) { + this.setAttribute("disabled", ""); + } else { + this.removeAttribute("disabled"); + } + } + /** + * Gets the autoscroll state of the frame. + * + * If true, the frame will be scrolled into view automatically on update. + */ + get autoscroll() { + return this.hasAttribute("autoscroll"); + } + /** + * Sets the autoscroll state of the frame. + * + * If true, the frame will be scrolled into view automatically on update. + */ + set autoscroll(value) { + if (value) { + this.setAttribute("autoscroll", ""); + } else { + this.removeAttribute("autoscroll"); + } + } + /** + * Determines if the element has finished loading + */ + get complete() { + return !this.delegate.isLoading; + } + /** + * Gets the active state of the frame. + * + * If inactive, source changes will not be observed. + */ + get isActive() { + return this.ownerDocument === document && !this.isPreview; + } + /** + * Sets the active state of the frame. + * + * If inactive, source changes will not be observed. + */ + get isPreview() { + return this.ownerDocument?.documentElement?.hasAttribute("data-turbo-preview"); + } +}; +function frameLoadingStyleFromString(style) { + switch (style.toLowerCase()) { + case "lazy": + return FrameLoadingStyle.lazy; + default: + return FrameLoadingStyle.eager; + } +} +var drive = { + enabled: true, + progressBarDelay: 500, + unvisitableExtensions: /* @__PURE__ */ 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 activateScriptElement(element) { + if (element.getAttribute("data-turbo-eval") == "false") { + return element; + } else { + const createdScriptElement = document.createElement("script"); + const cspNonce = getCspNonce(); + if (cspNonce) { + createdScriptElement.nonce = cspNonce; + } + createdScriptElement.textContent = element.textContent; + createdScriptElement.async = false; + copyElementAttributes(createdScriptElement, element); + return createdScriptElement; + } +} +function copyElementAttributes(destinationElement, sourceElement) { + for (const { name, value } of sourceElement.attributes) { + destinationElement.setAttribute(name, value); + } +} +function createDocumentFragment(html) { + const template = document.createElement("template"); + template.innerHTML = html; + return template.content; +} +function dispatch(eventName, { target, cancelable, detail } = {}) { + const event = new CustomEvent(eventName, { + cancelable, + bubbles: true, + composed: true, + detail + }); + if (target && target.isConnected) { + target.dispatchEvent(event); + } else { + document.documentElement.dispatchEvent(event); + } + return event; +} +function cancelEvent(event) { + event.preventDefault(); + event.stopImmediatePropagation(); +} +function nextRepaint() { + if (document.visibilityState === "hidden") { + return nextEventLoopTick(); + } else { + return nextAnimationFrame(); + } +} +function nextAnimationFrame() { + return new Promise((resolve) => requestAnimationFrame(() => resolve())); +} +function nextEventLoopTick() { + return new Promise((resolve) => setTimeout(() => resolve(), 0)); +} +function nextMicrotask() { + return Promise.resolve(); +} +function parseHTMLDocument(html = "") { + return new DOMParser().parseFromString(html, "text/html"); +} +function unindent(strings, ...values) { + const lines = interpolate(strings, values).replace(/^\n/, "").split("\n"); + const match = lines[0].match(/^\s+/); + const indent = match ? match[0].length : 0; + return lines.map((line) => line.slice(indent)).join("\n"); +} +function interpolate(strings, values) { + return strings.reduce((result, string, i) => { + const value = values[i] == void 0 ? "" : values[i]; + return result + string + value; + }, ""); +} +function uuid() { + return Array.from({ length: 36 }).map((_, i) => { + if (i == 8 || i == 13 || i == 18 || i == 23) { + return "-"; + } else if (i == 14) { + return "4"; + } else if (i == 19) { + return (Math.floor(Math.random() * 4) + 8).toString(16); + } else { + return Math.floor(Math.random() * 15).toString(16); + } + }).join(""); +} +function getAttribute(attributeName, ...elements) { + for (const value of elements.map((element) => element?.getAttribute(attributeName))) { + if (typeof value == "string") return value; + } + return null; +} +function hasAttribute(attributeName, ...elements) { + return elements.some((element) => element && element.hasAttribute(attributeName)); +} +function markAsBusy(...elements) { + for (const element of elements) { + if (element.localName == "turbo-frame") { + element.setAttribute("busy", ""); + } + element.setAttribute("aria-busy", "true"); + } +} +function clearBusyState(...elements) { + for (const element of elements) { + if (element.localName == "turbo-frame") { + element.removeAttribute("busy"); + } + element.removeAttribute("aria-busy"); + } +} +function waitForLoad(element, timeoutInMilliseconds = 2e3) { + return new Promise((resolve) => { + const onComplete = () => { + element.removeEventListener("error", onComplete); + element.removeEventListener("load", onComplete); + resolve(); + }; + element.addEventListener("load", onComplete, { once: true }); + element.addEventListener("error", onComplete, { once: true }); + setTimeout(resolve, timeoutInMilliseconds); + }); +} +function getHistoryMethodForAction(action) { + switch (action) { + case "replace": + return history.replaceState; + case "advance": + case "restore": + return history.pushState; + } +} +function isAction(action) { + return action == "advance" || action == "replace" || action == "restore"; +} +function getVisitAction(...elements) { + const action = getAttribute("data-turbo-action", ...elements); + return isAction(action) ? action : null; +} +function getMetaElement(name) { + return document.querySelector(`meta[name="${name}"]`); +} +function getMetaContent(name) { + const element = getMetaElement(name); + return element && element.content; +} +function getCspNonce() { + const element = getMetaElement("csp-nonce"); + if (element) { + const { nonce, content } = element; + return nonce == "" ? content : nonce; + } +} +function setMetaContent(name, content) { + let element = getMetaElement(name); + if (!element) { + element = document.createElement("meta"); + element.setAttribute("name", name); + document.head.appendChild(element); + } + element.setAttribute("content", content); + return element; +} +function findClosestRecursively(element, selector) { + if (element instanceof Element) { + return element.closest(selector) || findClosestRecursively(element.assignedSlot || element.getRootNode()?.host, selector); + } +} +function elementIsFocusable(element) { + const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])"; + return !!element && element.closest(inertDisabledOrHidden) == null && typeof element.focus == "function"; +} +function queryAutofocusableElement(elementOrDocumentFragment) { + return Array.from(elementOrDocumentFragment.querySelectorAll("[autofocus]")).find(elementIsFocusable); +} +async function around(callback, reader) { + const before = reader(); + callback(); + await nextAnimationFrame(); + const after = reader(); + return [before, after]; +} +function doesNotTargetIFrame(name) { + if (name === "_blank") { + return false; + } else if (name) { + for (const element of document.getElementsByName(name)) { + if (element instanceof HTMLIFrameElement) return false; + } + return true; + } else { + return true; + } +} +function findLinkFromClickTarget(target) { + const link = findClosestRecursively(target, "a[href], a[xlink\\:href]"); + if (!link) return null; + if (link.hasAttribute("download")) return null; + if (link.hasAttribute("target") && link.target !== "_self") return null; + return link; +} +function getLocationForLink(link) { + return expandURL(link.getAttribute("href") || ""); +} +function debounce(fn, delay) { + let timeoutId = null; + return (...args) => { + const callback = () => fn.apply(this, args); + clearTimeout(timeoutId); + timeoutId = setTimeout(callback, delay); + }; +} +var submitter = { + "aria-disabled": { + beforeSubmit: (submitter2) => { + submitter2.setAttribute("aria-disabled", "true"); + submitter2.addEventListener("click", cancelEvent); + }, + afterSubmit: (submitter2) => { + submitter2.removeAttribute("aria-disabled"); + submitter2.removeEventListener("click", cancelEvent); + } + }, + "disabled": { + beforeSubmit: (submitter2) => submitter2.disabled = true, + afterSubmit: (submitter2) => submitter2.disabled = false + } +}; +var Config = class { + #submitter = null; + constructor(config2) { + Object.assign(this, config2); + } + get submitter() { + return this.#submitter; + } + set submitter(value) { + this.#submitter = submitter[value] || value; + } +}; +var forms = new Config({ + mode: "on", + submitter: "disabled" +}); +var config = { + drive, + forms +}; +function expandURL(locatable) { + return new URL(locatable.toString(), document.baseURI); +} +function getAnchor(url) { + let anchorMatch; + if (url.hash) { + return url.hash.slice(1); + } else if (anchorMatch = url.href.match(/#(.*)$/)) { + return anchorMatch[1]; + } +} +function getAction$1(form, submitter2) { + const action = submitter2?.getAttribute("formaction") || form.getAttribute("action") || form.action; + return expandURL(action); +} +function getExtension(url) { + return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || ""; +} +function isPrefixedBy(baseURL, url) { + const prefix = addTrailingSlash(url.origin + url.pathname); + return addTrailingSlash(baseURL.href) === prefix || baseURL.href.startsWith(prefix); +} +function locationIsVisitable(location2, rootLocation) { + return isPrefixedBy(location2, rootLocation) && !config.drive.unvisitableExtensions.has(getExtension(location2)); +} +function getRequestURL(url) { + const anchor = getAnchor(url); + return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href; +} +function toCacheKey(url) { + return getRequestURL(url); +} +function urlsAreEqual(left, right) { + return expandURL(left).href == expandURL(right).href; +} +function getPathComponents(url) { + return url.pathname.split("/").slice(1); +} +function getLastPathComponent(url) { + return getPathComponents(url).slice(-1)[0]; +} +function addTrailingSlash(value) { + return value.endsWith("/") ? value : value + "/"; +} +var FetchResponse = class { + constructor(response) { + this.response = response; + } + get succeeded() { + return this.response.ok; + } + get failed() { + return !this.succeeded; + } + get clientError() { + return this.statusCode >= 400 && this.statusCode <= 499; + } + get serverError() { + return this.statusCode >= 500 && this.statusCode <= 599; + } + get redirected() { + return this.response.redirected; + } + get location() { + return expandURL(this.response.url); + } + get isHTML() { + return this.contentType && this.contentType.match(/^(?:text\/([^\s;,]+\b)?html|application\/xhtml\+xml)\b/); + } + get statusCode() { + return this.response.status; + } + get contentType() { + return this.header("Content-Type"); + } + get responseText() { + return this.response.clone().text(); + } + get responseHTML() { + if (this.isHTML) { + return this.response.clone().text(); + } else { + return Promise.resolve(void 0); + } + } + header(name) { + return this.response.headers.get(name); + } +}; +var LimitedSet = class extends Set { + constructor(maxSize) { + super(); + this.maxSize = maxSize; + } + add(value) { + if (this.size >= this.maxSize) { + const iterator = this.values(); + const oldestValue = iterator.next().value; + this.delete(oldestValue); + } + super.add(value); + } +}; +var recentRequests = new LimitedSet(20); +function fetchWithTurboHeaders(url, options = {}) { + const modifiedHeaders = new Headers(options.headers || {}); + const requestUID = uuid(); + recentRequests.add(requestUID); + modifiedHeaders.append("X-Turbo-Request-Id", requestUID); + return window.fetch(url, { + ...options, + headers: modifiedHeaders + }); +} +function fetchMethodFromString(method) { + switch (method.toLowerCase()) { + case "get": + return FetchMethod.get; + case "post": + return FetchMethod.post; + case "put": + return FetchMethod.put; + case "patch": + return FetchMethod.patch; + case "delete": + return FetchMethod.delete; + } +} +var FetchMethod = { + get: "get", + post: "post", + put: "put", + patch: "patch", + delete: "delete" +}; +function fetchEnctypeFromString(encoding) { + switch (encoding.toLowerCase()) { + case FetchEnctype.multipart: + return FetchEnctype.multipart; + case FetchEnctype.plain: + return FetchEnctype.plain; + default: + return FetchEnctype.urlEncoded; + } +} +var FetchEnctype = { + urlEncoded: "application/x-www-form-urlencoded", + multipart: "multipart/form-data", + plain: "text/plain" +}; +var FetchRequest = class { + abortController = new AbortController(); + #resolveRequestPromise = (_value) => { + }; + constructor(delegate, method, location2, requestBody = new URLSearchParams(), target = null, enctype = FetchEnctype.urlEncoded) { + const [url, body] = buildResourceAndBody(expandURL(location2), method, requestBody, enctype); + this.delegate = delegate; + this.url = url; + this.target = target; + this.fetchOptions = { + credentials: "same-origin", + redirect: "follow", + method: method.toUpperCase(), + headers: { ...this.defaultHeaders }, + body, + signal: this.abortSignal, + referrer: this.delegate.referrer?.href + }; + this.enctype = enctype; + } + get method() { + return this.fetchOptions.method; + } + set method(value) { + const fetchBody = this.isSafe ? this.url.searchParams : this.fetchOptions.body || new FormData(); + const fetchMethod = fetchMethodFromString(value) || FetchMethod.get; + this.url.search = ""; + const [url, body] = buildResourceAndBody(this.url, fetchMethod, fetchBody, this.enctype); + this.url = url; + this.fetchOptions.body = body; + this.fetchOptions.method = fetchMethod.toUpperCase(); + } + get headers() { + return this.fetchOptions.headers; + } + set headers(value) { + this.fetchOptions.headers = value; + } + get body() { + if (this.isSafe) { + return this.url.searchParams; + } else { + return this.fetchOptions.body; + } + } + set body(value) { + this.fetchOptions.body = value; + } + get location() { + return this.url; + } + get params() { + return this.url.searchParams; + } + get entries() { + return this.body ? Array.from(this.body.entries()) : []; + } + cancel() { + this.abortController.abort(); + } + async perform() { + const { fetchOptions } = this; + this.delegate.prepareRequest(this); + const event = await this.#allowRequestToBeIntercepted(fetchOptions); + try { + this.delegate.requestStarted(this); + if (event.detail.fetchRequest) { + this.response = event.detail.fetchRequest.response; + } else { + this.response = fetchWithTurboHeaders(this.url.href, fetchOptions); + } + const response = await this.response; + return await this.receive(response); + } catch (error2) { + if (error2.name !== "AbortError") { + if (this.#willDelegateErrorHandling(error2)) { + this.delegate.requestErrored(this, error2); + } + throw error2; + } + } finally { + this.delegate.requestFinished(this); + } + } + async receive(response) { + const fetchResponse = new FetchResponse(response); + const event = dispatch("turbo:before-fetch-response", { + cancelable: true, + detail: { fetchResponse }, + target: this.target + }); + if (event.defaultPrevented) { + this.delegate.requestPreventedHandlingResponse(this, fetchResponse); + } else if (fetchResponse.succeeded) { + this.delegate.requestSucceededWithResponse(this, fetchResponse); + } else { + this.delegate.requestFailedWithResponse(this, fetchResponse); + } + return fetchResponse; + } + get defaultHeaders() { + return { + Accept: "text/html, application/xhtml+xml" + }; + } + get isSafe() { + return isSafe(this.method); + } + get abortSignal() { + return this.abortController.signal; + } + acceptResponseType(mimeType) { + this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", "); + } + async #allowRequestToBeIntercepted(fetchOptions) { + const requestInterception = new Promise((resolve) => this.#resolveRequestPromise = resolve); + const event = dispatch("turbo:before-fetch-request", { + cancelable: true, + detail: { + fetchOptions, + url: this.url, + resume: this.#resolveRequestPromise + }, + target: this.target + }); + this.url = event.detail.url; + if (event.defaultPrevented) await requestInterception; + return event; + } + #willDelegateErrorHandling(error2) { + const event = dispatch("turbo:fetch-request-error", { + target: this.target, + cancelable: true, + detail: { request: this, error: error2 } + }); + return !event.defaultPrevented; + } +}; +function isSafe(fetchMethod) { + return fetchMethodFromString(fetchMethod) == FetchMethod.get; +} +function buildResourceAndBody(resource, method, requestBody, enctype) { + const searchParams = Array.from(requestBody).length > 0 ? new URLSearchParams(entriesExcludingFiles(requestBody)) : resource.searchParams; + if (isSafe(method)) { + return [mergeIntoURLSearchParams(resource, searchParams), null]; + } else if (enctype == FetchEnctype.urlEncoded) { + return [resource, searchParams]; + } else { + return [resource, requestBody]; + } +} +function entriesExcludingFiles(requestBody) { + const entries = []; + for (const [name, value] of requestBody) { + if (value instanceof File) continue; + else entries.push([name, value]); + } + return entries; +} +function mergeIntoURLSearchParams(url, requestBody) { + const searchParams = new URLSearchParams(entriesExcludingFiles(requestBody)); + url.search = searchParams.toString(); + return url; +} +var AppearanceObserver = class { + started = false; + constructor(delegate, element) { + this.delegate = delegate; + this.element = element; + this.intersectionObserver = new IntersectionObserver(this.intersect); + } + start() { + if (!this.started) { + this.started = true; + this.intersectionObserver.observe(this.element); + } + } + stop() { + if (this.started) { + this.started = false; + this.intersectionObserver.unobserve(this.element); + } + } + intersect = (entries) => { + const lastEntry = entries.slice(-1)[0]; + if (lastEntry?.isIntersecting) { + this.delegate.elementAppearedInViewport(this.element); + } + }; +}; +var StreamMessage = class { + static contentType = "text/vnd.turbo-stream.html"; + static wrap(message) { + if (typeof message == "string") { + return new this(createDocumentFragment(message)); + } else { + return message; + } + } + constructor(fragment) { + this.fragment = importStreamElements(fragment); + } +}; +function importStreamElements(fragment) { + for (const element of fragment.querySelectorAll("turbo-stream")) { + const streamElement = document.importNode(element, true); + for (const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")) { + inertScriptElement.replaceWith(activateScriptElement(inertScriptElement)); + } + element.replaceWith(streamElement); + } + return fragment; +} +var PREFETCH_DELAY = 100; +var PrefetchCache = class { + #prefetchTimeout = null; + #prefetched = null; + get(url) { + if (this.#prefetched && this.#prefetched.url === url && this.#prefetched.expire > Date.now()) { + return this.#prefetched.request; + } + } + setLater(url, request, ttl) { + this.clear(); + this.#prefetchTimeout = setTimeout(() => { + request.perform(); + this.set(url, request, ttl); + this.#prefetchTimeout = null; + }, PREFETCH_DELAY); + } + set(url, request, ttl) { + this.#prefetched = { url, request, expire: new Date((/* @__PURE__ */ new Date()).getTime() + ttl) }; + } + clear() { + if (this.#prefetchTimeout) clearTimeout(this.#prefetchTimeout); + this.#prefetched = null; + } +}; +var cacheTtl = 10 * 1e3; +var prefetchCache = new PrefetchCache(); +var FormSubmissionState = { + initialized: "initialized", + requesting: "requesting", + waiting: "waiting", + receiving: "receiving", + stopping: "stopping", + stopped: "stopped" +}; +var FormSubmission = class _FormSubmission { + state = FormSubmissionState.initialized; + static confirmMethod(message) { + return Promise.resolve(confirm(message)); + } + constructor(delegate, formElement, submitter2, mustRedirect = false) { + const method = getMethod(formElement, submitter2); + const action = getAction(getFormAction(formElement, submitter2), method); + const body = buildFormData(formElement, submitter2); + const enctype = getEnctype(formElement, submitter2); + this.delegate = delegate; + this.formElement = formElement; + this.submitter = submitter2; + this.fetchRequest = new FetchRequest(this, method, action, body, formElement, enctype); + this.mustRedirect = mustRedirect; + } + get method() { + return this.fetchRequest.method; + } + set method(value) { + this.fetchRequest.method = value; + } + get action() { + return this.fetchRequest.url.toString(); + } + set action(value) { + this.fetchRequest.url = expandURL(value); + } + get body() { + return this.fetchRequest.body; + } + get enctype() { + return this.fetchRequest.enctype; + } + get isSafe() { + return this.fetchRequest.isSafe; + } + get location() { + return this.fetchRequest.url; + } + // The submission process + async start() { + const { initialized, requesting } = FormSubmissionState; + const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement); + if (typeof confirmationMessage === "string") { + const confirmMethod = typeof config.forms.confirm === "function" ? config.forms.confirm : _FormSubmission.confirmMethod; + const answer = await confirmMethod(confirmationMessage, this.formElement, this.submitter); + if (!answer) { + return; + } + } + if (this.state == initialized) { + this.state = requesting; + return this.fetchRequest.perform(); + } + } + stop() { + const { stopping, stopped } = FormSubmissionState; + if (this.state != stopping && this.state != stopped) { + this.state = stopping; + this.fetchRequest.cancel(); + return true; + } + } + // Fetch request delegate + prepareRequest(request) { + if (!request.isSafe) { + const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token"); + if (token) { + request.headers["X-CSRF-Token"] = token; + } + } + if (this.requestAcceptsTurboStreamResponse(request)) { + request.acceptResponseType(StreamMessage.contentType); + } + } + requestStarted(_request) { + this.state = FormSubmissionState.waiting; + if (this.submitter) config.forms.submitter.beforeSubmit(this.submitter); + this.setSubmitsWith(); + markAsBusy(this.formElement); + dispatch("turbo:submit-start", { + target: this.formElement, + detail: { formSubmission: this } + }); + this.delegate.formSubmissionStarted(this); + } + requestPreventedHandlingResponse(request, response) { + prefetchCache.clear(); + this.result = { success: response.succeeded, fetchResponse: response }; + } + requestSucceededWithResponse(request, response) { + if (response.clientError || response.serverError) { + this.delegate.formSubmissionFailedWithResponse(this, response); + return; + } + prefetchCache.clear(); + if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) { + const error2 = new Error("Form responses must redirect to another location"); + this.delegate.formSubmissionErrored(this, error2); + } else { + this.state = FormSubmissionState.receiving; + this.result = { success: true, fetchResponse: response }; + this.delegate.formSubmissionSucceededWithResponse(this, response); + } + } + requestFailedWithResponse(request, response) { + this.result = { success: false, fetchResponse: response }; + this.delegate.formSubmissionFailedWithResponse(this, response); + } + requestErrored(request, error2) { + this.result = { success: false, error: error2 }; + this.delegate.formSubmissionErrored(this, error2); + } + requestFinished(_request) { + this.state = FormSubmissionState.stopped; + if (this.submitter) config.forms.submitter.afterSubmit(this.submitter); + this.resetSubmitterText(); + clearBusyState(this.formElement); + dispatch("turbo:submit-end", { + target: this.formElement, + detail: { formSubmission: this, ...this.result } + }); + this.delegate.formSubmissionFinished(this); + } + // Private + setSubmitsWith() { + if (!this.submitter || !this.submitsWith) return; + if (this.submitter.matches("button")) { + this.originalSubmitText = this.submitter.innerHTML; + this.submitter.innerHTML = this.submitsWith; + } else if (this.submitter.matches("input")) { + const input = this.submitter; + this.originalSubmitText = input.value; + input.value = this.submitsWith; + } + } + resetSubmitterText() { + if (!this.submitter || !this.originalSubmitText) return; + if (this.submitter.matches("button")) { + this.submitter.innerHTML = this.originalSubmitText; + } else if (this.submitter.matches("input")) { + const input = this.submitter; + input.value = this.originalSubmitText; + } + } + requestMustRedirect(request) { + return !request.isSafe && this.mustRedirect; + } + requestAcceptsTurboStreamResponse(request) { + return !request.isSafe || hasAttribute("data-turbo-stream", this.submitter, this.formElement); + } + get submitsWith() { + return this.submitter?.getAttribute("data-turbo-submits-with"); + } +}; +function buildFormData(formElement, submitter2) { + const formData = new FormData(formElement); + const name = submitter2?.getAttribute("name"); + const value = submitter2?.getAttribute("value"); + if (name) { + formData.append(name, value || ""); + } + return formData; +} +function getCookieValue(cookieName) { + if (cookieName != null) { + const cookies = document.cookie ? document.cookie.split("; ") : []; + const cookie = cookies.find((cookie2) => cookie2.startsWith(cookieName)); + if (cookie) { + const value = cookie.split("=").slice(1).join("="); + return value ? decodeURIComponent(value) : void 0; + } + } +} +function responseSucceededWithoutRedirect(response) { + return response.statusCode == 200 && !response.redirected; +} +function getFormAction(formElement, submitter2) { + const formElementAction = typeof formElement.action === "string" ? formElement.action : null; + if (submitter2?.hasAttribute("formaction")) { + return submitter2.getAttribute("formaction") || ""; + } else { + return formElement.getAttribute("action") || formElementAction || ""; + } +} +function getAction(formAction, fetchMethod) { + const action = expandURL(formAction); + if (isSafe(fetchMethod)) { + action.search = ""; + } + return action; +} +function getMethod(formElement, submitter2) { + const method = submitter2?.getAttribute("formmethod") || formElement.getAttribute("method") || ""; + return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get; +} +function getEnctype(formElement, submitter2) { + return fetchEnctypeFromString(submitter2?.getAttribute("formenctype") || formElement.enctype); +} +var Snapshot = class { + constructor(element) { + this.element = element; + } + get activeElement() { + return this.element.ownerDocument.activeElement; + } + get children() { + return [...this.element.children]; + } + hasAnchor(anchor) { + return this.getElementForAnchor(anchor) != null; + } + getElementForAnchor(anchor) { + return anchor ? this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`) : null; + } + get isConnected() { + return this.element.isConnected; + } + get firstAutofocusableElement() { + return queryAutofocusableElement(this.element); + } + get permanentElements() { + return queryPermanentElementsAll(this.element); + } + getPermanentElementById(id) { + return getPermanentElementById(this.element, id); + } + getPermanentElementMapForSnapshot(snapshot) { + const permanentElementMap = {}; + for (const currentPermanentElement of this.permanentElements) { + const { id } = currentPermanentElement; + const newPermanentElement = snapshot.getPermanentElementById(id); + if (newPermanentElement) { + permanentElementMap[id] = [currentPermanentElement, newPermanentElement]; + } + } + return permanentElementMap; + } +}; +function getPermanentElementById(node, id) { + return node.querySelector(`#${id}[data-turbo-permanent]`); +} +function queryPermanentElementsAll(node) { + return node.querySelectorAll("[id][data-turbo-permanent]"); +} +var FormSubmitObserver = class { + started = false; + constructor(delegate, eventTarget) { + this.delegate = delegate; + this.eventTarget = eventTarget; + } + start() { + if (!this.started) { + this.eventTarget.addEventListener("submit", this.submitCaptured, true); + this.started = true; + } + } + stop() { + if (this.started) { + this.eventTarget.removeEventListener("submit", this.submitCaptured, true); + this.started = false; + } + } + submitCaptured = () => { + this.eventTarget.removeEventListener("submit", this.submitBubbled, false); + this.eventTarget.addEventListener("submit", this.submitBubbled, false); + }; + submitBubbled = (event) => { + if (!event.defaultPrevented) { + const form = event.target instanceof HTMLFormElement ? event.target : void 0; + const submitter2 = event.submitter || void 0; + if (form && submissionDoesNotDismissDialog(form, submitter2) && submissionDoesNotTargetIFrame(form, submitter2) && this.delegate.willSubmitForm(form, submitter2)) { + event.preventDefault(); + event.stopImmediatePropagation(); + this.delegate.formSubmitted(form, submitter2); + } + } + }; +}; +function submissionDoesNotDismissDialog(form, submitter2) { + const method = submitter2?.getAttribute("formmethod") || form.getAttribute("method"); + return method != "dialog"; +} +function submissionDoesNotTargetIFrame(form, submitter2) { + const target = submitter2?.getAttribute("formtarget") || form.getAttribute("target"); + return doesNotTargetIFrame(target); +} +var View = class { + #resolveRenderPromise = (_value) => { + }; + #resolveInterceptionPromise = (_value) => { + }; + constructor(delegate, element) { + this.delegate = delegate; + this.element = element; + } + // Scrolling + scrollToAnchor(anchor) { + const element = this.snapshot.getElementForAnchor(anchor); + if (element) { + this.focusElement(element); + this.scrollToElement(element); + } else { + this.scrollToPosition({ x: 0, y: 0 }); + } + } + scrollToAnchorFromLocation(location2) { + this.scrollToAnchor(getAnchor(location2)); + } + scrollToElement(element) { + element.scrollIntoView(); + } + focusElement(element) { + if (element instanceof HTMLElement) { + if (element.hasAttribute("tabindex")) { + element.focus(); + } else { + element.setAttribute("tabindex", "-1"); + element.focus(); + element.removeAttribute("tabindex"); + } + } + } + scrollToPosition({ x, y }) { + this.scrollRoot.scrollTo(x, y); + } + scrollToTop() { + this.scrollToPosition({ x: 0, y: 0 }); + } + get scrollRoot() { + return window; + } + // Rendering + async render(renderer) { + const { isPreview, shouldRender, willRender, newSnapshot: snapshot } = renderer; + const shouldInvalidate = willRender; + if (shouldRender) { + try { + this.renderPromise = new Promise((resolve) => this.#resolveRenderPromise = resolve); + this.renderer = renderer; + await this.prepareToRenderSnapshot(renderer); + const renderInterception = new Promise((resolve) => this.#resolveInterceptionPromise = resolve); + const options = { resume: this.#resolveInterceptionPromise, render: this.renderer.renderElement, renderMethod: this.renderer.renderMethod }; + const immediateRender = this.delegate.allowsImmediateRender(snapshot, options); + if (!immediateRender) await renderInterception; + await this.renderSnapshot(renderer); + this.delegate.viewRenderedSnapshot(snapshot, isPreview, this.renderer.renderMethod); + this.delegate.preloadOnLoadLinksForView(this.element); + this.finishRenderingSnapshot(renderer); + } finally { + delete this.renderer; + this.#resolveRenderPromise(void 0); + delete this.renderPromise; + } + } else if (shouldInvalidate) { + this.invalidate(renderer.reloadReason); + } + } + invalidate(reason) { + this.delegate.viewInvalidated(reason); + } + async prepareToRenderSnapshot(renderer) { + this.markAsPreview(renderer.isPreview); + await renderer.prepareToRender(); + } + markAsPreview(isPreview) { + if (isPreview) { + this.element.setAttribute("data-turbo-preview", ""); + } else { + this.element.removeAttribute("data-turbo-preview"); + } + } + markVisitDirection(direction) { + this.element.setAttribute("data-turbo-visit-direction", direction); + } + unmarkVisitDirection() { + this.element.removeAttribute("data-turbo-visit-direction"); + } + async renderSnapshot(renderer) { + await renderer.render(); + } + finishRenderingSnapshot(renderer) { + renderer.finishRendering(); + } +}; +var FrameView = class extends View { + missing() { + this.element.innerHTML = `Content missing`; + } + get snapshot() { + return new Snapshot(this.element); + } +}; +var LinkInterceptor = class { + constructor(delegate, element) { + this.delegate = delegate; + this.element = element; + } + start() { + this.element.addEventListener("click", this.clickBubbled); + document.addEventListener("turbo:click", this.linkClicked); + document.addEventListener("turbo:before-visit", this.willVisit); + } + stop() { + this.element.removeEventListener("click", this.clickBubbled); + document.removeEventListener("turbo:click", this.linkClicked); + document.removeEventListener("turbo:before-visit", this.willVisit); + } + clickBubbled = (event) => { + if (this.clickEventIsSignificant(event)) { + this.clickEvent = event; + } else { + delete this.clickEvent; + } + }; + linkClicked = (event) => { + if (this.clickEvent && this.clickEventIsSignificant(event)) { + if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url, event.detail.originalEvent)) { + this.clickEvent.preventDefault(); + event.preventDefault(); + this.delegate.linkClickIntercepted(event.target, event.detail.url, event.detail.originalEvent); + } + } + delete this.clickEvent; + }; + willVisit = (_event) => { + delete this.clickEvent; + }; + clickEventIsSignificant(event) { + const target = event.composed ? event.target?.parentElement : event.target; + const element = findLinkFromClickTarget(target) || target; + return element instanceof Element && element.closest("turbo-frame, html") == this.element; + } +}; +var LinkClickObserver = class { + started = false; + constructor(delegate, eventTarget) { + this.delegate = delegate; + this.eventTarget = eventTarget; + } + start() { + if (!this.started) { + this.eventTarget.addEventListener("click", this.clickCaptured, true); + this.started = true; + } + } + stop() { + if (this.started) { + this.eventTarget.removeEventListener("click", this.clickCaptured, true); + this.started = false; + } + } + clickCaptured = () => { + this.eventTarget.removeEventListener("click", this.clickBubbled, false); + this.eventTarget.addEventListener("click", this.clickBubbled, false); + }; + clickBubbled = (event) => { + if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) { + const target = event.composedPath && event.composedPath()[0] || event.target; + const link = findLinkFromClickTarget(target); + if (link && doesNotTargetIFrame(link.target)) { + const location2 = getLocationForLink(link); + if (this.delegate.willFollowLinkToLocation(link, location2, event)) { + event.preventDefault(); + this.delegate.followedLinkToLocation(link, location2); + } + } + } + }; + clickEventIsSignificant(event) { + return !(event.target && event.target.isContentEditable || event.defaultPrevented || event.which > 1 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey); + } +}; +var FormLinkClickObserver = class { + constructor(delegate, element) { + this.delegate = delegate; + this.linkInterceptor = new LinkClickObserver(this, element); + } + start() { + this.linkInterceptor.start(); + } + stop() { + this.linkInterceptor.stop(); + } + // Link hover observer delegate + canPrefetchRequestToLocation(link, location2) { + return false; + } + prefetchAndCacheRequestToLocation(link, location2) { + return; + } + // Link click observer delegate + willFollowLinkToLocation(link, location2, originalEvent) { + return this.delegate.willSubmitFormLinkToLocation(link, location2, originalEvent) && (link.hasAttribute("data-turbo-method") || link.hasAttribute("data-turbo-stream")); + } + followedLinkToLocation(link, location2) { + const form = document.createElement("form"); + const type = "hidden"; + for (const [name, value] of location2.searchParams) { + form.append(Object.assign(document.createElement("input"), { type, name, value })); + } + const action = Object.assign(location2, { search: "" }); + form.setAttribute("data-turbo", "true"); + form.setAttribute("action", action.href); + form.setAttribute("hidden", ""); + const method = link.getAttribute("data-turbo-method"); + if (method) form.setAttribute("method", method); + const turboFrame = link.getAttribute("data-turbo-frame"); + if (turboFrame) form.setAttribute("data-turbo-frame", turboFrame); + const turboAction = getVisitAction(link); + if (turboAction) form.setAttribute("data-turbo-action", turboAction); + const turboConfirm = link.getAttribute("data-turbo-confirm"); + if (turboConfirm) form.setAttribute("data-turbo-confirm", turboConfirm); + const turboStream = link.hasAttribute("data-turbo-stream"); + if (turboStream) form.setAttribute("data-turbo-stream", ""); + this.delegate.submittedFormLinkToLocation(link, location2, form); + document.body.appendChild(form); + form.addEventListener("turbo:submit-end", () => form.remove(), { once: true }); + requestAnimationFrame(() => form.requestSubmit()); + } +}; +var Bardo = class { + static async preservingPermanentElements(delegate, permanentElementMap, callback) { + const bardo = new this(delegate, permanentElementMap); + bardo.enter(); + await callback(); + bardo.leave(); + } + constructor(delegate, permanentElementMap) { + this.delegate = delegate; + this.permanentElementMap = permanentElementMap; + } + enter() { + for (const id in this.permanentElementMap) { + const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id]; + this.delegate.enteringBardo(currentPermanentElement, newPermanentElement); + this.replaceNewPermanentElementWithPlaceholder(newPermanentElement); + } + } + leave() { + for (const id in this.permanentElementMap) { + const [currentPermanentElement] = this.permanentElementMap[id]; + this.replaceCurrentPermanentElementWithClone(currentPermanentElement); + this.replacePlaceholderWithPermanentElement(currentPermanentElement); + this.delegate.leavingBardo(currentPermanentElement); + } + } + replaceNewPermanentElementWithPlaceholder(permanentElement) { + const placeholder = createPlaceholderForPermanentElement(permanentElement); + permanentElement.replaceWith(placeholder); + } + replaceCurrentPermanentElementWithClone(permanentElement) { + const clone = permanentElement.cloneNode(true); + permanentElement.replaceWith(clone); + } + replacePlaceholderWithPermanentElement(permanentElement) { + const placeholder = this.getPlaceholderById(permanentElement.id); + placeholder?.replaceWith(permanentElement); + } + getPlaceholderById(id) { + return this.placeholders.find((element) => element.content == id); + } + get placeholders() { + return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")]; + } +}; +function createPlaceholderForPermanentElement(permanentElement) { + const element = document.createElement("meta"); + element.setAttribute("name", "turbo-permanent-placeholder"); + element.setAttribute("content", permanentElement.id); + return element; +} +var Renderer = class { + #activeElement = null; + static renderElement(currentElement, newElement) { + } + constructor(currentSnapshot, newSnapshot, isPreview, willRender = true) { + this.currentSnapshot = currentSnapshot; + this.newSnapshot = newSnapshot; + this.isPreview = isPreview; + this.willRender = willRender; + this.renderElement = this.constructor.renderElement; + this.promise = new Promise((resolve, reject) => this.resolvingFunctions = { resolve, reject }); + } + get shouldRender() { + return true; + } + get shouldAutofocus() { + return true; + } + get reloadReason() { + return; + } + prepareToRender() { + return; + } + render() { + } + finishRendering() { + if (this.resolvingFunctions) { + this.resolvingFunctions.resolve(); + delete this.resolvingFunctions; + } + } + async preservingPermanentElements(callback) { + await Bardo.preservingPermanentElements(this, this.permanentElementMap, callback); + } + focusFirstAutofocusableElement() { + if (this.shouldAutofocus) { + const element = this.connectedSnapshot.firstAutofocusableElement; + if (element) { + element.focus(); + } + } + } + // Bardo delegate + enteringBardo(currentPermanentElement) { + if (this.#activeElement) return; + if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) { + this.#activeElement = this.currentSnapshot.activeElement; + } + } + leavingBardo(currentPermanentElement) { + if (currentPermanentElement.contains(this.#activeElement) && this.#activeElement instanceof HTMLElement) { + this.#activeElement.focus(); + this.#activeElement = null; + } + } + get connectedSnapshot() { + return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot; + } + get currentElement() { + return this.currentSnapshot.element; + } + get newElement() { + return this.newSnapshot.element; + } + get permanentElementMap() { + return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot); + } + get renderMethod() { + return "replace"; + } +}; +var FrameRenderer = class extends Renderer { + static renderElement(currentElement, newElement) { + const destinationRange = document.createRange(); + destinationRange.selectNodeContents(currentElement); + destinationRange.deleteContents(); + const frameElement = newElement; + const sourceRange = frameElement.ownerDocument?.createRange(); + if (sourceRange) { + sourceRange.selectNodeContents(frameElement); + currentElement.appendChild(sourceRange.extractContents()); + } + } + constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) { + super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender); + this.delegate = delegate; + } + get shouldRender() { + return true; + } + async render() { + await nextRepaint(); + this.preservingPermanentElements(() => { + this.loadFrameElement(); + }); + this.scrollFrameIntoView(); + await nextRepaint(); + this.focusFirstAutofocusableElement(); + await nextRepaint(); + this.activateScriptElements(); + } + loadFrameElement() { + this.delegate.willRenderFrame(this.currentElement, this.newElement); + this.renderElement(this.currentElement, this.newElement); + } + scrollFrameIntoView() { + if (this.currentElement.autoscroll || this.newElement.autoscroll) { + const element = this.currentElement.firstElementChild; + const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end"); + const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto"); + if (element) { + element.scrollIntoView({ block, behavior }); + return true; + } + } + return false; + } + activateScriptElements() { + for (const inertScriptElement of this.newScriptElements) { + const activatedScriptElement = activateScriptElement(inertScriptElement); + inertScriptElement.replaceWith(activatedScriptElement); + } + } + get newScriptElements() { + return this.currentElement.querySelectorAll("script"); + } +}; +function readScrollLogicalPosition(value, defaultValue) { + if (value == "end" || value == "start" || value == "center" || value == "nearest") { + return value; + } else { + return defaultValue; + } +} +function readScrollBehavior(value, defaultValue) { + if (value == "auto" || value == "smooth") { + return value; + } else { + return defaultValue; + } +} +var Idiomorph = (function() { + const noOp = () => { + }; + const defaults = { + morphStyle: "outerHTML", + callbacks: { + beforeNodeAdded: noOp, + afterNodeAdded: noOp, + beforeNodeMorphed: noOp, + afterNodeMorphed: noOp, + beforeNodeRemoved: noOp, + afterNodeRemoved: noOp, + beforeAttributeUpdated: noOp + }, + head: { + style: "merge", + shouldPreserve: (elt) => elt.getAttribute("im-preserve") === "true", + shouldReAppend: (elt) => elt.getAttribute("im-re-append") === "true", + shouldRemove: noOp, + afterHeadMorphed: noOp + }, + restoreFocus: true + }; + function morph(oldNode, newContent, config2 = {}) { + oldNode = normalizeElement(oldNode); + const newNode = normalizeParent(newContent); + const ctx = createMorphContext(oldNode, newNode, config2); + const morphedNodes = saveAndRestoreFocus(ctx, () => { + return withHeadBlocking( + ctx, + oldNode, + newNode, + /** @param {MorphContext} ctx */ + (ctx2) => { + if (ctx2.morphStyle === "innerHTML") { + morphChildren2(ctx2, oldNode, newNode); + return Array.from(oldNode.childNodes); + } else { + return morphOuterHTML(ctx2, oldNode, newNode); + } + } + ); + }); + ctx.pantry.remove(); + return morphedNodes; + } + function morphOuterHTML(ctx, oldNode, newNode) { + const oldParent = normalizeParent(oldNode); + morphChildren2( + ctx, + oldParent, + newNode, + // these two optional params are the secret sauce + oldNode, + // start point for iteration + oldNode.nextSibling + // end point for iteration + ); + return Array.from(oldParent.childNodes); + } + function saveAndRestoreFocus(ctx, fn) { + if (!ctx.config.restoreFocus) return fn(); + let activeElement = ( + /** @type {HTMLInputElement|HTMLTextAreaElement|null} */ + document.activeElement + ); + if (!(activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement)) { + return fn(); + } + const { id: activeElementId, selectionStart, selectionEnd } = activeElement; + const results = fn(); + if (activeElementId && activeElementId !== document.activeElement?.getAttribute("id")) { + activeElement = ctx.target.querySelector(`[id="${activeElementId}"]`); + activeElement?.focus(); + } + if (activeElement && !activeElement.selectionEnd && selectionEnd) { + activeElement.setSelectionRange(selectionStart, selectionEnd); + } + return results; + } + const morphChildren2 = /* @__PURE__ */ (function() { + function morphChildren3(ctx, oldParent, newParent, insertionPoint = null, endPoint = null) { + if (oldParent instanceof HTMLTemplateElement && newParent instanceof HTMLTemplateElement) { + oldParent = oldParent.content; + newParent = newParent.content; + } + insertionPoint ||= oldParent.firstChild; + for (const newChild of newParent.childNodes) { + if (insertionPoint && insertionPoint != endPoint) { + const bestMatch = findBestMatch( + ctx, + newChild, + insertionPoint, + endPoint + ); + if (bestMatch) { + if (bestMatch !== insertionPoint) { + removeNodesBetween(ctx, insertionPoint, bestMatch); + } + morphNode(bestMatch, newChild, ctx); + insertionPoint = bestMatch.nextSibling; + continue; + } + } + if (newChild instanceof Element) { + const newChildId = ( + /** @type {String} */ + newChild.getAttribute("id") + ); + if (ctx.persistentIds.has(newChildId)) { + const movedChild = moveBeforeById( + oldParent, + newChildId, + insertionPoint, + ctx + ); + morphNode(movedChild, newChild, ctx); + insertionPoint = movedChild.nextSibling; + continue; + } + } + const insertedNode = createNode( + oldParent, + newChild, + insertionPoint, + ctx + ); + if (insertedNode) { + insertionPoint = insertedNode.nextSibling; + } + } + while (insertionPoint && insertionPoint != endPoint) { + const tempNode = insertionPoint; + insertionPoint = insertionPoint.nextSibling; + removeNode(ctx, tempNode); + } + } + function createNode(oldParent, newChild, insertionPoint, ctx) { + if (ctx.callbacks.beforeNodeAdded(newChild) === false) return null; + if (ctx.idMap.has(newChild)) { + const newEmptyChild = document.createElement( + /** @type {Element} */ + newChild.tagName + ); + oldParent.insertBefore(newEmptyChild, insertionPoint); + morphNode(newEmptyChild, newChild, ctx); + ctx.callbacks.afterNodeAdded(newEmptyChild); + return newEmptyChild; + } else { + const newClonedChild = document.importNode(newChild, true); + oldParent.insertBefore(newClonedChild, insertionPoint); + ctx.callbacks.afterNodeAdded(newClonedChild); + return newClonedChild; + } + } + const findBestMatch = /* @__PURE__ */ (function() { + function findBestMatch2(ctx, node, startPoint, endPoint) { + let softMatch = null; + let nextSibling = node.nextSibling; + let siblingSoftMatchCount = 0; + let cursor = startPoint; + while (cursor && cursor != endPoint) { + if (isSoftMatch(cursor, node)) { + if (isIdSetMatch(ctx, cursor, node)) { + return cursor; + } + if (softMatch === null) { + if (!ctx.idMap.has(cursor)) { + softMatch = cursor; + } + } + } + if (softMatch === null && nextSibling && isSoftMatch(cursor, nextSibling)) { + siblingSoftMatchCount++; + nextSibling = nextSibling.nextSibling; + if (siblingSoftMatchCount >= 2) { + softMatch = void 0; + } + } + if (ctx.activeElementAndParents.includes(cursor)) break; + cursor = cursor.nextSibling; + } + return softMatch || null; + } + function isIdSetMatch(ctx, oldNode, newNode) { + let oldSet = ctx.idMap.get(oldNode); + let newSet = ctx.idMap.get(newNode); + if (!newSet || !oldSet) return false; + for (const id of oldSet) { + if (newSet.has(id)) { + return true; + } + } + return false; + } + function isSoftMatch(oldNode, newNode) { + const oldElt = ( + /** @type {Element} */ + oldNode + ); + const newElt = ( + /** @type {Element} */ + newNode + ); + return oldElt.nodeType === newElt.nodeType && oldElt.tagName === newElt.tagName && // If oldElt has an `id` with possible state and it doesn't match newElt.id then avoid morphing. + // We'll still match an anonymous node with an IDed newElt, though, because if it got this far, + // its not persistent, and new nodes can't have any hidden state. + // We can't use .id because of form input shadowing, and we can't count on .getAttribute's presence because it could be a document-fragment + (!oldElt.getAttribute?.("id") || oldElt.getAttribute?.("id") === newElt.getAttribute?.("id")); + } + return findBestMatch2; + })(); + function removeNode(ctx, node) { + if (ctx.idMap.has(node)) { + moveBefore(ctx.pantry, node, null); + } else { + if (ctx.callbacks.beforeNodeRemoved(node) === false) return; + node.parentNode?.removeChild(node); + ctx.callbacks.afterNodeRemoved(node); + } + } + function removeNodesBetween(ctx, startInclusive, endExclusive) { + let cursor = startInclusive; + while (cursor && cursor !== endExclusive) { + let tempNode = ( + /** @type {Node} */ + cursor + ); + cursor = cursor.nextSibling; + removeNode(ctx, tempNode); + } + return cursor; + } + function moveBeforeById(parentNode, id, after, ctx) { + const target = ( + /** @type {Element} - will always be found */ + // ctx.target.id unsafe because of form input shadowing + // ctx.target could be a document fragment which doesn't have `getAttribute` + ctx.target.getAttribute?.("id") === id && ctx.target || ctx.target.querySelector(`[id="${id}"]`) || ctx.pantry.querySelector(`[id="${id}"]`) + ); + removeElementFromAncestorsIdMaps(target, ctx); + moveBefore(parentNode, target, after); + return target; + } + function removeElementFromAncestorsIdMaps(element, ctx) { + const id = ( + /** @type {String} */ + element.getAttribute("id") + ); + while (element = element.parentNode) { + let idSet = ctx.idMap.get(element); + if (idSet) { + idSet.delete(id); + if (!idSet.size) { + ctx.idMap.delete(element); + } + } + } + } + function moveBefore(parentNode, element, after) { + if (parentNode.moveBefore) { + try { + parentNode.moveBefore(element, after); + } catch (e) { + parentNode.insertBefore(element, after); + } + } else { + parentNode.insertBefore(element, after); + } + } + return morphChildren3; + })(); + const morphNode = /* @__PURE__ */ (function() { + function morphNode2(oldNode, newContent, ctx) { + if (ctx.ignoreActive && oldNode === document.activeElement) { + return null; + } + if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false) { + return oldNode; + } + if (oldNode instanceof HTMLHeadElement && ctx.head.ignore) ; + else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") { + handleHeadElement( + oldNode, + /** @type {HTMLHeadElement} */ + newContent, + ctx + ); + } else { + morphAttributes(oldNode, newContent, ctx); + if (!ignoreValueOfActiveElement(oldNode, ctx)) { + morphChildren2(ctx, oldNode, newContent); + } + } + ctx.callbacks.afterNodeMorphed(oldNode, newContent); + return oldNode; + } + function morphAttributes(oldNode, newNode, ctx) { + let type = newNode.nodeType; + if (type === 1) { + const oldElt = ( + /** @type {Element} */ + oldNode + ); + const newElt = ( + /** @type {Element} */ + newNode + ); + const oldAttributes = oldElt.attributes; + const newAttributes = newElt.attributes; + for (const newAttribute of newAttributes) { + if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + continue; + } + if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) { + oldElt.setAttribute(newAttribute.name, newAttribute.value); + } + } + for (let i = oldAttributes.length - 1; 0 <= i; i--) { + const oldAttribute = oldAttributes[i]; + if (!oldAttribute) continue; + if (!newElt.hasAttribute(oldAttribute.name)) { + if (ignoreAttribute(oldAttribute.name, oldElt, "remove", ctx)) { + continue; + } + oldElt.removeAttribute(oldAttribute.name); + } + } + if (!ignoreValueOfActiveElement(oldElt, ctx)) { + syncInputValue(oldElt, newElt, ctx); + } + } + if (type === 8 || type === 3) { + if (oldNode.nodeValue !== newNode.nodeValue) { + oldNode.nodeValue = newNode.nodeValue; + } + } + } + function syncInputValue(oldElement, newElement, ctx) { + if (oldElement instanceof HTMLInputElement && newElement instanceof HTMLInputElement && newElement.type !== "file") { + let newValue = newElement.value; + let oldValue = oldElement.value; + syncBooleanAttribute(oldElement, newElement, "checked", ctx); + syncBooleanAttribute(oldElement, newElement, "disabled", ctx); + if (!newElement.hasAttribute("value")) { + if (!ignoreAttribute("value", oldElement, "remove", ctx)) { + oldElement.value = ""; + oldElement.removeAttribute("value"); + } + } else if (oldValue !== newValue) { + if (!ignoreAttribute("value", oldElement, "update", ctx)) { + oldElement.setAttribute("value", newValue); + oldElement.value = newValue; + } + } + } else if (oldElement instanceof HTMLOptionElement && newElement instanceof HTMLOptionElement) { + syncBooleanAttribute(oldElement, newElement, "selected", ctx); + } else if (oldElement instanceof HTMLTextAreaElement && newElement instanceof HTMLTextAreaElement) { + let newValue = newElement.value; + let oldValue = oldElement.value; + if (ignoreAttribute("value", oldElement, "update", ctx)) { + return; + } + if (newValue !== oldValue) { + oldElement.value = newValue; + } + if (oldElement.firstChild && oldElement.firstChild.nodeValue !== newValue) { + oldElement.firstChild.nodeValue = newValue; + } + } + } + function syncBooleanAttribute(oldElement, newElement, attributeName, ctx) { + const newLiveValue = newElement[attributeName], oldLiveValue = oldElement[attributeName]; + if (newLiveValue !== oldLiveValue) { + const ignoreUpdate = ignoreAttribute( + attributeName, + oldElement, + "update", + ctx + ); + if (!ignoreUpdate) { + oldElement[attributeName] = newElement[attributeName]; + } + if (newLiveValue) { + if (!ignoreUpdate) { + oldElement.setAttribute(attributeName, ""); + } + } else { + if (!ignoreAttribute(attributeName, oldElement, "remove", ctx)) { + oldElement.removeAttribute(attributeName); + } + } + } + } + function ignoreAttribute(attr, element, updateType, ctx) { + if (attr === "value" && ctx.ignoreActiveValue && element === document.activeElement) { + return true; + } + return ctx.callbacks.beforeAttributeUpdated(attr, element, updateType) === false; + } + function ignoreValueOfActiveElement(possibleActiveElement, ctx) { + return !!ctx.ignoreActiveValue && possibleActiveElement === document.activeElement && possibleActiveElement !== document.body; + } + return morphNode2; + })(); + function withHeadBlocking(ctx, oldNode, newNode, callback) { + if (ctx.head.block) { + const oldHead = oldNode.querySelector("head"); + const newHead = newNode.querySelector("head"); + if (oldHead && newHead) { + const promises = handleHeadElement(oldHead, newHead, ctx); + return Promise.all(promises).then(() => { + const newCtx = Object.assign(ctx, { + head: { + block: false, + ignore: true + } + }); + return callback(newCtx); + }); + } + } + return callback(ctx); + } + function handleHeadElement(oldHead, newHead, ctx) { + let added = []; + let removed = []; + let preserved = []; + let nodesToAppend = []; + let srcToNewHeadNodes = /* @__PURE__ */ new Map(); + for (const newHeadChild of newHead.children) { + srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild); + } + for (const currentHeadElt of oldHead.children) { + let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML); + let isReAppended = ctx.head.shouldReAppend(currentHeadElt); + let isPreserved = ctx.head.shouldPreserve(currentHeadElt); + if (inNewContent || isPreserved) { + if (isReAppended) { + removed.push(currentHeadElt); + } else { + srcToNewHeadNodes.delete(currentHeadElt.outerHTML); + preserved.push(currentHeadElt); + } + } else { + if (ctx.head.style === "append") { + if (isReAppended) { + removed.push(currentHeadElt); + nodesToAppend.push(currentHeadElt); + } + } else { + if (ctx.head.shouldRemove(currentHeadElt) !== false) { + removed.push(currentHeadElt); + } + } + } + } + nodesToAppend.push(...srcToNewHeadNodes.values()); + let promises = []; + for (const newNode of nodesToAppend) { + let newElt = ( + /** @type {ChildNode} */ + document.createRange().createContextualFragment(newNode.outerHTML).firstChild + ); + if (ctx.callbacks.beforeNodeAdded(newElt) !== false) { + if ("href" in newElt && newElt.href || "src" in newElt && newElt.src) { + let resolve; + let promise = new Promise(function(_resolve) { + resolve = _resolve; + }); + newElt.addEventListener("load", function() { + resolve(); + }); + promises.push(promise); + } + oldHead.appendChild(newElt); + ctx.callbacks.afterNodeAdded(newElt); + added.push(newElt); + } + } + for (const removedElement of removed) { + if (ctx.callbacks.beforeNodeRemoved(removedElement) !== false) { + oldHead.removeChild(removedElement); + ctx.callbacks.afterNodeRemoved(removedElement); + } + } + ctx.head.afterHeadMorphed(oldHead, { + added, + kept: preserved, + removed + }); + return promises; + } + const createMorphContext = /* @__PURE__ */ (function() { + function createMorphContext2(oldNode, newContent, config2) { + const { persistentIds, idMap } = createIdMaps(oldNode, newContent); + const mergedConfig = mergeDefaults(config2); + const morphStyle = mergedConfig.morphStyle || "outerHTML"; + if (!["innerHTML", "outerHTML"].includes(morphStyle)) { + throw `Do not understand how to morph style ${morphStyle}`; + } + return { + target: oldNode, + newContent, + config: mergedConfig, + morphStyle, + ignoreActive: mergedConfig.ignoreActive, + ignoreActiveValue: mergedConfig.ignoreActiveValue, + restoreFocus: mergedConfig.restoreFocus, + idMap, + persistentIds, + pantry: createPantry(), + activeElementAndParents: createActiveElementAndParents(oldNode), + callbacks: mergedConfig.callbacks, + head: mergedConfig.head + }; + } + function mergeDefaults(config2) { + let finalConfig = Object.assign({}, defaults); + Object.assign(finalConfig, config2); + finalConfig.callbacks = Object.assign( + {}, + defaults.callbacks, + config2.callbacks + ); + finalConfig.head = Object.assign({}, defaults.head, config2.head); + return finalConfig; + } + function createPantry() { + const pantry = document.createElement("div"); + pantry.hidden = true; + document.body.insertAdjacentElement("afterend", pantry); + return pantry; + } + function createActiveElementAndParents(oldNode) { + let activeElementAndParents = []; + let elt = document.activeElement; + if (elt?.tagName !== "BODY" && oldNode.contains(elt)) { + while (elt) { + activeElementAndParents.push(elt); + if (elt === oldNode) break; + elt = elt.parentElement; + } + } + return activeElementAndParents; + } + function findIdElements(root) { + let elements = Array.from(root.querySelectorAll("[id]")); + if (root.getAttribute?.("id")) { + elements.push(root); + } + return elements; + } + function populateIdMapWithTree(idMap, persistentIds, root, elements) { + for (const elt of elements) { + const id = ( + /** @type {String} */ + elt.getAttribute("id") + ); + if (persistentIds.has(id)) { + let current = elt; + while (current) { + let idSet = idMap.get(current); + if (idSet == null) { + idSet = /* @__PURE__ */ new Set(); + idMap.set(current, idSet); + } + idSet.add(id); + if (current === root) break; + current = current.parentElement; + } + } + } + } + function createIdMaps(oldContent, newContent) { + const oldIdElements = findIdElements(oldContent); + const newIdElements = findIdElements(newContent); + const persistentIds = createPersistentIds(oldIdElements, newIdElements); + let idMap = /* @__PURE__ */ new Map(); + populateIdMapWithTree(idMap, persistentIds, oldContent, oldIdElements); + const newRoot = newContent.__idiomorphRoot || newContent; + populateIdMapWithTree(idMap, persistentIds, newRoot, newIdElements); + return { persistentIds, idMap }; + } + function createPersistentIds(oldIdElements, newIdElements) { + let duplicateIds = /* @__PURE__ */ new Set(); + let oldIdTagNameMap = /* @__PURE__ */ new Map(); + for (const { id, tagName } of oldIdElements) { + if (oldIdTagNameMap.has(id)) { + duplicateIds.add(id); + } else { + oldIdTagNameMap.set(id, tagName); + } + } + let persistentIds = /* @__PURE__ */ new Set(); + for (const { id, tagName } of newIdElements) { + if (persistentIds.has(id)) { + duplicateIds.add(id); + } else if (oldIdTagNameMap.get(id) === tagName) { + persistentIds.add(id); + } + } + for (const id of duplicateIds) { + persistentIds.delete(id); + } + return persistentIds; + } + return createMorphContext2; + })(); + const { normalizeElement, normalizeParent } = /* @__PURE__ */ (function() { + const generatedByIdiomorph = /* @__PURE__ */ new WeakSet(); + function normalizeElement2(content) { + if (content instanceof Document) { + return content.documentElement; + } else { + return content; + } + } + function normalizeParent2(newContent) { + if (newContent == null) { + return document.createElement("div"); + } else if (typeof newContent === "string") { + return normalizeParent2(parseContent(newContent)); + } else if (generatedByIdiomorph.has( + /** @type {Element} */ + newContent + )) { + return ( + /** @type {Element} */ + newContent + ); + } else if (newContent instanceof Node) { + if (newContent.parentNode) { + return ( + /** @type {any} */ + new SlicedParentNode(newContent) + ); + } else { + const dummyParent = document.createElement("div"); + dummyParent.append(newContent); + return dummyParent; + } + } else { + const dummyParent = document.createElement("div"); + for (const elt of [...newContent]) { + dummyParent.append(elt); + } + return dummyParent; + } + } + class SlicedParentNode { + /** @param {Node} node */ + constructor(node) { + this.originalNode = node; + this.realParentNode = /** @type {Element} */ + node.parentNode; + this.previousSibling = node.previousSibling; + this.nextSibling = node.nextSibling; + } + /** @returns {Node[]} */ + get childNodes() { + const nodes = []; + let cursor = this.previousSibling ? this.previousSibling.nextSibling : this.realParentNode.firstChild; + while (cursor && cursor != this.nextSibling) { + nodes.push(cursor); + cursor = cursor.nextSibling; + } + return nodes; + } + /** + * @param {string} selector + * @returns {Element[]} + */ + querySelectorAll(selector) { + return this.childNodes.reduce( + (results, node) => { + if (node instanceof Element) { + if (node.matches(selector)) results.push(node); + const nodeList = node.querySelectorAll(selector); + for (let i = 0; i < nodeList.length; i++) { + results.push(nodeList[i]); + } + } + return results; + }, + /** @type {Element[]} */ + [] + ); + } + /** + * @param {Node} node + * @param {Node} referenceNode + * @returns {Node} + */ + insertBefore(node, referenceNode) { + return this.realParentNode.insertBefore(node, referenceNode); + } + /** + * @param {Node} node + * @param {Node} referenceNode + * @returns {Node} + */ + moveBefore(node, referenceNode) { + return this.realParentNode.moveBefore(node, referenceNode); + } + /** + * for later use with populateIdMapWithTree to halt upwards iteration + * @returns {Node} + */ + get __idiomorphRoot() { + return this.originalNode; + } + } + function parseContent(newContent) { + let parser = new DOMParser(); + let contentWithSvgsRemoved = newContent.replace( + /]*>|>)([\s\S]*?)<\/svg>/gim, + "" + ); + if (contentWithSvgsRemoved.match(/<\/html>/) || contentWithSvgsRemoved.match(/<\/head>/) || contentWithSvgsRemoved.match(/<\/body>/)) { + let content = parser.parseFromString(newContent, "text/html"); + if (contentWithSvgsRemoved.match(/<\/html>/)) { + generatedByIdiomorph.add(content); + return content; + } else { + let htmlElement = content.firstChild; + if (htmlElement) { + generatedByIdiomorph.add(htmlElement); + } + return htmlElement; + } + } else { + let responseDoc = parser.parseFromString( + "", + "text/html" + ); + let content = ( + /** @type {HTMLTemplateElement} */ + responseDoc.body.querySelector("template").content + ); + generatedByIdiomorph.add(content); + return content; + } + } + return { normalizeElement: normalizeElement2, normalizeParent: normalizeParent2 }; + })(); + return { + morph, + defaults + }; +})(); +function morphElements(currentElement, newElement, { callbacks, ...options } = {}) { + Idiomorph.morph(currentElement, newElement, { + ...options, + callbacks: new DefaultIdiomorphCallbacks(callbacks) + }); +} +function morphChildren(currentElement, newElement, options = {}) { + morphElements(currentElement, newElement.childNodes, { + ...options, + morphStyle: "innerHTML" + }); +} +function shouldRefreshFrameWithMorphing(currentFrame, newFrame) { + return currentFrame instanceof FrameElement && currentFrame.shouldReloadWithMorph && (!newFrame || areFramesCompatibleForRefreshing(currentFrame, newFrame)) && !currentFrame.closest("[data-turbo-permanent]"); +} +function areFramesCompatibleForRefreshing(currentFrame, newFrame) { + return newFrame instanceof Element && newFrame.nodeName === "TURBO-FRAME" && currentFrame.id === newFrame.id && (!newFrame.getAttribute("src") || urlsAreEqual(currentFrame.src, newFrame.getAttribute("src"))); +} +function closestFrameReloadableWithMorphing(node) { + return node.parentElement.closest("turbo-frame[src][refresh=morph]"); +} +var DefaultIdiomorphCallbacks = class { + #beforeNodeMorphed; + constructor({ beforeNodeMorphed } = {}) { + this.#beforeNodeMorphed = beforeNodeMorphed || (() => true); + } + beforeNodeAdded = (node) => { + return !(node.id && node.hasAttribute("data-turbo-permanent") && document.getElementById(node.id)); + }; + beforeNodeMorphed = (currentElement, newElement) => { + if (currentElement instanceof Element) { + if (!currentElement.hasAttribute("data-turbo-permanent") && this.#beforeNodeMorphed(currentElement, newElement)) { + const event = dispatch("turbo:before-morph-element", { + cancelable: true, + target: currentElement, + detail: { currentElement, newElement } + }); + return !event.defaultPrevented; + } else { + return false; + } + } + }; + beforeAttributeUpdated = (attributeName, target, mutationType) => { + const event = dispatch("turbo:before-morph-attribute", { + cancelable: true, + target, + detail: { attributeName, mutationType } + }); + return !event.defaultPrevented; + }; + beforeNodeRemoved = (node) => { + return this.beforeNodeMorphed(node); + }; + afterNodeMorphed = (currentElement, newElement) => { + if (currentElement instanceof Element) { + dispatch("turbo:morph-element", { + target: currentElement, + detail: { currentElement, newElement } + }); + } + }; +}; +var MorphingFrameRenderer = class extends FrameRenderer { + static renderElement(currentElement, newElement) { + dispatch("turbo:before-frame-morph", { + target: currentElement, + detail: { currentElement, newElement } + }); + morphChildren(currentElement, newElement, { + callbacks: { + beforeNodeMorphed: (node, newNode) => { + if (shouldRefreshFrameWithMorphing(node, newNode) && closestFrameReloadableWithMorphing(node) === currentElement) { + node.reload(); + return false; + } + return true; + } + } + }); + } + async preservingPermanentElements(callback) { + return await callback(); + } +}; +var ProgressBar = class _ProgressBar { + static animationDuration = 300; + /*ms*/ + static get defaultCSS() { + return unindent` + .turbo-progress-bar { + position: fixed; + display: block; + top: 0; + left: 0; + height: 3px; + background: #0076ff; + z-index: 2147483647; + transition: + width ${_ProgressBar.animationDuration}ms ease-out, + opacity ${_ProgressBar.animationDuration / 2}ms ${_ProgressBar.animationDuration / 2}ms ease-in; + transform: translate3d(0, 0, 0); + } + `; + } + hiding = false; + value = 0; + visible = false; + constructor() { + this.stylesheetElement = this.createStylesheetElement(); + this.progressElement = this.createProgressElement(); + this.installStylesheetElement(); + this.setValue(0); + } + show() { + if (!this.visible) { + this.visible = true; + this.installProgressElement(); + this.startTrickling(); + } + } + hide() { + if (this.visible && !this.hiding) { + this.hiding = true; + this.fadeProgressElement(() => { + this.uninstallProgressElement(); + this.stopTrickling(); + this.visible = false; + this.hiding = false; + }); + } + } + setValue(value) { + this.value = value; + this.refresh(); + } + // Private + installStylesheetElement() { + document.head.insertBefore(this.stylesheetElement, document.head.firstChild); + } + installProgressElement() { + this.progressElement.style.width = "0"; + this.progressElement.style.opacity = "1"; + document.documentElement.insertBefore(this.progressElement, document.body); + this.refresh(); + } + fadeProgressElement(callback) { + this.progressElement.style.opacity = "0"; + setTimeout(callback, _ProgressBar.animationDuration * 1.5); + } + uninstallProgressElement() { + if (this.progressElement.parentNode) { + document.documentElement.removeChild(this.progressElement); + } + } + startTrickling() { + if (!this.trickleInterval) { + this.trickleInterval = window.setInterval(this.trickle, _ProgressBar.animationDuration); + } + } + stopTrickling() { + window.clearInterval(this.trickleInterval); + delete this.trickleInterval; + } + trickle = () => { + this.setValue(this.value + Math.random() / 100); + }; + refresh() { + requestAnimationFrame(() => { + this.progressElement.style.width = `${10 + this.value * 90}%`; + }); + } + createStylesheetElement() { + const element = document.createElement("style"); + element.type = "text/css"; + element.textContent = _ProgressBar.defaultCSS; + const cspNonce = getCspNonce(); + if (cspNonce) { + element.nonce = cspNonce; + } + return element; + } + createProgressElement() { + const element = document.createElement("div"); + element.className = "turbo-progress-bar"; + return element; + } +}; +var HeadSnapshot = class extends Snapshot { + detailsByOuterHTML = this.children.filter((element) => !elementIsNoscript(element)).map((element) => elementWithoutNonce(element)).reduce((result, element) => { + const { outerHTML } = element; + const details = outerHTML in result ? result[outerHTML] : { + type: elementType(element), + tracked: elementIsTracked(element), + elements: [] + }; + return { + ...result, + [outerHTML]: { + ...details, + elements: [...details.elements, element] + } + }; + }, {}); + get trackedElementSignature() { + return Object.keys(this.detailsByOuterHTML).filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked).join(""); + } + getScriptElementsNotInSnapshot(snapshot) { + return this.getElementsMatchingTypeNotInSnapshot("script", snapshot); + } + getStylesheetElementsNotInSnapshot(snapshot) { + return this.getElementsMatchingTypeNotInSnapshot("stylesheet", snapshot); + } + getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) { + return Object.keys(this.detailsByOuterHTML).filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML)).map((outerHTML) => this.detailsByOuterHTML[outerHTML]).filter(({ type }) => type == matchedType).map(({ elements: [element] }) => element); + } + get provisionalElements() { + return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => { + const { type, tracked, elements } = this.detailsByOuterHTML[outerHTML]; + if (type == null && !tracked) { + return [...result, ...elements]; + } else if (elements.length > 1) { + return [...result, ...elements.slice(1)]; + } else { + return result; + } + }, []); + } + getMetaValue(name) { + const element = this.findMetaElementByName(name); + return element ? element.getAttribute("content") : null; + } + findMetaElementByName(name) { + return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => { + const { + elements: [element] + } = this.detailsByOuterHTML[outerHTML]; + return elementIsMetaElementWithName(element, name) ? element : result; + }, void 0 | void 0); + } +}; +function elementType(element) { + if (elementIsScript(element)) { + return "script"; + } else if (elementIsStylesheet(element)) { + return "stylesheet"; + } +} +function elementIsTracked(element) { + return element.getAttribute("data-turbo-track") == "reload"; +} +function elementIsScript(element) { + const tagName = element.localName; + return tagName == "script"; +} +function elementIsNoscript(element) { + const tagName = element.localName; + return tagName == "noscript"; +} +function elementIsStylesheet(element) { + const tagName = element.localName; + return tagName == "style" || tagName == "link" && element.getAttribute("rel") == "stylesheet"; +} +function elementIsMetaElementWithName(element, name) { + const tagName = element.localName; + return tagName == "meta" && element.getAttribute("name") == name; +} +function elementWithoutNonce(element) { + if (element.hasAttribute("nonce")) { + element.setAttribute("nonce", ""); + } + return element; +} +var PageSnapshot = class _PageSnapshot extends Snapshot { + static fromHTMLString(html = "") { + return this.fromDocument(parseHTMLDocument(html)); + } + static fromElement(element) { + return this.fromDocument(element.ownerDocument); + } + static fromDocument({ documentElement, body, head }) { + return new this(documentElement, body, new HeadSnapshot(head)); + } + constructor(documentElement, body, headSnapshot) { + super(body); + this.documentElement = documentElement; + this.headSnapshot = headSnapshot; + } + clone() { + const clonedElement = this.element.cloneNode(true); + const selectElements = this.element.querySelectorAll("select"); + const clonedSelectElements = clonedElement.querySelectorAll("select"); + for (const [index, source] of selectElements.entries()) { + const clone = clonedSelectElements[index]; + for (const option of clone.selectedOptions) option.selected = false; + for (const option of source.selectedOptions) clone.options[option.index].selected = true; + } + for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) { + clonedPasswordInput.value = ""; + } + return new _PageSnapshot(this.documentElement, clonedElement, this.headSnapshot); + } + get lang() { + return this.documentElement.getAttribute("lang"); + } + get headElement() { + return this.headSnapshot.element; + } + get rootLocation() { + const root = this.getSetting("root") ?? "/"; + return expandURL(root); + } + get cacheControlValue() { + return this.getSetting("cache-control"); + } + get isPreviewable() { + return this.cacheControlValue != "no-preview"; + } + get isCacheable() { + return this.cacheControlValue != "no-cache"; + } + get isVisitable() { + return this.getSetting("visit-control") != "reload"; + } + get prefersViewTransitions() { + const viewTransitionEnabled = this.getSetting("view-transition") === "true" || this.headSnapshot.getMetaValue("view-transition") === "same-origin"; + return viewTransitionEnabled && !window.matchMedia("(prefers-reduced-motion: reduce)").matches; + } + get shouldMorphPage() { + return this.getSetting("refresh-method") === "morph"; + } + get shouldPreserveScrollPosition() { + return this.getSetting("refresh-scroll") === "preserve"; + } + // Private + getSetting(name) { + return this.headSnapshot.getMetaValue(`turbo-${name}`); + } +}; +var ViewTransitioner = class { + #viewTransitionStarted = false; + #lastOperation = Promise.resolve(); + renderChange(useViewTransition, render) { + if (useViewTransition && this.viewTransitionsAvailable && !this.#viewTransitionStarted) { + this.#viewTransitionStarted = true; + this.#lastOperation = this.#lastOperation.then(async () => { + await document.startViewTransition(render).finished; + }); + } else { + this.#lastOperation = this.#lastOperation.then(render); + } + return this.#lastOperation; + } + get viewTransitionsAvailable() { + return document.startViewTransition; + } +}; +var defaultOptions = { + action: "advance", + historyChanged: false, + visitCachedSnapshot: () => { + }, + willRender: true, + updateHistory: true, + shouldCacheSnapshot: true, + acceptsStreamResponse: false +}; +var TimingMetric = { + visitStart: "visitStart", + requestStart: "requestStart", + requestEnd: "requestEnd", + visitEnd: "visitEnd" +}; +var VisitState = { + initialized: "initialized", + started: "started", + canceled: "canceled", + failed: "failed", + completed: "completed" +}; +var SystemStatusCode = { + networkFailure: 0, + timeoutFailure: -1, + contentTypeMismatch: -2 +}; +var Direction = { + advance: "forward", + restore: "back", + replace: "none" +}; +var Visit = class { + identifier = uuid(); + // Required by turbo-ios + timingMetrics = {}; + followedRedirect = false; + historyChanged = false; + scrolled = false; + shouldCacheSnapshot = true; + acceptsStreamResponse = false; + snapshotCached = false; + state = VisitState.initialized; + viewTransitioner = new ViewTransitioner(); + constructor(delegate, location2, restorationIdentifier, options = {}) { + this.delegate = delegate; + this.location = location2; + this.restorationIdentifier = restorationIdentifier || uuid(); + const { + action, + historyChanged, + referrer, + snapshot, + snapshotHTML, + response, + visitCachedSnapshot, + willRender, + updateHistory, + shouldCacheSnapshot, + acceptsStreamResponse, + direction + } = { + ...defaultOptions, + ...options + }; + this.action = action; + this.historyChanged = historyChanged; + this.referrer = referrer; + this.snapshot = snapshot; + this.snapshotHTML = snapshotHTML; + this.response = response; + this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action); + this.isPageRefresh = this.view.isPageRefresh(this); + this.visitCachedSnapshot = visitCachedSnapshot; + this.willRender = willRender; + this.updateHistory = updateHistory; + this.scrolled = !willRender; + this.shouldCacheSnapshot = shouldCacheSnapshot; + this.acceptsStreamResponse = acceptsStreamResponse; + this.direction = direction || Direction[action]; + } + get adapter() { + return this.delegate.adapter; + } + get view() { + return this.delegate.view; + } + get history() { + return this.delegate.history; + } + get restorationData() { + return this.history.getRestorationDataForIdentifier(this.restorationIdentifier); + } + get silent() { + return this.isSamePage; + } + start() { + if (this.state == VisitState.initialized) { + this.recordTimingMetric(TimingMetric.visitStart); + this.state = VisitState.started; + this.adapter.visitStarted(this); + this.delegate.visitStarted(this); + } + } + cancel() { + if (this.state == VisitState.started) { + if (this.request) { + this.request.cancel(); + } + this.cancelRender(); + this.state = VisitState.canceled; + } + } + complete() { + if (this.state == VisitState.started) { + this.recordTimingMetric(TimingMetric.visitEnd); + this.adapter.visitCompleted(this); + this.state = VisitState.completed; + this.followRedirect(); + if (!this.followedRedirect) { + this.delegate.visitCompleted(this); + } + } + } + fail() { + if (this.state == VisitState.started) { + this.state = VisitState.failed; + this.adapter.visitFailed(this); + this.delegate.visitCompleted(this); + } + } + changeHistory() { + if (!this.historyChanged && this.updateHistory) { + const actionForHistory = this.location.href === this.referrer?.href ? "replace" : this.action; + const method = getHistoryMethodForAction(actionForHistory); + this.history.update(method, this.location, this.restorationIdentifier); + this.historyChanged = true; + } + } + issueRequest() { + if (this.hasPreloadedResponse()) { + this.simulateRequest(); + } else if (this.shouldIssueRequest() && !this.request) { + this.request = new FetchRequest(this, FetchMethod.get, this.location); + this.request.perform(); + } + } + simulateRequest() { + if (this.response) { + this.startRequest(); + this.recordResponse(); + this.finishRequest(); + } + } + startRequest() { + this.recordTimingMetric(TimingMetric.requestStart); + this.adapter.visitRequestStarted(this); + } + recordResponse(response = this.response) { + this.response = response; + if (response) { + const { statusCode } = response; + if (isSuccessful(statusCode)) { + this.adapter.visitRequestCompleted(this); + } else { + this.adapter.visitRequestFailedWithStatusCode(this, statusCode); + } + } + } + finishRequest() { + this.recordTimingMetric(TimingMetric.requestEnd); + this.adapter.visitRequestFinished(this); + } + loadResponse() { + if (this.response) { + const { statusCode, responseHTML } = this.response; + this.render(async () => { + if (this.shouldCacheSnapshot) this.cacheSnapshot(); + if (this.view.renderPromise) await this.view.renderPromise; + if (isSuccessful(statusCode) && responseHTML != null) { + const snapshot = PageSnapshot.fromHTMLString(responseHTML); + await this.renderPageSnapshot(snapshot, false); + this.adapter.visitRendered(this); + this.complete(); + } else { + await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this); + this.adapter.visitRendered(this); + this.fail(); + } + }); + } + } + getCachedSnapshot() { + const snapshot = this.view.getCachedSnapshotForLocation(this.location) || this.getPreloadedSnapshot(); + if (snapshot && (!getAnchor(this.location) || snapshot.hasAnchor(getAnchor(this.location)))) { + if (this.action == "restore" || snapshot.isPreviewable) { + return snapshot; + } + } + } + getPreloadedSnapshot() { + if (this.snapshotHTML) { + return PageSnapshot.fromHTMLString(this.snapshotHTML); + } + } + hasCachedSnapshot() { + return this.getCachedSnapshot() != null; + } + loadCachedSnapshot() { + const snapshot = this.getCachedSnapshot(); + if (snapshot) { + const isPreview = this.shouldIssueRequest(); + this.render(async () => { + this.cacheSnapshot(); + if (this.isSamePage || this.isPageRefresh) { + this.adapter.visitRendered(this); + } else { + if (this.view.renderPromise) await this.view.renderPromise; + await this.renderPageSnapshot(snapshot, isPreview); + this.adapter.visitRendered(this); + if (!isPreview) { + this.complete(); + } + } + }); + } + } + followRedirect() { + if (this.redirectedToLocation && !this.followedRedirect && this.response?.redirected) { + this.adapter.visitProposedToLocation(this.redirectedToLocation, { + action: "replace", + response: this.response, + shouldCacheSnapshot: false, + willRender: false + }); + this.followedRedirect = true; + } + } + goToSamePageAnchor() { + if (this.isSamePage) { + this.render(async () => { + this.cacheSnapshot(); + this.performScroll(); + this.changeHistory(); + this.adapter.visitRendered(this); + }); + } + } + // Fetch request delegate + prepareRequest(request) { + if (this.acceptsStreamResponse) { + request.acceptResponseType(StreamMessage.contentType); + } + } + requestStarted() { + this.startRequest(); + } + requestPreventedHandlingResponse(_request, _response) { + } + async requestSucceededWithResponse(request, response) { + const responseHTML = await response.responseHTML; + const { redirected, statusCode } = response; + if (responseHTML == void 0) { + this.recordResponse({ + statusCode: SystemStatusCode.contentTypeMismatch, + redirected + }); + } else { + this.redirectedToLocation = response.redirected ? response.location : void 0; + this.recordResponse({ statusCode, responseHTML, redirected }); + } + } + async requestFailedWithResponse(request, response) { + const responseHTML = await response.responseHTML; + const { redirected, statusCode } = response; + if (responseHTML == void 0) { + this.recordResponse({ + statusCode: SystemStatusCode.contentTypeMismatch, + redirected + }); + } else { + this.recordResponse({ statusCode, responseHTML, redirected }); + } + } + requestErrored(_request, _error) { + this.recordResponse({ + statusCode: SystemStatusCode.networkFailure, + redirected: false + }); + } + requestFinished() { + this.finishRequest(); + } + // Scrolling + performScroll() { + if (!this.scrolled && !this.view.forceReloaded && !this.view.shouldPreserveScrollPosition(this)) { + if (this.action == "restore") { + this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop(); + } else { + this.scrollToAnchor() || this.view.scrollToTop(); + } + if (this.isSamePage) { + this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation, this.location); + } + this.scrolled = true; + } + } + scrollToRestoredPosition() { + const { scrollPosition } = this.restorationData; + if (scrollPosition) { + this.view.scrollToPosition(scrollPosition); + return true; + } + } + scrollToAnchor() { + const anchor = getAnchor(this.location); + if (anchor != null) { + this.view.scrollToAnchor(anchor); + return true; + } + } + // Instrumentation + recordTimingMetric(metric) { + this.timingMetrics[metric] = (/* @__PURE__ */ new Date()).getTime(); + } + getTimingMetrics() { + return { ...this.timingMetrics }; + } + // Private + hasPreloadedResponse() { + return typeof this.response == "object"; + } + shouldIssueRequest() { + if (this.isSamePage) { + return false; + } else if (this.action == "restore") { + return !this.hasCachedSnapshot(); + } else { + return this.willRender; + } + } + cacheSnapshot() { + if (!this.snapshotCached) { + this.view.cacheSnapshot(this.snapshot).then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot)); + this.snapshotCached = true; + } + } + async render(callback) { + this.cancelRender(); + await new Promise((resolve) => { + this.frame = document.visibilityState === "hidden" ? setTimeout(() => resolve(), 0) : requestAnimationFrame(() => resolve()); + }); + await callback(); + delete this.frame; + } + async renderPageSnapshot(snapshot, isPreview) { + await this.viewTransitioner.renderChange(this.view.shouldTransitionTo(snapshot), async () => { + await this.view.renderPage(snapshot, isPreview, this.willRender, this); + this.performScroll(); + }); + } + cancelRender() { + if (this.frame) { + cancelAnimationFrame(this.frame); + delete this.frame; + } + } +}; +function isSuccessful(statusCode) { + return statusCode >= 200 && statusCode < 300; +} +var BrowserAdapter = class { + progressBar = new ProgressBar(); + constructor(session2) { + this.session = session2; + } + visitProposedToLocation(location2, options) { + if (locationIsVisitable(location2, this.navigator.rootLocation)) { + this.navigator.startVisit(location2, options?.restorationIdentifier || uuid(), options); + } else { + window.location.href = location2.toString(); + } + } + visitStarted(visit2) { + this.location = visit2.location; + this.redirectedToLocation = null; + visit2.loadCachedSnapshot(); + visit2.issueRequest(); + visit2.goToSamePageAnchor(); + } + visitRequestStarted(visit2) { + this.progressBar.setValue(0); + if (visit2.hasCachedSnapshot() || visit2.action != "restore") { + this.showVisitProgressBarAfterDelay(); + } else { + this.showProgressBar(); + } + } + visitRequestCompleted(visit2) { + visit2.loadResponse(); + if (visit2.response.redirected) { + this.redirectedToLocation = visit2.redirectedToLocation; + } + } + visitRequestFailedWithStatusCode(visit2, statusCode) { + switch (statusCode) { + case SystemStatusCode.networkFailure: + case SystemStatusCode.timeoutFailure: + case SystemStatusCode.contentTypeMismatch: + return this.reload({ + reason: "request_failed", + context: { + statusCode + } + }); + default: + return visit2.loadResponse(); + } + } + visitRequestFinished(_visit) { + } + visitCompleted(_visit) { + this.progressBar.setValue(1); + this.hideVisitProgressBar(); + } + pageInvalidated(reason) { + this.reload(reason); + } + visitFailed(_visit) { + this.progressBar.setValue(1); + this.hideVisitProgressBar(); + } + visitRendered(_visit) { + } + // Link prefetching + linkPrefetchingIsEnabledForLocation(location2) { + return true; + } + // Form Submission Delegate + formSubmissionStarted(_formSubmission) { + this.progressBar.setValue(0); + this.showFormProgressBarAfterDelay(); + } + formSubmissionFinished(_formSubmission) { + this.progressBar.setValue(1); + this.hideFormProgressBar(); + } + // Private + showVisitProgressBarAfterDelay() { + this.visitProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay); + } + hideVisitProgressBar() { + this.progressBar.hide(); + if (this.visitProgressBarTimeout != null) { + window.clearTimeout(this.visitProgressBarTimeout); + delete this.visitProgressBarTimeout; + } + } + showFormProgressBarAfterDelay() { + if (this.formProgressBarTimeout == null) { + this.formProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay); + } + } + hideFormProgressBar() { + this.progressBar.hide(); + if (this.formProgressBarTimeout != null) { + window.clearTimeout(this.formProgressBarTimeout); + delete this.formProgressBarTimeout; + } + } + showProgressBar = () => { + this.progressBar.show(); + }; + reload(reason) { + dispatch("turbo:reload", { detail: reason }); + window.location.href = (this.redirectedToLocation || this.location)?.toString() || window.location.href; + } + get navigator() { + return this.session.navigator; + } +}; +var CacheObserver = class { + selector = "[data-turbo-temporary]"; + deprecatedSelector = "[data-turbo-cache=false]"; + started = false; + start() { + if (!this.started) { + this.started = true; + addEventListener("turbo:before-cache", this.removeTemporaryElements, false); + } + } + stop() { + if (this.started) { + this.started = false; + removeEventListener("turbo:before-cache", this.removeTemporaryElements, false); + } + } + removeTemporaryElements = (_event) => { + for (const element of this.temporaryElements) { + element.remove(); + } + }; + get temporaryElements() { + return [...document.querySelectorAll(this.selector), ...this.temporaryElementsWithDeprecation]; + } + get temporaryElementsWithDeprecation() { + const elements = document.querySelectorAll(this.deprecatedSelector); + if (elements.length) { + console.warn( + `The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.` + ); + } + return [...elements]; + } +}; +var FrameRedirector = class { + constructor(session2, element) { + this.session = session2; + this.element = element; + this.linkInterceptor = new LinkInterceptor(this, element); + this.formSubmitObserver = new FormSubmitObserver(this, element); + } + start() { + this.linkInterceptor.start(); + this.formSubmitObserver.start(); + } + stop() { + this.linkInterceptor.stop(); + this.formSubmitObserver.stop(); + } + // Link interceptor delegate + shouldInterceptLinkClick(element, _location, _event) { + return this.#shouldRedirect(element); + } + linkClickIntercepted(element, url, event) { + const frame = this.#findFrameElement(element); + if (frame) { + frame.delegate.linkClickIntercepted(element, url, event); + } + } + // Form submit observer delegate + willSubmitForm(element, submitter2) { + return element.closest("turbo-frame") == null && this.#shouldSubmit(element, submitter2) && this.#shouldRedirect(element, submitter2); + } + formSubmitted(element, submitter2) { + const frame = this.#findFrameElement(element, submitter2); + if (frame) { + frame.delegate.formSubmitted(element, submitter2); + } + } + #shouldSubmit(form, submitter2) { + const action = getAction$1(form, submitter2); + const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`); + const rootLocation = expandURL(meta?.content ?? "/"); + return this.#shouldRedirect(form, submitter2) && locationIsVisitable(action, rootLocation); + } + #shouldRedirect(element, submitter2) { + const isNavigatable = element instanceof HTMLFormElement ? this.session.submissionIsNavigatable(element, submitter2) : this.session.elementIsNavigatable(element); + if (isNavigatable) { + const frame = this.#findFrameElement(element, submitter2); + return frame ? frame != element.closest("turbo-frame") : false; + } else { + return false; + } + } + #findFrameElement(element, submitter2) { + const id = submitter2?.getAttribute("data-turbo-frame") || element.getAttribute("data-turbo-frame"); + if (id && id != "_top") { + const frame = this.element.querySelector(`#${id}:not([disabled])`); + if (frame instanceof FrameElement) { + return frame; + } + } + } +}; +var History = class { + location; + restorationIdentifier = uuid(); + restorationData = {}; + started = false; + pageLoaded = false; + currentIndex = 0; + constructor(delegate) { + this.delegate = delegate; + } + start() { + if (!this.started) { + addEventListener("popstate", this.onPopState, false); + addEventListener("load", this.onPageLoad, false); + this.currentIndex = history.state?.turbo?.restorationIndex || 0; + this.started = true; + this.replace(new URL(window.location.href)); + } + } + stop() { + if (this.started) { + removeEventListener("popstate", this.onPopState, false); + removeEventListener("load", this.onPageLoad, false); + this.started = false; + } + } + push(location2, restorationIdentifier) { + this.update(history.pushState, location2, restorationIdentifier); + } + replace(location2, restorationIdentifier) { + this.update(history.replaceState, location2, restorationIdentifier); + } + update(method, location2, restorationIdentifier = uuid()) { + if (method === history.pushState) ++this.currentIndex; + const state = { turbo: { restorationIdentifier, restorationIndex: this.currentIndex } }; + method.call(history, state, "", location2.href); + this.location = location2; + this.restorationIdentifier = restorationIdentifier; + } + // Restoration data + getRestorationDataForIdentifier(restorationIdentifier) { + return this.restorationData[restorationIdentifier] || {}; + } + updateRestorationData(additionalData) { + const { restorationIdentifier } = this; + const restorationData = this.restorationData[restorationIdentifier]; + this.restorationData[restorationIdentifier] = { + ...restorationData, + ...additionalData + }; + } + // Scroll restoration + assumeControlOfScrollRestoration() { + if (!this.previousScrollRestoration) { + this.previousScrollRestoration = history.scrollRestoration ?? "auto"; + history.scrollRestoration = "manual"; + } + } + relinquishControlOfScrollRestoration() { + if (this.previousScrollRestoration) { + history.scrollRestoration = this.previousScrollRestoration; + delete this.previousScrollRestoration; + } + } + // Event handlers + onPopState = (event) => { + if (this.shouldHandlePopState()) { + const { turbo } = event.state || {}; + if (turbo) { + this.location = new URL(window.location.href); + const { restorationIdentifier, restorationIndex } = turbo; + this.restorationIdentifier = restorationIdentifier; + const direction = restorationIndex > this.currentIndex ? "forward" : "back"; + this.delegate.historyPoppedToLocationWithRestorationIdentifierAndDirection(this.location, restorationIdentifier, direction); + this.currentIndex = restorationIndex; + } + } + }; + onPageLoad = async (_event) => { + await nextMicrotask(); + this.pageLoaded = true; + }; + // Private + shouldHandlePopState() { + return this.pageIsLoaded(); + } + pageIsLoaded() { + return this.pageLoaded || document.readyState == "complete"; + } +}; +var LinkPrefetchObserver = class { + started = false; + #prefetchedLink = null; + constructor(delegate, eventTarget) { + this.delegate = delegate; + this.eventTarget = eventTarget; + } + start() { + if (this.started) return; + if (this.eventTarget.readyState === "loading") { + this.eventTarget.addEventListener("DOMContentLoaded", this.#enable, { once: true }); + } else { + this.#enable(); + } + } + stop() { + if (!this.started) return; + this.eventTarget.removeEventListener("mouseenter", this.#tryToPrefetchRequest, { + capture: true, + passive: true + }); + this.eventTarget.removeEventListener("mouseleave", this.#cancelRequestIfObsolete, { + capture: true, + passive: true + }); + this.eventTarget.removeEventListener("turbo:before-fetch-request", this.#tryToUsePrefetchedRequest, true); + this.started = false; + } + #enable = () => { + this.eventTarget.addEventListener("mouseenter", this.#tryToPrefetchRequest, { + capture: true, + passive: true + }); + this.eventTarget.addEventListener("mouseleave", this.#cancelRequestIfObsolete, { + capture: true, + passive: true + }); + this.eventTarget.addEventListener("turbo:before-fetch-request", this.#tryToUsePrefetchedRequest, true); + this.started = true; + }; + #tryToPrefetchRequest = (event) => { + if (getMetaContent("turbo-prefetch") === "false") return; + const target = event.target; + const isLink = target.matches && target.matches("a[href]:not([target^=_]):not([download])"); + if (isLink && this.#isPrefetchable(target)) { + const link = target; + const location2 = getLocationForLink(link); + if (this.delegate.canPrefetchRequestToLocation(link, location2)) { + this.#prefetchedLink = link; + const fetchRequest = new FetchRequest( + this, + FetchMethod.get, + location2, + new URLSearchParams(), + target + ); + fetchRequest.fetchOptions.priority = "low"; + prefetchCache.setLater(location2.toString(), fetchRequest, this.#cacheTtl); + } + } + }; + #cancelRequestIfObsolete = (event) => { + if (event.target === this.#prefetchedLink) this.#cancelPrefetchRequest(); + }; + #cancelPrefetchRequest = () => { + prefetchCache.clear(); + this.#prefetchedLink = null; + }; + #tryToUsePrefetchedRequest = (event) => { + if (event.target.tagName !== "FORM" && event.detail.fetchOptions.method === "GET") { + const cached = prefetchCache.get(event.detail.url.toString()); + if (cached) { + event.detail.fetchRequest = cached; + } + prefetchCache.clear(); + } + }; + prepareRequest(request) { + const link = request.target; + request.headers["X-Sec-Purpose"] = "prefetch"; + const turboFrame = link.closest("turbo-frame"); + const turboFrameTarget = link.getAttribute("data-turbo-frame") || turboFrame?.getAttribute("target") || turboFrame?.id; + if (turboFrameTarget && turboFrameTarget !== "_top") { + request.headers["Turbo-Frame"] = turboFrameTarget; + } + } + // Fetch request interface + requestSucceededWithResponse() { + } + requestStarted(fetchRequest) { + } + requestErrored(fetchRequest) { + } + requestFinished(fetchRequest) { + } + requestPreventedHandlingResponse(fetchRequest, fetchResponse) { + } + requestFailedWithResponse(fetchRequest, fetchResponse) { + } + get #cacheTtl() { + return Number(getMetaContent("turbo-prefetch-cache-time")) || cacheTtl; + } + #isPrefetchable(link) { + const href = link.getAttribute("href"); + if (!href) return false; + if (unfetchableLink(link)) return false; + if (linkToTheSamePage(link)) return false; + if (linkOptsOut(link)) return false; + if (nonSafeLink(link)) return false; + if (eventPrevented(link)) return false; + return true; + } +}; +var unfetchableLink = (link) => { + return link.origin !== document.location.origin || !["http:", "https:"].includes(link.protocol) || link.hasAttribute("target"); +}; +var linkToTheSamePage = (link) => { + return link.pathname + link.search === document.location.pathname + document.location.search || link.href.startsWith("#"); +}; +var linkOptsOut = (link) => { + if (link.getAttribute("data-turbo-prefetch") === "false") return true; + if (link.getAttribute("data-turbo") === "false") return true; + const turboPrefetchParent = findClosestRecursively(link, "[data-turbo-prefetch]"); + if (turboPrefetchParent && turboPrefetchParent.getAttribute("data-turbo-prefetch") === "false") return true; + return false; +}; +var nonSafeLink = (link) => { + const turboMethod = link.getAttribute("data-turbo-method"); + if (turboMethod && turboMethod.toLowerCase() !== "get") return true; + if (isUJS(link)) return true; + if (link.hasAttribute("data-turbo-confirm")) return true; + if (link.hasAttribute("data-turbo-stream")) return true; + return false; +}; +var isUJS = (link) => { + return link.hasAttribute("data-remote") || link.hasAttribute("data-behavior") || link.hasAttribute("data-confirm") || link.hasAttribute("data-method"); +}; +var eventPrevented = (link) => { + const event = dispatch("turbo:before-prefetch", { target: link, cancelable: true }); + return event.defaultPrevented; +}; +var Navigator = class { + constructor(delegate) { + this.delegate = delegate; + } + proposeVisit(location2, options = {}) { + if (this.delegate.allowsVisitingLocationWithAction(location2, options.action)) { + this.delegate.visitProposedToLocation(location2, options); + } + } + startVisit(locatable, restorationIdentifier, options = {}) { + this.stop(); + this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, { + referrer: this.location, + ...options + }); + this.currentVisit.start(); + } + submitForm(form, submitter2) { + this.stop(); + this.formSubmission = new FormSubmission(this, form, submitter2, true); + this.formSubmission.start(); + } + stop() { + if (this.formSubmission) { + this.formSubmission.stop(); + delete this.formSubmission; + } + if (this.currentVisit) { + this.currentVisit.cancel(); + delete this.currentVisit; + } + } + get adapter() { + return this.delegate.adapter; + } + get view() { + return this.delegate.view; + } + get rootLocation() { + return this.view.snapshot.rootLocation; + } + get history() { + return this.delegate.history; + } + // Form submission delegate + formSubmissionStarted(formSubmission) { + if (typeof this.adapter.formSubmissionStarted === "function") { + this.adapter.formSubmissionStarted(formSubmission); + } + } + async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) { + if (formSubmission == this.formSubmission) { + const responseHTML = await fetchResponse.responseHTML; + if (responseHTML) { + const shouldCacheSnapshot = formSubmission.isSafe; + if (!shouldCacheSnapshot) { + this.view.clearSnapshotCache(); + } + const { statusCode, redirected } = fetchResponse; + const action = this.#getActionForFormSubmission(formSubmission, fetchResponse); + const visitOptions = { + action, + shouldCacheSnapshot, + response: { statusCode, responseHTML, redirected } + }; + this.proposeVisit(fetchResponse.location, visitOptions); + } + } + } + async formSubmissionFailedWithResponse(formSubmission, fetchResponse) { + const responseHTML = await fetchResponse.responseHTML; + if (responseHTML) { + const snapshot = PageSnapshot.fromHTMLString(responseHTML); + if (fetchResponse.serverError) { + await this.view.renderError(snapshot, this.currentVisit); + } else { + await this.view.renderPage(snapshot, false, true, this.currentVisit); + } + if (!snapshot.shouldPreserveScrollPosition) { + this.view.scrollToTop(); + } + this.view.clearSnapshotCache(); + } + } + formSubmissionErrored(formSubmission, error2) { + console.error(error2); + } + formSubmissionFinished(formSubmission) { + if (typeof this.adapter.formSubmissionFinished === "function") { + this.adapter.formSubmissionFinished(formSubmission); + } + } + // Link prefetching + linkPrefetchingIsEnabledForLocation(location2) { + if (typeof this.adapter.linkPrefetchingIsEnabledForLocation === "function") { + return this.adapter.linkPrefetchingIsEnabledForLocation(location2); + } + return true; + } + // Visit delegate + visitStarted(visit2) { + this.delegate.visitStarted(visit2); + } + visitCompleted(visit2) { + this.delegate.visitCompleted(visit2); + delete this.currentVisit; + } + locationWithActionIsSamePage(location2, action) { + const anchor = getAnchor(location2); + const currentAnchor = getAnchor(this.view.lastRenderedLocation); + const isRestorationToTop = action === "restore" && typeof anchor === "undefined"; + return action !== "replace" && getRequestURL(location2) === getRequestURL(this.view.lastRenderedLocation) && (isRestorationToTop || anchor != null && anchor !== currentAnchor); + } + visitScrolledToSamePageLocation(oldURL, newURL) { + this.delegate.visitScrolledToSamePageLocation(oldURL, newURL); + } + // Visits + get location() { + return this.history.location; + } + get restorationIdentifier() { + return this.history.restorationIdentifier; + } + #getActionForFormSubmission(formSubmission, fetchResponse) { + const { submitter: submitter2, formElement } = formSubmission; + return getVisitAction(submitter2, formElement) || this.#getDefaultAction(fetchResponse); + } + #getDefaultAction(fetchResponse) { + const sameLocationRedirect = fetchResponse.redirected && fetchResponse.location.href === this.location?.href; + return sameLocationRedirect ? "replace" : "advance"; + } +}; +var PageStage = { + initial: 0, + loading: 1, + interactive: 2, + complete: 3 +}; +var PageObserver = class { + stage = PageStage.initial; + started = false; + constructor(delegate) { + this.delegate = delegate; + } + start() { + if (!this.started) { + if (this.stage == PageStage.initial) { + this.stage = PageStage.loading; + } + document.addEventListener("readystatechange", this.interpretReadyState, false); + addEventListener("pagehide", this.pageWillUnload, false); + this.started = true; + } + } + stop() { + if (this.started) { + document.removeEventListener("readystatechange", this.interpretReadyState, false); + removeEventListener("pagehide", this.pageWillUnload, false); + this.started = false; + } + } + interpretReadyState = () => { + const { readyState } = this; + if (readyState == "interactive") { + this.pageIsInteractive(); + } else if (readyState == "complete") { + this.pageIsComplete(); + } + }; + pageIsInteractive() { + if (this.stage == PageStage.loading) { + this.stage = PageStage.interactive; + this.delegate.pageBecameInteractive(); + } + } + pageIsComplete() { + this.pageIsInteractive(); + if (this.stage == PageStage.interactive) { + this.stage = PageStage.complete; + this.delegate.pageLoaded(); + } + } + pageWillUnload = () => { + this.delegate.pageWillUnload(); + }; + get readyState() { + return document.readyState; + } +}; +var ScrollObserver = class { + started = false; + constructor(delegate) { + this.delegate = delegate; + } + start() { + if (!this.started) { + addEventListener("scroll", this.onScroll, false); + this.onScroll(); + this.started = true; + } + } + stop() { + if (this.started) { + removeEventListener("scroll", this.onScroll, false); + this.started = false; + } + } + onScroll = () => { + this.updatePosition({ x: window.pageXOffset, y: window.pageYOffset }); + }; + // Private + updatePosition(position) { + this.delegate.scrollPositionChanged(position); + } +}; +var StreamMessageRenderer = class { + render({ fragment }) { + Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => { + withAutofocusFromFragment(fragment, () => { + withPreservedFocus(() => { + document.documentElement.appendChild(fragment); + }); + }); + }); + } + // Bardo delegate + enteringBardo(currentPermanentElement, newPermanentElement) { + newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true)); + } + leavingBardo() { + } +}; +function getPermanentElementMapForFragment(fragment) { + const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement); + const permanentElementMap = {}; + for (const permanentElementInDocument of permanentElementsInDocument) { + const { id } = permanentElementInDocument; + for (const streamElement of fragment.querySelectorAll("turbo-stream")) { + const elementInStream = getPermanentElementById(streamElement.templateElement.content, id); + if (elementInStream) { + permanentElementMap[id] = [permanentElementInDocument, elementInStream]; + } + } + } + return permanentElementMap; +} +async function withAutofocusFromFragment(fragment, callback) { + const generatedID = `turbo-stream-autofocus-${uuid()}`; + const turboStreams = fragment.querySelectorAll("turbo-stream"); + const elementWithAutofocus = firstAutofocusableElementInStreams(turboStreams); + let willAutofocusId = null; + if (elementWithAutofocus) { + if (elementWithAutofocus.id) { + willAutofocusId = elementWithAutofocus.id; + } else { + willAutofocusId = generatedID; + } + elementWithAutofocus.id = willAutofocusId; + } + callback(); + await nextRepaint(); + const hasNoActiveElement = document.activeElement == null || document.activeElement == document.body; + if (hasNoActiveElement && willAutofocusId) { + const elementToAutofocus = document.getElementById(willAutofocusId); + if (elementIsFocusable(elementToAutofocus)) { + elementToAutofocus.focus(); + } + if (elementToAutofocus && elementToAutofocus.id == generatedID) { + elementToAutofocus.removeAttribute("id"); + } + } +} +async function withPreservedFocus(callback) { + const [activeElementBeforeRender, activeElementAfterRender] = await around(callback, () => document.activeElement); + const restoreFocusTo = activeElementBeforeRender && activeElementBeforeRender.id; + if (restoreFocusTo) { + const elementToFocus = document.getElementById(restoreFocusTo); + if (elementIsFocusable(elementToFocus) && elementToFocus != activeElementAfterRender) { + elementToFocus.focus(); + } + } +} +function firstAutofocusableElementInStreams(nodeListOfStreamElements) { + for (const streamElement of nodeListOfStreamElements) { + const elementWithAutofocus = queryAutofocusableElement(streamElement.templateElement.content); + if (elementWithAutofocus) return elementWithAutofocus; + } + return null; +} +var StreamObserver = class { + sources = /* @__PURE__ */ new Set(); + #started = false; + constructor(delegate) { + this.delegate = delegate; + } + start() { + if (!this.#started) { + this.#started = true; + addEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false); + } + } + stop() { + if (this.#started) { + this.#started = false; + removeEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false); + } + } + connectStreamSource(source) { + if (!this.streamSourceIsConnected(source)) { + this.sources.add(source); + source.addEventListener("message", this.receiveMessageEvent, false); + } + } + disconnectStreamSource(source) { + if (this.streamSourceIsConnected(source)) { + this.sources.delete(source); + source.removeEventListener("message", this.receiveMessageEvent, false); + } + } + streamSourceIsConnected(source) { + return this.sources.has(source); + } + inspectFetchResponse = (event) => { + const response = fetchResponseFromEvent(event); + if (response && fetchResponseIsStream(response)) { + event.preventDefault(); + this.receiveMessageResponse(response); + } + }; + receiveMessageEvent = (event) => { + if (this.#started && typeof event.data == "string") { + this.receiveMessageHTML(event.data); + } + }; + async receiveMessageResponse(response) { + const html = await response.responseHTML; + if (html) { + this.receiveMessageHTML(html); + } + } + receiveMessageHTML(html) { + this.delegate.receivedMessageFromStream(StreamMessage.wrap(html)); + } +}; +function fetchResponseFromEvent(event) { + const fetchResponse = event.detail?.fetchResponse; + if (fetchResponse instanceof FetchResponse) { + return fetchResponse; + } +} +function fetchResponseIsStream(response) { + const contentType = response.contentType ?? ""; + return contentType.startsWith(StreamMessage.contentType); +} +var ErrorRenderer = class extends Renderer { + static renderElement(currentElement, newElement) { + const { documentElement, body } = document; + documentElement.replaceChild(newElement, body); + } + async render() { + this.replaceHeadAndBody(); + this.activateScriptElements(); + } + replaceHeadAndBody() { + const { documentElement, head } = document; + documentElement.replaceChild(this.newHead, head); + this.renderElement(this.currentElement, this.newElement); + } + activateScriptElements() { + for (const replaceableElement of this.scriptElements) { + const parentNode = replaceableElement.parentNode; + if (parentNode) { + const element = activateScriptElement(replaceableElement); + parentNode.replaceChild(element, replaceableElement); + } + } + } + get newHead() { + return this.newSnapshot.headSnapshot.element; + } + get scriptElements() { + return document.documentElement.querySelectorAll("script"); + } +}; +var PageRenderer = class extends Renderer { + static renderElement(currentElement, newElement) { + if (document.body && newElement instanceof HTMLBodyElement) { + document.body.replaceWith(newElement); + } else { + document.documentElement.appendChild(newElement); + } + } + get shouldRender() { + return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical; + } + get reloadReason() { + if (!this.newSnapshot.isVisitable) { + return { + reason: "turbo_visit_control_is_reload" + }; + } + if (!this.trackedElementsAreIdentical) { + return { + reason: "tracked_element_mismatch" + }; + } + } + async prepareToRender() { + this.#setLanguage(); + await this.mergeHead(); + } + async render() { + if (this.willRender) { + await this.replaceBody(); + } + } + finishRendering() { + super.finishRendering(); + if (!this.isPreview) { + this.focusFirstAutofocusableElement(); + } + } + get currentHeadSnapshot() { + return this.currentSnapshot.headSnapshot; + } + get newHeadSnapshot() { + return this.newSnapshot.headSnapshot; + } + get newElement() { + return this.newSnapshot.element; + } + #setLanguage() { + const { documentElement } = this.currentSnapshot; + const { lang } = this.newSnapshot; + if (lang) { + documentElement.setAttribute("lang", lang); + } else { + documentElement.removeAttribute("lang"); + } + } + async mergeHead() { + const mergedHeadElements = this.mergeProvisionalElements(); + const newStylesheetElements = this.copyNewHeadStylesheetElements(); + this.copyNewHeadScriptElements(); + await mergedHeadElements; + await newStylesheetElements; + if (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 loadingElements = []; + for (const element of this.newHeadStylesheetElements) { + loadingElements.push(waitForLoad(element)); + document.head.appendChild(element); + } + await Promise.all(loadingElements); + } + copyNewHeadScriptElements() { + for (const element of this.newHeadScriptElements) { + document.head.appendChild(activateScriptElement(element)); + } + } + removeUnusedDynamicStylesheetElements() { + for (const element of this.unusedDynamicStylesheetElements) { + document.head.removeChild(element); + } + } + async mergeProvisionalElements() { + const newHeadElements = [...this.newHeadProvisionalElements]; + for (const element of this.currentHeadProvisionalElements) { + if (!this.isCurrentElementInElementList(element, newHeadElements)) { + document.head.removeChild(element); + } + } + for (const element of newHeadElements) { + document.head.appendChild(element); + } + } + isCurrentElementInElementList(element, elementList) { + for (const [index, newElement] of elementList.entries()) { + if (element.tagName == "TITLE") { + if (newElement.tagName != "TITLE") { + continue; + } + if (element.innerHTML == newElement.innerHTML) { + elementList.splice(index, 1); + return true; + } + } + if (newElement.isEqualNode(element)) { + elementList.splice(index, 1); + return true; + } + } + return false; + } + removeCurrentHeadProvisionalElements() { + for (const element of this.currentHeadProvisionalElements) { + document.head.removeChild(element); + } + } + copyNewHeadProvisionalElements() { + for (const element of this.newHeadProvisionalElements) { + document.head.appendChild(element); + } + } + activateNewBody() { + document.adoptNode(this.newElement); + this.activateNewBodyScriptElements(); + } + activateNewBodyScriptElements() { + for (const inertScriptElement of this.newBodyScriptElements) { + const activatedScriptElement = activateScriptElement(inertScriptElement); + inertScriptElement.replaceWith(activatedScriptElement); + } + } + async assignNewBody() { + await this.renderElement(this.currentElement, this.newElement); + } + get unusedDynamicStylesheetElements() { + return this.oldHeadStylesheetElements.filter((element) => { + return element.getAttribute("data-turbo-track") === "dynamic"; + }); + } + 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"); + } +}; +var MorphingPageRenderer = class extends PageRenderer { + static renderElement(currentElement, newElement) { + morphElements(currentElement, newElement, { + callbacks: { + beforeNodeMorphed: (node, newNode) => { + if (shouldRefreshFrameWithMorphing(node, newNode) && !closestFrameReloadableWithMorphing(node)) { + node.reload(); + return false; + } + return true; + } + } + }); + dispatch("turbo:morph", { detail: { currentElement, newElement } }); + } + async preservingPermanentElements(callback) { + return await callback(); + } + get renderMethod() { + return "morph"; + } + get shouldAutofocus() { + return false; + } +}; +var SnapshotCache = class { + keys = []; + snapshots = {}; + constructor(size) { + this.size = size; + } + has(location2) { + return toCacheKey(location2) in this.snapshots; + } + get(location2) { + if (this.has(location2)) { + const snapshot = this.read(location2); + this.touch(location2); + return snapshot; + } + } + put(location2, snapshot) { + this.write(location2, snapshot); + this.touch(location2); + return snapshot; + } + clear() { + this.snapshots = {}; + } + // Private + read(location2) { + return this.snapshots[toCacheKey(location2)]; + } + write(location2, snapshot) { + this.snapshots[toCacheKey(location2)] = snapshot; + } + touch(location2) { + const key = toCacheKey(location2); + const index = this.keys.indexOf(key); + if (index > -1) this.keys.splice(index, 1); + this.keys.unshift(key); + this.trim(); + } + trim() { + for (const key of this.keys.splice(this.size)) { + delete this.snapshots[key]; + } + } +}; +var PageView = class extends View { + snapshotCache = new SnapshotCache(10); + lastRenderedLocation = new URL(location.href); + forceReloaded = false; + shouldTransitionTo(newSnapshot) { + return this.snapshot.prefersViewTransitions && newSnapshot.prefersViewTransitions; + } + renderPage(snapshot, isPreview = false, willRender = true, visit2) { + const shouldMorphPage = this.isPageRefresh(visit2) && this.snapshot.shouldMorphPage; + const rendererClass = shouldMorphPage ? MorphingPageRenderer : PageRenderer; + const renderer = new rendererClass(this.snapshot, snapshot, isPreview, willRender); + if (!renderer.shouldRender) { + this.forceReloaded = true; + } else { + visit2?.changeHistory(); + } + return this.render(renderer); + } + renderError(snapshot, visit2) { + visit2?.changeHistory(); + const renderer = new ErrorRenderer(this.snapshot, snapshot, false); + return this.render(renderer); + } + clearSnapshotCache() { + this.snapshotCache.clear(); + } + async cacheSnapshot(snapshot = this.snapshot) { + if (snapshot.isCacheable) { + this.delegate.viewWillCacheSnapshot(); + const { lastRenderedLocation: location2 } = this; + await nextEventLoopTick(); + const cachedSnapshot = snapshot.clone(); + this.snapshotCache.put(location2, cachedSnapshot); + return cachedSnapshot; + } + } + getCachedSnapshotForLocation(location2) { + return this.snapshotCache.get(location2); + } + isPageRefresh(visit2) { + return !visit2 || this.lastRenderedLocation.pathname === visit2.location.pathname && visit2.action === "replace"; + } + shouldPreserveScrollPosition(visit2) { + return this.isPageRefresh(visit2) && this.snapshot.shouldPreserveScrollPosition; + } + get snapshot() { + return PageSnapshot.fromElement(this.element); + } +}; +var Preloader = class { + selector = "a[data-turbo-preload]"; + constructor(delegate, snapshotCache) { + this.delegate = delegate; + this.snapshotCache = snapshotCache; + } + start() { + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", this.#preloadAll); + } else { + this.preloadOnLoadLinksForView(document.body); + } + } + stop() { + document.removeEventListener("DOMContentLoaded", this.#preloadAll); + } + preloadOnLoadLinksForView(element) { + for (const link of element.querySelectorAll(this.selector)) { + if (this.delegate.shouldPreloadLink(link)) { + this.preloadURL(link); + } + } + } + async preloadURL(link) { + const location2 = new URL(link.href); + if (this.snapshotCache.has(location2)) { + return; + } + const fetchRequest = new FetchRequest(this, FetchMethod.get, location2, new URLSearchParams(), link); + await fetchRequest.perform(); + } + // Fetch request delegate + prepareRequest(fetchRequest) { + fetchRequest.headers["X-Sec-Purpose"] = "prefetch"; + } + async requestSucceededWithResponse(fetchRequest, fetchResponse) { + try { + const responseHTML = await fetchResponse.responseHTML; + const snapshot = PageSnapshot.fromHTMLString(responseHTML); + this.snapshotCache.put(fetchRequest.url, snapshot); + } catch (_) { + } + } + requestStarted(fetchRequest) { + } + requestErrored(fetchRequest) { + } + requestFinished(fetchRequest) { + } + requestPreventedHandlingResponse(fetchRequest, fetchResponse) { + } + requestFailedWithResponse(fetchRequest, fetchResponse) { + } + #preloadAll = () => { + this.preloadOnLoadLinksForView(document.body); + }; +}; +var Cache = class { + constructor(session2) { + this.session = session2; + } + clear() { + this.session.clearCache(); + } + resetCacheControl() { + this.#setCacheControl(""); + } + exemptPageFromCache() { + this.#setCacheControl("no-cache"); + } + exemptPageFromPreview() { + this.#setCacheControl("no-preview"); + } + #setCacheControl(value) { + setMetaContent("turbo-cache-control", value); + } +}; +var Session = class { + navigator = new Navigator(this); + history = new History(this); + view = new PageView(this, document.documentElement); + adapter = new BrowserAdapter(this); + pageObserver = new PageObserver(this); + cacheObserver = new CacheObserver(); + linkPrefetchObserver = new LinkPrefetchObserver(this, document); + linkClickObserver = new LinkClickObserver(this, window); + formSubmitObserver = new FormSubmitObserver(this, document); + scrollObserver = new ScrollObserver(this); + streamObserver = new StreamObserver(this); + formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement); + frameRedirector = new FrameRedirector(this, document.documentElement); + streamMessageRenderer = new StreamMessageRenderer(); + cache = new Cache(this); + enabled = true; + started = false; + #pageRefreshDebouncePeriod = 150; + constructor(recentRequests2) { + this.recentRequests = recentRequests2; + this.preloader = new Preloader(this, this.view.snapshotCache); + this.debouncedRefresh = this.refresh; + this.pageRefreshDebouncePeriod = this.pageRefreshDebouncePeriod; + } + start() { + if (!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 = true; + this.enabled = true; + } + } + disable() { + this.enabled = false; + } + stop() { + if (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 = false; + } + } + registerAdapter(adapter) { + this.adapter = adapter; + } + visit(location2, options = {}) { + const frameElement = options.frame ? document.getElementById(options.frame) : null; + if (frameElement instanceof FrameElement) { + const action = options.action || getVisitAction(frameElement); + frameElement.delegate.proposeVisitIfNavigatedWithAction(frameElement, action); + frameElement.src = location2.toString(); + } else { + this.navigator.proposeVisit(expandURL(location2), options); + } + } + refresh(url, requestId) { + const isRecentRequest = requestId && this.recentRequests.has(requestId); + const isCurrentUrl = url === document.baseURI; + if (!isRecentRequest && !this.navigator.currentVisit && isCurrentUrl) { + this.visit(url, { action: "replace", shouldCacheSnapshot: false }); + } + } + connectStreamSource(source) { + this.streamObserver.connectStreamSource(source); + } + disconnectStreamSource(source) { + this.streamObserver.disconnectStreamSource(source); + } + renderStreamMessage(message) { + this.streamMessageRenderer.render(StreamMessage.wrap(message)); + } + clearCache() { + this.view.clearSnapshotCache(); + } + setProgressBarDelay(delay) { + 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 = delay; + } + set progressBarDelay(delay) { + config.drive.progressBarDelay = delay; + } + get progressBarDelay() { + return config.drive.progressBarDelay; + } + set drive(value) { + config.drive.enabled = value; + } + get drive() { + return config.drive.enabled; + } + set formMode(value) { + config.forms.mode = value; + } + get formMode() { + return config.forms.mode; + } + get location() { + return this.history.location; + } + get restorationIdentifier() { + return this.history.restorationIdentifier; + } + get pageRefreshDebouncePeriod() { + return this.#pageRefreshDebouncePeriod; + } + set pageRefreshDebouncePeriod(value) { + this.refresh = debounce(this.debouncedRefresh.bind(this), value); + this.#pageRefreshDebouncePeriod = value; + } + // Preloader delegate + shouldPreloadLink(element) { + const isUnsafe = element.hasAttribute("data-turbo-method"); + const isStream = element.hasAttribute("data-turbo-stream"); + const frameTarget = element.getAttribute("data-turbo-frame"); + const frame = frameTarget == "_top" ? null : document.getElementById(frameTarget) || findClosestRecursively(element, "turbo-frame:not([disabled])"); + if (isUnsafe || isStream || frame instanceof FrameElement) { + return false; + } else { + const location2 = new URL(element.href); + return this.elementIsNavigatable(element) && locationIsVisitable(location2, this.snapshot.rootLocation); + } + } + // History delegate + historyPoppedToLocationWithRestorationIdentifierAndDirection(location2, restorationIdentifier, direction) { + if (this.enabled) { + this.navigator.startVisit(location2, restorationIdentifier, { + action: "restore", + historyChanged: true, + direction + }); + } else { + this.adapter.pageInvalidated({ + reason: "turbo_disabled" + }); + } + } + // Scroll observer delegate + scrollPositionChanged(position) { + this.history.updateRestorationData({ scrollPosition: position }); + } + // Form click observer delegate + willSubmitFormLinkToLocation(link, location2) { + return this.elementIsNavigatable(link) && locationIsVisitable(location2, this.snapshot.rootLocation); + } + submittedFormLinkToLocation() { + } + // Link hover observer delegate + canPrefetchRequestToLocation(link, location2) { + return this.elementIsNavigatable(link) && locationIsVisitable(location2, this.snapshot.rootLocation) && this.navigator.linkPrefetchingIsEnabledForLocation(location2); + } + // Link click observer delegate + willFollowLinkToLocation(link, location2, event) { + return this.elementIsNavigatable(link) && locationIsVisitable(location2, this.snapshot.rootLocation) && this.applicationAllowsFollowingLinkToLocation(link, location2, event); + } + followedLinkToLocation(link, location2) { + const action = this.getActionForLink(link); + const acceptsStreamResponse = link.hasAttribute("data-turbo-stream"); + this.visit(location2.href, { action, acceptsStreamResponse }); + } + // Navigator delegate + allowsVisitingLocationWithAction(location2, action) { + return this.locationWithActionIsSamePage(location2, action) || this.applicationAllowsVisitingLocation(location2); + } + visitProposedToLocation(location2, options) { + extendURLWithDeprecatedProperties(location2); + this.adapter.visitProposedToLocation(location2, options); + } + // Visit delegate + visitStarted(visit2) { + if (!visit2.acceptsStreamResponse) { + markAsBusy(document.documentElement); + this.view.markVisitDirection(visit2.direction); + } + extendURLWithDeprecatedProperties(visit2.location); + if (!visit2.silent) { + this.notifyApplicationAfterVisitingLocation(visit2.location, visit2.action); + } + } + visitCompleted(visit2) { + this.view.unmarkVisitDirection(); + clearBusyState(document.documentElement); + this.notifyApplicationAfterPageLoad(visit2.getTimingMetrics()); + } + locationWithActionIsSamePage(location2, action) { + return this.navigator.locationWithActionIsSamePage(location2, action); + } + visitScrolledToSamePageLocation(oldURL, newURL) { + this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL); + } + // Form submit observer delegate + willSubmitForm(form, submitter2) { + const action = getAction$1(form, submitter2); + return this.submissionIsNavigatable(form, submitter2) && locationIsVisitable(expandURL(action), this.snapshot.rootLocation); + } + formSubmitted(form, submitter2) { + this.navigator.submitForm(form, submitter2); + } + // Page observer delegate + pageBecameInteractive() { + this.view.lastRenderedLocation = this.location; + this.notifyApplicationAfterPageLoad(); + } + pageLoaded() { + this.history.assumeControlOfScrollRestoration(); + } + pageWillUnload() { + this.history.relinquishControlOfScrollRestoration(); + } + // Stream observer delegate + receivedMessageFromStream(message) { + this.renderStreamMessage(message); + } + // Page view delegate + viewWillCacheSnapshot() { + if (!this.navigator.currentVisit?.silent) { + this.notifyApplicationBeforeCachingSnapshot(); + } + } + allowsImmediateRender({ element }, options) { + const event = this.notifyApplicationBeforeRender(element, options); + const { + defaultPrevented, + detail: { render } + } = event; + if (this.view.renderer && render) { + this.view.renderer.renderElement = render; + } + return !defaultPrevented; + } + viewRenderedSnapshot(_snapshot, _isPreview, renderMethod) { + this.view.lastRenderedLocation = this.history.location; + this.notifyApplicationAfterRender(renderMethod); + } + preloadOnLoadLinksForView(element) { + this.preloader.preloadOnLoadLinksForView(element); + } + viewInvalidated(reason) { + this.adapter.pageInvalidated(reason); + } + // Frame element + frameLoaded(frame) { + this.notifyApplicationAfterFrameLoad(frame); + } + frameRendered(fetchResponse, frame) { + this.notifyApplicationAfterFrameRender(fetchResponse, frame); + } + // Application events + applicationAllowsFollowingLinkToLocation(link, location2, ev) { + const event = this.notifyApplicationAfterClickingLinkToLocation(link, location2, ev); + return !event.defaultPrevented; + } + applicationAllowsVisitingLocation(location2) { + const event = this.notifyApplicationBeforeVisitingLocation(location2); + return !event.defaultPrevented; + } + notifyApplicationAfterClickingLinkToLocation(link, location2, event) { + return dispatch("turbo:click", { + target: link, + detail: { url: location2.href, originalEvent: event }, + cancelable: true + }); + } + notifyApplicationBeforeVisitingLocation(location2) { + return dispatch("turbo:before-visit", { + detail: { url: location2.href }, + cancelable: true + }); + } + notifyApplicationAfterVisitingLocation(location2, action) { + return dispatch("turbo:visit", { detail: { url: location2.href, action } }); + } + notifyApplicationBeforeCachingSnapshot() { + return dispatch("turbo:before-cache"); + } + notifyApplicationBeforeRender(newBody, options) { + return dispatch("turbo:before-render", { + detail: { newBody, ...options }, + cancelable: true + }); + } + notifyApplicationAfterRender(renderMethod) { + return dispatch("turbo:render", { detail: { renderMethod } }); + } + notifyApplicationAfterPageLoad(timing = {}) { + return dispatch("turbo:load", { + detail: { url: this.location.href, timing } + }); + } + notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) { + dispatchEvent( + new HashChangeEvent("hashchange", { + oldURL: oldURL.toString(), + newURL: newURL.toString() + }) + ); + } + notifyApplicationAfterFrameLoad(frame) { + return dispatch("turbo:frame-load", { target: frame }); + } + notifyApplicationAfterFrameRender(fetchResponse, frame) { + return dispatch("turbo:frame-render", { + detail: { fetchResponse }, + target: frame, + cancelable: true + }); + } + // Helpers + submissionIsNavigatable(form, submitter2) { + if (config.forms.mode == "off") { + return false; + } else { + const submitterIsNavigatable = submitter2 ? this.elementIsNavigatable(submitter2) : true; + if (config.forms.mode == "optin") { + return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null; + } else { + return submitterIsNavigatable && this.elementIsNavigatable(form); + } + } + } + elementIsNavigatable(element) { + const container = findClosestRecursively(element, "[data-turbo]"); + const withinFrame = findClosestRecursively(element, "turbo-frame"); + if (config.drive.enabled || withinFrame) { + if (container) { + return container.getAttribute("data-turbo") != "false"; + } else { + return true; + } + } else { + if (container) { + return container.getAttribute("data-turbo") == "true"; + } else { + return false; + } + } + } + // Private + getActionForLink(link) { + return getVisitAction(link) || "advance"; + } + get snapshot() { + return this.view.snapshot; + } +}; +function extendURLWithDeprecatedProperties(url) { + Object.defineProperties(url, deprecatedLocationPropertyDescriptors); +} +var deprecatedLocationPropertyDescriptors = { + absoluteURL: { + get() { + return this.toString(); + } + } +}; +var session = new Session(recentRequests); +var { cache, navigator: navigator$1 } = session; +function start() { + session.start(); +} +function registerAdapter(adapter) { + session.registerAdapter(adapter); +} +function visit(location2, options) { + session.visit(location2, options); +} +function connectStreamSource(source) { + session.connectStreamSource(source); +} +function disconnectStreamSource(source) { + session.disconnectStreamSource(source); +} +function renderStreamMessage(message) { + session.renderStreamMessage(message); +} +function clearCache() { + console.warn( + "Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`" + ); + session.clearCache(); +} +function setProgressBarDelay(delay) { + 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.`" + ); + config.drive.progressBarDelay = delay; +} +function setConfirmMethod(confirmMethod) { + 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.`" + ); + config.forms.confirm = confirmMethod; +} +function setFormMode(mode) { + 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.`" + ); + config.forms.mode = mode; +} +function morphBodyElements(currentBody, newBody) { + MorphingPageRenderer.renderElement(currentBody, newBody); +} +function morphTurboFrameElements(currentFrame, newFrame) { + MorphingFrameRenderer.renderElement(currentFrame, newFrame); +} +var Turbo = /* @__PURE__ */ Object.freeze({ + __proto__: null, + navigator: navigator$1, + session, + cache, + PageRenderer, + PageSnapshot, + FrameRenderer, + fetch: fetchWithTurboHeaders, + config, + start, + registerAdapter, + visit, + connectStreamSource, + disconnectStreamSource, + renderStreamMessage, + clearCache, + setProgressBarDelay, + setConfirmMethod, + setFormMode, + morphBodyElements, + morphTurboFrameElements, + morphChildren, + morphElements +}); +var TurboFrameMissingError = class extends Error { +}; +var FrameController = class { + fetchResponseLoaded = (_fetchResponse) => Promise.resolve(); + #currentFetchRequest = null; + #resolveVisitPromise = () => { + }; + #connected = false; + #hasBeenLoaded = false; + #ignoredAttributes = /* @__PURE__ */ new Set(); + #shouldMorphFrame = false; + action = null; + constructor(element) { + this.element = element; + this.view = new FrameView(this, this.element); + this.appearanceObserver = new AppearanceObserver(this, this.element); + this.formLinkClickObserver = new FormLinkClickObserver(this, this.element); + this.linkInterceptor = new LinkInterceptor(this, this.element); + this.restorationIdentifier = uuid(); + this.formSubmitObserver = new FormSubmitObserver(this, this.element); + } + // Frame delegate + connect() { + if (!this.#connected) { + this.#connected = true; + if (this.loadingStyle == FrameLoadingStyle.lazy) { + this.appearanceObserver.start(); + } else { + this.#loadSourceURL(); + } + this.formLinkClickObserver.start(); + this.linkInterceptor.start(); + this.formSubmitObserver.start(); + } + } + disconnect() { + if (this.#connected) { + this.#connected = false; + this.appearanceObserver.stop(); + this.formLinkClickObserver.stop(); + this.linkInterceptor.stop(); + this.formSubmitObserver.stop(); + } + } + disabledChanged() { + if (this.loadingStyle == FrameLoadingStyle.eager) { + this.#loadSourceURL(); + } + } + sourceURLChanged() { + if (this.#isIgnoringChangesTo("src")) return; + if (this.element.isConnected) { + this.complete = false; + } + if (this.loadingStyle == FrameLoadingStyle.eager || this.#hasBeenLoaded) { + this.#loadSourceURL(); + } + } + sourceURLReloaded() { + const { refresh, src } = this.element; + this.#shouldMorphFrame = src && refresh === "morph"; + this.element.removeAttribute("complete"); + this.element.src = null; + this.element.src = src; + return this.element.loaded; + } + loadingStyleChanged() { + if (this.loadingStyle == FrameLoadingStyle.lazy) { + this.appearanceObserver.start(); + } else { + this.appearanceObserver.stop(); + this.#loadSourceURL(); + } + } + async #loadSourceURL() { + if (this.enabled && this.isActive && !this.complete && this.sourceURL) { + this.element.loaded = this.#visit(expandURL(this.sourceURL)); + this.appearanceObserver.stop(); + await this.element.loaded; + this.#hasBeenLoaded = true; + } + } + async loadResponse(fetchResponse) { + if (fetchResponse.redirected || fetchResponse.succeeded && fetchResponse.isHTML) { + this.sourceURL = fetchResponse.response.url; + } + try { + const html = await fetchResponse.responseHTML; + if (html) { + const document2 = parseHTMLDocument(html); + const pageSnapshot = PageSnapshot.fromDocument(document2); + if (pageSnapshot.isVisitable) { + await this.#loadFrameResponse(fetchResponse, document2); + } else { + await this.#handleUnvisitableFrameResponse(fetchResponse); + } + } + } finally { + this.#shouldMorphFrame = false; + this.fetchResponseLoaded = () => Promise.resolve(); + } + } + // Appearance observer delegate + elementAppearedInViewport(element) { + this.proposeVisitIfNavigatedWithAction(element, getVisitAction(element)); + this.#loadSourceURL(); + } + // Form link click observer delegate + willSubmitFormLinkToLocation(link) { + return this.#shouldInterceptNavigation(link); + } + submittedFormLinkToLocation(link, _location, form) { + const frame = this.#findFrameElement(link); + if (frame) form.setAttribute("data-turbo-frame", frame.id); + } + // Link interceptor delegate + shouldInterceptLinkClick(element, _location, _event) { + return this.#shouldInterceptNavigation(element); + } + linkClickIntercepted(element, location2) { + this.#navigateFrame(element, location2); + } + // Form submit observer delegate + willSubmitForm(element, submitter2) { + return element.closest("turbo-frame") == this.element && this.#shouldInterceptNavigation(element, submitter2); + } + formSubmitted(element, submitter2) { + if (this.formSubmission) { + this.formSubmission.stop(); + } + this.formSubmission = new FormSubmission(this, element, submitter2); + const { fetchRequest } = this.formSubmission; + this.prepareRequest(fetchRequest); + this.formSubmission.start(); + } + // Fetch request delegate + prepareRequest(request) { + request.headers["Turbo-Frame"] = this.id; + if (this.currentNavigationElement?.hasAttribute("data-turbo-stream")) { + request.acceptResponseType(StreamMessage.contentType); + } + } + requestStarted(_request) { + markAsBusy(this.element); + } + requestPreventedHandlingResponse(_request, _response) { + this.#resolveVisitPromise(); + } + async requestSucceededWithResponse(request, response) { + await this.loadResponse(response); + this.#resolveVisitPromise(); + } + async requestFailedWithResponse(request, response) { + await this.loadResponse(response); + this.#resolveVisitPromise(); + } + requestErrored(request, error2) { + console.error(error2); + this.#resolveVisitPromise(); + } + requestFinished(_request) { + clearBusyState(this.element); + } + // Form submission delegate + formSubmissionStarted({ formElement }) { + markAsBusy(formElement, this.#findFrameElement(formElement)); + } + formSubmissionSucceededWithResponse(formSubmission, response) { + const frame = this.#findFrameElement(formSubmission.formElement, formSubmission.submitter); + frame.delegate.proposeVisitIfNavigatedWithAction(frame, getVisitAction(formSubmission.submitter, formSubmission.formElement, frame)); + frame.delegate.loadResponse(response); + if (!formSubmission.isSafe) { + session.clearCache(); + } + } + formSubmissionFailedWithResponse(formSubmission, fetchResponse) { + this.element.delegate.loadResponse(fetchResponse); + session.clearCache(); + } + formSubmissionErrored(formSubmission, error2) { + console.error(error2); + } + formSubmissionFinished({ formElement }) { + clearBusyState(formElement, this.#findFrameElement(formElement)); + } + // View delegate + allowsImmediateRender({ element: newFrame }, options) { + const event = dispatch("turbo:before-frame-render", { + target: this.element, + detail: { newFrame, ...options }, + cancelable: true + }); + const { + defaultPrevented, + detail: { render } + } = event; + if (this.view.renderer && render) { + this.view.renderer.renderElement = render; + } + return !defaultPrevented; + } + viewRenderedSnapshot(_snapshot, _isPreview, _renderMethod) { + } + preloadOnLoadLinksForView(element) { + session.preloadOnLoadLinksForView(element); + } + viewInvalidated() { + } + // Frame renderer delegate + willRenderFrame(currentElement, _newElement) { + this.previousFrameElement = currentElement.cloneNode(true); + } + visitCachedSnapshot = ({ element }) => { + const frame = element.querySelector("#" + this.element.id); + if (frame && this.previousFrameElement) { + frame.replaceChildren(...this.previousFrameElement.children); + } + delete this.previousFrameElement; + }; + // Private + async #loadFrameResponse(fetchResponse, document2) { + const newFrameElement = await this.extractForeignFrameElement(document2.body); + const rendererClass = this.#shouldMorphFrame ? MorphingFrameRenderer : FrameRenderer; + if (newFrameElement) { + const snapshot = new Snapshot(newFrameElement); + const renderer = new rendererClass(this, this.view.snapshot, snapshot, false, false); + if (this.view.renderPromise) await this.view.renderPromise; + this.changeHistory(); + await this.view.render(renderer); + this.complete = true; + session.frameRendered(fetchResponse, this.element); + session.frameLoaded(this.element); + await this.fetchResponseLoaded(fetchResponse); + } else if (this.#willHandleFrameMissingFromResponse(fetchResponse)) { + this.#handleFrameMissingFromResponse(fetchResponse); + } + } + async #visit(url) { + const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element); + this.#currentFetchRequest?.cancel(); + this.#currentFetchRequest = request; + return new Promise((resolve) => { + this.#resolveVisitPromise = () => { + this.#resolveVisitPromise = () => { + }; + this.#currentFetchRequest = null; + resolve(); + }; + request.perform(); + }); + } + #navigateFrame(element, url, submitter2) { + const frame = this.#findFrameElement(element, submitter2); + frame.delegate.proposeVisitIfNavigatedWithAction(frame, getVisitAction(submitter2, element, frame)); + this.#withCurrentNavigationElement(element, () => { + frame.src = url; + }); + } + proposeVisitIfNavigatedWithAction(frame, action = null) { + this.action = action; + if (this.action) { + const pageSnapshot = PageSnapshot.fromElement(frame).clone(); + const { visitCachedSnapshot } = frame.delegate; + frame.delegate.fetchResponseLoaded = async (fetchResponse) => { + if (frame.src) { + const { statusCode, redirected } = fetchResponse; + const responseHTML = await fetchResponse.responseHTML; + const response = { statusCode, redirected, responseHTML }; + const options = { + response, + visitCachedSnapshot, + willRender: false, + updateHistory: false, + restorationIdentifier: this.restorationIdentifier, + snapshot: pageSnapshot + }; + if (this.action) options.action = this.action; + session.visit(frame.src, options); + } + }; + } + } + changeHistory() { + if (this.action) { + const method = getHistoryMethodForAction(this.action); + session.history.update(method, expandURL(this.element.src || ""), this.restorationIdentifier); + } + } + async #handleUnvisitableFrameResponse(fetchResponse) { + console.warn( + `The response (${fetchResponse.statusCode}) from is performing a full page visit due to turbo-visit-control.` + ); + await this.#visitResponse(fetchResponse.response); + } + #willHandleFrameMissingFromResponse(fetchResponse) { + this.element.setAttribute("complete", ""); + const response = fetchResponse.response; + const visit2 = async (url, options) => { + if (url instanceof Response) { + this.#visitResponse(url); + } else { + session.visit(url, options); + } + }; + const event = dispatch("turbo:frame-missing", { + target: this.element, + detail: { response, visit: visit2 }, + cancelable: true + }); + return !event.defaultPrevented; + } + #handleFrameMissingFromResponse(fetchResponse) { + this.view.missing(); + this.#throwFrameMissingError(fetchResponse); + } + #throwFrameMissingError(fetchResponse) { + const message = `The response (${fetchResponse.statusCode}) did not contain the expected and will be ignored. To perform a full page visit instead, set turbo-visit-control to reload.`; + throw new TurboFrameMissingError(message); + } + async #visitResponse(response) { + const wrapped = new FetchResponse(response); + const responseHTML = await wrapped.responseHTML; + const { location: location2, redirected, statusCode } = wrapped; + return session.visit(location2, { response: { redirected, statusCode, responseHTML } }); + } + #findFrameElement(element, submitter2) { + const id = getAttribute("data-turbo-frame", submitter2, element) || this.element.getAttribute("target"); + return getFrameElementById(id) ?? this.element; + } + async extractForeignFrameElement(container) { + let element; + const id = CSS.escape(this.id); + try { + element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL); + if (element) { + return element; + } + element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL); + if (element) { + await element.loaded; + return await this.extractForeignFrameElement(element); + } + } catch (error2) { + console.error(error2); + return new FrameElement(); + } + return null; + } + #formActionIsVisitable(form, submitter2) { + const action = getAction$1(form, submitter2); + return locationIsVisitable(expandURL(action), this.rootLocation); + } + #shouldInterceptNavigation(element, submitter2) { + const id = getAttribute("data-turbo-frame", submitter2, element) || this.element.getAttribute("target"); + if (element instanceof HTMLFormElement && !this.#formActionIsVisitable(element, submitter2)) { + return false; + } + if (!this.enabled || id == "_top") { + return false; + } + if (id) { + const frameElement = getFrameElementById(id); + if (frameElement) { + return !frameElement.disabled; + } + } + if (!session.elementIsNavigatable(element)) { + return false; + } + if (submitter2 && !session.elementIsNavigatable(submitter2)) { + return false; + } + return true; + } + // Computed properties + get id() { + return this.element.id; + } + get enabled() { + return !this.element.disabled; + } + get sourceURL() { + if (this.element.src) { + return this.element.src; + } + } + set sourceURL(sourceURL) { + this.#ignoringChangesToAttribute("src", () => { + this.element.src = sourceURL ?? null; + }); + } + get loadingStyle() { + return this.element.loading; + } + get isLoading() { + return this.formSubmission !== void 0 || this.#resolveVisitPromise() !== void 0; + } + get complete() { + return this.element.hasAttribute("complete"); + } + set complete(value) { + if (value) { + this.element.setAttribute("complete", ""); + } else { + this.element.removeAttribute("complete"); + } + } + get isActive() { + return this.element.isActive && this.#connected; + } + get rootLocation() { + const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`); + const root = meta?.content ?? "/"; + return expandURL(root); + } + #isIgnoringChangesTo(attributeName) { + return this.#ignoredAttributes.has(attributeName); + } + #ignoringChangesToAttribute(attributeName, callback) { + this.#ignoredAttributes.add(attributeName); + callback(); + this.#ignoredAttributes.delete(attributeName); + } + #withCurrentNavigationElement(element, callback) { + this.currentNavigationElement = element; + callback(); + delete this.currentNavigationElement; + } +}; +function getFrameElementById(id) { + if (id != null) { + const element = document.getElementById(id); + if (element instanceof FrameElement) { + return element; + } + } +} +function activateElement(element, currentURL) { + if (element) { + const src = element.getAttribute("src"); + if (src != null && currentURL != null && urlsAreEqual(src, currentURL)) { + throw new Error(`Matching element has a source URL which references itself`); + } + if (element.ownerDocument !== document) { + element = document.importNode(element, true); + } + if (element instanceof FrameElement) { + element.connectedCallback(); + element.disconnectedCallback(); + return element; + } + } +} +var StreamActions = { + 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 method = this.getAttribute("method"); + this.targetElements.forEach((targetElement) => { + if (method === "morph") { + morphElements(targetElement, this.templateContent); + } else { + targetElement.replaceWith(this.templateContent); + } + }); + }, + update() { + const method = this.getAttribute("method"); + this.targetElements.forEach((targetElement) => { + if (method === "morph") { + morphChildren(targetElement, this.templateContent); + } else { + targetElement.innerHTML = ""; + targetElement.append(this.templateContent); + } + }); + }, + refresh() { + session.refresh(this.baseURI, this.requestId); + } +}; +var StreamElement = class _StreamElement extends HTMLElement { + static async renderElement(newElement) { + await newElement.performAction(); + } + async connectedCallback() { + try { + await this.render(); + } catch (error2) { + console.error(error2); + } finally { + this.disconnect(); + } + } + async render() { + return this.renderPromise ??= (async () => { + const event = this.beforeRenderEvent; + if (this.dispatchEvent(event)) { + await nextRepaint(); + await event.detail.render(this); + } + })(); + } + disconnect() { + try { + this.remove(); + } catch { + } + } + /** + * Removes duplicate children (by ID) + */ + removeDuplicateTargetChildren() { + this.duplicateChildren.forEach((c) => c.remove()); + } + /** + * Gets the list of duplicate children (i.e. those with the same ID) + */ + get duplicateChildren() { + const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.getAttribute("id")); + const newChildrenIds = [...this.templateContent?.children || []].filter((c) => !!c.getAttribute("id")).map((c) => c.getAttribute("id")); + return existingChildren.filter((c) => newChildrenIds.includes(c.getAttribute("id"))); + } + /** + * Gets the action function to be performed. + */ + get performAction() { + if (this.action) { + const actionFunction = StreamActions[this.action]; + if (actionFunction) { + return actionFunction; + } + this.#raise("unknown action"); + } + this.#raise("action attribute is missing"); + } + /** + * Gets the target elements which the template will be rendered to. + */ + get targetElements() { + if (this.target) { + return this.targetElementsById; + } else if (this.targets) { + return this.targetElementsByQuery; + } else { + this.#raise("target or targets attribute is missing"); + } + } + /** + * Gets the contents of the main `