├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── .reek.yml
├── .rspec
├── .rubocop.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
├── console
└── setup
├── htmx-rails.gemspec
├── lib
├── assets
│ └── javascripts
│ │ └── htmx.js
├── generators
│ └── htmx
│ │ └── install_generator.rb
├── htmx-rails.rb
└── htmx
│ └── rails
│ └── version.rb
└── spec
├── lib
└── generators
│ └── install_generator_spec.rb
├── spec_helper.rb
└── support
└── files_helper.rb
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 |
3 | on: [push, pull_request]
4 | jobs:
5 | test:
6 | strategy:
7 | fail-fast: false
8 | matrix:
9 | os: [ubuntu-latest, macos-latest]
10 | ruby: ['2.7', '3.0', '3.1', head, jruby, jruby-head, truffleruby, truffleruby-head]
11 | runs-on: ${{ matrix.os }}
12 | steps:
13 | - uses: actions/checkout@v3
14 | - name: Download CodeClimate reporter
15 | run: |
16 | if [ "$RUNNER_OS" == "Linux" ]; then
17 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
18 | chmod +x ./cc-test-reporter
19 | ./cc-test-reporter before-build
20 | elif [ "$RUNNER_OS" == "macOS" ]; then
21 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-darwin-amd64 > ./cc-test-reporter
22 | chmod +x ./cc-test-reporter
23 | ./cc-test-reporter before-build
24 | else
25 | echo "$RUNNER_OS not supported"
26 | exit 1
27 | fi
28 | env:
29 | CC_TEST_REPORTER_ID: 903dc1a761aaec438bc9f39467e2cb4a2ea332bdb65241b37abef6338c5e6326
30 | shell: bash
31 | - uses: ruby/setup-ruby@v1
32 | with:
33 | ruby-version: ${{ matrix.ruby }}
34 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically
35 | - name: Run code analysis
36 | run: |
37 | bundle exec rake code_analysis
38 | - name: Run tests
39 | run: |
40 | bundle exec rspec
41 | - name: Report to CodeClimate
42 | run: |
43 | ./cc-test-reporter after-build --exit-code 0
44 | env:
45 | CC_TEST_REPORTER_ID: 903dc1a761aaec438bc9f39467e2cb4a2ea332bdb65241b37abef6338c5e6326
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /.yardoc
3 | /_yardoc/
4 | /coverage/
5 | /doc/
6 | /pkg/
7 | /spec/reports/
8 | /tmp/
9 |
10 | Gemfile.lock
11 |
12 | # rspec failure tracking
13 | .rspec_status
14 |
15 | # tests output
16 | node_modules/
17 | package.json
18 | spec/tmp/
19 | yarn.lock
20 | bun.lockb
--------------------------------------------------------------------------------
/.reek.yml:
--------------------------------------------------------------------------------
1 | detectors:
2 | Attribute:
3 | enabled: false
4 | exclude: []
5 | BooleanParameter:
6 | enabled: true
7 | exclude: []
8 | ClassVariable:
9 | enabled: false
10 | exclude: []
11 | ControlParameter:
12 | enabled: true
13 | exclude: []
14 | DataClump:
15 | enabled: true
16 | exclude: []
17 | max_copies: 2
18 | min_clump_size: 2
19 | DuplicateMethodCall:
20 | enabled: true
21 | exclude: []
22 | max_calls: 1
23 | allow_calls: []
24 | FeatureEnvy:
25 | enabled: true
26 | exclude: []
27 | InstanceVariableAssumption:
28 | enabled: false
29 | IrresponsibleModule:
30 | enabled: false
31 | exclude: []
32 | LongParameterList:
33 | enabled: true
34 | exclude: []
35 | max_params: 4
36 | overrides:
37 | initialize:
38 | max_params: 5
39 | LongYieldList:
40 | enabled: true
41 | exclude: []
42 | max_params: 3
43 | ManualDispatch:
44 | enabled: true
45 | exclude: []
46 | MissingSafeMethod:
47 | enabled: false
48 | exclude: []
49 | ModuleInitialize:
50 | enabled: true
51 | exclude: []
52 | NestedIterators:
53 | enabled: true
54 | exclude: []
55 | max_allowed_nesting: 2
56 | ignore_iterators: []
57 | NilCheck:
58 | enabled: false
59 | exclude: []
60 | RepeatedConditional:
61 | enabled: true
62 | exclude: []
63 | max_ifs: 3
64 | SubclassedFromCoreClass:
65 | enabled: true
66 | exclude: []
67 | TooManyConstants:
68 | enabled: true
69 | exclude: []
70 | max_constants: 5
71 | TooManyInstanceVariables:
72 | enabled: true
73 | exclude: []
74 | max_instance_variables: 9
75 | TooManyMethods:
76 | enabled: true
77 | exclude: []
78 | max_methods: 25
79 | TooManyStatements:
80 | enabled: true
81 | exclude:
82 | - initialize
83 | max_statements: 12
84 | UncommunicativeMethodName:
85 | enabled: true
86 | exclude: []
87 | reject:
88 | - "/^[a-z]$/"
89 | - "/[0-9]$/"
90 | - "/[A-Z]/"
91 | accept: []
92 | UncommunicativeModuleName:
93 | enabled: true
94 | exclude: []
95 | reject:
96 | - "/^.$/"
97 | - "/[0-9]$/"
98 | accept:
99 | - Inline::C
100 | - "/V[0-9]/"
101 | UncommunicativeParameterName:
102 | enabled: true
103 | exclude: []
104 | reject:
105 | - "/^.$/"
106 | - "/[0-9]$/"
107 | - "/[A-Z]/"
108 | accept: []
109 | UncommunicativeVariableName:
110 | enabled: true
111 | exclude: []
112 | reject:
113 | - "/^.$/"
114 | - "/[0-9]$/"
115 | - "/[A-Z]/"
116 | accept:
117 | - _
118 | UnusedParameters:
119 | enabled: true
120 | exclude: []
121 | UnusedPrivateMethod:
122 | enabled: false
123 | UtilityFunction:
124 | enabled: false
125 |
126 | exclude_paths:
127 | - config
128 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --format documentation
2 | --color
3 | --require spec_helper
4 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_mode:
2 | merge:
3 | - Exclude
4 |
5 | require:
6 | - rubocop-rspec
7 | - rubocop-rake
8 |
9 | AllCops:
10 | NewCops: enable
11 | TargetRubyVersion: 2.7
12 |
13 | Naming/FileName:
14 | Exclude:
15 | - 'lib/htmx-rails.rb'
16 |
17 | Gemspec/RequireMFA:
18 | Enabled: false
19 |
20 | RSpec/BeforeAfterAll:
21 | Enabled: false
22 |
23 | Style/Documentation:
24 | Enabled: false
25 |
26 | RSpec/FilePath:
27 | Enabled: false
28 |
29 | RSpec/SpecFilePathFormat:
30 | Enabled: false
31 |
32 | RSpec/AnyInstance:
33 | Exclude:
34 | - 'spec/lib/generators/install_generator_spec.rb'
35 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## [Unreleased]
9 |
10 | ### Added
11 |
12 | - Added CHANGELOG.md (#10)
13 | - Added importmap support (#9)
14 |
15 | ## [0.2.0] - 2023-10-07
16 |
17 | ### Changed
18 |
19 | - Update HTMX lib to 1.9.2 (#4)
20 | - Update GH actions build
21 |
22 | ## [0.1.0] - 2021-01-11
23 |
24 | ### Added
25 |
26 | - Introduce installation generator (support for Sprokets and Webpacker)
27 | - Simple example on README usage section
28 |
29 | ## [0.0.0] - 2020-12-11
30 |
31 | ### Added
32 |
33 | - Initial release
34 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at julian.pasquale@rootstrap.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [https://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: https://contributor-covenant.org
74 | [version]: https://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source 'https://rubygems.org'
4 |
5 | # Specify your gem's dependencies in htmx-rails.gemspec
6 | gemspec
7 |
8 | gem 'generator_spec', '~> 0.9.4'
9 | gem 'psych', '>= 5.1.1.1' # pinned due to https://github.com/ruby/psych/issues/655
10 | gem 'rake', '~> 13.1'
11 | gem 'reek', '~> 6.1'
12 | gem 'rspec', '~> 3.12'
13 | gem 'rubocop', '~> 1.57'
14 | gem 'rubocop-rake', '~> 0.6'
15 | gem 'rubocop-rspec', '~> 2.25'
16 | gem 'simplecov', '~> 0.17.1'
17 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 Rootstrap
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Htmx::Rails
2 |
3 | [HTMX](https://htmx.org/) ruby gem for Ruby on Rails.
4 |
5 | ## Installation
6 |
7 | ### Gem installation
8 |
9 | Add `htmx-rails` to your `Gemfile`:
10 |
11 | ```ruby
12 | gem 'htmx-rails'
13 | ```
14 |
15 | And then execute in your shell:
16 |
17 | ```
18 | bundle install
19 | ```
20 |
21 | Or install it yourself by executing:
22 |
23 | ```
24 | gem install htmx-rails
25 | ```
26 |
27 | ### After installing the gem, run the installer:
28 |
29 | ```
30 | rails g htmx:install
31 | ```
32 |
33 | ## Usage
34 |
35 |
36 | ```HTML
37 |
38 |
41 | ```
42 |
43 | ### Read the [docs](https://htmx.org/docs/) for a more in-depth introduction.
44 |
45 | ## Development
46 |
47 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
48 |
49 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
50 |
51 | ## Contributing
52 |
53 | Bug reports and pull requests are welcome on GitHub at https://github.com/rootstrap/htmx-rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/rootstrap/htmx-rails/blob/master/CODE_OF_CONDUCT.md).
54 |
55 |
56 | ## License
57 |
58 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
59 |
60 | ## Code of Conduct
61 |
62 | Everyone interacting in the Htmx::Rails project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/rootstrap/htmx-rails/blob/master/CODE_OF_CONDUCT.md).
63 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bundler/gem_tasks'
4 | require 'rspec/core/rake_task'
5 |
6 | RSpec::Core::RakeTask.new(:spec)
7 |
8 | task default: :spec
9 |
10 | desc 'Run analysis tools to ensure code quality'
11 | task :code_analysis do
12 | sh 'bundle exec rubocop lib spec'
13 | sh 'bundle exec reek lib'
14 | end
15 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | require 'bundler/setup'
5 | require 'htmx/rails'
6 |
7 | # You can add fixtures and/or initialization code here to make experimenting
8 | # with your gem easier. You can also use a different console, if you like.
9 |
10 | # (If you use this, don't forget to add pry to your Gemfile!)
11 | # require "pry"
12 | # Pry.start
13 |
14 | require 'irb'
15 | IRB.start(__FILE__)
16 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 | set -vx
5 |
6 | bundle install
7 |
8 | # Do any other automated setup that you need to do here
9 |
--------------------------------------------------------------------------------
/htmx-rails.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative 'lib/htmx/rails/version'
4 |
5 | Gem::Specification.new do |spec|
6 | spec.name = 'htmx-rails'
7 | spec.version = Htmx::Rails::VERSION
8 | spec.authors = ['Julian Pasquale']
9 | spec.email = ['julian.pasquale@rootstrap.com']
10 | spec.summary = 'Ruby gem for use HTMX in Rails applications'
11 | spec.description = 'Ruby gem for use HTMX in Rails applications'
12 | spec.homepage = 'https://github.com/rootstrap/htmx-rails'
13 | spec.license = 'MIT'
14 |
15 | spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
16 |
17 | spec.metadata['homepage_uri'] = spec.homepage
18 | spec.metadata['source_code_uri'] = 'https://github.com/rootstrap/htmx-rails'
19 | spec.metadata['changelog_uri'] = 'https://github.com/rootstrap/htmx-rails'
20 |
21 | spec.files = Dir['LICENSE.txt', 'README.md', 'lib/**/*']
22 | spec.bindir = 'exe'
23 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24 | spec.require_paths = ['lib']
25 |
26 | spec.add_dependency 'rails', '>= 5.0'
27 | end
28 |
--------------------------------------------------------------------------------
/lib/assets/javascripts/htmx.js:
--------------------------------------------------------------------------------
1 | (function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else if(typeof module==="object"&&module.exports){module.exports=t()}else{e.htmx=e.htmx||t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var z={onLoad:t,process:Tt,on:le,off:ue,trigger:ie,ajax:dr,find:b,findAll:f,closest:d,values:function(e,t){var r=Jt(e,t||"post");return r.values},remove:B,addClass:j,removeClass:n,toggleClass:U,takeClass:V,defineExtension:yr,removeExtension:br,logAll:F,logger:null,config:{historyEnabled:true,historyCacheSize:10,refreshOnHistoryMiss:false,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:true,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:true,inlineScriptNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:false,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false,getCacheBusterParam:false,globalViewTransitions:false},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=z.config.wsBinaryType;return t},version:"1.9.2"};var C={addTriggerHandler:xt,bodyContains:ee,canAccessLocalStorage:D,filterValues:er,hasAttribute:q,getAttributeValue:G,getClosestMatch:c,getExpressionVars:fr,getHeaders:Qt,getInputValues:Jt,getInternalData:Y,getSwapSpecification:rr,getTriggerSpecs:ze,getTarget:de,makeFragment:l,mergeObjects:te,makeSettleInfo:S,oobSwap:me,selectAndSwap:Me,settleImmediately:Bt,shouldCancel:Ke,triggerEvent:ie,triggerErrorEvent:ne,withExtensions:w};var R=["get","post","put","delete","patch"];var O=R.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");function v(e){if(e==undefined){return undefined}if(e.slice(-2)=="ms"){return parseFloat(e.slice(0,-2))||undefined}if(e.slice(-1)=="s"){return parseFloat(e.slice(0,-1))*1e3||undefined}if(e.slice(-1)=="m"){return parseFloat(e.slice(0,-1))*1e3*60||undefined}return parseFloat(e)||undefined}function $(e,t){return e.getAttribute&&e.getAttribute(t)}function q(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function G(e,t){return $(e,t)||$(e,"data-"+t)}function u(e){return e.parentElement}function J(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function T(e,t,r){var n=G(t,r);var i=G(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function Z(t,r){var n=null;c(t,function(e){return n=T(t,e,r)});if(n!=="unset"){return n}}function h(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function H(e){var t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i;var r=t.exec(e);if(r){return r[1].toLowerCase()}else{return""}}function i(e,t){var r=new DOMParser;var n=r.parseFromString(e,"text/html");var i=n.body;while(t>0){t--;i=i.firstChild}if(i==null){i=J().createDocumentFragment()}return i}function L(e){return e.match(/
"+e+"",0);return r.querySelector("template").content}else{var n=H(e);switch(n){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return i("",1);case"col":return i("",2);case"tr":return i("",2);case"td":case"th":return i("",3);case"script":return i(""+e+"
",1);default:return i(e,0)}}}function K(e){if(e){e()}}function A(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function N(e){return A(e,"Function")}function I(e){return A(e,"Object")}function Y(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function k(e){var t=[];if(e){for(var r=0;r=0}function ee(e){if(e.getRootNode&&e.getRootNode()instanceof ShadowRoot){return J().body.contains(e.getRootNode().host)}else{return J().body.contains(e)}}function M(e){return e.trim().split(/\s+/)}function te(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function y(e){try{return JSON.parse(e)}catch(e){x(e);return null}}function D(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function X(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!t.match("^/$")){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function e(e){return sr(J().body,function(){return eval(e)})}function t(t){var e=z.on("htmx:load",function(e){t(e.detail.elt)});return e}function F(){z.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function b(e,t){if(t){return e.querySelector(t)}else{return b(J(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(J(),e)}}function B(e,t){e=s(e);if(t){setTimeout(function(){B(e);e=null},t)}else{e.parentElement.removeChild(e)}}function j(e,t,r){e=s(e);if(r){setTimeout(function(){j(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=s(e);if(r){setTimeout(function(){n(e,t);e=null},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function U(e,t){e=s(e);e.classList.toggle(t)}function V(e,t){e=s(e);Q(e.parentElement.children,function(e){n(e,t)});j(e,t)}function d(e,t){e=s(e);if(e.closest){return e.closest(t)}else{do{if(e==null||h(e,t)){return e}}while(e=e&&u(e));return null}}function r(e){var t=e.trim();if(t.startsWith("<")&&t.endsWith("/>")){return t.substring(1,t.length-2)}else{return t}}function _(e,t){if(t.indexOf("closest ")===0){return[d(e,r(t.substr(8)))]}else if(t.indexOf("find ")===0){return[b(e,r(t.substr(5)))]}else if(t.indexOf("next ")===0){return[W(e,r(t.substr(5)))]}else if(t.indexOf("previous ")===0){return[oe(e,r(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else{return J().querySelectorAll(r(t))}}var W=function(e,t){var r=J().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function re(e,t){if(t){return _(e,t)[0]}else{return _(J().body,e)[0]}}function s(e){if(A(e,"String")){return b(e)}else{return e}}function se(e,t,r){if(N(t)){return{target:J().body,event:e,listener:t}}else{return{target:s(e),event:t,listener:r}}}function le(t,r,n){Sr(function(){var e=se(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=N(r);return e?r:n}function ue(t,r,n){Sr(function(){var e=se(t,r,n);e.target.removeEventListener(e.event,e.listener)});return N(r)?r:n}var fe=J().createElement("output");function ce(e,t){var r=Z(e,t);if(r){if(r==="this"){return[he(e,t)]}else{var n=_(e,r);if(n.length===0){x('The selector "'+r+'" on '+t+" returned no matches!");return[fe]}else{return n}}}}function he(e,t){return c(e,function(e){return G(e,t)!=null})}function de(e){var t=Z(e,"hx-target");if(t){if(t==="this"){return he(e,"hx-target")}else{return re(e,t)}}else{var r=Y(e);if(r.boosted){return J().body}else{return e}}}function ve(e){var t=z.config.attributesToSettle;for(var r=0;r0){o=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{o=e}var r=J().querySelectorAll(t);if(r){Q(r,function(e){var t;var r=i.cloneNode(true);t=J().createDocumentFragment();t.appendChild(r);if(!pe(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!ie(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){ke(o,e,e,t,a)}Q(a.elts,function(e){ie(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);ne(J().body,"htmx:oobErrorNoTarget",{content:i})}return e}function xe(e,t,r){var n=Z(e,"hx-select-oob");if(n){var i=n.split(",");for(let e=0;e0){var t=e.id.replace("'","\\'");var r=e.tagName.replace(":","\\:");var n=a.querySelector(r+"[id='"+t+"']");if(n&&n!==a){var i=e.cloneNode();ge(e,n);o.tasks.push(function(){ge(e,i)})}}})}function we(e){return function(){n(e,z.config.addedClass);Tt(e);bt(e);Se(e);ie(e,"htmx:load")}}function Se(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function a(e,t,r,n){be(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;j(i,z.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(we(i))}}}function Ee(e,t){var r=0;while(r-1){var t=e.replace(/